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" 14*c75b037fSSasha Levin #include "kvm/guest_compat.h" 15376ac44cSSasha Levin 16376ac44cSSasha Levin #include <linux/virtio_ring.h> 17376ac44cSSasha Levin #include <linux/virtio_rng.h> 18376ac44cSSasha Levin 1980ac1d05SSasha Levin #include <linux/list.h> 20376ac44cSSasha Levin #include <fcntl.h> 21376ac44cSSasha Levin #include <sys/types.h> 22376ac44cSSasha Levin #include <sys/stat.h> 23376ac44cSSasha Levin #include <pthread.h> 24376ac44cSSasha Levin 25376ac44cSSasha Levin #define NUM_VIRT_QUEUES 1 26376ac44cSSasha Levin #define VIRTIO_RNG_QUEUE_SIZE 128 27376ac44cSSasha Levin 2880ac1d05SSasha Levin struct rng_dev_job { 2980ac1d05SSasha Levin struct virt_queue *vq; 3080ac1d05SSasha Levin struct rng_dev *rdev; 31df0c7f57SSasha Levin struct thread_pool__job job_id; 322449f6e3SSasha Levin }; 332449f6e3SSasha Levin 3480ffe4d1SSasha Levin struct rng_dev { 3580ac1d05SSasha Levin struct pci_device_header pci_hdr; 3680ac1d05SSasha Levin struct list_head list; 3780ac1d05SSasha Levin 3880ac1d05SSasha Levin u16 base_addr; 393fdf659dSSasha Levin u8 status; 40d2963622SAsias He u8 isr; 413fdf659dSSasha Levin u16 config_vector; 4280ffe4d1SSasha Levin int fd; 43bc485053SSasha Levin u32 vq_vector[NUM_VIRT_QUEUES]; 44bc485053SSasha Levin u32 msix_io_block; 45*c75b037fSSasha Levin int compat_id; 46376ac44cSSasha Levin 47376ac44cSSasha Levin /* virtio queue */ 483fdf659dSSasha Levin u16 queue_selector; 49376ac44cSSasha Levin struct virt_queue vqs[NUM_VIRT_QUEUES]; 5080ac1d05SSasha Levin struct rng_dev_job jobs[NUM_VIRT_QUEUES]; 51376ac44cSSasha Levin }; 52376ac44cSSasha Levin 5380ac1d05SSasha Levin static LIST_HEAD(rdevs); 54376ac44cSSasha Levin 55c9f6a037SXiao Guangrong static bool virtio_rng_pci_io_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size) 56376ac44cSSasha Levin { 57376ac44cSSasha Levin unsigned long offset; 58376ac44cSSasha Levin bool ret = true; 5980ac1d05SSasha Levin struct rng_dev *rdev; 60376ac44cSSasha Levin 6180ac1d05SSasha Levin rdev = ioport->priv; 6280ac1d05SSasha Levin offset = port - rdev->base_addr; 63376ac44cSSasha Levin 64376ac44cSSasha Levin switch (offset) { 65376ac44cSSasha Levin case VIRTIO_PCI_HOST_FEATURES: 66376ac44cSSasha Levin case VIRTIO_PCI_GUEST_FEATURES: 67376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_SEL: 68376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_NOTIFY: 69376ac44cSSasha Levin ret = false; 70376ac44cSSasha Levin break; 71376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_PFN: 7280ac1d05SSasha Levin ioport__write32(data, rdev->vqs[rdev->queue_selector].pfn); 73376ac44cSSasha Levin break; 74376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_NUM: 75376ac44cSSasha Levin ioport__write16(data, VIRTIO_RNG_QUEUE_SIZE); 76376ac44cSSasha Levin break; 77376ac44cSSasha Levin case VIRTIO_PCI_STATUS: 7880ac1d05SSasha Levin ioport__write8(data, rdev->status); 79376ac44cSSasha Levin break; 80376ac44cSSasha Levin case VIRTIO_PCI_ISR: 8180ac1d05SSasha Levin ioport__write8(data, rdev->isr); 8280ac1d05SSasha Levin kvm__irq_line(kvm, rdev->pci_hdr.irq_line, VIRTIO_IRQ_LOW); 8380ac1d05SSasha Levin rdev->isr = VIRTIO_IRQ_LOW; 84376ac44cSSasha Levin break; 85376ac44cSSasha Levin case VIRTIO_MSI_CONFIG_VECTOR: 8680ac1d05SSasha Levin ioport__write16(data, rdev->config_vector); 87376ac44cSSasha Levin break; 88bc485053SSasha Levin case VIRTIO_MSI_QUEUE_VECTOR: 89bc485053SSasha Levin ioport__write16(data, rdev->vq_vector[rdev->queue_selector]); 90bc485053SSasha Levin break; 91376ac44cSSasha Levin default: 92376ac44cSSasha Levin ret = false; 93407475bfSPekka Enberg break; 94376ac44cSSasha Levin }; 95376ac44cSSasha Levin 96376ac44cSSasha Levin return ret; 97376ac44cSSasha Levin } 98376ac44cSSasha Levin 9980ac1d05SSasha Levin static bool virtio_rng_do_io_request(struct kvm *kvm, struct rng_dev *rdev, struct virt_queue *queue) 100376ac44cSSasha Levin { 101376ac44cSSasha Levin struct iovec iov[VIRTIO_RNG_QUEUE_SIZE]; 102376ac44cSSasha Levin unsigned int len = 0; 103407475bfSPekka Enberg u16 out, in, head; 104376ac44cSSasha Levin 10580ffe4d1SSasha Levin head = virt_queue__get_iov(queue, iov, &out, &in, kvm); 10680ac1d05SSasha Levin len = readv(rdev->fd, iov, in); 107407475bfSPekka Enberg 108376ac44cSSasha Levin virt_queue__set_used_elem(queue, head, len); 109376ac44cSSasha Levin 110376ac44cSSasha Levin return true; 111376ac44cSSasha Levin } 112376ac44cSSasha Levin 113376ac44cSSasha Levin static void virtio_rng_do_io(struct kvm *kvm, void *param) 114376ac44cSSasha Levin { 11580ac1d05SSasha Levin struct rng_dev_job *job = param; 11680ac1d05SSasha Levin struct virt_queue *vq = job->vq; 11780ac1d05SSasha Levin struct rng_dev *rdev = job->rdev; 118376ac44cSSasha Levin 119bc485053SSasha Levin while (virt_queue__available(vq)) 12080ac1d05SSasha Levin virtio_rng_do_io_request(kvm, rdev, vq); 121bc485053SSasha Levin 122bc485053SSasha Levin kvm__irq_line(kvm, rdev->pci_hdr.irq_line, VIRTIO_IRQ_HIGH); 123376ac44cSSasha Levin } 124376ac44cSSasha Levin 125c9f6a037SXiao Guangrong static bool virtio_rng_pci_io_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size) 126376ac44cSSasha Levin { 127376ac44cSSasha Levin unsigned long offset; 128376ac44cSSasha Levin bool ret = true; 12980ac1d05SSasha Levin struct rng_dev *rdev; 130376ac44cSSasha Levin 13180ac1d05SSasha Levin rdev = ioport->priv; 13280ac1d05SSasha Levin offset = port - rdev->base_addr; 133376ac44cSSasha Levin 134376ac44cSSasha Levin switch (offset) { 135376ac44cSSasha Levin case VIRTIO_PCI_GUEST_FEATURES: 136376ac44cSSasha Levin break; 137376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_PFN: { 138376ac44cSSasha Levin struct virt_queue *queue; 13980ac1d05SSasha Levin struct rng_dev_job *job; 140376ac44cSSasha Levin void *p; 141376ac44cSSasha Levin 142*c75b037fSSasha Levin compat__remove_message(rdev->compat_id); 143*c75b037fSSasha Levin 14480ac1d05SSasha Levin queue = &rdev->vqs[rdev->queue_selector]; 145376ac44cSSasha Levin queue->pfn = ioport__read32(data); 146aaf0b445SSasha Levin p = guest_pfn_to_host(kvm, queue->pfn); 147376ac44cSSasha Levin 14880ac1d05SSasha Levin job = &rdev->jobs[rdev->queue_selector]; 14980ac1d05SSasha Levin 150b8f43678SSasha Levin vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN); 151376ac44cSSasha Levin 15280ac1d05SSasha Levin *job = (struct rng_dev_job) { 15380ac1d05SSasha Levin .vq = queue, 15480ac1d05SSasha Levin .rdev = rdev, 15580ac1d05SSasha Levin }; 15680ac1d05SSasha Levin 157df0c7f57SSasha Levin thread_pool__init_job(&job->job_id, kvm, virtio_rng_do_io, job); 158376ac44cSSasha Levin 159376ac44cSSasha Levin break; 160376ac44cSSasha Levin } 161376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_SEL: 16280ac1d05SSasha Levin rdev->queue_selector = ioport__read16(data); 163376ac44cSSasha Levin break; 164376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_NOTIFY: { 1653fdf659dSSasha Levin u16 queue_index; 166376ac44cSSasha Levin queue_index = ioport__read16(data); 167df0c7f57SSasha Levin thread_pool__do_job(&rdev->jobs[queue_index].job_id); 168376ac44cSSasha Levin break; 169376ac44cSSasha Levin } 170376ac44cSSasha Levin case VIRTIO_PCI_STATUS: 17180ac1d05SSasha Levin rdev->status = ioport__read8(data); 172376ac44cSSasha Levin break; 173376ac44cSSasha Levin case VIRTIO_MSI_CONFIG_VECTOR: 174bc485053SSasha Levin rdev->config_vector = ioport__read16(data); 175376ac44cSSasha Levin break; 176bc485053SSasha Levin case VIRTIO_MSI_QUEUE_VECTOR: { 177bc485053SSasha Levin u32 gsi; 178bc485053SSasha Levin u32 vec; 179bc485053SSasha Levin 180bc485053SSasha Levin vec = rdev->vq_vector[rdev->queue_selector] = ioport__read16(data); 181bc485053SSasha Levin 182bc485053SSasha Levin gsi = irq__add_msix_route(kvm, 183bc485053SSasha Levin rdev->pci_hdr.msix.table[vec].low, 184bc485053SSasha Levin rdev->pci_hdr.msix.table[vec].high, 185bc485053SSasha Levin rdev->pci_hdr.msix.table[vec].data); 186bc485053SSasha Levin rdev->pci_hdr.irq_line = gsi; 187bc485053SSasha Levin break; 188bc485053SSasha Levin } 189376ac44cSSasha Levin default: 190376ac44cSSasha Levin ret = false; 191407475bfSPekka Enberg break; 192376ac44cSSasha Levin }; 193376ac44cSSasha Levin 194376ac44cSSasha Levin return ret; 195376ac44cSSasha Levin } 196376ac44cSSasha Levin 197376ac44cSSasha Levin static struct ioport_operations virtio_rng_io_ops = { 198376ac44cSSasha Levin .io_in = virtio_rng_pci_io_in, 199376ac44cSSasha Levin .io_out = virtio_rng_pci_io_out, 200376ac44cSSasha Levin }; 201376ac44cSSasha Levin 202b2533581SSasha Levin static void ioevent_callback(struct kvm *kvm, void *param) 203b2533581SSasha Levin { 204b2533581SSasha Levin struct rng_dev_job *job = param; 205b2533581SSasha Levin 206df0c7f57SSasha Levin thread_pool__do_job(&job->job_id); 207b2533581SSasha Levin } 208b2533581SSasha Levin 209bc485053SSasha Levin static void callback_mmio(u64 addr, u8 *data, u32 len, u8 is_write, void *ptr) 210bc485053SSasha Levin { 211bc485053SSasha Levin struct rng_dev *rdev = ptr; 212bc485053SSasha Levin void *table = &rdev->pci_hdr.msix.table; 213bc485053SSasha Levin if (is_write) 214bc485053SSasha Levin memcpy(table + addr - rdev->msix_io_block, data, len); 215bc485053SSasha Levin else 216bc485053SSasha Levin memcpy(data, table + addr - rdev->msix_io_block, len); 217bc485053SSasha Levin } 218bc485053SSasha Levin 219376ac44cSSasha Levin void virtio_rng__init(struct kvm *kvm) 220376ac44cSSasha Levin { 221b2533581SSasha Levin u8 pin, line, dev, i; 22280ac1d05SSasha Levin u16 rdev_base_addr; 22380ac1d05SSasha Levin struct rng_dev *rdev; 224b2533581SSasha Levin struct ioevent ioevent; 2252449f6e3SSasha Levin 22680ac1d05SSasha Levin rdev = malloc(sizeof(*rdev)); 22780ac1d05SSasha Levin if (rdev == NULL) 22880ac1d05SSasha Levin return; 22980ac1d05SSasha Levin 230bc485053SSasha Levin rdev->msix_io_block = pci_get_io_space_block(); 231bc485053SSasha Levin 23280ac1d05SSasha Levin rdev_base_addr = ioport__register(IOPORT_EMPTY, &virtio_rng_io_ops, IOPORT_SIZE, rdev); 233bc485053SSasha Levin kvm__register_mmio(kvm, rdev->msix_io_block, 0x100, callback_mmio, rdev); 23480ac1d05SSasha Levin 23580ac1d05SSasha Levin rdev->pci_hdr = (struct pci_device_header) { 23680ac1d05SSasha Levin .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 23780ac1d05SSasha Levin .device_id = PCI_DEVICE_ID_VIRTIO_RNG, 23880ac1d05SSasha Levin .header_type = PCI_HEADER_TYPE_NORMAL, 23980ac1d05SSasha Levin .revision_id = 0, 24080ac1d05SSasha Levin .class = 0x010000, 24180ac1d05SSasha Levin .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 24280ac1d05SSasha Levin .subsys_id = VIRTIO_ID_RNG, 24380ac1d05SSasha Levin .bar[0] = rdev_base_addr | PCI_BASE_ADDRESS_SPACE_IO, 244bc485053SSasha Levin .bar[1] = rdev->msix_io_block | 245bc485053SSasha Levin PCI_BASE_ADDRESS_SPACE_MEMORY | 246bc485053SSasha Levin PCI_BASE_ADDRESS_MEM_TYPE_64, 247bc485053SSasha Levin /* bar[2] is the continuation of bar[1] for 64bit addressing */ 248bc485053SSasha Levin .bar[2] = 0, 249bc485053SSasha Levin .status = PCI_STATUS_CAP_LIST, 250bc485053SSasha Levin .capabilities = (void *)&rdev->pci_hdr.msix - (void *)&rdev->pci_hdr, 25180ac1d05SSasha Levin }; 25280ac1d05SSasha Levin 253bc485053SSasha Levin rdev->pci_hdr.msix.cap = PCI_CAP_ID_MSIX; 254bc485053SSasha Levin rdev->pci_hdr.msix.next = 0; 255bc485053SSasha Levin rdev->pci_hdr.msix.table_size = (NUM_VIRT_QUEUES + 1) | PCI_MSIX_FLAGS_ENABLE; 256bc485053SSasha Levin rdev->pci_hdr.msix.table_offset = 1; /* Use BAR 1 */ 257bc485053SSasha Levin 258bc485053SSasha Levin rdev->config_vector = 0; 25980ac1d05SSasha Levin rdev->base_addr = rdev_base_addr; 26080ac1d05SSasha Levin rdev->fd = open("/dev/urandom", O_RDONLY); 26180ac1d05SSasha Levin if (rdev->fd < 0) 262376ac44cSSasha Levin die("Failed initializing RNG"); 263376ac44cSSasha Levin 2640a7ab0c6SSasha Levin if (irq__register_device(VIRTIO_ID_RNG, &dev, &pin, &line) < 0) 2652449f6e3SSasha Levin return; 2662449f6e3SSasha Levin 26780ac1d05SSasha Levin rdev->pci_hdr.irq_pin = pin; 26880ac1d05SSasha Levin rdev->pci_hdr.irq_line = line; 26980ac1d05SSasha Levin pci__register(&rdev->pci_hdr, dev); 270376ac44cSSasha Levin 27180ac1d05SSasha Levin list_add_tail(&rdev->list, &rdevs); 272b2533581SSasha Levin 273b2533581SSasha Levin for (i = 0; i < NUM_VIRT_QUEUES; i++) { 274b2533581SSasha Levin ioevent = (struct ioevent) { 275b2533581SSasha Levin .io_addr = rdev_base_addr + VIRTIO_PCI_QUEUE_NOTIFY, 276b2533581SSasha Levin .io_len = sizeof(u16), 277b2533581SSasha Levin .fn = ioevent_callback, 278b2533581SSasha Levin .fn_ptr = &rdev->jobs[i], 279b2533581SSasha Levin .datamatch = i, 280b2533581SSasha Levin .fn_kvm = kvm, 281b2533581SSasha Levin .fd = eventfd(0, 0), 282b2533581SSasha Levin }; 283b2533581SSasha Levin 284b2533581SSasha Levin ioeventfd__add_event(&ioevent); 285b2533581SSasha Levin } 286*c75b037fSSasha Levin 287*c75b037fSSasha Levin rdev->compat_id = compat__add_message("virtio-rng device was not detected", 288*c75b037fSSasha Levin "While you have requested a virtio-rng device, " 289*c75b037fSSasha Levin "the guest kernel didn't seem to detect it.\n" 290*c75b037fSSasha Levin "Please make sure that the kernel was compiled" 291*c75b037fSSasha Levin "with CONFIG_HW_RANDOM_VIRTIO."); 29280ac1d05SSasha Levin } 29380ac1d05SSasha Levin 29480ac1d05SSasha Levin void virtio_rng__delete_all(struct kvm *kvm) 29580ac1d05SSasha Levin { 29680ac1d05SSasha Levin while (!list_empty(&rdevs)) { 29780ac1d05SSasha Levin struct rng_dev *rdev; 29880ac1d05SSasha Levin 29980ac1d05SSasha Levin rdev = list_first_entry(&rdevs, struct rng_dev, list); 30080ac1d05SSasha Levin list_del(&rdev->list); 301b2533581SSasha Levin ioeventfd__del_event(rdev->base_addr + VIRTIO_PCI_QUEUE_NOTIFY, 0); 30280ac1d05SSasha Levin free(rdev); 30380ac1d05SSasha Levin } 304376ac44cSSasha Levin } 305