1376ac44cSSasha Levin #include "kvm/virtio-rng.h" 2376ac44cSSasha Levin 3376ac44cSSasha Levin #include "kvm/virtio-pci.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/mutex.h" 9376ac44cSSasha Levin #include "kvm/util.h" 10376ac44cSSasha Levin #include "kvm/kvm.h" 11376ac44cSSasha Levin #include "kvm/pci.h" 12376ac44cSSasha Levin #include "kvm/threadpool.h" 13376ac44cSSasha Levin 14376ac44cSSasha Levin #include <linux/virtio_ring.h> 15376ac44cSSasha Levin #include <linux/virtio_rng.h> 16376ac44cSSasha Levin 17376ac44cSSasha Levin #include <fcntl.h> 18376ac44cSSasha Levin #include <sys/types.h> 19376ac44cSSasha Levin #include <sys/stat.h> 20376ac44cSSasha Levin #include <pthread.h> 21376ac44cSSasha Levin 22376ac44cSSasha Levin #define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 23376ac44cSSasha Levin #define PCI_DEVICE_ID_VIRTIO_RNG 0x1004 24376ac44cSSasha Levin #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET 0x1af4 25376ac44cSSasha Levin #define PCI_SUBSYSTEM_ID_VIRTIO_RNG 0x0004 26376ac44cSSasha Levin #define PCI_VIRTIO_RNG_DEVNUM 4 27376ac44cSSasha Levin 28376ac44cSSasha Levin #define VIRTIO_RNG_IRQ 11 29376ac44cSSasha Levin #define VIRTIO_RNG_PIN 1 30376ac44cSSasha Levin 31376ac44cSSasha Levin #define NUM_VIRT_QUEUES 1 32376ac44cSSasha Levin 33376ac44cSSasha Levin #define VIRTIO_RNG_QUEUE_SIZE 128 34376ac44cSSasha Levin 35376ac44cSSasha Levin struct rng_device { 36*3fdf659dSSasha Levin u8 status; 37*3fdf659dSSasha Levin u16 config_vector; 38376ac44cSSasha Levin int fd_rng; 39376ac44cSSasha Levin 40376ac44cSSasha Levin /* virtio queue */ 41*3fdf659dSSasha Levin u16 queue_selector; 42376ac44cSSasha Levin struct virt_queue vqs[NUM_VIRT_QUEUES]; 43376ac44cSSasha Levin void *jobs[NUM_VIRT_QUEUES]; 44376ac44cSSasha Levin }; 45376ac44cSSasha Levin 46376ac44cSSasha Levin static struct rng_device rng_device; 47376ac44cSSasha Levin 48*3fdf659dSSasha Levin static bool virtio_rng_pci_io_in(struct kvm *kvm, u16 port, void *data, int size, u32 count) 49376ac44cSSasha Levin { 50376ac44cSSasha Levin unsigned long offset; 51376ac44cSSasha Levin bool ret = true; 52376ac44cSSasha Levin 53376ac44cSSasha Levin offset = port - IOPORT_VIRTIO_RNG; 54376ac44cSSasha Levin 55376ac44cSSasha Levin switch (offset) { 56376ac44cSSasha Levin case VIRTIO_PCI_HOST_FEATURES: 57376ac44cSSasha Levin case VIRTIO_PCI_GUEST_FEATURES: 58376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_SEL: 59376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_NOTIFY: 60376ac44cSSasha Levin ret = false; 61376ac44cSSasha Levin break; 62376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_PFN: 63376ac44cSSasha Levin ioport__write32(data, rng_device.vqs[rng_device.queue_selector].pfn); 64376ac44cSSasha Levin break; 65376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_NUM: 66376ac44cSSasha Levin ioport__write16(data, VIRTIO_RNG_QUEUE_SIZE); 67376ac44cSSasha Levin break; 68376ac44cSSasha Levin case VIRTIO_PCI_STATUS: 69376ac44cSSasha Levin ioport__write8(data, rng_device.status); 70376ac44cSSasha Levin break; 71376ac44cSSasha Levin case VIRTIO_PCI_ISR: 72376ac44cSSasha Levin ioport__write8(data, 0x1); 73376ac44cSSasha Levin kvm__irq_line(kvm, VIRTIO_RNG_IRQ, 0); 74376ac44cSSasha Levin break; 75376ac44cSSasha Levin case VIRTIO_MSI_CONFIG_VECTOR: 76376ac44cSSasha Levin ioport__write16(data, rng_device.config_vector); 77376ac44cSSasha Levin break; 78376ac44cSSasha Levin default: 79376ac44cSSasha Levin ret = false; 80376ac44cSSasha Levin }; 81376ac44cSSasha Levin 82376ac44cSSasha Levin return ret; 83376ac44cSSasha Levin } 84376ac44cSSasha Levin 85376ac44cSSasha Levin static bool virtio_rng_do_io_request(struct kvm *self, struct virt_queue *queue) 86376ac44cSSasha Levin { 87376ac44cSSasha Levin struct iovec iov[VIRTIO_RNG_QUEUE_SIZE]; 88*3fdf659dSSasha Levin u16 out, in, head; 89376ac44cSSasha Levin unsigned int len = 0; 90376ac44cSSasha Levin 91376ac44cSSasha Levin head = virt_queue__get_iov(queue, iov, &out, &in, self); 92376ac44cSSasha Levin len = readv(rng_device.fd_rng, iov, in); 93376ac44cSSasha Levin virt_queue__set_used_elem(queue, head, len); 94376ac44cSSasha Levin 95376ac44cSSasha Levin return true; 96376ac44cSSasha Levin } 97376ac44cSSasha Levin 98376ac44cSSasha Levin static void virtio_rng_do_io(struct kvm *kvm, void *param) 99376ac44cSSasha Levin { 100376ac44cSSasha Levin struct virt_queue *vq = param; 101376ac44cSSasha Levin 102376ac44cSSasha Levin while (virt_queue__available(vq)) { 103376ac44cSSasha Levin virtio_rng_do_io_request(kvm, vq); 104376ac44cSSasha Levin kvm__irq_line(kvm, VIRTIO_RNG_IRQ, 1); 105376ac44cSSasha Levin } 106376ac44cSSasha Levin } 107376ac44cSSasha Levin 108*3fdf659dSSasha Levin static bool virtio_rng_pci_io_out(struct kvm *kvm, u16 port, void *data, int size, u32 count) 109376ac44cSSasha Levin { 110376ac44cSSasha Levin unsigned long offset; 111376ac44cSSasha Levin bool ret = true; 112376ac44cSSasha Levin 113376ac44cSSasha Levin offset = port - IOPORT_VIRTIO_RNG; 114376ac44cSSasha Levin 115376ac44cSSasha Levin switch (offset) { 116376ac44cSSasha Levin case VIRTIO_MSI_QUEUE_VECTOR: 117376ac44cSSasha Levin case VIRTIO_PCI_GUEST_FEATURES: 118376ac44cSSasha Levin break; 119376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_PFN: { 120376ac44cSSasha Levin struct virt_queue *queue; 121376ac44cSSasha Levin void *p; 122376ac44cSSasha Levin 123376ac44cSSasha Levin queue = &rng_device.vqs[rng_device.queue_selector]; 124376ac44cSSasha Levin queue->pfn = ioport__read32(data); 125376ac44cSSasha Levin p = guest_flat_to_host(kvm, queue->pfn << 12); 126376ac44cSSasha Levin 127376ac44cSSasha Levin vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, 4096); 128376ac44cSSasha Levin 129376ac44cSSasha Levin rng_device.jobs[rng_device.queue_selector] = 130c9ad1e3cSPekka Enberg thread_pool__add_job(kvm, virtio_rng_do_io, queue); 131376ac44cSSasha Levin 132376ac44cSSasha Levin break; 133376ac44cSSasha Levin } 134376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_SEL: 135376ac44cSSasha Levin rng_device.queue_selector = ioport__read16(data); 136376ac44cSSasha Levin break; 137376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_NOTIFY: { 138*3fdf659dSSasha Levin u16 queue_index; 139376ac44cSSasha Levin queue_index = ioport__read16(data); 140c9ad1e3cSPekka Enberg thread_pool__do_job(rng_device.jobs[queue_index]); 141376ac44cSSasha Levin break; 142376ac44cSSasha Levin } 143376ac44cSSasha Levin case VIRTIO_PCI_STATUS: 144376ac44cSSasha Levin rng_device.status = ioport__read8(data); 145376ac44cSSasha Levin break; 146376ac44cSSasha Levin case VIRTIO_MSI_CONFIG_VECTOR: 147376ac44cSSasha Levin rng_device.config_vector = VIRTIO_MSI_NO_VECTOR; 148376ac44cSSasha Levin break; 149376ac44cSSasha Levin default: 150376ac44cSSasha Levin ret = false; 151376ac44cSSasha Levin }; 152376ac44cSSasha Levin 153376ac44cSSasha Levin return ret; 154376ac44cSSasha Levin } 155376ac44cSSasha Levin 156376ac44cSSasha Levin static struct ioport_operations virtio_rng_io_ops = { 157376ac44cSSasha Levin .io_in = virtio_rng_pci_io_in, 158376ac44cSSasha Levin .io_out = virtio_rng_pci_io_out, 159376ac44cSSasha Levin }; 160376ac44cSSasha Levin 161376ac44cSSasha Levin static struct pci_device_header virtio_rng_pci_device = { 162376ac44cSSasha Levin .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 163376ac44cSSasha Levin .device_id = PCI_DEVICE_ID_VIRTIO_RNG, 164376ac44cSSasha Levin .header_type = PCI_HEADER_TYPE_NORMAL, 165376ac44cSSasha Levin .revision_id = 0, 166376ac44cSSasha Levin .class = 0x010000, 167376ac44cSSasha Levin .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 168376ac44cSSasha Levin .subsys_id = PCI_SUBSYSTEM_ID_VIRTIO_RNG, 169376ac44cSSasha Levin .bar[0] = IOPORT_VIRTIO_RNG | PCI_BASE_ADDRESS_SPACE_IO, 170376ac44cSSasha Levin .irq_pin = VIRTIO_RNG_PIN, 171376ac44cSSasha Levin .irq_line = VIRTIO_RNG_IRQ, 172376ac44cSSasha Levin }; 173376ac44cSSasha Levin 174376ac44cSSasha Levin void virtio_rng__init(struct kvm *kvm) 175376ac44cSSasha Levin { 176376ac44cSSasha Levin rng_device.fd_rng = open("/dev/urandom", O_RDONLY); 177376ac44cSSasha Levin if (rng_device.fd_rng < 0) 178376ac44cSSasha Levin die("Failed initializing RNG"); 179376ac44cSSasha Levin 180376ac44cSSasha Levin pci__register(&virtio_rng_pci_device, PCI_VIRTIO_RNG_DEVNUM); 181376ac44cSSasha Levin 182376ac44cSSasha Levin ioport__register(IOPORT_VIRTIO_RNG, &virtio_rng_io_ops, IOPORT_VIRTIO_RNG_SIZE); 183376ac44cSSasha Levin } 184