sun4m_iommu.c 12.8 KB
Newer Older
1
/*
2
 * QEMU Sun4m iommu emulation
3
 *
B
bellard 已提交
4
 * Copyright (c) 2003-2005 Fabrice Bellard
5
 *
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
B
Blue Swirl 已提交
24

P
Peter Maydell 已提交
25
#include "qemu/osdep.h"
P
Paolo Bonzini 已提交
26
#include "hw/sparc/sun4m.h"
27
#include "hw/sysbus.h"
28
#include "exec/address-spaces.h"
29
#include "trace.h"
30

31 32 33 34 35 36 37 38
/*
 * I/O MMU used by Sun4m systems
 *
 * Chipset docs:
 * "Sun-4M System Architecture (revision 2.0) by Chuck Narad", 950-1373-01,
 * http://mediacast.sun.com/users/Barton808/media/Sun4M_SystemArchitecture_edited2.pdf
 */

B
bellard 已提交
39
#define IOMMU_CTRL          (0x0000 >> 2)
40 41 42 43 44 45 46 47 48 49 50 51
#define IOMMU_CTRL_IMPL     0xf0000000 /* Implementation */
#define IOMMU_CTRL_VERS     0x0f000000 /* Version */
#define IOMMU_CTRL_RNGE     0x0000001c /* Mapping RANGE */
#define IOMMU_RNGE_16MB     0x00000000 /* 0xff000000 -> 0xffffffff */
#define IOMMU_RNGE_32MB     0x00000004 /* 0xfe000000 -> 0xffffffff */
#define IOMMU_RNGE_64MB     0x00000008 /* 0xfc000000 -> 0xffffffff */
#define IOMMU_RNGE_128MB    0x0000000c /* 0xf8000000 -> 0xffffffff */
#define IOMMU_RNGE_256MB    0x00000010 /* 0xf0000000 -> 0xffffffff */
#define IOMMU_RNGE_512MB    0x00000014 /* 0xe0000000 -> 0xffffffff */
#define IOMMU_RNGE_1GB      0x00000018 /* 0xc0000000 -> 0xffffffff */
#define IOMMU_RNGE_2GB      0x0000001c /* 0x80000000 -> 0xffffffff */
#define IOMMU_CTRL_ENAB     0x00000001 /* IOMMU Enable */
B
bellard 已提交
52 53 54 55 56 57 58 59 60 61 62
#define IOMMU_CTRL_MASK     0x0000001d

#define IOMMU_BASE          (0x0004 >> 2)
#define IOMMU_BASE_MASK     0x07fffc00

#define IOMMU_TLBFLUSH      (0x0014 >> 2)
#define IOMMU_TLBFLUSH_MASK 0xffffffff

#define IOMMU_PGFLUSH       (0x0018 >> 2)
#define IOMMU_PGFLUSH_MASK  0xffffffff

63 64
#define IOMMU_AFSR          (0x1000 >> 2)
#define IOMMU_AFSR_ERR      0x80000000 /* LE, TO, or BE asserted */
65 66 67 68 69 70
#define IOMMU_AFSR_LE       0x40000000 /* SBUS reports error after
                                          transaction */
#define IOMMU_AFSR_TO       0x20000000 /* Write access took more than
                                          12.8 us. */
#define IOMMU_AFSR_BE       0x10000000 /* Write access received error
                                          acknowledge */
71 72
#define IOMMU_AFSR_SIZE     0x0e000000 /* Size of transaction causing error */
#define IOMMU_AFSR_S        0x01000000 /* Sparc was in supervisor mode */
73 74
#define IOMMU_AFSR_RESV     0x00800000 /* Reserved, forced to 0x8 by
                                          hardware */
75 76 77
#define IOMMU_AFSR_ME       0x00080000 /* Multiple errors occurred */
#define IOMMU_AFSR_RD       0x00040000 /* A read operation was in progress */
#define IOMMU_AFSR_FAV      0x00020000 /* IOMMU afar has valid contents */
78
#define IOMMU_AFSR_MASK     0xff0fffff
79 80 81

