On Mon, May 10, 2010 at 10:48:42AM -0500, Anthony Liguori wrote:
> On 05/10/2010 03:11 AM, Gleb Natapov wrote:
> >This patch adds native support for booting from virtio disks to Seabios.
> >
> >Signed-off-by: Gleb Natapov<g...@redhat.com>
> 
> A related problem that I think we need to think about how we solve
> is indicating to Seabios which device we want to boot from
> 
> With your patch, a user can select a virtio device explicitly or if
> they use only one virtio device, it will Just Work.
> 
> However, if a user uses IDE and virtio, or a user has multiple
> disks, they cannot select a device via -boot.
> 
Isn't this problem unrelated to this patch?  I mean if I start qemu with
two ide devices can I specify from qemu command line which one I want to
boot from?

> Is this something we need to address?  I don't think we'd break
> libvirt if we didn't.
> 
> Regards,
> 
> Anthony Liguori
> 
> >---
> >
> >Changelog:
> >  v1->v2:
> >   - free memory in case of vq initialization error.
> >   - change license of virtio ring/pci to LGPLv3 with permission
> >     of Laurent Vivier (aka the author).
> >
> >diff --git a/Makefile b/Makefile
> >index 327a1bf..d0b8881 100644
> >--- a/Makefile
> >+++ b/Makefile
> >@@ -14,7 +14,8 @@ OUT=out/
> >  SRCBOTH=misc.c pmm.c stacks.c output.c util.c block.c floppy.c ata.c 
> > mouse.c \
> >          kbd.c pci.c serial.c clock.c pic.c cdrom.c ps2port.c smp.c 
> > resume.c \
> >          pnpbios.c pirtable.c vgahooks.c ramdisk.c pcibios.c blockcmd.c \
> >-        usb.c usb-uhci.c usb-ohci.c usb-ehci.c usb-hid.c usb-msc.c
> >+        usb.c usb-uhci.c usb-ohci.c usb-ehci.c usb-hid.c usb-msc.c \
> >+        virtio-ring.c virtio-pci.c virtio-blk.c
> >  SRC16=$(SRCBOTH) system.c disk.c apm.c font.c
> >  SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \
> >        acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \
> >diff --git a/src/block.c b/src/block.c
> >index ddf441f..b6b1902 100644
> >--- a/src/block.c
> >+++ b/src/block.c
> >@@ -11,6 +11,7 @@
> >  #include "util.h" // dprintf
> >  #include "ata.h" // process_ata_op
> >  #include "usb-msc.h" // process_usb_op
> >+#include "virtio-blk.h" // process_virtio_op
> >
> >  struct drives_s Drives VAR16VISIBLE;
> >
> >@@ -289,6 +290,8 @@ process_op(struct disk_op_s *op)
> >          return process_cdemu_op(op);
> >      case DTYPE_USB:
> >          return process_usb_op(op);
> >+    case DTYPE_VIRTIO:
> >+    return process_virtio_op(op);
> >      default:
> >          op->count = 0;
> >          return DISK_RET_EPARAM;
> >diff --git a/src/config.h b/src/config.h
> >index b101174..ad569c6 100644
> >--- a/src/config.h
> >+++ b/src/config.h
> >@@ -136,6 +136,9 @@
> >  #define CONFIG_SUBMODEL_ID   0x00
> >  #define CONFIG_BIOS_REVISION 0x01
> >
> >+// Support boot from virtio storage
> >+#define CONFIG_VIRTIO_BLK 1
> >+
> >  // Various memory addresses used by the code.
> >  #define BUILD_STACK_ADDR          0x7000
> >  #define BUILD_S3RESUME_STACK_ADDR 0x1000
> >diff --git a/src/disk.h b/src/disk.h
> >index 0cd1b74..9e5b083 100644
> >--- a/src/disk.h
> >+++ b/src/disk.h
> >@@ -197,6 +197,7 @@ struct drive_s {
> >  #define DTYPE_RAMDISK  0x04
> >  #define DTYPE_CDEMU    0x05
> >  #define DTYPE_USB      0x06
> >+#define DTYPE_VIRTIO   0x07
> >
> >  #define MAXDESCSIZE 80
> >
> >diff --git a/src/pci_ids.h b/src/pci_ids.h
> >index 1800f1d..e1cded2 100644
> >--- a/src/pci_ids.h
> >+++ b/src/pci_ids.h
> >@@ -2605,3 +2605,6 @@
> >  #define PCI_DEVICE_ID_RME_DIGI32   0x9896
> >  #define PCI_DEVICE_ID_RME_DIGI32_PRO       0x9897
> >  #define PCI_DEVICE_ID_RME_DIGI32_8 0x9898
> >+
> >+#define PCI_VENDOR_ID_REDHAT_QUMRANET       0x1af4
> >+#define PCI_DEVICE_ID_VIRTIO_BLK    0x1001
> >diff --git a/src/post.c b/src/post.c
> >index 638b0f7..25535e2 100644
> >--- a/src/post.c
> >+++ b/src/post.c
> >@@ -23,6 +23,7 @@
> >  #include "smbios.h" // smbios_init
> >  #include "paravirt.h" // qemu_cfg_port_probe
> >  #include "ps2port.h" // ps2port_setup
> >+#include "virtio-blk.h" // virtio_blk_setup
> >
> >  void
> >  __set_irq(int vector, void *loc)
> >@@ -184,6 +185,7 @@ init_hw(void)
> >      floppy_setup();
> >      ata_setup();
> >      ramdisk_setup();
> >+    virtio_blk_setup();
> >  }
> >
> >  // Main setup code.
> >diff --git a/src/virtio-blk.c b/src/virtio-blk.c
> >new file mode 100644
> >index 0000000..a41c336
> >--- /dev/null
> >+++ b/src/virtio-blk.c
> >@@ -0,0 +1,155 @@
> >+// Virtio blovl boot support.
> >+//
> >+// Copyright (C) 2010 Red Hat Inc.
> >+//
> >+// Authors:
> >+//  Gleb Natapov<gnata...@redhat.com>
> >+//
> >+// This file may be distributed under the terms of the GNU LGPLv3 license.
> >+
> >+#include "util.h" // dprintf
> >+#include "pci.h" // foreachpci
> >+#include "config.h" // CONFIG_*
> >+#include "virtio-pci.h"
> >+#include "virtio-blk.h"
> >+#include "disk.h"
> >+
> >+struct virtiodrive_s {
> >+    struct drive_s drive;
> >+    struct vring_virtqueue *vq;
> >+    u16 ioaddr;
> >+};
> >+
> >+static int
> >+virtio_blk_read(struct disk_op_s *op)
> >+{
> >+    struct virtiodrive_s *vdrive_g =
> >+        container_of(op->drive_g, struct virtiodrive_s, drive);
> >+    struct vring_virtqueue *vq = GET_GLOBAL(vdrive_g->vq);
> >+    struct virtio_blk_outhdr hdr = {
> >+        .type = VIRTIO_BLK_T_IN,
> >+        .ioprio = 0,
> >+        .sector = op->lba,
> >+    };
> >+    u8 status = VIRTIO_BLK_S_UNSUPP;
> >+    struct vring_list sg[] = {
> >+        {
> >+            .addr   = MAKE_FLATPTR(GET_SEG(SS),&hdr),
> >+            .length = sizeof(hdr),
> >+        },
> >+        {
> >+            .addr   = op->buf_fl,
> >+            .length = GET_GLOBAL(vdrive_g->drive.blksize) * op->count,
> >+        },
> >+        {
> >+            .addr   = MAKE_FLATPTR(GET_SEG(SS),&status),
> >+            .length = sizeof(status),
> >+        },
> >+    };
> >+
> >+    /* Add to virtqueue and kick host */
> >+    vring_add_buf(vq, sg, 1, 2, 0, 0);
> >+    vring_kick(GET_GLOBAL(vdrive_g->ioaddr), vq, 1);
> >+
> >+    /* Wait for reply */
> >+    while (!vring_more_used(vq))
> >+        udelay(5);
> >+
> >+    /* Reclaim virtqueue element */
> >+    vring_get_buf(vq, NULL);
> >+    return status == VIRTIO_BLK_S_OK ? DISK_RET_SUCCESS : 
> >DISK_RET_EBADTRACK;
> >+}
> >+
> >+int
> >+process_virtio_op(struct disk_op_s *op)
> >+{
> >+    switch (op->command) {
> >+    case CMD_READ:
> >+        return virtio_blk_read(op);
> >+    case CMD_FORMAT:
> >+    case CMD_WRITE:
> >+        return DISK_RET_EWRITEPROTECT;
> >+    case CMD_RESET:
> >+    case CMD_ISREADY:
> >+    case CMD_VERIFY:
> >+    case CMD_SEEK:
> >+        return DISK_RET_SUCCESS;
> >+    default:
> >+        op->count = 0;
> >+        return DISK_RET_EPARAM;
> >+    }
> >+}
> >+
> >+void
> >+virtio_blk_setup(void)
> >+{
> >+    ASSERT32FLAT();
> >+    if (! CONFIG_VIRTIO_BLK)
> >+        return;
> >+
> >+    dprintf(3, "init virtio-blk\n");
> >+
> >+    int bdf, max;
> >+    u32 id = PCI_VENDOR_ID_REDHAT_QUMRANET | (PCI_DEVICE_ID_VIRTIO_BLK<<  
> >16);
> >+    foreachpci(bdf, max) {
> >+        u32 v = pci_config_readl(bdf, PCI_VENDOR_ID);
> >+        if (v != id)
> >+            continue;
> >+        dprintf(3, "found virtio-blk at %x:%x\n", pci_bdf_to_bus(bdf),
> >+                pci_bdf_to_dev(bdf));
> >+        char *desc = malloc_tmphigh(MAXDESCSIZE);
> >+        struct virtiodrive_s *vdrive_g = malloc_fseg(sizeof(*vdrive_g));
> >+        struct vring_virtqueue *vq = malloc_low(sizeof(*vq));
> >+        if (!vdrive_g || !desc || !vq) {
> >+            warn_noalloc();
> >+            return;
> >+        }
> >+        memset(vdrive_g, 0, sizeof(*vdrive_g));
> >+        vdrive_g->drive.type = DTYPE_VIRTIO;
> >+        vdrive_g->drive.cntl_id = bdf;
> >+        vdrive_g->vq = vq;
> >+
> >+        u16 ioaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0)&
> >+            PCI_BASE_ADDRESS_IO_MASK;
> >+
> >+        vdrive_g->ioaddr = ioaddr;
> >+
> >+        vp_reset(ioaddr);
> >+        vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
> >+                      VIRTIO_CONFIG_S_DRIVER );
> >+
> >+        if (vp_find_vq(ioaddr, 0, vdrive_g->vq)<  0 ) {
> >+            free(vdrive_g);
> >+            free(desc);
> >+            free(vq);
> >+            dprintf(1, "fail to find vq for virtio-blk %x:%x\n",
> >+                    pci_bdf_to_bus (bdf), pci_bdf_to_dev(bdf));
> >+            continue;
> >+        }
> >+
> >+        struct virtio_blk_config cfg;
> >+        vp_get(ioaddr, 0,&cfg, sizeof(cfg));
> >+
> >+        vdrive_g->drive.blksize = cfg.blk_size;
> >+        vdrive_g->drive.sectors = cfg.capacity;
> >+        dprintf(3, "virtio-blk %x:%x blksize=%d sectors=%u\n",
> >+                pci_bdf_to_bus (bdf), pci_bdf_to_dev(bdf),
> >+                vdrive_g->drive.blksize, (u32)vdrive_g->drive.sectors);
> >+
> >+        vdrive_g->drive.pchs.cylinders = cfg.cylinders;
> >+        vdrive_g->drive.pchs.heads = cfg.heads;
> >+        vdrive_g->drive.pchs.spt = cfg.sectors;
> >+
> >+        setup_translation(&vdrive_g->drive);
> >+        add_bcv_internal(&vdrive_g->drive);
> >+
> >+        snprintf(desc, MAXDESCSIZE, "Virtio disk PCI:%x:%x",
> >+                 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
> >+
> >+        vdrive_g->drive.desc = desc;
> >+
> >+        vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
> >+                      VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK);
> >+    }
> >+}
> >+
> >diff --git a/src/virtio-blk.h b/src/virtio-blk.h
> >new file mode 100644
> >index 0000000..3369ea4
> >--- /dev/null
> >+++ b/src/virtio-blk.h
> >@@ -0,0 +1,40 @@
> >+#ifndef _VIRTIO_BLK_H
> >+#define _VIRTIO_BLK_H
> >+
> >+struct virtio_blk_config
> >+{
> >+    u64 capacity;
> >+    u32 size_max;
> >+    u32 seg_max;
> >+    u16 cylinders;
> >+    u8 heads;
> >+    u8 sectors;
> >+    u32 blk_size;
> >+    u8 physical_block_exp;
> >+    u8 alignment_offset;
> >+    u16 min_io_size;
> >+    u32 opt_io_size;
> >+} __attribute__((packed));
> >+
> >+/* These two define direction. */
> >+#define VIRTIO_BLK_T_IN             0
> >+#define VIRTIO_BLK_T_OUT    1
> >+
> >+/* This is the first element of the read scatter-gather list. */
> >+struct virtio_blk_outhdr {
> >+    /* VIRTIO_BLK_T* */
> >+    u32 type;
> >+    /* io priority. */
> >+    u32 ioprio;
> >+    /* Sector (ie. 512 byte offset) */
> >+    u64 sector;
> >+};
> >+
> >+#define VIRTIO_BLK_S_OK             0
> >+#define VIRTIO_BLK_S_IOERR  1
> >+#define VIRTIO_BLK_S_UNSUPP 2
> >+
> >+int process_virtio_op(struct disk_op_s *op);
> >+void virtio_blk_setup(void);
> >+
> >+#endif /* _VIRTIO_BLK_H */
> >diff --git a/src/virtio-pci.c b/src/virtio-pci.c
> >new file mode 100644
> >index 0000000..e171ea3
> >--- /dev/null
> >+++ b/src/virtio-pci.c
> >@@ -0,0 +1,67 @@
> >+/* virtio-pci.c - pci interface for virtio interface
> >+ *
> >+ * (c) Copyright 2008 Bull S.A.S.
> >+ *
> >+ *  Author: Laurent Vivier<laurent.viv...@bull.net>
> >+ *
> >+ * some parts from Linux Virtio PCI driver
> >+ *
> >+ *  Copyright IBM Corp. 2007
> >+ *  Authors: Anthony Liguori<aligu...@us.ibm.com>
> >+ *
> >+ *  Adopted for Seabios: Gleb Natapov<g...@redhat.com>
> >+ *
> >+ * This work is licensed under the terms of the GNU LGPLv3
> >+ * See the COPYING file in the top-level directory.
> >+ */
> >+
> >+#include "virtio-ring.h"
> >+#include "virtio-pci.h"
> >+
> >+int vp_find_vq(unsigned int ioaddr, int queue_index,
> >+               struct vring_virtqueue *vq)
> >+{
> >+   struct vring * vr =&vq->vring;
> >+   u16 num;
> >+
> >+   ASSERT32FLAT();
> >+   /* select the queue */
> >+
> >+   outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL);
> >+
> >+   /* check if the queue is available */
> >+
> >+   num = inw(ioaddr + VIRTIO_PCI_QUEUE_NUM);
> >+   if (!num) {
> >+       dprintf(1, "ERROR: queue size is 0\n");
> >+       return -1;
> >+   }
> >+
> >+   if (num>  MAX_QUEUE_NUM) {
> >+       dprintf(1, "ERROR: queue size %d>  %d\n", num, MAX_QUEUE_NUM);
> >+       return -1;
> >+   }
> >+
> >+   /* check if the queue is already active */
> >+
> >+   if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) {
> >+       dprintf(1, "ERROR: queue already active\n");
> >+       return -1;
> >+   }
> >+
> >+   vq->queue_index = queue_index;
> >+
> >+   /* initialize the queue */
> >+
> >+   vring_init(vr, num, (unsigned char*)&vq->queue);
> >+
> >+   /* activate the queue
> >+    *
> >+    * NOTE: vr->desc is initialized by vring_init()
> >+    */
> >+
> >+   outl((unsigned long)virt_to_phys(vr->desc)>>  PAGE_SHIFT,
> >+        ioaddr + VIRTIO_PCI_QUEUE_PFN);
> >+
> >+   return num;
> >+}
> >diff --git a/src/virtio-pci.h b/src/virtio-pci.h
> >new file mode 100644
> >index 0000000..6932036
> >--- /dev/null
> >+++ b/src/virtio-pci.h
> >@@ -0,0 +1,97 @@
> >+#ifndef _VIRTIO_PCI_H
> >+#define _VIRTIO_PCI_H
> >+
> >+/* A 32-bit r/o bitmask of the features supported by the host */
> >+#define VIRTIO_PCI_HOST_FEATURES        0
> >+
> >+/* A 32-bit r/w bitmask of features activated by the guest */
> >+#define VIRTIO_PCI_GUEST_FEATURES       4
> >+
> >+/* A 32-bit r/w PFN for the currently selected queue */
> >+#define VIRTIO_PCI_QUEUE_PFN            8
> >+
> >+/* A 16-bit r/o queue size for the currently selected queue */
> >+#define VIRTIO_PCI_QUEUE_NUM            12
> >+
> >+/* A 16-bit r/w queue selector */
> >+#define VIRTIO_PCI_QUEUE_SEL            14
> >+
> >+/* A 16-bit r/w queue notifier */
> >+#define VIRTIO_PCI_QUEUE_NOTIFY         16
> >+
> >+/* An 8-bit device status register.  */
> >+#define VIRTIO_PCI_STATUS               18
> >+
> >+/* An 8-bit r/o interrupt status register.  Reading the value will return 
> >the
> >+ * current contents of the ISR and will also clear it.  This is effectively
> >+ * a read-and-acknowledge. */
> >+#define VIRTIO_PCI_ISR                  19
> >+
> >+/* The bit of the ISR which indicates a device configuration change. */
> >+#define VIRTIO_PCI_ISR_CONFIG           0x2
> >+
> >+/* The remaining space is defined by each driver as the per-driver
> >+ * configuration space */
> >+#define VIRTIO_PCI_CONFIG               20
> >+
> >+/* Virtio ABI version, this must match exactly */
> >+#define VIRTIO_PCI_ABI_VERSION          0
> >+
> >+static inline u32 vp_get_features(unsigned int ioaddr)
> >+{
> >+   return inl(ioaddr + VIRTIO_PCI_HOST_FEATURES);
> >+}
> >+
> >+static inline void vp_set_features(unsigned int ioaddr, u32 features)
> >+{
> >+        outl(features, ioaddr + VIRTIO_PCI_GUEST_FEATURES);
> >+}
> >+
> >+static inline void vp_get(unsigned int ioaddr, unsigned offset,
> >+                     void *buf, unsigned len)
> >+{
> >+   u8 *ptr = buf;
> >+   unsigned i;
> >+
> >+   for (i = 0; i<  len; i++)
> >+           ptr[i] = inb(ioaddr + VIRTIO_PCI_CONFIG + offset + i);
> >+}
> >+
> >+static inline u8 vp_get_status(unsigned int ioaddr)
> >+{
> >+   return inb(ioaddr + VIRTIO_PCI_STATUS);
> >+}
> >+
> >+static inline void vp_set_status(unsigned int ioaddr, u8 status)
> >+{
> >+   if (status == 0)        /* reset */
> >+           return;
> >+   outb(status, ioaddr + VIRTIO_PCI_STATUS);
> >+}
> >+
> >+
> >+static inline void vp_reset(unsigned int ioaddr)
> >+{
> >+   outb(0, ioaddr + VIRTIO_PCI_STATUS);
> >+   (void)inb(ioaddr + VIRTIO_PCI_ISR);
> >+}
> >+
> >+static inline void vp_notify(unsigned int ioaddr, int queue_index)
> >+{
> >+   outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_NOTIFY);
> >+}
> >+
> >+static inline void vp_del_vq(unsigned int ioaddr, int queue_index)
> >+{
> >+   /* select the queue */
> >+
> >+   outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL);
> >+
> >+   /* deactivate the queue */
> >+
> >+   outl(0, ioaddr + VIRTIO_PCI_QUEUE_PFN);
> >+}
> >+
> >+int vp_find_vq(unsigned int ioaddr, int queue_index,
> >+               struct vring_virtqueue *vq);
> >+#endif /* _VIRTIO_PCI_H_ */
> >diff --git a/src/virtio-ring.c b/src/virtio-ring.c
> >new file mode 100644
> >index 0000000..f4a2efe
> >--- /dev/null
> >+++ b/src/virtio-ring.c
> >@@ -0,0 +1,152 @@
> >+/* virtio-pci.c - virtio ring management
> >+ *
> >+ * (c) Copyright 2008 Bull S.A.S.
> >+ *
> >+ *  Author: Laurent Vivier<laurent.viv...@bull.net>
> >+ *
> >+ *  some parts from Linux Virtio Ring
> >+ *
> >+ *  Copyright Rusty Russell IBM Corporation 2007
> >+ *
> >+ *  Adopted for Seabios: Gleb Natapov<g...@redhat.com>
> >+ *
> >+ * This work is licensed under the terms of the GNU LGPLv3
> >+ * See the COPYING file in the top-level directory.
> >+ *
> >+ *
> >+ */
> >+
> >+#include "virtio-ring.h"
> >+#include "virtio-pci.h"
> >+
> >+#define BUG() do {                                      \
> >+        dprintf(1, "BUG: failure at %s:%d/%s()!\n",     \
> >+                __FILE__, __LINE__, __FUNCTION__);      \
> >+                while(1);                               \
> >+        } while (0)
> >+#define BUG_ON(condition) do { if (condition) BUG(); } while (0)
> >+
> >+/*
> >+ * vring_more_used
> >+ *
> >+ * is there some used buffers ?
> >+ *
> >+ */
> >+
> >+int vring_more_used(struct vring_virtqueue *vq)
> >+{
> >+    struct vring_used *used = GET_FLATPTR(vq->vring.used);
> >+    wmb();
> >+    return GET_FLATPTR(vq->last_used_idx) != GET_FLATPTR(used->idx);
> >+}
> >+
> >+/*
> >+ * vring_free
> >+ *
> >+ * put at the begin of the free list the current desc[head]
> >+ */
> >+
> >+void vring_detach(struct vring_virtqueue *vq, unsigned int head)
> >+{
> >+    struct vring *vr =&vq->vring;
> >+    struct vring_desc *desc = GET_FLATPTR(vr->desc);
> >+    unsigned int i;
> >+
> >+    /* find end of given descriptor */
> >+
> >+    i = head;
> >+    while (GET_FLATPTR(desc[i].flags)&  VRING_DESC_F_NEXT)
> >+        i = GET_FLATPTR(desc[i].next);
> >+
> >+    /* link it with free list and point to it */
> >+
> >+    SET_FLATPTR(desc[i].next, GET_FLATPTR(vq->free_head));
> >+    wmb();
> >+    SET_FLATPTR(vq->free_head, head);
> >+}
> >+
> >+/*
> >+ * vring_get_buf
> >+ *
> >+ * get a buffer from the used list
> >+ *
> >+ */
> >+
> >+int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len)
> >+{
> >+    struct vring *vr =&vq->vring;
> >+    struct vring_used_elem *elem;
> >+    struct vring_used *used = GET_FLATPTR(vq->vring.used);
> >+    u32 id;
> >+    int ret;
> >+
> >+//    BUG_ON(!vring_more_used(vq));
> >+
> >+    elem =&used->ring[GET_FLATPTR(vq->last_used_idx) % 
> >GET_FLATPTR(vr->num)];
> >+    wmb();
> >+    id = GET_FLATPTR(elem->id);
> >+    if (len != NULL)
> >+        *len = GET_FLATPTR(elem->len);
> >+
> >+    ret = GET_FLATPTR(vq->vdata[id]);
> >+
> >+    vring_detach(vq, id);
> >+
> >+    SET_FLATPTR(vq->last_used_idx, GET_FLATPTR(vq->last_used_idx) + 1);
> >+
> >+    return ret;
> >+}
> >+
> >+void vring_add_buf(struct vring_virtqueue *vq,
> >+                   struct vring_list list[],
> >+                   unsigned int out, unsigned int in,
> >+                   int index, int num_added)
> >+{
> >+    struct vring *vr =&vq->vring;
> >+    int i, av, head, prev;
> >+    struct vring_desc *desc = GET_FLATPTR(vr->desc);
> >+    struct vring_avail *avail = GET_FLATPTR(vr->avail);
> >+
> >+    BUG_ON(out + in == 0);
> >+
> >+    prev = 0;
> >+    head = GET_FLATPTR(vq->free_head);
> >+    for (i = head; out; i = GET_FLATPTR(desc[i].next), out--) {
> >+        SET_FLATPTR(desc[i].flags, VRING_DESC_F_NEXT);
> >+        SET_FLATPTR(desc[i].addr, (u64)virt_to_phys(list->addr));
> >+        SET_FLATPTR(desc[i].len, list->length);
> >+        prev = i;
> >+        list++;
> >+    }
> >+    for ( ; in; i = GET_FLATPTR(desc[i].next), in--) {
> >+        SET_FLATPTR(desc[i].flags, VRING_DESC_F_NEXT|VRING_DESC_F_WRITE);
> >+        SET_FLATPTR(desc[i].addr, (u64)virt_to_phys(list->addr));
> >+        SET_FLATPTR(desc[i].len, list->length);
> >+        prev = i;
> >+        list++;
> >+    }
> >+    SET_FLATPTR(desc[prev].flags,
> >+                GET_FLATPTR(desc[prev].flags)&  ~VRING_DESC_F_NEXT);
> >+
> >+    SET_FLATPTR(vq->free_head, i);
> >+
> >+    SET_FLATPTR(vq->vdata[head], index);
> >+
> >+    av = (GET_FLATPTR(avail->idx) + num_added) % GET_FLATPTR(vr->num);
> >+    SET_FLATPTR(avail->ring[av], head);
> >+    wmb();
> >+}
> >+
> >+void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int 
> >num_added)
> >+{
> >+    struct vring *vr =&vq->vring;
> >+    struct vring_avail *avail = GET_FLATPTR(vr->avail);
> >+    struct vring_used *used = GET_FLATPTR(vq->vring.used);
> >+
> >+    wmb();
> >+    SET_FLATPTR(avail->idx, GET_FLATPTR(avail->idx) + num_added);
> >+
> >+    mb();
> >+    if (!(GET_FLATPTR(used->flags)&  VRING_USED_F_NO_NOTIFY))
> >+        vp_notify(ioaddr, GET_FLATPTR(vq->queue_index));
> >+}
> >diff --git a/src/virtio-ring.h b/src/virtio-ring.h
> >new file mode 100644
> >index 0000000..b97d572
> >--- /dev/null
> >+++ b/src/virtio-ring.h
> >@@ -0,0 +1,125 @@
> >+#ifndef _VIRTIO_RING_H
> >+#define _VIRTIO_RING_H
> >+
> >+#define PAGE_SHIFT 12
> >+#define PAGE_MASK  (PAGE_SIZE-1)
> >+
> >+#define virt_to_phys(v) (unsigned long)(v)
> >+#define phys_to_virt(p) (void*)(p)
> >+#define wmb() barrier()
> >+#define mb() barrier()
> >+
> >+/* Status byte for guest to report progress, and synchronize features. */
> >+/* We have seen device and processed generic fields 
> >(VIRTIO_CONFIG_F_VIRTIO) */
> >+#define VIRTIO_CONFIG_S_ACKNOWLEDGE     1
> >+/* We have found a driver for the device. */
> >+#define VIRTIO_CONFIG_S_DRIVER          2
> >+/* Driver has used its parts of the config, and is happy */
> >+#define VIRTIO_CONFIG_S_DRIVER_OK       4
> >+/* We've given up on this device. */
> >+#define VIRTIO_CONFIG_S_FAILED          0x80
> >+
> >+#define MAX_QUEUE_NUM      (128)
> >+
> >+#define VRING_DESC_F_NEXT  1
> >+#define VRING_DESC_F_WRITE 2
> >+
> >+#define VRING_AVAIL_F_NO_INTERRUPT 1
> >+
> >+#define VRING_USED_F_NO_NOTIFY     1
> >+
> >+struct vring_desc
> >+{
> >+   u64 addr;
> >+   u32 len;
> >+   u16 flags;
> >+   u16 next;
> >+};
> >+
> >+struct vring_avail
> >+{
> >+   u16 flags;
> >+   u16 idx;
> >+   u16 ring[0];
> >+};
> >+
> >+struct vring_used_elem
> >+{
> >+   u32 id;
> >+   u32 len;
> >+};
> >+
> >+struct vring_used
> >+{
> >+   u16 flags;
> >+   u16 idx;
> >+   struct vring_used_elem ring[];
> >+};
> >+
> >+struct vring {
> >+   unsigned int num;
> >+   struct vring_desc *desc;
> >+   struct vring_avail *avail;
> >+   struct vring_used *used;
> >+};
> >+
> >+#define vring_size(num) \
> >+   (((((sizeof(struct vring_desc) * num) + \
> >+      (sizeof(struct vring_avail) + sizeof(u16) * num)) \
> >+         + PAGE_MASK)&  ~PAGE_MASK) + \
> >+         (sizeof(struct vring_used) + sizeof(struct vring_used_elem) * num))
> >+
> >+typedef unsigned char virtio_queue_t[PAGE_MASK + vring_size(MAX_QUEUE_NUM)];
> >+
> >+struct vring_virtqueue {
> >+   virtio_queue_t queue;
> >+   struct vring vring;
> >+   u16 free_head;
> >+   u16 last_used_idx;
> >+   u16 vdata[MAX_QUEUE_NUM];
> >+   /* PCI */
> >+   int queue_index;
> >+};
> >+
> >+struct vring_list {
> >+  char *addr;
> >+  unsigned int length;
> >+};
> >+
> >+static inline void vring_init(struct vring *vr,
> >+                         unsigned int num, unsigned char *queue)
> >+{
> >+   unsigned int i;
> >+   unsigned long pa;
> >+
> >+   ASSERT32FLAT();
> >+   vr->num = num;
> >+
> >+   /* physical address of desc must be page aligned */
> >+
> >+   pa = virt_to_phys(queue);
> >+   pa = (pa + PAGE_MASK)&  ~PAGE_MASK;
> >+   vr->desc = phys_to_virt(pa);
> >+
> >+   vr->avail = (struct vring_avail *)&vr->desc[num];
> >+
> >+   /* physical address of used must be page aligned */
> >+
> >+   pa = virt_to_phys(&vr->avail->ring[num]);
> >+   pa = (pa + PAGE_MASK)&  ~PAGE_MASK;
> >+   vr->used = phys_to_virt(pa);
> >+
> >+   for (i = 0; i<  num - 1; i++)
> >+           vr->desc[i].next = i + 1;
> >+   vr->desc[i].next = 0;
> >+}
> >+
> >+int vring_more_used(struct vring_virtqueue *vq);
> >+void vring_detach(struct vring_virtqueue *vq, unsigned int head);
> >+int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len);
> >+void vring_add_buf(struct vring_virtqueue *vq, struct vring_list list[],
> >+                   unsigned int out, unsigned int in,
> >+                   int index, int num_added);
> >+void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int 
> >num_added);
> >+
> >+#endif /* _VIRTIO_RING_H_ */
> >
> >--
> >                     Gleb.
> >--
> >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

--
                        Gleb.

Reply via email to