From: "Michael S. Tsirkin" <mst@redhat.com>
To: Eduard - Gabriel Munteanu <eduard.munteanu@linux360.ro>
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
Subject: [Qemu-devel] Re: [PATCH 01/13] Generic DMA memory access interface
Date: Sun, 6 Feb 2011 13:13:05 +0200 [thread overview]
Message-ID: <20110206111305.GB26242@redhat.com> (raw)
In-Reply-To: <7ec6f2018811566a4b207c4f5b8d7b8b7342b786.1296321798.git.eduard.munteanu@linux360.ro>
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 <eduard.munteanu@linux360.ro>
> ---
> 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
> 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
> 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);
So len is in/out here which is a bit confusing,
and apparently not documented until you look at the usage.
I also don't think it needs to be dma_addr_t - it's not
an address. I don't believe we ever need to
translate more than 2G in one go: how about returning
the length on success, negative on error?
Or add a comment.
> +
> +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
I am guessing the assumption is that address is size-aligned
(which is right) so translation will fail for all addresses
or pass for all of them. But in that case,
assert() is better?
> --
> 1.7.3.4
next prev parent reply other threads:[~2011-02-06 11:13 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-01-29 17:40 [Qemu-devel] [PATCH 00/13] AMD IOMMU emulation patchset Eduard - Gabriel Munteanu
2011-01-29 17:40 ` [Qemu-devel] [PATCH 01/13] Generic DMA memory access interface Eduard - Gabriel Munteanu
2011-02-05 10:20 ` [Qemu-devel] " Blue Swirl
2011-02-06 11:13 ` Michael S. Tsirkin [this message]
2011-02-06 11:16 ` Michael S. Tsirkin
2011-01-29 17:40 ` [Qemu-devel] [PATCH 02/13] pci: add IOMMU support via the generic DMA layer Eduard - Gabriel Munteanu
2011-01-29 17:40 ` [Qemu-devel] [PATCH 03/13] AMD IOMMU emulation Eduard - Gabriel Munteanu
2011-02-06 10:54 ` [Qemu-devel] " Michael S. Tsirkin
2011-01-29 17:40 ` [Qemu-devel] [PATCH 04/13] ide: use the DMA memory access interface for PCI IDE controllers Eduard - Gabriel Munteanu
2011-02-06 11:14 ` [Qemu-devel] " Michael S. Tsirkin
2011-01-29 17:40 ` [Qemu-devel] [PATCH 05/13] rtl8139: use the DMA memory access interface Eduard - Gabriel Munteanu
2011-01-29 17:40 ` [Qemu-devel] [PATCH 06/13] eepro100: " Eduard - Gabriel Munteanu
2011-01-29 17:40 ` [Qemu-devel] [PATCH 07/13] ac97: " Eduard - Gabriel Munteanu
2011-01-29 17:40 ` [Qemu-devel] [PATCH 08/13] es1370: " Eduard - Gabriel Munteanu
2011-01-29 17:40 ` [Qemu-devel] [PATCH 09/13] e1000: " Eduard - Gabriel Munteanu
2011-01-29 17:40 ` [Qemu-devel] [PATCH 10/13] lsi53c895a: " Eduard - Gabriel Munteanu
2011-01-29 17:40 ` [Qemu-devel] [PATCH 11/13] pcnet: " Eduard - Gabriel Munteanu
2011-01-29 17:40 ` [Qemu-devel] [PATCH 12/13] usb-uhci: " Eduard - Gabriel Munteanu
2011-01-29 17:40 ` [Qemu-devel] [PATCH 13/13] usb-ohci: " Eduard - Gabriel Munteanu
2011-01-29 20:19 ` [Qemu-devel] Re: [PATCH 00/13] AMD IOMMU emulation patchset malc
2011-02-03 23:24 ` [Qemu-devel] [PATCH 0/3] SeaBIOS AMD IOMMU initialization patches Eduard - Gabriel Munteanu
2011-02-03 23:24 ` [Qemu-devel] [PATCH 1/3] pci: add pci_find_capability() helper Eduard - Gabriel Munteanu
2011-02-03 23:24 ` [Qemu-devel] [PATCH 2/3] AMD IOMMU support Eduard - Gabriel Munteanu
2011-02-04 2:37 ` [Qemu-devel] " Isaku Yamahata
2011-02-06 11:47 ` Michael S. Tsirkin
2011-02-06 13:41 ` Eduard - Gabriel Munteanu
2011-02-06 15:22 ` Michael S. Tsirkin
2011-02-03 23:24 ` [Qemu-devel] [PATCH 3/3] Clarify address space layout Eduard - Gabriel Munteanu
2011-02-05 13:07 ` [Qemu-devel] Re: [PATCH 00/13] AMD IOMMU emulation patchset (reworked cc/to) Blue Swirl
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=20110206111305.GB26242@redhat.com \
--to=mst@redhat.com \
--cc=avi@redhat.com \
--cc=blauwirbel@gmail.com \
--cc=eduard.munteanu@linux360.ro \
--cc=joro@8bytes.org \
--cc=kevin@koconnor.net \
--cc=kvm@vger.kernel.org \
--cc=paul@codesourcery.com \
--cc=qemu-devel@nongnu.org \
--cc=seabios@seabios.org \
--cc=yamahata@valinux.co.jp \
/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).