#define IOMMU_AFAR          (0x1004 >> 2)

82 83 84 85 86 87 88 89 90 91 92 93 94
#define IOMMU_AER           (0x1008 >> 2) /* Arbiter Enable Register */
#define IOMMU_AER_EN_P0_ARB 0x00000001    /* MBus master 0x8 (Always 1) */
#define IOMMU_AER_EN_P1_ARB 0x00000002    /* MBus master 0x9 */
#define IOMMU_AER_EN_P2_ARB 0x00000004    /* MBus master 0xa */
#define IOMMU_AER_EN_P3_ARB 0x00000008    /* MBus master 0xb */
#define IOMMU_AER_EN_0      0x00010000    /* SBus slot 0 */
#define IOMMU_AER_EN_1      0x00020000    /* SBus slot 1 */
#define IOMMU_AER_EN_2      0x00040000    /* SBus slot 2 */
#define IOMMU_AER_EN_3      0x00080000    /* SBus slot 3 */
#define IOMMU_AER_EN_F      0x00100000    /* SBus on-board */
#define IOMMU_AER_SBW       0x80000000    /* S-to-M asynchronous writes */
#define IOMMU_AER_MASK      0x801f000f

B
bellard 已提交
95 96 97 98
#define IOMMU_SBCFG0        (0x1010 >> 2) /* SBUS configration per-slot */
#define IOMMU_SBCFG1        (0x1014 >> 2) /* SBUS configration per-slot */
#define IOMMU_SBCFG2        (0x1018 >> 2) /* SBUS configration per-slot */
#define IOMMU_SBCFG3        (0x101c >> 2) /* SBUS configration per-slot */
99 100
#define IOMMU_SBCFG_SAB30   0x00010000 /* Phys-address bit 30 when
                                          bypass enabled */
B
bellard 已提交
101 102 103
#define IOMMU_SBCFG_BA16    0x00000004 /* Slave supports 16 byte bursts */
#define IOMMU_SBCFG_BA8     0x00000002 /* Slave supports 8 byte bursts */
#define IOMMU_SBCFG_BYPASS  0x00000001 /* Bypass IOMMU, treat all addresses
B
blueswir1 已提交
104
                                          produced by this device as pure
B
bellard 已提交
105 106 107 108 109 110
                                          physical. */
#define IOMMU_SBCFG_MASK    0x00010003

#define IOMMU_ARBEN         (0x2000 >> 2) /* SBUS arbitration enable */
#define IOMMU_ARBEN_MASK    0x001f0000
#define IOMMU_MID           0x00000008
111

112 113 114 115 116 117
#define IOMMU_MASK_ID       (0x3018 >> 2) /* Mask ID */
#define IOMMU_MASK_ID_MASK  0x00ffffff

#define IOMMU_MSII_MASK     0x26000000 /* microSPARC II mask number */
#define IOMMU_TS_MASK       0x23000000 /* turboSPARC mask number */

118
/* The format of an iopte in the page tables */
B
blueswir1 已提交
119
#define IOPTE_PAGE          0xffffff00 /* Physical page number (PA[35:12]) */
120 121
#define IOPTE_CACHE         0x00000080 /* Cached (in vme IOCACHE or
                                          Viking/MXCC) */
S
Stefan Weil 已提交
122
#define IOPTE_WRITE         0x00000004 /* Writable */
123 124 125
#define IOPTE_VALID         0x00000002 /* IOPTE is valid */
#define IOPTE_WAZ           0x00000001 /* Write as zeros */

126 127 128
#define IOMMU_PAGE_SHIFT    12
#define IOMMU_PAGE_SIZE     (1 << IOMMU_PAGE_SHIFT)
#define IOMMU_PAGE_MASK     ~(IOMMU_PAGE_SIZE - 1)
129

