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" 13376ac44cSSasha Levin 14376ac44cSSasha Levin #include <linux/virtio_ring.h> 15376ac44cSSasha Levin #include <linux/virtio_rng.h> 16376ac44cSSasha Levin 17*80ac1d05SSasha Levin #include <linux/list.h> 18376ac44cSSasha Levin #include <fcntl.h> 19376ac44cSSasha Levin #include <sys/types.h> 20376ac44cSSasha Levin #include <sys/stat.h> 21376ac44cSSasha Levin #include <pthread.h> 22376ac44cSSasha Levin 23376ac44cSSasha Levin #define NUM_VIRT_QUEUES 1 24376ac44cSSasha Levin #define VIRTIO_RNG_QUEUE_SIZE 128 25376ac44cSSasha Levin 26*80ac1d05SSasha Levin struct rng_dev_job { 27*80ac1d05SSasha Levin struct virt_queue *vq; 28*80ac1d05SSasha Levin struct rng_dev *rdev; 29*80ac1d05SSasha Levin void *job_id; 302449f6e3SSasha Levin }; 312449f6e3SSasha Levin 3280ffe4d1SSasha Levin struct rng_dev { 33*80ac1d05SSasha Levin struct pci_device_header pci_hdr; 34*80ac1d05SSasha Levin struct list_head list; 35*80ac1d05SSasha Levin 36*80ac1d05SSasha Levin u16 base_addr; 373fdf659dSSasha Levin u8 status; 38d2963622SAsias He u8 isr; 393fdf659dSSasha Levin u16 config_vector; 4080ffe4d1SSasha Levin int fd; 41376ac44cSSasha Levin 42376ac44cSSasha Levin /* virtio queue */ 433fdf659dSSasha Levin u16 queue_selector; 44376ac44cSSasha Levin struct virt_queue vqs[NUM_VIRT_QUEUES]; 45*80ac1d05SSasha Levin struct rng_dev_job jobs[NUM_VIRT_QUEUES]; 46376ac44cSSasha Levin }; 47376ac44cSSasha Levin 48*80ac1d05SSasha Levin static LIST_HEAD(rdevs); 49376ac44cSSasha Levin 503d62dea6SSasha Levin static bool virtio_rng_pci_io_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count) 51376ac44cSSasha Levin { 52376ac44cSSasha Levin unsigned long offset; 53376ac44cSSasha Levin bool ret = true; 54*80ac1d05SSasha Levin struct rng_dev *rdev; 55376ac44cSSasha Levin 56*80ac1d05SSasha Levin rdev = ioport->priv; 57*80ac1d05SSasha Levin offset = port - rdev->base_addr; 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: 67*80ac1d05SSasha 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: 73*80ac1d05SSasha Levin ioport__write8(data, rdev->status); 74376ac44cSSasha Levin break; 75376ac44cSSasha Levin case VIRTIO_PCI_ISR: 76*80ac1d05SSasha Levin ioport__write8(data, rdev->isr); 77*80ac1d05SSasha Levin kvm__irq_line(kvm, rdev->pci_hdr.irq_line, VIRTIO_IRQ_LOW); 78*80ac1d05SSasha Levin rdev->isr = VIRTIO_IRQ_LOW; 79376ac44cSSasha Levin break; 80376ac44cSSasha Levin case VIRTIO_MSI_CONFIG_VECTOR: 81*80ac1d05SSasha 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 91*80ac1d05SSasha Levin static bool virtio_rng_do_io_request(struct kvm *kvm, struct rng_dev *rdev, 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); 98*80ac1d05SSasha 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 { 107*80ac1d05SSasha Levin struct rng_dev_job *job = param; 108*80ac1d05SSasha Levin struct virt_queue *vq = job->vq; 109*80ac1d05SSasha Levin struct rng_dev *rdev = job->rdev; 110376ac44cSSasha Levin 111376ac44cSSasha Levin while (virt_queue__available(vq)) { 112*80ac1d05SSasha Levin virtio_rng_do_io_request(kvm, rdev, vq); 113*80ac1d05SSasha Levin virt_queue__trigger_irq(vq, rdev->pci_hdr.irq_line, &rdev->isr, kvm); 114376ac44cSSasha Levin } 115376ac44cSSasha Levin } 116376ac44cSSasha Levin 1173d62dea6SSasha Levin static bool virtio_rng_pci_io_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count) 118376ac44cSSasha Levin { 119376ac44cSSasha Levin unsigned long offset; 120376ac44cSSasha Levin bool ret = true; 121*80ac1d05SSasha Levin struct rng_dev *rdev; 122376ac44cSSasha Levin 123*80ac1d05SSasha Levin rdev = ioport->priv; 124*80ac1d05SSasha Levin offset = port - rdev->base_addr; 125376ac44cSSasha Levin 126376ac44cSSasha Levin switch (offset) { 127376ac44cSSasha Levin case VIRTIO_MSI_QUEUE_VECTOR: 128376ac44cSSasha Levin case VIRTIO_PCI_GUEST_FEATURES: 129376ac44cSSasha Levin break; 130376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_PFN: { 131376ac44cSSasha Levin struct virt_queue *queue; 132*80ac1d05SSasha Levin struct rng_dev_job *job; 133376ac44cSSasha Levin void *p; 134376ac44cSSasha Levin 135*80ac1d05SSasha Levin queue = &rdev->vqs[rdev->queue_selector]; 136376ac44cSSasha Levin queue->pfn = ioport__read32(data); 137aaf0b445SSasha Levin p = guest_pfn_to_host(kvm, queue->pfn); 138376ac44cSSasha Levin 139*80ac1d05SSasha Levin job = &rdev->jobs[rdev->queue_selector]; 140*80ac1d05SSasha Levin 141b8f43678SSasha Levin vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN); 142376ac44cSSasha Levin 143*80ac1d05SSasha Levin *job = (struct rng_dev_job) { 144*80ac1d05SSasha Levin .vq = queue, 145*80ac1d05SSasha Levin .rdev = rdev, 146*80ac1d05SSasha Levin }; 147*80ac1d05SSasha Levin 148*80ac1d05SSasha Levin job->job_id = thread_pool__add_job(kvm, virtio_rng_do_io, job); 149376ac44cSSasha Levin 150376ac44cSSasha Levin break; 151376ac44cSSasha Levin } 152376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_SEL: 153*80ac1d05SSasha Levin rdev->queue_selector = ioport__read16(data); 154376ac44cSSasha Levin break; 155376ac44cSSasha Levin case VIRTIO_PCI_QUEUE_NOTIFY: { 1563fdf659dSSasha Levin u16 queue_index; 157376ac44cSSasha Levin queue_index = ioport__read16(data); 158*80ac1d05SSasha Levin thread_pool__do_job(rdev->jobs[queue_index].job_id); 159376ac44cSSasha Levin break; 160376ac44cSSasha Levin } 161376ac44cSSasha Levin case VIRTIO_PCI_STATUS: 162*80ac1d05SSasha Levin rdev->status = ioport__read8(data); 163376ac44cSSasha Levin break; 164376ac44cSSasha Levin case VIRTIO_MSI_CONFIG_VECTOR: 165*80ac1d05SSasha Levin rdev->config_vector = VIRTIO_MSI_NO_VECTOR; 166376ac44cSSasha Levin break; 167376ac44cSSasha Levin default: 168376ac44cSSasha Levin ret = false; 169407475bfSPekka Enberg break; 170376ac44cSSasha Levin }; 171376ac44cSSasha Levin 172376ac44cSSasha Levin return ret; 173376ac44cSSasha Levin } 174376ac44cSSasha Levin 175376ac44cSSasha Levin static struct ioport_operations virtio_rng_io_ops = { 176376ac44cSSasha Levin .io_in = virtio_rng_pci_io_in, 177376ac44cSSasha Levin .io_out = virtio_rng_pci_io_out, 178376ac44cSSasha Levin }; 179376ac44cSSasha Levin 180376ac44cSSasha Levin void virtio_rng__init(struct kvm *kvm) 181376ac44cSSasha Levin { 1822449f6e3SSasha Levin u8 pin, line, dev; 183*80ac1d05SSasha Levin u16 rdev_base_addr; 184*80ac1d05SSasha Levin struct rng_dev *rdev; 1852449f6e3SSasha Levin 186*80ac1d05SSasha Levin rdev = malloc(sizeof(*rdev)); 187*80ac1d05SSasha Levin if (rdev == NULL) 188*80ac1d05SSasha Levin return; 189*80ac1d05SSasha Levin 190*80ac1d05SSasha Levin rdev_base_addr = ioport__register(IOPORT_EMPTY, &virtio_rng_io_ops, IOPORT_SIZE, rdev); 191*80ac1d05SSasha Levin 192*80ac1d05SSasha Levin rdev->pci_hdr = (struct pci_device_header) { 193*80ac1d05SSasha Levin .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 194*80ac1d05SSasha Levin .device_id = PCI_DEVICE_ID_VIRTIO_RNG, 195*80ac1d05SSasha Levin .header_type = PCI_HEADER_TYPE_NORMAL, 196*80ac1d05SSasha Levin .revision_id = 0, 197*80ac1d05SSasha Levin .class = 0x010000, 198*80ac1d05SSasha Levin .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 199*80ac1d05SSasha Levin .subsys_id = VIRTIO_ID_RNG, 200*80ac1d05SSasha Levin .bar[0] = rdev_base_addr | PCI_BASE_ADDRESS_SPACE_IO, 201*80ac1d05SSasha Levin }; 202*80ac1d05SSasha Levin 203*80ac1d05SSasha Levin rdev->base_addr = rdev_base_addr; 204*80ac1d05SSasha Levin rdev->fd = open("/dev/urandom", O_RDONLY); 205*80ac1d05SSasha Levin if (rdev->fd < 0) 206376ac44cSSasha Levin die("Failed initializing RNG"); 207376ac44cSSasha Levin 2080a7ab0c6SSasha Levin if (irq__register_device(VIRTIO_ID_RNG, &dev, &pin, &line) < 0) 2092449f6e3SSasha Levin return; 2102449f6e3SSasha Levin 211*80ac1d05SSasha Levin rdev->pci_hdr.irq_pin = pin; 212*80ac1d05SSasha Levin rdev->pci_hdr.irq_line = line; 213*80ac1d05SSasha Levin pci__register(&rdev->pci_hdr, dev); 214376ac44cSSasha Levin 215*80ac1d05SSasha Levin list_add_tail(&rdev->list, &rdevs); 216*80ac1d05SSasha Levin } 217*80ac1d05SSasha Levin 218*80ac1d05SSasha Levin void virtio_rng__delete_all(struct kvm *kvm) 219*80ac1d05SSasha Levin { 220*80ac1d05SSasha Levin while (!list_empty(&rdevs)) { 221*80ac1d05SSasha Levin struct rng_dev *rdev; 222*80ac1d05SSasha Levin 223*80ac1d05SSasha Levin rdev = list_first_entry(&rdevs, struct rng_dev, list); 224*80ac1d05SSasha Levin list_del(&rdev->list); 225*80ac1d05SSasha Levin free(rdev); 226*80ac1d05SSasha Levin } 227376ac44cSSasha Levin } 228