qemu

Форк
0
/
tz-mpc.c 
636 строк · 19.0 Кб
1
/*
2
 * ARM AHB5 TrustZone Memory Protection Controller emulation
3
 *
4
 * Copyright (c) 2018 Linaro Limited
5
 * Written by Peter Maydell
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License version 2 or
9
 * (at your option) any later version.
10
 */
11

12
#include "qemu/osdep.h"
13
#include "qemu/log.h"
14
#include "qemu/module.h"
15
#include "qapi/error.h"
16
#include "trace.h"
17
#include "hw/sysbus.h"
18
#include "migration/vmstate.h"
19
#include "hw/registerfields.h"
20
#include "hw/irq.h"
21
#include "hw/misc/tz-mpc.h"
22
#include "hw/qdev-properties.h"
23

24
/* Our IOMMU has two IOMMU indexes, one for secure transactions and one for
25
 * non-secure transactions.
26
 */
27
enum {
28
    IOMMU_IDX_S,
29
    IOMMU_IDX_NS,
30
    IOMMU_NUM_INDEXES,
31
};
32

33
/* Config registers */
34
REG32(CTRL, 0x00)
35
    FIELD(CTRL, SEC_RESP, 4, 1)
36
    FIELD(CTRL, AUTOINC, 8, 1)
37
    FIELD(CTRL, LOCKDOWN, 31, 1)
38
REG32(BLK_MAX, 0x10)
39
REG32(BLK_CFG, 0x14)
40
REG32(BLK_IDX, 0x18)
41
REG32(BLK_LUT, 0x1c)
42
REG32(INT_STAT, 0x20)
43
    FIELD(INT_STAT, IRQ, 0, 1)
44
REG32(INT_CLEAR, 0x24)
45
    FIELD(INT_CLEAR, IRQ, 0, 1)
46
REG32(INT_EN, 0x28)
47
    FIELD(INT_EN, IRQ, 0, 1)
48
REG32(INT_INFO1, 0x2c)
49
REG32(INT_INFO2, 0x30)
50
    FIELD(INT_INFO2, HMASTER, 0, 16)
51
    FIELD(INT_INFO2, HNONSEC, 16, 1)
52
    FIELD(INT_INFO2, CFG_NS, 17, 1)
53
REG32(INT_SET, 0x34)
54
    FIELD(INT_SET, IRQ, 0, 1)
55
REG32(PIDR4, 0xfd0)
56
REG32(PIDR5, 0xfd4)
57
REG32(PIDR6, 0xfd8)
58
REG32(PIDR7, 0xfdc)
59
REG32(PIDR0, 0xfe0)
60
REG32(PIDR1, 0xfe4)
61
REG32(PIDR2, 0xfe8)
62
REG32(PIDR3, 0xfec)
63
REG32(CIDR0, 0xff0)
64
REG32(CIDR1, 0xff4)
65
REG32(CIDR2, 0xff8)
66
REG32(CIDR3, 0xffc)
67

68
static const uint8_t tz_mpc_idregs[] = {
69
    0x04, 0x00, 0x00, 0x00,
70
    0x60, 0xb8, 0x1b, 0x00,
71
    0x0d, 0xf0, 0x05, 0xb1,
72
};
73

74
static void tz_mpc_irq_update(TZMPC *s)
75
{
76
    qemu_set_irq(s->irq, s->int_stat && s->int_en);
77
}
78

79
static void tz_mpc_iommu_notify(TZMPC *s, uint32_t lutidx,
80
                                uint32_t oldlut, uint32_t newlut)
81
{
82
    /* Called when the LUT word at lutidx has changed from oldlut to newlut;
83
     * must call the IOMMU notifiers for the changed blocks.
84
     */
85
    IOMMUTLBEvent event = {
86
        .entry = {
87
            .addr_mask = s->blocksize - 1,
88
        }
89
    };
90
    hwaddr addr = lutidx * s->blocksize * 32;
91
    int i;
92

93
    for (i = 0; i < 32; i++, addr += s->blocksize) {
94
        bool block_is_ns;
95

96
        if (!((oldlut ^ newlut) & (1 << i))) {
97
            continue;
98
        }
99
        /* This changes the mappings for both the S and the NS space,
100
         * so we need to do four notifies: an UNMAP then a MAP for each.
101
         */
102
        block_is_ns = newlut & (1 << i);
103

104
        trace_tz_mpc_iommu_notify(addr);
105
        event.entry.iova = addr;
106
        event.entry.translated_addr = addr;
107

108
        event.type = IOMMU_NOTIFIER_UNMAP;
109
        event.entry.perm = IOMMU_NONE;
110
        memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, event);
111
        memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, event);
