/* * ARM AHB5 TrustZone Memory Protection Controller emulation * * Copyright (c) 2018 Linaro Limited * Written by Peter Maydell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 or * (at your option) any later version. */ #include "qemu/osdep.h" #include "qemu/log.h" #include "qapi/error.h" #include "trace.h" #include "hw/sysbus.h" #include "hw/registerfields.h" #include "hw/misc/tz-mpc.h" /* Our IOMMU has two IOMMU indexes, one for secure transactions and one for * non-secure transactions. */ enum { IOMMU_IDX_S, IOMMU_IDX_NS, IOMMU_NUM_INDEXES, }; /* Config registers */ REG32(CTRL, 0x00) FIELD(CTRL, SEC_RESP, 4, 1) FIELD(CTRL, AUTOINC, 8, 1) FIELD(CTRL, LOCKDOWN, 31, 1) REG32(BLK_MAX, 0x10) REG32(BLK_CFG, 0x14) REG32(BLK_IDX, 0x18) REG32(BLK_LUT, 0x1c) REG32(INT_STAT, 0x20) FIELD(INT_STAT, IRQ, 0, 1) REG32(INT_CLEAR, 0x24) FIELD(INT_CLEAR, IRQ, 0, 1) REG32(INT_EN, 0x28) FIELD(INT_EN, IRQ, 0, 1) REG32(INT_INFO1, 0x2c) REG32(INT_INFO2, 0x30) FIELD(INT_INFO2, HMASTER, 0, 16) FIELD(INT_INFO2, HNONSEC, 16, 1) FIELD(INT_INFO2, CFG_NS, 17, 1) REG32(INT_SET, 0x34) FIELD(INT_SET, IRQ, 0, 1) REG32(PIDR4, 0xfd0) REG32(PIDR5, 0xfd4) REG32(PIDR6, 0xfd8) REG32(PIDR7, 0xfdc) REG32(PIDR0, 0xfe0) REG32(PIDR1, 0xfe4) REG32(PIDR2, 0xfe8) REG32(PIDR3, 0xfec) REG32(CIDR0, 0xff0) REG32(CIDR1, 0xff4) REG32(CIDR2, 0xff8) REG32(CIDR3, 0xffc) static const uint8_t tz_mpc_idregs[] = { 0x04, 0x00, 0x00, 0x00, 0x60, 0xb8, 0x1b, 0x00, 0x0d, 0xf0, 0x05, 0xb1, }; static void tz_mpc_irq_update(TZMPC *s) { qemu_set_irq(s->irq, s->int_stat && s->int_en); } static void tz_mpc_iommu_notify(TZMPC *s, uint32_t lutidx, uint32_t oldlut, uint32_t newlut) { /* Called when the LUT word at lutidx has changed from oldlut to newlut; * must call the IOMMU notifiers for the changed blocks. */ IOMMUTLBEntry entry = { .addr_mask = s->blocksize - 1, }; hwaddr addr = lutidx * s->blocksize * 32; int i; for (i = 0; i < 32; i++, addr += s->blocksize) { bool block_is_ns; if (!((oldlut ^ newlut) & (1 << i))) { continue; } /* This changes the mappings for both the S and the NS space, * so we need to do four notifies: an UNMAP then a MAP for each. */ block_is_ns = newlut & (1 << i); trace_tz_mpc_iommu_notify(addr); entry.iova = addr; entry.translated_addr = addr; entry.perm = IOMMU_NONE; memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry); memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry); entry.perm = IOMMU_RW; if (block_is_ns) { entry.target_as = &s->blocked_io_as; } else { entry.target_as = &s->downstream_as; } memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry); if (block_is_ns) { entry.target_as = &s->downstream_as; } else { entry.target_as = &s->blocked_io_as; } memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry); } } static void tz_mpc_autoinc_idx(TZMPC *s, unsigned access_size) { /* Auto-increment BLK_IDX if necessary */ if (access_size == 4 && (s->ctrl & R_CTRL_AUTOINC_MASK)) { s->blk_idx++; s->blk_idx %= s->blk_max; } } static MemTxResult tz_mpc_reg_read(void *opaque, hwaddr addr, uint64_t *pdata, unsigned size, MemTxAttrs attrs) { TZMPC *s = TZ_MPC(opaque); uint64_t r; uint32_t offset = addr & ~0x3; if (!attrs.secure && offset < A_PIDR4) { /* NS accesses can only see the ID registers */ qemu_log_mask(LOG_GUEST_ERROR, "TZ MPC register read: NS access to offset 0x%x\n", offset); r = 0; goto read_out; } switch (offset) { case A_CTRL: r = s->ctrl; break; case A_BLK_MAX: r = s->blk_max; break; case A_BLK_CFG: /* We are never in "init in progress state", so this just indicates * the block size. s->blocksize == (1 << BLK_CFG + 5), so * BLK_CFG == ctz32(s->blocksize) - 5 */ r = ctz32(s->blocksize) - 5; break; case A_BLK_IDX: r = s->blk_idx; break; case A_BLK_LUT: r = s->blk_lut[s->blk_idx]; tz_mpc_autoinc_idx(s, size); break; case A_INT_STAT: r = s->int_stat; break; case A_INT_EN: r = s->int_en; break; case A_INT_INFO1: r = s->int_info1; break; case A_INT_INFO2: r = s->int_info2; break; case A_PIDR4: case A_PIDR5: case A_PIDR6: case A_PIDR7: case A_PIDR0: case A_PIDR1: case A_PIDR2: case A_PIDR3: case A_CIDR0: case A_CIDR1: case A_CIDR2: case A_CIDR3: r = tz_mpc_idregs[(offset - A_PIDR4) / 4]; break; case A_INT_CLEAR: case A_INT_SET: qemu_log_mask(LOG_GUEST_ERROR, "TZ MPC register read: write-only offset 0x%x\n", offset); r = 0; break; default: qemu_log_mask(LOG_GUEST_ERROR, "TZ MPC register read: bad offset 0x%x\n", offset); r = 0; break; } if (size != 4) { /* None of our registers are read-sensitive (except BLK_LUT, * which can special case the "size not 4" case), so just * pull the right bytes out of the word read result. */ r = extract32(r, (addr & 3) * 8, size * 8); } read_out: trace_tz_mpc_reg_read(addr, r, size); *pdata = r; return MEMTX_OK; } static MemTxResult tz_mpc_reg_write(void *opaque, hwaddr addr, uint64_t value, unsigned size, MemTxAttrs attrs) { TZMPC *s = TZ_MPC(opaque); uint32_t offset = addr & ~0x3; trace_tz_mpc_reg_write(addr, value, size); if (!attrs.secure && offset < A_PIDR4) { /* NS accesses can only see the ID registers */ qemu_log_mask(LOG_GUEST_ERROR, "TZ MPC register write: NS access to offset 0x%x\n", offset); return MEMTX_OK; } if (size != 4) { /* Expand the byte or halfword write to a full word size. * In most cases we can do this with zeroes; the exceptions * are CTRL, BLK_IDX and BLK_LUT. */ uint32_t oldval; switch (offset) { case A_CTRL: oldval = s->ctrl; break; case A_BLK_IDX: oldval = s->blk_idx; break; case A_BLK_LUT: oldval = s->blk_lut[s->blk_idx]; break; default: oldval = 0; break; } value = deposit32(oldval, (addr & 3) * 8, size * 8, value); } if ((s->ctrl & R_CTRL_LOCKDOWN_MASK) && (offset == A_CTRL || offset == A_BLK_LUT || offset == A_INT_EN)) { /* Lockdown mode makes these three registers read-only, and * the only way out of it is to reset the device. */ qemu_log_mask(LOG_GUEST_ERROR, "TZ MPC register write to offset 0x%x " "while MPC is in lockdown mode\n", offset); return MEMTX_OK; } switch (offset) { case A_CTRL: /* We don't implement the 'data gating' feature so all other bits * are reserved and we make them RAZ/WI. */ s->ctrl = value & (R_CTRL_SEC_RESP_MASK | R_CTRL_AUTOINC_MASK | R_CTRL_LOCKDOWN_MASK); break; case A_BLK_IDX: s->blk_idx = value % s->blk_max; break; case A_BLK_LUT: tz_mpc_iommu_notify(s, s->blk_idx, s->blk_lut[s->blk_idx], value); s->blk_lut[s->blk_idx] = value; tz_mpc_autoinc_idx(s, size); break; case A_INT_CLEAR: if (value & R_INT_CLEAR_IRQ_MASK) { s->int_stat = 0; tz_mpc_irq_update(s); } break; case A_INT_EN: s->int_en = value & R_INT_EN_IRQ_MASK; tz_mpc_irq_update(s); break; case A_INT_SET: if (value & R_INT_SET_IRQ_MASK) { s->int_stat = R_INT_STAT_IRQ_MASK; tz_mpc_irq_update(s); } break; case A_PIDR4: case A_PIDR5: case A_PIDR6: case A_PIDR7: case A_PIDR0: case A_PIDR1: case A_PIDR2: case A_PIDR3: case A_CIDR0: case A_CIDR1: case A_CIDR2: case A_CIDR3: qemu_log_mask(LOG_GUEST_ERROR, "TZ MPC register write: read-only offset 0x%x\n", offset); break; default: qemu_log_mask(LOG_GUEST_ERROR, "TZ MPC register write: bad offset 0x%x\n", offset); break; } return MEMTX_OK; } static const MemoryRegionOps tz_mpc_reg_ops = { .read_with_attrs = tz_mpc_reg_read, .write_with_attrs = tz_mpc_reg_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid.min_access_size = 1, .valid.max_access_size = 4, .impl.min_access_size = 1, .impl.max_access_size = 4, }; static inline bool tz_mpc_cfg_ns(TZMPC *s, hwaddr addr) { /* Return the cfg_ns bit from the LUT for the specified address */ hwaddr blknum = addr / s->blocksize; hwaddr blkword = blknum / 32; uint32_t blkbit = 1U << (blknum % 32); /* This would imply the address was larger than the size we * defined this memory region to be, so it can't happen. */ assert(blkword < s->blk_max); return s->blk_lut[blkword] & blkbit; } static MemTxResult tz_mpc_handle_block(TZMPC *s, hwaddr addr, MemTxAttrs attrs) { /* Handle a blocked transaction: raise IRQ, capture info, etc */ if (!s->int_stat) { /* First blocked transfer: capture information into INT_INFO1 and * INT_INFO2. Subsequent transfers are still blocked but don't * capture information until the guest clears the interrupt. */ s->int_info1 = addr; s->int_info2 = 0; s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, HMASTER, attrs.requester_id & 0xffff); s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, HNONSEC, ~attrs.secure); s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, CFG_NS, tz_mpc_cfg_ns(s, addr)); s->int_stat |= R_INT_STAT_IRQ_MASK; tz_mpc_irq_update(s); } /* Generate bus error if desired; otherwise RAZ/WI */ return (s->ctrl & R_CTRL_SEC_RESP_MASK) ? MEMTX_ERROR : MEMTX_OK; } /* Accesses only reach these read and write functions if the MPC is * blocking them; non-blocked accesses go directly to the downstream * memory region without passing through this code. */ static MemTxResult tz_mpc_mem_blocked_read(void *opaque, hwaddr addr, uint64_t *pdata, unsigned size, MemTxAttrs attrs) { TZMPC *s = TZ_MPC(opaque); trace_tz_mpc_mem_blocked_read(addr, size, attrs.secure); *pdata = 0; return tz_mpc_handle_block(s, addr, attrs); } static MemTxResult tz_mpc_mem_blocked_write(void *opaque, hwaddr addr, uint64_t value, unsigned size, MemTxAttrs attrs) { TZMPC *s = TZ_MPC(opaque); trace_tz_mpc_mem_blocked_write(addr, value, size, attrs.secure); return tz_mpc_handle_block(s, addr, attrs); } static const MemoryRegionOps tz_mpc_mem_blocked_ops = { .read_with_attrs = tz_mpc_mem_blocked_read, .write_with_attrs = tz_mpc_mem_blocked_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid.min_access_size = 1, .valid.max_access_size = 8, .impl.min_access_size = 1, .impl.max_access_size = 8, }; static IOMMUTLBEntry tz_mpc_translate(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUAccessFlags flags, int iommu_idx) { TZMPC *s = TZ_MPC(container_of(iommu, TZMPC, upstream)); bool ok; IOMMUTLBEntry ret = { .iova = addr & ~(s->blocksize - 1), .translated_addr = addr & ~(s->blocksize - 1), .addr_mask = s->blocksize - 1, .perm = IOMMU_RW, }; /* Look at the per-block configuration for this address, and * return a TLB entry directing the transaction at either * downstream_as or blocked_io_as, as appropriate. * If the LUT cfg_ns bit is 1, only non-secure transactions * may pass. If the bit is 0, only secure transactions may pass. */ ok = tz_mpc_cfg_ns(s, addr) == (iommu_idx == IOMMU_IDX_NS); trace_tz_mpc_translate(addr, flags, iommu_idx == IOMMU_IDX_S ? "S" : "NS", ok ? "pass" : "block"); ret.target_as = ok ? &s->downstream_as : &s->blocked_io_as; return ret; } static int tz_mpc_attrs_to_index(IOMMUMemoryRegion *iommu, MemTxAttrs attrs) { /* We treat unspecified attributes like secure. Transactions with * unspecified attributes come from places like * cpu_physical_memory_write_rom() for initial image load, and we want * those to pass through the from-reset "everything is secure" config. * All the real during-emulation transactions from the CPU will * specify attributes. */ return (attrs.unspecified || attrs.secure) ? IOMMU_IDX_S : IOMMU_IDX_NS; } static int tz_mpc_num_indexes(IOMMUMemoryRegion *iommu) { return IOMMU_NUM_INDEXES; } static void tz_mpc_reset(DeviceState *dev) { TZMPC *s = TZ_MPC(dev); s->ctrl = 0x00000100; s->blk_idx = 0; s->int_stat = 0; s->int_en = 1; s->int_info1 = 0; s->int_info2 = 0; memset(s->blk_lut, 0, s->blk_max * sizeof(uint32_t)); } static void tz_mpc_init(Object *obj) { DeviceState *dev = DEVICE(obj); TZMPC *s = TZ_MPC(obj); qdev_init_gpio_out_named(dev, &s->irq, "irq", 1); } static void tz_mpc_realize(DeviceState *dev, Error **errp) { Object *obj = OBJECT(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); TZMPC *s = TZ_MPC(dev); uint64_t size; /* We can't create the upstream end of the port until realize, * as we don't know the size of the MR used as the downstream until then. * We insist on having a downstream, to avoid complicating the code * with handling the "don't know how big this is" case. It's easy * enough for the user to create an unimplemented_device as downstream * if they have nothing else to plug into this. */ if (!s->downstream) { error_setg(errp, "MPC 'downstream' link not set"); return; } size = memory_region_size(s->downstream); memory_region_init_iommu(&s->upstream, sizeof(s->upstream), TYPE_TZ_MPC_IOMMU_MEMORY_REGION, obj, "tz-mpc-upstream", size); /* In real hardware the block size is configurable. In QEMU we could * make it configurable but will need it to be at least as big as the * target page size so we can execute out of the resulting MRs. Guest * software is supposed to check the block size using the BLK_CFG * register, so make it fixed at the page size. */ s->blocksize = memory_region_iommu_get_min_page_size(&s->upstream); if (size % s->blocksize != 0) { error_setg(errp, "MPC 'downstream' size %" PRId64 " is not a multiple of %" HWADDR_PRIx " bytes", size, s->blocksize); object_unref(OBJECT(&s->upstream)); return; } /* BLK_MAX is the max value of BLK_IDX, which indexes an array of 32-bit * words, each bit of which indicates one block. */ s->blk_max = DIV_ROUND_UP(size / s->blocksize, 32); memory_region_init_io(&s->regmr, obj, &tz_mpc_reg_ops, s, "tz-mpc-regs", 0x1000); sysbus_init_mmio(sbd, &s->regmr); sysbus_init_mmio(sbd, MEMORY_REGION(&s->upstream)); /* This memory region is not exposed to users of this device as a * sysbus MMIO region, but is instead used internally as something * that our IOMMU translate function might direct accesses to. */ memory_region_init_io(&s->blocked_io, obj, &tz_mpc_mem_blocked_ops, s, "tz-mpc-blocked-io", size); address_space_init(&s->downstream_as, s->downstream, "tz-mpc-downstream"); address_space_init(&s->blocked_io_as, &s->blocked_io, "tz-mpc-blocked-io"); s->blk_lut = g_new(uint32_t, s->blk_max); } static int tz_mpc_post_load(void *opaque, int version_id) { TZMPC *s = TZ_MPC(opaque); /* Check the incoming data doesn't point blk_idx off the end of blk_lut. */ if (s->blk_idx >= s->blk_max) { return -1; } return 0; } static const VMStateDescription tz_mpc_vmstate = { .name = "tz-mpc", .version_id = 1, .minimum_version_id = 1, .post_load = tz_mpc_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(ctrl, TZMPC), VMSTATE_UINT32(blk_idx, TZMPC), VMSTATE_UINT32(int_stat, TZMPC), VMSTATE_UINT32(int_en, TZMPC), VMSTATE_UINT32(int_info1, TZMPC), VMSTATE_UINT32(int_info2, TZMPC), VMSTATE_VARRAY_UINT32(blk_lut, TZMPC, blk_max, 0, vmstate_info_uint32, uint32_t), VMSTATE_END_OF_LIST() } }; static Property tz_mpc_properties[] = { DEFINE_PROP_LINK("downstream", TZMPC, downstream, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), }; static void tz_mpc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = tz_mpc_realize; dc->vmsd = &tz_mpc_vmstate; dc->reset = tz_mpc_reset; dc->props = tz_mpc_properties; } static const TypeInfo tz_mpc_info = { .name = TYPE_TZ_MPC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(TZMPC), .instance_init = tz_mpc_init, .class_init = tz_mpc_class_init, }; static void tz_mpc_iommu_memory_region_class_init(ObjectClass *klass, void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); imrc->translate = tz_mpc_translate; imrc->attrs_to_index = tz_mpc_attrs_to_index; imrc->num_indexes = tz_mpc_num_indexes; } static const TypeInfo tz_mpc_iommu_memory_region_info = { .name = TYPE_TZ_MPC_IOMMU_MEMORY_REGION, .parent = TYPE_IOMMU_MEMORY_REGION, .class_init = tz_mpc_iommu_memory_region_class_init, }; static void tz_mpc_register_types(void) { type_register_static(&tz_mpc_info); type_register_static(&tz_mpc_iommu_memory_region_info); } type_init(tz_mpc_register_types);