aboutsummaryrefslogtreecommitdiff
path: root/hw/spapr_vio.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/spapr_vio.c')
-rw-r--r--hw/spapr_vio.c238
1 files changed, 238 insertions, 0 deletions
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index 605079cda5..39d77ee28b 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;