A
Avi Kivity 已提交
130
static uint64_t iommu_mem_read(void *opaque, hwaddr addr,
A
Avi Kivity 已提交
131
                               unsigned size)
132 133
{
    IOMMUState *s = opaque;
A
Avi Kivity 已提交
134
    hwaddr saddr;
135
    uint32_t ret;
136

137
    saddr = addr >> 2;
138 139
    switch (saddr) {
    default:
140 141 142 143 144 145
        ret = s->regs[saddr];
        break;
    case IOMMU_AFAR:
    case IOMMU_AFSR:
        ret = s->regs[saddr];
        qemu_irq_lower(s->irq);
B
blueswir1 已提交
146
        break;
147
    }
148
    trace_sun4m_iommu_mem_readl(saddr, ret);
149
    return ret;
150 151
}

A
Avi Kivity 已提交
152
static void iommu_mem_write(void *opaque, hwaddr addr,
A
Avi Kivity 已提交
153
                            uint64_t val, unsigned size)
154 155
{
    IOMMUState *s = opaque;
A
Avi Kivity 已提交
156
    hwaddr saddr;
157

158
    saddr = addr >> 2;
159
    trace_sun4m_iommu_mem_writel(saddr, val);
160
    switch (saddr) {
B
bellard 已提交
161
    case IOMMU_CTRL:
B
blueswir1 已提交
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
        switch (val & IOMMU_CTRL_RNGE) {
        case IOMMU_RNGE_16MB:
            s->iostart = 0xffffffffff000000ULL;
            break;
        case IOMMU_RNGE_32MB:
            s->iostart = 0xfffffffffe000000ULL;
            break;
        case IOMMU_RNGE_64MB:
            s->iostart = 0xfffffffffc000000ULL;
            break;
        case IOMMU_RNGE_128MB:
            s->iostart = 0xfffffffff8000000ULL;
            break;
        case IOMMU_RNGE_256MB:
            s->iostart = 0xfffffffff0000000ULL;
            break;
        case IOMMU_RNGE_512MB:
            s->iostart = 0xffffffffe0000000ULL;
            break;
        case IOMMU_RNGE_1GB:
            s->iostart = 0xffffffffc0000000ULL;
            break;
        default:
        case IOMMU_RNGE_2GB:
            s->iostart = 0xffffffff80000000ULL;
            break;
        }
189
        trace_sun4m_iommu_mem_writel_ctrl(s->iostart);
190
        s->regs[saddr] = ((val & IOMMU_CTRL_MASK) | s->version);
B
blueswir1 已提交
191
        break;
B
bellard 已提交
192
    case IOMMU_BASE:
B
blueswir1 已提交
193 194
        s->regs[saddr] = val & IOMMU_BASE_MASK;
        break;
B
bellard 已提交
195
    case IOMMU_TLBFLUSH:
196
        trace_sun4m_iommu_mem_writel_tlbflush(val);
B
blueswir1 已提交
197 198
        s->regs[saddr] = val & IOMMU_TLBFLUSH_MASK;
        break;
B
bellard 已提交
199
    case IOMMU_PGFLUSH:
200
        trace_sun4m_iommu_mem_writel_pgflush(val);
B
blueswir1 已提交
201 202
        s->regs[saddr] = val & IOMMU_PGFLUSH_MASK;
        break;
203 204 205 206
    case IOMMU_AFAR:
        s->regs[saddr] = val;
        qemu_irq_lower(s->irq);
        break;
207 208 209
    case IOMMU_AER:
        s->regs[saddr] = (val & IOMMU_AER_MASK) | IOMMU_AER_EN_P0_ARB;
        break;
210 211
    case IOMMU_AFSR:
        s->regs[saddr] = (val & IOMMU_AFSR_MASK) | IOMMU_AFSR_RESV;
212
        qemu_irq_lower(s->irq);
213
        break;
B
bellard 已提交
214 215 216 217
    case IOMMU_SBCFG0:
    case IOMMU_SBCFG1:
    case IOMMU_SBCFG2:
    case IOMMU_SBCFG3:
B
blueswir1 已提交
218 219
        s->regs[saddr] = val & IOMMU_SBCFG_MASK;
        break;
B
bellard 已提交
220 221 222
    case IOMMU_ARBEN:
        // XXX implement SBus probing: fault when reading unmapped
        // addresses, fault cause and address stored to MMU/IOMMU
B
blueswir1 已提交
223 224
        s->regs[saddr] = (val & IOMMU_ARBEN_MASK) | IOMMU_MID;
        break;
225 226 227
    case IOMMU_MASK_ID:
        s->regs[saddr] |= val & IOMMU_MASK_ID_MASK;
        break;
228
    default:
B
blueswir1 已提交
229 230
        s->regs[saddr] = val;
        break;
231 232 233
    }
}

