/* * 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 * * 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 #include #include #include #include #include #include /* 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, }; }