[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 21/27] Implement TCE translation for sPAPR VIO
From: |
David Gibson |
Subject: |
[Qemu-devel] [PATCH 21/27] Implement TCE translation for sPAPR VIO |
Date: |
Fri, 25 Mar 2011 14:21:26 +1100 |
This patch implements the necessary infrastructure and hypercalls for
sPAPR's TCE (Translation Control Entry) IOMMU mechanism. This is necessary
for all virtual IO devices which do DMA (i.e. nearly all of them).
Signed-off-by: Ben Herrenschmidt <address@hidden>
Signed-off-by: David Gibson <address@hidden>
---
hw/spapr.c | 3 +-
hw/spapr_vio.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/spapr_vio.h | 32 ++++++++
3 files changed, 272 insertions(+), 1 deletions(-)
diff --git a/hw/spapr.c b/hw/spapr.c
index 56dba8e..69759c5 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -64,7 +64,8 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t
ramsize,
uint32_t start_prop = cpu_to_be32(initrd_base);
uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)};
- char hypertas_prop[] =
"hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt";
+ char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt"
+ "\0hcall-tce";
uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
int i;
char *modelname;
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index 605079c..39d77ee 100644
--- a/hw/spapr_vio.c
+++ b/hw/spapr_vio.c
@@ -37,6 +37,7 @@
#endif /* CONFIG_FDT */
/* #define DEBUG_SPAPR */
+/* #define DEBUG_TCE */
#ifdef DEBUG_SPAPR
#define dprintf(fmt, ...) \
@@ -115,6 +116,28 @@ 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;
+ }
+ }
+
if (info->devnode) {
ret = (info->devnode)(dev, fdt, node_off);
if (ret < 0) {
@@ -126,6 +149,216 @@ 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) {
+ 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
+
+ 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
+
+ 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);
+}
+
static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo)
{
VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
@@ -138,6 +371,8 @@ static int spapr_vio_busdev_init(DeviceState *qdev,
DeviceInfo *qinfo)
dev->qdev.id = id;
+ rtce_init(dev);
+
return info->init(dev);
}
@@ -193,6 +428,9 @@ 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);
+
for (qinfo = device_info_list; qinfo; qinfo = qinfo->next) {
VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h
index 2013927..9d864c2 100644
--- a/hw/spapr_vio.h
+++ b/hw/spapr_vio.h
@@ -21,12 +21,29 @@
* 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)
+
+enum VIOsPAPR_TCEAccess {
+ SPAPR_TCE_FAULT = 0,
+ SPAPR_TCE_RO = 1,
+ SPAPR_TCE_WO = 2,
+ SPAPR_TCE_RW = 3,
+};
+
+typedef struct VIOsPAPR_RTCE {
+ uint64_t tce;
+} VIOsPAPR_RTCE;
+
typedef struct VIOsPAPRDevice {
DeviceState qdev;
uint32_t reg;
qemu_irq qirq;
uint32_t vio_irq_num;
target_ulong signal_state;
+ uint32_t rtce_window_size;
+ VIOsPAPR_RTCE *rtce_table;
} VIOsPAPRDevice;
typedef struct VIOsPAPRBus {
@@ -49,6 +66,21 @@ 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);
+
void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len);
void spapr_vty_create(VIOsPAPRBus *bus,
uint32_t reg, CharDriverState *chardev,
--
1.7.1
- [Qemu-devel] [PATCH 09/27] Use "hash" more consistently in ppc mmu code, (continued)
- [Qemu-devel] [PATCH 09/27] Use "hash" more consistently in ppc mmu code, David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 11/27] Support 1T segments on ppc, David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 12/27] Add POWER7 support for ppc, David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 02/27] Allow qemu_devtree_setprop() to take arbitrary values, David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 13/27] Start implementing pSeries logical partition machine, David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 14/27] Implement the bus structure for PAPR virtual IO, David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 16/27] Implement hcall based RTAS for pSeries machines, David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 20/27] Add (virtual) interrupt to PAPR virtual tty device, David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 19/27] Add PAPR H_VIO_SIGNAL hypercall and infrastructure for VIO interrupts, David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 15/27] Virtual hash page table handling on pSeries machine, David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 21/27] Implement TCE translation for sPAPR VIO,
David Gibson <=
- [Qemu-devel] [PATCH 17/27] Implement assorted pSeries hcalls and RTAS methods, David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 26/27] Implement PAPR VPA functions for pSeries shared processor partitions, David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 25/27] Add a PAPR TCE-bypass mechanism for the pSeries machine, David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 22/27] Implement sPAPR Virtual LAN (ibmveth), David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 23/27] Implement PAPR CRQ hypercalls, David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 24/27] Implement PAPR virtual SCSI interface (ibmvscsi), David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 18/27] Implement the PAPR (pSeries) virtualized interrupt controller (xics), David Gibson, 2011/03/24
- [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options, David Gibson, 2011/03/24