1376ac44cSSasha Levin #include "kvm/virtio-rng.h" 2376ac44cSSasha Levin 331638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h" 4376ac44cSSasha Levin 5376ac44cSSasha Levin #include "kvm/disk-image.h" 6376ac44cSSasha Levin #include "kvm/virtio.h" 7376ac44cSSasha Levin #include "kvm/ioport.h" 8376ac44cSSasha Levin #include "kvm/util.h" 9376ac44cSSasha Levin #include "kvm/kvm.h" 10376ac44cSSasha Levin #include "kvm/pci.h" 11376ac44cSSasha Levin #include "kvm/threadpool.h" 122449f6e3SSasha Levin #include "kvm/irq.h" 13b2533581SSasha Levin #include "kvm/ioeventfd.h" 14376ac44cSSasha Levin 15376ac44cSSasha Levin #include <linux/virtio_ring.h> 16376ac44cSSasha Levin #include <linux/virtio_rng.h> 17376ac44cSSasha Levin 1880ac1d05SSasha Levin #include <linux/list.h> 19376ac44cSSasha Levin #include <fcntl.h> 20376ac44cSSasha Levin #include <sys/types.h> 21376ac44cSSasha Levin #include <sys/stat.h> 22376ac44cSSasha Levin #include <pthread.h> 23376ac44cSSasha Levin 24376ac44cSSasha Levin #define NUM_VIRT_QUEUES 1 25376ac44cSSasha Levin #define VIRTIO_RNG_QUEUE_SIZE 128 26376ac44cSSasha Levin 2780ac1d05SSasha Levin struct rng_dev_job { 2880ac1d05SSasha Levin struct virt_queue *vq; 2980ac1d05SSasha Levin struct rng_dev *rdev; 30*df0c7f57SSasha Levin struct thread_pool__job job_id; 312449f6e3SSasha Levin }; 322449f6e3SSasha Levin 3380ffe4d1SSasha Levin struct rng_dev { 3480ac1d05SSasha Levin struct pci_device_header pci_hdr; 3580ac1d05SSasha Levin struct list_head list; 3680ac1d05SSasha Levin 3780ac1d05SSasha Levin u16 base_addr; 383fdf659dSSasha Levin u8 status; 39d2963622SAsias He u8 isr; 403fdf659dSSasha Levin u16 config_vector; 4180ffe4d1SSasha Levin int fd; 42376ac44cSSasha Levin 43376ac44cSSasha Levin /* virtio queue */ 443fdf659dSSasha Levin u16 queue_selector; 45376ac44cSSasha Levin struct virt_queue vqs[NUM_VIRT_QUEUES]; 4680ac1d05SSasha Levin struct rng_dev_job jobs[NUM_VIRT_QUEUES]; 47376ac44cSSasha Levin }; 48376ac44cSSasha Levin 4980ac1d05SSasha Levin static LIST_HEAD(rdevs); 50376ac44cSSasha Levin 513d62dea6SSasha Levin static bool virtio_rng_pci_io_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count) 52376ac44cSSasha Levin { 53376ac44cSSasha Levin unsigned long offset; 54376ac44cSSasha Levin bool ret = true; 5580ac1d05SSasha Levin struct rng_dev *rdev; 56376ac44cSSasha Levin 5780ac1d05SSasha Levin rdev = ioport->priv; 5880ac1d05SSasha Levin offset = port - rdev->base_addr; 59376ac44cSSasha Levin 60376ac44cSSasha Levin switch (offset) { 61376ac44cSSasha Levin case VIRTIO_PCI_HOST_FEATURES: 62376ac44cSSasha Levin case VIRTIO_PCI_GUEST_FEATURES: 63376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_SEL: 64376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_NOTIFY: 65376ac44cSSasha Levin ret = false; 66376ac44cSSasha Levin break; 67376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_PFN: 6880ac1d05SSasha Levin ioport__write32(data, rdev->vqs[rdev->queue_selector].pfn); 69376ac44cSSasha Levin break; 70376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_NUM: 71376ac44cSSasha Levin ioport__write16(data, VIRTIO_RNG_QUEUE_SIZE); 72376ac44cSSasha Levin break; 73376ac44cSSasha Levin case VIRTIO_PCI_STATUS: 7480ac1d05SSasha Levin ioport__write8(data, rdev->status); 75376ac44cSSasha Levin break; 76376ac44cSSasha Levin case VIRTIO_PCI_ISR: 7780ac1d05SSasha Levin ioport__write8(data, rdev->isr); 7880ac1d05SSasha Levin kvm__irq_line(kvm, rdev->pci_hdr.irq_line, VIRTIO_IRQ_LOW); 7980ac1d05SSasha Levin rdev->isr = VIRTIO_IRQ_LOW; 80376ac44cSSasha Levin break; 81376ac44cSSasha Levin case VIRTIO_MSI_CONFIG_VECTOR: 8280ac1d05SSasha Levin ioport__write16(data, rdev->config_vector); 83376ac44cSSasha Levin break; 84376ac44cSSasha Levin default: 85376ac44cSSasha Levin ret = false; 86407475bfSPekka Enberg break; 87376ac44cSSasha Levin }; 88376ac44cSSasha Levin 89376ac44cSSasha Levin return ret; 90376ac44cSSasha Levin } 91376ac44cSSasha Levin 9280ac1d05SSasha Levin static bool virtio_rng_do_io_request(struct kvm *kvm, struct rng_dev *rdev, struct virt_queue *queue) 93376ac44cSSasha Levin { 94376ac44cSSasha Levin struct iovec iov[VIRTIO_RNG_QUEUE_SIZE]; 95376ac44cSSasha Levin unsigned int len = 0; 96407475bfSPekka Enberg u16 out, in, head; 97376ac44cSSasha Levin 9880ffe4d1SSasha Levin head = virt_queue__get_iov(queue, iov, &out, &in, kvm); 9980ac1d05SSasha Levin len = readv(rdev->fd, iov, in); 100407475bfSPekka Enberg 101376ac44cSSasha Levin virt_queue__set_used_elem(queue, head, len); 102376ac44cSSasha Levin 103376ac44cSSasha Levin return true; 104376ac44cSSasha Levin } 105376ac44cSSasha Levin 106376ac44cSSasha Levin static void virtio_rng_do_io(struct kvm *kvm, void *param) 107376ac44cSSasha Levin { 10880ac1d05SSasha Levin struct rng_dev_job *job = param; 10980ac1d05SSasha Levin struct virt_queue *vq = job->vq; 11080ac1d05SSasha Levin struct rng_dev *rdev = job->rdev; 111376ac44cSSasha Levin 112376ac44cSSasha Levin while (virt_queue__available(vq)) { 11380ac1d05SSasha Levin virtio_rng_do_io_request(kvm, rdev, vq); 11480ac1d05SSasha Levin virt_queue__trigger_irq(vq, rdev->pci_hdr.irq_line, &rdev->isr, kvm); 115376ac44cSSasha Levin } 116376ac44cSSasha Levin } 117376ac44cSSasha Levin 1183d62dea6SSasha Levin static bool virtio_rng_pci_io_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count) 119376ac44cSSasha Levin { 120376ac44cSSasha Levin unsigned long offset; 121376ac44cSSasha Levin bool ret = true; 12280ac1d05SSasha Levin struct rng_dev *rdev; 123376ac44cSSasha Levin 12480ac1d05SSasha Levin rdev = ioport->priv; 12580ac1d05SSasha Levin offset = port - rdev->base_addr; 126376ac44cSSasha Levin 127376ac44cSSasha Levin switch (offset) { 128376ac44cSSasha Levin case VIRTIO_MSI_QUEUE_VECTOR: 129376ac44cSSasha Levin case VIRTIO_PCI_GUEST_FEATURES: 130376ac44cSSasha Levin break; 131376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_PFN: { 132376ac44cSSasha Levin struct virt_queue *queue; 13380ac1d05SSasha Levin struct rng_dev_job *job; 134376ac44cSSasha Levin void *p; 135376ac44cSSasha Levin 13680ac1d05SSasha Levin queue = &rdev->vqs[rdev->queue_selector]; 137376ac44cSSasha Levin queue->pfn = ioport__read32(data); 138aaf0b445SSasha Levin p = guest_pfn_to_host(kvm, queue->pfn); 139376ac44cSSasha Levin 14080ac1d05SSasha Levin job = &rdev->jobs[rdev->queue_selector]; 14180ac1d05SSasha Levin 142b8f43678SSasha Levin vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN); 143376ac44cSSasha Levin 14480ac1d05SSasha Levin *job = (struct rng_dev_job) { 14580ac1d05SSasha Levin .vq = queue, 14680ac1d05SSasha Levin .rdev = rdev, 14780ac1d05SSasha Levin }; 14880ac1d05SSasha Levin 149*df0c7f57SSasha Levin thread_pool__init_job(&job->job_id, kvm, virtio_rng_do_io, job); 150376ac44cSSasha Levin 151376ac44cSSasha Levin break; 152376ac44cSSasha Levin } 153376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_SEL: 15480ac1d05SSasha Levin rdev->queue_selector = ioport__read16(data); 155376ac44cSSasha Levin break; 156376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_NOTIFY: { 1573fdf659dSSasha Levin u16 queue_index; 158376ac44cSSasha Levin queue_index = ioport__read16(data); 159*df0c7f57SSasha Levin thread_pool__do_job(&rdev->jobs[queue_index].job_id); 160376ac44cSSasha Levin break; 161376ac44cSSasha Levin } 162376ac44cSSasha Levin case VIRTIO_PCI_STATUS: 16380ac1d05SSasha Levin rdev->status = ioport__read8(data); 164376ac44cSSasha Levin break; 165376ac44cSSasha Levin case VIRTIO_MSI_CONFIG_VECTOR: 16680ac1d05SSasha Levin rdev->config_vector = VIRTIO_MSI_NO_VECTOR; 167376ac44cSSasha Levin break; 168376ac44cSSasha Levin default: 169376ac44cSSasha Levin ret = false; 170407475bfSPekka Enberg break; 171376ac44cSSasha Levin }; 172376ac44cSSasha Levin 173376ac44cSSasha Levin return ret; 174376ac44cSSasha Levin } 175376ac44cSSasha Levin 176376ac44cSSasha Levin static struct ioport_operations virtio_rng_io_ops = { 177376ac44cSSasha Levin .io_in = virtio_rng_pci_io_in, 178376ac44cSSasha Levin .io_out = virtio_rng_pci_io_out, 179376ac44cSSasha Levin }; 180376ac44cSSasha Levin 181b2533581SSasha Levin static void ioevent_callback(struct kvm *kvm, void *param) 182b2533581SSasha Levin { 183b2533581SSasha Levin struct rng_dev_job *job = param; 184b2533581SSasha Levin 185*df0c7f57SSasha Levin thread_pool__do_job(&job->job_id); 186b2533581SSasha Levin } 187b2533581SSasha Levin 188376ac44cSSasha Levin void virtio_rng__init(struct kvm *kvm) 189376ac44cSSasha Levin { 190b2533581SSasha Levin u8 pin, line, dev, i; 19180ac1d05SSasha Levin u16 rdev_base_addr; 19280ac1d05SSasha Levin struct rng_dev *rdev; 193b2533581SSasha Levin struct ioevent ioevent; 1942449f6e3SSasha Levin 19580ac1d05SSasha Levin rdev = malloc(sizeof(*rdev)); 19680ac1d05SSasha Levin if (rdev == NULL) 19780ac1d05SSasha Levin return; 19880ac1d05SSasha Levin 19980ac1d05SSasha Levin rdev_base_addr = ioport__register(IOPORT_EMPTY, &virtio_rng_io_ops, IOPORT_SIZE, rdev); 20080ac1d05SSasha Levin 20180ac1d05SSasha Levin rdev->pci_hdr = (struct pci_device_header) { 20280ac1d05SSasha Levin .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 20380ac1d05SSasha Levin .device_id = PCI_DEVICE_ID_VIRTIO_RNG, 20480ac1d05SSasha Levin .header_type = PCI_HEADER_TYPE_NORMAL, 20580ac1d05SSasha Levin .revision_id = 0, 20680ac1d05SSasha Levin .class = 0x010000, 20780ac1d05SSasha Levin .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 20880ac1d05SSasha Levin .subsys_id = VIRTIO_ID_RNG, 20980ac1d05SSasha Levin .bar[0] = rdev_base_addr | PCI_BASE_ADDRESS_SPACE_IO, 21080ac1d05SSasha Levin }; 21180ac1d05SSasha Levin 21280ac1d05SSasha Levin rdev->base_addr = rdev_base_addr; 21380ac1d05SSasha Levin rdev->fd = open("/dev/urandom", O_RDONLY); 21480ac1d05SSasha Levin if (rdev->fd < 0) 215376ac44cSSasha Levin die("Failed initializing RNG"); 216376ac44cSSasha Levin 2170a7ab0c6SSasha Levin if (irq__register_device(VIRTIO_ID_RNG, &dev, &pin, &line) < 0) 2182449f6e3SSasha Levin return; 2192449f6e3SSasha Levin 22080ac1d05SSasha Levin rdev->pci_hdr.irq_pin = pin; 22180ac1d05SSasha Levin rdev->pci_hdr.irq_line = line; 22280ac1d05SSasha Levin pci__register(&rdev->pci_hdr, dev); 223376ac44cSSasha Levin 22480ac1d05SSasha Levin list_add_tail(&rdev->list, &rdevs); 225b2533581SSasha Levin 226b2533581SSasha Levin for (i = 0; i < NUM_VIRT_QUEUES; i++) { 227b2533581SSasha Levin ioevent = (struct ioevent) { 228b2533581SSasha Levin .io_addr = rdev_base_addr + VIRTIO_PCI_QUEUE_NOTIFY, 229b2533581SSasha Levin .io_len = sizeof(u16), 230b2533581SSasha Levin .fn = ioevent_callback, 231b2533581SSasha Levin .fn_ptr = &rdev->jobs[i], 232b2533581SSasha Levin .datamatch = i, 233b2533581SSasha Levin .fn_kvm = kvm, 234b2533581SSasha Levin .fd = eventfd(0, 0), 235b2533581SSasha Levin }; 236b2533581SSasha Levin 237b2533581SSasha Levin ioeventfd__add_event(&ioevent); 238b2533581SSasha Levin } 23980ac1d05SSasha Levin } 24080ac1d05SSasha Levin 24180ac1d05SSasha Levin void virtio_rng__delete_all(struct kvm *kvm) 24280ac1d05SSasha Levin { 24380ac1d05SSasha Levin while (!list_empty(&rdevs)) { 24480ac1d05SSasha Levin struct rng_dev *rdev; 24580ac1d05SSasha Levin 24680ac1d05SSasha Levin rdev = list_first_entry(&rdevs, struct rng_dev, list); 24780ac1d05SSasha Levin list_del(&rdev->list); 248b2533581SSasha Levin ioeventfd__del_event(rdev->base_addr + VIRTIO_PCI_QUEUE_NOTIFY, 0); 24980ac1d05SSasha Levin free(rdev); 25080ac1d05SSasha Levin } 251376ac44cSSasha Levin } 252