We're pretty sloppy in virtio right now about phys_ram_base assumptions.  This
patch is an incremental step between what we have today and a full blown DMA
API.  I backported the DMA API but the performance impact was not acceptable
to me. There's only a slight performance impact with this particular patch.

Since we're no longer assuming guest physical memory is contiguous, we need
a more complex way to validate the memory regions than just checking if it's
within ram_size.

Signed-off-by: Anthony Liguori <[EMAIL PROTECTED]>

diff --git a/qemu/hw/virtio.c b/qemu/hw/virtio.c
index 6a50001..a4c9d10 100644
--- a/qemu/hw/virtio.c
+++ b/qemu/hw/virtio.c
@@ -58,6 +58,48 @@
 
 /* virt queue functions */
 
+static void *virtio_map_gpa(target_phys_addr_t addr, size_t size)
+{
+    ram_addr_t off;
+    target_phys_addr_t addr1;
+
+    off = cpu_get_physical_page_desc(addr);
+    if ((off & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+       fprintf(stderr, "virtio DMA to IO ram\n");
+       exit(1);
+    }
+
+    off = (off & TARGET_PAGE_MASK) | (addr & ~TARGET_PAGE_MASK);
+
+    for (addr1 = addr + TARGET_PAGE_SIZE;
+        addr1 < TARGET_PAGE_ALIGN(addr + size);
+        addr1 += TARGET_PAGE_SIZE) {
+       ram_addr_t off1;
+
+       off1 = cpu_get_physical_page_desc(addr1);
+       if ((off1 & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+           fprintf(stderr, "virtio DMA to IO ram\n");
+           exit(1);
+       }
+
+       off1 = (off1 & TARGET_PAGE_MASK) | (addr1 & ~TARGET_PAGE_MASK);
+
+       if (off1 != (off + (addr1 - addr))) {
+           fprintf(stderr, "discontigous virtio memory\n");
+           exit(1);
+       }
+    }
+
+    return phys_ram_base + off;
+}
+
+static size_t virtqueue_size(int num)
+{
+    return TARGET_PAGE_ALIGN((sizeof(VRingDesc) * num) +
+                            (sizeof(VRingAvail) + sizeof(uint16_t) * num)) +
+       (sizeof(VRingUsed) + sizeof(VRingUsedElem) * num);
+}
+
 static void virtqueue_init(VirtQueue *vq, void *p)
 {
     vq->vring.desc = p;
@@ -127,9 +169,6 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
     do {
        struct iovec *sg;
 
-       if ((vq->vring.desc[i].addr + vq->vring.desc[i].len) > ram_size)
-           errx(1, "Guest sent invalid pointer");
-
        if (vq->vring.desc[i].flags & VRING_DESC_F_WRITE)
            sg = &elem->in_sg[elem->in_num++];
        else
@@ -137,7 +176,9 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
 
        /* Grab the first descriptor, and check it's OK. */
        sg->iov_len = vq->vring.desc[i].len;
-       sg->iov_base = phys_ram_base + vq->vring.desc[i].addr;
+       sg->iov_base = virtio_map_gpa(vq->vring.desc[i].addr, sg->iov_len);
+       if (sg->iov_base == NULL)
+           errx(1, "Invalid mapping\n");
 
        /* If we've got too many, that implies a descriptor loop. */
        if ((elem->in_num + elem->out_num) > vq->vring.num)
@@ -198,9 +239,10 @@ static void virtio_ioport_write(void *opaque, uint32_t 
addr, uint32_t val)
        vdev->vq[vdev->queue_sel].pfn = val;
        if (pa == 0) {
             virtio_reset(vdev);
-       } else if (pa < (ram_size - TARGET_PAGE_SIZE)) {
-           virtqueue_init(&vdev->vq[vdev->queue_sel], phys_ram_base + pa);
-           /* FIXME if pa == 0, deal with device tear down */
+       } else {
+           size_t size = virtqueue_size(vdev->vq[vdev->queue_sel].vring.num);
+           virtqueue_init(&vdev->vq[vdev->queue_sel],
+                          virtio_map_gpa(pa, size));
        }
        break;
     case VIRTIO_PCI_QUEUE_SEL:

-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference 
Don't miss this year's exciting event. There's still time to save $100. 
Use priority code J8TL2D2. 
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________
kvm-devel mailing list
kvm-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/kvm-devel

Reply via email to