qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Anthony Liguori <aliguori@us.ibm.com>
To: qemu-devel@nongnu.org
Cc: kvm-devel@lists.sourceforge.net,
	Marcelo Tosatti <marcelo@kvack.org>,
	Anthony Liguori <aliguori@us.ibm.com>,
	Aurelien Jarno <aurelien@aurel32.net>
Subject: [Qemu-devel] [PATCH 2/6] PCI DMA API
Date: Sat, 29 Mar 2008 16:55:56 -0500	[thread overview]
Message-ID: <1206827760-4566-2-git-send-email-aliguori@us.ibm.com> (raw)
In-Reply-To: <1206827760-4566-1-git-send-email-aliguori@us.ibm.com>

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 <aliguori@us.ibm.com>

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   <aliguori@us.ibm.com>
+ *
+ * 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   <aliguori@us.ibm.com>
+ *
+ * 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	other threads:[~2008-03-29 21:58 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-03-29 21:55 [Qemu-devel] [PATCH 1/6] Use ram_addr_t for cpu_get_physical_page_desc Anthony Liguori
2008-03-29 21:55 ` Anthony Liguori [this message]
2008-03-30  7:06   ` [Qemu-devel] [PATCH 2/6] PCI DMA API Blue Swirl
2008-03-30 14:44     ` Anthony Liguori
2008-03-30 14:49       ` Avi Kivity
2008-03-30 14:56         ` [kvm-devel] " Anthony Liguori
2008-03-30 14:58       ` Blue Swirl
2008-03-30 15:11         ` Anthony Liguori
2008-03-30 10:18   ` Paul Brook
2008-03-30 14:42     ` Anthony Liguori
2008-03-30 18:19       ` Paul Brook
2008-03-30 19:02         ` Anthony Liguori
2008-03-30 10:25   ` [Qemu-devel] Re: [kvm-devel] " Avi Kivity
2008-03-30 14:49     ` Anthony Liguori
2008-03-29 21:55 ` [Qemu-devel] [PATCH 3/6] virtio for QEMU Anthony Liguori
2008-03-30 17:25   ` Dor Laor
2008-03-30 22:59     ` [kvm-devel] " Anthony Liguori
2008-04-05  3:09     ` Anthony Liguori
2008-03-29 21:55 ` [Qemu-devel] [PATCH 4/6] virtio network driver Anthony Liguori
2008-03-30 10:27   ` Paul Brook
2008-03-30 14:47     ` Anthony Liguori
2008-03-29 21:55 ` [Qemu-devel] [PATCH 5/6] virtio block driver Anthony Liguori
2008-03-29 21:56 ` [Qemu-devel] [PATCH 6/6] virtio balloon driver Anthony Liguori

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1206827760-4566-2-git-send-email-aliguori@us.ibm.com \
    --to=aliguori@us.ibm.com \
    --cc=aurelien@aurel32.net \
    --cc=kvm-devel@lists.sourceforge.net \
    --cc=marcelo@kvack.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).