From 5577d2e508acc66fdc03fa5bb5098b512c758eaf Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Sat, 30 Apr 2011 16:30:24 +0300 Subject: [PATCH] kvm tools: Introduce virtio-rng Enable virtio-rng, a virtio random number generator. Guest kernel should be compiled with CONFIG_HW_RANDOM_VIRTIO. Once enabled, A RNG device will be located at /dev/hwrng. Signed-off-by: Sasha Levin Signed-off-by: Pekka Enberg --- tools/kvm/Makefile | 1 + tools/kvm/include/kvm/ioport.h | 2 + tools/kvm/include/kvm/virtio-rng.h | 8 ++ tools/kvm/virtio-rng.c | 184 +++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+) create mode 100644 tools/kvm/include/kvm/virtio-rng.h create mode 100644 tools/kvm/virtio-rng.c diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile index 659bc351cabc..19e7b9e6f7c9 100644 --- a/tools/kvm/Makefile +++ b/tools/kvm/Makefile @@ -38,6 +38,7 @@ OBJS += kvm-run.o OBJS += qcow.o OBJS += mptable.o OBJS += threadpool.o +OBJS += virtio-rng.o DEPS := $(patsubst %.o,%.d,$(OBJS)) diff --git a/tools/kvm/include/kvm/ioport.h b/tools/kvm/include/kvm/ioport.h index 2fdcca459fa4..60201245a381 100644 --- a/tools/kvm/include/kvm/ioport.h +++ b/tools/kvm/include/kvm/ioport.h @@ -12,6 +12,8 @@ #define IOPORT_VIRTIO_CONSOLE_SIZE 256 #define IOPORT_VIRTIO_NET 0xe200 /* Virtio network device */ #define IOPORT_VIRTIO_NET_SIZE 256 +#define IOPORT_VIRTIO_RNG 0xf200 /* Virtio network device */ +#define IOPORT_VIRTIO_RNG_SIZE 256 struct kvm; diff --git a/tools/kvm/include/kvm/virtio-rng.h b/tools/kvm/include/kvm/virtio-rng.h new file mode 100644 index 000000000000..7015c1f04682 --- /dev/null +++ b/tools/kvm/include/kvm/virtio-rng.h @@ -0,0 +1,8 @@ +#ifndef KVM__RNG_VIRTIO_H +#define KVM__RNG_VIRTIO_H + +struct kvm; + +void virtio_rng__init(struct kvm *kvm); + +#endif /* KVM__RNG_VIRTIO_H */ diff --git a/tools/kvm/virtio-rng.c b/tools/kvm/virtio-rng.c new file mode 100644 index 000000000000..4cec97dfa34c --- /dev/null +++ b/tools/kvm/virtio-rng.c @@ -0,0 +1,184 @@ +#include "kvm/virtio-rng.h" + +#include "kvm/virtio-pci.h" + +#include "kvm/disk-image.h" +#include "kvm/virtio.h" +#include "kvm/ioport.h" +#include "kvm/mutex.h" +#include "kvm/util.h" +#include "kvm/kvm.h" +#include "kvm/pci.h" +#include "kvm/threadpool.h" + +#include +#include + +#include +#include +#include +#include +#include + +#define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 +#define PCI_DEVICE_ID_VIRTIO_RNG 0x1004 +#define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET 0x1af4 +#define PCI_SUBSYSTEM_ID_VIRTIO_RNG 0x0004 +#define PCI_VIRTIO_RNG_DEVNUM 4 + +#define VIRTIO_RNG_IRQ 11 +#define VIRTIO_RNG_PIN 1 + +#define NUM_VIRT_QUEUES 1 + +#define VIRTIO_RNG_QUEUE_SIZE 128 + +struct rng_device { + uint8_t status; + uint16_t config_vector; + int fd_rng; + + /* virtio queue */ + uint16_t queue_selector; + struct virt_queue vqs[NUM_VIRT_QUEUES]; + void *jobs[NUM_VIRT_QUEUES]; +}; + +static struct rng_device rng_device; + +static bool virtio_rng_pci_io_in(struct kvm *kvm, uint16_t port, void *data, int size, uint32_t count) +{ + unsigned long offset; + bool ret = true; + + offset = port - IOPORT_VIRTIO_RNG; + + switch (offset) { + case VIRTIO_PCI_HOST_FEATURES: + case VIRTIO_PCI_GUEST_FEATURES: + case VIRTIO_PCI_QUEUE_SEL: + case VIRTIO_PCI_QUEUE_NOTIFY: + ret = false; + break; + case VIRTIO_PCI_QUEUE_PFN: + ioport__write32(data, rng_device.vqs[rng_device.queue_selector].pfn); + break; + case VIRTIO_PCI_QUEUE_NUM: + ioport__write16(data, VIRTIO_RNG_QUEUE_SIZE); + break; + case VIRTIO_PCI_STATUS: + ioport__write8(data, rng_device.status); + break; + case VIRTIO_PCI_ISR: + ioport__write8(data, 0x1); + kvm__irq_line(kvm, VIRTIO_RNG_IRQ, 0); + break; + case VIRTIO_MSI_CONFIG_VECTOR: + ioport__write16(data, rng_device.config_vector); + break; + default: + ret = false; + }; + + return ret; +} + +static bool virtio_rng_do_io_request(struct kvm *self, struct virt_queue *queue) +{ + struct iovec iov[VIRTIO_RNG_QUEUE_SIZE]; + uint16_t out, in, head; + unsigned int len = 0; + + head = virt_queue__get_iov(queue, iov, &out, &in, self); + len = readv(rng_device.fd_rng, iov, in); + virt_queue__set_used_elem(queue, head, len); + + return true; +} + +static void virtio_rng_do_io(struct kvm *kvm, void *param) +{ + struct virt_queue *vq = param; + + while (virt_queue__available(vq)) { + virtio_rng_do_io_request(kvm, vq); + kvm__irq_line(kvm, VIRTIO_RNG_IRQ, 1); + } +} + +static bool virtio_rng_pci_io_out(struct kvm *kvm, uint16_t port, void *data, int size, uint32_t count) +{ + unsigned long offset; + bool ret = true; + + offset = port - IOPORT_VIRTIO_RNG; + + switch (offset) { + case VIRTIO_MSI_QUEUE_VECTOR: + case VIRTIO_PCI_GUEST_FEATURES: + break; + case VIRTIO_PCI_QUEUE_PFN: { + struct virt_queue *queue; + void *p; + + queue = &rng_device.vqs[rng_device.queue_selector]; + queue->pfn = ioport__read32(data); + p = guest_flat_to_host(kvm, queue->pfn << 12); + + vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, 4096); + + rng_device.jobs[rng_device.queue_selector] = + thread_pool__add_jobtype(kvm, virtio_rng_do_io, queue); + + break; + } + case VIRTIO_PCI_QUEUE_SEL: + rng_device.queue_selector = ioport__read16(data); + break; + case VIRTIO_PCI_QUEUE_NOTIFY: { + uint16_t queue_index; + queue_index = ioport__read16(data); + thread_pool__signal_work(rng_device.jobs[queue_index]); + break; + } + case VIRTIO_PCI_STATUS: + rng_device.status = ioport__read8(data); + break; + case VIRTIO_MSI_CONFIG_VECTOR: + rng_device.config_vector = VIRTIO_MSI_NO_VECTOR; + break; + default: + ret = false; + }; + + return ret; +} + +static struct ioport_operations virtio_rng_io_ops = { + .io_in = virtio_rng_pci_io_in, + .io_out = virtio_rng_pci_io_out, +}; + +static struct pci_device_header virtio_rng_pci_device = { + .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, + .device_id = PCI_DEVICE_ID_VIRTIO_RNG, + .header_type = PCI_HEADER_TYPE_NORMAL, + .revision_id = 0, + .class = 0x010000, + .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, + .subsys_id = PCI_SUBSYSTEM_ID_VIRTIO_RNG, + .bar[0] = IOPORT_VIRTIO_RNG | PCI_BASE_ADDRESS_SPACE_IO, + .irq_pin = VIRTIO_RNG_PIN, + .irq_line = VIRTIO_RNG_IRQ, +}; + +void virtio_rng__init(struct kvm *kvm) +{ + rng_device.fd_rng = open("/dev/urandom", O_RDONLY); + if (rng_device.fd_rng < 0) + die("Failed initializing RNG"); + + pci__register(&virtio_rng_pci_device, PCI_VIRTIO_RNG_DEVNUM); + + ioport__register(IOPORT_VIRTIO_RNG, &virtio_rng_io_ops, IOPORT_VIRTIO_RNG_SIZE); +} -- 2.39.5