/* * Arm IoT Kit security controller * * 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/iotkit-secctl.h" /* Registers in the secure privilege control block */ REG32(SECRESPCFG, 0x10) REG32(NSCCFG, 0x14) REG32(SECMPCINTSTATUS, 0x1c) REG32(SECPPCINTSTAT, 0x20) REG32(SECPPCINTCLR, 0x24) REG32(SECPPCINTEN, 0x28) REG32(SECMSCINTSTAT, 0x30) REG32(SECMSCINTCLR, 0x34) REG32(SECMSCINTEN, 0x38) REG32(BRGINTSTAT, 0x40) REG32(BRGINTCLR, 0x44) REG32(BRGINTEN, 0x48) REG32(AHBNSPPC0, 0x50) REG32(AHBNSPPCEXP0, 0x60) REG32(AHBNSPPCEXP1, 0x64) REG32(AHBNSPPCEXP2, 0x68) REG32(AHBNSPPCEXP3, 0x6c) REG32(APBNSPPC0, 0x70) REG32(APBNSPPC1, 0x74) REG32(APBNSPPCEXP0, 0x80) REG32(APBNSPPCEXP1, 0x84) REG32(APBNSPPCEXP2, 0x88) REG32(APBNSPPCEXP3, 0x8c) REG32(AHBSPPPC0, 0x90) REG32(AHBSPPPCEXP0, 0xa0) REG32(AHBSPPPCEXP1, 0xa4) REG32(AHBSPPPCEXP2, 0xa8) REG32(AHBSPPPCEXP3, 0xac) REG32(APBSPPPC0, 0xb0) REG32(APBSPPPC1, 0xb4) REG32(APBSPPPCEXP0, 0xc0) REG32(APBSPPPCEXP1, 0xc4) REG32(APBSPPPCEXP2, 0xc8) REG32(APBSPPPCEXP3, 0xcc) REG32(NSMSCEXP, 0xd0) REG32(PID4, 0xfd0) REG32(PID5, 0xfd4) REG32(PID6, 0xfd8) REG32(PID7, 0xfdc) REG32(PID0, 0xfe0) REG32(PID1, 0xfe4) REG32(PID2, 0xfe8) REG32(PID3, 0xfec) REG32(CID0, 0xff0) REG32(CID1, 0xff4) REG32(CID2, 0xff8) REG32(CID3, 0xffc) /* Registers in the non-secure privilege control block */ REG32(AHBNSPPPC0, 0x90) REG32(AHBNSPPPCEXP0, 0xa0) REG32(AHBNSPPPCEXP1, 0xa4) REG32(AHBNSPPPCEXP2, 0xa8) REG32(AHBNSPPPCEXP3, 0xac) REG32(APBNSPPPC0, 0xb0) REG32(APBNSPPPC1, 0xb4) REG32(APBNSPPPCEXP0, 0xc0) REG32(APBNSPPPCEXP1, 0xc4) REG32(APBNSPPPCEXP2, 0xc8) REG32(APBNSPPPCEXP3, 0xcc) /* PID and CID registers are also present in the NS block */ static const uint8_t iotkit_secctl_s_idregs[] = { 0x04, 0x00, 0x00, 0x00, 0x52, 0xb8, 0x0b, 0x00, 0x0d, 0xf0, 0x05, 0xb1, }; static const uint8_t iotkit_secctl_ns_idregs[] = { 0x04, 0x00, 0x00, 0x00, 0x53, 0xb8, 0x0b, 0x00, 0x0d, 0xf0, 0x05, 0xb1, }; /* The register sets for the various PPCs (AHB internal, APB internal, * AHB expansion, APB expansion) are all set up so that they are * in 16-aligned blocks so offsets 0xN0, 0xN4, 0xN8, 0xNC are PPCs * 0, 1, 2, 3 of that type, so we can convert a register address offset * into an an index into a PPC array easily. */ static inline int offset_to_ppc_idx(uint32_t offset) { return extract32(offset, 2, 2); } typedef void PerPPCFunction(IoTKitSecCtlPPC *ppc); static void foreach_ppc(IoTKitSecCtl *s, PerPPCFunction *fn) { int i; for (i = 0; i < IOTS_NUM_APB_PPC; i++) { fn(&s->apb[i]); } for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) { fn(&s->apbexp[i]); } for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) { fn(&s->ahbexp[i]); } } static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr, uint64_t *pdata, unsigned size, MemTxAttrs attrs) { uint64_t r; uint32_t offset = addr & ~0x3; IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); switch (offset) { case A_AHBNSPPC0: case A_AHBSPPPC0: r = 0; break; case A_SECRESPCFG: r = s->secrespcfg; break; case A_NSCCFG: r = s->nsccfg; break; case A_SECPPCINTSTAT: r = s->secppcintstat; break; case A_SECPPCINTEN: r = s->secppcinten; break; case A_BRGINTSTAT: /* QEMU's bus fabric can never report errors as it doesn't buffer * writes, so we never report bridge interrupts. */ r = 0; break; case A_BRGINTEN: r = s->brginten; break; case A_AHBNSPPCEXP0: case A_AHBNSPPCEXP1: case A_AHBNSPPCEXP2: case A_AHBNSPPCEXP3: r = s->ahbexp[offset_to_ppc_idx(offset)].ns; break; case A_APBNSPPC0: case A_APBNSPPC1: r = s->apb[offset_to_ppc_idx(offset)].ns; break; case A_APBNSPPCEXP0: case A_APBNSPPCEXP1: case A_APBNSPPCEXP2: case A_APBNSPPCEXP3: r = s->apbexp[offset_to_ppc_idx(offset)].ns; break; case A_AHBSPPPCEXP0: case A_AHBSPPPCEXP1: case A_AHBSPPPCEXP2: case A_AHBSPPPCEXP3: r = s->apbexp[offset_to_ppc_idx(offset)].sp; break; case A_APBSPPPC0: case A_APBSPPPC1: r = s->apb[offset_to_ppc_idx(offset)].sp; break; case A_APBSPPPCEXP0: case A_APBSPPPCEXP1: case A_APBSPPPCEXP2: case A_APBSPPPCEXP3: r = s->apbexp[offset_to_ppc_idx(offset)].sp; break; case A_SECMPCINTSTATUS: case A_SECMSCINTSTAT: case A_SECMSCINTEN: case A_NSMSCEXP: qemu_log_mask(LOG_UNIMP, "IoTKit SecCtl S block read: " "unimplemented offset 0x%x\n", offset); r = 0; break; case A_PID4: case A_PID5: case A_PID6: case A_PID7: case A_PID0: case A_PID1: case A_PID2: case A_PID3: case A_CID0: case A_CID1: case A_CID2: case A_CID3: r = iotkit_secctl_s_idregs[(offset - A_PID4) / 4]; break; case A_SECPPCINTCLR: case A_SECMSCINTCLR: case A_BRGINTCLR: qemu_log_mask(LOG_GUEST_ERROR, "IotKit SecCtl S block read: write-only offset 0x%x\n", offset); r = 0; break; default: qemu_log_mask(LOG_GUEST_ERROR, "IotKit SecCtl S block read: bad offset 0x%x\n", offset); r = 0; break; } if (size != 4) { /* None of our registers are access-sensitive, so just pull the right * byte out of the word read result. */ r = extract32(r, (addr & 3) * 8, size * 8); } trace_iotkit_secctl_s_read(offset, r, size); *pdata = r; return MEMTX_OK; } static void iotkit_secctl_update_ppc_ap(IoTKitSecCtlPPC *ppc) { int i; for (i = 0; i < ppc->numports; i++) { bool v; if (extract32(ppc->ns, i, 1)) { v = extract32(ppc->nsp, i, 1); } else { v = extract32(ppc->sp, i, 1); } qemu_set_irq(ppc->ap[i], v); } } static void iotkit_secctl_ppc_ns_write(IoTKitSecCtlPPC *ppc, uint32_t value) { int i; ppc->ns = value & MAKE_64BIT_MASK(0, ppc->numports); for (i = 0; i < ppc->numports; i++) { qemu_set_irq(ppc->nonsec[i], extract32(ppc->ns, i, 1)); } iotkit_secctl_update_ppc_ap(ppc); } static void iotkit_secctl_ppc_sp_write(IoTKitSecCtlPPC *ppc, uint32_t value) { ppc->sp = value & MAKE_64BIT_MASK(0, ppc->numports); iotkit_secctl_update_ppc_ap(ppc); } static void iotkit_secctl_ppc_nsp_write(IoTKitSecCtlPPC *ppc, uint32_t value) { ppc->nsp = value & MAKE_64BIT_MASK(0, ppc->numports); iotkit_secctl_update_ppc_ap(ppc); } static void iotkit_secctl_ppc_update_irq_clear(IoTKitSecCtlPPC *ppc) { uint32_t value = ppc->parent->secppcintstat; qemu_set_irq(ppc->irq_clear, extract32(value, ppc->irq_bit_offset, 1)); } static void iotkit_secctl_ppc_update_irq_enable(IoTKitSecCtlPPC *ppc) { uint32_t value = ppc->parent->secppcinten; qemu_set_irq(ppc->irq_enable, extract32(value, ppc->irq_bit_offset, 1)); } static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr, uint64_t value, unsigned size, MemTxAttrs attrs) { IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); uint32_t offset = addr; IoTKitSecCtlPPC *ppc; trace_iotkit_secctl_s_write(offset, value, size); if (size != 4) { /* Byte and halfword writes are ignored */ qemu_log_mask(LOG_GUEST_ERROR, "IotKit SecCtl S block write: bad size, ignored\n"); return MEMTX_OK; } switch (offset) { case A_NSCCFG: s->nsccfg = value & 3; qemu_set_irq(s->nsc_cfg_irq, s->nsccfg); break; case A_SECRESPCFG: value &= 1; s->secrespcfg = value; qemu_set_irq(s->sec_resp_cfg, s->secrespcfg); break; case A_SECPPCINTCLR: value &= 0x00f000f3; foreach_ppc(s, iotkit_secctl_ppc_update_irq_clear); break; case A_SECPPCINTEN: s->secppcinten = value & 0x00f000f3; foreach_ppc(s, iotkit_secctl_ppc_update_irq_enable); break; case A_BRGINTCLR: break; case A_BRGINTEN: s->brginten = value & 0xffff0000; break; case A_AHBNSPPCEXP0: case A_AHBNSPPCEXP1: case A_AHBNSPPCEXP2: case A_AHBNSPPCEXP3: ppc = &s->ahbexp[offset_to_ppc_idx(offset)]; iotkit_secctl_ppc_ns_write(ppc, value); break; case A_APBNSPPC0: case A_APBNSPPC1: ppc = &s->apb[offset_to_ppc_idx(offset)]; iotkit_secctl_ppc_ns_write(ppc, value); break; case A_APBNSPPCEXP0: case A_APBNSPPCEXP1: case A_APBNSPPCEXP2: case A_APBNSPPCEXP3: ppc = &s->apbexp[offset_to_ppc_idx(offset)]; iotkit_secctl_ppc_ns_write(ppc, value); break; case A_AHBSPPPCEXP0: case A_AHBSPPPCEXP1: case A_AHBSPPPCEXP2: case A_AHBSPPPCEXP3: ppc = &s->ahbexp[offset_to_ppc_idx(offset)]; iotkit_secctl_ppc_sp_write(ppc, value); break; case A_APBSPPPC0: case A_APBSPPPC1: ppc = &s->apb[offset_to_ppc_idx(offset)]; iotkit_secctl_ppc_sp_write(ppc, value); break; case A_APBSPPPCEXP0: case A_APBSPPPCEXP1: case A_APBSPPPCEXP2: case A_APBSPPPCEXP3: ppc = &s->apbexp[offset_to_ppc_idx(offset)]; iotkit_secctl_ppc_sp_write(ppc, value); break; case A_SECMSCINTCLR: case A_SECMSCINTEN: qemu_log_mask(LOG_UNIMP, "IoTKit SecCtl S block write: " "unimplemented offset 0x%x\n", offset); break; case A_SECMPCINTSTATUS: case A_SECPPCINTSTAT: case A_SECMSCINTSTAT: case A_BRGINTSTAT: case A_AHBNSPPC0: case A_AHBSPPPC0: case A_NSMSCEXP: case A_PID4: case A_PID5: case A_PID6: case A_PID7: case A_PID0: case A_PID1: case A_PID2: case A_PID3: case A_CID0: case A_CID1: case A_CID2: case A_CID3: qemu_log_mask(LOG_GUEST_ERROR, "IoTKit SecCtl S block write: " "read-only offset 0x%x\n", offset); break; default: qemu_log_mask(LOG_GUEST_ERROR, "IotKit SecCtl S block write: bad offset 0x%x\n", offset); break; } return MEMTX_OK; } static MemTxResult iotkit_secctl_ns_read(void *opaque, hwaddr addr, uint64_t *pdata, unsigned size, MemTxAttrs attrs) { IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); uint64_t r; uint32_t offset = addr & ~0x3; switch (offset) { case A_AHBNSPPPC0: r = 0; break; case A_AHBNSPPPCEXP0: case A_AHBNSPPPCEXP1: case A_AHBNSPPPCEXP2: case A_AHBNSPPPCEXP3: r = s->ahbexp[offset_to_ppc_idx(offset)].nsp; break; case A_APBNSPPPC0: case A_APBNSPPPC1: r = s->apb[offset_to_ppc_idx(offset)].nsp; break; case A_APBNSPPPCEXP0: case A_APBNSPPPCEXP1: case A_APBNSPPPCEXP2: case A_APBNSPPPCEXP3: r = s->apbexp[offset_to_ppc_idx(offset)].nsp; break; case A_PID4: case A_PID5: case A_PID6: case A_PID7: case A_PID0: case A_PID1: case A_PID2: case A_PID3: case A_CID0: case A_CID1: case A_CID2: case A_CID3: r = iotkit_secctl_ns_idregs[(offset - A_PID4) / 4]; break; default: qemu_log_mask(LOG_GUEST_ERROR, "IotKit SecCtl NS block write: bad offset 0x%x\n", offset); r = 0; break; } if (size != 4) { /* None of our registers are access-sensitive, so just pull the right * byte out of the word read result. */ r = extract32(r, (addr & 3) * 8, size * 8); } trace_iotkit_secctl_ns_read(offset, r, size); *pdata = r; return MEMTX_OK; } static MemTxResult iotkit_secctl_ns_write(void *opaque, hwaddr addr, uint64_t value, unsigned size, MemTxAttrs attrs) { IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); uint32_t offset = addr; IoTKitSecCtlPPC *ppc; trace_iotkit_secctl_ns_write(offset, value, size); if (size != 4) { /* Byte and halfword writes are ignored */ qemu_log_mask(LOG_GUEST_ERROR, "IotKit SecCtl NS block write: bad size, ignored\n"); return MEMTX_OK; } switch (offset) { case A_AHBNSPPPCEXP0: case A_AHBNSPPPCEXP1: case A_AHBNSPPPCEXP2: case A_AHBNSPPPCEXP3: ppc = &s->ahbexp[offset_to_ppc_idx(offset)]; iotkit_secctl_ppc_nsp_write(ppc, value); break; case A_APBNSPPPC0: case A_APBNSPPPC1: ppc = &s->apb[offset_to_ppc_idx(offset)]; iotkit_secctl_ppc_nsp_write(ppc, value); break; case A_APBNSPPPCEXP0: case A_APBNSPPPCEXP1: case A_APBNSPPPCEXP2: case A_APBNSPPPCEXP3: ppc = &s->apbexp[offset_to_ppc_idx(offset)]; iotkit_secctl_ppc_nsp_write(ppc, value); break; case A_AHBNSPPPC0: case A_PID4: case A_PID5: case A_PID6: case A_PID7: case A_PID0: case A_PID1: case A_PID2: case A_PID3: case A_CID0: case A_CID1: case A_CID2: case A_CID3: qemu_log_mask(LOG_GUEST_ERROR, "IoTKit SecCtl NS block write: " "read-only offset 0x%x\n", offset); break; default: qemu_log_mask(LOG_GUEST_ERROR, "IotKit SecCtl NS block write: bad offset 0x%x\n", offset); break; } return MEMTX_OK; } static const MemoryRegionOps iotkit_secctl_s_ops = { .read_with_attrs = iotkit_secctl_s_read, .write_with_attrs = iotkit_secctl_s_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 const MemoryRegionOps iotkit_secctl_ns_ops = { .read_with_attrs = iotkit_secctl_ns_read, .write_with_attrs = iotkit_secctl_ns_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 void iotkit_secctl_reset_ppc(IoTKitSecCtlPPC *ppc) { ppc->ns = 0; ppc->sp = 0; ppc->nsp = 0; } static void iotkit_secctl_reset(DeviceState *dev) { IoTKitSecCtl *s = IOTKIT_SECCTL(dev); s->secppcintstat = 0; s->secppcinten = 0; s->secrespcfg = 0; s->nsccfg = 0; s->brginten = 0; foreach_ppc(s, iotkit_secctl_reset_ppc); } static void iotkit_secctl_ppc_irqstatus(void *opaque, int n, int level) { IoTKitSecCtlPPC *ppc = opaque; IoTKitSecCtl *s = IOTKIT_SECCTL(ppc->parent); int irqbit = ppc->irq_bit_offset + n; s->secppcintstat = deposit32(s->secppcintstat, irqbit, 1, level); } static void iotkit_secctl_init_ppc(IoTKitSecCtl *s, IoTKitSecCtlPPC *ppc, const char *name, int numports, int irq_bit_offset) { char *gpioname; DeviceState *dev = DEVICE(s); ppc->numports = numports; ppc->irq_bit_offset = irq_bit_offset; ppc->parent = s; gpioname = g_strdup_printf("%s_nonsec", name); qdev_init_gpio_out_named(dev, ppc->nonsec, gpioname, numports); g_free(gpioname); gpioname = g_strdup_printf("%s_ap", name); qdev_init_gpio_out_named(dev, ppc->ap, gpioname, numports); g_free(gpioname); gpioname = g_strdup_printf("%s_irq_enable", name); qdev_init_gpio_out_named(dev, &ppc->irq_enable, gpioname, 1); g_free(gpioname); gpioname = g_strdup_printf("%s_irq_clear", name); qdev_init_gpio_out_named(dev, &ppc->irq_clear, gpioname, 1); g_free(gpioname); gpioname = g_strdup_printf("%s_irq_status", name); qdev_init_gpio_in_named_with_opaque(dev, iotkit_secctl_ppc_irqstatus, ppc, gpioname, 1); g_free(gpioname); } static void iotkit_secctl_init(Object *obj) { IoTKitSecCtl *s = IOTKIT_SECCTL(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); DeviceState *dev = DEVICE(obj); int i; iotkit_secctl_init_ppc(s, &s->apb[0], "apb_ppc0", IOTS_APB_PPC0_NUM_PORTS, 0); iotkit_secctl_init_ppc(s, &s->apb[1], "apb_ppc1", IOTS_APB_PPC1_NUM_PORTS, 1); for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) { IoTKitSecCtlPPC *ppc = &s->apbexp[i]; char *ppcname = g_strdup_printf("apb_ppcexp%d", i); iotkit_secctl_init_ppc(s, ppc, ppcname, IOTS_PPC_NUM_PORTS, 4 + i); g_free(ppcname); } for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) { IoTKitSecCtlPPC *ppc = &s->ahbexp[i]; char *ppcname = g_strdup_printf("ahb_ppcexp%d", i); iotkit_secctl_init_ppc(s, ppc, ppcname, IOTS_PPC_NUM_PORTS, 20 + i); g_free(ppcname); } qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); qdev_init_gpio_out_named(dev, &s->nsc_cfg_irq, "nsc_cfg", 1); memory_region_init_io(&s->s_regs, obj, &iotkit_secctl_s_ops, s, "iotkit-secctl-s-regs", 0x1000); memory_region_init_io(&s->ns_regs, obj, &iotkit_secctl_ns_ops, s, "iotkit-secctl-ns-regs", 0x1000); sysbus_init_mmio(sbd, &s->s_regs); sysbus_init_mmio(sbd, &s->ns_regs); } static const VMStateDescription iotkit_secctl_ppc_vmstate = { .name = "iotkit-secctl-ppc", .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_UINT32(ns, IoTKitSecCtlPPC), VMSTATE_UINT32(sp, IoTKitSecCtlPPC), VMSTATE_UINT32(nsp, IoTKitSecCtlPPC), VMSTATE_END_OF_LIST() } }; static const VMStateDescription iotkit_secctl_vmstate = { .name = "iotkit-secctl", .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_UINT32(secppcintstat, IoTKitSecCtl), VMSTATE_UINT32(secppcinten, IoTKitSecCtl), VMSTATE_UINT32(secrespcfg, IoTKitSecCtl), VMSTATE_UINT32(nsccfg, IoTKitSecCtl), VMSTATE_UINT32(brginten, IoTKitSecCtl), VMSTATE_STRUCT_ARRAY(apb, IoTKitSecCtl, IOTS_NUM_APB_PPC, 1, iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC), VMSTATE_STRUCT_ARRAY(apbexp, IoTKitSecCtl, IOTS_NUM_APB_EXP_PPC, 1, iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC), VMSTATE_STRUCT_ARRAY(ahbexp, IoTKitSecCtl, IOTS_NUM_AHB_EXP_PPC, 1, iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC), VMSTATE_END_OF_LIST() } }; static void iotkit_secctl_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &iotkit_secctl_vmstate; dc->reset = iotkit_secctl_reset; } static const TypeInfo iotkit_secctl_info = { .name = TYPE_IOTKIT_SECCTL, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IoTKitSecCtl), .instance_init = iotkit_secctl_init, .class_init = iotkit_secctl_class_init, }; static void iotkit_secctl_register_types(void) { type_register_static(&iotkit_secctl_info); } type_init(iotkit_secctl_register_types);