qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 2/6] PCI DMA API


From: Anthony Liguori
Subject: [Qemu-devel] [PATCH 2/6] PCI DMA API
Date: Sat, 29 Mar 2008 16:55:56 -0500

This patch introduces a PCI DMA API and some generic code to support other DMA
APIs.  Two types are introduced: PhysIOVector and IOVector.  A DMA API
maps a PhysIOVector, which is composed of target_phys_addr_t, into an IOVector,
which is composed of void *.

This enables zero-copy IO to be preformed without introducing assumptions of
phys_ram_base.  This API is at the PCI device level to enable support of
per-device IOMMU remapping.

Signed-off-by: Anthony Liguori <address@hidden>

diff --git a/Makefile.target b/Makefile.target
index 5ac29a7..94f3e58 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -173,7 +173,7 @@ all: $(PROGS)
 #########################################################
 # cpu emulator library
 LIBOBJS=exec.o kqemu.o translate-all.o cpu-exec.o\
-        translate.o host-utils.o
+        translate.o host-utils.o iovector.o
 ifndef CONFIG_NO_DYNGEN_OP
 LIBOBJS+=op.o
 endif
diff --git a/cpu-all.h b/cpu-all.h
index 9e5d33b..23b0a11 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -24,6 +24,8 @@
 #define WORDS_ALIGNED
 #endif
 
+#include "iovector.h"
+
 /* some important defines:
  *
  * WORDS_ALIGNED : if defined, the host cpu can only make word aligned
@@ -835,6 +837,8 @@ void cpu_register_physical_memory(target_phys_addr_t 
start_addr,
                                   unsigned long size,
                                   unsigned long phys_offset);
 ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr);
+IOVector *cpu_translate_physical_page_vector(PhysIOVector *phys);
+void cpu_physical_page_vector_set_dirty(PhysIOVector *phys);
 ram_addr_t qemu_ram_alloc(unsigned int size);
 void qemu_ram_free(ram_addr_t addr);
 int cpu_register_io_memory(int io_index,
diff --git a/exec.c b/exec.c
index c25872d..4b4b1a9 100644
--- a/exec.c
+++ b/exec.c
@@ -2085,6 +2085,65 @@ ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t 
addr)
     return p->phys_offset;
 }
 
+IOVector *cpu_translate_physical_page_vector(PhysIOVector *phys)
+{
+    unsigned int i;
+    IOVector *virt;
+
+    /* QEMU represents guest physical memory as virtually contiguous so we
+       never should need more IOVector's than PhysIOVectors */
+
+    virt = qemu_malloc(sizeof(IOVector) + phys->num * sizeof(IOVectorElement));
+
+    virt->num = phys->num;
+    for (i = 0; i < phys->num; i++) {
+       ram_addr_t base_offset = 0;
+       ram_addr_t offset;
+
+       /* we need to check that the guest is trying to DMA to somewhere they
+          shouldn't */
+       for (offset = 0; offset < phys->sg[i].len; offset += TARGET_PAGE_SIZE){
+           ram_addr_t phys_offset;
+
+           /* DMA'ing to MMIO, just skip */
+           phys_offset = cpu_get_physical_page_desc(phys->sg[i].base);
+           if ((phys_offset & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+               fprintf(stderr, "dma'ing to non-RAM region\n");
+               qemu_free(virt);
+               return NULL;
+           }
+
+           phys_offset &= TARGET_PAGE_MASK;
+           phys_offset += phys->sg[i].base & ~TARGET_PAGE_MASK;
+
+           if (offset == 0)
+               base_offset = phys_offset;
+           else if ((phys_offset - base_offset) != offset) {
+               fprintf(stderr, "bug: discontiguous guest memory?\n");
+               qemu_free(virt);
+               return NULL;
+           }
+       }
+
+       virt->sg[i].base = phys_ram_base + base_offset;
+       virt->sg[i].len = phys->sg[i].len;
+    }
+
+    return virt;
+}
+
+void cpu_physical_page_vector_set_dirty(PhysIOVector *phys)
+{
+    int i;
+
+    for (i = 0; i < phys->num; i++) {
+       ram_addr_t offset;
+       for (offset = 0; offset < phys->sg[i].len;
+            offset += TARGET_PAGE_SIZE)
+           cpu_physical_memory_set_dirty(phys->sg[i].base + offset);
+    }
+}
+
 /* XXX: better than nothing */
 ram_addr_t qemu_ram_alloc(unsigned int size)
 {
diff --git a/hw/pci.c b/hw/pci.c
index bc55989..99c206f 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -145,6 +145,20 @@ int pci_device_load(PCIDevice *s, QEMUFile *f)
     return 0;
 }
 
