Since multiple hardware rng devices of the same type are currently
unsupported by the kernel, this serves more as an example of a basic
virtio driver under kvm tools and can be used to debug the PCI layer.

Signed-off-by: Sasha Levin <levinsasha...@gmail.com>
---
 tools/kvm/include/kvm/ioport.h        |    2 -
 tools/kvm/include/kvm/parse-options.h |    9 +++
 tools/kvm/include/kvm/virtio-rng.h    |    1 +
 tools/kvm/kvm-run.c                   |    8 ++-
 tools/kvm/virtio/rng.c                |  126 ++++++++++++++++++++++-----------
 5 files changed, 100 insertions(+), 46 deletions(-)

diff --git a/tools/kvm/include/kvm/ioport.h b/tools/kvm/include/kvm/ioport.h
index 47f9fb5..ffa6893 100644
--- a/tools/kvm/include/kvm/ioport.h
+++ b/tools/kvm/include/kvm/ioport.h
@@ -18,8 +18,6 @@
 #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
 
 #define IOPORT_EMPTY                   USHRT_MAX
 
diff --git a/tools/kvm/include/kvm/parse-options.h 
b/tools/kvm/include/kvm/parse-options.h
index 2d5c99e..6bf9a1d 100644
--- a/tools/kvm/include/kvm/parse-options.h
+++ b/tools/kvm/include/kvm/parse-options.h
@@ -132,6 +132,15 @@ intptr_t defval;
        .help = (h)                         \
 }
 
