[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 14/14] Make spapr tces use generic dma layer
From: |
David Gibson |
Subject: |
[Qemu-devel] [PATCH 14/14] Make spapr tces use generic dma layer |
Date: |
Fri, 3 Jun 2011 01:12:42 +1000 |
---
Makefile.target | 4 +-
hw/spapr.c | 3 +
hw/spapr.h | 14 +++-
hw/spapr_iommu.c | 236 ++++++++++++++++++++++++++++++++++++++++++++
hw/spapr_llan.c | 70 ++++++++------
hw/spapr_vio.c | 289 +++--------------------------------------------------
hw/spapr_vio.h | 60 ++++++------
hw/spapr_vscsi.c | 26 +++---
hw/spapr_vty.c | 1 +
9 files changed, 354 insertions(+), 349 deletions(-)
create mode 100644 hw/spapr_iommu.c
diff --git a/Makefile.target b/Makefile.target
index 042ba1b..365d43e 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -252,8 +252,8 @@ obj-ppc-y += ppc_oldworld.o
# NewWorld PowerMac
obj-ppc-y += ppc_newworld.o
# IBM pSeries (sPAPR)
-ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy)
-obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
+ifeq ($(CONFIG_FDT)$(CONFIG_IOMMU)$(TARGET_PPC64),yyy)
+obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o spapr_iommu.o
obj-ppc-y += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o
obj-ppc-$(CONFIG_VIRTIO) += spapr_virtio.o
endif
diff --git a/hw/spapr.c b/hw/spapr.c
index febcad4..45b87bb 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -415,6 +415,9 @@ static void ppc_spapr_init(ram_addr_t ram_size,
/* Set up Interrupt Controller */
spapr->icp = xics_system_init(XICS_IRQS);
+ /* Set up IOMMU */
+ spapr_iommu_init();
+
/* Set up VIO bus */
spapr->vio_bus = spapr_vio_bus_init();
diff --git a/hw/spapr.h b/hw/spapr.h
index 382b0f1..525c9ba 100644
--- a/hw/spapr.h
+++ b/hw/spapr.h
@@ -1,6 +1,8 @@
#if !defined(__HW_SPAPR_H__)
#define __HW_SPAPR_H__
+#include "dma_rw.h"
+
struct VIOsPAPRBus;
struct VirtIOsPAPRBus;
struct icp_state;
@@ -269,7 +271,7 @@ typedef struct sPAPREnvironment {
extern sPAPREnvironment *spapr;
-/*#define DEBUG_SPAPR_HCALLS*/
+#define DEBUG_SPAPR_HCALLS
#ifdef DEBUG_SPAPR_HCALLS
#define hcall_dprintf(fmt, ...) \
@@ -307,4 +309,14 @@ target_ulong spapr_rtas_call(sPAPREnvironment *spapr,
int spapr_rtas_device_tree_setup(void *fdt, target_phys_addr_t rtas_addr,
target_phys_addr_t rtas_size);
+#define SPAPR_TCE_PAGE_SHIFT 12
+#define SPAPR_TCE_PAGE_SIZE (1ULL << SPAPR_TCE_PAGE_SHIFT)
+#define SPAPR_TCE_PAGE_MASK (SPAPR_TCE_PAGE_SIZE - 1)
+
+void spapr_iommu_init(void);
+void spapr_tce_init_dev(DeviceState *dev, uint64_t liobn,
+ size_t window_size);
+void spapr_tce_clear_dev(DeviceState *dev);
+int spapr_dma_dt(void *fdt, int node_off, DMAMmu *iommu);
+
#endif /* !defined (__HW_SPAPR_H__) */
diff --git a/hw/spapr_iommu.c b/hw/spapr_iommu.c
new file mode 100644
index 0000000..49b930d
--- /dev/null
+++ b/hw/spapr_iommu.c
@@ -0,0 +1,236 @@
+/*
+ * QEMU sPAPR IOMMU (TCE) code
+ *
+ * Copyright (c) 2010 David Gibson, IBM Corporation <address@hidden>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "hw.h"
+#include "kvm.h"
+#include "qdev.h"
+#include "kvm_ppc.h"
+#include "hw/dma_rw.h"
+
+#include "hw/spapr.h"
+
+#include <libfdt.h>
+
+/* #define DEBUG_TCE */
+
+enum sPAPRTCEAccess {
+ SPAPR_TCE_FAULT = 0,
+ SPAPR_TCE_RO = 1,
+ SPAPR_TCE_WO = 2,
+ SPAPR_TCE_RW = 3,
+};
+
+typedef struct sPAPRTCE {
+ uint64_t tce;
+} sPAPRTCE;
+
+
+typedef struct sPAPRTCETable sPAPRTCETable;
+
+struct sPAPRTCETable {
+ struct DMAMmu dma;
+ target_ulong liobn;
+ uint32_t window_size;
+ sPAPRTCE *table;
+ int fd;
+ QLIST_ENTRY(sPAPRTCETable) list;
+};
+
+
+QLIST_HEAD(spapr_tce_tables, sPAPRTCETable) spapr_tce_tables;
+
+static sPAPRTCETable *spapr_tce_find_by_liobn(target_ulong liobn)
+{
+ sPAPRTCETable *tcet;
+
+ QLIST_FOREACH(tcet, &spapr_tce_tables, list) {
+ if (tcet->liobn == liobn) {
+ return tcet;
+ }
+ }
+
+ return NULL;
+}
+
+static int spapr_tce_translate(DeviceState *qdev,
+ dma_addr_t addr,
+ dma_addr_t *paddr,
+ dma_addr_t *len,
+ int is_write)
+{
+ sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, qdev->iommu);
+ enum sPAPRTCEAccess access = is_write ? SPAPR_TCE_WO : SPAPR_TCE_RO;
+ uint64_t tce;
+
+#ifdef DEBUG_TCE
+ fprintf(stderr, "spapr_tce_translate addr=0x%llx\n",
+ (unsigned long long)addr);
+#endif
+
+ /* Check if we are in bound */
+ if (addr >= tcet->window_size) {
+#ifdef DEBUG_TCE
+ fprintf(stderr, "spapr_tce_translate out of bounds\n");
+#endif
+ return -EFAULT;
+ }
+
+ tce = tcet->table[addr >> SPAPR_TCE_PAGE_SHIFT].tce;
+
+ /* Check TCE */
+ if (!(tce & access)) {
+ return -EPERM;
+ }
+
+ /* How much til end of page ? */
+ *len = ((~addr) & SPAPR_TCE_PAGE_MASK) + 1;
+
+ /* Translate */
+ *paddr = (tce & ~SPAPR_TCE_PAGE_MASK) |
+ (addr & SPAPR_TCE_PAGE_MASK);
+
+#ifdef DEBUG_TCE
+ fprintf(stderr, " -> *paddr=0x%llx, *len=0x%llxx\n",
+ (unsigned long long)*paddr, (unsigned long long )*len);
+#endif
+
+ return 0;
+}
+
+void spapr_tce_init_dev(DeviceState *dev, target_ulong liobn,
+ size_t window_size)
+{
+ sPAPRTCETable *tcet;
+
+ if (dev->iommu || !window_size) {
+ return;
+ }
+
+ tcet = qemu_mallocz(sizeof(*tcet));
+ dma_mmu_init(&tcet->dma, spapr_tce_translate);
+ dev->iommu = &tcet->dma;
+
+ tcet->liobn = liobn;
+ tcet->window_size = window_size;
+
+ if (kvm_enabled()) {
+ tcet->table = kvmppc_create_spapr_tce(liobn,
+ window_size,
+ &tcet->fd);
+ }
+
+ if (!tcet->table) {
+ size_t table_size = (window_size >> SPAPR_TCE_PAGE_SHIFT)
+ * sizeof(sPAPRTCE);
+ tcet->table = qemu_mallocz(table_size);
+ }
+
+
+ QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list);
+}
+
+void spapr_tce_clear_dev(DeviceState *dev)
+{
+
+ if (dev->iommu) {
+ sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dev->iommu);
+
+ QLIST_REMOVE(tcet, list);
+
+ if (!kvm_enabled() ||
+ (kvmppc_remove_spapr_tce(tcet->table, tcet->fd,
+ tcet->window_size) != 0)) {
+ qemu_free(tcet->table);
+ }
+
+ qemu_free(tcet);
+ dev->iommu = NULL;
+ }
+}
+
+
+static target_ulong h_put_tce(CPUState *env, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong liobn = args[0];
+ target_ulong ioba = args[1];
+ target_ulong tce = args[2];
+ sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn);
+ sPAPRTCE *tcep;
+
+ if (!tcet) {
+ hcall_dprintf("spapr_vio_put_tce on non-existent LIOBN "
+ TARGET_FMT_lx "\n", liobn);
+ return H_PARAMETER;
+ }
+
+ ioba &= ~(SPAPR_TCE_PAGE_SIZE - 1);
+
+#ifdef DEBUG_TCE
+ fprintf(stderr, "spapr_vio_put_tce on %s ioba 0x" TARGET_FMT_lx
+ " TCE 0x" TARGET_FMT_lx "\n", dev->qdev.id, ioba, tce);
+#endif
+
+ if (ioba >= tcet->window_size) {
+ hcall_dprintf("spapr_vio_put_tce on out-of-boards IOBA 0x"
+ TARGET_FMT_lx "\n", ioba);
+ return H_PARAMETER;
+ }
+
+ tcep = tcet->table + (ioba >> SPAPR_TCE_PAGE_SHIFT);
+ tcep->tce = tce;
+
+ return H_SUCCESS;
+}
+
+void spapr_iommu_init(void)
+{
+ QLIST_INIT(&spapr_tce_tables);
+
+ /* hcall-tce */
+ spapr_register_hypercall(H_PUT_TCE, h_put_tce);
+}
+
+int spapr_dma_dt(void *fdt, int node_off, DMAMmu *iommu)
+{
+ if (iommu) {
+ sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, iommu);
+ uint32_t dma_prop[] = {cpu_to_be32(tcet->liobn),
+ 0, 0,
+ 0, cpu_to_be32(tcet->window_size)};
+ int ret;
+
+ ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = fdt_setprop(fdt, node_off, "ibm,my-dma-window", dma_prop,
+ sizeof(dma_prop));
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c
index c18efc7..d206f0c 100644
--- a/hw/spapr_llan.c
+++ b/hw/spapr_llan.c
@@ -27,6 +27,7 @@
#include "hw.h"
#include "net.h"
#include "hw/qdev.h"
+#include "hw/dma_rw.h"
#include "hw/spapr.h"
#include "hw/spapr_vio.h"
@@ -71,7 +72,7 @@ typedef uint64_t vlan_bd_t;
#define VLAN_RXQ_BD_OFF 0
#define VLAN_FILTER_BD_OFF 8
#define VLAN_RX_BDS_OFF 16
-#define VLAN_MAX_BUFS ((SPAPR_VIO_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF) / 8)
+#define VLAN_MAX_BUFS ((SPAPR_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF) / 8)
typedef struct VIOsPAPRVLANDevice {
VIOsPAPRDevice sdev;
@@ -95,7 +96,7 @@ static ssize_t spapr_vlan_receive(VLANClientState *nc, const
uint8_t *buf,
{
VIOsPAPRDevice *sdev = DO_UPCAST(NICState, nc, nc)->opaque;
VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
- vlan_bd_t rxq_bd = ldq_tce(sdev, dev->buf_list + VLAN_RXQ_BD_OFF);
+ vlan_bd_t rxq_bd = vio_ldq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF);
vlan_bd_t bd;
int buf_ptr = dev->use_buf_ptr;
uint64_t handle;
@@ -114,11 +115,11 @@ static ssize_t spapr_vlan_receive(VLANClientState *nc,
const uint8_t *buf,
do {
buf_ptr += 8;
- if (buf_ptr >= SPAPR_VIO_TCE_PAGE_SIZE) {
+ if (buf_ptr >= SPAPR_TCE_PAGE_SIZE) {
buf_ptr = VLAN_RX_BDS_OFF;
}
- bd = ldq_tce(sdev, dev->buf_list + buf_ptr);
+ bd = vio_ldq(sdev, dev->buf_list + buf_ptr);
dprintf("use_buf_ptr=%d bd=0x%016llx\n",
buf_ptr, (unsigned long long)bd);
} while ((!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8)))
@@ -132,12 +133,12 @@ static ssize_t spapr_vlan_receive(VLANClientState *nc,
const uint8_t *buf,
/* Remove the buffer from the pool */
dev->rx_bufs--;
dev->use_buf_ptr = buf_ptr;
- stq_tce(sdev, dev->buf_list + dev->use_buf_ptr, 0);
+ vio_stq(sdev, dev->buf_list + dev->use_buf_ptr, 0);
dprintf("Found buffer: ptr=%d num=%d\n", dev->use_buf_ptr, dev->rx_bufs);
/* Transfer the packet data */
- if (spapr_tce_dma_write(sdev, VLAN_BD_ADDR(bd) + 8, buf, size) < 0) {
+ if (spapr_vio_dma_write(sdev, VLAN_BD_ADDR(bd) + 8, buf, size) < 0) {
return -1;
}
@@ -149,23 +150,23 @@ static ssize_t spapr_vlan_receive(VLANClientState *nc,
const uint8_t *buf,
control ^= VLAN_RXQC_TOGGLE;
}
- handle = ldq_tce(sdev, VLAN_BD_ADDR(bd));
- stq_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 8, handle);
- stw_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 4, size);
- sth_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 2, 8);
- stb_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr, control);
+ handle = vio_ldq(sdev, VLAN_BD_ADDR(bd));
+ vio_stq(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 8, handle);
+ vio_stl(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 4, size);
+ vio_sth(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 2, 8);
+ vio_stb(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr, control);
dprintf("wrote rxq entry (ptr=0x%llx): 0x%016llx 0x%016llx\n",
(unsigned long long)dev->rxq_ptr,
- (unsigned long long)ldq_tce(sdev, VLAN_BD_ADDR(rxq_bd) +
+ (unsigned long long)vio_ldq(sdev, VLAN_BD_ADDR(rxq_bd) +
dev->rxq_ptr),
- (unsigned long long)ldq_tce(sdev, VLAN_BD_ADDR(rxq_bd) +
+ (unsigned long long)vio_ldq(sdev, VLAN_BD_ADDR(rxq_bd) +
dev->rxq_ptr + 8));
dev->rxq_ptr += 16;
if (dev->rxq_ptr >= VLAN_BD_LEN(rxq_bd)) {
dev->rxq_ptr = 0;
- stq_tce(sdev, dev->buf_list + VLAN_RXQ_BD_OFF, rxq_bd ^
VLAN_BD_TOGGLE);
+ vio_stq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF, rxq_bd ^
VLAN_BD_TOGGLE);
}
if (sdev->signal_state & 1) {
@@ -252,13 +253,23 @@ static int check_bd(VIOsPAPRVLANDevice *dev, vlan_bd_t bd,
}
if (spapr_vio_check_tces(&dev->sdev, VLAN_BD_ADDR(bd),
- VLAN_BD_LEN(bd), SPAPR_TCE_RW) != 0) {
+ VLAN_BD_LEN(bd), 1, 1) != 0) {
return -1;
}
return 0;
}
+static int vio_dma_zero(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t size)
+{
+ /* FIXME: allocating a temp buffer is nasty, but just stepping
+ * through writing zeroes is awkward. This will do for now. */
+ uint8_t zeroes[size];
+
+ memset(zeroes, 0, size);
+ return spapr_vio_dma_write(dev, taddr, zeroes, size);
+}
+
static target_ulong h_register_logical_lan(CPUState *env,
sPAPREnvironment *spapr,
target_ulong opcode,
@@ -282,15 +293,15 @@ static target_ulong h_register_logical_lan(CPUState *env,
return H_RESOURCE;
}
- if (check_bd(dev, VLAN_VALID_BD(buf_list, SPAPR_VIO_TCE_PAGE_SIZE),
- SPAPR_VIO_TCE_PAGE_SIZE) < 0) {
+ if (check_bd(dev, VLAN_VALID_BD(buf_list, SPAPR_TCE_PAGE_SIZE),
+ SPAPR_TCE_PAGE_SIZE) < 0) {
hcall_dprintf("Bad buf_list 0x" TARGET_FMT_lx " for "
"H_REGISTER_LOGICAL_LAN\n", buf_list);
return H_PARAMETER;
}
- filter_list_bd = VLAN_VALID_BD(filter_list, SPAPR_VIO_TCE_PAGE_SIZE);
- if (check_bd(dev, filter_list_bd, SPAPR_VIO_TCE_PAGE_SIZE) < 0) {
+ filter_list_bd = VLAN_VALID_BD(filter_list, SPAPR_TCE_PAGE_SIZE);
+ if (check_bd(dev, filter_list_bd, SPAPR_TCE_PAGE_SIZE) < 0) {
hcall_dprintf("Bad filter_list 0x" TARGET_FMT_lx " for "
"H_REGISTER_LOGICAL_LAN\n", filter_list);
return H_PARAMETER;
@@ -308,17 +319,17 @@ static target_ulong h_register_logical_lan(CPUState *env,
rec_queue &= ~VLAN_BD_TOGGLE;
/* Initialize the buffer list */
- stq_tce(sdev, buf_list, rec_queue);
- stq_tce(sdev, buf_list + 8, filter_list_bd);
- spapr_tce_dma_zero(sdev, buf_list + VLAN_RX_BDS_OFF,
- SPAPR_VIO_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF);
+ vio_stq(sdev, buf_list, rec_queue);
+ vio_stq(sdev, buf_list + 8, filter_list_bd);
+ vio_dma_zero(sdev, buf_list + VLAN_RX_BDS_OFF,
+ SPAPR_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF);
dev->add_buf_ptr = VLAN_RX_BDS_OFF - 8;
dev->use_buf_ptr = VLAN_RX_BDS_OFF - 8;
dev->rx_bufs = 0;
dev->rxq_ptr = 0;
/* Initialize the receive queue */
- spapr_tce_dma_zero(sdev, VLAN_BD_ADDR(rec_queue), VLAN_BD_LEN(rec_queue));
+ vio_dma_zero(sdev, VLAN_BD_ADDR(rec_queue), VLAN_BD_LEN(rec_queue));
dev->isopen = 1;
return H_SUCCESS;
@@ -379,14 +390,14 @@ static target_ulong h_add_logical_lan_buffer(CPUState
*env,
do {
dev->add_buf_ptr += 8;
- if (dev->add_buf_ptr >= SPAPR_VIO_TCE_PAGE_SIZE) {
+ if (dev->add_buf_ptr >= SPAPR_TCE_PAGE_SIZE) {
dev->add_buf_ptr = VLAN_RX_BDS_OFF;
}
- bd = ldq_tce(sdev, dev->buf_list + dev->add_buf_ptr);
+ bd = vio_ldq(sdev, dev->buf_list + dev->add_buf_ptr);
} while (bd & VLAN_BD_VALID);
- stq_tce(sdev, dev->buf_list + dev->add_buf_ptr, buf);
+ vio_stq(sdev, dev->buf_list + dev->add_buf_ptr, buf);
dev->rx_bufs++;
@@ -452,7 +463,7 @@ static target_ulong h_send_logical_lan(CPUState *env,
sPAPREnvironment *spapr,
lbuf = alloca(total_len);
p = lbuf;
for (i = 0; i < nbufs; i++) {
- ret = spapr_tce_dma_read(sdev, VLAN_BD_ADDR(bufs[i]),
+ ret = spapr_vio_dma_read(sdev, VLAN_BD_ADDR(bufs[i]),
p, VLAN_BD_LEN(bufs[i]));
if (ret < 0) {
return ret;
@@ -496,13 +507,12 @@ static VIOsPAPRDeviceInfo spapr_vlan = {
.dt_type = "network",
.dt_compatible = "IBM,l-lan",
.signal_mask = 0x1,
+ .rtce_window_size = 0x10000000,
.hcalls = vlan_hcalls,
.qdev.name = "spapr-vlan",
.qdev.size = sizeof(VIOsPAPRVLANDevice),
.qdev.props = (Property[]) {
DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x1000),
- DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice, rtce_window_size,
- 0x10000000),
DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf),
DEFINE_PROP_END_OF_LIST(),
},
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index a75f1d6..21a9182 100644
--- a/hw/spapr_vio.c
+++ b/hw/spapr_vio.c
@@ -29,6 +29,7 @@
#include "kvm.h"
#include "device_tree.h"
#include "kvm_ppc.h"
+#include "hw/dma_rw.h"
#include "hw/spapr.h"
#include "hw/spapr_vio.h"
@@ -38,7 +39,6 @@
#endif /* CONFIG_FDT */
/* #define DEBUG_SPAPR */
-/* #define DEBUG_TCE */
#ifdef DEBUG_SPAPR
#define dprintf(fmt, ...) \
@@ -117,26 +117,9 @@ static int vio_make_devnode(VIOsPAPRDevice *dev,
}
}
- if (dev->rtce_window_size) {
- uint32_t dma_prop[] = {cpu_to_be32(dev->reg),
- 0, 0,
- 0, cpu_to_be32(dev->rtce_window_size)};
-
- ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2);
- if (ret < 0) {
- return ret;
- }
-
- ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2);
- if (ret < 0) {
- return ret;
- }
-
- ret = fdt_setprop(fdt, node_off, "ibm,my-dma-window", dma_prop,
- sizeof(dma_prop));
- if (ret < 0) {
- return ret;
- }
+ ret = spapr_dma_dt(fdt, node_off, dev->qdev.iommu);
+ if (ret < 0) {
+ return ret;
}
if (info->devnode) {
@@ -151,236 +134,6 @@ static int vio_make_devnode(VIOsPAPRDevice *dev,
#endif /* CONFIG_FDT */
/*
- * RTCE handling
- */
-
-static void rtce_init(VIOsPAPRDevice *dev)
-{
- size_t size = (dev->rtce_window_size >> SPAPR_VIO_TCE_PAGE_SHIFT)
- * sizeof(VIOsPAPR_RTCE);
-
- if (size) {
- if (kvm_enabled()) {
- dev->rtce_table = kvmppc_create_spapr_tce(dev->reg,
- dev->rtce_window_size,
- &dev->kvmtce_fd);
- }
-
- if (!dev->rtce_table) {
- dev->rtce_table = qemu_mallocz(size);
- }
- }
-}
-
-static target_ulong h_put_tce(CPUState *env, sPAPREnvironment *spapr,
- target_ulong opcode, target_ulong *args)
-{
- target_ulong liobn = args[0];
- target_ulong ioba = args[1];
- target_ulong tce = args[2];
- VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, liobn);
- VIOsPAPR_RTCE *rtce;
-
- if (!dev) {
- hcall_dprintf("spapr_vio_put_tce on non-existent LIOBN "
- TARGET_FMT_lx "\n", liobn);
- return H_PARAMETER;
- }
-
- ioba &= ~(SPAPR_VIO_TCE_PAGE_SIZE - 1);
-
-#ifdef DEBUG_TCE
- fprintf(stderr, "spapr_vio_put_tce on %s ioba 0x" TARGET_FMT_lx
- " TCE 0x" TARGET_FMT_lx "\n", dev->qdev.id, ioba, tce);
-#endif
-
- if (ioba >= dev->rtce_window_size) {
- hcall_dprintf("spapr_vio_put_tce on out-of-boards IOBA 0x"
- TARGET_FMT_lx "\n", ioba);
- return H_PARAMETER;
- }
-
- rtce = dev->rtce_table + (ioba >> SPAPR_VIO_TCE_PAGE_SHIFT);
- rtce->tce = tce;
-
- return H_SUCCESS;
-}
-
-int spapr_vio_check_tces(VIOsPAPRDevice *dev, target_ulong ioba,
- target_ulong len, enum VIOsPAPR_TCEAccess access)
-{
- int start, end, i;
-
- start = ioba >> SPAPR_VIO_TCE_PAGE_SHIFT;
- end = (ioba + len - 1) >> SPAPR_VIO_TCE_PAGE_SHIFT;
-
- for (i = start; i <= end; i++) {
- if ((dev->rtce_table[i].tce & access) != access) {
-#ifdef DEBUG_TCE
- fprintf(stderr, "FAIL on %d\n", i);
-#endif
- return -1;
- }
- }
-
- return 0;
-}
-
-int spapr_tce_dma_write(VIOsPAPRDevice *dev, uint64_t taddr, const void *buf,
- uint32_t size)
-{
-#ifdef DEBUG_TCE
- fprintf(stderr, "spapr_tce_dma_write taddr=0x%llx size=0x%x\n",
- (unsigned long long)taddr, size);
-#endif
-
- /* Check for bypass */
- if (dev->flags & VIO_PAPR_FLAG_DMA_BYPASS) {
- cpu_physical_memory_write(taddr, buf, size);
- return 0;
- }
-
- while (size) {
- uint64_t tce;
- uint32_t lsize;
- uint64_t txaddr;
-
- /* Check if we are in bound */
- if (taddr >= dev->rtce_window_size) {
-#ifdef DEBUG_TCE
- fprintf(stderr, "spapr_tce_dma_write out of bounds\n");
-#endif
- return H_DEST_PARM;
- }
- tce = dev->rtce_table[taddr >> SPAPR_VIO_TCE_PAGE_SHIFT].tce;
-
- /* How much til end of page ? */
- lsize = MIN(size, ((~taddr) & SPAPR_VIO_TCE_PAGE_MASK) + 1);
-
- /* Check TCE */
- if (!(tce & 2)) {
- return H_DEST_PARM;
- }
-
- /* Translate */
- txaddr = (tce & ~SPAPR_VIO_TCE_PAGE_MASK) |
- (taddr & SPAPR_VIO_TCE_PAGE_MASK);
-
-#ifdef DEBUG_TCE
- fprintf(stderr, " -> write to txaddr=0x%llx, size=0x%x\n",
- (unsigned long long)txaddr, lsize);
-#endif
-
- /* Do it */
- cpu_physical_memory_write(txaddr, buf, lsize);
- buf += lsize;
- taddr += lsize;
- size -= lsize;
- }
- return 0;
-}
-
-int spapr_tce_dma_zero(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t size)
-{
- /* FIXME: allocating a temp buffer is nasty, but just stepping
- * through writing zeroes is awkward. This will do for now. */
- uint8_t zeroes[size];
-
-#ifdef DEBUG_TCE
- fprintf(stderr, "spapr_tce_dma_zero taddr=0x%llx size=0x%x\n",
- (unsigned long long)taddr, size);
-#endif
-
- memset(zeroes, 0, size);
- return spapr_tce_dma_write(dev, taddr, zeroes, size);
-}
-
-void stb_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint8_t val)
-{
- spapr_tce_dma_write(dev, taddr, &val, sizeof(val));
-}
-
-void sth_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint16_t val)
-{
- val = tswap16(val);
- spapr_tce_dma_write(dev, taddr, &val, sizeof(val));
-}
-
-
-void stw_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t val)
-{
- val = tswap32(val);
- spapr_tce_dma_write(dev, taddr, &val, sizeof(val));
-}
-
-void stq_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint64_t val)
-{
- val = tswap64(val);
- spapr_tce_dma_write(dev, taddr, &val, sizeof(val));
-}
-
-int spapr_tce_dma_read(VIOsPAPRDevice *dev, uint64_t taddr, void *buf,
- uint32_t size)
-{
-#ifdef DEBUG_TCE
- fprintf(stderr, "spapr_tce_dma_write taddr=0x%llx size=0x%x\n",
- (unsigned long long)taddr, size);
-#endif
-
- /* Check for bypass */
- if (dev->flags & VIO_PAPR_FLAG_DMA_BYPASS) {
- cpu_physical_memory_read(taddr, buf, size);
- return 0;
- }
-
- while (size) {
- uint64_t tce;
- uint32_t lsize;
- uint64_t txaddr;
-
- /* Check if we are in bound */
- if (taddr >= dev->rtce_window_size) {
-#ifdef DEBUG_TCE
- fprintf(stderr, "spapr_tce_dma_read out of bounds\n");
-#endif
- return H_DEST_PARM;
- }
- tce = dev->rtce_table[taddr >> SPAPR_VIO_TCE_PAGE_SHIFT].tce;
-
- /* How much til end of page ? */
- lsize = MIN(size, ((~taddr) & SPAPR_VIO_TCE_PAGE_MASK) + 1);
-
- /* Check TCE */
- if (!(tce & 1)) {
- return H_DEST_PARM;
- }
-
- /* Translate */
- txaddr = (tce & ~SPAPR_VIO_TCE_PAGE_MASK) |
- (taddr & SPAPR_VIO_TCE_PAGE_MASK);
-
-#ifdef DEBUG_TCE
- fprintf(stderr, " -> write to txaddr=0x%llx, size=0x%x\n",
- (unsigned long long)txaddr, lsize);
-#endif
- /* Do it */
- cpu_physical_memory_read(txaddr, buf, lsize);
- buf += lsize;
- taddr += lsize;
- size -= lsize;
- }
- return H_SUCCESS;
-}
-
-uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr)
-{
- uint64_t val;
-
- spapr_tce_dma_read(dev, taddr, &val, sizeof(val));
- return tswap64(val);
-}
-
-/*
* CRQ handling
*/
static target_ulong h_reg_crq(CPUState *env, sPAPREnvironment *spapr,
@@ -503,7 +256,7 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq)
}
/* Maybe do a fast path for KVM just writing to the pages */
- rc = spapr_tce_dma_read(dev, dev->crq.qladdr + dev->crq.qnext, &byte, 1);
+ rc = spapr_vio_dma_read(dev, dev->crq.qladdr + dev->crq.qnext, &byte, 1);
if (rc) {
return rc;
}
@@ -511,7 +264,7 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq)
return 1;
}
- rc = spapr_tce_dma_write(dev, dev->crq.qladdr + dev->crq.qnext + 8,
+ rc = spapr_vio_dma_write(dev, dev->crq.qladdr + dev->crq.qnext + 8,
&crq[8], 8);
if (rc) {
return rc;
@@ -519,7 +272,7 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq)
kvmppc_eieio();
- rc = spapr_tce_dma_write(dev, dev->crq.qladdr + dev->crq.qnext, crq, 8);
+ rc = spapr_vio_dma_write(dev, dev->crq.qladdr + dev->crq.qnext, crq, 8);
if (rc) {
return rc;
}
@@ -537,19 +290,12 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq)
static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev)
{
- dev->flags &= ~VIO_PAPR_FLAG_DMA_BYPASS;
-
- if (dev->rtce_table) {
- if (kvm_enabled() &&
- (kvmppc_remove_spapr_tce(dev->rtce_table, dev->kvmtce_fd,
- dev->rtce_window_size) == 0)) {
- rtce_init(dev);
- } else {
- size_t size = (dev->rtce_window_size >> SPAPR_VIO_TCE_PAGE_SHIFT)
- * sizeof(VIOsPAPR_RTCE);
- memset(dev->rtce_table, 0, size);
- }
+ VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)dev->qdev.info;
+
+ if (dev->qdev.iommu) {
+ spapr_tce_clear_dev(&dev->qdev);
}
+ spapr_tce_init_dev(&dev->qdev, dev->reg, info->rtce_window_size);
dev->crq.qladdr = 0;
dev->crq.qsize = 0;
@@ -576,9 +322,11 @@ static void rtas_set_tce_bypass(sPAPREnvironment *spapr,
uint32_t token,
return;
}
if (enable) {
- dev->flags |= VIO_PAPR_FLAG_DMA_BYPASS;
+ spapr_tce_clear_dev(&dev->qdev);
} else {
- dev->flags &= ~VIO_PAPR_FLAG_DMA_BYPASS;
+ VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)dev->qdev.info;
+
+ spapr_tce_init_dev(&dev->qdev, dev->reg, info->rtce_window_size);
}
rtas_st(rets, 0, 0);
@@ -617,7 +365,7 @@ static int spapr_vio_busdev_init(DeviceState *qdev,
DeviceInfo *qinfo)
dev->qdev.id = id;
- rtce_init(dev);
+ spapr_tce_init_dev(&dev->qdev, dev->reg, info->rtce_window_size);
return info->init(dev);
}
@@ -674,9 +422,6 @@ VIOsPAPRBus *spapr_vio_bus_init(void)
/* hcall-vio */
spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal);
- /* hcall-tce */
- spapr_register_hypercall(H_PUT_TCE, h_put_tce);
-
/* hcall-crq */
spapr_register_hypercall(H_REG_CRQ, h_reg_crq);
spapr_register_hypercall(H_FREE_CRQ, h_free_crq);
diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h
index d6073b5..7578be3 100644
--- a/hw/spapr_vio.h
+++ b/hw/spapr_vio.h
@@ -21,25 +21,13 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#define SPAPR_VIO_TCE_PAGE_SHIFT 12
-#define SPAPR_VIO_TCE_PAGE_SIZE (1ULL << SPAPR_VIO_TCE_PAGE_SHIFT)
-#define SPAPR_VIO_TCE_PAGE_MASK (SPAPR_VIO_TCE_PAGE_SIZE - 1)
+#include "hw/dma_rw.h"
-enum VIOsPAPR_TCEAccess {
- SPAPR_TCE_FAULT = 0,
- SPAPR_TCE_RO = 1,
- SPAPR_TCE_WO = 2,
- SPAPR_TCE_RW = 3,
-};
#define SPAPR_VTY_BASE_ADDRESS 0x30000000
struct VIOsPAPRDevice;
-typedef struct VIOsPAPR_RTCE {
- uint64_t tce;
-} VIOsPAPR_RTCE;
-
typedef struct VIOsPAPR_CRQ {
uint64_t qladdr;
uint32_t qsize;
@@ -51,13 +39,9 @@ typedef struct VIOsPAPRDevice {
DeviceState qdev;
uint32_t reg;
uint32_t flags;
-#define VIO_PAPR_FLAG_DMA_BYPASS 0x1
qemu_irq qirq;
uint32_t vio_irq_num;
target_ulong signal_state;
- uint32_t rtce_window_size;
- VIOsPAPR_RTCE *rtce_table;
- int kvmtce_fd;
VIOsPAPR_CRQ crq;
} VIOsPAPRDevice;
@@ -69,6 +53,7 @@ typedef struct {
DeviceInfo qdev;
const char *dt_name, *dt_type, *dt_compatible;
target_ulong signal_mask;
+ uint32_t rtce_window_size;
int (*init)(VIOsPAPRDevice *dev);
void (*hcalls)(VIOsPAPRBus *bus);
int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off);
@@ -81,20 +66,33 @@ extern int spapr_populate_vdevice(VIOsPAPRBus *bus, void
*fdt);
extern int spapr_vio_signal(VIOsPAPRDevice *dev, target_ulong mode);
-int spapr_vio_check_tces(VIOsPAPRDevice *dev, target_ulong ioba,
- target_ulong len,
- enum VIOsPAPR_TCEAccess access);
-
-int spapr_tce_dma_read(VIOsPAPRDevice *dev, uint64_t taddr,
- void *buf, uint32_t size);
-int spapr_tce_dma_write(VIOsPAPRDevice *dev, uint64_t taddr,
- const void *buf, uint32_t size);
-int spapr_tce_dma_zero(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t size);
-void stb_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint8_t val);
-void sth_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint16_t val);
-void stw_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t val);
-void stq_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint64_t val);
-uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr);
+static inline int spapr_vio_check_tces(VIOsPAPRDevice *dev, target_ulong ioba,
+ target_ulong len,
+ int check_r, int check_w)
+{
+ return (dma_memory_check(&dev->qdev, ioba, len, check_r, check_w) != 0) ?
+ H_DEST_PARM : H_SUCCESS;
+}
+
+static inline int spapr_vio_dma_read(VIOsPAPRDevice *dev, uint64_t taddr,
+ void *buf, uint32_t size)
+{
+ return (dma_memory_read(&dev->qdev, taddr, buf, size) != 0) ?
+ H_DEST_PARM : H_SUCCESS;
+}
+
+static inline int spapr_vio_dma_write(VIOsPAPRDevice *dev, uint64_t taddr,
+ const void *buf, uint32_t size)
+{
+ return (dma_memory_write(&dev->qdev, taddr, buf, size) != 0) ?
+ H_DEST_PARM : H_SUCCESS;
+}
+
+#define vio_stb(_dev, _addr, _val) (dma_stb(&(_dev)->qdev, (_addr), (_val)))
+#define vio_sth(_dev, _addr, _val) (dma_stw(&(_dev)->qdev, (_addr), (_val)))
+#define vio_stl(_dev, _addr, _val) (dma_stl(&(_dev)->qdev, (_addr), (_val)))
+#define vio_stq(_dev, _addr, _val) (dma_stq(&(_dev)->qdev, (_addr), (_val)))
+#define vio_ldq(_dev, _addr) (dma_ldq(&(_dev)->qdev, (_addr)))
int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq);
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 1c901ef..d8431b3 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -37,6 +37,7 @@
#include "net.h" /* Remove that when we can */
#include "srp.h"
#include "hw/qdev.h"
+#include "hw/dma_rw.h"
#include "hw/spapr.h"
#include "hw/spapr_vio.h"
#include "hw/ppc-viosrp.h"
@@ -152,7 +153,7 @@ static int vscsi_send_iu(VSCSIState *s, vscsi_req *req,
long rc, rc1;
/* First copy the SRP */
- rc = spapr_tce_dma_write(&s->vdev, req->crq.s.IU_data_ptr,
+ rc = spapr_vio_dma_write(&s->vdev, req->crq.s.IU_data_ptr,
&req->iu, length);
if (rc) {
fprintf(stderr, "vscsi_send_iu: DMA write failure !\n");
@@ -268,9 +269,9 @@ static int vscsi_srp_direct_data(VSCSIState *s, vscsi_req
*req,
llen = MIN(len, md->len);
if (llen) {
if (req->writing) { /* writing = to device = reading from memory */
- rc = spapr_tce_dma_read(&s->vdev, md->va, buf, llen);
+ rc = spapr_vio_dma_read(&s->vdev, md->va, buf, llen);
} else {
- rc = spapr_tce_dma_write(&s->vdev, md->va, buf, llen);
+ rc = spapr_vio_dma_write(&s->vdev, md->va, buf, llen);
}
}
md->len -= llen;
@@ -316,10 +317,10 @@ static int vscsi_srp_indirect_data(VSCSIState *s,
vscsi_req *req,
md = req->cur_desc = &req->ext_desc;
dprintf("VSCSI: Reading desc from 0x%llx\n",
(unsigned long long)td->va);
- rc = spapr_tce_dma_read(&s->vdev, td->va, md,
+ rc = spapr_vio_dma_read(&s->vdev, td->va, md,
sizeof(struct srp_direct_buf));
if (rc) {
- dprintf("VSCSI: tce_dma_read -> %d reading ext_desc\n", rc);
+ dprintf("VSCSI: spapr_vio_dma_read -> %d reading ext_desc\n",
rc);
break;
}
vscsi_swap_desc(md);
@@ -332,12 +333,12 @@ static int vscsi_srp_indirect_data(VSCSIState *s,
vscsi_req *req,
/* Perform transfer */
llen = MIN(len, md->len);
if (req->writing) { /* writing = to device = reading from memory */
- rc = spapr_tce_dma_read(&s->vdev, md->va, buf, llen);
+ rc = spapr_vio_dma_read(&s->vdev, md->va, buf, llen);
} else {
- rc = spapr_tce_dma_write(&s->vdev, md->va, buf, llen);
+ rc = spapr_vio_dma_write(&s->vdev, md->va, buf, llen);
}
if (rc) {
- dprintf("VSCSI: tce_dma_r/w(%d) -> %d\n", req->writing, rc);
+ dprintf("VSCSI: spapr_vio_dma_r/w(%d) -> %d\n", req->writing, rc);
break;
}
dprintf("VSCSI: data: %02x %02x %02x %02x...\n",
@@ -768,7 +769,7 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req
*req)
sinfo = &req->iu.mad.adapter_info;
#if 0 /* What for ? */
- rc = spapr_tce_dma_read(&s->vdev, be64_to_cpu(sinfo->buffer),
+ rc = spapr_vio_dma_read(&s->vdev, be64_to_cpu(sinfo->buffer),
&info, be16_to_cpu(sinfo->common.length));
if (rc) {
fprintf(stderr, "vscsi_send_adapter_info: DMA read failure !\n");
@@ -782,7 +783,7 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req
*req)
info.os_type = cpu_to_be32(2);
info.port_max_txu[0] = cpu_to_be32(VSCSI_MAX_SECTORS << 9);
- rc = spapr_tce_dma_write(&s->vdev, be64_to_cpu(sinfo->buffer),
+ rc = spapr_vio_dma_write(&s->vdev, be64_to_cpu(sinfo->buffer),
&info, be16_to_cpu(sinfo->common.length));
if (rc) {
fprintf(stderr, "vscsi_send_adapter_info: DMA write failure !\n");
@@ -844,7 +845,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq)
}
/* XXX Handle failure differently ? */
- if (spapr_tce_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->iu,
+ if (spapr_vio_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->iu,
crq->s.IU_length)) {
fprintf(stderr, "vscsi_got_payload: DMA read failure !\n");
qemu_free(req);
@@ -992,12 +993,11 @@ static VIOsPAPRDeviceInfo spapr_vscsi = {
.dt_type = "vscsi",
.dt_compatible = "IBM,v-scsi",
.signal_mask = 0x00000001,
+ .rtce_window_size = 0x10000000,
.qdev.name = "spapr-vscsi",
.qdev.size = sizeof(VSCSIState),
.qdev.props = (Property[]) {
DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x2000),
- DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice,
- rtce_window_size, 0x10000000),
DEFINE_PROP_END_OF_LIST(),
},
};
diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c
index 6fc0105..284d789 100644
--- a/hw/spapr_vty.c
+++ b/hw/spapr_vty.c
@@ -1,5 +1,6 @@
#include "qdev.h"
#include "qemu-char.h"
+#include "hw/dma_rw.h"
#include "hw/spapr.h"
#include "hw/spapr_vio.h"
--
1.7.4.4
- Re: [Qemu-devel] [PATCH 01/14] Generic DMA memory access interface, (continued)
[Qemu-devel] [PATCH 10/14] lsi53c895a: use the DMA memory access interface, David Gibson, 2011/06/02
[Qemu-devel] [PATCH 09/14] e1000: use the DMA memory access interface, David Gibson, 2011/06/02
[Qemu-devel] [PATCH 08/14] es1370: use the DMA memory access interface, David Gibson, 2011/06/02
[Qemu-devel] [PATCH 05/14] rtl8139: use the DMA memory access interface, David Gibson, 2011/06/02
[Qemu-devel] [PATCH 07/14] ac97: use the DMA memory access interface, David Gibson, 2011/06/02
[Qemu-devel] [PATCH 03/14] AMD IOMMU emulation, David Gibson, 2011/06/02
[Qemu-devel] [PATCH 11/14] pcnet: use the DMA memory access interface, David Gibson, 2011/06/02
[Qemu-devel] [PATCH 14/14] Make spapr tces use generic dma layer,
David Gibson <=
[Qemu-devel] [PATCH 12/14] usb-uhci: use the DMA memory access interface, David Gibson, 2011/06/02
[Qemu-devel] [PATCH 06/14] eepro100: use the DMA memory access interface, David Gibson, 2011/06/02