aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/spapr.c3
-rw-r--r--hw/spapr_vio.c238
-rw-r--r--hw/spapr_vio.h32
3 files changed, 272 insertions, 1 deletions
diff --git a/hw/spapr.c b/hw/spapr.c
index 859cf86011..f8749cc57d 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -65,7 +65,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 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;
diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h
index 20139273d3..9d864c20fe 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,