19f64bd8aSPaolo Bonzini /* 29f64bd8aSPaolo Bonzini * QEMU sPAPR IOMMU (TCE) code 39f64bd8aSPaolo Bonzini * 49f64bd8aSPaolo Bonzini * Copyright (c) 2010 David Gibson, IBM Corporation <dwg@au1.ibm.com> 59f64bd8aSPaolo Bonzini * 69f64bd8aSPaolo Bonzini * This library is free software; you can redistribute it and/or 79f64bd8aSPaolo Bonzini * modify it under the terms of the GNU Lesser General Public 89f64bd8aSPaolo Bonzini * License as published by the Free Software Foundation; either 99f64bd8aSPaolo Bonzini * version 2 of the License, or (at your option) any later version. 109f64bd8aSPaolo Bonzini * 119f64bd8aSPaolo Bonzini * This library is distributed in the hope that it will be useful, 129f64bd8aSPaolo Bonzini * but WITHOUT ANY WARRANTY; without even the implied warranty of 139f64bd8aSPaolo Bonzini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 149f64bd8aSPaolo Bonzini * Lesser General Public License for more details. 159f64bd8aSPaolo Bonzini * 169f64bd8aSPaolo Bonzini * You should have received a copy of the GNU Lesser General Public 179f64bd8aSPaolo Bonzini * License along with this library; if not, see <http://www.gnu.org/licenses/>. 189f64bd8aSPaolo Bonzini */ 190d75590dSPeter Maydell #include "qemu/osdep.h" 209f64bd8aSPaolo Bonzini #include "hw/hw.h" 2103dd024fSPaolo Bonzini #include "qemu/log.h" 229f64bd8aSPaolo Bonzini #include "sysemu/kvm.h" 239f64bd8aSPaolo Bonzini #include "hw/qdev.h" 249f64bd8aSPaolo Bonzini #include "kvm_ppc.h" 259f64bd8aSPaolo Bonzini #include "sysemu/dma.h" 269f64bd8aSPaolo Bonzini #include "exec/address-spaces.h" 277e472264SAlexey Kardashevskiy #include "trace.h" 289f64bd8aSPaolo Bonzini 290d09e41aSPaolo Bonzini #include "hw/ppc/spapr.h" 30ee9a569aSAlexey Kardashevskiy #include "hw/ppc/spapr_vio.h" 319f64bd8aSPaolo Bonzini 329f64bd8aSPaolo Bonzini #include <libfdt.h> 339f64bd8aSPaolo Bonzini 349f64bd8aSPaolo Bonzini enum sPAPRTCEAccess { 359f64bd8aSPaolo Bonzini SPAPR_TCE_FAULT = 0, 369f64bd8aSPaolo Bonzini SPAPR_TCE_RO = 1, 379f64bd8aSPaolo Bonzini SPAPR_TCE_WO = 2, 389f64bd8aSPaolo Bonzini SPAPR_TCE_RW = 3, 399f64bd8aSPaolo Bonzini }; 409f64bd8aSPaolo Bonzini 41650f33adSAlexey Kardashevskiy #define IOMMU_PAGE_SIZE(shift) (1ULL << (shift)) 42650f33adSAlexey Kardashevskiy #define IOMMU_PAGE_MASK(shift) (~(IOMMU_PAGE_SIZE(shift) - 1)) 43650f33adSAlexey Kardashevskiy 446a0a70b0SStefan Weil static QLIST_HEAD(spapr_tce_tables, sPAPRTCETable) spapr_tce_tables; 459f64bd8aSPaolo Bonzini 46f9ce8e0aSThomas Huth sPAPRTCETable *spapr_tce_find_by_liobn(target_ulong liobn) 479f64bd8aSPaolo Bonzini { 489f64bd8aSPaolo Bonzini sPAPRTCETable *tcet; 499f64bd8aSPaolo Bonzini 50d4261662SDavid Gibson if (liobn & 0xFFFFFFFF00000000ULL) { 51d4261662SDavid Gibson hcall_dprintf("Request for out-of-bounds LIOBN 0x" TARGET_FMT_lx "\n", 52d4261662SDavid Gibson liobn); 53d4261662SDavid Gibson return NULL; 54d4261662SDavid Gibson } 55d4261662SDavid Gibson 569f64bd8aSPaolo Bonzini QLIST_FOREACH(tcet, &spapr_tce_tables, list) { 57f9ce8e0aSThomas Huth if (tcet->liobn == (uint32_t)liobn) { 589f64bd8aSPaolo Bonzini return tcet; 599f64bd8aSPaolo Bonzini } 609f64bd8aSPaolo Bonzini } 619f64bd8aSPaolo Bonzini 629f64bd8aSPaolo Bonzini return NULL; 639f64bd8aSPaolo Bonzini } 649f64bd8aSPaolo Bonzini 655709af3bSGreg Kurz static IOMMUAccessFlags spapr_tce_iommu_access_flags(uint64_t tce) 665709af3bSGreg Kurz { 675709af3bSGreg Kurz switch (tce & SPAPR_TCE_RW) { 685709af3bSGreg Kurz case SPAPR_TCE_FAULT: 695709af3bSGreg Kurz return IOMMU_NONE; 705709af3bSGreg Kurz case SPAPR_TCE_RO: 715709af3bSGreg Kurz return IOMMU_RO; 725709af3bSGreg Kurz case SPAPR_TCE_WO: 735709af3bSGreg Kurz return IOMMU_WO; 745709af3bSGreg Kurz default: /* SPAPR_TCE_RW */ 755709af3bSGreg Kurz return IOMMU_RW; 765709af3bSGreg Kurz } 775709af3bSGreg Kurz } 785709af3bSGreg Kurz 79*fec5d3a1SAlexey Kardashevskiy static uint64_t *spapr_tce_alloc_table(uint32_t liobn, 80*fec5d3a1SAlexey Kardashevskiy uint32_t page_shift, 81*fec5d3a1SAlexey Kardashevskiy uint32_t nb_table, 82*fec5d3a1SAlexey Kardashevskiy int *fd, 83*fec5d3a1SAlexey Kardashevskiy bool need_vfio) 84*fec5d3a1SAlexey Kardashevskiy { 85*fec5d3a1SAlexey Kardashevskiy uint64_t *table = NULL; 86*fec5d3a1SAlexey Kardashevskiy uint64_t window_size = (uint64_t)nb_table << page_shift; 87*fec5d3a1SAlexey Kardashevskiy 88*fec5d3a1SAlexey Kardashevskiy if (kvm_enabled() && !(window_size >> 32)) { 89*fec5d3a1SAlexey Kardashevskiy table = kvmppc_create_spapr_tce(liobn, window_size, fd, need_vfio); 90*fec5d3a1SAlexey Kardashevskiy } 91*fec5d3a1SAlexey Kardashevskiy 92*fec5d3a1SAlexey Kardashevskiy if (!table) { 93*fec5d3a1SAlexey Kardashevskiy *fd = -1; 94*fec5d3a1SAlexey Kardashevskiy table = g_malloc0(nb_table * sizeof(uint64_t)); 95*fec5d3a1SAlexey Kardashevskiy } 96*fec5d3a1SAlexey Kardashevskiy 97*fec5d3a1SAlexey Kardashevskiy trace_spapr_iommu_new_table(liobn, table, *fd); 98*fec5d3a1SAlexey Kardashevskiy 99*fec5d3a1SAlexey Kardashevskiy return table; 100*fec5d3a1SAlexey Kardashevskiy } 101*fec5d3a1SAlexey Kardashevskiy 102*fec5d3a1SAlexey Kardashevskiy static void spapr_tce_free_table(uint64_t *table, int fd, uint32_t nb_table) 103*fec5d3a1SAlexey Kardashevskiy { 104*fec5d3a1SAlexey Kardashevskiy if (!kvm_enabled() || 105*fec5d3a1SAlexey Kardashevskiy (kvmppc_remove_spapr_tce(table, fd, nb_table) != 0)) { 106*fec5d3a1SAlexey Kardashevskiy g_free(table); 107*fec5d3a1SAlexey Kardashevskiy } 108*fec5d3a1SAlexey Kardashevskiy } 109*fec5d3a1SAlexey Kardashevskiy 11079e2b9aeSPaolo Bonzini /* Called from RCU critical section */ 1118d7b8cb9SLe Tan static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr, 1128d7b8cb9SLe Tan bool is_write) 1139f64bd8aSPaolo Bonzini { 114a84bb436SPaolo Bonzini sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu); 1159f64bd8aSPaolo Bonzini uint64_t tce; 1167e472264SAlexey Kardashevskiy IOMMUTLBEntry ret = { 117a71bfbfeSPaolo Bonzini .target_as = &address_space_memory, 118a71bfbfeSPaolo Bonzini .iova = 0, 119a71bfbfeSPaolo Bonzini .translated_addr = 0, 120a71bfbfeSPaolo Bonzini .addr_mask = ~(hwaddr)0, 1217e472264SAlexey Kardashevskiy .perm = IOMMU_NONE, 122a71bfbfeSPaolo Bonzini }; 1239f64bd8aSPaolo Bonzini 124ee9a569aSAlexey Kardashevskiy if ((addr >> tcet->page_shift) < tcet->nb_table) { 1259f64bd8aSPaolo Bonzini /* Check if we are in bound */ 126650f33adSAlexey Kardashevskiy hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift); 127650f33adSAlexey Kardashevskiy 128650f33adSAlexey Kardashevskiy tce = tcet->table[addr >> tcet->page_shift]; 129650f33adSAlexey Kardashevskiy ret.iova = addr & page_mask; 130650f33adSAlexey Kardashevskiy ret.translated_addr = tce & page_mask; 131650f33adSAlexey Kardashevskiy ret.addr_mask = ~page_mask; 1325709af3bSGreg Kurz ret.perm = spapr_tce_iommu_access_flags(tce); 1337e472264SAlexey Kardashevskiy } 1347e472264SAlexey Kardashevskiy trace_spapr_iommu_xlate(tcet->liobn, addr, ret.iova, ret.perm, 1357e472264SAlexey Kardashevskiy ret.addr_mask); 1369f64bd8aSPaolo Bonzini 1377e472264SAlexey Kardashevskiy return ret; 138a71bfbfeSPaolo Bonzini } 139a71bfbfeSPaolo Bonzini 140ee9a569aSAlexey Kardashevskiy static int spapr_tce_table_post_load(void *opaque, int version_id) 141ee9a569aSAlexey Kardashevskiy { 142ee9a569aSAlexey Kardashevskiy sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque); 143ee9a569aSAlexey Kardashevskiy 144ee9a569aSAlexey Kardashevskiy if (tcet->vdev) { 145ee9a569aSAlexey Kardashevskiy spapr_vio_set_bypass(tcet->vdev, tcet->bypass); 146ee9a569aSAlexey Kardashevskiy } 147ee9a569aSAlexey Kardashevskiy 148ee9a569aSAlexey Kardashevskiy return 0; 149ee9a569aSAlexey Kardashevskiy } 150ee9a569aSAlexey Kardashevskiy 151a83000f5SAnthony Liguori static const VMStateDescription vmstate_spapr_tce_table = { 152a83000f5SAnthony Liguori .name = "spapr_iommu", 153523e7b8aSAlexey Kardashevskiy .version_id = 2, 154523e7b8aSAlexey Kardashevskiy .minimum_version_id = 2, 155ee9a569aSAlexey Kardashevskiy .post_load = spapr_tce_table_post_load, 156a83000f5SAnthony Liguori .fields = (VMStateField []) { 157a83000f5SAnthony Liguori /* Sanity check */ 158a83000f5SAnthony Liguori VMSTATE_UINT32_EQUAL(liobn, sPAPRTCETable), 159523e7b8aSAlexey Kardashevskiy VMSTATE_UINT32_EQUAL(nb_table, sPAPRTCETable), 160a83000f5SAnthony Liguori 161a83000f5SAnthony Liguori /* IOMMU state */ 162a83000f5SAnthony Liguori VMSTATE_BOOL(bypass, sPAPRTCETable), 163a83000f5SAnthony Liguori VMSTATE_VARRAY_UINT32(table, sPAPRTCETable, nb_table, 0, vmstate_info_uint64, uint64_t), 164a83000f5SAnthony Liguori 165a83000f5SAnthony Liguori VMSTATE_END_OF_LIST() 166a83000f5SAnthony Liguori }, 167a83000f5SAnthony Liguori }; 168a83000f5SAnthony Liguori 169a84bb436SPaolo Bonzini static MemoryRegionIOMMUOps spapr_iommu_ops = { 170a84bb436SPaolo Bonzini .translate = spapr_tce_translate_iommu, 171a84bb436SPaolo Bonzini }; 1729f64bd8aSPaolo Bonzini 173a83000f5SAnthony Liguori static int spapr_tce_table_realize(DeviceState *dev) 174a83000f5SAnthony Liguori { 175a83000f5SAnthony Liguori sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); 176a83000f5SAnthony Liguori 177*fec5d3a1SAlexey Kardashevskiy tcet->fd = -1; 178*fec5d3a1SAlexey Kardashevskiy tcet->table = spapr_tce_alloc_table(tcet->liobn, 179*fec5d3a1SAlexey Kardashevskiy tcet->page_shift, 180*fec5d3a1SAlexey Kardashevskiy tcet->nb_table, 1819bb62a07SAlexey Kardashevskiy &tcet->fd, 1826a81dd17SDavid Gibson tcet->need_vfio); 183a83000f5SAnthony Liguori 184a83000f5SAnthony Liguori memory_region_init_iommu(&tcet->iommu, OBJECT(dev), &spapr_iommu_ops, 185ee9a569aSAlexey Kardashevskiy "iommu-spapr", 186ee9a569aSAlexey Kardashevskiy (uint64_t)tcet->nb_table << tcet->page_shift); 187a83000f5SAnthony Liguori 188a83000f5SAnthony Liguori QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); 189a83000f5SAnthony Liguori 19000d4f525SAlexey Kardashevskiy vmstate_register(DEVICE(tcet), tcet->liobn, &vmstate_spapr_tce_table, 19100d4f525SAlexey Kardashevskiy tcet); 19200d4f525SAlexey Kardashevskiy 193a83000f5SAnthony Liguori return 0; 194a83000f5SAnthony Liguori } 195a83000f5SAnthony Liguori 196c10325d6SDavid Gibson void spapr_tce_set_need_vfio(sPAPRTCETable *tcet, bool need_vfio) 197c10325d6SDavid Gibson { 198c10325d6SDavid Gibson size_t table_size = tcet->nb_table * sizeof(uint64_t); 199c10325d6SDavid Gibson void *newtable; 200c10325d6SDavid Gibson 201c10325d6SDavid Gibson if (need_vfio == tcet->need_vfio) { 202c10325d6SDavid Gibson /* Nothing to do */ 203c10325d6SDavid Gibson return; 204c10325d6SDavid Gibson } 205c10325d6SDavid Gibson 206c10325d6SDavid Gibson if (!need_vfio) { 207c10325d6SDavid Gibson /* FIXME: We don't support transition back to KVM accelerated 208c10325d6SDavid Gibson * TCEs yet */ 209c10325d6SDavid Gibson return; 210c10325d6SDavid Gibson } 211c10325d6SDavid Gibson 212c10325d6SDavid Gibson tcet->need_vfio = true; 213c10325d6SDavid Gibson 214c10325d6SDavid Gibson if (tcet->fd < 0) { 215c10325d6SDavid Gibson /* Table is already in userspace, nothing to be do */ 216c10325d6SDavid Gibson return; 217c10325d6SDavid Gibson } 218c10325d6SDavid Gibson 219c10325d6SDavid Gibson newtable = g_malloc(table_size); 220c10325d6SDavid Gibson memcpy(newtable, tcet->table, table_size); 221c10325d6SDavid Gibson 222c10325d6SDavid Gibson kvmppc_remove_spapr_tce(tcet->table, tcet->fd, tcet->nb_table); 223c10325d6SDavid Gibson 224c10325d6SDavid Gibson tcet->fd = -1; 225c10325d6SDavid Gibson tcet->table = newtable; 226c10325d6SDavid Gibson } 227c10325d6SDavid Gibson 228523e7b8aSAlexey Kardashevskiy sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, 2291b8eceeeSAlexey Kardashevskiy uint64_t bus_offset, 230650f33adSAlexey Kardashevskiy uint32_t page_shift, 2319bb62a07SAlexey Kardashevskiy uint32_t nb_table, 2326a81dd17SDavid Gibson bool need_vfio) 2339f64bd8aSPaolo Bonzini { 2349f64bd8aSPaolo Bonzini sPAPRTCETable *tcet; 235dea1b3ceSAlexey Kardashevskiy char tmp[64]; 2369f64bd8aSPaolo Bonzini 2379f64bd8aSPaolo Bonzini if (spapr_tce_find_by_liobn(liobn)) { 2389f64bd8aSPaolo Bonzini fprintf(stderr, "Attempted to create TCE table with duplicate" 2399f64bd8aSPaolo Bonzini " LIOBN 0x%x\n", liobn); 2409f64bd8aSPaolo Bonzini return NULL; 2419f64bd8aSPaolo Bonzini } 2429f64bd8aSPaolo Bonzini 243523e7b8aSAlexey Kardashevskiy if (!nb_table) { 2449f64bd8aSPaolo Bonzini return NULL; 2459f64bd8aSPaolo Bonzini } 2469f64bd8aSPaolo Bonzini 247a83000f5SAnthony Liguori tcet = SPAPR_TCE_TABLE(object_new(TYPE_SPAPR_TCE_TABLE)); 2489f64bd8aSPaolo Bonzini tcet->liobn = liobn; 2491b8eceeeSAlexey Kardashevskiy tcet->bus_offset = bus_offset; 250650f33adSAlexey Kardashevskiy tcet->page_shift = page_shift; 251523e7b8aSAlexey Kardashevskiy tcet->nb_table = nb_table; 2526a81dd17SDavid Gibson tcet->need_vfio = need_vfio; 2539f64bd8aSPaolo Bonzini 254dea1b3ceSAlexey Kardashevskiy snprintf(tmp, sizeof(tmp), "tce-table-%x", liobn); 255dea1b3ceSAlexey Kardashevskiy object_property_add_child(OBJECT(owner), tmp, OBJECT(tcet), NULL); 2569f64bd8aSPaolo Bonzini 257e4c35b78SAlexey Kardashevskiy object_property_set_bool(OBJECT(tcet), true, "realized", NULL); 2589f64bd8aSPaolo Bonzini 2592b7dc949SPaolo Bonzini return tcet; 2609f64bd8aSPaolo Bonzini } 2619f64bd8aSPaolo Bonzini 2625f9490deSDavid Gibson static void spapr_tce_table_unrealize(DeviceState *dev, Error **errp) 2639f64bd8aSPaolo Bonzini { 2645f9490deSDavid Gibson sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); 265a83000f5SAnthony Liguori 2669f64bd8aSPaolo Bonzini QLIST_REMOVE(tcet, list); 2679f64bd8aSPaolo Bonzini 268*fec5d3a1SAlexey Kardashevskiy spapr_tce_free_table(tcet->table, tcet->fd, tcet->nb_table); 269*fec5d3a1SAlexey Kardashevskiy tcet->fd = -1; 2709f64bd8aSPaolo Bonzini } 2712b7dc949SPaolo Bonzini 272a84bb436SPaolo Bonzini MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet) 273a84bb436SPaolo Bonzini { 274a84bb436SPaolo Bonzini return &tcet->iommu; 275a84bb436SPaolo Bonzini } 276a84bb436SPaolo Bonzini 277a83000f5SAnthony Liguori static void spapr_tce_reset(DeviceState *dev) 2789f64bd8aSPaolo Bonzini { 279a83000f5SAnthony Liguori sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); 280523e7b8aSAlexey Kardashevskiy size_t table_size = tcet->nb_table * sizeof(uint64_t); 2819f64bd8aSPaolo Bonzini 2829f64bd8aSPaolo Bonzini memset(tcet->table, 0, table_size); 2839f64bd8aSPaolo Bonzini } 2849f64bd8aSPaolo Bonzini 2859f64bd8aSPaolo Bonzini static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, 2869f64bd8aSPaolo Bonzini target_ulong tce) 2879f64bd8aSPaolo Bonzini { 288a84bb436SPaolo Bonzini IOMMUTLBEntry entry; 289650f33adSAlexey Kardashevskiy hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift); 2901b8eceeeSAlexey Kardashevskiy unsigned long index = (ioba - tcet->bus_offset) >> tcet->page_shift; 2919f64bd8aSPaolo Bonzini 2921b8eceeeSAlexey Kardashevskiy if (index >= tcet->nb_table) { 293b55519a0SDavid Gibson hcall_dprintf("spapr_vio_put_tce on out-of-bounds IOBA 0x" 2949f64bd8aSPaolo Bonzini TARGET_FMT_lx "\n", ioba); 2959f64bd8aSPaolo Bonzini return H_PARAMETER; 2969f64bd8aSPaolo Bonzini } 2979f64bd8aSPaolo Bonzini 2981b8eceeeSAlexey Kardashevskiy tcet->table[index] = tce; 2999f64bd8aSPaolo Bonzini 300a84bb436SPaolo Bonzini entry.target_as = &address_space_memory, 301d78c19b5SAlexey Kardashevskiy entry.iova = (ioba - tcet->bus_offset) & page_mask; 302650f33adSAlexey Kardashevskiy entry.translated_addr = tce & page_mask; 303650f33adSAlexey Kardashevskiy entry.addr_mask = ~page_mask; 3045709af3bSGreg Kurz entry.perm = spapr_tce_iommu_access_flags(tce); 305a84bb436SPaolo Bonzini memory_region_notify_iommu(&tcet->iommu, entry); 306a84bb436SPaolo Bonzini 3079f64bd8aSPaolo Bonzini return H_SUCCESS; 3089f64bd8aSPaolo Bonzini } 3099f64bd8aSPaolo Bonzini 310da95324eSAlexey Kardashevskiy static target_ulong h_put_tce_indirect(PowerPCCPU *cpu, 31128e02042SDavid Gibson sPAPRMachineState *spapr, 312da95324eSAlexey Kardashevskiy target_ulong opcode, target_ulong *args) 313da95324eSAlexey Kardashevskiy { 314da95324eSAlexey Kardashevskiy int i; 315da95324eSAlexey Kardashevskiy target_ulong liobn = args[0]; 316da95324eSAlexey Kardashevskiy target_ulong ioba = args[1]; 317da95324eSAlexey Kardashevskiy target_ulong ioba1 = ioba; 318da95324eSAlexey Kardashevskiy target_ulong tce_list = args[2]; 319da95324eSAlexey Kardashevskiy target_ulong npages = args[3]; 320f1215ea7SAlexey Kardashevskiy target_ulong ret = H_PARAMETER, tce = 0; 321da95324eSAlexey Kardashevskiy sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); 322da95324eSAlexey Kardashevskiy CPUState *cs = CPU(cpu); 323650f33adSAlexey Kardashevskiy hwaddr page_mask, page_size; 324da95324eSAlexey Kardashevskiy 325da95324eSAlexey Kardashevskiy if (!tcet) { 326da95324eSAlexey Kardashevskiy return H_PARAMETER; 327da95324eSAlexey Kardashevskiy } 328da95324eSAlexey Kardashevskiy 329650f33adSAlexey Kardashevskiy if ((npages > 512) || (tce_list & SPAPR_TCE_PAGE_MASK)) { 330da95324eSAlexey Kardashevskiy return H_PARAMETER; 331da95324eSAlexey Kardashevskiy } 332da95324eSAlexey Kardashevskiy 333650f33adSAlexey Kardashevskiy page_mask = IOMMU_PAGE_MASK(tcet->page_shift); 334650f33adSAlexey Kardashevskiy page_size = IOMMU_PAGE_SIZE(tcet->page_shift); 335650f33adSAlexey Kardashevskiy ioba &= page_mask; 336da95324eSAlexey Kardashevskiy 337650f33adSAlexey Kardashevskiy for (i = 0; i < npages; ++i, ioba += page_size) { 3384d9ab7d4SGreg Kurz tce = ldq_be_phys(cs->as, tce_list + i * sizeof(target_ulong)); 339650f33adSAlexey Kardashevskiy 340da95324eSAlexey Kardashevskiy ret = put_tce_emu(tcet, ioba, tce); 341da95324eSAlexey Kardashevskiy if (ret) { 342da95324eSAlexey Kardashevskiy break; 343da95324eSAlexey Kardashevskiy } 344da95324eSAlexey Kardashevskiy } 345da95324eSAlexey Kardashevskiy 346da95324eSAlexey Kardashevskiy /* Trace last successful or the first problematic entry */ 347da95324eSAlexey Kardashevskiy i = i ? (i - 1) : 0; 348d9d96a3cSAlexey Kardashevskiy if (SPAPR_IS_PCI_LIOBN(liobn)) { 349d9d96a3cSAlexey Kardashevskiy trace_spapr_iommu_pci_indirect(liobn, ioba1, tce_list, i, tce, ret); 350d9d96a3cSAlexey Kardashevskiy } else { 351d9d96a3cSAlexey Kardashevskiy trace_spapr_iommu_indirect(liobn, ioba1, tce_list, i, tce, ret); 352d9d96a3cSAlexey Kardashevskiy } 353da95324eSAlexey Kardashevskiy return ret; 354da95324eSAlexey Kardashevskiy } 355da95324eSAlexey Kardashevskiy 35628e02042SDavid Gibson static target_ulong h_stuff_tce(PowerPCCPU *cpu, sPAPRMachineState *spapr, 357da95324eSAlexey Kardashevskiy target_ulong opcode, target_ulong *args) 358da95324eSAlexey Kardashevskiy { 359da95324eSAlexey Kardashevskiy int i; 360da95324eSAlexey Kardashevskiy target_ulong liobn = args[0]; 361da95324eSAlexey Kardashevskiy target_ulong ioba = args[1]; 362da95324eSAlexey Kardashevskiy target_ulong tce_value = args[2]; 363da95324eSAlexey Kardashevskiy target_ulong npages = args[3]; 364da95324eSAlexey Kardashevskiy target_ulong ret = H_PARAMETER; 365da95324eSAlexey Kardashevskiy sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); 366650f33adSAlexey Kardashevskiy hwaddr page_mask, page_size; 367da95324eSAlexey Kardashevskiy 368da95324eSAlexey Kardashevskiy if (!tcet) { 369da95324eSAlexey Kardashevskiy return H_PARAMETER; 370da95324eSAlexey Kardashevskiy } 371da95324eSAlexey Kardashevskiy 372da95324eSAlexey Kardashevskiy if (npages > tcet->nb_table) { 373da95324eSAlexey Kardashevskiy return H_PARAMETER; 374da95324eSAlexey Kardashevskiy } 375da95324eSAlexey Kardashevskiy 376650f33adSAlexey Kardashevskiy page_mask = IOMMU_PAGE_MASK(tcet->page_shift); 377650f33adSAlexey Kardashevskiy page_size = IOMMU_PAGE_SIZE(tcet->page_shift); 378650f33adSAlexey Kardashevskiy ioba &= page_mask; 379da95324eSAlexey Kardashevskiy 380650f33adSAlexey Kardashevskiy for (i = 0; i < npages; ++i, ioba += page_size) { 381da95324eSAlexey Kardashevskiy ret = put_tce_emu(tcet, ioba, tce_value); 382da95324eSAlexey Kardashevskiy if (ret) { 383da95324eSAlexey Kardashevskiy break; 384da95324eSAlexey Kardashevskiy } 385da95324eSAlexey Kardashevskiy } 386d9d96a3cSAlexey Kardashevskiy if (SPAPR_IS_PCI_LIOBN(liobn)) { 387d9d96a3cSAlexey Kardashevskiy trace_spapr_iommu_pci_stuff(liobn, ioba, tce_value, npages, ret); 388d9d96a3cSAlexey Kardashevskiy } else { 389da95324eSAlexey Kardashevskiy trace_spapr_iommu_stuff(liobn, ioba, tce_value, npages, ret); 390d9d96a3cSAlexey Kardashevskiy } 391da95324eSAlexey Kardashevskiy 392da95324eSAlexey Kardashevskiy return ret; 393da95324eSAlexey Kardashevskiy } 394da95324eSAlexey Kardashevskiy 39528e02042SDavid Gibson static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPRMachineState *spapr, 3969f64bd8aSPaolo Bonzini target_ulong opcode, target_ulong *args) 3979f64bd8aSPaolo Bonzini { 3989f64bd8aSPaolo Bonzini target_ulong liobn = args[0]; 3999f64bd8aSPaolo Bonzini target_ulong ioba = args[1]; 4009f64bd8aSPaolo Bonzini target_ulong tce = args[2]; 4017e472264SAlexey Kardashevskiy target_ulong ret = H_PARAMETER; 4029f64bd8aSPaolo Bonzini sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); 4039f64bd8aSPaolo Bonzini 4049f64bd8aSPaolo Bonzini if (tcet) { 405650f33adSAlexey Kardashevskiy hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift); 406650f33adSAlexey Kardashevskiy 407650f33adSAlexey Kardashevskiy ioba &= page_mask; 408650f33adSAlexey Kardashevskiy 4097e472264SAlexey Kardashevskiy ret = put_tce_emu(tcet, ioba, tce); 4109f64bd8aSPaolo Bonzini } 411d9d96a3cSAlexey Kardashevskiy if (SPAPR_IS_PCI_LIOBN(liobn)) { 412d9d96a3cSAlexey Kardashevskiy trace_spapr_iommu_pci_put(liobn, ioba, tce, ret); 413d9d96a3cSAlexey Kardashevskiy } else { 4147e472264SAlexey Kardashevskiy trace_spapr_iommu_put(liobn, ioba, tce, ret); 415d9d96a3cSAlexey Kardashevskiy } 4169f64bd8aSPaolo Bonzini 4177e472264SAlexey Kardashevskiy return ret; 4189f64bd8aSPaolo Bonzini } 4199f64bd8aSPaolo Bonzini 420a0fcac9cSLaurent Dufour static target_ulong get_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, 421a0fcac9cSLaurent Dufour target_ulong *tce) 422a0fcac9cSLaurent Dufour { 4231b8eceeeSAlexey Kardashevskiy unsigned long index = (ioba - tcet->bus_offset) >> tcet->page_shift; 4241b8eceeeSAlexey Kardashevskiy 4251b8eceeeSAlexey Kardashevskiy if (index >= tcet->nb_table) { 426a0fcac9cSLaurent Dufour hcall_dprintf("spapr_iommu_get_tce on out-of-bounds IOBA 0x" 427a0fcac9cSLaurent Dufour TARGET_FMT_lx "\n", ioba); 428a0fcac9cSLaurent Dufour return H_PARAMETER; 429a0fcac9cSLaurent Dufour } 430a0fcac9cSLaurent Dufour 4311b8eceeeSAlexey Kardashevskiy *tce = tcet->table[index]; 432a0fcac9cSLaurent Dufour 433a0fcac9cSLaurent Dufour return H_SUCCESS; 434a0fcac9cSLaurent Dufour } 435a0fcac9cSLaurent Dufour 43628e02042SDavid Gibson static target_ulong h_get_tce(PowerPCCPU *cpu, sPAPRMachineState *spapr, 437a0fcac9cSLaurent Dufour target_ulong opcode, target_ulong *args) 438a0fcac9cSLaurent Dufour { 439a0fcac9cSLaurent Dufour target_ulong liobn = args[0]; 440a0fcac9cSLaurent Dufour target_ulong ioba = args[1]; 441a0fcac9cSLaurent Dufour target_ulong tce = 0; 442a0fcac9cSLaurent Dufour target_ulong ret = H_PARAMETER; 443a0fcac9cSLaurent Dufour sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); 444a0fcac9cSLaurent Dufour 445a0fcac9cSLaurent Dufour if (tcet) { 446650f33adSAlexey Kardashevskiy hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift); 447650f33adSAlexey Kardashevskiy 448650f33adSAlexey Kardashevskiy ioba &= page_mask; 449650f33adSAlexey Kardashevskiy 450a0fcac9cSLaurent Dufour ret = get_tce_emu(tcet, ioba, &tce); 451a0fcac9cSLaurent Dufour if (!ret) { 452a0fcac9cSLaurent Dufour args[0] = tce; 453a0fcac9cSLaurent Dufour } 454a0fcac9cSLaurent Dufour } 455d9d96a3cSAlexey Kardashevskiy if (SPAPR_IS_PCI_LIOBN(liobn)) { 456d9d96a3cSAlexey Kardashevskiy trace_spapr_iommu_pci_get(liobn, ioba, ret, tce); 457d9d96a3cSAlexey Kardashevskiy } else { 458a0fcac9cSLaurent Dufour trace_spapr_iommu_get(liobn, ioba, ret, tce); 459d9d96a3cSAlexey Kardashevskiy } 460a0fcac9cSLaurent Dufour 461a0fcac9cSLaurent Dufour return ret; 462a0fcac9cSLaurent Dufour } 463a0fcac9cSLaurent Dufour 4649f64bd8aSPaolo Bonzini int spapr_dma_dt(void *fdt, int node_off, const char *propname, 4659f64bd8aSPaolo Bonzini uint32_t liobn, uint64_t window, uint32_t size) 4669f64bd8aSPaolo Bonzini { 4679f64bd8aSPaolo Bonzini uint32_t dma_prop[5]; 4689f64bd8aSPaolo Bonzini int ret; 4699f64bd8aSPaolo Bonzini 4709f64bd8aSPaolo Bonzini dma_prop[0] = cpu_to_be32(liobn); 4719f64bd8aSPaolo Bonzini dma_prop[1] = cpu_to_be32(window >> 32); 4729f64bd8aSPaolo Bonzini dma_prop[2] = cpu_to_be32(window & 0xFFFFFFFF); 4739f64bd8aSPaolo Bonzini dma_prop[3] = 0; /* window size is 32 bits */ 4749f64bd8aSPaolo Bonzini dma_prop[4] = cpu_to_be32(size); 4759f64bd8aSPaolo Bonzini 4769f64bd8aSPaolo Bonzini ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2); 4779f64bd8aSPaolo Bonzini if (ret < 0) { 4789f64bd8aSPaolo Bonzini return ret; 4799f64bd8aSPaolo Bonzini } 4809f64bd8aSPaolo Bonzini 4819f64bd8aSPaolo Bonzini ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2); 4829f64bd8aSPaolo Bonzini if (ret < 0) { 4839f64bd8aSPaolo Bonzini return ret; 4849f64bd8aSPaolo Bonzini } 4859f64bd8aSPaolo Bonzini 4869f64bd8aSPaolo Bonzini ret = fdt_setprop(fdt, node_off, propname, dma_prop, sizeof(dma_prop)); 4879f64bd8aSPaolo Bonzini if (ret < 0) { 4889f64bd8aSPaolo Bonzini return ret; 4899f64bd8aSPaolo Bonzini } 4909f64bd8aSPaolo Bonzini 4919f64bd8aSPaolo Bonzini return 0; 4929f64bd8aSPaolo Bonzini } 4939f64bd8aSPaolo Bonzini 4949f64bd8aSPaolo Bonzini int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, 4952b7dc949SPaolo Bonzini sPAPRTCETable *tcet) 4969f64bd8aSPaolo Bonzini { 4972b7dc949SPaolo Bonzini if (!tcet) { 4989f64bd8aSPaolo Bonzini return 0; 4999f64bd8aSPaolo Bonzini } 5009f64bd8aSPaolo Bonzini 5019f64bd8aSPaolo Bonzini return spapr_dma_dt(fdt, node_off, propname, 502650f33adSAlexey Kardashevskiy tcet->liobn, 0, tcet->nb_table << tcet->page_shift); 5039f64bd8aSPaolo Bonzini } 504a83000f5SAnthony Liguori 505a83000f5SAnthony Liguori static void spapr_tce_table_class_init(ObjectClass *klass, void *data) 506a83000f5SAnthony Liguori { 507a83000f5SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 508a83000f5SAnthony Liguori dc->init = spapr_tce_table_realize; 509a83000f5SAnthony Liguori dc->reset = spapr_tce_reset; 5105f9490deSDavid Gibson dc->unrealize = spapr_tce_table_unrealize; 511a83000f5SAnthony Liguori 512a83000f5SAnthony Liguori QLIST_INIT(&spapr_tce_tables); 513a83000f5SAnthony Liguori 514a83000f5SAnthony Liguori /* hcall-tce */ 515a83000f5SAnthony Liguori spapr_register_hypercall(H_PUT_TCE, h_put_tce); 516a0fcac9cSLaurent Dufour spapr_register_hypercall(H_GET_TCE, h_get_tce); 517da95324eSAlexey Kardashevskiy spapr_register_hypercall(H_PUT_TCE_INDIRECT, h_put_tce_indirect); 518da95324eSAlexey Kardashevskiy spapr_register_hypercall(H_STUFF_TCE, h_stuff_tce); 519a83000f5SAnthony Liguori } 520a83000f5SAnthony Liguori 521a83000f5SAnthony Liguori static TypeInfo spapr_tce_table_info = { 522a83000f5SAnthony Liguori .name = TYPE_SPAPR_TCE_TABLE, 523a83000f5SAnthony Liguori .parent = TYPE_DEVICE, 524a83000f5SAnthony Liguori .instance_size = sizeof(sPAPRTCETable), 525a83000f5SAnthony Liguori .class_init = spapr_tce_table_class_init, 526a83000f5SAnthony Liguori }; 527a83000f5SAnthony Liguori 528a83000f5SAnthony Liguori static void register_types(void) 529a83000f5SAnthony Liguori { 530a83000f5SAnthony Liguori type_register_static(&spapr_tce_table_info); 531a83000f5SAnthony Liguori } 532a83000f5SAnthony Liguori 533a83000f5SAnthony Liguori type_init(register_types); 534