+#define OPT_INCR(s, l, v, h)                \
+{                                           \
+       .type = OPTION_INCR,                \
+       .short_name = (s),                  \
+       .long_name = (l),                   \
+       .value = check_vtype(v, int *),     \
+       .help = (h)                         \
+}
+
 #define OPT_GROUP(h)                        \
 {                                           \
        .type = OPTION_GROUP,               \
diff --git a/tools/kvm/include/kvm/virtio-rng.h 
b/tools/kvm/include/kvm/virtio-rng.h
index 7015c1f..c0a413b 100644
--- a/tools/kvm/include/kvm/virtio-rng.h
+++ b/tools/kvm/include/kvm/virtio-rng.h
@@ -4,5 +4,6 @@
 struct kvm;
 
 void virtio_rng__init(struct kvm *kvm);
+void virtio_rng__delete_all(struct kvm *kvm);
 
 #endif /* KVM__RNG_VIRTIO_H */
diff --git a/tools/kvm/kvm-run.c b/tools/kvm/kvm-run.c
index adbb25b..76b5782 100644
--- a/tools/kvm/kvm-run.c
+++ b/tools/kvm/kvm-run.c
@@ -52,6 +52,7 @@ static __thread struct kvm_cpu *current_kvm_cpu;
 
 static u64 ram_size;
 static u8  image_count;
+static int virtio_rng;
 static const char *kernel_cmdline;
 static const char *kernel_filename;
 static const char *vmlinux_filename;
@@ -66,7 +67,6 @@ static const char *script;
 static const char *virtio_9p_dir;
 static bool single_step;
 static bool readonly_image[MAX_DISK_IMAGES];
-static bool virtio_rng;
 static bool vnc;
 extern bool ioport_debug;
 extern int  active_console;
@@ -107,7 +107,7 @@ static const struct option options[] = {
        OPT_CALLBACK('d', "disk", NULL, "image", "Disk image", img_name_parser),
        OPT_STRING('\0', "console", &console, "serial or virtio",
                        "Console to use"),
-       OPT_BOOLEAN('\0', "rng", &virtio_rng,
+       OPT_INCR('\0', "rng", &virtio_rng,
                        "Enable virtio Random Number Generator"),
        OPT_STRING('\0', "kvm-dev", &kvm_dev, "kvm-dev", "KVM device file"),
        OPT_STRING('\0', "virtio-9p", &virtio_9p_dir, "root dir",
@@ -570,7 +570,8 @@ int kvm_cmd_run(int argc, const char **argv, const char 
*prefix)
                virtio_console__init(kvm);
 
        if (virtio_rng)
-               virtio_rng__init(kvm);
+               while (virtio_rng--)
+                       virtio_rng__init(kvm);
 
        if (!network)
                network = DEFAULT_NETWORK;
@@ -631,6 +632,7 @@ int kvm_cmd_run(int argc, const char **argv, const char 
*prefix)
        }
 
        virtio_blk__delete_all(kvm);
+       virtio_rng__delete_all(kvm);
 
        disk_image__close_all(kvm->disks, image_count);
        kvm__delete(kvm);
diff --git a/tools/kvm/virtio/rng.c b/tools/kvm/virtio/rng.c
index 9bd0098..f71a59b 100644
--- a/tools/kvm/virtio/rng.c
+++ b/tools/kvm/virtio/rng.c
@@ -5,7 +5,6 @@
 #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"
@@ -15,6 +14,7 @@
 #include <linux/virtio_ring.h>
 #include <linux/virtio_rng.h>
 
+#include <linux/list.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -23,18 +23,17 @@
 #define NUM_VIRT_QUEUES                                1
 #define VIRTIO_RNG_QUEUE_SIZE                  128
 
-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              = VIRTIO_ID_RNG,
-       .bar[0]                 = IOPORT_VIRTIO_RNG | PCI_BASE_ADDRESS_SPACE_IO,
+struct rng_dev_job {
+       struct virt_queue               *vq;
+       struct rng_dev                  *rdev;
+       void                            *job_id;
 };
 
 struct rng_dev {
+       struct pci_device_header pci_hdr;
+       struct list_head        list;
+
+       u16                     base_addr;
        u8                      status;
        u8                      isr;
        u16                     config_vector;
@@ -43,17 +42,19 @@ struct rng_dev {
        /* virtio queue */
        u16                     queue_selector;
        struct virt_queue       vqs[NUM_VIRT_QUEUES];
-       void                    *jobs[NUM_VIRT_QUEUES];
+       struct rng_dev_job      jobs[NUM_VIRT_QUEUES];
 };
 
-static struct rng_dev rdev;
+static LIST_HEAD(rdevs);
 
-static bool virtio_rng_pci_io_in(struct kvm *kvm, u16 port, void *data, int 
size, u32 count)
+static bool virtio_rng_pci_io_in(struct kvm *kvm, u16 port, void *data, int 
size, u32 count, void *param)
 {
        unsigned long offset;
        bool ret = true;
+       struct rng_dev *rdev;
 
-       offset = port - IOPORT_VIRTIO_RNG;
+       rdev = param;
+       offset = port - rdev->base_addr;
 
        switch (offset) {
        case VIRTIO_PCI_HOST_FEATURES:
@@ -63,21 +64,21 @@ static bool virtio_rng_pci_io_in(struct kvm *kvm, u16 port, 
void *data, int size
                ret             = false;
                break;
        case VIRTIO_PCI_QUEUE_PFN:
-               ioport__write32(data, rdev.vqs[rdev.queue_selector].pfn);
+               ioport__write32(data, rdev->vqs[rdev->queue_selector].pfn);
                break;
        case VIRTIO_PCI_QUEUE_NUM:
                ioport__write16(data, VIRTIO_RNG_QUEUE_SIZE);
                break;
        case VIRTIO_PCI_STATUS:
-               ioport__write8(data, rdev.status);
+               ioport__write8(data, rdev->status);
                break;
        case VIRTIO_PCI_ISR:
-               ioport__write8(data, rdev.isr);
-               kvm__irq_line(kvm, virtio_rng_pci_device.irq_line, 
VIRTIO_IRQ_LOW);
-               rdev.isr = VIRTIO_IRQ_LOW;
+               ioport__write8(data, rdev->isr);
+               kvm__irq_line(kvm, rdev->pci_hdr.irq_line, VIRTIO_IRQ_LOW);
+               rdev->isr = VIRTIO_IRQ_LOW;
                break;
        case VIRTIO_MSI_CONFIG_VECTOR:
-               ioport__write16(data, rdev.config_vector);
+               ioport__write16(data, rdev->config_vector);
                break;
        default:
                ret             = false;
@@ -87,14 +88,14 @@ static bool virtio_rng_pci_io_in(struct kvm *kvm, u16 port, 
void *data, int size
        return ret;
 }
 
-static bool virtio_rng_do_io_request(struct kvm *kvm, struct virt_queue *queue)
+static bool virtio_rng_do_io_request(struct kvm *kvm, struct rng_dev *rdev, 
struct virt_queue *queue)
 {
        struct iovec iov[VIRTIO_RNG_QUEUE_SIZE];
        unsigned int len = 0;
        u16 out, in, head;
 
        head            = virt_queue__get_iov(queue, iov, &out, &in, kvm);
-       len             = readv(rdev.fd, iov, in);
+       len             = readv(rdev->fd, iov, in);
 
        virt_queue__set_used_elem(queue, head, len);
 
@@ -103,20 +104,24 @@ static bool virtio_rng_do_io_request(struct kvm *kvm, 
struct virt_queue *queue)
 
 static void virtio_rng_do_io(struct kvm *kvm, void *param)
 {
-       struct virt_queue *vq = param;
+       struct rng_dev_job *job = param;
+       struct virt_queue *vq = job->vq;
+       struct rng_dev *rdev = job->rdev;
 
        while (virt_queue__available(vq)) {
-               virtio_rng_do_io_request(kvm, vq);
-               virt_queue__trigger_irq(vq, virtio_rng_pci_device.irq_line, 
&rdev.isr, kvm);
+               virtio_rng_do_io_request(kvm, rdev, vq);
+               virt_queue__trigger_irq(vq, rdev->pci_hdr.irq_line, &rdev->isr, 
kvm);
        }
 }
 
-static bool virtio_rng_pci_io_out(struct kvm *kvm, u16 port, void *data, int 
size, u32 count)
+static bool virtio_rng_pci_io_out(struct kvm *kvm, u16 port, void *data, int 
size, u32 count, void *param)
 {
        unsigned long offset;
        bool ret = true;
+       struct rng_dev *rdev;
 
-       offset          = port - IOPORT_VIRTIO_RNG;
+       rdev = param;
+       offset = port - rdev->base_addr;
 
        switch (offset) {
        case VIRTIO_MSI_QUEUE_VECTOR:
@@ -124,32 +129,40 @@ static bool virtio_rng_pci_io_out(struct kvm *kvm, u16 
port, void *data, int siz
                break;
        case VIRTIO_PCI_QUEUE_PFN: {
                struct virt_queue *queue;
+               struct rng_dev_job *job;
                void *p;
 
-               queue                   = &rdev.vqs[rdev.queue_selector];
+               queue                   = &rdev->vqs[rdev->queue_selector];
                queue->pfn              = ioport__read32(data);
                p                       = guest_pfn_to_host(kvm, queue->pfn);
 
+               job = &rdev->jobs[rdev->queue_selector];
+
                vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, 
VIRTIO_PCI_VRING_ALIGN);
 
-               rdev.jobs[rdev.queue_selector] = thread_pool__add_job(kvm, 
virtio_rng_do_io, queue);
+               *job                    = (struct rng_dev_job) {
+                       .vq                     = queue,
+                       .rdev                   = rdev,
+               };
+
+               job->job_id = thread_pool__add_job(kvm, virtio_rng_do_io, job);
 
                break;
        }
        case VIRTIO_PCI_QUEUE_SEL:
-               rdev.queue_selector     = ioport__read16(data);
+               rdev->queue_selector    = ioport__read16(data);
                break;
        case VIRTIO_PCI_QUEUE_NOTIFY: {
                u16 queue_index;
                queue_index             = ioport__read16(data);
-               thread_pool__do_job(rdev.jobs[queue_index]);
+               thread_pool__do_job(rdev->jobs[queue_index].job_id);
                break;
        }
        case VIRTIO_PCI_STATUS:
-               rdev.status             = ioport__read8(data);
+               rdev->status            = ioport__read8(data);
                break;
        case VIRTIO_MSI_CONFIG_VECTOR:
-               rdev.config_vector      = VIRTIO_MSI_NO_VECTOR;
+               rdev->config_vector     = VIRTIO_MSI_NO_VECTOR;
                break;
        default:
                ret                     = false;
@@ -160,24 +173,55 @@ static bool virtio_rng_pci_io_out(struct kvm *kvm, u16 
port, void *data, int siz
 }
 
 static struct ioport_operations virtio_rng_io_ops = {
-       .io_in                          = virtio_rng_pci_io_in,
-       .io_out                         = virtio_rng_pci_io_out,
+       .io_in_param                    = virtio_rng_pci_io_in,
+       .io_out_param                   = virtio_rng_pci_io_out,
 };
 
 void virtio_rng__init(struct kvm *kvm)
 {
        u8 pin, line, dev;
+       u16 rdev_base_addr;
+       struct rng_dev *rdev;
+
+       rdev = malloc(sizeof(*rdev));
+       if (rdev == NULL)
+               return;
+
+       rdev_base_addr = ioport__register_param(IOPORT_EMPTY, 
&virtio_rng_io_ops, IOPORT_SIZE, rdev);
+
+       rdev->pci_hdr = (struct pci_device_header) {
+               .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              = VIRTIO_ID_RNG,
+               .bar[0]                 = rdev_base_addr | 
PCI_BASE_ADDRESS_SPACE_IO,
+       };
 
-       rdev.fd = open("/dev/urandom", O_RDONLY);
-       if (rdev.fd < 0)
+       rdev->base_addr = rdev_base_addr;
+       rdev->fd = open("/dev/urandom", O_RDONLY);
+       if (rdev->fd < 0)
                die("Failed initializing RNG");
 
        if (irq__register_device(VIRTIO_ID_RNG, &dev, &pin, &line) < 0)
                return;
 
-       virtio_rng_pci_device.irq_pin   = pin;
-       virtio_rng_pci_device.irq_line  = line;
-       pci__register(&virtio_rng_pci_device, dev);
+       rdev->pci_hdr.irq_pin   = pin;
+       rdev->pci_hdr.irq_line  = line;
+       pci__register(&rdev->pci_hdr, dev);
+
+       list_add_tail(&rdev->list, &rdevs);
+}
+
+void virtio_rng__delete_all(struct kvm *kvm)
+{
+       while (!list_empty(&rdevs)) {
+               struct rng_dev *rdev;
 
-       ioport__register(IOPORT_VIRTIO_RNG, &virtio_rng_io_ops, 
IOPORT_VIRTIO_RNG_SIZE);
+               rdev = list_first_entry(&rdevs, struct rng_dev, list);
+               list_del(&rdev->list);
+               free(rdev);
+       }
 }
-- 
1.7.5.rc3

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to