diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2019-02-01 16:39:17 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2019-02-01 16:39:17 +0000 |
commit | e83d74286cad2b9b967e1ba0ce5c8d16cba9679f (patch) | |
tree | bd57034a1550568ec8f9d6aa4457fd38531e9c17 /hw | |
parent | a1bc3e7dc8f89facee6d3c25fb8465f8feccef1f (diff) | |
parent | 7743b70ffe7a8ce168adce2cf50ad156b1fefb8c (diff) | |
download | qemu-e83d74286cad2b9b967e1ba0ce5c8d16cba9679f.zip qemu-e83d74286cad2b9b967e1ba0ce5c8d16cba9679f.tar.gz qemu-e83d74286cad2b9b967e1ba0ce5c8d16cba9679f.tar.bz2 |
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20190201' into staging
target-arm queue:
* New machine mps2-an521 -- this is a model of the AN521 FPGA image for the MPS2 devboard
* Fix various places where we failed to UNDEF invalid A64 instructions
* Don't UNDEF a valid FCMLA on 32-bit inputs
* Fix some bugs in the newly-added PAuth implementation
* microbit: Implement NVMC non-volatile memory controller
# gpg: Signature made Fri 01 Feb 2019 16:06:03 GMT
# gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg: issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate]
# gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate]
# gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE
* remotes/pmaydell/tags/pull-target-arm-20190201: (47 commits)
tests/microbit-test: Add tests for nRF51 NVMC
arm: Instantiate NRF51 special NVM's and NVMC
hw/nvram/nrf51_nvm: Add nRF51 non-volatile memories
target/arm: fix decoding of B{,L}RA{A,B}
target/arm: fix AArch64 virtual address space size
linux-user: Initialize aarch64 pac keys
aarch64-linux-user: Enable HWCAP bits for PAuth
aarch64-linux-user: Update HWCAP bits from linux 5.0-rc1
target/arm: Always enable pac keys for user-only
arm: Clarify the logic of set_pc()
target/arm: Enable API, APK bits in SCR, HCR
target/arm: Add a timer to predict PMU counter overflow
target/arm: Send interrupts on PMU counter overflow
target/arm/translate-a64: Fix mishandling of size in FCMLA decode
target/arm/translate-a64: Fix FCMLA decoding error
exec.c: Don't reallocate IOMMUNotifiers that are in use
target/arm/translate-a64: Don't underdecode SDOT and UDOT
target/arm/translate-a64: Don't underdecode FP insns
target/arm/translate-a64: Don't underdecode add/sub extended register
target/arm/translate-a64: Don't underdecode SIMD ld/st single
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/arm/Makefile.objs | 2 | ||||
-rw-r--r-- | hw/arm/armsse.c | 1241 | ||||
-rw-r--r-- | hw/arm/armv7m.c | 23 | ||||
-rw-r--r-- | hw/arm/boot.c | 4 | ||||
-rw-r--r-- | hw/arm/iotkit.c | 759 | ||||
-rw-r--r-- | hw/arm/mps2-tz.c | 121 | ||||
-rw-r--r-- | hw/arm/nrf51_soc.c | 44 | ||||
-rw-r--r-- | hw/intc/armv7m_nvic.c | 3 | ||||
-rw-r--r-- | hw/misc/Makefile.objs | 1 | ||||
-rw-r--r-- | hw/misc/armsse-cpuid.c | 134 | ||||
-rw-r--r-- | hw/misc/iotkit-secctl.c | 5 | ||||
-rw-r--r-- | hw/misc/iotkit-sysinfo.c | 15 | ||||
-rw-r--r-- | hw/misc/trace-events | 4 | ||||
-rw-r--r-- | hw/nvram/Makefile.objs | 1 | ||||
-rw-r--r-- | hw/nvram/nrf51_nvm.c | 388 |
15 files changed, 1935 insertions, 810 deletions
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 50c7b4a..22b7f0e 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -34,7 +34,7 @@ obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o obj-$(CONFIG_MPS2) += mps2.o obj-$(CONFIG_MPS2) += mps2-tz.o obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o -obj-$(CONFIG_IOTKIT) += iotkit.o +obj-$(CONFIG_ARMSSE) += armsse.o obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o smmuv3.o obj-$(CONFIG_FSL_IMX6UL) += fsl-imx6ul.o mcimx6ul-evk.o diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c new file mode 100644 index 0000000..5d53071 --- /dev/null +++ b/hw/arm/armsse.c @@ -0,0 +1,1241 @@ +/* + * Arm SSE (Subsystems for Embedded): IoTKit + * + * 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/arm/armsse.h" +#include "hw/arm/arm.h" + +/* Format of the System Information block SYS_CONFIG register */ +typedef enum SysConfigFormat { + IoTKitFormat, + SSE200Format, +} SysConfigFormat; + +struct ARMSSEInfo { + const char *name; + int sram_banks; + int num_cpus; + uint32_t sys_version; + SysConfigFormat sys_config_format; + bool has_mhus; + bool has_ppus; + bool has_cachectrl; + bool has_cpusecctrl; + bool has_cpuid; +}; + +static const ARMSSEInfo armsse_variants[] = { + { + .name = TYPE_IOTKIT, + .sram_banks = 1, + .num_cpus = 1, + .sys_version = 0x41743, + .sys_config_format = IoTKitFormat, + .has_mhus = false, + .has_ppus = false, + .has_cachectrl = false, + .has_cpusecctrl = false, + .has_cpuid = false, + }, + { + .name = TYPE_SSE200, + .sram_banks = 4, + .num_cpus = 2, + .sys_version = 0x22041743, + .sys_config_format = SSE200Format, + .has_mhus = true, + .has_ppus = true, + .has_cachectrl = true, + .has_cpusecctrl = true, + .has_cpuid = true, + }, +}; + +static uint32_t armsse_sys_config_value(ARMSSE *s, const ARMSSEInfo *info) +{ + /* Return the SYS_CONFIG value for this SSE */ + uint32_t sys_config; + + switch (info->sys_config_format) { + case IoTKitFormat: + sys_config = 0; + sys_config = deposit32(sys_config, 0, 4, info->sram_banks); + sys_config = deposit32(sys_config, 4, 4, s->sram_addr_width - 12); + break; + case SSE200Format: + sys_config = 0; + sys_config = deposit32(sys_config, 0, 4, info->sram_banks); + sys_config = deposit32(sys_config, 4, 5, s->sram_addr_width); + sys_config = deposit32(sys_config, 24, 4, 2); + if (info->num_cpus > 1) { + sys_config = deposit32(sys_config, 10, 1, 1); + sys_config = deposit32(sys_config, 20, 4, info->sram_banks - 1); + sys_config = deposit32(sys_config, 28, 4, 2); + } + break; + default: + g_assert_not_reached(); + } + return sys_config; +} + +/* Clock frequency in HZ of the 32KHz "slow clock" */ +#define S32KCLK (32 * 1000) + +/* Is internal IRQ n shared between CPUs in a multi-core SSE ? */ +static bool irq_is_common[32] = { + [0 ... 5] = true, + /* 6, 7: per-CPU MHU interrupts */ + [8 ... 12] = true, + /* 13: per-CPU icache interrupt */ + /* 14: reserved */ + [15 ... 20] = true, + /* 21: reserved */ + [22 ... 26] = true, + /* 27: reserved */ + /* 28, 29: per-CPU CTI interrupts */ + /* 30, 31: reserved */ +}; + +/* Create an alias region of @size bytes starting at @base + * which mirrors the memory starting at @orig. + */ +static void make_alias(ARMSSE *s, MemoryRegion *mr, const char *name, + hwaddr base, hwaddr size, hwaddr orig) +{ + memory_region_init_alias(mr, NULL, name, &s->container, orig, size); + /* The alias is even lower priority than unimplemented_device regions */ + memory_region_add_subregion_overlap(&s->container, base, mr, -1500); +} + +static void irq_status_forwarder(void *opaque, int n, int level) +{ + qemu_irq destirq = opaque; + + qemu_set_irq(destirq, level); +} + +static void nsccfg_handler(void *opaque, int n, int level) +{ + ARMSSE *s = ARMSSE(opaque); + + s->nsccfg = level; +} + +static void armsse_forward_ppc(ARMSSE *s, const char *ppcname, int ppcnum) +{ + /* Each of the 4 AHB and 4 APB PPCs that might be present in a + * system using the ARMSSE has a collection of control lines which + * are provided by the security controller and which we want to + * expose as control lines on the ARMSSE device itself, so the + * code using the ARMSSE can wire them up to the PPCs. + */ + SplitIRQ *splitter = &s->ppc_irq_splitter[ppcnum]; + DeviceState *armssedev = DEVICE(s); + DeviceState *dev_secctl = DEVICE(&s->secctl); + DeviceState *dev_splitter = DEVICE(splitter); + char *name; + + name = g_strdup_printf("%s_nonsec", ppcname); + qdev_pass_gpios(dev_secctl, armssedev, name); + g_free(name); + name = g_strdup_printf("%s_ap", ppcname); + qdev_pass_gpios(dev_secctl, armssedev, name); + g_free(name); + name = g_strdup_printf("%s_irq_enable", ppcname); + qdev_pass_gpios(dev_secctl, armssedev, name); + g_free(name); + name = g_strdup_printf("%s_irq_clear", ppcname); + qdev_pass_gpios(dev_secctl, armssedev, name); + g_free(name); + + /* irq_status is a little more tricky, because we need to + * split it so we can send it both to the security controller + * and to our OR gate for the NVIC interrupt line. + * Connect up the splitter's outputs, and create a GPIO input + * which will pass the line state to the input splitter. + */ + name = g_strdup_printf("%s_irq_status", ppcname); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_secctl, + name, 0)); + qdev_connect_gpio_out(dev_splitter, 1, + qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), ppcnum)); + s->irq_status_in[ppcnum] = qdev_get_gpio_in(dev_splitter, 0); + qdev_init_gpio_in_named_with_opaque(armssedev, irq_status_forwarder, + s->irq_status_in[ppcnum], name, 1); + g_free(name); +} + +static void armsse_forward_sec_resp_cfg(ARMSSE *s) +{ + /* Forward the 3rd output from the splitter device as a + * named GPIO output of the armsse object. + */ + DeviceState *dev = DEVICE(s); + DeviceState *dev_splitter = DEVICE(&s->sec_resp_splitter); + + qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); + s->sec_resp_cfg_in = qemu_allocate_irq(irq_status_forwarder, + s->sec_resp_cfg, 1); + qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in); +} + +static void armsse_init(Object *obj) +{ + ARMSSE *s = ARMSSE(obj); + ARMSSEClass *asc = ARMSSE_GET_CLASS(obj); + const ARMSSEInfo *info = asc->info; + int i; + + assert(info->sram_banks <= MAX_SRAM_BANKS); + assert(info->num_cpus <= SSE_MAX_CPUS); + + memory_region_init(&s->container, obj, "armsse-container", UINT64_MAX); + + for (i = 0; i < info->num_cpus; i++) { + /* + * We put each CPU in its own cluster as they are logically + * distinct and may be configured differently. + */ + char *name; + + name = g_strdup_printf("cluster%d", i); + object_initialize_child(obj, name, &s->cluster[i], + sizeof(s->cluster[i]), TYPE_CPU_CLUSTER, + &error_abort, NULL); + qdev_prop_set_uint32(DEVICE(&s->cluster[i]), "cluster-id", i); + g_free(name); + + name = g_strdup_printf("armv7m%d", i); + sysbus_init_child_obj(OBJECT(&s->cluster[i]), name, + &s->armv7m[i], sizeof(s->armv7m), TYPE_ARMV7M); + qdev_prop_set_string(DEVICE(&s->armv7m[i]), "cpu-type", + ARM_CPU_TYPE_NAME("cortex-m33")); + g_free(name); + name = g_strdup_printf("arm-sse-cpu-container%d", i); + memory_region_init(&s->cpu_container[i], obj, name, UINT64_MAX); + g_free(name); + if (i > 0) { + name = g_strdup_printf("arm-sse-container-alias%d", i); + memory_region_init_alias(&s->container_alias[i - 1], obj, + name, &s->container, 0, UINT64_MAX); + g_free(name); + } + } + + sysbus_init_child_obj(obj, "secctl", &s->secctl, sizeof(s->secctl), + TYPE_IOTKIT_SECCTL); + sysbus_init_child_obj(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0), + TYPE_TZ_PPC); + sysbus_init_child_obj(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), + TYPE_TZ_PPC); + for (i = 0; i < info->sram_banks; i++) { + char *name = g_strdup_printf("mpc%d", i); + sysbus_init_child_obj(obj, name, &s->mpc[i], + sizeof(s->mpc[i]), TYPE_TZ_MPC); + g_free(name); + } + object_initialize_child(obj, "mpc-irq-orgate", &s->mpc_irq_orgate, + sizeof(s->mpc_irq_orgate), TYPE_OR_IRQ, + &error_abort, NULL); + + for (i = 0; i < IOTS_NUM_EXP_MPC + info->sram_banks; i++) { + char *name = g_strdup_printf("mpc-irq-splitter-%d", i); + SplitIRQ *splitter = &s->mpc_irq_splitter[i]; + + object_initialize_child(obj, name, splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_abort, NULL); + g_free(name); + } + sysbus_init_child_obj(obj, "timer0", &s->timer0, sizeof(s->timer0), + TYPE_CMSDK_APB_TIMER); + sysbus_init_child_obj(obj, "timer1", &s->timer1, sizeof(s->timer1), + TYPE_CMSDK_APB_TIMER); + sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), + TYPE_CMSDK_APB_TIMER); + sysbus_init_child_obj(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer), + TYPE_CMSDK_APB_DUALTIMER); + sysbus_init_child_obj(obj, "s32kwatchdog", &s->s32kwatchdog, + sizeof(s->s32kwatchdog), TYPE_CMSDK_APB_WATCHDOG); + sysbus_init_child_obj(obj, "nswatchdog", &s->nswatchdog, + sizeof(s->nswatchdog), TYPE_CMSDK_APB_WATCHDOG); + sysbus_init_child_obj(obj, "swatchdog", &s->swatchdog, + sizeof(s->swatchdog), TYPE_CMSDK_APB_WATCHDOG); + sysbus_init_child_obj(obj, "armsse-sysctl", &s->sysctl, + sizeof(s->sysctl), TYPE_IOTKIT_SYSCTL); + sysbus_init_child_obj(obj, "armsse-sysinfo", &s->sysinfo, + sizeof(s->sysinfo), TYPE_IOTKIT_SYSINFO); + if (info->has_mhus) { + sysbus_init_child_obj(obj, "mhu0", &s->mhu[0], sizeof(s->mhu[0]), + TYPE_UNIMPLEMENTED_DEVICE); + sysbus_init_child_obj(obj, "mhu1", &s->mhu[1], sizeof(s->mhu[1]), + TYPE_UNIMPLEMENTED_DEVICE); + } + if (info->has_ppus) { + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("CPU%dCORE_PPU", i); + int ppuidx = CPU0CORE_PPU + i; + + sysbus_init_child_obj(obj, name, &s->ppu[ppuidx], + sizeof(s->ppu[ppuidx]), + TYPE_UNIMPLEMENTED_DEVICE); + g_free(name); + } + sysbus_init_child_obj(obj, "DBG_PPU", &s->ppu[DBG_PPU], + sizeof(s->ppu[DBG_PPU]), + TYPE_UNIMPLEMENTED_DEVICE); + for (i = 0; i < info->sram_banks; i++) { + char *name = g_strdup_printf("RAM%d_PPU", i); + int ppuidx = RAM0_PPU + i; + + sysbus_init_child_obj(obj, name, &s->ppu[ppuidx], + sizeof(s->ppu[ppuidx]), + TYPE_UNIMPLEMENTED_DEVICE); + g_free(name); + } + } + if (info->has_cachectrl) { + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("cachectrl%d", i); + + sysbus_init_child_obj(obj, name, &s->cachectrl[i], + sizeof(s->cachectrl[i]), + TYPE_UNIMPLEMENTED_DEVICE); + g_free(name); + } + } + if (info->has_cpusecctrl) { + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("cpusecctrl%d", i); + + sysbus_init_child_obj(obj, name, &s->cpusecctrl[i], + sizeof(s->cpusecctrl[i]), + TYPE_UNIMPLEMENTED_DEVICE); + g_free(name); + } + } + if (info->has_cpuid) { + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("cpuid%d", i); + + sysbus_init_child_obj(obj, name, &s->cpuid[i], + sizeof(s->cpuid[i]), + TYPE_ARMSSE_CPUID); + g_free(name); + } + } + object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate, + sizeof(s->nmi_orgate), TYPE_OR_IRQ, + &error_abort, NULL); + object_initialize_child(obj, "ppc-irq-orgate", &s->ppc_irq_orgate, + sizeof(s->ppc_irq_orgate), TYPE_OR_IRQ, + &error_abort, NULL); + object_initialize_child(obj, "sec-resp-splitter", &s->sec_resp_splitter, + sizeof(s->sec_resp_splitter), TYPE_SPLIT_IRQ, + &error_abort, NULL); + for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { + char *name = g_strdup_printf("ppc-irq-splitter-%d", i); + SplitIRQ *splitter = &s->ppc_irq_splitter[i]; + + object_initialize_child(obj, name, splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_abort, NULL); + g_free(name); + } + if (info->num_cpus > 1) { + for (i = 0; i < ARRAY_SIZE(s->cpu_irq_splitter); i++) { + if (irq_is_common[i]) { + char *name = g_strdup_printf("cpu-irq-splitter%d", i); + SplitIRQ *splitter = &s->cpu_irq_splitter[i]; + + object_initialize_child(obj, name, splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_abort, NULL); + g_free(name); + } + } + } +} + +static void armsse_exp_irq(void *opaque, int n, int level) +{ + qemu_irq *irqarray = opaque; + + qemu_set_irq(irqarray[n], level); +} + +static void armsse_mpcexp_status(void *opaque, int n, int level) +{ + ARMSSE *s = ARMSSE(opaque); + qemu_set_irq(s->mpcexp_status_in[n], level); +} + +static qemu_irq armsse_get_common_irq_in(ARMSSE *s, int irqno) +{ + /* + * Return a qemu_irq which can be used to signal IRQ n to + * all CPUs in the SSE. + */ + ARMSSEClass *asc = ARMSSE_GET_CLASS(s); + const ARMSSEInfo *info = asc->info; + + assert(irq_is_common[irqno]); + + if (info->num_cpus == 1) { + /* Only one CPU -- just connect directly to it */ + return qdev_get_gpio_in(DEVICE(&s->armv7m[0]), irqno); + } else { + /* Connect to the splitter which feeds all CPUs */ + return qdev_get_gpio_in(DEVICE(&s->cpu_irq_splitter[irqno]), 0); + } +} + +static void map_ppu(ARMSSE *s, int ppuidx, const char *name, hwaddr addr) +{ + /* Map a PPU unimplemented device stub */ + DeviceState *dev = DEVICE(&s->ppu[ppuidx]); + + qdev_prop_set_string(dev, "name", name); + qdev_prop_set_uint64(dev, "size", 0x1000); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ppu[ppuidx]), 0, addr); +} + +static void armsse_realize(DeviceState *dev, Error **errp) +{ + ARMSSE *s = ARMSSE(dev); + ARMSSEClass *asc = ARMSSE_GET_CLASS(dev); + const ARMSSEInfo *info = asc->info; + int i; + MemoryRegion *mr; + Error *err = NULL; + SysBusDevice *sbd_apb_ppc0; + SysBusDevice *sbd_secctl; + DeviceState *dev_apb_ppc0; + DeviceState *dev_apb_ppc1; + DeviceState *dev_secctl; + DeviceState *dev_splitter; + uint32_t addr_width_max; + + if (!s->board_memory) { + error_setg(errp, "memory property was not set"); + return; + } + + if (!s->mainclk_frq) { + error_setg(errp, "MAINCLK property was not set"); + return; + } + + /* max SRAM_ADDR_WIDTH: 24 - log2(SRAM_NUM_BANK) */ + assert(is_power_of_2(info->sram_banks)); + addr_width_max = 24 - ctz32(info->sram_banks); + if (s->sram_addr_width < 1 || s->sram_addr_width > addr_width_max) { + error_setg(errp, "SRAM_ADDR_WIDTH must be between 1 and %d", + addr_width_max); + return; + } + + /* Handling of which devices should be available only to secure + * code is usually done differently for M profile than for A profile. + * Instead of putting some devices only into the secure address space, + * devices exist in both address spaces but with hard-wired security + * permissions that will cause the CPU to fault for non-secure accesses. + * + * The ARMSSE has an IDAU (Implementation Defined Access Unit), + * which specifies hard-wired security permissions for different + * areas of the physical address space. For the ARMSSE IDAU, the + * top 4 bits of the physical address are the IDAU region ID, and + * if bit 28 (ie the lowest bit of the ID) is 0 then this is an NS + * region, otherwise it is an S region. + * + * The various devices and RAMs are generally all mapped twice, + * once into a region that the IDAU defines as secure and once + * into a non-secure region. They sit behind either a Memory + * Protection Controller (for RAM) or a Peripheral Protection + * Controller (for devices), which allow a more fine grained + * configuration of whether non-secure accesses are permitted. + * + * (The other place that guest software can configure security + * permissions is in the architected SAU (Security Attribution + * Unit), which is entirely inside the CPU. The IDAU can upgrade + * the security attributes for a region to more restrictive than + * the SAU specifies, but cannot downgrade them.) + * + * 0x10000000..0x1fffffff alias of 0x00000000..0x0fffffff + * 0x20000000..0x2007ffff 32KB FPGA block RAM + * 0x30000000..0x3fffffff alias of 0x20000000..0x2fffffff + * 0x40000000..0x4000ffff base peripheral region 1 + * 0x40010000..0x4001ffff CPU peripherals (none for ARMSSE) + * 0x40020000..0x4002ffff system control element peripherals + * 0x40080000..0x400fffff base peripheral region 2 + * 0x50000000..0x5fffffff alias of 0x40000000..0x4fffffff + */ + + memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -2); + + for (i = 0; i < info->num_cpus; i++) { + DeviceState *cpudev = DEVICE(&s->armv7m[i]); + Object *cpuobj = OBJECT(&s->armv7m[i]); + int j; + char *gpioname; + + qdev_prop_set_uint32(cpudev, "num-irq", s->exp_numirq + 32); + /* + * In real hardware the initial Secure VTOR is set from the INITSVTOR0 + * register in the IoT Kit System Control Register block, and the + * initial value of that is in turn specifiable by the FPGA that + * instantiates the IoT Kit. In QEMU we don't implement this wrinkle, + * and simply set the CPU's init-svtor to the IoT Kit default value. + * In SSE-200 the situation is similar, except that the default value + * is a reset-time signal input. Typically a board using the SSE-200 + * will have a system control processor whose boot firmware initializes + * the INITSVTOR* registers before powering up the CPUs in any case, + * so the hardware's default value doesn't matter. QEMU doesn't emulate + * the control processor, so instead we behave in the way that the + * firmware does. All boards currently known about have firmware that + * sets the INITSVTOR0 and INITSVTOR1 registers to 0x10000000, like the + * IoTKit default. We can make this more configurable if necessary. + */ + qdev_prop_set_uint32(cpudev, "init-svtor", 0x10000000); + /* + * Start all CPUs except CPU0 powered down. In real hardware it is + * a configurable property of the SSE-200 which CPUs start powered up + * (via the CPUWAIT0_RST and CPUWAIT1_RST parameters), but since all + * the boards we care about start CPU0 and leave CPU1 powered off, + * we hard-code that for now. We can add QOM properties for this + * later if necessary. + */ + if (i > 0) { + object_property_set_bool(cpuobj, true, "start-powered-off", &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + if (i > 0) { + memory_region_add_subregion_overlap(&s->cpu_container[i], 0, + &s->container_alias[i - 1], -1); + } else { + memory_region_add_subregion_overlap(&s->cpu_container[i], 0, + &s->container, -1); + } + object_property_set_link(cpuobj, OBJECT(&s->cpu_container[i]), + "memory", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_link(cpuobj, OBJECT(s), "idau", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(cpuobj, true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + /* + * The cluster must be realized after the armv7m container, as + * the container's CPU object is only created on realize, and the + * CPU must exist and have been parented into the cluster before + * the cluster is realized. + */ + object_property_set_bool(OBJECT(&s->cluster[i]), + true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + /* Connect EXP_IRQ/EXP_CPUn_IRQ GPIOs to the NVIC's lines 32 and up */ + s->exp_irqs[i] = g_new(qemu_irq, s->exp_numirq); + for (j = 0; j < s->exp_numirq; j++) { + s->exp_irqs[i][j] = qdev_get_gpio_in(cpudev, i + 32); + } + if (i == 0) { + gpioname = g_strdup("EXP_IRQ"); + } else { + gpioname = g_strdup_printf("EXP_CPU%d_IRQ", i); + } + qdev_init_gpio_in_named_with_opaque(dev, armsse_exp_irq, + s->exp_irqs[i], + gpioname, s->exp_numirq); + g_free(gpioname); + } + + /* Wire up the splitters that connect common IRQs to all CPUs */ + if (info->num_cpus > 1) { + for (i = 0; i < ARRAY_SIZE(s->cpu_irq_splitter); i++) { + if (irq_is_common[i]) { + Object *splitter = OBJECT(&s->cpu_irq_splitter[i]); + DeviceState *devs = DEVICE(splitter); + int cpunum; + + object_property_set_int(splitter, info->num_cpus, + "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(splitter, true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + for (cpunum = 0; cpunum < info->num_cpus; cpunum++) { + DeviceState *cpudev = DEVICE(&s->armv7m[cpunum]); + + qdev_connect_gpio_out(devs, cpunum, + qdev_get_gpio_in(cpudev, i)); + } + } + } + } + + /* Set up the big aliases first */ + make_alias(s, &s->alias1, "alias 1", 0x10000000, 0x10000000, 0x00000000); + make_alias(s, &s->alias2, "alias 2", 0x30000000, 0x10000000, 0x20000000); + /* The 0x50000000..0x5fffffff region is not a pure alias: it has + * a few extra devices that only appear there (generally the + * control interfaces for the protection controllers). + * We implement this by mapping those devices over the top of this + * alias MR at a higher priority. + */ + make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000); + + + /* Security controller */ + object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sbd_secctl = SYS_BUS_DEVICE(&s->secctl); + dev_secctl = DEVICE(&s->secctl); + sysbus_mmio_map(sbd_secctl, 0, 0x50080000); + sysbus_mmio_map(sbd_secctl, 1, 0x40080000); + + s->nsc_cfg_in = qemu_allocate_irq(nsccfg_handler, s, 1); + qdev_connect_gpio_out_named(dev_secctl, "nsc_cfg", 0, s->nsc_cfg_in); + + /* The sec_resp_cfg output from the security controller must be split into + * multiple lines, one for each of the PPCs within the ARMSSE and one + * that will be an output from the ARMSSE to the system. + */ + object_property_set_int(OBJECT(&s->sec_resp_splitter), 3, + "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->sec_resp_splitter), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + dev_splitter = DEVICE(&s->sec_resp_splitter); + qdev_connect_gpio_out_named(dev_secctl, "sec_resp_cfg", 0, + qdev_get_gpio_in(dev_splitter, 0)); + + /* Each SRAM bank lives behind its own Memory Protection Controller */ + for (i = 0; i < info->sram_banks; i++) { + char *ramname = g_strdup_printf("armsse.sram%d", i); + SysBusDevice *sbd_mpc; + uint32_t sram_bank_size = 1 << s->sram_addr_width; + + memory_region_init_ram(&s->sram[i], NULL, ramname, + sram_bank_size, &err); + g_free(ramname); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_link(OBJECT(&s->mpc[i]), OBJECT(&s->sram[i]), + "downstream", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->mpc[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + /* Map the upstream end of the MPC into the right place... */ + sbd_mpc = SYS_BUS_DEVICE(&s->mpc[i]); + memory_region_add_subregion(&s->container, + 0x20000000 + i * sram_bank_size, + sysbus_mmio_get_region(sbd_mpc, 1)); + /* ...and its register interface */ + memory_region_add_subregion(&s->container, 0x50083000 + i * 0x1000, + sysbus_mmio_get_region(sbd_mpc, 0)); + } + + /* We must OR together lines from the MPC splitters to go to the NVIC */ + object_property_set_int(OBJECT(&s->mpc_irq_orgate), + IOTS_NUM_EXP_MPC + info->sram_banks, + "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->mpc_irq_orgate), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + qdev_connect_gpio_out(DEVICE(&s->mpc_irq_orgate), 0, + armsse_get_common_irq_in(s, 9)); + + /* Devices behind APB PPC0: + * 0x40000000: timer0 + * 0x40001000: timer1 + * 0x40002000: dual timer + * 0x40003000: MHU0 (SSE-200 only) + * 0x40004000: MHU1 (SSE-200 only) + * We must configure and realize each downstream device and connect + * it to the appropriate PPC port; then we can realize the PPC and + * map its upstream ends to the right place in the container. + */ + qdev_prop_set_uint32(DEVICE(&s->timer0), "pclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->timer0), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer0), 0, + armsse_get_common_irq_in(s, 3)); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer0), 0); + object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[0]", &err); + if (err) { + error_propagate(errp, err); + return; + } + + qdev_prop_set_uint32(DEVICE(&s->timer1), "pclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->timer1), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer1), 0, + armsse_get_common_irq_in(s, 4)); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer1), 0); + object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[1]", &err); + if (err) { + error_propagate(errp, err); + return; + } + + + qdev_prop_set_uint32(DEVICE(&s->dualtimer), "pclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->dualtimer), 0, + armsse_get_common_irq_in(s, 5)); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0); + object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err); + if (err) { + error_propagate(errp, err); + return; + } + + if (info->has_mhus) { + for (i = 0; i < ARRAY_SIZE(s->mhu); i++) { + char *name = g_strdup_printf("MHU%d", i); + char *port = g_strdup_printf("port[%d]", i + 3); + + qdev_prop_set_string(DEVICE(&s->mhu[i]), "name", name); + qdev_prop_set_uint64(DEVICE(&s->mhu[i]), "size", 0x1000); + object_property_set_bool(OBJECT(&s->mhu[i]), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mhu[i]), 0); + object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), + port, &err); + if (err) { + error_propagate(errp, err); + return; + } + g_free(name); + g_free(port); + } + } + + object_property_set_bool(OBJECT(&s->apb_ppc0), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sbd_apb_ppc0 = SYS_BUS_DEVICE(&s->apb_ppc0); + dev_apb_ppc0 = DEVICE(&s->apb_ppc0); + + mr = sysbus_mmio_get_region(sbd_apb_ppc0, 0); + memory_region_add_subregion(&s->container, 0x40000000, mr); + mr = sysbus_mmio_get_region(sbd_apb_ppc0, 1); + memory_region_add_subregion(&s->container, 0x40001000, mr); + mr = sysbus_mmio_get_region(sbd_apb_ppc0, 2); + memory_region_add_subregion(&s->container, 0x40002000, mr); + if (info->has_mhus) { + mr = sysbus_mmio_get_region(sbd_apb_ppc0, 3); + memory_region_add_subregion(&s->container, 0x40003000, mr); + mr = sysbus_mmio_get_region(sbd_apb_ppc0, 4); + memory_region_add_subregion(&s->container, 0x40004000, mr); + } + for (i = 0; i < IOTS_APB_PPC0_NUM_PORTS; i++) { + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_nonsec", i, + qdev_get_gpio_in_named(dev_apb_ppc0, + "cfg_nonsec", i)); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_ap", i, + qdev_get_gpio_in_named(dev_apb_ppc0, + "cfg_ap", i)); + } + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_enable", 0, + qdev_get_gpio_in_named(dev_apb_ppc0, + "irq_enable", 0)); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_clear", 0, + qdev_get_gpio_in_named(dev_apb_ppc0, + "irq_clear", 0)); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_apb_ppc0, + "cfg_sec_resp", 0)); + + /* All the PPC irq lines (from the 2 internal PPCs and the 8 external + * ones) are sent individually to the security controller, and also + * ORed together to give a single combined PPC interrupt to the NVIC. + */ + object_property_set_int(OBJECT(&s->ppc_irq_orgate), + NUM_PPCS, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->ppc_irq_orgate), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + qdev_connect_gpio_out(DEVICE(&s->ppc_irq_orgate), 0, + armsse_get_common_irq_in(s, 10)); + + /* + * 0x40010000 .. 0x4001ffff (and the 0x5001000... secure-only alias): + * private per-CPU region (all these devices are SSE-200 only): + * 0x50010000: L1 icache control registers + * 0x50011000: CPUSECCTRL (CPU local security control registers) + * 0x4001f000 and 0x5001f000: CPU_IDENTITY register block + */ + if (info->has_cachectrl) { + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("cachectrl%d", i); + MemoryRegion *mr; + + qdev_prop_set_string(DEVICE(&s->cachectrl[i]), "name", name); + g_free(name); + qdev_prop_set_uint64(DEVICE(&s->cachectrl[i]), "size", 0x1000); + object_property_set_bool(OBJECT(&s->cachectrl[i]), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cachectrl[i]), 0); + memory_region_add_subregion(&s->cpu_container[i], 0x50010000, mr); + } + } + if (info->has_cpusecctrl) { + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("CPUSECCTRL%d", i); + MemoryRegion *mr; + + qdev_prop_set_string(DEVICE(&s->cpusecctrl[i]), "name", name); + g_free(name); + qdev_prop_set_uint64(DEVICE(&s->cpusecctrl[i]), "size", 0x1000); + object_property_set_bool(OBJECT(&s->cpusecctrl[i]), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cpusecctrl[i]), 0); + memory_region_add_subregion(&s->cpu_container[i], 0x50011000, mr); + } + } + if (info->has_cpuid) { + for (i = 0; i < info->num_cpus; i++) { + MemoryRegion *mr; + + qdev_prop_set_uint32(DEVICE(&s->cpuid[i]), "CPUID", i); + object_property_set_bool(OBJECT(&s->cpuid[i]), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cpuid[i]), 0); + memory_region_add_subregion(&s->cpu_container[i], 0x4001F000, mr); + } + } + + /* 0x40020000 .. 0x4002ffff : ARMSSE system control peripheral region */ + /* Devices behind APB PPC1: + * 0x4002f000: S32K timer + */ + qdev_prop_set_uint32(DEVICE(&s->s32ktimer), "pclk-frq", S32KCLK); + object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32ktimer), 0, + armsse_get_common_irq_in(s, 2)); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0); + object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->apb_ppc1), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->apb_ppc1), 0); + memory_region_add_subregion(&s->container, 0x4002f000, mr); + + dev_apb_ppc1 = DEVICE(&s->apb_ppc1); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_nonsec", 0, + qdev_get_gpio_in_named(dev_apb_ppc1, + "cfg_nonsec", 0)); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_ap", 0, + qdev_get_gpio_in_named(dev_apb_ppc1, + "cfg_ap", 0)); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_enable", 0, + qdev_get_gpio_in_named(dev_apb_ppc1, + "irq_enable", 0)); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_clear", 0, + qdev_get_gpio_in_named(dev_apb_ppc1, + "irq_clear", 0)); + qdev_connect_gpio_out(dev_splitter, 1, + qdev_get_gpio_in_named(dev_apb_ppc1, + "cfg_sec_resp", 0)); + + object_property_set_int(OBJECT(&s->sysinfo), info->sys_version, + "SYS_VERSION", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_int(OBJECT(&s->sysinfo), + armsse_sys_config_value(s, info), + "SYS_CONFIG", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->sysinfo), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + /* System information registers */ + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, 0x40020000); + /* System control registers */ + object_property_set_bool(OBJECT(&s->sysctl), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctl), 0, 0x50021000); + + if (info->has_ppus) { + /* CPUnCORE_PPU for each CPU */ + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("CPU%dCORE_PPU", i); + + map_ppu(s, CPU0CORE_PPU + i, name, 0x50023000 + i * 0x2000); + /* + * We don't support CPU debug so don't create the + * CPU0DEBUG_PPU at 0x50024000 and 0x50026000. + */ + g_free(name); + } + map_ppu(s, DBG_PPU, "DBG_PPU", 0x50029000); + + for (i = 0; i < info->sram_banks; i++) { + char *name = g_strdup_printf("RAM%d_PPU", i); + + map_ppu(s, RAM0_PPU + i, name, 0x5002a000 + i * 0x1000); + g_free(name); + } + } + + /* This OR gate wires together outputs from the secure watchdogs to NMI */ + object_property_set_int(OBJECT(&s->nmi_orgate), 2, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->nmi_orgate), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + qdev_connect_gpio_out(DEVICE(&s->nmi_orgate), 0, + qdev_get_gpio_in_named(DEVICE(&s->armv7m), "NMI", 0)); + + qdev_prop_set_uint32(DEVICE(&s->s32kwatchdog), "wdogclk-frq", S32KCLK); + object_property_set_bool(OBJECT(&s->s32kwatchdog), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, + qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 0)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, 0x5002e000); + + /* 0x40080000 .. 0x4008ffff : ARMSSE second Base peripheral region */ + + qdev_prop_set_uint32(DEVICE(&s->nswatchdog), "wdogclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->nswatchdog), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->nswatchdog), 0, + armsse_get_common_irq_in(s, 1)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->nswatchdog), 0, 0x40081000); + + qdev_prop_set_uint32(DEVICE(&s->swatchdog), "wdogclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->swatchdog), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->swatchdog), 0, + qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 1)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->swatchdog), 0, 0x50081000); + + for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { + Object *splitter = OBJECT(&s->ppc_irq_splitter[i]); + + object_property_set_int(splitter, 2, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(splitter, true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) { + char *ppcname = g_strdup_printf("ahb_ppcexp%d", i); + + armsse_forward_ppc(s, ppcname, i); + g_free(ppcname); + } + + for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) { + char *ppcname = g_strdup_printf("apb_ppcexp%d", i); + + armsse_forward_ppc(s, ppcname, i + IOTS_NUM_AHB_EXP_PPC); + g_free(ppcname); + } + + for (i = NUM_EXTERNAL_PPCS; i < NUM_PPCS; i++) { + /* Wire up IRQ splitter for internal PPCs */ + DeviceState *devs = DEVICE(&s->ppc_irq_splitter[i]); + char *gpioname = g_strdup_printf("apb_ppc%d_irq_status", + i - NUM_EXTERNAL_PPCS); + TZPPC *ppc = (i == NUM_EXTERNAL_PPCS) ? &s->apb_ppc0 : &s->apb_ppc1; + + qdev_connect_gpio_out(devs, 0, + qdev_get_gpio_in_named(dev_secctl, gpioname, 0)); + qdev_connect_gpio_out(devs, 1, + qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), i)); + qdev_connect_gpio_out_named(DEVICE(ppc), "irq", 0, + qdev_get_gpio_in(devs, 0)); + g_free(gpioname); + } + + /* Wire up the splitters for the MPC IRQs */ + for (i = 0; i < IOTS_NUM_EXP_MPC + info->sram_banks; i++) { + SplitIRQ *splitter = &s->mpc_irq_splitter[i]; + DeviceState *dev_splitter = DEVICE(splitter); + + object_property_set_int(OBJECT(splitter), 2, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(splitter), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + if (i < IOTS_NUM_EXP_MPC) { + /* Splitter input is from GPIO input line */ + s->mpcexp_status_in[i] = qdev_get_gpio_in(dev_splitter, 0); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_secctl, + "mpcexp_status", i)); + } else { + /* Splitter input is from our own MPC */ + qdev_connect_gpio_out_named(DEVICE(&s->mpc[i - IOTS_NUM_EXP_MPC]), + "irq", 0, + qdev_get_gpio_in(dev_splitter, 0)); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_secctl, + "mpc_status", 0)); + } + + qdev_connect_gpio_out(dev_splitter, 1, + qdev_get_gpio_in(DEVICE(&s->mpc_irq_orgate), i)); + } + /* Create GPIO inputs which will pass the line state for our + * mpcexp_irq inputs to the correct splitter devices. + */ + qdev_init_gpio_in_named(dev, armsse_mpcexp_status, "mpcexp_status", + IOTS_NUM_EXP_MPC); + + armsse_forward_sec_resp_cfg(s); + + /* Forward the MSC related signals */ + qdev_pass_gpios(dev_secctl, dev, "mscexp_status"); + qdev_pass_gpios(dev_secctl, dev, "mscexp_clear"); + qdev_pass_gpios(dev_secctl, dev, "mscexp_ns"); + qdev_connect_gpio_out_named(dev_secctl, "msc_irq", 0, + armsse_get_common_irq_in(s, 11)); + + /* + * Expose our container region to the board model; this corresponds + * to the AHB Slave Expansion ports which allow bus master devices + * (eg DMA controllers) in the board model to make transactions into + * devices in the ARMSSE. + */ + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container); + + system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq; +} + +static void armsse_idau_check(IDAUInterface *ii, uint32_t address, + int *iregion, bool *exempt, bool *ns, bool *nsc) +{ + /* + * For ARMSSE systems the IDAU responses are simple logical functions + * of the address bits. The NSC attribute is guest-adjustable via the + * NSCCFG register in the security controller. + */ + ARMSSE *s = ARMSSE(ii); + int region = extract32(address, 28, 4); + + *ns = !(region & 1); + *nsc = (region == 1 && (s->nsccfg & 1)) || (region == 3 && (s->nsccfg & 2)); + /* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */ + *exempt = (address & 0xeff00000) == 0xe0000000; + *iregion = region; +} + +static const VMStateDescription armsse_vmstate = { + .name = "iotkit", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(nsccfg, ARMSSE), + VMSTATE_END_OF_LIST() + } +}; + +static Property armsse_properties[] = { + DEFINE_PROP_LINK("memory", ARMSSE, board_memory, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64), + DEFINE_PROP_UINT32("MAINCLK", ARMSSE, mainclk_frq, 0), + DEFINE_PROP_UINT32("SRAM_ADDR_WIDTH", ARMSSE, sram_addr_width, 15), + DEFINE_PROP_END_OF_LIST() +}; + +static void armsse_reset(DeviceState *dev) +{ + ARMSSE *s = ARMSSE(dev); + + s->nsccfg = 0; +} + +static void armsse_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(klass); + ARMSSEClass *asc = ARMSSE_CLASS(klass); + + dc->realize = armsse_realize; + dc->vmsd = &armsse_vmstate; + dc->props = armsse_properties; + dc->reset = armsse_reset; + iic->check = armsse_idau_check; + asc->info = data; +} + +static const TypeInfo armsse_info = { + .name = TYPE_ARMSSE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ARMSSE), + .instance_init = armsse_init, + .abstract = true, + .interfaces = (InterfaceInfo[]) { + { TYPE_IDAU_INTERFACE }, + { } + } +}; + +static void armsse_register_types(void) +{ + int i; + + type_register_static(&armsse_info); + + for (i = 0; i < ARRAY_SIZE(armsse_variants); i++) { + TypeInfo ti = { + .name = armsse_variants[i].name, + .parent = TYPE_ARMSSE, + .class_init = armsse_class_init, + .class_data = (void *)&armsse_variants[i], + }; + type_register(&ti); + } +} + +type_init(armsse_register_types); diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index f444652..adae11e 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -158,7 +158,12 @@ static void armv7m_realize(DeviceState *dev, Error **errp) memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); - s->cpu = ARM_CPU(object_new(s->cpu_type)); + s->cpu = ARM_CPU(object_new_with_props(s->cpu_type, OBJECT(s), "cpu", + &err, NULL)); + if (err != NULL) { + error_propagate(errp, err); + return; + } object_property_set_link(OBJECT(s->cpu), OBJECT(&s->container), "memory", &error_abort); @@ -177,11 +182,21 @@ static void armv7m_realize(DeviceState *dev, Error **errp) return; } } + if (object_property_find(OBJECT(s->cpu), "start-powered-off", NULL)) { + object_property_set_bool(OBJECT(s->cpu), s->start_powered_off, + "start-powered-off", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + } - /* Tell the CPU where the NVIC is; it will fail realize if it doesn't - * have one. + /* + * Tell the CPU where the NVIC is; it will fail realize if it doesn't + * have one. Similarly, tell the NVIC where its CPU is. */ s->cpu->env.nvic = &s->nvic; + s->nvic.cpu = s->cpu; object_property_set_bool(OBJECT(s->cpu), true, "realized", &err); if (err != NULL) { @@ -243,6 +258,8 @@ static Property armv7m_properties[] = { DEFINE_PROP_LINK("idau", ARMv7MState, idau, TYPE_IDAU_INTERFACE, Object *), DEFINE_PROP_UINT32("init-svtor", ARMv7MState, init_svtor, 0), DEFINE_PROP_BOOL("enable-bitband", ARMv7MState, enable_bitband, false), + DEFINE_PROP_BOOL("start-powered-off", ARMv7MState, start_powered_off, + false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/arm/boot.c b/hw/arm/boot.c index c7a67af..05762d0 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -697,10 +697,6 @@ static void do_cpu_reset(void *opaque) g_assert_not_reached(); } - if (!env->aarch64) { - env->thumb = info->entry & 1; - entry &= 0xfffffffe; - } cpu_set_pc(cs, entry); } else { /* If we are booting Linux then we need to check whether we are diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c deleted file mode 100644 index 8742200..0000000 --- a/hw/arm/iotkit.c +++ /dev/null @@ -1,759 +0,0 @@ -/* - * Arm IoT Kit - * - * 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/arm/iotkit.h" -#include "hw/arm/arm.h" - -/* Clock frequency in HZ of the 32KHz "slow clock" */ -#define S32KCLK (32 * 1000) - -/* Create an alias region of @size bytes starting at @base - * which mirrors the memory starting at @orig. - */ -static void make_alias(IoTKit *s, MemoryRegion *mr, const char *name, - hwaddr base, hwaddr size, hwaddr orig) -{ - memory_region_init_alias(mr, NULL, name, &s->container, orig, size); - /* The alias is even lower priority than unimplemented_device regions */ - memory_region_add_subregion_overlap(&s->container, base, mr, -1500); -} - -static void irq_status_forwarder(void *opaque, int n, int level) -{ - qemu_irq destirq = opaque; - - qemu_set_irq(destirq, level); -} - -static void nsccfg_handler(void *opaque, int n, int level) -{ - IoTKit *s = IOTKIT(opaque); - - s->nsccfg = level; -} - -static void iotkit_forward_ppc(IoTKit *s, const char *ppcname, int ppcnum) -{ - /* Each of the 4 AHB and 4 APB PPCs that might be present in a - * system using the IoTKit has a collection of control lines which - * are provided by the security controller and which we want to - * expose as control lines on the IoTKit device itself, so the - * code using the IoTKit can wire them up to the PPCs. - */ - SplitIRQ *splitter = &s->ppc_irq_splitter[ppcnum]; - DeviceState *iotkitdev = DEVICE(s); - DeviceState *dev_secctl = DEVICE(&s->secctl); - DeviceState *dev_splitter = DEVICE(splitter); - char *name; - - name = g_strdup_printf("%s_nonsec", ppcname); - qdev_pass_gpios(dev_secctl, iotkitdev, name); - g_free(name); - name = g_strdup_printf("%s_ap", ppcname); - qdev_pass_gpios(dev_secctl, iotkitdev, name); - g_free(name); - name = g_strdup_printf("%s_irq_enable", ppcname); - qdev_pass_gpios(dev_secctl, iotkitdev, name); - g_free(name); - name = g_strdup_printf("%s_irq_clear", ppcname); - qdev_pass_gpios(dev_secctl, iotkitdev, name); - g_free(name); - - /* irq_status is a little more tricky, because we need to - * split it so we can send it both to the security controller - * and to our OR gate for the NVIC interrupt line. - * Connect up the splitter's outputs, and create a GPIO input - * which will pass the line state to the input splitter. - */ - name = g_strdup_printf("%s_irq_status", ppcname); - qdev_connect_gpio_out(dev_splitter, 0, - qdev_get_gpio_in_named(dev_secctl, - name, 0)); - qdev_connect_gpio_out(dev_splitter, 1, - qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), ppcnum)); - s->irq_status_in[ppcnum] = qdev_get_gpio_in(dev_splitter, 0); - qdev_init_gpio_in_named_with_opaque(iotkitdev, irq_status_forwarder, - s->irq_status_in[ppcnum], name, 1); - g_free(name); -} - -static void iotkit_forward_sec_resp_cfg(IoTKit *s) -{ - /* Forward the 3rd output from the splitter device as a - * named GPIO output of the iotkit object. - */ - DeviceState *dev = DEVICE(s); - DeviceState *dev_splitter = DEVICE(&s->sec_resp_splitter); - - qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); - s->sec_resp_cfg_in = qemu_allocate_irq(irq_status_forwarder, - s->sec_resp_cfg, 1); - qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in); -} - -static void iotkit_init(Object *obj) -{ - IoTKit *s = IOTKIT(obj); - int i; - - memory_region_init(&s->container, obj, "iotkit-container", UINT64_MAX); - - sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), - TYPE_ARMV7M); - qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type", - ARM_CPU_TYPE_NAME("cortex-m33")); - - sysbus_init_child_obj(obj, "secctl", &s->secctl, sizeof(s->secctl), - TYPE_IOTKIT_SECCTL); - sysbus_init_child_obj(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0), - TYPE_TZ_PPC); - sysbus_init_child_obj(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), - TYPE_TZ_PPC); - sysbus_init_child_obj(obj, "mpc", &s->mpc, sizeof(s->mpc), TYPE_TZ_MPC); - object_initialize_child(obj, "mpc-irq-orgate", &s->mpc_irq_orgate, - sizeof(s->mpc_irq_orgate), TYPE_OR_IRQ, - &error_abort, NULL); - - for (i = 0; i < ARRAY_SIZE(s->mpc_irq_splitter); i++) { - char *name = g_strdup_printf("mpc-irq-splitter-%d", i); - SplitIRQ *splitter = &s->mpc_irq_splitter[i]; - - object_initialize_child(obj, name, splitter, sizeof(*splitter), - TYPE_SPLIT_IRQ, &error_abort, NULL); - g_free(name); - } - sysbus_init_child_obj(obj, "timer0", &s->timer0, sizeof(s->timer0), - TYPE_CMSDK_APB_TIMER); - sysbus_init_child_obj(obj, "timer1", &s->timer1, sizeof(s->timer1), - TYPE_CMSDK_APB_TIMER); - sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), - TYPE_CMSDK_APB_TIMER); - sysbus_init_child_obj(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer), - TYPE_CMSDK_APB_DUALTIMER); - sysbus_init_child_obj(obj, "s32kwatchdog", &s->s32kwatchdog, - sizeof(s->s32kwatchdog), TYPE_CMSDK_APB_WATCHDOG); - sysbus_init_child_obj(obj, "nswatchdog", &s->nswatchdog, - sizeof(s->nswatchdog), TYPE_CMSDK_APB_WATCHDOG); - sysbus_init_child_obj(obj, "swatchdog", &s->swatchdog, - sizeof(s->swatchdog), TYPE_CMSDK_APB_WATCHDOG); - sysbus_init_child_obj(obj, "iotkit-sysctl", &s->sysctl, - sizeof(s->sysctl), TYPE_IOTKIT_SYSCTL); - sysbus_init_child_obj(obj, "iotkit-sysinfo", &s->sysinfo, - sizeof(s->sysinfo), TYPE_IOTKIT_SYSINFO); - object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate, - sizeof(s->nmi_orgate), TYPE_OR_IRQ, - &error_abort, NULL); - object_initialize_child(obj, "ppc-irq-orgate", &s->ppc_irq_orgate, - sizeof(s->ppc_irq_orgate), TYPE_OR_IRQ, - &error_abort, NULL); - object_initialize_child(obj, "sec-resp-splitter", &s->sec_resp_splitter, - sizeof(s->sec_resp_splitter), TYPE_SPLIT_IRQ, - &error_abort, NULL); - for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { - char *name = g_strdup_printf("ppc-irq-splitter-%d", i); - SplitIRQ *splitter = &s->ppc_irq_splitter[i]; - - object_initialize_child(obj, name, splitter, sizeof(*splitter), - TYPE_SPLIT_IRQ, &error_abort, NULL); - g_free(name); - } -} - -static void iotkit_exp_irq(void *opaque, int n, int level) -{ - IoTKit *s = IOTKIT(opaque); - - qemu_set_irq(s->exp_irqs[n], level); -} - -static void iotkit_mpcexp_status(void *opaque, int n, int level) -{ - IoTKit *s = IOTKIT(opaque); - qemu_set_irq(s->mpcexp_status_in[n], level); -} - -static void iotkit_realize(DeviceState *dev, Error **errp) -{ - IoTKit *s = IOTKIT(dev); - int i; - MemoryRegion *mr; - Error *err = NULL; - SysBusDevice *sbd_apb_ppc0; - SysBusDevice *sbd_secctl; - DeviceState *dev_apb_ppc0; - DeviceState *dev_apb_ppc1; - DeviceState *dev_secctl; - DeviceState *dev_splitter; - - if (!s->board_memory) { - error_setg(errp, "memory property was not set"); - return; - } - - if (!s->mainclk_frq) { - error_setg(errp, "MAINCLK property was not set"); - return; - } - - /* Handling of which devices should be available only to secure - * code is usually done differently for M profile than for A profile. - * Instead of putting some devices only into the secure address space, - * devices exist in both address spaces but with hard-wired security - * permissions that will cause the CPU to fault for non-secure accesses. - * - * The IoTKit has an IDAU (Implementation Defined Access Unit), - * which specifies hard-wired security permissions for different - * areas of the physical address space. For the IoTKit IDAU, the - * top 4 bits of the physical address are the IDAU region ID, and - * if bit 28 (ie the lowest bit of the ID) is 0 then this is an NS - * region, otherwise it is an S region. - * - * The various devices and RAMs are generally all mapped twice, - * once into a region that the IDAU defines as secure and once - * into a non-secure region. They sit behind either a Memory - * Protection Controller (for RAM) or a Peripheral Protection - * Controller (for devices), which allow a more fine grained - * configuration of whether non-secure accesses are permitted. - * - * (The other place that guest software can configure security - * permissions is in the architected SAU (Security Attribution - * Unit), which is entirely inside the CPU. The IDAU can upgrade - * the security attributes for a region to more restrictive than - * the SAU specifies, but cannot downgrade them.) - * - * 0x10000000..0x1fffffff alias of 0x00000000..0x0fffffff - * 0x20000000..0x2007ffff 32KB FPGA block RAM - * 0x30000000..0x3fffffff alias of 0x20000000..0x2fffffff - * 0x40000000..0x4000ffff base peripheral region 1 - * 0x40010000..0x4001ffff CPU peripherals (none for IoTKit) - * 0x40020000..0x4002ffff system control element peripherals - * 0x40080000..0x400fffff base peripheral region 2 - * 0x50000000..0x5fffffff alias of 0x40000000..0x4fffffff - */ - - memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); - - qdev_prop_set_uint32(DEVICE(&s->armv7m), "num-irq", s->exp_numirq + 32); - /* In real hardware the initial Secure VTOR is set from the INITSVTOR0 - * register in the IoT Kit System Control Register block, and the - * initial value of that is in turn specifiable by the FPGA that - * instantiates the IoT Kit. In QEMU we don't implement this wrinkle, - * and simply set the CPU's init-svtor to the IoT Kit default value. - */ - qdev_prop_set_uint32(DEVICE(&s->armv7m), "init-svtor", 0x10000000); - object_property_set_link(OBJECT(&s->armv7m), OBJECT(&s->container), - "memory", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_link(OBJECT(&s->armv7m), OBJECT(s), "idau", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(OBJECT(&s->armv7m), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - - /* Connect our EXP_IRQ GPIOs to the NVIC's lines 32 and up. */ - s->exp_irqs = g_new(qemu_irq, s->exp_numirq); - for (i = 0; i < s->exp_numirq; i++) { - s->exp_irqs[i] = qdev_get_gpio_in(DEVICE(&s->armv7m), i + 32); - } - qdev_init_gpio_in_named(dev, iotkit_exp_irq, "EXP_IRQ", s->exp_numirq); - - /* Set up the big aliases first */ - make_alias(s, &s->alias1, "alias 1", 0x10000000, 0x10000000, 0x00000000); - make_alias(s, &s->alias2, "alias 2", 0x30000000, 0x10000000, 0x20000000); - /* The 0x50000000..0x5fffffff region is not a pure alias: it has - * a few extra devices that only appear there (generally the - * control interfaces for the protection controllers). - * We implement this by mapping those devices over the top of this - * alias MR at a higher priority. - */ - make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000); - - - /* Security controller */ - object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sbd_secctl = SYS_BUS_DEVICE(&s->secctl); - dev_secctl = DEVICE(&s->secctl); - sysbus_mmio_map(sbd_secctl, 0, 0x50080000); - sysbus_mmio_map(sbd_secctl, 1, 0x40080000); - - s->nsc_cfg_in = qemu_allocate_irq(nsccfg_handler, s, 1); - qdev_connect_gpio_out_named(dev_secctl, "nsc_cfg", 0, s->nsc_cfg_in); - - /* The sec_resp_cfg output from the security controller must be split into - * multiple lines, one for each of the PPCs within the IoTKit and one - * that will be an output from the IoTKit to the system. - */ - object_property_set_int(OBJECT(&s->sec_resp_splitter), 3, - "num-lines", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(OBJECT(&s->sec_resp_splitter), true, - "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - dev_splitter = DEVICE(&s->sec_resp_splitter); - qdev_connect_gpio_out_named(dev_secctl, "sec_resp_cfg", 0, - qdev_get_gpio_in(dev_splitter, 0)); - - /* This RAM lives behind the Memory Protection Controller */ - memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_link(OBJECT(&s->mpc), OBJECT(&s->sram0), - "downstream", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(OBJECT(&s->mpc), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - /* Map the upstream end of the MPC into the right place... */ - memory_region_add_subregion(&s->container, 0x20000000, - sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc), - 1)); - /* ...and its register interface */ - memory_region_add_subregion(&s->container, 0x50083000, - sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc), - 0)); - - /* We must OR together lines from the MPC splitters to go to the NVIC */ - object_property_set_int(OBJECT(&s->mpc_irq_orgate), - IOTS_NUM_EXP_MPC + IOTS_NUM_MPC, "num-lines", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(OBJECT(&s->mpc_irq_orgate), true, - "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - qdev_connect_gpio_out(DEVICE(&s->mpc_irq_orgate), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 9)); - - /* Devices behind APB PPC0: - * 0x40000000: timer0 - * 0x40001000: timer1 - * 0x40002000: dual timer - * We must configure and realize each downstream device and connect - * it to the appropriate PPC port; then we can realize the PPC and - * map its upstream ends to the right place in the container. - */ - qdev_prop_set_uint32(DEVICE(&s->timer0), "pclk-frq", s->mainclk_frq); - object_property_set_bool(OBJECT(&s->timer0), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer0), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 3)); - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer0), 0); - object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[0]", &err); - if (err) { - error_propagate(errp, err); - return; - } - - qdev_prop_set_uint32(DEVICE(&s->timer1), "pclk-frq", s->mainclk_frq); - object_property_set_bool(OBJECT(&s->timer1), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer1), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 4)); - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer1), 0); - object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[1]", &err); - if (err) { - error_propagate(errp, err); - return; - } - - - qdev_prop_set_uint32(DEVICE(&s->dualtimer), "pclk-frq", s->mainclk_frq); - object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_connect_irq(SYS_BUS_DEVICE(&s->dualtimer), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 5)); - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0); - object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err); - if (err) { - error_propagate(errp, err); - return; - } - - object_property_set_bool(OBJECT(&s->apb_ppc0), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - - sbd_apb_ppc0 = SYS_BUS_DEVICE(&s->apb_ppc0); - dev_apb_ppc0 = DEVICE(&s->apb_ppc0); - - mr = sysbus_mmio_get_region(sbd_apb_ppc0, 0); - memory_region_add_subregion(&s->container, 0x40000000, mr); - mr = sysbus_mmio_get_region(sbd_apb_ppc0, 1); - memory_region_add_subregion(&s->container, 0x40001000, mr); - mr = sysbus_mmio_get_region(sbd_apb_ppc0, 2); - memory_region_add_subregion(&s->container, 0x40002000, mr); - for (i = 0; i < IOTS_APB_PPC0_NUM_PORTS; i++) { - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_nonsec", i, - qdev_get_gpio_in_named(dev_apb_ppc0, - "cfg_nonsec", i)); - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_ap", i, - qdev_get_gpio_in_named(dev_apb_ppc0, - "cfg_ap", i)); - } - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_enable", 0, - qdev_get_gpio_in_named(dev_apb_ppc0, - "irq_enable", 0)); - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_clear", 0, - qdev_get_gpio_in_named(dev_apb_ppc0, - "irq_clear", 0)); - qdev_connect_gpio_out(dev_splitter, 0, - qdev_get_gpio_in_named(dev_apb_ppc0, - "cfg_sec_resp", 0)); - - /* All the PPC irq lines (from the 2 internal PPCs and the 8 external - * ones) are sent individually to the security controller, and also - * ORed together to give a single combined PPC interrupt to the NVIC. - */ - object_property_set_int(OBJECT(&s->ppc_irq_orgate), - NUM_PPCS, "num-lines", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(OBJECT(&s->ppc_irq_orgate), true, - "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - qdev_connect_gpio_out(DEVICE(&s->ppc_irq_orgate), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 10)); - - /* 0x40010000 .. 0x4001ffff: private CPU region: unused in IoTKit */ - - /* 0x40020000 .. 0x4002ffff : IoTKit system control peripheral region */ - /* Devices behind APB PPC1: - * 0x4002f000: S32K timer - */ - qdev_prop_set_uint32(DEVICE(&s->s32ktimer), "pclk-frq", S32KCLK); - object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32ktimer), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 2)); - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0); - object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err); - if (err) { - error_propagate(errp, err); - return; - } - - object_property_set_bool(OBJECT(&s->apb_ppc1), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->apb_ppc1), 0); - memory_region_add_subregion(&s->container, 0x4002f000, mr); - - dev_apb_ppc1 = DEVICE(&s->apb_ppc1); - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_nonsec", 0, - qdev_get_gpio_in_named(dev_apb_ppc1, - "cfg_nonsec", 0)); - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_ap", 0, - qdev_get_gpio_in_named(dev_apb_ppc1, - "cfg_ap", 0)); - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_enable", 0, - qdev_get_gpio_in_named(dev_apb_ppc1, - "irq_enable", 0)); - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_clear", 0, - qdev_get_gpio_in_named(dev_apb_ppc1, - "irq_clear", 0)); - qdev_connect_gpio_out(dev_splitter, 1, - qdev_get_gpio_in_named(dev_apb_ppc1, - "cfg_sec_resp", 0)); - - object_property_set_bool(OBJECT(&s->sysinfo), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - /* System information registers */ - sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, 0x40020000); - /* System control registers */ - object_property_set_bool(OBJECT(&s->sysctl), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctl), 0, 0x50021000); - - /* This OR gate wires together outputs from the secure watchdogs to NMI */ - object_property_set_int(OBJECT(&s->nmi_orgate), 2, "num-lines", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(OBJECT(&s->nmi_orgate), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - qdev_connect_gpio_out(DEVICE(&s->nmi_orgate), 0, - qdev_get_gpio_in_named(DEVICE(&s->armv7m), "NMI", 0)); - - qdev_prop_set_uint32(DEVICE(&s->s32kwatchdog), "wdogclk-frq", S32KCLK); - object_property_set_bool(OBJECT(&s->s32kwatchdog), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, - qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 0)); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, 0x5002e000); - - /* 0x40080000 .. 0x4008ffff : IoTKit second Base peripheral region */ - - qdev_prop_set_uint32(DEVICE(&s->nswatchdog), "wdogclk-frq", s->mainclk_frq); - object_property_set_bool(OBJECT(&s->nswatchdog), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_connect_irq(SYS_BUS_DEVICE(&s->nswatchdog), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 1)); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->nswatchdog), 0, 0x40081000); - - qdev_prop_set_uint32(DEVICE(&s->swatchdog), "wdogclk-frq", s->mainclk_frq); - object_property_set_bool(OBJECT(&s->swatchdog), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_connect_irq(SYS_BUS_DEVICE(&s->swatchdog), 0, - qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 1)); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->swatchdog), 0, 0x50081000); - - for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { - Object *splitter = OBJECT(&s->ppc_irq_splitter[i]); - - object_property_set_int(splitter, 2, "num-lines", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(splitter, true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - } - - for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) { - char *ppcname = g_strdup_printf("ahb_ppcexp%d", i); - - iotkit_forward_ppc(s, ppcname, i); - g_free(ppcname); - } - - for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) { - char *ppcname = g_strdup_printf("apb_ppcexp%d", i); - - iotkit_forward_ppc(s, ppcname, i + IOTS_NUM_AHB_EXP_PPC); - g_free(ppcname); - } - - for (i = NUM_EXTERNAL_PPCS; i < NUM_PPCS; i++) { - /* Wire up IRQ splitter for internal PPCs */ - DeviceState *devs = DEVICE(&s->ppc_irq_splitter[i]); - char *gpioname = g_strdup_printf("apb_ppc%d_irq_status", - i - NUM_EXTERNAL_PPCS); - TZPPC *ppc = (i == NUM_EXTERNAL_PPCS) ? &s->apb_ppc0 : &s->apb_ppc1; - - qdev_connect_gpio_out(devs, 0, - qdev_get_gpio_in_named(dev_secctl, gpioname, 0)); - qdev_connect_gpio_out(devs, 1, - qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), i)); - qdev_connect_gpio_out_named(DEVICE(ppc), "irq", 0, - qdev_get_gpio_in(devs, 0)); - g_free(gpioname); - } - - /* Wire up the splitters for the MPC IRQs */ - for (i = 0; i < IOTS_NUM_EXP_MPC + IOTS_NUM_MPC; i++) { - SplitIRQ *splitter = &s->mpc_irq_splitter[i]; - DeviceState *dev_splitter = DEVICE(splitter); - - object_property_set_int(OBJECT(splitter), 2, "num-lines", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(OBJECT(splitter), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - - if (i < IOTS_NUM_EXP_MPC) { - /* Splitter input is from GPIO input line */ - s->mpcexp_status_in[i] = qdev_get_gpio_in(dev_splitter, 0); - qdev_connect_gpio_out(dev_splitter, 0, - qdev_get_gpio_in_named(dev_secctl, - "mpcexp_status", i)); - } else { - /* Splitter input is from our own MPC */ - qdev_connect_gpio_out_named(DEVICE(&s->mpc), "irq", 0, - qdev_get_gpio_in(dev_splitter, 0)); - qdev_connect_gpio_out(dev_splitter, 0, - qdev_get_gpio_in_named(dev_secctl, - "mpc_status", 0)); - } - - qdev_connect_gpio_out(dev_splitter, 1, - qdev_get_gpio_in(DEVICE(&s->mpc_irq_orgate), i)); - } - /* Create GPIO inputs which will pass the line state for our - * mpcexp_irq inputs to the correct splitter devices. - */ - qdev_init_gpio_in_named(dev, iotkit_mpcexp_status, "mpcexp_status", - IOTS_NUM_EXP_MPC); - - iotkit_forward_sec_resp_cfg(s); - - /* Forward the MSC related signals */ - qdev_pass_gpios(dev_secctl, dev, "mscexp_status"); - qdev_pass_gpios(dev_secctl, dev, "mscexp_clear"); - qdev_pass_gpios(dev_secctl, dev, "mscexp_ns"); - qdev_connect_gpio_out_named(dev_secctl, "msc_irq", 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 11)); - - /* - * Expose our container region to the board model; this corresponds - * to the AHB Slave Expansion ports which allow bus master devices - * (eg DMA controllers) in the board model to make transactions into - * devices in the IoTKit. - */ - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container); - - system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq; -} - -static void iotkit_idau_check(IDAUInterface *ii, uint32_t address, - int *iregion, bool *exempt, bool *ns, bool *nsc) -{ - /* For IoTKit systems the IDAU responses are simple logical functions - * of the address bits. The NSC attribute is guest-adjustable via the - * NSCCFG register in the security controller. - */ - IoTKit *s = IOTKIT(ii); - int region = extract32(address, 28, 4); - - *ns = !(region & 1); - *nsc = (region == 1 && (s->nsccfg & 1)) || (region == 3 && (s->nsccfg & 2)); - /* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */ - *exempt = (address & 0xeff00000) == 0xe0000000; - *iregion = region; -} - -static const VMStateDescription iotkit_vmstate = { - .name = "iotkit", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(nsccfg, IoTKit), - VMSTATE_END_OF_LIST() - } -}; - -static Property iotkit_properties[] = { - DEFINE_PROP_LINK("memory", IoTKit, board_memory, TYPE_MEMORY_REGION, - MemoryRegion *), - DEFINE_PROP_UINT32("EXP_NUMIRQ", IoTKit, exp_numirq, 64), - DEFINE_PROP_UINT32("MAINCLK", IoTKit, mainclk_frq, 0), - DEFINE_PROP_END_OF_LIST() -}; - -static void iotkit_reset(DeviceState *dev) -{ - IoTKit *s = IOTKIT(dev); - - s->nsccfg = 0; -} - -static void iotkit_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(klass); - - dc->realize = iotkit_realize; - dc->vmsd = &iotkit_vmstate; - dc->props = iotkit_properties; - dc->reset = iotkit_reset; - iic->check = iotkit_idau_check; -} - -static const TypeInfo iotkit_info = { - .name = TYPE_IOTKIT, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IoTKit), - .instance_init = iotkit_init, - .class_init = iotkit_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_IDAU_INTERFACE }, - { } - } -}; - -static void iotkit_register_types(void) -{ - type_register_static(&iotkit_info); -} - -type_init(iotkit_register_types); diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 82b1d02..f5f0b0e 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -15,6 +15,7 @@ * as seen by the guest depend significantly on the FPGA image. * This source file covers the following FPGA images, for TrustZone cores: * "mps2-an505" -- Cortex-M33 as documented in ARM Application Note AN505 + * "mps2-an521" -- Dual Cortex-M33 as documented in Application Note AN521 * * Links to the TRM for the board itself and to the various Application * Notes which document the FPGA images can be found here: @@ -24,10 +25,16 @@ * http://infocenter.arm.com/help/topic/com.arm.doc.100112_0200_06_en/versatile_express_cortex_m_prototyping_systems_v2m_mps2_and_v2m_mps2plus_technical_reference_100112_0200_06_en.pdf * Application Note AN505: * http://infocenter.arm.com/help/topic/com.arm.doc.dai0505b/index.html + * Application Note AN521: + * http://infocenter.arm.com/help/topic/com.arm.doc.dai0521c/index.html * * The AN505 defers to the Cortex-M33 processor ARMv8M IoT Kit FVP User Guide * (ARM ECM0601256) for the details of some of the device layout: * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * Similarly, the AN521 uses the SSE-200, and the SSE-200 TRM defines + * most of the device layout: + * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf + * */ #include "qemu/osdep.h" @@ -46,27 +53,31 @@ #include "hw/misc/mps2-fpgaio.h" #include "hw/misc/tz-mpc.h" #include "hw/misc/tz-msc.h" -#include "hw/arm/iotkit.h" +#include "hw/arm/armsse.h" #include "hw/dma/pl080.h" #include "hw/ssi/pl022.h" #include "hw/devices.h" #include "net/net.h" #include "hw/core/split-irq.h" +#define MPS2TZ_NUMIRQ 92 + typedef enum MPS2TZFPGAType { FPGA_AN505, + FPGA_AN521, } MPS2TZFPGAType; typedef struct { MachineClass parent; MPS2TZFPGAType fpga_type; uint32_t scc_id; + const char *armsse_type; } MPS2TZMachineClass; typedef struct { MachineState parent; - IoTKit iotkit; + ARMSSE iotkit; MemoryRegion psram; MemoryRegion ssram[3]; MemoryRegion ssram1_m; @@ -85,10 +96,12 @@ typedef struct { SplitIRQ sec_resp_splitter; qemu_or_irq uart_irq_orgate; DeviceState *lan9118; + SplitIRQ cpu_irq_splitter[MPS2TZ_NUMIRQ]; } MPS2TZMachineState; #define TYPE_MPS2TZ_MACHINE "mps2tz" #define TYPE_MPS2TZ_AN505_MACHINE MACHINE_TYPE_NAME("mps2-an505") +#define TYPE_MPS2TZ_AN521_MACHINE MACHINE_TYPE_NAME("mps2-an521") #define MPS2TZ_MACHINE(obj) \ OBJECT_CHECK(MPS2TZMachineState, obj, TYPE_MPS2TZ_MACHINE) @@ -111,6 +124,23 @@ static void make_ram_alias(MemoryRegion *mr, const char *name, memory_region_add_subregion(get_system_memory(), base, mr); } +static qemu_irq get_sse_irq_in(MPS2TZMachineState *mms, int irqno) +{ + /* Return a qemu_irq which will signal IRQ n to all CPUs in the SSE. */ + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); + + assert(irqno < MPS2TZ_NUMIRQ); + + switch (mmc->fpga_type) { + case FPGA_AN505: + return qdev_get_gpio_in_named(DEVICE(&mms->iotkit), "EXP_IRQ", irqno); + case FPGA_AN521: + return qdev_get_gpio_in(DEVICE(&mms->cpu_irq_splitter[irqno]), 0); + default: + g_assert_not_reached(); + } +} + /* Most of the devices in the AN505 FPGA image sit behind * Peripheral Protection Controllers. These data structures * define the layout of which devices sit behind which PPCs. @@ -161,7 +191,6 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, int txirqno = i * 2 + 1; int combirqno = i + 10; SysBusDevice *s; - DeviceState *iotkitdev = DEVICE(&mms->iotkit); DeviceState *orgate_dev = DEVICE(&mms->uart_irq_orgate); sysbus_init_child_obj(OBJECT(mms), name, uart, sizeof(mms->uart[0]), @@ -170,14 +199,11 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, qdev_prop_set_uint32(DEVICE(uart), "pclk-frq", SYSCLK_FRQ); object_property_set_bool(OBJECT(uart), true, "realized", &error_fatal); s = SYS_BUS_DEVICE(uart); - sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev, - "EXP_IRQ", txirqno)); - sysbus_connect_irq(s, 1, qdev_get_gpio_in_named(iotkitdev, - "EXP_IRQ", rxirqno)); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, txirqno)); + sysbus_connect_irq(s, 1, get_sse_irq_in(mms, rxirqno)); sysbus_connect_irq(s, 2, qdev_get_gpio_in(orgate_dev, i * 2)); sysbus_connect_irq(s, 3, qdev_get_gpio_in(orgate_dev, i * 2 + 1)); - sysbus_connect_irq(s, 4, qdev_get_gpio_in_named(iotkitdev, - "EXP_IRQ", combirqno)); + sysbus_connect_irq(s, 4, get_sse_irq_in(mms, combirqno)); return sysbus_mmio_get_region(SYS_BUS_DEVICE(uart), 0); } @@ -213,7 +239,6 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size) { SysBusDevice *s; - DeviceState *iotkitdev = DEVICE(&mms->iotkit); NICInfo *nd = &nd_table[0]; /* In hardware this is a LAN9220; the LAN9118 is software compatible @@ -225,7 +250,7 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque, qdev_init_nofail(mms->lan9118); s = SYS_BUS_DEVICE(mms->lan9118); - sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 16)); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 16)); return sysbus_mmio_get_region(s, 0); } @@ -315,12 +340,9 @@ static MemoryRegion *make_dma(MPS2TZMachineState *mms, void *opaque, s = SYS_BUS_DEVICE(dma); /* Wire up DMACINTR, DMACINTERR, DMACINTTC */ - sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev, - "EXP_IRQ", 58 + i * 3)); - sysbus_connect_irq(s, 1, qdev_get_gpio_in_named(iotkitdev, - "EXP_IRQ", 56 + i * 3)); - sysbus_connect_irq(s, 2, qdev_get_gpio_in_named(iotkitdev, - "EXP_IRQ", 57 + i * 3)); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 58 + i * 3)); + sysbus_connect_irq(s, 1, get_sse_irq_in(mms, 56 + i * 3)); + sysbus_connect_irq(s, 2, get_sse_irq_in(mms, 57 + i * 3)); g_free(mscname); return sysbus_mmio_get_region(s, 0); @@ -339,21 +361,20 @@ static MemoryRegion *make_spi(MPS2TZMachineState *mms, void *opaque, */ PL022State *spi = opaque; int i = spi - &mms->spi[0]; - DeviceState *iotkitdev = DEVICE(&mms->iotkit); SysBusDevice *s; sysbus_init_child_obj(OBJECT(mms), name, spi, sizeof(mms->spi[0]), TYPE_PL022); object_property_set_bool(OBJECT(spi), true, "realized", &error_fatal); s = SYS_BUS_DEVICE(spi); - sysbus_connect_irq(s, 0, - qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 51 + i)); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 51 + i)); return sysbus_mmio_get_region(s, 0); } static void mps2tz_common_init(MachineState *machine) { MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine); + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); MachineClass *mc = MACHINE_GET_CLASS(machine); MemoryRegion *system_memory = get_system_memory(); DeviceState *iotkitdev; @@ -367,15 +388,42 @@ static void mps2tz_common_init(MachineState *machine) } sysbus_init_child_obj(OBJECT(machine), "iotkit", &mms->iotkit, - sizeof(mms->iotkit), TYPE_IOTKIT); + sizeof(mms->iotkit), mmc->armsse_type); iotkitdev = DEVICE(&mms->iotkit); object_property_set_link(OBJECT(&mms->iotkit), OBJECT(system_memory), "memory", &error_abort); - qdev_prop_set_uint32(iotkitdev, "EXP_NUMIRQ", 92); + qdev_prop_set_uint32(iotkitdev, "EXP_NUMIRQ", MPS2TZ_NUMIRQ); qdev_prop_set_uint32(iotkitdev, "MAINCLK", SYSCLK_FRQ); object_property_set_bool(OBJECT(&mms->iotkit), true, "realized", &error_fatal); + /* + * The AN521 needs us to create splitters to feed the IRQ inputs + * for each CPU in the SSE-200 from each device in the board. + */ + if (mmc->fpga_type == FPGA_AN521) { + for (i = 0; i < MPS2TZ_NUMIRQ; i++) { + char *name = g_strdup_printf("mps2-irq-splitter%d", i); + SplitIRQ *splitter = &mms->cpu_irq_splitter[i]; + + object_initialize_child(OBJECT(machine), name, + splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_fatal, NULL); + g_free(name); + + object_property_set_int(OBJECT(splitter), 2, "num-lines", + &error_fatal); + object_property_set_bool(OBJECT(splitter), true, "realized", + &error_fatal); + qdev_connect_gpio_out(DEVICE(splitter), 0, + qdev_get_gpio_in_named(DEVICE(&mms->iotkit), + "EXP_IRQ", i)); + qdev_connect_gpio_out(DEVICE(splitter), 1, + qdev_get_gpio_in_named(DEVICE(&mms->iotkit), + "EXP_CPU1_IRQ", i)); + } + } + /* The sec_resp_cfg output from the IoTKit must be split into multiple * lines, one for each of the PPCs we create here, plus one per MSC. */ @@ -426,7 +474,7 @@ static void mps2tz_common_init(MachineState *machine) object_property_set_bool(OBJECT(&mms->uart_irq_orgate), true, "realized", &error_fatal); qdev_connect_gpio_out(DEVICE(&mms->uart_irq_orgate), 0, - qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 15)); + get_sse_irq_in(mms, 15)); /* Most of the devices in the FPGA are behind Peripheral Protection * Controllers. The required order for initializing things is: @@ -593,7 +641,6 @@ static void mps2tz_class_init(ObjectClass *oc, void *data) IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(oc); mc->init = mps2tz_common_init; - mc->max_cpus = 1; iic->check = mps2_tz_idau_check; } @@ -603,9 +650,28 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data) MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); mc->desc = "ARM MPS2 with AN505 FPGA image for Cortex-M33"; + mc->default_cpus = 1; + mc->min_cpus = mc->default_cpus; + mc->max_cpus = mc->default_cpus; mmc->fpga_type = FPGA_AN505; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); mmc->scc_id = 0x41045050; + mmc->armsse_type = TYPE_IOTKIT; +} + +static void mps2tz_an521_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); + + mc->desc = "ARM MPS2 with AN521 FPGA image for dual Cortex-M33"; + mc->default_cpus = 2; + mc->min_cpus = mc->default_cpus; + mc->max_cpus = mc->default_cpus; + mmc->fpga_type = FPGA_AN521; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); + mmc->scc_id = 0x41045210; + mmc->armsse_type = TYPE_SSE200; } static const TypeInfo mps2tz_info = { @@ -627,10 +693,17 @@ static const TypeInfo mps2tz_an505_info = { .class_init = mps2tz_an505_class_init, }; +static const TypeInfo mps2tz_an521_info = { + .name = TYPE_MPS2TZ_AN521_MACHINE, + .parent = TYPE_MPS2TZ_MACHINE, + .class_init = mps2tz_an521_class_init, +}; + static void mps2tz_machine_init(void) { type_register_static(&mps2tz_info); type_register_static(&mps2tz_an505_info); + type_register_static(&mps2tz_an521_info); } type_init(mps2tz_machine_init); diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index 1630c27..bbaf050 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -29,8 +29,10 @@ * are supported in the future, add a sub-class of NRF51SoC for * the specific variants */ -#define NRF51822_FLASH_SIZE (256 * NRF51_PAGE_SIZE) -#define NRF51822_SRAM_SIZE (16 * NRF51_PAGE_SIZE) +#define NRF51822_FLASH_PAGES 256 +#define NRF51822_SRAM_PAGES 16 +#define NRF51822_FLASH_SIZE (NRF51822_FLASH_PAGES * NRF51_PAGE_SIZE) +#define NRF51822_SRAM_SIZE (NRF51822_SRAM_PAGES * NRF51_PAGE_SIZE) #define BASE_TO_IRQ(base) ((base >> 12) & 0x1F) @@ -81,15 +83,8 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); - memory_region_init_rom(&s->flash, OBJECT(s), "nrf51.flash", s->flash_size, - &err); - if (err) { - error_propagate(errp, err); - return; - } - memory_region_add_subregion(&s->container, NRF51_FLASH_BASE, &s->flash); - - memory_region_init_ram(&s->sram, NULL, "nrf51.sram", s->sram_size, &err); + memory_region_init_ram(&s->sram, OBJECT(s), "nrf51.sram", s->sram_size, + &err); if (err) { error_propagate(errp, err); return; @@ -121,6 +116,29 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) qdev_get_gpio_in(DEVICE(&s->cpu), BASE_TO_IRQ(NRF51_RNG_BASE))); + /* UICR, FICR, NVMC, FLASH */ + object_property_set_uint(OBJECT(&s->nvm), s->flash_size, "flash-size", + &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->nvm), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 0); + memory_region_add_subregion_overlap(&s->container, NRF51_NVMC_BASE, mr, 0); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 1); + memory_region_add_subregion_overlap(&s->container, NRF51_FICR_BASE, mr, 0); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 2); + memory_region_add_subregion_overlap(&s->container, NRF51_UICR_BASE, mr, 0); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 3); + memory_region_add_subregion_overlap(&s->container, NRF51_FLASH_BASE, mr, 0); + /* GPIO */ object_property_set_bool(OBJECT(&s->gpio), true, "realized", &err); if (err) { @@ -158,8 +176,6 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("nrf51_soc.io", NRF51_IOMEM_BASE, NRF51_IOMEM_SIZE); - create_unimplemented_device("nrf51_soc.ficr", NRF51_FICR_BASE, - NRF51_FICR_SIZE); create_unimplemented_device("nrf51_soc.private", NRF51_PRIVATE_BASE, NRF51_PRIVATE_SIZE); } @@ -186,6 +202,8 @@ static void nrf51_soc_init(Object *obj) sysbus_init_child_obj(obj, "rng", &s->rng, sizeof(s->rng), TYPE_NRF51_RNG); + sysbus_init_child_obj(obj, "nvm", &s->nvm, sizeof(s->nvm), TYPE_NRF51_NVM); + sysbus_init_child_obj(obj, "gpio", &s->gpio, sizeof(s->gpio), TYPE_NRF51_GPIO); diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 0beefb0..790a3d9 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -2274,8 +2274,7 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp) Error *err = NULL; int regionlen; - s->cpu = ARM_CPU(qemu_get_cpu(0)); - + /* The armv7m container object will have set our CPU pointer */ if (!s->cpu || !arm_feature(&s->cpu->env, ARM_FEATURE_M)) { error_setg(errp, "The NVIC can only be used with a Cortex-M CPU"); return; diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 04f3bfa..74c91d2 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -69,6 +69,7 @@ obj-$(CONFIG_TZ_PPC) += tz-ppc.o obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o obj-$(CONFIG_IOTKIT_SYSCTL) += iotkit-sysctl.o obj-$(CONFIG_IOTKIT_SYSINFO) += iotkit-sysinfo.o +obj-$(CONFIG_ARMSSE_CPUID) += armsse-cpuid.o obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_AUX) += auxbus.o diff --git a/hw/misc/armsse-cpuid.c b/hw/misc/armsse-cpuid.c new file mode 100644 index 0000000..7788f6c --- /dev/null +++ b/hw/misc/armsse-cpuid.c @@ -0,0 +1,134 @@ +/* + * ARM SSE-200 CPU_IDENTITY register block + * + * Copyright (c) 2019 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 "CPU_IDENTITY" register block which is part of the + * Arm SSE-200 and documented in + * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf + * + * It consists of one read-only CPUID register (set by QOM property), plus the + * usual ID registers. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/armsse-cpuid.h" + +REG32(CPUID, 0x0) +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) + +/* PID/CID values */ +static const int sysinfo_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x58, 0xb8, 0x0b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static uint64_t armsse_cpuid_read(void *opaque, hwaddr offset, + unsigned size) +{ + ARMSSECPUID *s = ARMSSE_CPUID(opaque); + uint64_t r; + + switch (offset) { + case A_CPUID: + r = s->cpuid; + break; + case A_PID4 ... A_CID3: + r = sysinfo_id[(offset - A_PID4) / 4]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "SSE CPU_IDENTITY read: bad offset 0x%x\n", (int)offset); + r = 0; + break; + } + trace_armsse_cpuid_read(offset, r, size); + return r; +} + +static void armsse_cpuid_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + trace_armsse_cpuid_write(offset, value, size); + + qemu_log_mask(LOG_GUEST_ERROR, + "SSE CPU_IDENTITY: write to RO offset 0x%x\n", (int)offset); +} + +static const MemoryRegionOps armsse_cpuid_ops = { + .read = armsse_cpuid_read, + .write = armsse_cpuid_write, + .endianness = DEVICE_LITTLE_ENDIAN, + /* byte/halfword accesses are just zero-padded on reads and writes */ + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, +}; + +static Property armsse_cpuid_props[] = { + DEFINE_PROP_UINT32("CPUID", ARMSSECPUID, cpuid, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void armsse_cpuid_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + ARMSSECPUID *s = ARMSSE_CPUID(obj); + + memory_region_init_io(&s->iomem, obj, &armsse_cpuid_ops, + s, "armsse-cpuid", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void armsse_cpuid_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + /* + * This device has no guest-modifiable state and so it + * does not need a reset function or VMState. + */ + + dc->props = armsse_cpuid_props; +} + +static const TypeInfo armsse_cpuid_info = { + .name = TYPE_ARMSSE_CPUID, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ARMSSECPUID), + .instance_init = armsse_cpuid_init, + .class_init = armsse_cpuid_class_init, +}; + +static void armsse_cpuid_register_types(void) +{ + type_register_static(&armsse_cpuid_info); +} + +type_init(armsse_cpuid_register_types); diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c index 2222b3e..537601c 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -600,7 +600,7 @@ static void iotkit_secctl_mpc_status(void *opaque, int n, int level) { IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); - s->mpcintstatus = deposit32(s->mpcintstatus, 0, 1, !!level); + s->mpcintstatus = deposit32(s->mpcintstatus, n, 1, !!level); } static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level) @@ -686,7 +686,8 @@ static void iotkit_secctl_init(Object *obj) 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); - qdev_init_gpio_in_named(dev, iotkit_secctl_mpc_status, "mpc_status", 1); + qdev_init_gpio_in_named(dev, iotkit_secctl_mpc_status, "mpc_status", + IOTS_NUM_MPC); qdev_init_gpio_in_named(dev, iotkit_secctl_mpcexp_status, "mpcexp_status", IOTS_NUM_EXP_MPC); diff --git a/hw/misc/iotkit-sysinfo.c b/hw/misc/iotkit-sysinfo.c index 78955bc..026ba94 100644 --- a/hw/misc/iotkit-sysinfo.c +++ b/hw/misc/iotkit-sysinfo.c @@ -51,15 +51,16 @@ static const int sysinfo_id[] = { static uint64_t iotkit_sysinfo_read(void *opaque, hwaddr offset, unsigned size) { + IoTKitSysInfo *s = IOTKIT_SYSINFO(opaque); uint64_t r; switch (offset) { case A_SYS_VERSION: - r = 0x41743; + r = s->sys_version; break; case A_SYS_CONFIG: - r = 0x31; + r = s->sys_config; break; case A_PID4 ... A_CID3: r = sysinfo_id[(offset - A_PID4) / 4]; @@ -94,6 +95,12 @@ static const MemoryRegionOps iotkit_sysinfo_ops = { .valid.max_access_size = 4, }; +static Property iotkit_sysinfo_props[] = { + DEFINE_PROP_UINT32("SYS_VERSION", IoTKitSysInfo, sys_version, 0), + DEFINE_PROP_UINT32("SYS_CONFIG", IoTKitSysInfo, sys_config, 0), + DEFINE_PROP_END_OF_LIST() +}; + static void iotkit_sysinfo_init(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); @@ -106,10 +113,14 @@ static void iotkit_sysinfo_init(Object *obj) static void iotkit_sysinfo_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); + /* * This device has no guest-modifiable state and so it * does not need a reset function or VMState. */ + + dc->props = iotkit_sysinfo_props; } static const TypeInfo iotkit_sysinfo_info = { diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 52466c7..b0701bd 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -132,3 +132,7 @@ iotkit_sysinfo_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysI iotkit_sysctl_read(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" iotkit_sysctl_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" iotkit_sysctl_reset(void) "IoTKit SysCtl: reset" + +# hw/misc/armsse-cpuid.c +armsse_cpuid_read(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +armsse_cpuid_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs index b318e53..26f7b4c 100644 --- a/hw/nvram/Makefile.objs +++ b/hw/nvram/Makefile.objs @@ -5,3 +5,4 @@ common-obj-y += fw_cfg.o common-obj-y += chrp_nvram.o common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o obj-$(CONFIG_PSERIES) += spapr_nvram.o +obj-$(CONFIG_NRF51_SOC) += nrf51_nvm.o diff --git a/hw/nvram/nrf51_nvm.c b/hw/nvram/nrf51_nvm.c new file mode 100644 index 0000000..7d94cef --- /dev/null +++ b/hw/nvram/nrf51_nvm.c @@ -0,0 +1,388 @@ +/* + * Nordic Semiconductor nRF51 non-volatile memory + * + * It provides an interface to erase regions in flash memory. + * Furthermore it provides the user and factory information registers. + * + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf + * + * See nRF51 reference manual and product sheet sections: + * + Non-Volatile Memory Controller (NVMC) + * + Factory Information Configuration Registers (FICR) + * + User Information Configuration Registers (UICR) + * + * Copyright 2018 Steffen Görtz <contrib@steffen-goertz.de> + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "exec/address-spaces.h" +#include "hw/arm/nrf51.h" +#include "hw/nvram/nrf51_nvm.h" + +/* + * FICR Registers Assignments + * CODEPAGESIZE 0x010 + * CODESIZE 0x014 + * CLENR0 0x028 + * PPFC 0x02C + * NUMRAMBLOCK 0x034 + * SIZERAMBLOCKS 0x038 + * SIZERAMBLOCK[0] 0x038 + * SIZERAMBLOCK[1] 0x03C + * SIZERAMBLOCK[2] 0x040 + * SIZERAMBLOCK[3] 0x044 + * CONFIGID 0x05C + * DEVICEID[0] 0x060 + * DEVICEID[1] 0x064 + * ER[0] 0x080 + * ER[1] 0x084 + * ER[2] 0x088 + * ER[3] 0x08C + * IR[0] 0x090 + * IR[1] 0x094 + * IR[2] 0x098 + * IR[3] 0x09C + * DEVICEADDRTYPE 0x0A0 + * DEVICEADDR[0] 0x0A4 + * DEVICEADDR[1] 0x0A8 + * OVERRIDEEN 0x0AC + * NRF_1MBIT[0] 0x0B0 + * NRF_1MBIT[1] 0x0B4 + * NRF_1MBIT[2] 0x0B8 + * NRF_1MBIT[3] 0x0BC + * NRF_1MBIT[4] 0x0C0 + * BLE_1MBIT[0] 0x0EC + * BLE_1MBIT[1] 0x0F0 + * BLE_1MBIT[2] 0x0F4 + * BLE_1MBIT[3] 0x0F8 + * BLE_1MBIT[4] 0x0FC + */ +static const uint32_t ficr_content[64] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000400, + 0x00000100, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000002, 0x00002000, + 0x00002000, 0x00002000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000003, + 0x12345678, 0x9ABCDEF1, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +static uint64_t ficr_read(void *opaque, hwaddr offset, unsigned int size) +{ + assert(offset < sizeof(ficr_content)); + return ficr_content[offset / 4]; +} + +static void ficr_write(void *opaque, hwaddr offset, uint64_t value, + unsigned int size) +{ + /* Intentionally do nothing */ +} + +static const MemoryRegionOps ficr_ops = { + .read = ficr_read, + .write = ficr_write, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN +}; + +/* + * UICR Registers Assignments + * CLENR0 0x000 + * RBPCONF 0x004 + * XTALFREQ 0x008 + * FWID 0x010 + * BOOTLOADERADDR 0x014 + * NRFFW[0] 0x014 + * NRFFW[1] 0x018 + * NRFFW[2] 0x01C + * NRFFW[3] 0x020 + * NRFFW[4] 0x024 + * NRFFW[5] 0x028 + * NRFFW[6] 0x02C + * NRFFW[7] 0x030 + * NRFFW[8] 0x034 + * NRFFW[9] 0x038 + * NRFFW[10] 0x03C + * NRFFW[11] 0x040 + * NRFFW[12] 0x044 + * NRFFW[13] 0x048 + * NRFFW[14] 0x04C + * NRFHW[0] 0x050 + * NRFHW[1] 0x054 + * NRFHW[2] 0x058 + * NRFHW[3] 0x05C + * NRFHW[4] 0x060 + * NRFHW[5] 0x064 + * NRFHW[6] 0x068 + * NRFHW[7] 0x06C + * NRFHW[8] 0x070 + * NRFHW[9] 0x074 + * NRFHW[10] 0x078 + * NRFHW[11] 0x07C + * CUSTOMER[0] 0x080 + * CUSTOMER[1] 0x084 + * CUSTOMER[2] 0x088 + * CUSTOMER[3] 0x08C + * CUSTOMER[4] 0x090 + * CUSTOMER[5] 0x094 + * CUSTOMER[6] 0x098 + * CUSTOMER[7] 0x09C + * CUSTOMER[8] 0x0A0 + * CUSTOMER[9] 0x0A4 + * CUSTOMER[10] 0x0A8 + * CUSTOMER[11] 0x0AC + * CUSTOMER[12] 0x0B0 + * CUSTOMER[13] 0x0B4 + * CUSTOMER[14] 0x0B8 + * CUSTOMER[15] 0x0BC + * CUSTOMER[16] 0x0C0 + * CUSTOMER[17] 0x0C4 + * CUSTOMER[18] 0x0C8 + * CUSTOMER[19] 0x0CC + * CUSTOMER[20] 0x0D0 + * CUSTOMER[21] 0x0D4 + * CUSTOMER[22] 0x0D8 + * CUSTOMER[23] 0x0DC + * CUSTOMER[24] 0x0E0 + * CUSTOMER[25] 0x0E4 + * CUSTOMER[26] 0x0E8 + * CUSTOMER[27] 0x0EC + * CUSTOMER[28] 0x0F0 + * CUSTOMER[29] 0x0F4 + * CUSTOMER[30] 0x0F8 + * CUSTOMER[31] 0x0FC + */ + +static uint64_t uicr_read(void *opaque, hwaddr offset, unsigned int size) +{ + NRF51NVMState *s = NRF51_NVM(opaque); + + assert(offset < sizeof(s->uicr_content)); + return s->uicr_content[offset / 4]; +} + +static void uicr_write(void *opaque, hwaddr offset, uint64_t value, + unsigned int size) +{ + NRF51NVMState *s = NRF51_NVM(opaque); + + assert(offset < sizeof(s->uicr_content)); + s->uicr_content[offset / 4] = value; +} + +static const MemoryRegionOps uicr_ops = { + .read = uicr_read, + .write = uicr_write, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN +}; + + +static uint64_t io_read(void *opaque, hwaddr offset, unsigned int size) +{ + NRF51NVMState *s = NRF51_NVM(opaque); + uint64_t r = 0; + + switch (offset) { + case NRF51_NVMC_READY: + r = NRF51_NVMC_READY_READY; + break; + case NRF51_NVMC_CONFIG: + r = s->config; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad read offset 0x%" HWADDR_PRIx "\n", __func__, offset); + break; + } + + return r; +} + +static void io_write(void *opaque, hwaddr offset, uint64_t value, + unsigned int size) +{ + NRF51NVMState *s = NRF51_NVM(opaque); + + switch (offset) { + case NRF51_NVMC_CONFIG: + s->config = value & NRF51_NVMC_CONFIG_MASK; + break; + case NRF51_NVMC_ERASEPCR0: + case NRF51_NVMC_ERASEPCR1: + if (s->config & NRF51_NVMC_CONFIG_EEN) { + /* Mask in-page sub address */ + value &= ~(NRF51_PAGE_SIZE - 1); + if (value <= (s->flash_size - NRF51_PAGE_SIZE)) { + memset(s->storage + value, 0xFF, NRF51_PAGE_SIZE); + memory_region_flush_rom_device(&s->flash, value, + NRF51_PAGE_SIZE); + } + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Flash erase at 0x%" HWADDR_PRIx" while flash not erasable.\n", + __func__, offset); + } + break; + case NRF51_NVMC_ERASEALL: + if (value == NRF51_NVMC_ERASE) { + if (s->config & NRF51_NVMC_CONFIG_EEN) { + memset(s->storage, 0xFF, s->flash_size); + memory_region_flush_rom_device(&s->flash, 0, s->flash_size); + memset(s->uicr_content, 0xFF, sizeof(s->uicr_content)); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Flash not erasable.\n", + __func__); + } + } + break; + case NRF51_NVMC_ERASEUICR: + if (value == NRF51_NVMC_ERASE) { + memset(s->uicr_content, 0xFF, sizeof(s->uicr_content)); + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad write offset 0x%" HWADDR_PRIx "\n", __func__, offset); + } +} + +static const MemoryRegionOps io_ops = { + .read = io_read, + .write = io_write, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + + +static void flash_write(void *opaque, hwaddr offset, uint64_t value, + unsigned int size) +{ + NRF51NVMState *s = NRF51_NVM(opaque); + + if (s->config & NRF51_NVMC_CONFIG_WEN) { + uint32_t oldval; + + assert(offset + size <= s->flash_size); + + /* NOR Flash only allows bits to be flipped from 1's to 0's on write */ + oldval = ldl_le_p(s->storage + offset); + oldval &= value; + stl_le_p(s->storage + offset, oldval); + + memory_region_flush_rom_device(&s->flash, offset, size); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Flash write 0x%" HWADDR_PRIx" while flash not writable.\n", + __func__, offset); + } +} + + + +static const MemoryRegionOps flash_ops = { + .write = flash_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void nrf51_nvm_init(Object *obj) +{ + NRF51NVMState *s = NRF51_NVM(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->mmio, obj, &io_ops, s, "nrf51_soc.nvmc", + NRF51_NVMC_SIZE); + sysbus_init_mmio(sbd, &s->mmio); + + memory_region_init_io(&s->ficr, obj, &ficr_ops, s, "nrf51_soc.ficr", + sizeof(ficr_content)); + sysbus_init_mmio(sbd, &s->ficr); + + memory_region_init_io(&s->uicr, obj, &uicr_ops, s, "nrf51_soc.uicr", + sizeof(s->uicr_content)); + sysbus_init_mmio(sbd, &s->uicr); +} + +static void nrf51_nvm_realize(DeviceState *dev, Error **errp) +{ + NRF51NVMState *s = NRF51_NVM(dev); + Error *err = NULL; + + memory_region_init_rom_device(&s->flash, OBJECT(dev), &flash_ops, s, + "nrf51_soc.flash", s->flash_size, &err); + if (err) { + error_propagate(errp, err); + return; + } + + s->storage = memory_region_get_ram_ptr(&s->flash); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->flash); +} + +static void nrf51_nvm_reset(DeviceState *dev) +{ + NRF51NVMState *s = NRF51_NVM(dev); + + s->config = 0x00; + memset(s->uicr_content, 0xFF, sizeof(s->uicr_content)); +} + +static Property nrf51_nvm_properties[] = { + DEFINE_PROP_UINT32("flash-size", NRF51NVMState, flash_size, 0x40000), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_nvm = { + .name = "nrf51_soc.nvm", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(uicr_content, NRF51NVMState, + NRF51_UICR_FIXTURE_SIZE), + VMSTATE_UINT32(config, NRF51NVMState), + VMSTATE_END_OF_LIST() + } +}; + +static void nrf51_nvm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = nrf51_nvm_properties; + dc->vmsd = &vmstate_nvm; + dc->realize = nrf51_nvm_realize; + dc->reset = nrf51_nvm_reset; +} + +static const TypeInfo nrf51_nvm_info = { + .name = TYPE_NRF51_NVM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NRF51NVMState), + .instance_init = nrf51_nvm_init, + .class_init = nrf51_nvm_class_init +}; + +static void nrf51_nvm_register_types(void) +{ + type_register_static(&nrf51_nvm_info); +} + +type_init(nrf51_nvm_register_types) |