xref: /openbmc/qemu/hw/ppc/spapr_iommu.c (revision fec5d3a1cd8fea7d70778f8b408589d75c88c3e7)
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