112

113
        event.type = IOMMU_NOTIFIER_MAP;
114
        event.entry.perm = IOMMU_RW;
115
        if (block_is_ns) {
116
            event.entry.target_as = &s->blocked_io_as;
117
        } else {
118
            event.entry.target_as = &s->downstream_as;
119
        }
120
        memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, event);
121
        if (block_is_ns) {
122
            event.entry.target_as = &s->downstream_as;
123
        } else {
124
            event.entry.target_as = &s->blocked_io_as;
125
        }
126
        memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, event);
127
    }
128
}
129

130
static void tz_mpc_autoinc_idx(TZMPC *s, unsigned access_size)
131
{
132
    /* Auto-increment BLK_IDX if necessary */
133
    if (access_size == 4 && (s->ctrl & R_CTRL_AUTOINC_MASK)) {
134
        s->blk_idx++;
135
        s->blk_idx %= s->blk_max;
136
    }
137
}
138

139
static MemTxResult tz_mpc_reg_read(void *opaque, hwaddr addr,
140
                                   uint64_t *pdata,
141
                                   unsigned size, MemTxAttrs attrs)
142
{
143
    TZMPC *s = TZ_MPC(opaque);
144
    uint64_t r;
145
    uint32_t offset = addr & ~0x3;
146

147
    if (!attrs.secure && offset < A_PIDR4) {
148
        /* NS accesses can only see the ID registers */
149
        qemu_log_mask(LOG_GUEST_ERROR,
150
                      "TZ MPC register read: NS access to offset 0x%x\n",
151
                      offset);
152
        r = 0;
153
        goto read_out;
154
    }
155

156
    switch (offset) {
157
    case A_CTRL:
158
        r = s->ctrl;
159
        break;
160
    case A_BLK_MAX:
161
        r = s->blk_max - 1;
162
        break;
163
    case A_BLK_CFG:
164
        /* We are never in "init in progress state", so this just indicates
165
         * the block size. s->blocksize == (1 << BLK_CFG + 5), so
166
         * BLK_CFG == ctz32(s->blocksize) - 5
167
         */
168
        r = ctz32(s->blocksize) - 5;
169
        break;
170
    case A_BLK_IDX:
171
        r = s->blk_idx;
172
        break;
173
    case A_BLK_LUT:
174
        r = s->blk_lut[s->blk_idx];
175
        tz_mpc_autoinc_idx(s, size);
176
        break;
177
    case A_INT_STAT:
178
        r = s->int_stat;
179
        break;
180
    case A_INT_EN:
181
        r = s->int_en;
182
        break;
183
    case A_INT_INFO1:
184
        r = s->int_info1;
185
        break;
186
    case A_INT_INFO2:
187
        r = s->int_info2;
188
        break;
189
    case A_PIDR4:
190
    case A_PIDR5:
191
    case A_PIDR6:
192
    case A_PIDR7:
193
    case A_PIDR0:
194
    case A_PIDR1:
195
    case A_PIDR2:
196
    case A_PIDR3:
197
    case A_CIDR0:
198
    case A_CIDR1:
199
    case A_CIDR2:
200
    case A_CIDR3:
201
        r = tz_mpc_idregs[(offset - A_PIDR4) / 4];
202
        break;
203
    case A_INT_CLEAR:
204
    case A_INT_SET:
205
        qemu_log_mask(LOG_GUEST_ERROR,
206
                      "TZ MPC register read: write-only offset 0x%x\n",
207
                      offset);
208
        r = 0;
209
        break;
210
    default:
211
        qemu_log_mask(LOG_GUEST_ERROR,
212
                      "TZ MPC register read: bad offset 0x%x\n", offset);
213
        r = 0;
214
        break;
215
    }
216

217
    if (size != 4) {
218
        /* None of our registers are read-sensitive (except BLK_LUT,
219
         * which can special case the "size not 4" case), so just
220
         * pull the right bytes out of the word read result.
221
         */
222
        r = extract32(r, (addr & 3) * 8, size * 8);
223
    }
224

225
read_out:
226
    trace_tz_mpc_reg_read(addr, r, size);
227
    *pdata = r;
228
    return MEMTX_OK;
229
}
230

