diff options
Diffstat (limited to 'virtio_rng.c')
-rw-r--r-- | virtio_rng.c | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/virtio_rng.c b/virtio_rng.c new file mode 100644 index 0000000..6a5b24b --- /dev/null +++ b/virtio_rng.c @@ -0,0 +1,179 @@ +/* + * A virtio device implementing a hardware random number generator. + * + * Based on virtio-rng.c of QEMU project + * Copyright 2012 Red Hat, Inc. + * Copyright 2012 Amit Shah <amit.shah@redhat.com> + * + * Copyright 2022-2023 Virtual Open Systems SAS. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <stdbool.h> +#include <sys/param.h> + +/* Project header files */ +#include "virtio_loopback.h" +#include "virtio_rng.h" + +#ifdef DEBUG +#define DBG(...) printf("virtio-rng: " __VA_ARGS__) +#else +#define DBG(...) +#endif /* DEBUG */ + +bool is_guest_ready(VirtIORNG *vrng) +{ + VirtIODevice *vdev = vrng->parent_obj; + + if (virtio_queue_ready(vrng->vq) + && (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { + return true; + } + return false; +} + +size_t get_request_size(VirtQueue *vq, unsigned quota) +{ + unsigned int in, out; + + virtqueue_get_avail_bytes(vq, &in, &out, quota, 0); + return in; +} + +void virtio_rng_set_status(VirtIODevice *vdev, uint8_t status) +{ + VirtIORNG *vrng = vdev->vrng; + + vdev->status = status; + + /* Something changed, try to process buffers */ + virtio_rng_process(vrng); +} + +/* Send data from a char device over to the guest */ +void chr_read(VirtIORNG *vrng, const void *buf, size_t size) +{ + VirtIODevice *vdev = vrng->parent_obj; + VirtQueueElement *elem; + size_t len; + int offset; + + if (!is_guest_ready(vrng)) { + return; + } + + vrng->quota_remaining -= size; + + offset = 0; + while (offset < size) { + elem = virtqueue_pop(vrng->vq, sizeof(VirtQueueElement)); + + + if (!elem) { + break; + } + len = qemu_iov_from_buf(elem->in_sg, elem->in_num, + 0, buf + offset, size - offset); + offset += len; + + virtqueue_push(vrng->vq, elem, len); + + /* + * TODO: We need tp free the elem + * + * g_free(elem); + */ + } + virtio_notify(vdev, vrng->vq); + + if (!virtio_queue_empty(vrng->vq)) { + /* + * If we didn't drain the queue, call virtio_rng_process + * to take care of asking for more data as appropriate. + */ + virtio_rng_process(vrng); + } +} + +const char test_str[64] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63}; + +void virtio_rng_process(VirtIORNG *vrng) +{ + size_t size; + unsigned quota; + + if (!is_guest_ready(vrng)) { + return; + } + + if (vrng->quota_remaining < 0) { + quota = 0; + } else { + quota = MIN((uint64_t)vrng->quota_remaining, (uint64_t)UINT32_MAX); + } + size = get_request_size(vrng->vq, quota); + size = MIN(vrng->quota_remaining, size); + + if (size) { + chr_read(vrng, &test_str, size); + } +} + +void handle_input(VirtIODevice *vdev, VirtQueue *vq) +{ + virtio_rng_process(vdev->vrng); +} + +static void virtio_dev_class_init(VirtIODevice *vdev) +{ + vdev->vdev_class = (VirtioDeviceClass *)malloc(sizeof(VirtioDeviceClass)); + vdev->vdev_class->parent = vdev; + vdev->vdev_class->set_status = virtio_rng_set_status; +} + +void virtio_rng_init(VirtIODevice *vdev) +{ + VirtIORNG *vrng = (VirtIORNG *)malloc(sizeof(VirtIORNG)); + vdev->vrng = vrng; + vrng->parent_obj = vdev; + vrng->vq = vdev->vq; + vrng->quota_remaining = LONG_MAX; + + /* Prepare dev_class */ + virtio_dev_class_init(vdev); +} + + +void virtio_rng_realize(void) +{ + /* prepare procy and virtio dev*/ + proxy = (VirtIOMMIOProxy *)malloc(sizeof(VirtIOMMIOProxy)); + + virtio_dev_init(global_vdev, "virtio-rng", 4, 0); + + virtio_rng_init(global_vdev); + + global_vdev->vq = virtio_add_queue(global_vdev, 8, handle_input); + + global_vdev->host_features = 0x39000000; + + *proxy = (VirtIOMMIOProxy) { + .legacy = 1, + }; +} + |