diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index d18dbaf6cc..aa4bbeb664 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -10,7 +10,7 @@ obj-y += ppc_newworld.o # IBM pSeries (sPAPR) obj-$(CONFIG_PSERIES) += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o obj-$(CONFIG_PSERIES) += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o -obj-$(CONFIG_PSERIES) += spapr_pci.o pci-hotplug.o +obj-$(CONFIG_PSERIES) += spapr_pci.o pci-hotplug.o spapr_iommu.o # PowerPC 4xx boards obj-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o obj-y += ppc440_bamboo.o diff --git a/hw/spapr.c b/hw/spapr.c index 09a23ff092..81c9343ca5 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -674,6 +674,9 @@ static void ppc_spapr_init(ram_addr_t ram_size, spapr->icp = xics_system_init(XICS_IRQS); spapr->next_irq = 16; + /* 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 c75172e0c0..1c4d85f977 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -1,6 +1,7 @@ #if !defined(__HW_SPAPR_H__) #define __HW_SPAPR_H__ +#include "dma.h" #include "hw/xics.h" struct VIOsPAPRBus; @@ -320,4 +321,20 @@ 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) + +typedef struct sPAPRTCE { + uint64_t tce; +} sPAPRTCE; + +#define SPAPR_VIO_BASE_LIOBN 0x00000000 + +void spapr_iommu_init(void); +DMAContext *spapr_tce_new_dma_context(uint32_t liobn, size_t window_size); +void spapr_tce_free(DMAContext *dma); +int spapr_dma_dt(void *fdt, int node_off, const char *propname, + DMAContext *dma); + #endif /* !defined (__HW_SPAPR_H__) */ diff --git a/hw/spapr_iommu.c b/hw/spapr_iommu.c new file mode 100644 index 0000000000..5a769b9f4a --- /dev/null +++ b/hw/spapr_iommu.c @@ -0,0 +1,242 @@ +/* + * QEMU sPAPR IOMMU (TCE) code + * + * Copyright (c) 2010 David Gibson, IBM Corporation + * + * 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 . + */ +#include "hw.h" +#include "kvm.h" +#include "qdev.h" +#include "kvm_ppc.h" +#include "dma.h" + +#include "hw/spapr.h" + +#include + +/* #define DEBUG_TCE */ + +enum sPAPRTCEAccess { + SPAPR_TCE_FAULT = 0, + SPAPR_TCE_RO = 1, + SPAPR_TCE_WO = 2, + SPAPR_TCE_RW = 3, +}; + +typedef struct sPAPRTCETable sPAPRTCETable; + +struct sPAPRTCETable { + DMAContext dma; + uint32_t 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(uint32_t liobn) +{ + sPAPRTCETable *tcet; + + QLIST_FOREACH(tcet, &spapr_tce_tables, list) { + if (tcet->liobn == liobn) { + return tcet; + } + } + + return NULL; +} + +static int spapr_tce_translate(DMAContext *dma, + dma_addr_t addr, + target_phys_addr_t *paddr, + target_phys_addr_t *len, + DMADirection dir) +{ + sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma); + enum sPAPRTCEAccess access = (dir == DMA_DIRECTION_FROM_DEVICE) + ? SPAPR_TCE_WO : SPAPR_TCE_RO; + uint64_t tce; + +#ifdef DEBUG_TCE + fprintf(stderr, "spapr_tce_translate liobn=0x%" PRIx32 " addr=0x" + DMA_ADDR_FMT "\n", tcet->liobn, 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" TARGET_FMT_plx ", *len=0x" + TARGET_FMT_plx "\n", *paddr, *len); +#endif + + return 0; +} + +DMAContext *spapr_tce_new_dma_context(uint32_t liobn, size_t window_size) +{ + sPAPRTCETable *tcet; + + if (!window_size) { + return NULL; + } + + tcet = g_malloc0(sizeof(*tcet)); + dma_context_init(&tcet->dma, spapr_tce_translate, NULL, NULL); + + 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 = g_malloc0(table_size); + } + +#ifdef DEBUG_TCE + fprintf(stderr, "spapr_iommu: New TCE table, liobn=0x%x, context @ %p, " + "table @ %p, fd=%d\n", liobn, &tcet->dma, tcet->table, tcet->fd); +#endif + + QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); + + return &tcet->dma; +} + +void spapr_tce_free(DMAContext *dma) +{ + + if (dma) { + sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma); + + QLIST_REMOVE(tcet, list); + + if (!kvm_enabled() || + (kvmppc_remove_spapr_tce(tcet->table, tcet->fd, + tcet->window_size) != 0)) { + g_free(tcet->table); + } + + g_free(tcet); + } +} + + +static target_ulong h_put_tce(CPUPPCState *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 (liobn & 0xFFFFFFFF00000000ULL) { + hcall_dprintf("spapr_vio_put_tce on out-of-boundsw LIOBN " + TARGET_FMT_lx "\n", liobn); + return H_PARAMETER; + } + 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 liobn=" TARGET_FMT_lx /*%s*/ + " ioba 0x" TARGET_FMT_lx " TCE 0x" TARGET_FMT_lx "\n", + liobn, /*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, const char *propname, + DMAContext *dma) +{ + if (dma) { + sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma); + 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, propname, 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 8313043652..d26fe9fea3 100644 --- a/hw/spapr_llan.c +++ b/hw/spapr_llan.c @@ -71,7 +71,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 +95,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 +114,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 +132,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 +149,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) { @@ -254,8 +254,10 @@ static int check_bd(VIOsPAPRVLANDevice *dev, vlan_bd_t bd, return -1; } - if (spapr_vio_check_tces(&dev->sdev, VLAN_BD_ADDR(bd), - VLAN_BD_LEN(bd), SPAPR_TCE_RW) != 0) { + if (!spapr_vio_dma_valid(&dev->sdev, VLAN_BD_ADDR(bd), + VLAN_BD_LEN(bd), DMA_DIRECTION_FROM_DEVICE) + || !spapr_vio_dma_valid(&dev->sdev, VLAN_BD_ADDR(bd), + VLAN_BD_LEN(bd), DMA_DIRECTION_TO_DEVICE)) { return -1; } @@ -285,14 +287,14 @@ static target_ulong h_register_logical_lan(CPUPPCState *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 "\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 "\n", filter_list); return H_PARAMETER; } @@ -309,17 +311,17 @@ static target_ulong h_register_logical_lan(CPUPPCState *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); + spapr_vio_dma_set(sdev, buf_list + VLAN_RX_BDS_OFF, 0, + 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)); + spapr_vio_dma_set(sdev, VLAN_BD_ADDR(rec_queue), 0, VLAN_BD_LEN(rec_queue)); dev->isopen = 1; return H_SUCCESS; @@ -378,14 +380,14 @@ static target_ulong h_add_logical_lan_buffer(CPUPPCState *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++; @@ -451,7 +453,7 @@ static target_ulong h_send_logical_lan(CPUPPCState *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; @@ -479,7 +481,7 @@ static target_ulong h_multicast_ctrl(CPUPPCState *env, sPAPREnvironment *spapr, } static Property spapr_vlan_properties[] = { - DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev, 0x10000000), + DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev), DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf), DEFINE_PROP_END_OF_LIST(), }; @@ -497,6 +499,7 @@ static void spapr_vlan_class_init(ObjectClass *klass, void *data) k->dt_compatible = "IBM,l-lan"; k->signal_mask = 0x1; dc->props = spapr_vlan_properties; + k->rtce_window_size = 0x10000000; } static TypeInfo spapr_vlan_info = { diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c index c8271c626c..05b55032a9 100644 --- a/hw/spapr_vio.c +++ b/hw/spapr_vio.c @@ -39,7 +39,6 @@ #endif /* CONFIG_FDT */ /* #define DEBUG_SPAPR */ -/* #define DEBUG_TCE */ #ifdef DEBUG_SPAPR #define dprintf(fmt, ...) \ @@ -143,26 +142,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, "ibm,my-dma-window", dev->dma); + if (ret < 0) { + return ret; } if (pc->devnode) { @@ -176,232 +158,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) { - dev->rtce_table = kvmppc_create_spapr_tce(dev->reg, - dev->rtce_window_size, - &dev->kvmtce_fd); - - if (!dev->rtce_table) { - dev->rtce_table = g_malloc0(size); - } - } -} - -static target_ulong h_put_tce(CPUPPCState *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("LIOBN 0x" TARGET_FMT_lx " does not exist\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("Out-of-bounds 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 */ @@ -526,7 +282,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; } @@ -534,7 +290,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; @@ -542,7 +298,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; } @@ -560,13 +316,13 @@ 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; + VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); + uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg; - if (dev->rtce_table) { - size_t size = (dev->rtce_window_size >> SPAPR_VIO_TCE_PAGE_SHIFT) - * sizeof(VIOsPAPR_RTCE); - memset(dev->rtce_table, 0, size); + if (dev->dma) { + spapr_tce_free(dev->dma); } + dev->dma = spapr_tce_new_dma_context(liobn, pc->rtce_window_size); dev->crq.qladdr = 0; dev->crq.qsize = 0; @@ -593,9 +349,13 @@ static void rtas_set_tce_bypass(sPAPREnvironment *spapr, uint32_t token, return; } if (enable) { - dev->flags |= VIO_PAPR_FLAG_DMA_BYPASS; + spapr_tce_free(dev->dma); + dev->dma = NULL; } else { - dev->flags &= ~VIO_PAPR_FLAG_DMA_BYPASS; + VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); + uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg; + + dev->dma = spapr_tce_new_dma_context(liobn, pc->rtce_window_size); } rtas_st(rets, 0, 0); @@ -662,6 +422,7 @@ static int spapr_vio_busdev_init(DeviceState *qdev) { VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev; VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); + uint32_t liobn; char *id; if (dev->reg != -1) { @@ -703,7 +464,8 @@ static int spapr_vio_busdev_init(DeviceState *qdev) return -1; } - rtce_init(dev); + liobn = SPAPR_VIO_BASE_LIOBN | dev->reg; + dev->dma = spapr_tce_new_dma_context(liobn, pc->rtce_window_size); return pc->init(dev); } @@ -751,9 +513,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 2adad77d02..6f9a498ccd 100644 --- a/hw/spapr_vio.h +++ b/hw/spapr_vio.h @@ -21,16 +21,7 @@ * License along with this library; if not, see . */ -#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, -}; +#include "dma.h" #define TYPE_VIO_SPAPR_DEVICE "vio-spapr-device" #define VIO_SPAPR_DEVICE(obj) \ @@ -45,10 +36,6 @@ enum VIOsPAPR_TCEAccess { struct VIOsPAPRDevice; -typedef struct VIOsPAPR_RTCE { - uint64_t tce; -} VIOsPAPR_RTCE; - typedef struct VIOsPAPR_CRQ { uint64_t qladdr; uint32_t qsize; @@ -64,6 +51,7 @@ typedef struct VIOsPAPRDeviceClass { const char *dt_name, *dt_type, *dt_compatible; target_ulong signal_mask; + uint32_t rtce_window_size; int (*init)(VIOsPAPRDevice *dev); void (*reset)(VIOsPAPRDevice *dev); int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off); @@ -73,20 +61,15 @@ 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; + DMAContext *dma; }; -#define DEFINE_SPAPR_PROPERTIES(type, field, default_dma_window) \ - DEFINE_PROP_UINT32("reg", type, field.reg, -1), \ - DEFINE_PROP_UINT32("dma-window", type, field.rtce_window_size, \ - default_dma_window) +#define DEFINE_SPAPR_PROPERTIES(type, field) \ + DEFINE_PROP_UINT32("reg", type, field.reg, -1) struct VIOsPAPRBus { BusState bus; @@ -102,20 +85,38 @@ extern int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus); 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); +static inline bool spapr_vio_dma_valid(VIOsPAPRDevice *dev, uint64_t taddr, + uint32_t size, DMADirection dir) +{ + return dma_memory_valid(dev->dma, taddr, size, dir); +} -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_dma_read(VIOsPAPRDevice *dev, uint64_t taddr, + void *buf, uint32_t size) +{ + return (dma_memory_read(dev->dma, 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->dma, taddr, buf, size) != 0) ? + H_DEST_PARM : H_SUCCESS; +} + +static inline int spapr_vio_dma_set(VIOsPAPRDevice *dev, uint64_t taddr, + uint8_t c, uint32_t size) +{ + return (dma_memory_set(dev->dma, taddr, c, size) != 0) ? + H_DEST_PARM : H_SUCCESS; +} + +#define vio_stb(_dev, _addr, _val) (stb_dma((_dev)->dma, (_addr), (_val))) +#define vio_sth(_dev, _addr, _val) (stw_be_dma((_dev)->dma, (_addr), (_val))) +#define vio_stl(_dev, _addr, _val) (stl_be_dma((_dev)->dma, (_addr), (_val))) +#define vio_stq(_dev, _addr, _val) (stq_be_dma((_dev)->dma, (_addr), (_val))) +#define vio_ldq(_dev, _addr) (ldq_be_dma((_dev)->dma, (_addr))) int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq); diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 2f09616dd5..3cf5844e0f 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -165,7 +165,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"); @@ -281,9 +281,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; @@ -329,10 +329,11 @@ 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); @@ -345,12 +346,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", @@ -728,7 +729,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"); @@ -742,7 +743,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"); @@ -805,7 +806,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"); vscsi_put_req(req); @@ -947,7 +948,7 @@ static int spapr_vscsi_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) } static Property spapr_vscsi_properties[] = { - DEFINE_SPAPR_PROPERTIES(VSCSIState, vdev, 0x10000000), + DEFINE_SPAPR_PROPERTIES(VSCSIState, vdev), DEFINE_PROP_END_OF_LIST(), }; @@ -964,6 +965,7 @@ static void spapr_vscsi_class_init(ObjectClass *klass, void *data) k->dt_compatible = "IBM,v-scsi"; k->signal_mask = 0x00000001; dc->props = spapr_vscsi_properties; + k->rtce_window_size = 0x10000000; } static TypeInfo spapr_vscsi_info = { diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c index f340b83237..99e52cc6b7 100644 --- a/hw/spapr_vty.c +++ b/hw/spapr_vty.c @@ -133,7 +133,7 @@ void spapr_vty_create(VIOsPAPRBus *bus, CharDriverState *chardev) } static Property spapr_vty_properties[] = { - DEFINE_SPAPR_PROPERTIES(VIOsPAPRVTYDevice, sdev, 0), + DEFINE_SPAPR_PROPERTIES(VIOsPAPRVTYDevice, sdev), DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev), DEFINE_PROP_END_OF_LIST(), }; diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index b6ef72d16b..829e180f8b 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -1067,7 +1067,7 @@ void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd) return NULL; } - len = (window_size / SPAPR_VIO_TCE_PAGE_SIZE) * sizeof(VIOsPAPR_RTCE); + len = (window_size / SPAPR_TCE_PAGE_SIZE) * sizeof(sPAPRTCE); /* FIXME: round this up to page size */ table = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); @@ -1090,7 +1090,7 @@ int kvmppc_remove_spapr_tce(void *table, int fd, uint32_t window_size) return -1; } - len = (window_size / SPAPR_VIO_TCE_PAGE_SIZE)*sizeof(VIOsPAPR_RTCE); + len = (window_size / SPAPR_TCE_PAGE_SIZE)*sizeof(sPAPRTCE); if ((munmap(table, len) < 0) || (close(fd) < 0)) { fprintf(stderr, "KVM: Unexpected error removing TCE table: %s",