231
static MemTxResult tz_mpc_reg_write(void *opaque, hwaddr addr,
232
                                    uint64_t value,
233
                                    unsigned size, MemTxAttrs attrs)
234
{
235
    TZMPC *s = TZ_MPC(opaque);
236
    uint32_t offset = addr & ~0x3;
237

238
    trace_tz_mpc_reg_write(addr, value, size);
239

240
    if (!attrs.secure && offset < A_PIDR4) {
241
        /* NS accesses can only see the ID registers */
242
        qemu_log_mask(LOG_GUEST_ERROR,
243
                      "TZ MPC register write: NS access to offset 0x%x\n",
244
                      offset);
245
        return MEMTX_OK;
246
    }
247

248
    if (size != 4) {
249
        /* Expand the byte or halfword write to a full word size.
250
         * In most cases we can do this with zeroes; the exceptions
251
         * are CTRL, BLK_IDX and BLK_LUT.
252
         */
253
        uint32_t oldval;
254

255
        switch (offset) {
256
        case A_CTRL:
257
            oldval = s->ctrl;
258
            break;
259
        case A_BLK_IDX:
260
            oldval = s->blk_idx;
261
            break;
262
        case A_BLK_LUT:
263
            oldval = s->blk_lut[s->blk_idx];
264
            break;
265
        default:
266
            oldval = 0;
267
            break;
268
        }
269
        value = deposit32(oldval, (addr & 3) * 8, size * 8, value);
270
    }
271

272
    if ((s->ctrl & R_CTRL_LOCKDOWN_MASK) &&
273
        (offset == A_CTRL || offset == A_BLK_LUT || offset == A_INT_EN)) {
274
        /* Lockdown mode makes these three registers read-only, and
275
         * the only way out of it is to reset the device.
276
         */
277
        qemu_log_mask(LOG_GUEST_ERROR, "TZ MPC register write to offset 0x%x "
278
                      "while MPC is in lockdown mode\n", offset);
279
        return MEMTX_OK;
280
    }
281

282
    switch (offset) {
283
    case A_CTRL:
284
        /* We don't implement the 'data gating' feature so all other bits
285
         * are reserved and we make them RAZ/WI.
286
         */
287
        s->ctrl = value & (R_CTRL_SEC_RESP_MASK |
288
                           R_CTRL_AUTOINC_MASK |
289
                           R_CTRL_LOCKDOWN_MASK);
290
        break;
291
    case A_BLK_IDX:
292
        s->blk_idx = value % s->blk_max;
293
        break;
294
    case A_BLK_LUT:
295
        tz_mpc_iommu_notify(s, s->blk_idx, s->blk_lut[s->blk_idx], value);
296
        s->blk_lut[s->blk_idx] = value;
297
        tz_mpc_autoinc_idx(s, size);
298
        break;
299
    case A_INT_CLEAR:
300
        if (value & R_INT_CLEAR_IRQ_MASK) {
301
            s->int_stat = 0;
302
            tz_mpc_irq_update(s);
303
        }
304
        break;
305
    case A_INT_EN:
306
        s->int_en = value & R_INT_EN_IRQ_MASK;
307
        tz_mpc_irq_update(s);
308
        break;
309
    case A_INT_SET:
310
        if (value & R_INT_SET_IRQ_MASK) {
311
            s->int_stat = R_INT_STAT_IRQ_MASK;
312
            tz_mpc_irq_update(s);
313
        }
314
        break;
315
    case A_PIDR4:
316
    case A_PIDR5:
317
    case A_PIDR6:
318
    case A_PIDR7:
319
    case A_PIDR0:
320
    case A_PIDR1:
321
    case A_PIDR2:
322
    case A_PIDR3:
323
    case A_CIDR0:
324
    case A_CIDR1:
325
    case A_CIDR2:
326
    case A_CIDR3:
327
        qemu_log_mask(LOG_GUEST_ERROR,
328
                      "TZ MPC register write: read-only offset 0x%x\n", offset);
329
        break;
330
    default:
331
        qemu_log_mask(LOG_GUEST_ERROR,
332
                      "TZ MPC register write: bad offset 0x%x\n", offset);
333
        break;
334
    }
335

336
    return MEMTX_OK;
337
}
338

