1376ac44cSSasha Levin #include "kvm/virtio-rng.h" 2376ac44cSSasha Levin 3376ac44cSSasha Levin #include "kvm/virtio-pci.h" 431638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h" 5376ac44cSSasha Levin 6376ac44cSSasha Levin #include "kvm/disk-image.h" 7376ac44cSSasha Levin #include "kvm/virtio.h" 8376ac44cSSasha Levin #include "kvm/ioport.h" 9376ac44cSSasha Levin #include "kvm/mutex.h" 10376ac44cSSasha Levin #include "kvm/util.h" 11376ac44cSSasha Levin #include "kvm/kvm.h" 12376ac44cSSasha Levin #include "kvm/pci.h" 13376ac44cSSasha Levin #include "kvm/threadpool.h" 142449f6e3SSasha Levin #include "kvm/irq.h" 15376ac44cSSasha Levin 16376ac44cSSasha Levin #include <linux/virtio_ring.h> 17376ac44cSSasha Levin #include <linux/virtio_rng.h> 18376ac44cSSasha Levin 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 272449f6e3SSasha Levin static struct pci_device_header virtio_rng_pci_device = { 282449f6e3SSasha Levin .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 292449f6e3SSasha Levin .device_id = PCI_DEVICE_ID_VIRTIO_RNG, 302449f6e3SSasha Levin .header_type = PCI_HEADER_TYPE_NORMAL, 312449f6e3SSasha Levin .revision_id = 0, 322449f6e3SSasha Levin .class = 0x010000, 332449f6e3SSasha Levin .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 342449f6e3SSasha Levin .subsys_id = PCI_SUBSYSTEM_ID_VIRTIO_RNG, 352449f6e3SSasha Levin .bar[0] = IOPORT_VIRTIO_RNG | PCI_BASE_ADDRESS_SPACE_IO, 362449f6e3SSasha Levin }; 372449f6e3SSasha Levin 3880ffe4d1SSasha Levin struct rng_dev { 393fdf659dSSasha Levin u8 status; 40d2963622SAsias He u8 isr; 413fdf659dSSasha Levin u16 config_vector; 4280ffe4d1SSasha Levin int fd; 43376ac44cSSasha Levin 44376ac44cSSasha Levin /* virtio queue */ 453fdf659dSSasha Levin u16 queue_selector; 46376ac44cSSasha Levin struct virt_queue vqs[NUM_VIRT_QUEUES]; 47376ac44cSSasha Levin void *jobs[NUM_VIRT_QUEUES]; 48376ac44cSSasha Levin }; 49376ac44cSSasha Levin 5080ffe4d1SSasha Levin static struct rng_dev rdev; 51376ac44cSSasha Levin 523fdf659dSSasha Levin static bool virtio_rng_pci_io_in(struct kvm *kvm, u16 port, void *data, int size, u32 count) 53376ac44cSSasha Levin { 54376ac44cSSasha Levin unsigned long offset; 55376ac44cSSasha Levin bool ret = true; 56376ac44cSSasha Levin 57376ac44cSSasha Levin offset = port - IOPORT_VIRTIO_RNG; 58376ac44cSSasha Levin 59376ac44cSSasha Levin switch (offset) { 60376ac44cSSasha Levin case VIRTIO_PCI_HOST_FEATURES: 61376ac44cSSasha Levin case VIRTIO_PCI_GUEST_FEATURES: 62376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_SEL: 63376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_NOTIFY: 64376ac44cSSasha Levin ret = false; 65376ac44cSSasha Levin break; 66376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_PFN: 6780ffe4d1SSasha Levin ioport__write32(data, rdev.vqs[rdev.queue_selector].pfn); 68376ac44cSSasha Levin break; 69376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_NUM: 70376ac44cSSasha Levin ioport__write16(data, VIRTIO_RNG_QUEUE_SIZE); 71376ac44cSSasha Levin break; 72376ac44cSSasha Levin case VIRTIO_PCI_STATUS: 7380ffe4d1SSasha Levin ioport__write8(data, rdev.status); 74376ac44cSSasha Levin break; 75376ac44cSSasha Levin case VIRTIO_PCI_ISR: 76d2963622SAsias He ioport__write8(data, rdev.isr); 77d2963622SAsias He kvm__irq_line(kvm, virtio_rng_pci_device.irq_line, VIRTIO_IRQ_LOW); 78d2963622SAsias He rdev.isr = VIRTIO_IRQ_LOW; 79376ac44cSSasha Levin break; 80376ac44cSSasha Levin case VIRTIO_MSI_CONFIG_VECTOR: 8180ffe4d1SSasha Levin ioport__write16(data, rdev.config_vector); 82376ac44cSSasha Levin break; 83376ac44cSSasha Levin default: 84376ac44cSSasha Levin ret = false; 85407475bfSPekka Enberg break; 86376ac44cSSasha Levin }; 87376ac44cSSasha Levin 88376ac44cSSasha Levin return ret; 89376ac44cSSasha Levin } 90376ac44cSSasha Levin 9180ffe4d1SSasha Levin static bool virtio_rng_do_io_request(struct kvm *kvm, struct virt_queue *queue) 92376ac44cSSasha Levin { 93376ac44cSSasha Levin struct iovec iov[VIRTIO_RNG_QUEUE_SIZE]; 94376ac44cSSasha Levin unsigned int len = 0; 95407475bfSPekka Enberg u16 out, in, head; 96376ac44cSSasha Levin 9780ffe4d1SSasha Levin head = virt_queue__get_iov(queue, iov, &out, &in, kvm); 9880ffe4d1SSasha Levin len = readv(rdev.fd, iov, in); 99407475bfSPekka Enberg 100376ac44cSSasha Levin virt_queue__set_used_elem(queue, head, len); 101376ac44cSSasha Levin 102376ac44cSSasha Levin return true; 103376ac44cSSasha Levin } 104376ac44cSSasha Levin 105376ac44cSSasha Levin static void virtio_rng_do_io(struct kvm *kvm, void *param) 106376ac44cSSasha Levin { 107376ac44cSSasha Levin struct virt_queue *vq = param; 108376ac44cSSasha Levin 109376ac44cSSasha Levin while (virt_queue__available(vq)) { 110376ac44cSSasha Levin virtio_rng_do_io_request(kvm, vq); 111d2963622SAsias He virt_queue__trigger_irq(vq, virtio_rng_pci_device.irq_line, &rdev.isr, kvm); 112376ac44cSSasha Levin } 113376ac44cSSasha Levin } 114376ac44cSSasha Levin 1153fdf659dSSasha Levin static bool virtio_rng_pci_io_out(struct kvm *kvm, u16 port, void *data, int size, u32 count) 116376ac44cSSasha Levin { 117376ac44cSSasha Levin unsigned long offset; 118376ac44cSSasha Levin bool ret = true; 119376ac44cSSasha Levin 120376ac44cSSasha Levin offset = port - IOPORT_VIRTIO_RNG; 121376ac44cSSasha Levin 122376ac44cSSasha Levin switch (offset) { 123376ac44cSSasha Levin case VIRTIO_MSI_QUEUE_VECTOR: 124376ac44cSSasha Levin case VIRTIO_PCI_GUEST_FEATURES: 125376ac44cSSasha Levin break; 126376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_PFN: { 127376ac44cSSasha Levin struct virt_queue *queue; 128376ac44cSSasha Levin void *p; 129376ac44cSSasha Levin 13080ffe4d1SSasha Levin queue = &rdev.vqs[rdev.queue_selector]; 131376ac44cSSasha Levin queue->pfn = ioport__read32(data); 132*aaf0b445SSasha Levin p = guest_pfn_to_host(kvm, queue->pfn); 133376ac44cSSasha Levin 134376ac44cSSasha Levin vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, 4096); 135376ac44cSSasha Levin 136407475bfSPekka Enberg rdev.jobs[rdev.queue_selector] = thread_pool__add_job(kvm, virtio_rng_do_io, queue); 137376ac44cSSasha Levin 138376ac44cSSasha Levin break; 139376ac44cSSasha Levin } 140376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_SEL: 14180ffe4d1SSasha Levin rdev.queue_selector = ioport__read16(data); 142376ac44cSSasha Levin break; 143376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_NOTIFY: { 1443fdf659dSSasha Levin u16 queue_index; 145376ac44cSSasha Levin queue_index = ioport__read16(data); 14680ffe4d1SSasha Levin thread_pool__do_job(rdev.jobs[queue_index]); 147376ac44cSSasha Levin break; 148376ac44cSSasha Levin } 149376ac44cSSasha Levin case VIRTIO_PCI_STATUS: 15080ffe4d1SSasha Levin rdev.status = ioport__read8(data); 151376ac44cSSasha Levin break; 152376ac44cSSasha Levin case VIRTIO_MSI_CONFIG_VECTOR: 15380ffe4d1SSasha Levin rdev.config_vector = VIRTIO_MSI_NO_VECTOR; 154376ac44cSSasha Levin break; 155376ac44cSSasha Levin default: 156376ac44cSSasha Levin ret = false; 157407475bfSPekka Enberg break; 158376ac44cSSasha Levin }; 159376ac44cSSasha Levin 160376ac44cSSasha Levin return ret; 161376ac44cSSasha Levin } 162376ac44cSSasha Levin 163376ac44cSSasha Levin static struct ioport_operations virtio_rng_io_ops = { 164376ac44cSSasha Levin .io_in = virtio_rng_pci_io_in, 165376ac44cSSasha Levin .io_out = virtio_rng_pci_io_out, 166376ac44cSSasha Levin }; 167376ac44cSSasha Levin 168376ac44cSSasha Levin void virtio_rng__init(struct kvm *kvm) 169376ac44cSSasha Levin { 1702449f6e3SSasha Levin u8 pin, line, dev; 1712449f6e3SSasha Levin 17280ffe4d1SSasha Levin rdev.fd = open("/dev/urandom", O_RDONLY); 17380ffe4d1SSasha Levin if (rdev.fd < 0) 174376ac44cSSasha Levin die("Failed initializing RNG"); 175376ac44cSSasha Levin 1762449f6e3SSasha Levin if (irq__register_device(PCI_DEVICE_ID_VIRTIO_RNG, &dev, &pin, &line) < 0) 1772449f6e3SSasha Levin return; 1782449f6e3SSasha Levin 1792449f6e3SSasha Levin virtio_rng_pci_device.irq_pin = pin; 1802449f6e3SSasha Levin virtio_rng_pci_device.irq_line = line; 1812449f6e3SSasha Levin pci__register(&virtio_rng_pci_device, dev); 182376ac44cSSasha Levin 183376ac44cSSasha Levin ioport__register(IOPORT_VIRTIO_RNG, &virtio_rng_io_ops, IOPORT_VIRTIO_RNG_SIZE); 184376ac44cSSasha Levin } 185