qemu/hw/sparc64/sun4u_iommu.c
<<
>>
Prefs
   1/*
   2 * QEMU sun4u IOMMU emulation
   3 *
   4 * Copyright (c) 2006 Fabrice Bellard
   5 * Copyright (c) 2012,2013 Artyom Tarasenko
   6 * Copyright (c) 2017 Mark Cave-Ayland
   7 *
   8 * Permission is hereby granted, free of charge, to any person obtaining a copy
   9 * of this software and associated documentation files (the "Software"), to deal
  10 * in the Software without restriction, including without limitation the rights
  11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12 * copies of the Software, and to permit persons to whom the Software is
  13 * furnished to do so, subject to the following conditions:
  14 *
  15 * The above copyright notice and this permission notice shall be included in
  16 * all copies or substantial portions of the Software.
  17 *
  18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24 * THE SOFTWARE.
  25 */
  26
  27#include "qemu/osdep.h"
  28#include "hw/sysbus.h"
  29#include "hw/sparc/sun4u_iommu.h"
  30#include "exec/address-spaces.h"
  31#include "qemu/log.h"
  32#include "qemu/module.h"
  33#include "trace.h"
  34
  35
  36#define IOMMU_PAGE_SIZE_8K      (1ULL << 13)
  37#define IOMMU_PAGE_MASK_8K      (~(IOMMU_PAGE_SIZE_8K - 1))
  38#define IOMMU_PAGE_SIZE_64K     (1ULL << 16)
  39#define IOMMU_PAGE_MASK_64K     (~(IOMMU_PAGE_SIZE_64K - 1))
  40
  41#define IOMMU_CTRL              0x0
  42#define IOMMU_CTRL_TBW_SIZE     (1ULL << 2)
  43#define IOMMU_CTRL_MMU_EN       (1ULL)
  44
  45#define IOMMU_CTRL_TSB_SHIFT    16
  46
  47#define IOMMU_BASE              0x8
  48#define IOMMU_FLUSH             0x10
  49
  50#define IOMMU_TTE_DATA_V        (1ULL << 63)
  51#define IOMMU_TTE_DATA_SIZE     (1ULL << 61)
  52#define IOMMU_TTE_DATA_W        (1ULL << 1)
  53
  54#define IOMMU_TTE_PHYS_MASK_8K  0x1ffffffe000ULL
  55#define IOMMU_TTE_PHYS_MASK_64K 0x1ffffff8000ULL
  56
  57#define IOMMU_TSB_8K_OFFSET_MASK_8M    0x00000000007fe000ULL
  58#define IOMMU_TSB_8K_OFFSET_MASK_16M   0x0000000000ffe000ULL
  59#define IOMMU_TSB_8K_OFFSET_MASK_32M   0x0000000001ffe000ULL
  60#define IOMMU_TSB_8K_OFFSET_MASK_64M   0x0000000003ffe000ULL
  61#define IOMMU_TSB_8K_OFFSET_MASK_128M  0x0000000007ffe000ULL
  62#define IOMMU_TSB_8K_OFFSET_MASK_256M  0x000000000fffe000ULL
  63#define IOMMU_TSB_8K_OFFSET_MASK_512M  0x000000001fffe000ULL
  64#define IOMMU_TSB_8K_OFFSET_MASK_1G    0x000000003fffe000ULL
  65
  66#define IOMMU_TSB_64K_OFFSET_MASK_64M  0x0000000003ff0000ULL
  67#define IOMMU_TSB_64K_OFFSET_MASK_128M 0x0000000007ff0000ULL
  68#define IOMMU_TSB_64K_OFFSET_MASK_256M 0x000000000fff0000ULL
  69#define IOMMU_TSB_64K_OFFSET_MASK_512M 0x000000001fff0000ULL
  70#define IOMMU_TSB_64K_OFFSET_MASK_1G   0x000000003fff0000ULL
  71#define IOMMU_TSB_64K_OFFSET_MASK_2G   0x000000007fff0000ULL
  72
  73
  74/* Called from RCU critical section */
  75static IOMMUTLBEntry sun4u_translate_iommu(IOMMUMemoryRegion *iommu,
  76                                           hwaddr addr,
  77                                           IOMMUAccessFlags flag, int iommu_idx)
  78{
  79    IOMMUState *is = container_of(iommu, IOMMUState, iommu);
  80    hwaddr baseaddr, offset;
  81    uint64_t tte;
  82    uint32_t tsbsize;
  83    IOMMUTLBEntry ret = {
  84        .target_as = &address_space_memory,
  85        .iova = 0,
  86        .translated_addr = 0,
  87        .addr_mask = ~(hwaddr)0,
  88        .perm = IOMMU_NONE,
  89    };
  90
  91    if (!(is->regs[IOMMU_CTRL >> 3] & IOMMU_CTRL_MMU_EN)) {
  92        /* IOMMU disabled, passthrough using standard 8K page */
  93        ret.iova = addr & IOMMU_PAGE_MASK_8K;
  94        ret.translated_addr = addr;
  95        ret.addr_mask = IOMMU_PAGE_MASK_8K;
  96        ret.perm = IOMMU_RW;
  97
  98        return ret;
  99    }
 100
 101    baseaddr = is->regs[IOMMU_BASE >> 3];
 102    tsbsize = (is->regs[IOMMU_CTRL >> 3] >> IOMMU_CTRL_TSB_SHIFT) & 0x7;
 103
 104    if (is->regs[IOMMU_CTRL >> 3] & IOMMU_CTRL_TBW_SIZE) {
 105        /* 64K */
 106        switch (tsbsize) {
 107        case 0:
 108            offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_64M) >> 13;
 109            break;
 110        case 1:
 111            offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_128M) >> 13;
 112            break;
 113        case 2:
 114            offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_256M) >> 13;
 115            break;
 116        case 3:
 117            offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_512M) >> 13;
 118            break;
 119        case 4:
 120            offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_1G) >> 13;
 121            break;
 122        case 5:
 123            offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_2G) >> 13;
 124            break;
 125        default:
 126            /* Not implemented, error */
 127            return ret;
 128        }
 129    } else {
 130        /* 8K */
 131        switch (tsbsize) {
 132        case 0:
 133            offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_8M) >> 10;
 134            break;
 135        case 1:
 136            offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_16M) >> 10;
 137            break;
 138        case 2:
 139            offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_32M) >> 10;
 140            break;
 141        case 3:
 142            offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_64M) >> 10;
 143            break;
 144        case 4:
 145            offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_128M) >> 10;
 146            break;
 147        case 5:
 148            offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_256M) >> 10;
 149            break;
 150        case 6:
 151            offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_512M) >> 10;
 152            break;
 153        case 7:
 154            offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_1G) >> 10;
 155            break;
 156        }
 157    }
 158
 159    tte = address_space_ldq_be(&address_space_memory, baseaddr + offset,
 160                               MEMTXATTRS_UNSPECIFIED, NULL);
 161
 162    if (!(tte & IOMMU_TTE_DATA_V)) {
 163        /* Invalid mapping */
 164        return ret;
 165    }
 166
 167    if (tte & IOMMU_TTE_DATA_W) {
 168        /* Writeable */
 169        ret.perm = IOMMU_RW;
 170    } else {
 171        ret.perm = IOMMU_RO;
 172    }
 173
 174    /* Extract phys */
 175    if (tte & IOMMU_TTE_DATA_SIZE) {
 176        /* 64K */
 177        ret.iova = addr & IOMMU_PAGE_MASK_64K;
 178        ret.translated_addr = tte & IOMMU_TTE_PHYS_MASK_64K;
 179        ret.addr_mask = (IOMMU_PAGE_SIZE_64K - 1);
 180    } else {
 181        /* 8K */
 182        ret.iova = addr & IOMMU_PAGE_MASK_8K;
 183        ret.translated_addr = tte & IOMMU_TTE_PHYS_MASK_8K;
 184        ret.addr_mask = (IOMMU_PAGE_SIZE_8K - 1);
 185    }
 186
 187    trace_sun4u_iommu_translate(ret.iova, ret.translated_addr, tte);
 188
 189    return ret;
 190}
 191
 192static void iommu_mem_write(void *opaque, hwaddr addr,
 193                            uint64_t val, unsigned size)
 194{
 195    IOMMUState *is = opaque;
 196
 197    trace_sun4u_iommu_mem_write(addr, val, size);
 198
 199    switch (addr) {
 200    case IOMMU_CTRL:
 201        if (size == 4) {
 202            is->regs[IOMMU_CTRL >> 3] &= 0xffffffffULL;
 203            is->regs[IOMMU_CTRL >> 3] |= val << 32;
 204        } else {
 205            is->regs[IOMMU_CTRL >> 3] = val;
 206        }
 207        break;
 208    case IOMMU_CTRL + 0x4:
 209        is->regs[IOMMU_CTRL >> 3] &= 0xffffffff00000000ULL;
 210        is->regs[IOMMU_CTRL >> 3] |= val & 0xffffffffULL;
 211        break;
 212    case IOMMU_BASE:
 213        if (size == 4) {
 214            is->regs[IOMMU_BASE >> 3] &= 0xffffffffULL;
 215            is->regs[IOMMU_BASE >> 3] |= val << 32;
 216        } else {
 217            is->regs[IOMMU_BASE >> 3] = val;
 218        }
 219        break;
 220    case IOMMU_BASE + 0x4:
 221        is->regs[IOMMU_BASE >> 3] &= 0xffffffff00000000ULL;
 222        is->regs[IOMMU_BASE >> 3] |= val & 0xffffffffULL;
 223        break;
 224    case IOMMU_FLUSH:
 225    case IOMMU_FLUSH + 0x4:
 226        break;
 227    default:
 228        qemu_log_mask(LOG_UNIMP,
 229                  "sun4u-iommu: Unimplemented register write "
 230                  "reg 0x%" HWADDR_PRIx " size 0x%x value 0x%" PRIx64 "\n",
 231                  addr, size, val);
 232        break;
 233    }
 234}
 235
 236static uint64_t iommu_mem_read(void *opaque, hwaddr addr, unsigned size)
 237{
 238    IOMMUState *is = opaque;
 239    uint64_t val;
 240
 241    switch (addr) {
 242    case IOMMU_CTRL:
 243        if (size == 4) {
 244            val = is->regs[IOMMU_CTRL >> 3] >> 32;
 245        } else {
 246            val = is->regs[IOMMU_CTRL >> 3];
 247        }
 248        break;
 249    case IOMMU_CTRL + 0x4:
 250        val = is->regs[IOMMU_CTRL >> 3] & 0xffffffffULL;
 251        break;
 252    case IOMMU_BASE:
 253        if (size == 4) {
 254            val = is->regs[IOMMU_BASE >> 3] >> 32;
 255        } else {
 256            val = is->regs[IOMMU_BASE >> 3];
 257        }
 258        break;
 259    case IOMMU_BASE + 0x4:
 260        val = is->regs[IOMMU_BASE >> 3] & 0xffffffffULL;
 261        break;
 262    case IOMMU_FLUSH:
 263    case IOMMU_FLUSH + 0x4:
 264        val = 0;
 265        break;
 266    default:
 267        qemu_log_mask(LOG_UNIMP,
 268                      "sun4u-iommu: Unimplemented register read "
 269                      "reg 0x%" HWADDR_PRIx " size 0x%x\n",
 270                      addr, size);
 271        val = 0;
 272        break;
 273    }
 274
 275    trace_sun4u_iommu_mem_read(addr, val, size);
 276
 277    return val;
 278}
 279
 280static const MemoryRegionOps iommu_mem_ops = {
 281    .read = iommu_mem_read,
 282    .write = iommu_mem_write,
 283    .endianness = DEVICE_BIG_ENDIAN,
 284};
 285
 286static void iommu_reset(DeviceState *d)
 287{
 288    IOMMUState *s = SUN4U_IOMMU(d);
 289
 290    memset(s->regs, 0, IOMMU_NREGS * sizeof(uint64_t));
 291}
 292
 293static void iommu_init(Object *obj)
 294{
 295    IOMMUState *s = SUN4U_IOMMU(obj);
 296    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 297
 298    memory_region_init_iommu(&s->iommu, sizeof(s->iommu),
 299                             TYPE_SUN4U_IOMMU_MEMORY_REGION, OBJECT(s),
 300                             "iommu-sun4u", UINT64_MAX);
 301    address_space_init(&s->iommu_as, MEMORY_REGION(&s->iommu), "iommu-as");
 302
 303    memory_region_init_io(&s->iomem, obj, &iommu_mem_ops, s, "iommu",
 304                          IOMMU_NREGS * sizeof(uint64_t));
 305    sysbus_init_mmio(sbd, &s->iomem);
 306}
 307
 308static void iommu_class_init(ObjectClass *klass, void *data)
 309{
 310    DeviceClass *dc = DEVICE_CLASS(klass);
 311
 312    dc->reset = iommu_reset;
 313}
 314
 315static const TypeInfo iommu_info = {
 316    .name          = TYPE_SUN4U_IOMMU,
 317    .parent        = TYPE_SYS_BUS_DEVICE,
 318    .instance_size = sizeof(IOMMUState),
 319    .instance_init = iommu_init,
 320    .class_init    = iommu_class_init,
 321};
 322
 323static void sun4u_iommu_memory_region_class_init(ObjectClass *klass, void *data)
 324{
 325    IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
 326
 327    imrc->translate = sun4u_translate_iommu;
 328}
 329
 330static const TypeInfo sun4u_iommu_memory_region_info = {
 331    .parent = TYPE_IOMMU_MEMORY_REGION,
 332    .name = TYPE_SUN4U_IOMMU_MEMORY_REGION,
 333    .class_init = sun4u_iommu_memory_region_class_init,
 334};
 335
 336static void iommu_register_types(void)
 337{
 338    type_register_static(&iommu_info);
 339    type_register_static(&sun4u_iommu_memory_region_info);
 340}
 341
 342type_init(iommu_register_types)
 343