339
static const MemoryRegionOps tz_mpc_reg_ops = {
340
    .read_with_attrs = tz_mpc_reg_read,
341
    .write_with_attrs = tz_mpc_reg_write,
342
    .endianness = DEVICE_LITTLE_ENDIAN,
343
    .valid.min_access_size = 1,
344
    .valid.max_access_size = 4,
345
    .impl.min_access_size = 1,
346
    .impl.max_access_size = 4,
347
};
348

349
static inline bool tz_mpc_cfg_ns(TZMPC *s, hwaddr addr)
350
{
351
    /* Return the cfg_ns bit from the LUT for the specified address */
352
    hwaddr blknum = addr / s->blocksize;
353
    hwaddr blkword = blknum / 32;
354
    uint32_t blkbit = 1U << (blknum % 32);
355

356
    /* This would imply the address was larger than the size we
357
     * defined this memory region to be, so it can't happen.
358
     */
359
    assert(blkword < s->blk_max);
360
    return s->blk_lut[blkword] & blkbit;
361
}
362

363
static MemTxResult tz_mpc_handle_block(TZMPC *s, hwaddr addr, MemTxAttrs attrs)
364
{
365
    /* Handle a blocked transaction: raise IRQ, capture info, etc */
366
    if (!s->int_stat) {
367
        /* First blocked transfer: capture information into INT_INFO1 and
368
         * INT_INFO2. Subsequent transfers are still blocked but don't
369
         * capture information until the guest clears the interrupt.
370
         */
371

372
        s->int_info1 = addr;
373
        s->int_info2 = 0;
374
        s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, HMASTER,
375
                                  attrs.requester_id & 0xffff);
376
        s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, HNONSEC,
377
                                  ~attrs.secure);
378
        s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, CFG_NS,
379
                                  tz_mpc_cfg_ns(s, addr));
380
        s->int_stat |= R_INT_STAT_IRQ_MASK;
381
        tz_mpc_irq_update(s);
382
    }
383

384
    /* Generate bus error if desired; otherwise RAZ/WI */
385
    return (s->ctrl & R_CTRL_SEC_RESP_MASK) ? MEMTX_ERROR : MEMTX_OK;
386
}
387

388
/* Accesses only reach these read and write functions if the MPC is
389
 * blocking them; non-blocked accesses go directly to the downstream
390
 * memory region without passing through this code.
391
 */
392
static MemTxResult tz_mpc_mem_blocked_read(void *opaque, hwaddr addr,
393
                                           uint64_t *pdata,
394
                                           unsigned size, MemTxAttrs attrs)
395
{
396
    TZMPC *s = TZ_MPC(opaque);
397

398
    trace_tz_mpc_mem_blocked_read(addr, size, attrs.secure);
399

400
    *pdata = 0;
401
    return tz_mpc_handle_block(s, addr, attrs);
402
}
403

404
static MemTxResult tz_mpc_mem_blocked_write(void *opaque, hwaddr addr,
405
                                            uint64_t value,
406
                                            unsigned size, MemTxAttrs attrs)
407
{
408
    TZMPC *s = TZ_MPC(opaque);
409

410
    trace_tz_mpc_mem_blocked_write(addr, value, size, attrs.secure);
411

412
    return tz_mpc_handle_block(s, addr, attrs);
413
}
414

415
static const MemoryRegionOps tz_mpc_mem_blocked_ops = {
416
    .read_with_attrs = tz_mpc_mem_blocked_read,
417
    .write_with_attrs = tz_mpc_mem_blocked_write,
418
    .endianness = DEVICE_LITTLE_ENDIAN,
419
    .valid.min_access_size = 1,
420
    .valid.max_access_size = 8,
421
    .impl.min_access_size = 1,
422
    .impl.max_access_size = 8,
423
};
424

425
static IOMMUTLBEntry tz_mpc_translate(IOMMUMemoryRegion *iommu,
426
                                      hwaddr addr, IOMMUAccessFlags flags,
427
                                      int iommu_idx)
