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