+IOVector *pci_device_dma_map(PCIDevice *s, PhysIOVector *phys)
+{
+    return cpu_translate_physical_page_vector(phys);
+}
+
+void pci_device_dma_unmap(PCIDevice *s, PhysIOVector *phys, IOVector *virt,
+                         int write)
+{
+    /* mark memory as dirty if necessary */
+    if (write)
+       cpu_physical_page_vector_set_dirty(phys);
+    qemu_free(virt);
+}
+
 /* -1 for devfn means auto assign */
 PCIDevice *pci_register_device(PCIBus *bus, const char *name,
                                int instance_size, int devfn,
diff --git a/hw/pci.h b/hw/pci.h
index e870987..b965919 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -81,6 +81,10 @@ void pci_default_write_config(PCIDevice *d,
 void pci_device_save(PCIDevice *s, QEMUFile *f);
 int pci_device_load(PCIDevice *s, QEMUFile *f);
 
+IOVector *pci_device_dma_map(PCIDevice *s, PhysIOVector *phys);
+void pci_device_dma_unmap(PCIDevice *s, PhysIOVector *phys, IOVector *virt,
+                         int write);
+
 typedef void (*pci_set_irq_fn)(qemu_irq *pic, int irq_num, int level);
 typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num);
 PCIBus *pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
diff --git a/iovector.c b/iovector.c
new file mode 100644
index 0000000..432b483
--- /dev/null
+++ b/iovector.c
@@ -0,0 +1,121 @@
+/*
+ * IO Vectors
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ *  Anthony Liguori   <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "iovector.h"
+
+static size_t iovector_rw(void *buffer, size_t size, IOVector *iov, int read)
+{
+    uint8_t *ptr = buffer;
+    size_t offset = 0;
+    int i;
+
+    for (i = 0; i < iov->num; i++) {
+       size_t len;
+
+       len = MIN(iov->sg[i].len, size - offset);
+
+       if (read)
+           memcpy(ptr + offset, iov->sg[i].base, len);
+       else
+           memcpy(iov->sg[i].base, ptr + offset, len);
+
+       offset += len;
+    }
+
+    return offset;
+}
+
+size_t memcpy_from_iovector(void *buffer, size_t offset, size_t size,
+                           const IOVector *iov)
+{
+    IOVector *sg;
+    size_t len;
+
+    if (offset)
+       sg = iovector_trim(iov, offset, size);
+    else
+       sg = (IOVector *)iov;
+
+    len = iovector_rw(buffer, size, sg, 1);
+
+    if (offset)
+       qemu_free(sg);
+
+    return len;
+}
+
+size_t memcpy_to_iovector(const void *buffer, size_t offset, size_t size,
+                         IOVector *iov)
+{
+    IOVector *sg;
+    size_t len;
+
+    if (offset)
+       sg = iovector_trim(iov, offset, size);
+    else
+       sg = iov;
+
+    len = iovector_rw((void *)buffer, size, sg, 0);
+
+    if (offset)
+       qemu_free(sg);
+
+    return len;
+}
+
+IOVector *iovector_trim(const IOVector *iov, size_t offset, size_t size)
+{
+    IOVector *ret;
+    size_t off, total_size;
+    int i;
+
+    ret = qemu_malloc(sizeof(IOVector) + sizeof(IOVectorElement) * iov->num);
+    if (ret == NULL)
+       return NULL;
+
+    total_size = 0;
+    ret->num = 0;
+    off = 0;
+    for (i = 0; i < iov->num; i++) {
+       if (off >= offset || offset < (off + iov->sg[i].len)) {
+           size_t fudge = 0;
+           if (off < offset)
+               fudge = offset - off;
+
+           ret->sg[ret->num].base = iov->sg[i].base + fudge;
+           ret->sg[ret->num].len = MIN(iov->sg[i].len - fudge,
+                                       size - total_size);
+           total_size += ret->sg[ret->num].len;
+           ret->num++;
+
+           if (total_size == size)
+               break;
+       }
+
+       off += iov->sg[i].len;
+    }
+
+    return ret;
+}
+
+size_t iovector_size(const IOVector *iov)
+{
+    size_t size = 0;
+    int i;
+
+    for (i = 0; i < iov->num; i++)
+       size += iov->sg[i].len;
+    
+    return size;
+}
diff --git a/iovector.h b/iovector.h
new file mode 100644
index 0000000..042ea3a
--- /dev/null
+++ b/iovector.h
@@ -0,0 +1,49 @@
+/*
+ * IO Vectors
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ *  Anthony Liguori   <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef _QEMU_IOVECTOR_H
+#define _QEMU_IOVECTOR_H
+
+typedef struct IOVectorElement IOVectorElement;
+
+typedef struct PhysIOVectorElement PhysIOVectorElement;
+
+typedef struct IOVector
+{
+    int num;
+    struct IOVectorElement {
+       void *base;
+       size_t len;
+    } sg[0];
+} IOVector;
+
+typedef struct PhysIOVector
+{
+    int num;
+    struct PhysIOVectorElement {
+       target_phys_addr_t base;
+       size_t len;
+    } sg[0];
+} PhysIOVector;
+
+size_t memcpy_from_iovector(void *buffer, size_t offset, size_t size,
+                           const IOVector *iov);
+
+size_t memcpy_to_iovector(const void *buffer, size_t offset, size_t size,
+                         IOVector *iov);
+
+IOVector *iovector_trim(const IOVector *iov, size_t offset, size_t size);
+
+size_t iovector_size(const IOVector *iov);
+
+#endif




reply via email to

[Prev in Thread] Current Thread [Next in Thread]