A
Avi Kivity 已提交
234 235 236 237 238 239 240 241
static const MemoryRegionOps iommu_mem_ops = {
    .read = iommu_mem_read,
    .write = iommu_mem_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid = {
        .min_access_size = 4,
        .max_access_size = 4,
    },
242 243
};

A
Avi Kivity 已提交
244
static uint32_t iommu_page_get_flags(IOMMUState *s, hwaddr addr)
245
{
246
    uint32_t ret;
A
Avi Kivity 已提交
247 248
    hwaddr iopte;
    hwaddr pa = addr;
249

250
    iopte = s->regs[IOMMU_BASE] << 4;
B
bellard 已提交
251
    addr &= ~s->iostart;
252
    iopte += (addr >> (IOMMU_PAGE_SHIFT - 2)) & ~3;
253 254
    ret = address_space_ldl_be(&address_space_memory, iopte,
                               MEMTXATTRS_UNSPECIFIED, NULL);
255
    trace_sun4m_iommu_page_get_flags(pa, iopte, ret);
256
    return ret;
P
pbrook 已提交
257 258
}

A
Avi Kivity 已提交
259
static hwaddr iommu_translate_pa(hwaddr addr,
260
                                             uint32_t pte)
P
pbrook 已提交
261
{
A
Avi Kivity 已提交
262
    hwaddr pa;
263

264
    pa = ((pte & IOPTE_PAGE) << 4) + (addr & ~IOMMU_PAGE_MASK);
265
    trace_sun4m_iommu_translate_pa(addr, pa, pte);
B
bellard 已提交
266
    return pa;
267 268
}

A
Avi Kivity 已提交
269
static void iommu_bad_addr(IOMMUState *s, hwaddr addr,
270
                           int is_write)
271
{
272
    trace_sun4m_iommu_bad_addr(addr);
273
    s->regs[IOMMU_AFSR] = IOMMU_AFSR_ERR | IOMMU_AFSR_LE | IOMMU_AFSR_RESV |
274 275 276 277
        IOMMU_AFSR_FAV;
    if (!is_write)
        s->regs[IOMMU_AFSR] |= IOMMU_AFSR_RD;
    s->regs[IOMMU_AFAR] = addr;
278
    qemu_irq_raise(s->irq);
279 280
}

A
Avi Kivity 已提交
281
void sparc_iommu_memory_rw(void *opaque, hwaddr addr,
282
                           uint8_t *buf, int len, int is_write)
P
pbrook 已提交
283
{
284 285
    int l;
    uint32_t flags;
A
Avi Kivity 已提交
286
    hwaddr page, phys_addr;
P
pbrook 已提交
287 288

    while (len > 0) {
289 290
        page = addr & IOMMU_PAGE_MASK;
        l = (page + IOMMU_PAGE_SIZE) - addr;
P
pbrook 已提交
291 292 293
        if (l > len)
            l = len;
        flags = iommu_page_get_flags(opaque, page);
294 295
        if (!(flags & IOPTE_VALID)) {
            iommu_bad_addr(opaque, page, is_write);
P
pbrook 已提交
296
            return;
297
        }
B
blueswir1 已提交
298
        phys_addr = iommu_translate_pa(addr, flags);
P
pbrook 已提交
299
        if (is_write) {
300 301
            if (!(flags & IOPTE_WRITE)) {
                iommu_bad_addr(opaque, page, is_write);
P
pbrook 已提交
302
                return;
303
            }
304
            cpu_physical_memory_write(phys_addr, buf, l);
P
pbrook 已提交
305
        } else {
306
            cpu_physical_memory_read(phys_addr, buf, l);
P
pbrook 已提交
307 308 309 310 311 312 313
        }
        len -= l;
        buf += l;
        addr += l;
    }
}