428
{
429
    TZMPC *s = TZ_MPC(container_of(iommu, TZMPC, upstream));
430
    bool ok;
431

432
    IOMMUTLBEntry ret = {
433
        .iova = addr & ~(s->blocksize - 1),
434
        .translated_addr = addr & ~(s->blocksize - 1),
435
        .addr_mask = s->blocksize - 1,
436
        .perm = IOMMU_RW,
437
    };
438

439
    /* Look at the per-block configuration for this address, and
440
     * return a TLB entry directing the transaction at either
441
     * downstream_as or blocked_io_as, as appropriate.
442
     * If the LUT cfg_ns bit is 1, only non-secure transactions
443
     * may pass. If the bit is 0, only secure transactions may pass.
444
     */
445
    ok = tz_mpc_cfg_ns(s, addr) == (iommu_idx == IOMMU_IDX_NS);
446

447
    trace_tz_mpc_translate(addr, flags,
448
                           iommu_idx == IOMMU_IDX_S ? "S" : "NS",
449
                           ok ? "pass" : "block");
450

451
    ret.target_as = ok ? &s->downstream_as : &s->blocked_io_as;
452
    return ret;
453
}
454

455
static int tz_mpc_attrs_to_index(IOMMUMemoryRegion *iommu, MemTxAttrs attrs)
456
{
457
    /* We treat unspecified attributes like secure. Transactions with
458
     * unspecified attributes come from places like
459
     * rom_reset() for initial image load, and we want
460
     * those to pass through the from-reset "everything is secure" config.
461
     * All the real during-emulation transactions from the CPU will
462
     * specify attributes.
463
     */
464
    return (attrs.unspecified || attrs.secure) ? IOMMU_IDX_S : IOMMU_IDX_NS;
465
}
466

467
static int tz_mpc_num_indexes(IOMMUMemoryRegion *iommu)
468
{
469
    return IOMMU_NUM_INDEXES;
470
}
471

472
static void tz_mpc_reset(DeviceState *dev)
473
{
474
    TZMPC *s = TZ_MPC(dev);
475

476
    s->ctrl = 0x00000100;
477
    s->blk_idx = 0;
478
    s->int_stat = 0;
479
    s->int_en = 1;
480
    s->int_info1 = 0;
481
    s->int_info2 = 0;
482

483
    memset(s->blk_lut, 0, s->blk_max * sizeof(uint32_t));
484
}
485

486
static void tz_mpc_init(Object *obj)
487
{
488
    DeviceState *dev = DEVICE(obj);
489
    TZMPC *s = TZ_MPC(obj);
490

491
    qdev_init_gpio_out_named(dev, &s->irq, "irq", 1);
492
}
493

494
static void tz_mpc_realize(DeviceState *dev, Error **errp)
495
{
496
    Object *obj = OBJECT(dev);
497
    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
498
    TZMPC *s = TZ_MPC(dev);
499
    uint64_t size;
500

501
    /* We can't create the upstream end of the port until realize,
502
     * as we don't know the size of the MR used as the downstream until then.
503
     * We insist on having a downstream, to avoid complicating the code
504
     * with handling the "don't know how big this is" case. It's easy
505
     * enough for the user to create an unimplemented_device as downstream
506
     * if they have nothing else to plug into this.
507
     */
508
    if (!s->downstream) {
509
        error_setg(errp, "MPC 'downstream' link not set");
510
        return;
511
    }
512

513
    size = memory_region_size(s->downstream);
514

515
    memory_region_init_iommu(&s->upstream, sizeof(s->upstream),
516
                             TYPE_TZ_MPC_IOMMU_MEMORY_REGION,
517
                             obj, "tz-mpc-upstream", size);
518

519
    /* In real hardware the block size is configurable. In QEMU we could
520
     * make it configurable but will need it to be at least as big as the
521
     * target page size so we can execute out of the resulting MRs. Guest
522
     * software is supposed to check the block size using the BLK_CFG
523
     * register, so make it fixed at the page size.
524
     */
525
    s->blocksize = memory_region_iommu_get_min_page_size(&s->upstream);
526
    if (size % s->blocksize != 0) {
527
        error_setg(errp,
528
                   "MPC 'downstream' size %" PRId64
529
                   " is not a multiple of %" HWADDR_PRIx " bytes",
530
                   size, s->blocksize);
531
        object_unref(OBJECT(&s->upstream));
532
        return;
533
    }
534

535
    /* BLK_MAX is the max value of BLK_IDX, which indexes an array of 32-bit
536
     * words, each bit of which indicates one block.
537
     */
538
    s->blk_max = DIV_ROUND_UP(size / s->blocksize, 32);
539

540
    memory_region_init_io(&s->regmr, obj, &tz_mpc_reg_ops,
541
                          s, "tz-mpc-regs", 0x1000);
542
    sysbus_init_mmio(sbd, &s->regmr);
543

544
    sysbus_init_mmio(sbd, MEMORY_REGION(&s->upstream));
545

546
    /* This memory region is not exposed to users of this device as a
547
     * sysbus MMIO region, but is instead used internally as something
548
     * that our IOMMU translate function might direct accesses to.
549
     */
550
    memory_region_init_io(&s->blocked_io, obj, &tz_mpc_mem_blocked_ops,
551
                          s, "tz-mpc-blocked-io", size);
552

553
    address_space_init(&s->downstream_as, s->downstream,
554
                       "tz-mpc-downstream");
555
    address_space_init(&s->blocked_io_as, &s->blocked_io,
556
                       "tz-mpc-blocked-io");
557

558
    s->blk_lut = g_new0(uint32_t, s->blk_max);
559
}
560

