From mboxrd@z Thu Jan 1 00:00:00 1970 From: "Michael S. Tsirkin" Subject: Re: [PATCH 01/13] Generic DMA memory access interface Date: Sun, 6 Feb 2011 13:16:52 +0200 Message-ID: <20110206111651.GD26242@redhat.com> References: <7ec6f2018811566a4b207c4f5b8d7b8b7342b786.1296321798.git.eduard.munteanu@linux360.ro> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Cc: kvm@vger.kernel.org, joro@8bytes.org, seabios@seabios.org, qemu-devel@nongnu.org, blauwirbel@gmail.com, yamahata@valinux.co.jp, av1474@comtv.ru, avi@redhat.com, paul@codesourcery.com To: Eduard - Gabriel Munteanu Return-path: Content-Disposition: inline In-Reply-To: <7ec6f2018811566a4b207c4f5b8d7b8b7342b786.1296321798.git.eduard.munteanu@linux360.ro> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: seabios-bounces@seabios.org Sender: seabios-bounces@seabios.org List-Id: kvm.vger.kernel.org On Fri, Feb 04, 2011 at 01:32:55AM +0200, Eduard - Gabriel Munteanu wrote: > This introduces replacements for memory access functions like > cpu_physical_memory_read(). The new interface can handle address > translation and access checking through an IOMMU. > > Signed-off-by: Eduard - Gabriel Munteanu > --- > Makefile.target | 2 +- > hw/dma_rw.c | 124 +++++++++++++++++++++++++++++++++++++++++++ > hw/dma_rw.h | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 282 insertions(+), 1 deletions(-) > create mode 100644 hw/dma_rw.c > create mode 100644 hw/dma_rw.h > > diff --git a/Makefile.target b/Makefile.target > index e15b1c4..e5817ab 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -218,7 +218,7 @@ obj-i386-y += cirrus_vga.o apic.o ioapic.o piix_pci.o > obj-i386-y += vmmouse.o vmport.o hpet.o applesmc.o > obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o > obj-i386-y += debugcon.o multiboot.o > -obj-i386-y += pc_piix.o > +obj-i386-y += pc_piix.o dma_rw.o Does this need to be target specific? > obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o > > # shared objects > diff --git a/hw/dma_rw.c b/hw/dma_rw.c > new file mode 100644 > index 0000000..ef8e7f8 > --- /dev/null > +++ b/hw/dma_rw.c > @@ -0,0 +1,124 @@ > +/* > + * Generic DMA memory access interface. > + * > + * Copyright (c) 2011 Eduard - Gabriel Munteanu > + * > + * Permission is hereby granted, free of charge, to any person obtaining a copy > + * of this software and associated documentation files (the "Software"), to deal > + * in the Software without restriction, including without limitation the rights > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell > + * copies of the Software, and to permit persons to whom the Software is > + * furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be included in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN > + * THE SOFTWARE. > + */ > + > +#include "dma_rw.h" > +#include "range.h" > + > +static void dma_register_memory_map(DMADevice *dev, > + dma_addr_t addr, > + dma_addr_t len, > + target_phys_addr_t paddr, > + DMAInvalidateMapFunc *invalidate, > + void *invalidate_opaque) > +{ > + DMAMemoryMap *map; > + > + map = qemu_malloc(sizeof(DMAMemoryMap)); > + map->addr = addr; > + map->len = len; > + map->paddr = paddr; > + map->invalidate = invalidate; > + map->invalidate_opaque = invalidate_opaque; > + > + QLIST_INSERT_HEAD(&dev->mmu->memory_maps, map, list); > +} > + > +static void dma_unregister_memory_map(DMADevice *dev, > + target_phys_addr_t paddr, > + dma_addr_t len) > +{ > + DMAMemoryMap *map; > + > + QLIST_FOREACH(map, &dev->mmu->memory_maps, list) { > + if (map->paddr == paddr && map->len == len) { > + QLIST_REMOVE(map, list); > + free(map); > + } > + } > +} > + > +void dma_invalidate_memory_range(DMADevice *dev, > + dma_addr_t addr, > + dma_addr_t len) > +{ > + DMAMemoryMap *map; > + > + QLIST_FOREACH(map, &dev->mmu->memory_maps, list) { > + if (ranges_overlap(addr, len, map->addr, map->len)) { > + map->invalidate(map->invalidate_opaque); > + QLIST_REMOVE(map, list); > + free(map); > + } > + } > +} > + > +void *dma_memory_map(DMADevice *dev, > + DMAInvalidateMapFunc *cb, > + void *opaque, > + dma_addr_t addr, > + dma_addr_t *len, > + int is_write) > +{ > + int err; > + target_phys_addr_t paddr, plen; > + > + if (!dev || !dev->mmu) { > + return cpu_physical_memory_map(addr, len, is_write); > + } > + > + plen = *len; > + err = dev->mmu->translate(dev, addr, &paddr, &plen, is_write); > + if (err) { > + return NULL; > + } > + > + /* > + * If this is true, the virtual region is contiguous, > + * but the translated physical region isn't. We just > + * clamp *len, much like cpu_physical_memory_map() does. > + */ > + if (plen < *len) { > + *len = plen; > + } > + > + /* We treat maps as remote TLBs to cope with stuff like AIO. */ > + if (cb) { > + dma_register_memory_map(dev, addr, *len, paddr, cb, opaque); > + } > + > + return cpu_physical_memory_map(paddr, len, is_write); > +} > + > +void dma_memory_unmap(DMADevice *dev, > + void *buffer, > + dma_addr_t len, > + int is_write, > + dma_addr_t access_len) > +{ > + cpu_physical_memory_unmap(buffer, len, is_write, access_len); > + if (dev && dev->mmu) { > + dma_unregister_memory_map(dev, (target_phys_addr_t) buffer, len); > + } > +} > + > diff --git a/hw/dma_rw.h b/hw/dma_rw.h Can we have a configure option to disable this at compile time? Add stubs to avoid propagating ifdefs all over the code. > new file mode 100644 > index 0000000..bc93511 > --- /dev/null > +++ b/hw/dma_rw.h > @@ -0,0 +1,157 @@ > +#ifndef DMA_RW_H > +#define DMA_RW_H > + > +#include "qemu-common.h" > + > +typedef uint64_t dma_addr_t; > + > +typedef struct DMAMmu DMAMmu; > +typedef struct DMADevice DMADevice; > +typedef struct DMAMemoryMap DMAMemoryMap; > + > +typedef int DMATranslateFunc(DMADevice *dev, > + dma_addr_t addr, > + dma_addr_t *paddr, > + dma_addr_t *len, > + int is_write); > + > +typedef void DMAInvalidateMapFunc(void *); > + > +struct DMAMmu { > + DeviceState *iommu; > + DMATranslateFunc *translate; > + QLIST_HEAD(memory_maps, DMAMemoryMap) memory_maps; > +}; > + > +struct DMADevice { > + DMAMmu *mmu; > +}; > + > +struct DMAMemoryMap { > + dma_addr_t addr; > + dma_addr_t len; > + target_phys_addr_t paddr; > + DMAInvalidateMapFunc *invalidate; > + void *invalidate_opaque; > + > + QLIST_ENTRY(DMAMemoryMap) list; > +}; > + > +static inline void dma_memory_rw(DMADevice *dev, > + dma_addr_t addr, > + void *buf, > + dma_addr_t len, > + int is_write) > +{ > + dma_addr_t paddr, plen; > + int err; > + > + /* > + * Fast-path non-iommu. > + * More importantly, makes it obvious what this function does. > + */ > + if (!dev || !dev->mmu) { > + cpu_physical_memory_rw(addr, buf, plen, is_write); > + return; > + } > + > + while (len) { > + err = dev->mmu->translate(dev, addr, &paddr, &plen, is_write); > + if (err) { > + return; > + } > + > + /* The translation might be valid for larger regions. */ > + if (plen > len) { > + plen = len; > + } > + > + cpu_physical_memory_rw(paddr, buf, plen, is_write); > + > + len -= plen; > + addr += plen; > + buf += plen; > + } > +} > + > +static inline void dma_memory_read(DMADevice *dev, > + dma_addr_t addr, > + void *buf, > + dma_addr_t len) > +{ > + dma_memory_rw(dev, addr, buf, len, 0); > +} > + > +static inline void dma_memory_write(DMADevice *dev, > + dma_addr_t addr, > + const void *buf, > + dma_addr_t len) > +{ > + dma_memory_rw(dev, addr, (void *) buf, len, 1); > +} > + > +void *dma_memory_map(DMADevice *dev, > + DMAInvalidateMapFunc *cb, > + void *opaque, > + dma_addr_t addr, > + dma_addr_t *len, > + int is_write); > +void dma_memory_unmap(DMADevice *dev, > + void *buffer, > + dma_addr_t len, > + int is_write, > + dma_addr_t access_len); > + > + > +void dma_invalidate_memory_range(DMADevice *dev, > + dma_addr_t addr, > + dma_addr_t len); > + > + > +#define DEFINE_DMA_LD(suffix, size) \ > +static inline uint##size##_t \ > +dma_ld##suffix(DMADevice *dev, dma_addr_t addr) \ > +{ \ > + int err; \ > + dma_addr_t paddr, plen; \ > + \ > + if (!dev || !dev->mmu) { \ > + return ld##suffix##_phys(addr); \ > + } \ > + \ > + err = dev->mmu->translate(dev, addr, &paddr, &plen, 0); \ > + if (err || (plen < size / 8)) \ > + return 0; \ > + \ > + return ld##suffix##_phys(paddr); \ > +} > + > +#define DEFINE_DMA_ST(suffix, size) \ > +static inline void \ > +dma_st##suffix(DMADevice *dev, dma_addr_t addr, uint##size##_t val) \ > +{ \ > + int err; \ > + target_phys_addr_t paddr, plen; \ > + \ > + if (!dev || !dev->mmu) { \ > + st##suffix##_phys(addr, val); \ > + return; \ > + } \ > + err = dev->mmu->translate(dev, addr, &paddr, &plen, 1); \ > + if (err || (plen < size / 8)) \ > + return; \ > + \ > + st##suffix##_phys(paddr, val); \ > +} > + > +DEFINE_DMA_LD(ub, 8) > +DEFINE_DMA_LD(uw, 16) > +DEFINE_DMA_LD(l, 32) > +DEFINE_DMA_LD(q, 64) > + > +DEFINE_DMA_ST(b, 8) > +DEFINE_DMA_ST(w, 16) > +DEFINE_DMA_ST(l, 32) > +DEFINE_DMA_ST(q, 64) > + > +#endif > -- > 1.7.3.4 From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from [140.186.70.92] (port=59060 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Pm2cX-0005S6-4v for qemu-devel@nongnu.org; Sun, 06 Feb 2011 06:17:38 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Pm2cV-0001XF-Cn for qemu-devel@nongnu.org; Sun, 06 Feb 2011 06:17:32 -0500 Received: from mx1.redhat.com ([209.132.183.28]:24472) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Pm2cV-0001Wy-28 for qemu-devel@nongnu.org; Sun, 06 Feb 2011 06:17:31 -0500 Date: Sun, 6 Feb 2011 13:16:52 +0200 From: "Michael S. Tsirkin" Message-ID: <20110206111651.GD26242@redhat.com> References: <7ec6f2018811566a4b207c4f5b8d7b8b7342b786.1296321798.git.eduard.munteanu@linux360.ro> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <7ec6f2018811566a4b207c4f5b8d7b8b7342b786.1296321798.git.eduard.munteanu@linux360.ro> Subject: [Qemu-devel] Re: [PATCH 01/13] Generic DMA memory access interface List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Eduard - Gabriel Munteanu Cc: kvm@vger.kernel.org, joro@8bytes.org, seabios@seabios.org, qemu-devel@nongnu.org, blauwirbel@gmail.com, yamahata@valinux.co.jp, kevin@koconnor.net, avi@redhat.com, paul@codesourcery.com On Fri, Feb 04, 2011 at 01:32:55AM +0200, Eduard - Gabriel Munteanu wrote: > This introduces replacements for memory access functions like > cpu_physical_memory_read(). The new interface can handle address > translation and access checking through an IOMMU. > > Signed-off-by: Eduard - Gabriel Munteanu > --- > Makefile.target | 2 +- > hw/dma_rw.c | 124 +++++++++++++++++++++++++++++++++++++++++++ > hw/dma_rw.h | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 282 insertions(+), 1 deletions(-) > create mode 100644 hw/dma_rw.c > create mode 100644 hw/dma_rw.h > > diff --git a/Makefile.target b/Makefile.target > index e15b1c4..e5817ab 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -218,7 +218,7 @@ obj-i386-y += cirrus_vga.o apic.o ioapic.o piix_pci.o > obj-i386-y += vmmouse.o vmport.o hpet.o applesmc.o > obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o > obj-i386-y += debugcon.o multiboot.o > -obj-i386-y += pc_piix.o > +obj-i386-y += pc_piix.o dma_rw.o Does this need to be target specific? > obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o > > # shared objects > diff --git a/hw/dma_rw.c b/hw/dma_rw.c > new file mode 100644 > index 0000000..ef8e7f8 > --- /dev/null > +++ b/hw/dma_rw.c > @@ -0,0 +1,124 @@ > +/* > + * Generic DMA memory access interface. > + * > + * Copyright (c) 2011 Eduard - Gabriel Munteanu > + * > + * Permission is hereby granted, free of charge, to any person obtaining a copy > + * of this software and associated documentation files (the "Software"), to deal > + * in the Software without restriction, including without limitation the rights > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell > + * copies of the Software, and to permit persons to whom the Software is > + * furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be included in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN > + * THE SOFTWARE. > + */ > + > +#include "dma_rw.h" > +#include "range.h" > + > +static void dma_register_memory_map(DMADevice *dev, > + dma_addr_t addr, > + dma_addr_t len, > + target_phys_addr_t paddr, > + DMAInvalidateMapFunc *invalidate, > + void *invalidate_opaque) > +{ > + DMAMemoryMap *map; > + > + map = qemu_malloc(sizeof(DMAMemoryMap)); > + map->addr = addr; > + map->len = len; > + map->paddr = paddr; > + map->invalidate = invalidate; > + map->invalidate_opaque = invalidate_opaque; > + > + QLIST_INSERT_HEAD(&dev->mmu->memory_maps, map, list); > +} > + > +static void dma_unregister_memory_map(DMADevice *dev, > + target_phys_addr_t paddr, > + dma_addr_t len) > +{ > + DMAMemoryMap *map; > + > + QLIST_FOREACH(map, &dev->mmu->memory_maps, list) { > + if (map->paddr == paddr && map->len == len) { > + QLIST_REMOVE(map, list); > + free(map); > + } > + } > +} > + > +void dma_invalidate_memory_range(DMADevice *dev, > + dma_addr_t addr, > + dma_addr_t len) > +{ > + DMAMemoryMap *map; > + > + QLIST_FOREACH(map, &dev->mmu->memory_maps, list) { > + if (ranges_overlap(addr, len, map->addr, map->len)) { > + map->invalidate(map->invalidate_opaque); > + QLIST_REMOVE(map, list); > + free(map); > + } > + } > +} > + > +void *dma_memory_map(DMADevice *dev, > + DMAInvalidateMapFunc *cb, > + void *opaque, > + dma_addr_t addr, > + dma_addr_t *len, > + int is_write) > +{ > + int err; > + target_phys_addr_t paddr, plen; > + > + if (!dev || !dev->mmu) { > + return cpu_physical_memory_map(addr, len, is_write); > + } > + > + plen = *len; > + err = dev->mmu->translate(dev, addr, &paddr, &plen, is_write); > + if (err) { > + return NULL; > + } > + > + /* > + * If this is true, the virtual region is contiguous, > + * but the translated physical region isn't. We just > + * clamp *len, much like cpu_physical_memory_map() does. > + */ > + if (plen < *len) { > + *len = plen; > + } > + > + /* We treat maps as remote TLBs to cope with stuff like AIO. */ > + if (cb) { > + dma_register_memory_map(dev, addr, *len, paddr, cb, opaque); > + } > + > + return cpu_physical_memory_map(paddr, len, is_write); > +} > + > +void dma_memory_unmap(DMADevice *dev, > + void *buffer, > + dma_addr_t len, > + int is_write, > + dma_addr_t access_len) > +{ > + cpu_physical_memory_unmap(buffer, len, is_write, access_len); > + if (dev && dev->mmu) { > + dma_unregister_memory_map(dev, (target_phys_addr_t) buffer, len); > + } > +} > + > diff --git a/hw/dma_rw.h b/hw/dma_rw.h Can we have a configure option to disable this at compile time? Add stubs to avoid propagating ifdefs all over the code. > new file mode 100644 > index 0000000..bc93511 > --- /dev/null > +++ b/hw/dma_rw.h > @@ -0,0 +1,157 @@ > +#ifndef DMA_RW_H > +#define DMA_RW_H > + > +#include "qemu-common.h" > + > +typedef uint64_t dma_addr_t; > + > +typedef struct DMAMmu DMAMmu; > +typedef struct DMADevice DMADevice; > +typedef struct DMAMemoryMap DMAMemoryMap; > + > +typedef int DMATranslateFunc(DMADevice *dev, > + dma_addr_t addr, > + dma_addr_t *paddr, > + dma_addr_t *len, > + int is_write); > + > +typedef void DMAInvalidateMapFunc(void *); > + > +struct DMAMmu { > + DeviceState *iommu; > + DMATranslateFunc *translate; > + QLIST_HEAD(memory_maps, DMAMemoryMap) memory_maps; > +}; > + > +struct DMADevice { > + DMAMmu *mmu; > +}; > + > +struct DMAMemoryMap { > + dma_addr_t addr; > + dma_addr_t len; > + target_phys_addr_t paddr; > + DMAInvalidateMapFunc *invalidate; > + void *invalidate_opaque; > + > + QLIST_ENTRY(DMAMemoryMap) list; > +}; > + > +static inline void dma_memory_rw(DMADevice *dev, > + dma_addr_t addr, > + void *buf, > + dma_addr_t len, > + int is_write) > +{ > + dma_addr_t paddr, plen; > + int err; > + > + /* > + * Fast-path non-iommu. > + * More importantly, makes it obvious what this function does. > + */ > + if (!dev || !dev->mmu) { > + cpu_physical_memory_rw(addr, buf, plen, is_write); > + return; > + } > + > + while (len) { > + err = dev->mmu->translate(dev, addr, &paddr, &plen, is_write); > + if (err) { > + return; > + } > + > + /* The translation might be valid for larger regions. */ > + if (plen > len) { > + plen = len; > + } > + > + cpu_physical_memory_rw(paddr, buf, plen, is_write); > + > + len -= plen; > + addr += plen; > + buf += plen; > + } > +} > + > +static inline void dma_memory_read(DMADevice *dev, > + dma_addr_t addr, > + void *buf, > + dma_addr_t len) > +{ > + dma_memory_rw(dev, addr, buf, len, 0); > +} > + > +static inline void dma_memory_write(DMADevice *dev, > + dma_addr_t addr, > + const void *buf, > + dma_addr_t len) > +{ > + dma_memory_rw(dev, addr, (void *) buf, len, 1); > +} > + > +void *dma_memory_map(DMADevice *dev, > + DMAInvalidateMapFunc *cb, > + void *opaque, > + dma_addr_t addr, > + dma_addr_t *len, > + int is_write); > +void dma_memory_unmap(DMADevice *dev, > + void *buffer, > + dma_addr_t len, > + int is_write, > + dma_addr_t access_len); > + > + > +void dma_invalidate_memory_range(DMADevice *dev, > + dma_addr_t addr, > + dma_addr_t len); > + > + > +#define DEFINE_DMA_LD(suffix, size) \ > +static inline uint##size##_t \ > +dma_ld##suffix(DMADevice *dev, dma_addr_t addr) \ > +{ \ > + int err; \ > + dma_addr_t paddr, plen; \ > + \ > + if (!dev || !dev->mmu) { \ > + return ld##suffix##_phys(addr); \ > + } \ > + \ > + err = dev->mmu->translate(dev, addr, &paddr, &plen, 0); \ > + if (err || (plen < size / 8)) \ > + return 0; \ > + \ > + return ld##suffix##_phys(paddr); \ > +} > + > +#define DEFINE_DMA_ST(suffix, size) \ > +static inline void \ > +dma_st##suffix(DMADevice *dev, dma_addr_t addr, uint##size##_t val) \ > +{ \ > + int err; \ > + target_phys_addr_t paddr, plen; \ > + \ > + if (!dev || !dev->mmu) { \ > + st##suffix##_phys(addr, val); \ > + return; \ > + } \ > + err = dev->mmu->translate(dev, addr, &paddr, &plen, 1); \ > + if (err || (plen < size / 8)) \ > + return; \ > + \ > + st##suffix##_phys(paddr, val); \ > +} > + > +DEFINE_DMA_LD(ub, 8) > +DEFINE_DMA_LD(uw, 16) > +DEFINE_DMA_LD(l, 32) > +DEFINE_DMA_LD(q, 64) > + > +DEFINE_DMA_ST(b, 8) > +DEFINE_DMA_ST(w, 16) > +DEFINE_DMA_ST(l, 32) > +DEFINE_DMA_ST(q, 64) > + > +#endif > -- > 1.7.3.4