diff options
Diffstat (limited to 'hw/misc')
-rw-r--r-- | hw/misc/Makefile.objs | 4 | ||||
-rw-r--r-- | hw/misc/iotkit-secctl.c | 704 | ||||
-rw-r--r-- | hw/misc/mps2-fpgaio.c | 176 | ||||
-rw-r--r-- | hw/misc/trace-events | 24 | ||||
-rw-r--r-- | hw/misc/tz-ppc.c | 302 | ||||
-rw-r--r-- | hw/misc/unimp.c | 10 |
6 files changed, 1210 insertions, 10 deletions
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index f33b37a8e5..00e834d0f0 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -58,8 +58,12 @@ obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o obj-$(CONFIG_MIPS_CPS) += mips_cmgcr.o obj-$(CONFIG_MIPS_CPS) += mips_cpc.o obj-$(CONFIG_MIPS_ITU) += mips_itu.o +obj-$(CONFIG_MPS2_FPGAIO) += mps2-fpgaio.o obj-$(CONFIG_MPS2_SCC) += mps2-scc.o +obj-$(CONFIG_TZ_PPC) += tz-ppc.o +obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o + obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o obj-$(CONFIG_AUX) += auxbus.o diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c new file mode 100644 index 0000000000..ddd1584d34 --- /dev/null +++ b/hw/misc/iotkit-secctl.c @@ -0,0 +1,704 @@ +/* + * 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); diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c new file mode 100644 index 0000000000..7394a057d8 --- /dev/null +++ b/hw/misc/mps2-fpgaio.c @@ -0,0 +1,176 @@ +/* + * ARM MPS2 AN505 FPGAIO 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. + */ + +/* This is a model of the "FPGA system control and I/O" block found + * in the AN505 FPGA image for the MPS2 devboard. + * It is documented in AN505: + * http://infocenter.arm.com/help/topic/com.arm.doc.dai0505b/index.html + */ + +#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/mps2-fpgaio.h" + +REG32(LED0, 0) +REG32(BUTTON, 8) +REG32(CLK1HZ, 0x10) +REG32(CLK100HZ, 0x14) +REG32(COUNTER, 0x18) +REG32(PRESCALE, 0x1c) +REG32(PSCNTR, 0x20) +REG32(MISC, 0x4c) + +static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size) +{ + MPS2FPGAIO *s = MPS2_FPGAIO(opaque); + uint64_t r; + + switch (offset) { + case A_LED0: + r = s->led0; + break; + case A_BUTTON: + /* User-pressable board buttons. We don't model that, so just return + * zeroes. + */ + r = 0; + break; + case A_PRESCALE: + r = s->prescale; + break; + case A_MISC: + r = s->misc; + break; + case A_CLK1HZ: + case A_CLK100HZ: + case A_COUNTER: + case A_PSCNTR: + /* These are all upcounters of various frequencies. */ + qemu_log_mask(LOG_UNIMP, "MPS2 FPGAIO: counters unimplemented\n"); + r = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "MPS2 FPGAIO read: bad offset %x\n", (int) offset); + r = 0; + break; + } + + trace_mps2_fpgaio_read(offset, r, size); + return r; +} + +static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + MPS2FPGAIO *s = MPS2_FPGAIO(opaque); + + trace_mps2_fpgaio_write(offset, value, size); + + switch (offset) { + case A_LED0: + /* LED bits [1:0] control board LEDs. We don't currently have + * a mechanism for displaying this graphically, so use a trace event. + */ + trace_mps2_fpgaio_leds(value & 0x02 ? '*' : '.', + value & 0x01 ? '*' : '.'); + s->led0 = value & 0x3; + break; + case A_PRESCALE: + s->prescale = value; + break; + case A_MISC: + /* These are control bits for some of the other devices on the + * board (SPI, CLCD, etc). We don't implement that yet, so just + * make the bits read as written. + */ + qemu_log_mask(LOG_UNIMP, + "MPS2 FPGAIO: MISC control bits unimplemented\n"); + s->misc = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "MPS2 FPGAIO write: bad offset 0x%x\n", (int) offset); + break; + } +} + +static const MemoryRegionOps mps2_fpgaio_ops = { + .read = mps2_fpgaio_read, + .write = mps2_fpgaio_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void mps2_fpgaio_reset(DeviceState *dev) +{ + MPS2FPGAIO *s = MPS2_FPGAIO(dev); + + trace_mps2_fpgaio_reset(); + s->led0 = 0; + s->prescale = 0; + s->misc = 0; +} + +static void mps2_fpgaio_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + MPS2FPGAIO *s = MPS2_FPGAIO(obj); + + memory_region_init_io(&s->iomem, obj, &mps2_fpgaio_ops, s, + "mps2-fpgaio", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription mps2_fpgaio_vmstate = { + .name = "mps2-fpgaio", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(led0, MPS2FPGAIO), + VMSTATE_UINT32(prescale, MPS2FPGAIO), + VMSTATE_UINT32(misc, MPS2FPGAIO), + VMSTATE_END_OF_LIST() + } +}; + +static Property mps2_fpgaio_properties[] = { + /* Frequency of the prescale counter */ + DEFINE_PROP_UINT32("prescale-clk", MPS2FPGAIO, prescale_clk, 20000000), + DEFINE_PROP_END_OF_LIST(), +}; + +static void mps2_fpgaio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &mps2_fpgaio_vmstate; + dc->reset = mps2_fpgaio_reset; + dc->props = mps2_fpgaio_properties; +} + +static const TypeInfo mps2_fpgaio_info = { + .name = TYPE_MPS2_FPGAIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MPS2FPGAIO), + .instance_init = mps2_fpgaio_init, + .class_init = mps2_fpgaio_class_init, +}; + +static void mps2_fpgaio_register_types(void) +{ + type_register_static(&mps2_fpgaio_info); +} + +type_init(mps2_fpgaio_register_types); diff --git a/hw/misc/trace-events b/hw/misc/trace-events index b340d4e81c..eb5ffcc0a8 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -62,6 +62,12 @@ mps2_scc_leds(char led7, char led6, char led5, char led4, char led3, char led2, mps2_scc_cfg_write(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config write: function %d device %d data 0x%" PRIx32 mps2_scc_cfg_read(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config read: function %d device %d data 0x%" PRIx32 +# hw/misc/mps2_fpgaio.c +mps2_fpgaio_read(uint64_t offset, uint64_t data, unsigned size) "MPS2 FPGAIO read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +mps2_fpgaio_write(uint64_t offset, uint64_t data, unsigned size) "MPS2 FPGAIO write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +mps2_fpgaio_reset(void) "MPS2 FPGAIO: reset" +mps2_fpgaio_leds(char led1, char led0) "MPS2 FPGAIO LEDs: %c%c" + # hw/misc/msf2-sysreg.c msf2_sysreg_write(uint64_t offset, uint32_t val, uint32_t prev) "msf2-sysreg write: addr 0x%08" HWADDR_PRIx " data 0x%" PRIx32 " prev 0x%" PRIx32 msf2_sysreg_read(uint64_t offset, uint32_t val) "msf2-sysreg read: addr 0x%08" HWADDR_PRIx " data 0x%08" PRIx32 @@ -77,3 +83,21 @@ mos6522_get_next_irq_time(uint16_t latch, int64_t d, int64_t delta) "latch=%d co mos6522_set_sr_int(void) "set sr_int" mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64 mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x" + +# hw/misc/tz-ppc.c +tz_ppc_reset(void) "TZ PPC: reset" +tz_ppc_cfg_nonsec(int n, int level) "TZ PPC: cfg_nonsec[%d] = %d" +tz_ppc_cfg_ap(int n, int level) "TZ PPC: cfg_ap[%d] = %d" +tz_ppc_cfg_sec_resp(int level) "TZ PPC: cfg_sec_resp = %d" +tz_ppc_irq_enable(int level) "TZ PPC: int_enable = %d" +tz_ppc_irq_clear(int level) "TZ PPC: int_clear = %d" +tz_ppc_update_irq(int level) "TZ PPC: setting irq line to %d" +tz_ppc_read_blocked(int n, hwaddr offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" HWADDR_PRIx " read (secure %d user %d) blocked" +tz_ppc_write_blocked(int n, hwaddr offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" HWADDR_PRIx " write (secure %d user %d) blocked" + +# hw/misc/iotkit-secctl.c +iotkit_secctl_s_read(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl S regs read: offset 0x%x data 0x%" PRIx64 " size %u" +iotkit_secctl_s_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl S regs write: offset 0x%x data 0x%" PRIx64 " size %u" +iotkit_secctl_ns_read(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs read: offset 0x%x data 0x%" PRIx64 " size %u" +iotkit_secctl_ns_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs write: offset 0x%x data 0x%" PRIx64 " size %u" +iotkit_secctl_reset(void) "IoTKit SecCtl: reset" diff --git a/hw/misc/tz-ppc.c b/hw/misc/tz-ppc.c new file mode 100644 index 0000000000..3dd045c15f --- /dev/null +++ b/hw/misc/tz-ppc.c @@ -0,0 +1,302 @@ +/* + * ARM TrustZone peripheral 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-ppc.h" + +static void tz_ppc_update_irq(TZPPC *s) +{ + bool level = s->irq_status && s->irq_enable; + + trace_tz_ppc_update_irq(level); + qemu_set_irq(s->irq, level); +} + +static void tz_ppc_cfg_nonsec(void *opaque, int n, int level) +{ + TZPPC *s = TZ_PPC(opaque); + + assert(n < TZ_NUM_PORTS); + trace_tz_ppc_cfg_nonsec(n, level); + s->cfg_nonsec[n] = level; +} + +static void tz_ppc_cfg_ap(void *opaque, int n, int level) +{ + TZPPC *s = TZ_PPC(opaque); + + assert(n < TZ_NUM_PORTS); + trace_tz_ppc_cfg_ap(n, level); + s->cfg_ap[n] = level; +} + +static void tz_ppc_cfg_sec_resp(void *opaque, int n, int level) +{ + TZPPC *s = TZ_PPC(opaque); + + trace_tz_ppc_cfg_sec_resp(level); + s->cfg_sec_resp = level; +} + +static void tz_ppc_irq_enable(void *opaque, int n, int level) +{ + TZPPC *s = TZ_PPC(opaque); + + trace_tz_ppc_irq_enable(level); + s->irq_enable = level; + tz_ppc_update_irq(s); +} + +static void tz_ppc_irq_clear(void *opaque, int n, int level) +{ + TZPPC *s = TZ_PPC(opaque); + + trace_tz_ppc_irq_clear(level); + + s->irq_clear = level; + if (level) { + s->irq_status = false; + tz_ppc_update_irq(s); + } +} + +static bool tz_ppc_check(TZPPC *s, int n, MemTxAttrs attrs) +{ + /* Check whether to allow an access to port n; return true if + * the check passes, and false if the transaction must be blocked. + * If the latter, the caller must check cfg_sec_resp to determine + * whether to abort or RAZ/WI the transaction. + * The checks are: + * + nonsec_mask suppresses any check of the secure attribute + * + otherwise, block if cfg_nonsec is 1 and transaction is secure, + * or if cfg_nonsec is 0 and transaction is non-secure + * + block if transaction is usermode and cfg_ap is 0 + */ + if ((attrs.secure == s->cfg_nonsec[n] && !(s->nonsec_mask & (1 << n))) || + (attrs.user && !s->cfg_ap[n])) { + /* Block the transaction. */ + if (!s->irq_clear) { + /* Note that holding irq_clear high suppresses interrupts */ + s->irq_status = true; + tz_ppc_update_irq(s); + } + return false; + } + return true; +} + +static MemTxResult tz_ppc_read(void *opaque, hwaddr addr, uint64_t *pdata, + unsigned size, MemTxAttrs attrs) +{ + TZPPCPort *p = opaque; + TZPPC *s = p->ppc; + int n = p - s->port; + AddressSpace *as = &p->downstream_as; + uint64_t data; + MemTxResult res; + + if (!tz_ppc_check(s, n, attrs)) { + trace_tz_ppc_read_blocked(n, addr, attrs.secure, attrs.user); + if (s->cfg_sec_resp) { + return MEMTX_ERROR; + } else { + *pdata = 0; + return MEMTX_OK; + } + } + + switch (size) { + case 1: + data = address_space_ldub(as, addr, attrs, &res); + break; + case 2: + data = address_space_lduw_le(as, addr, attrs, &res); + break; + case 4: + data = address_space_ldl_le(as, addr, attrs, &res); + break; + case 8: + data = address_space_ldq_le(as, addr, attrs, &res); + break; + default: + g_assert_not_reached(); + } + *pdata = data; + return res; +} + +static MemTxResult tz_ppc_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) +{ + TZPPCPort *p = opaque; + TZPPC *s = p->ppc; + AddressSpace *as = &p->downstream_as; + int n = p - s->port; + MemTxResult res; + + if (!tz_ppc_check(s, n, attrs)) { + trace_tz_ppc_write_blocked(n, addr, attrs.secure, attrs.user); + if (s->cfg_sec_resp) { + return MEMTX_ERROR; + } else { + return MEMTX_OK; + } + } + + switch (size) { + case 1: + address_space_stb(as, addr, val, attrs, &res); + break; + case 2: + address_space_stw_le(as, addr, val, attrs, &res); + break; + case 4: + address_space_stl_le(as, addr, val, attrs, &res); + break; + case 8: + address_space_stq_le(as, addr, val, attrs, &res); + break; + default: + g_assert_not_reached(); + } + return res; +} + +static const MemoryRegionOps tz_ppc_ops = { + .read_with_attrs = tz_ppc_read, + .write_with_attrs = tz_ppc_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void tz_ppc_reset(DeviceState *dev) +{ + TZPPC *s = TZ_PPC(dev); + + trace_tz_ppc_reset(); + s->cfg_sec_resp = false; + memset(s->cfg_nonsec, 0, sizeof(s->cfg_nonsec)); + memset(s->cfg_ap, 0, sizeof(s->cfg_ap)); +} + +static void tz_ppc_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + TZPPC *s = TZ_PPC(obj); + + qdev_init_gpio_in_named(dev, tz_ppc_cfg_nonsec, "cfg_nonsec", TZ_NUM_PORTS); + qdev_init_gpio_in_named(dev, tz_ppc_cfg_ap, "cfg_ap", TZ_NUM_PORTS); + qdev_init_gpio_in_named(dev, tz_ppc_cfg_sec_resp, "cfg_sec_resp", 1); + qdev_init_gpio_in_named(dev, tz_ppc_irq_enable, "irq_enable", 1); + qdev_init_gpio_in_named(dev, tz_ppc_irq_clear, "irq_clear", 1); + qdev_init_gpio_out_named(dev, &s->irq, "irq", 1); +} + +static void tz_ppc_realize(DeviceState *dev, Error **errp) +{ + Object *obj = OBJECT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + TZPPC *s = TZ_PPC(dev); + int i; + + /* 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. + */ + for (i = 0; i < TZ_NUM_PORTS; i++) { + TZPPCPort *port = &s->port[i]; + char *name; + uint64_t size; + + if (!port->downstream) { + continue; + } + + name = g_strdup_printf("tz-ppc-port[%d]", i); + + port->ppc = s; + address_space_init(&port->downstream_as, port->downstream, name); + + size = memory_region_size(port->downstream); + memory_region_init_io(&port->upstream, obj, &tz_ppc_ops, + port, name, size); + sysbus_init_mmio(sbd, &port->upstream); + g_free(name); + } +} + +static const VMStateDescription tz_ppc_vmstate = { + .name = "tz-ppc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL_ARRAY(cfg_nonsec, TZPPC, 16), + VMSTATE_BOOL_ARRAY(cfg_ap, TZPPC, 16), + VMSTATE_BOOL(cfg_sec_resp, TZPPC), + VMSTATE_BOOL(irq_enable, TZPPC), + VMSTATE_BOOL(irq_clear, TZPPC), + VMSTATE_BOOL(irq_status, TZPPC), + VMSTATE_END_OF_LIST() + } +}; + +#define DEFINE_PORT(N) \ + DEFINE_PROP_LINK("port[" #N "]", TZPPC, port[N].downstream, \ + TYPE_MEMORY_REGION, MemoryRegion *) + +static Property tz_ppc_properties[] = { + DEFINE_PROP_UINT32("NONSEC_MASK", TZPPC, nonsec_mask, 0), + DEFINE_PORT(0), + DEFINE_PORT(1), + DEFINE_PORT(2), + DEFINE_PORT(3), + DEFINE_PORT(4), + DEFINE_PORT(5), + DEFINE_PORT(6), + DEFINE_PORT(7), + DEFINE_PORT(8), + DEFINE_PORT(9), + DEFINE_PORT(10), + DEFINE_PORT(11), + DEFINE_PORT(12), + DEFINE_PORT(13), + DEFINE_PORT(14), + DEFINE_PORT(15), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tz_ppc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = tz_ppc_realize; + dc->vmsd = &tz_ppc_vmstate; + dc->reset = tz_ppc_reset; + dc->props = tz_ppc_properties; +} + +static const TypeInfo tz_ppc_info = { + .name = TYPE_TZ_PPC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(TZPPC), + .instance_init = tz_ppc_init, + .class_init = tz_ppc_class_init, +}; + +static void tz_ppc_register_types(void) +{ + type_register_static(&tz_ppc_info); +} + +type_init(tz_ppc_register_types); diff --git a/hw/misc/unimp.c b/hw/misc/unimp.c index bcbb585888..1c0ba2f0a7 100644 --- a/hw/misc/unimp.c +++ b/hw/misc/unimp.c @@ -18,16 +18,6 @@ #include "qemu/log.h" #include "qapi/error.h" -#define UNIMPLEMENTED_DEVICE(obj) \ - OBJECT_CHECK(UnimplementedDeviceState, (obj), TYPE_UNIMPLEMENTED_DEVICE) - -typedef struct { - SysBusDevice parent_obj; - MemoryRegion iomem; - char *name; - uint64_t size; -} UnimplementedDeviceState; - static uint64_t unimp_read(void *opaque, hwaddr offset, unsigned size) { UnimplementedDeviceState *s = UNIMPLEMENTED_DEVICE(opaque); |