561
static int tz_mpc_post_load(void *opaque, int version_id)
562
{
563
    TZMPC *s = TZ_MPC(opaque);
564

565
    /* Check the incoming data doesn't point blk_idx off the end of blk_lut. */
566
    if (s->blk_idx >= s->blk_max) {
567
        return -1;
568
    }
569
    return 0;
570
}
571

572
static const VMStateDescription tz_mpc_vmstate = {
573
    .name = "tz-mpc",
574
    .version_id = 1,
575
    .minimum_version_id = 1,
576
    .post_load = tz_mpc_post_load,
577
    .fields = (const VMStateField[]) {
578
        VMSTATE_UINT32(ctrl, TZMPC),
579
        VMSTATE_UINT32(blk_idx, TZMPC),
580
        VMSTATE_UINT32(int_stat, TZMPC),
581
        VMSTATE_UINT32(int_en, TZMPC),
582
        VMSTATE_UINT32(int_info1, TZMPC),
583
        VMSTATE_UINT32(int_info2, TZMPC),
584
        VMSTATE_VARRAY_UINT32(blk_lut, TZMPC, blk_max,
585
                              0, vmstate_info_uint32, uint32_t),
586
        VMSTATE_END_OF_LIST()
587
    }
588
};
589

590
static Property tz_mpc_properties[] = {
591
    DEFINE_PROP_LINK("downstream", TZMPC, downstream,
592
                     TYPE_MEMORY_REGION, MemoryRegion *),
593
    DEFINE_PROP_END_OF_LIST(),
594
};
595

596
static void tz_mpc_class_init(ObjectClass *klass, void *data)
597
{
598
    DeviceClass *dc = DEVICE_CLASS(klass);
599

600
    dc->realize = tz_mpc_realize;
601
    dc->vmsd = &tz_mpc_vmstate;
602
    dc->reset = tz_mpc_reset;
603
    device_class_set_props(dc, tz_mpc_properties);
604
}
605

606
static const TypeInfo tz_mpc_info = {
607
    .name = TYPE_TZ_MPC,
608
    .parent = TYPE_SYS_BUS_DEVICE,
609
    .instance_size = sizeof(TZMPC),
610
    .instance_init = tz_mpc_init,
611
    .class_init = tz_mpc_class_init,
612
};
613

614
static void tz_mpc_iommu_memory_region_class_init(ObjectClass *klass,
615
                                                  void *data)
616
{
617
    IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
618

619
    imrc->translate = tz_mpc_translate;
620
    imrc->attrs_to_index = tz_mpc_attrs_to_index;
621
    imrc->num_indexes = tz_mpc_num_indexes;
622
}
623

624
static const TypeInfo tz_mpc_iommu_memory_region_info = {
625
    .name = TYPE_TZ_MPC_IOMMU_MEMORY_REGION,
626
    .parent = TYPE_IOMMU_MEMORY_REGION,
627
    .class_init = tz_mpc_iommu_memory_region_class_init,
628
};
629

630
static void tz_mpc_register_types(void)
631
{
632
    type_register_static(&tz_mpc_info);
633
    type_register_static(&tz_mpc_iommu_memory_region_info);
634
}
635

636
type_init(tz_mpc_register_types);
637

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.