qemu/hw/ppc/spapr_iommu.c
<<
>>
Prefs
   1/*
   2 * QEMU sPAPR IOMMU (TCE) code
   3 *
   4 * Copyright (c) 2010 David Gibson, IBM Corporation <dwg@au1.ibm.com>
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2 of the License, or (at your option) any later version.
  10 *
  11 * This library is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include "qemu/osdep.h"
  21#include "qemu/error-report.h"
  22#include "qemu/log.h"
  23#include "qemu/module.h"
  24#include "sysemu/kvm.h"
  25#include "kvm_ppc.h"
  26#include "migration/vmstate.h"
  27#include "sysemu/dma.h"
  28#include "exec/address-spaces.h"
  29#include "trace.h"
  30
  31#include "hw/ppc/spapr.h"
  32#include "hw/ppc/spapr_vio.h"
  33
  34#include <libfdt.h>
  35
  36enum SpaprTceAccess {
  37    SPAPR_TCE_FAULT = 0,
  38    SPAPR_TCE_RO = 1,
  39    SPAPR_TCE_WO = 2,
  40    SPAPR_TCE_RW = 3,
  41};
  42
  43#define IOMMU_PAGE_SIZE(shift)      (1ULL << (shift))
  44#define IOMMU_PAGE_MASK(shift)      (~(IOMMU_PAGE_SIZE(shift) - 1))
  45
  46static QLIST_HEAD(, SpaprTceTable) spapr_tce_tables;
  47
  48SpaprTceTable *spapr_tce_find_by_liobn(target_ulong liobn)
  49{
  50    SpaprTceTable *tcet;
  51
  52    if (liobn & 0xFFFFFFFF00000000ULL) {
  53        hcall_dprintf("Request for out-of-bounds LIOBN 0x" TARGET_FMT_lx "\n",
  54                      liobn);
  55        return NULL;
  56    }
  57
  58    QLIST_FOREACH(tcet, &spapr_tce_tables, list) {
  59        if (tcet->liobn == (uint32_t)liobn) {
  60            return tcet;
  61        }
  62    }
  63
  64    return NULL;
  65}
  66
  67static IOMMUAccessFlags spapr_tce_iommu_access_flags(uint64_t tce)
  68{
  69    switch (tce & SPAPR_TCE_RW) {
  70    case SPAPR_TCE_FAULT:
  71        return IOMMU_NONE;
  72    case SPAPR_TCE_RO:
  73        return IOMMU_RO;
  74    case SPAPR_TCE_WO:
  75        return IOMMU_WO;
  76    default: /* SPAPR_TCE_RW */
  77        return IOMMU_RW;
  78    }
  79}
  80
  81static uint64_t *spapr_tce_alloc_table(uint32_t liobn,
  82                                       uint32_t page_shift,
  83                                       uint64_t bus_offset,
  84                                       uint32_t nb_table,
  85                                       int *fd,
  86                                       bool need_vfio)
  87{
  88    uint64_t *table = NULL;
  89
  90    if (kvm_enabled()) {
  91        table = kvmppc_create_spapr_tce(liobn, page_shift, bus_offset, nb_table,
  92                                        fd, need_vfio);
  93    }
  94
  95    if (!table) {
  96        *fd = -1;
  97        table = g_new0(uint64_t, nb_table);
  98    }
  99
 100    trace_spapr_iommu_new_table(liobn, table, *fd);
 101
 102    return table;
 103}
 104
 105static void spapr_tce_free_table(uint64_t *table, int fd, uint32_t nb_table)
 106{
 107    if (!kvm_enabled() ||
 108        (kvmppc_remove_spapr_tce(table, fd, nb_table) != 0)) {
 109        g_free(table);
 110    }
 111}
 112
 113/* Called from RCU critical section */
 114static IOMMUTLBEntry spapr_tce_translate_iommu(IOMMUMemoryRegion *iommu,
 115                                               hwaddr addr,
 116                                               IOMMUAccessFlags flag,
 117                                               int iommu_idx)
 118{
 119    SpaprTceTable *tcet = container_of(iommu, SpaprTceTable, iommu);
 120    uint64_t tce;
 121    IOMMUTLBEntry ret = {
 122        .target_as = &address_space_memory,
 123        .iova = 0,
 124        .translated_addr = 0,
 125        .addr_mask = ~(hwaddr)0,
 126        .perm = IOMMU_NONE,
 127    };
 128
 129    if ((addr >> tcet->page_shift) < tcet->nb_table) {
 130        /* Check if we are in bound */
 131        hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift);
 132
 133        tce = tcet->table[addr >> tcet->page_shift];
 134        ret.iova = addr & page_mask;
 135        ret.translated_addr = tce & page_mask;
 136        ret.addr_mask = ~page_mask;
 137        ret.perm = spapr_tce_iommu_access_flags(tce);
 138    }
 139    trace_spapr_iommu_xlate(tcet->liobn, addr, ret.translated_addr, ret.perm,
 140                            ret.addr_mask);
 141
 142    return ret;
 143}
 144
 145static void spapr_tce_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n)
 146{
 147    MemoryRegion *mr = MEMORY_REGION(iommu_mr);
 148    IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr);
 149    hwaddr addr, granularity;
 150    IOMMUTLBEntry iotlb;
 151    SpaprTceTable *tcet = container_of(iommu_mr, SpaprTceTable, iommu);
 152
 153    if (tcet->skipping_replay) {
 154        return;
 155    }
 156
 157    granularity = memory_region_iommu_get_min_page_size(iommu_mr);
 158
 159    for (addr = 0; addr < memory_region_size(mr); addr += granularity) {
 160        iotlb = imrc->translate(iommu_mr, addr, IOMMU_NONE, n->iommu_idx);
 161        if (iotlb.perm != IOMMU_NONE) {
 162            n->notify(n, &iotlb);
 163        }
 164
 165        /*
 166         * if (2^64 - MR size) < granularity, it's possible to get an
 167         * infinite loop here.  This should catch such a wraparound.
 168         */
 169        if ((addr + granularity) < addr) {
 170            break;
 171        }
 172    }
 173}
 174
 175static int spapr_tce_table_pre_save(void *opaque)
 176{
 177    SpaprTceTable *tcet = SPAPR_TCE_TABLE(opaque);
 178
 179    tcet->mig_table = tcet->table;
 180    tcet->mig_nb_table = tcet->nb_table;
 181
 182    trace_spapr_iommu_pre_save(tcet->liobn, tcet->mig_nb_table,
 183                               tcet->bus_offset, tcet->page_shift);
 184
 185    return 0;
 186}
 187
 188static uint64_t spapr_tce_get_min_page_size(IOMMUMemoryRegion *iommu)
 189{
 190    SpaprTceTable *tcet = container_of(iommu, SpaprTceTable, iommu);
 191
 192    return 1ULL << tcet->page_shift;
 193}
 194
 195static int spapr_tce_get_attr(IOMMUMemoryRegion *iommu,
 196                              enum IOMMUMemoryRegionAttr attr, void *data)
 197{
 198    SpaprTceTable *tcet = container_of(iommu, SpaprTceTable, iommu);
 199
 200    if (attr == IOMMU_ATTR_SPAPR_TCE_FD && kvmppc_has_cap_spapr_vfio()) {
 201        *(int *) data = tcet->fd;
 202        return 0;
 203    }
 204
 205    return -EINVAL;
 206}
 207
 208static int spapr_tce_notify_flag_changed(IOMMUMemoryRegion *iommu,
 209                                         IOMMUNotifierFlag old,
 210                                         IOMMUNotifierFlag new,
 211                                         Error **errp)
 212{
 213    struct SpaprTceTable *tbl = container_of(iommu, SpaprTceTable, iommu);
 214
 215    if (old == IOMMU_NOTIFIER_NONE && new != IOMMU_NOTIFIER_NONE) {
 216        spapr_tce_set_need_vfio(tbl, true);
 217    } else if (old != IOMMU_NOTIFIER_NONE && new == IOMMU_NOTIFIER_NONE) {
 218        spapr_tce_set_need_vfio(tbl, false);
 219    }
 220    return 0;
 221}
 222
 223static int spapr_tce_table_post_load(void *opaque, int version_id)
 224{
 225    SpaprTceTable *tcet = SPAPR_TCE_TABLE(opaque);
 226    uint32_t old_nb_table = tcet->nb_table;
 227    uint64_t old_bus_offset = tcet->bus_offset;
 228    uint32_t old_page_shift = tcet->page_shift;
 229
 230    if (tcet->vdev) {
 231        spapr_vio_set_bypass(tcet->vdev, tcet->bypass);
 232    }
 233
 234    if (tcet->mig_nb_table != tcet->nb_table) {
 235        spapr_tce_table_disable(tcet);
 236    }
 237
 238    if (tcet->mig_nb_table) {
 239        if (!tcet->nb_table) {
 240            spapr_tce_table_enable(tcet, old_page_shift, old_bus_offset,
 241                                   tcet->mig_nb_table);
 242        }
 243
 244        memcpy(tcet->table, tcet->mig_table,
 245               tcet->nb_table * sizeof(tcet->table[0]));
 246
 247        free(tcet->mig_table);
 248        tcet->mig_table = NULL;
 249    }
 250
 251    trace_spapr_iommu_post_load(tcet->liobn, old_nb_table, tcet->nb_table,
 252                                tcet->bus_offset, tcet->page_shift);
 253
 254    return 0;
 255}
 256
 257static bool spapr_tce_table_ex_needed(void *opaque)
 258{
 259    SpaprTceTable *tcet = opaque;
 260
 261    return tcet->bus_offset || tcet->page_shift != 0xC;
 262}
 263
 264static const VMStateDescription vmstate_spapr_tce_table_ex = {
 265    .name = "spapr_iommu_ex",
 266    .version_id = 1,
 267    .minimum_version_id = 1,
 268    .needed = spapr_tce_table_ex_needed,
 269    .fields = (VMStateField[]) {
 270        VMSTATE_UINT64(bus_offset, SpaprTceTable),
 271        VMSTATE_UINT32(page_shift, SpaprTceTable),
 272        VMSTATE_END_OF_LIST()
 273    },
 274};
 275
 276static const VMStateDescription vmstate_spapr_tce_table = {
 277    .name = "spapr_iommu",
 278    .version_id = 2,
 279    .minimum_version_id = 2,
 280    .pre_save = spapr_tce_table_pre_save,
 281    .post_load = spapr_tce_table_post_load,
 282    .fields      = (VMStateField []) {
 283        /* Sanity check */
 284        VMSTATE_UINT32_EQUAL(liobn, SpaprTceTable, NULL),
 285
 286        /* IOMMU state */
 287        VMSTATE_UINT32(mig_nb_table, SpaprTceTable),
 288        VMSTATE_BOOL(bypass, SpaprTceTable),
 289        VMSTATE_VARRAY_UINT32_ALLOC(mig_table, SpaprTceTable, mig_nb_table, 0,
 290                                    vmstate_info_uint64, uint64_t),
 291
 292        VMSTATE_END_OF_LIST()
 293    },
 294    .subsections = (const VMStateDescription*[]) {
 295        &vmstate_spapr_tce_table_ex,
 296        NULL
 297    }
 298};
 299
 300static void spapr_tce_table_realize(DeviceState *dev, Error **errp)
 301{
 302    SpaprTceTable *tcet = SPAPR_TCE_TABLE(dev);
 303    Object *tcetobj = OBJECT(tcet);
 304    gchar *tmp;
 305
 306    tcet->fd = -1;
 307    tcet->need_vfio = false;
 308    tmp = g_strdup_printf("tce-root-%x", tcet->liobn);
 309    memory_region_init(&tcet->root, tcetobj, tmp, UINT64_MAX);
 310    g_free(tmp);
 311
 312    tmp = g_strdup_printf("tce-iommu-%x", tcet->liobn);
 313    memory_region_init_iommu(&tcet->iommu, sizeof(tcet->iommu),
 314                             TYPE_SPAPR_IOMMU_MEMORY_REGION,
 315                             tcetobj, tmp, 0);
 316    g_free(tmp);
 317
 318    QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list);
 319
 320    vmstate_register(DEVICE(tcet), tcet->liobn, &vmstate_spapr_tce_table,
 321                     tcet);
 322}
 323
 324void spapr_tce_set_need_vfio(SpaprTceTable *tcet, bool need_vfio)
 325{
 326    size_t table_size = tcet->nb_table * sizeof(uint64_t);
 327    uint64_t *oldtable;
 328    int newfd = -1;
 329
 330    g_assert(need_vfio != tcet->need_vfio);
 331
 332    tcet->need_vfio = need_vfio;
 333
 334    if (!need_vfio || (tcet->fd != -1 && kvmppc_has_cap_spapr_vfio())) {
 335        return;
 336    }
 337
 338    oldtable = tcet->table;
 339
 340    tcet->table = spapr_tce_alloc_table(tcet->liobn,
 341                                        tcet->page_shift,
 342                                        tcet->bus_offset,
 343                                        tcet->nb_table,
 344                                        &newfd,
 345                                        need_vfio);
 346    memcpy(tcet->table, oldtable, table_size);
 347
 348    spapr_tce_free_table(oldtable, tcet->fd, tcet->nb_table);
 349
 350    tcet->fd = newfd;
 351}
 352
 353SpaprTceTable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn)
 354{
 355    SpaprTceTable *tcet;
 356    gchar *tmp;
 357
 358    if (spapr_tce_find_by_liobn(liobn)) {
 359        error_report("Attempted to create TCE table with duplicate"
 360                " LIOBN 0x%x", liobn);
 361        return NULL;
 362    }
 363
 364    tcet = SPAPR_TCE_TABLE(object_new(TYPE_SPAPR_TCE_TABLE));
 365    tcet->liobn = liobn;
 366
 367    tmp = g_strdup_printf("tce-table-%x", liobn);
 368    object_property_add_child(OBJECT(owner), tmp, OBJECT(tcet), NULL);
 369    g_free(tmp);
 370    object_unref(OBJECT(tcet));
 371
 372    object_property_set_bool(OBJECT(tcet), true, "realized", NULL);
 373
 374    return tcet;
 375}
 376
 377void spapr_tce_table_enable(SpaprTceTable *tcet,
 378                            uint32_t page_shift, uint64_t bus_offset,
 379                            uint32_t nb_table)
 380{
 381    if (tcet->nb_table) {
 382        warn_report("trying to enable already enabled TCE table");
 383        return;
 384    }
 385
 386    tcet->bus_offset = bus_offset;
 387    tcet->page_shift = page_shift;
 388    tcet->nb_table = nb_table;
 389    tcet->table = spapr_tce_alloc_table(tcet->liobn,
 390                                        tcet->page_shift,
 391                                        tcet->bus_offset,
 392                                        tcet->nb_table,
 393                                        &tcet->fd,
 394                                        tcet->need_vfio);
 395
 396    memory_region_set_size(MEMORY_REGION(&tcet->iommu),
 397                           (uint64_t)tcet->nb_table << tcet->page_shift);
 398    memory_region_add_subregion(&tcet->root, tcet->bus_offset,
 399                                MEMORY_REGION(&tcet->iommu));
 400}
 401
 402void spapr_tce_table_disable(SpaprTceTable *tcet)
 403{
 404    if (!tcet->nb_table) {
 405        return;
 406    }
 407
 408    memory_region_del_subregion(&tcet->root, MEMORY_REGION(&tcet->iommu));
 409    memory_region_set_size(MEMORY_REGION(&tcet->iommu), 0);
 410
 411    spapr_tce_free_table(tcet->table, tcet->fd, tcet->nb_table);
 412    tcet->fd = -1;
 413    tcet->table = NULL;
 414    tcet->bus_offset = 0;
 415    tcet->page_shift = 0;
 416    tcet->nb_table = 0;
 417}
 418
 419static void spapr_tce_table_unrealize(DeviceState *dev, Error **errp)
 420{
 421    SpaprTceTable *tcet = SPAPR_TCE_TABLE(dev);
 422
 423    vmstate_unregister(DEVICE(tcet), &vmstate_spapr_tce_table, tcet);
 424
 425    QLIST_REMOVE(tcet, list);
 426
 427    spapr_tce_table_disable(tcet);
 428}
 429
 430MemoryRegion *spapr_tce_get_iommu(SpaprTceTable *tcet)
 431{
 432    return &tcet->root;
 433}
 434
 435static void spapr_tce_reset(DeviceState *dev)
 436{
 437    SpaprTceTable *tcet = SPAPR_TCE_TABLE(dev);
 438    size_t table_size = tcet->nb_table * sizeof(uint64_t);
 439
 440    if (tcet->nb_table) {
 441        memset(tcet->table, 0, table_size);
 442    }
 443}
 444
 445static target_ulong put_tce_emu(SpaprTceTable *tcet, target_ulong ioba,
 446                                target_ulong tce)
 447{
 448    IOMMUTLBEntry entry;
 449    hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift);
 450    unsigned long index = (ioba - tcet->bus_offset) >> tcet->page_shift;
 451
 452    if (index >= tcet->nb_table) {
 453        hcall_dprintf("spapr_vio_put_tce on out-of-bounds IOBA 0x"
 454                      TARGET_FMT_lx "\n", ioba);
 455        return H_PARAMETER;
 456    }
 457
 458    tcet->table[index] = tce;
 459
 460    entry.target_as = &address_space_memory,
 461    entry.iova = (ioba - tcet->bus_offset) & page_mask;
 462    entry.translated_addr = tce & page_mask;
 463    entry.addr_mask = ~page_mask;
 464    entry.perm = spapr_tce_iommu_access_flags(tce);
 465    memory_region_notify_iommu(&tcet->iommu, 0, entry);
 466
 467    return H_SUCCESS;
 468}
 469
 470static target_ulong h_put_tce_indirect(PowerPCCPU *cpu,
 471                                       SpaprMachineState *spapr,
 472                                       target_ulong opcode, target_ulong *args)
 473{
 474    int i;
 475    target_ulong liobn = args[0];
 476    target_ulong ioba = args[1];
 477    target_ulong ioba1 = ioba;
 478    target_ulong tce_list = args[2];
 479    target_ulong npages = args[3];
 480    target_ulong ret = H_PARAMETER, tce = 0;
 481    SpaprTceTable *tcet = spapr_tce_find_by_liobn(liobn);
 482    CPUState *cs = CPU(cpu);
 483    hwaddr page_mask, page_size;
 484
 485    if (!tcet) {
 486        return H_PARAMETER;
 487    }
 488
 489    if ((npages > 512) || (tce_list & SPAPR_TCE_PAGE_MASK)) {
 490        return H_PARAMETER;
 491    }
 492
 493    page_mask = IOMMU_PAGE_MASK(tcet->page_shift);
 494    page_size = IOMMU_PAGE_SIZE(tcet->page_shift);
 495    ioba &= page_mask;
 496
 497    for (i = 0; i < npages; ++i, ioba += page_size) {
 498        tce = ldq_be_phys(cs->as, tce_list + i * sizeof(target_ulong));
 499
 500        ret = put_tce_emu(tcet, ioba, tce);
 501        if (ret) {
 502            break;
 503        }
 504    }
 505
 506    /* Trace last successful or the first problematic entry */
 507    i = i ? (i - 1) : 0;
 508    if (SPAPR_IS_PCI_LIOBN(liobn)) {
 509        trace_spapr_iommu_pci_indirect(liobn, ioba1, tce_list, i, tce, ret);
 510    } else {
 511        trace_spapr_iommu_indirect(liobn, ioba1, tce_list, i, tce, ret);
 512    }
 513    return ret;
 514}
 515
 516static target_ulong h_stuff_tce(PowerPCCPU *cpu, SpaprMachineState *spapr,
 517                              target_ulong opcode, target_ulong *args)
 518{
 519    int i;
 520    target_ulong liobn = args[0];
 521    target_ulong ioba = args[1];
 522    target_ulong tce_value = args[2];
 523    target_ulong npages = args[3];
 524    target_ulong ret = H_PARAMETER;
 525    SpaprTceTable *tcet = spapr_tce_find_by_liobn(liobn);
 526    hwaddr page_mask, page_size;
 527
 528    if (!tcet) {
 529        return H_PARAMETER;
 530    }
 531
 532    if (npages > tcet->nb_table) {
 533        return H_PARAMETER;
 534    }
 535
 536    page_mask = IOMMU_PAGE_MASK(tcet->page_shift);
 537    page_size = IOMMU_PAGE_SIZE(tcet->page_shift);
 538    ioba &= page_mask;
 539
 540    for (i = 0; i < npages; ++i, ioba += page_size) {
 541        ret = put_tce_emu(tcet, ioba, tce_value);
 542        if (ret) {
 543            break;
 544        }
 545    }
 546    if (SPAPR_IS_PCI_LIOBN(liobn)) {
 547        trace_spapr_iommu_pci_stuff(liobn, ioba, tce_value, npages, ret);
 548    } else {
 549        trace_spapr_iommu_stuff(liobn, ioba, tce_value, npages, ret);
 550    }
 551
 552    return ret;
 553}
 554
 555static target_ulong h_put_tce(PowerPCCPU *cpu, SpaprMachineState *spapr,
 556                              target_ulong opcode, target_ulong *args)
 557{
 558    target_ulong liobn = args[0];
 559    target_ulong ioba = args[1];
 560    target_ulong tce = args[2];
 561    target_ulong ret = H_PARAMETER;
 562    SpaprTceTable *tcet = spapr_tce_find_by_liobn(liobn);
 563
 564    if (tcet) {
 565        hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift);
 566
 567        ioba &= page_mask;
 568
 569        ret = put_tce_emu(tcet, ioba, tce);
 570    }
 571    if (SPAPR_IS_PCI_LIOBN(liobn)) {
 572        trace_spapr_iommu_pci_put(liobn, ioba, tce, ret);
 573    } else {
 574        trace_spapr_iommu_put(liobn, ioba, tce, ret);
 575    }
 576
 577    return ret;
 578}
 579
 580static target_ulong get_tce_emu(SpaprTceTable *tcet, target_ulong ioba,
 581                                target_ulong *tce)
 582{
 583    unsigned long index = (ioba - tcet->bus_offset) >> tcet->page_shift;
 584
 585    if (index >= tcet->nb_table) {
 586        hcall_dprintf("spapr_iommu_get_tce on out-of-bounds IOBA 0x"
 587                      TARGET_FMT_lx "\n", ioba);
 588        return H_PARAMETER;
 589    }
 590
 591    *tce = tcet->table[index];
 592
 593    return H_SUCCESS;
 594}
 595
 596static target_ulong h_get_tce(PowerPCCPU *cpu, SpaprMachineState *spapr,
 597                              target_ulong opcode, target_ulong *args)
 598{
 599    target_ulong liobn = args[0];
 600    target_ulong ioba = args[1];
 601    target_ulong tce = 0;
 602    target_ulong ret = H_PARAMETER;
 603    SpaprTceTable *tcet = spapr_tce_find_by_liobn(liobn);
 604
 605    if (tcet) {
 606        hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift);
 607
 608        ioba &= page_mask;
 609
 610        ret = get_tce_emu(tcet, ioba, &tce);
 611        if (!ret) {
 612            args[0] = tce;
 613        }
 614    }
 615    if (SPAPR_IS_PCI_LIOBN(liobn)) {
 616        trace_spapr_iommu_pci_get(liobn, ioba, ret, tce);
 617    } else {
 618        trace_spapr_iommu_get(liobn, ioba, ret, tce);
 619    }
 620
 621    return ret;
 622}
 623
 624int spapr_dma_dt(void *fdt, int node_off, const char *propname,
 625                 uint32_t liobn, uint64_t window, uint32_t size)
 626{
 627    uint32_t dma_prop[5];
 628    int ret;
 629
 630    dma_prop[0] = cpu_to_be32(liobn);
 631    dma_prop[1] = cpu_to_be32(window >> 32);
 632    dma_prop[2] = cpu_to_be32(window & 0xFFFFFFFF);
 633    dma_prop[3] = 0; /* window size is 32 bits */
 634    dma_prop[4] = cpu_to_be32(size);
 635
 636    ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2);
 637    if (ret < 0) {
 638        return ret;
 639    }
 640
 641    ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2);
 642    if (ret < 0) {
 643        return ret;
 644    }
 645
 646    ret = fdt_setprop(fdt, node_off, propname, dma_prop, sizeof(dma_prop));
 647    if (ret < 0) {
 648        return ret;
 649    }
 650
 651    return 0;
 652}
 653
 654int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname,
 655                      SpaprTceTable *tcet)
 656{
 657    if (!tcet) {
 658        return 0;
 659    }
 660
 661    return spapr_dma_dt(fdt, node_off, propname,
 662                        tcet->liobn, 0, tcet->nb_table << tcet->page_shift);
 663}
 664
 665static void spapr_tce_table_class_init(ObjectClass *klass, void *data)
 666{
 667    DeviceClass *dc = DEVICE_CLASS(klass);
 668    dc->realize = spapr_tce_table_realize;
 669    dc->reset = spapr_tce_reset;
 670    dc->unrealize = spapr_tce_table_unrealize;
 671    /* Reason: This is just an internal device for handling the hypercalls */
 672    dc->user_creatable = false;
 673
 674    QLIST_INIT(&spapr_tce_tables);
 675
 676    /* hcall-tce */
 677    spapr_register_hypercall(H_PUT_TCE, h_put_tce);
 678    spapr_register_hypercall(H_GET_TCE, h_get_tce);
 679    spapr_register_hypercall(H_PUT_TCE_INDIRECT, h_put_tce_indirect);
 680    spapr_register_hypercall(H_STUFF_TCE, h_stuff_tce);
 681}
 682
 683static TypeInfo spapr_tce_table_info = {
 684    .name = TYPE_SPAPR_TCE_TABLE,
 685    .parent = TYPE_DEVICE,
 686    .instance_size = sizeof(SpaprTceTable),
 687    .class_init = spapr_tce_table_class_init,
 688};
 689
 690static void spapr_iommu_memory_region_class_init(ObjectClass *klass, void *data)
 691{
 692    IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
 693
 694    imrc->translate = spapr_tce_translate_iommu;
 695    imrc->replay = spapr_tce_replay;
 696    imrc->get_min_page_size = spapr_tce_get_min_page_size;
 697    imrc->notify_flag_changed = spapr_tce_notify_flag_changed;
 698    imrc->get_attr = spapr_tce_get_attr;
 699}
 700
 701static const TypeInfo spapr_iommu_memory_region_info = {
 702    .parent = TYPE_IOMMU_MEMORY_REGION,
 703    .name = TYPE_SPAPR_IOMMU_MEMORY_REGION,
 704    .class_init = spapr_iommu_memory_region_class_init,
 705};
 706
 707static void register_types(void)
 708{
 709    type_register_static(&spapr_tce_table_info);
 710    type_register_static(&spapr_iommu_memory_region_info);
 711}
 712
 713type_init(register_types);
 714