314 315 316 317
static const VMStateDescription vmstate_iommu = {
    .name ="iommu",
    .version_id = 2,
    .minimum_version_id = 2,
318
    .fields = (VMStateField[]) {
319 320 321 322 323
        VMSTATE_UINT32_ARRAY(regs, IOMMUState, IOMMU_NREGS),
        VMSTATE_UINT64(iostart, IOMMUState),
        VMSTATE_END_OF_LIST()
    }
};
B
bellard 已提交
324

325
static void iommu_reset(DeviceState *d)
B
bellard 已提交
326
{
A
Andreas Färber 已提交
327
    IOMMUState *s = SUN4M_IOMMU(d);
B
bellard 已提交
328

B
bellard 已提交
329
    memset(s->regs, 0, IOMMU_NREGS * 4);
B
bellard 已提交
330
    s->iostart = 0;
331 332
    s->regs[IOMMU_CTRL] = s->version;
    s->regs[IOMMU_ARBEN] = IOMMU_MID;
333
    s->regs[IOMMU_AFSR] = IOMMU_AFSR_RESV;
334
    s->regs[IOMMU_AER] = IOMMU_AER_EN_P0_ARB | IOMMU_AER_EN_P1_ARB;
335
    s->regs[IOMMU_MASK_ID] = IOMMU_TS_MASK;
B
bellard 已提交
336 337
}

X
xiaoqiang zhao 已提交
338
static void iommu_init(Object *obj)
B
Blue Swirl 已提交
339
{
X
xiaoqiang zhao 已提交
340 341
    IOMMUState *s = SUN4M_IOMMU(obj);
    SysBusDevice *dev = SYS_BUS_DEVICE(obj);
342

B
Blue Swirl 已提交
343
    sysbus_init_irq(dev, &s->irq);
344

X
xiaoqiang zhao 已提交
345
    memory_region_init_io(&s->iomem, obj, &iommu_mem_ops, s, "iommu",
A
Avi Kivity 已提交
346
                          IOMMU_NREGS * sizeof(uint32_t));
347
    sysbus_init_mmio(dev, &s->iomem);
348
}
B
Blue Swirl 已提交
349

350
static Property iommu_properties[] = {
351
    DEFINE_PROP_UINT32("version", IOMMUState, version, 0),
352 353 354 355 356
    DEFINE_PROP_END_OF_LIST(),
};

static void iommu_class_init(ObjectClass *klass, void *data)
{
357
    DeviceClass *dc = DEVICE_CLASS(klass);
358

359 360 361
    dc->reset = iommu_reset;
    dc->vmsd = &vmstate_iommu;
    dc->props = iommu_properties;
362 363
}

364
static const TypeInfo iommu_info = {
A
Andreas Färber 已提交
365
    .name          = TYPE_SUN4M_IOMMU,
366 367
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(IOMMUState),
X
xiaoqiang zhao 已提交
368
    .instance_init = iommu_init,
369
    .class_init    = iommu_class_init,
B
Blue Swirl 已提交
370 371
};

A
Andreas Färber 已提交
372
static void iommu_register_types(void)
B
Blue Swirl 已提交
373
{
374
    type_register_static(&iommu_info);
B
Blue Swirl 已提交
375 376
}

A
Andreas Färber 已提交
377
type_init(iommu_register_types)