From 66647809f56cc8c7c8a39032e54106f3cd4283f4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:41 +0000 Subject: armv7m: Pass through start-powered-off CPU property Expose "start-powered-off" as a property of the ARMv7M container, which we just pass through to the CPU object in the same way that we do for "init-svtor" and "idau". (We want this for the SSE-200, which powers up only the first CPU at reset and leaves the second powered down.) As with the other CPU properties here, we can't just use alias properties, because the CPU QOM object is not created until armv7m realize time. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-4-peter.maydell@linaro.org --- include/hw/arm/armv7m.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/hw/arm/armv7m.h b/include/hw/arm/armv7m.h index 2ba24953b6..e96a98f809 100644 --- a/include/hw/arm/armv7m.h +++ b/include/hw/arm/armv7m.h @@ -65,6 +65,7 @@ typedef struct ARMv7MState { Object *idau; uint32_t init_svtor; bool enable_bitband; + bool start_powered_off; } ARMv7MState; #endif -- cgit v1.2.3-70-g09d2 From 93dbd10347871133b3e005d6826defbd41d63371 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:41 +0000 Subject: hw/arm/iotkit: Rename IoTKit to ARMSSE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Arm IoTKit was effectively the forerunner of a series of subsystems for embedded SoCs, named the SSE-050, SSE-100 and SSE-200: https://developer.arm.com/products/system-design/subsystems These are generally quite similar, though later iterations have extra devices that earlier ones do not. We want to add a model of the SSE-200, which means refactoring the IoTKit code into an abstract base class and subclasses (using the same design that the bcm283x SoC and Aspeed SoC family implementations do). As a first step, rename the IoTKit struct and QOM macros to ARMSSE, which is what we're going to name the base class. We temporarily retain TYPE_IOTKIT to avoid changing the code that instantiates a TYPE_IOTKIT device here and then changing it back again when it is re-introduced as a subclass. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-5-peter.maydell@linaro.org --- hw/arm/iotkit.c | 59 +++++++++++++++++++++++++------------------------ hw/arm/mps2-tz.c | 2 +- include/hw/arm/iotkit.h | 22 +++++++++++++----- 3 files changed, 47 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c index 8742200fb4..9360053184 100644 --- a/hw/arm/iotkit.c +++ b/hw/arm/iotkit.c @@ -1,5 +1,5 @@ /* - * Arm IoT Kit + * Arm SSE (Subsystems for Embedded): IoTKit * * Copyright (c) 2018 Linaro Limited * Written by Peter Maydell @@ -24,7 +24,7 @@ /* 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, +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); @@ -41,18 +41,18 @@ static void irq_status_forwarder(void *opaque, int n, int level) static void nsccfg_handler(void *opaque, int n, int level) { - IoTKit *s = IOTKIT(opaque); + ARMSSE *s = ARMSSE(opaque); s->nsccfg = level; } -static void iotkit_forward_ppc(IoTKit *s, const char *ppcname, int ppcnum) +static void iotkit_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 IoTKit has a collection of control lines which + * 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 IoTKit device itself, so the - * code using the IoTKit can wire them up to the PPCs. + * 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 *iotkitdev = DEVICE(s); @@ -91,7 +91,7 @@ static void iotkit_forward_ppc(IoTKit *s, const char *ppcname, int ppcnum) g_free(name); } -static void iotkit_forward_sec_resp_cfg(IoTKit *s) +static void iotkit_forward_sec_resp_cfg(ARMSSE *s) { /* Forward the 3rd output from the splitter device as a * named GPIO output of the iotkit object. @@ -107,7 +107,7 @@ static void iotkit_forward_sec_resp_cfg(IoTKit *s) static void iotkit_init(Object *obj) { - IoTKit *s = IOTKIT(obj); + ARMSSE *s = ARMSSE(obj); int i; memory_region_init(&s->container, obj, "iotkit-container", UINT64_MAX); @@ -175,20 +175,20 @@ static void iotkit_init(Object *obj) static void iotkit_exp_irq(void *opaque, int n, int level) { - IoTKit *s = IOTKIT(opaque); + ARMSSE *s = ARMSSE(opaque); qemu_set_irq(s->exp_irqs[n], level); } static void iotkit_mpcexp_status(void *opaque, int n, int level) { - IoTKit *s = IOTKIT(opaque); + ARMSSE *s = ARMSSE(opaque); qemu_set_irq(s->mpcexp_status_in[n], level); } static void iotkit_realize(DeviceState *dev, Error **errp) { - IoTKit *s = IOTKIT(dev); + ARMSSE *s = ARMSSE(dev); int i; MemoryRegion *mr; Error *err = NULL; @@ -215,9 +215,9 @@ static void iotkit_realize(DeviceState *dev, Error **errp) * 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), + * 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 IoTKit IDAU, the + * 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. @@ -239,7 +239,7 @@ static void iotkit_realize(DeviceState *dev, Error **errp) * 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) + * 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 @@ -306,8 +306,8 @@ static void iotkit_realize(DeviceState *dev, Error **errp) 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. + * 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); @@ -475,7 +475,7 @@ static void iotkit_realize(DeviceState *dev, Error **errp) /* 0x40010000 .. 0x4001ffff: private CPU region: unused in IoTKit */ - /* 0x40020000 .. 0x4002ffff : IoTKit system control peripheral region */ + /* 0x40020000 .. 0x4002ffff : ARMSSE system control peripheral region */ /* Devices behind APB PPC1: * 0x4002f000: S32K timer */ @@ -558,7 +558,7 @@ static void iotkit_realize(DeviceState *dev, Error **errp) 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 */ + /* 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); @@ -678,7 +678,7 @@ static void iotkit_realize(DeviceState *dev, Error **errp) * 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. + * devices in the ARMSSE. */ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container); @@ -688,11 +688,12 @@ static void iotkit_realize(DeviceState *dev, Error **errp) 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 + /* + * 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. */ - IoTKit *s = IOTKIT(ii); + ARMSSE *s = ARMSSE(ii); int region = extract32(address, 28, 4); *ns = !(region & 1); @@ -707,22 +708,22 @@ static const VMStateDescription iotkit_vmstate = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_UINT32(nsccfg, IoTKit), + VMSTATE_UINT32(nsccfg, ARMSSE), VMSTATE_END_OF_LIST() } }; static Property iotkit_properties[] = { - DEFINE_PROP_LINK("memory", IoTKit, board_memory, TYPE_MEMORY_REGION, + DEFINE_PROP_LINK("memory", ARMSSE, 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_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64), + DEFINE_PROP_UINT32("MAINCLK", ARMSSE, mainclk_frq, 0), DEFINE_PROP_END_OF_LIST() }; static void iotkit_reset(DeviceState *dev) { - IoTKit *s = IOTKIT(dev); + ARMSSE *s = ARMSSE(dev); s->nsccfg = 0; } @@ -740,9 +741,9 @@ static void iotkit_class_init(ObjectClass *klass, void *data) } static const TypeInfo iotkit_info = { - .name = TYPE_IOTKIT, + .name = TYPE_ARMSSE, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IoTKit), + .instance_size = sizeof(ARMSSE), .instance_init = iotkit_init, .class_init = iotkit_class_init, .interfaces = (InterfaceInfo[]) { diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 82b1d020a5..5824335b4f 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -66,7 +66,7 @@ typedef struct { typedef struct { MachineState parent; - IoTKit iotkit; + ARMSSE iotkit; MemoryRegion psram; MemoryRegion ssram[3]; MemoryRegion ssram1_m; diff --git a/include/hw/arm/iotkit.h b/include/hw/arm/iotkit.h index 3a8ee63908..9701738ec7 100644 --- a/include/hw/arm/iotkit.h +++ b/include/hw/arm/iotkit.h @@ -1,5 +1,5 @@ /* - * ARM IoT Kit + * ARM SSE (Subsystems for Embedded): IoTKit * * Copyright (c) 2018 Linaro Limited * Written by Peter Maydell @@ -9,7 +9,10 @@ * (at your option) any later version. */ -/* This is a model of the Arm IoT Kit which is documented in +/* + * This is a model of the Arm "Subsystems for Embedded" family of + * hardware, which include the IoT Kit and the SSE-050, SSE-100 and + * SSE-200. Currently we model only the Arm IoT Kit which is documented in * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html * It contains: * a Cortex-M33 @@ -71,8 +74,15 @@ #include "hw/or-irq.h" #include "hw/core/split-irq.h" -#define TYPE_IOTKIT "iotkit" -#define IOTKIT(obj) OBJECT_CHECK(IoTKit, (obj), TYPE_IOTKIT) +#define TYPE_ARMSSE "iotkit" +#define ARMSSE(obj) OBJECT_CHECK(ARMSSE, (obj), TYPE_ARMSSE) + +/* + * For the moment TYPE_IOTKIT is a synonym for TYPE_ARMSSE (and the + * latter's underlying name is left as "iotkit"); in a later + * commit it will become a subclass of TYPE_ARMSSE. + */ +#define TYPE_IOTKIT TYPE_ARMSSE /* We have an IRQ splitter and an OR gate input for each external PPC * and the 2 internal PPCs @@ -80,7 +90,7 @@ #define NUM_EXTERNAL_PPCS (IOTS_NUM_AHB_EXP_PPC + IOTS_NUM_APB_EXP_PPC) #define NUM_PPCS (NUM_EXTERNAL_PPCS + 2) -typedef struct IoTKit { +typedef struct ARMSSE { /*< private >*/ SysBusDevice parent_obj; @@ -131,6 +141,6 @@ typedef struct IoTKit { MemoryRegion *board_memory; uint32_t exp_numirq; uint32_t mainclk_frq; -} IoTKit; +} ARMSSE; #endif -- cgit v1.2.3-70-g09d2 From 4c3690b591b9b8f62c7ddc7fabf4f60e29b0921d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:41 +0000 Subject: hw/arm/iotkit: Refactor into abstract base class and subclass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Arm SSE-200 Subsystem for Embedded is a revised and extended version of the older IoTKit SoC. Prepare for adding a model of it by refactoring the IoTKit code into an abstract base class which contains the functionality, driven by a class data block specific to each subclass. (This is the same approach used by the existing bcm283x SoC family implementation.) Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-6-peter.maydell@linaro.org --- hw/arm/iotkit.c | 34 +++++++++++++++++++++++++++++----- include/hw/arm/iotkit.h | 22 +++++++++++++++++----- 2 files changed, 46 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c index 9360053184..d5b172933c 100644 --- a/hw/arm/iotkit.c +++ b/hw/arm/iotkit.c @@ -18,6 +18,16 @@ #include "hw/arm/iotkit.h" #include "hw/arm/arm.h" +struct ARMSSEInfo { + const char *name; +}; + +static const ARMSSEInfo armsse_variants[] = { + { + .name = TYPE_IOTKIT, + }, +}; + /* Clock frequency in HZ of the 32KHz "slow clock" */ #define S32KCLK (32 * 1000) @@ -732,29 +742,43 @@ static void iotkit_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(klass); + ARMSSEClass *asc = ARMSSE_CLASS(klass); dc->realize = iotkit_realize; dc->vmsd = &iotkit_vmstate; dc->props = iotkit_properties; dc->reset = iotkit_reset; iic->check = iotkit_idau_check; + asc->info = data; } -static const TypeInfo iotkit_info = { +static const TypeInfo armsse_info = { .name = TYPE_ARMSSE, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ARMSSE), .instance_init = iotkit_init, - .class_init = iotkit_class_init, + .abstract = true, .interfaces = (InterfaceInfo[]) { { TYPE_IDAU_INTERFACE }, { } } }; -static void iotkit_register_types(void) +static void armsse_register_types(void) { - type_register_static(&iotkit_info); + 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 = iotkit_class_init, + .class_data = (void *)&armsse_variants[i], + }; + type_register(&ti); + } } -type_init(iotkit_register_types); +type_init(armsse_register_types); diff --git a/include/hw/arm/iotkit.h b/include/hw/arm/iotkit.h index 9701738ec7..521d1f7375 100644 --- a/include/hw/arm/iotkit.h +++ b/include/hw/arm/iotkit.h @@ -74,15 +74,15 @@ #include "hw/or-irq.h" #include "hw/core/split-irq.h" -#define TYPE_ARMSSE "iotkit" +#define TYPE_ARMSSE "arm-sse" #define ARMSSE(obj) OBJECT_CHECK(ARMSSE, (obj), TYPE_ARMSSE) /* - * For the moment TYPE_IOTKIT is a synonym for TYPE_ARMSSE (and the - * latter's underlying name is left as "iotkit"); in a later - * commit it will become a subclass of TYPE_ARMSSE. + * These type names are for specific IoTKit subsystems; other than + * instantiating them, code using these devices should always handle + * them via the ARMSSE base class, so they have no IOTKIT() etc macros. */ -#define TYPE_IOTKIT TYPE_ARMSSE +#define TYPE_IOTKIT "iotkit" /* We have an IRQ splitter and an OR gate input for each external PPC * and the 2 internal PPCs @@ -143,4 +143,16 @@ typedef struct ARMSSE { uint32_t mainclk_frq; } ARMSSE; +typedef struct ARMSSEInfo ARMSSEInfo; + +typedef struct ARMSSEClass { + DeviceClass parent_class; + const ARMSSEInfo *info; +} ARMSSEClass; + +#define ARMSSE_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMSSEClass, (klass), TYPE_ARMSSE) +#define ARMSSE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMSSEClass, (obj), TYPE_ARMSSE) + #endif -- cgit v1.2.3-70-g09d2 From 6eee5d241a87615a31d46bb043101eceeaa4a799 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:42 +0000 Subject: hw/arm/iotkit: Rename files to hw/arm/armsse.[ch] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the files that used to be iotkit.[ch] to armsse.[ch] to reflect the fact they new cover multiple Arm subsystems for embedded. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-8-peter.maydell@linaro.org --- MAINTAINERS | 4 +- default-configs/arm-softmmu.mak | 2 +- hw/arm/Makefile.objs | 2 +- hw/arm/armsse.c | 784 ++++++++++++++++++++++++++++++++++++++++ hw/arm/iotkit.c | 784 ---------------------------------------- hw/arm/mps2-tz.c | 2 +- include/hw/arm/armsse.h | 158 ++++++++ include/hw/arm/iotkit.h | 158 -------- 8 files changed, 947 insertions(+), 947 deletions(-) create mode 100644 hw/arm/armsse.c delete mode 100644 hw/arm/iotkit.c create mode 100644 include/hw/arm/armsse.h delete mode 100644 include/hw/arm/iotkit.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 234e5c413b..c2ad4e52c9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -623,8 +623,8 @@ F: hw/arm/mps2.c F: hw/arm/mps2-tz.c F: hw/misc/mps2-*.c F: include/hw/misc/mps2-*.h -F: hw/arm/iotkit.c -F: include/hw/arm/iotkit.h +F: hw/arm/armsse.c +F: include/hw/arm/armsse.h F: hw/misc/iotkit-secctl.c F: include/hw/misc/iotkit-secctl.h F: hw/misc/iotkit-sysctl.c diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 2420491aac..3f20015787 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -114,7 +114,7 @@ CONFIG_MPS2_SCC=y CONFIG_TZ_MPC=y CONFIG_TZ_MSC=y CONFIG_TZ_PPC=y -CONFIG_IOTKIT=y +CONFIG_ARMSSE=y CONFIG_IOTKIT_SECCTL=y CONFIG_IOTKIT_SYSCTL=y CONFIG_IOTKIT_SYSINFO=y diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 50c7b4a927..22b7f0ed0b 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 0000000000..8554be1412 --- /dev/null +++ b/hw/arm/armsse.c @@ -0,0 +1,784 @@ +/* + * 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" + +struct ARMSSEInfo { + const char *name; +}; + +static const ARMSSEInfo armsse_variants[] = { + { + .name = TYPE_IOTKIT, + }, +}; + +/* 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(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); + int i; + + memory_region_init(&s->container, obj, "armsse-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, "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); + 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 armsse_exp_irq(void *opaque, int n, int level) +{ + ARMSSE *s = ARMSSE(opaque); + + qemu_set_irq(s->exp_irqs[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 void armsse_realize(DeviceState *dev, Error **errp) +{ + ARMSSE *s = ARMSSE(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 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, -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, armsse_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 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)); + + /* This RAM lives behind the Memory Protection Controller */ + memory_region_init_ram(&s->sram0, NULL, "armsse.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 : 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, + 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 : 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, + 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); + + 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 + 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, 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, + 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 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_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/iotkit.c b/hw/arm/iotkit.c deleted file mode 100644 index 7ff14fd5ae..0000000000 --- a/hw/arm/iotkit.c +++ /dev/null @@ -1,784 +0,0 @@ -/* - * 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/iotkit.h" -#include "hw/arm/arm.h" - -struct ARMSSEInfo { - const char *name; -}; - -static const ARMSSEInfo armsse_variants[] = { - { - .name = TYPE_IOTKIT, - }, -}; - -/* 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(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); - int i; - - memory_region_init(&s->container, obj, "armsse-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, "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); - 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 armsse_exp_irq(void *opaque, int n, int level) -{ - ARMSSE *s = ARMSSE(opaque); - - qemu_set_irq(s->exp_irqs[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 void armsse_realize(DeviceState *dev, Error **errp) -{ - ARMSSE *s = ARMSSE(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 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, -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, armsse_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 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)); - - /* This RAM lives behind the Memory Protection Controller */ - memory_region_init_ram(&s->sram0, NULL, "armsse.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 : 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, - 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 : 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, - 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); - - 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 + 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, 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, - 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 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_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/mps2-tz.c b/hw/arm/mps2-tz.c index 5824335b4f..3859f17d98 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -46,7 +46,7 @@ #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" diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h new file mode 100644 index 0000000000..ff51205498 --- /dev/null +++ b/include/hw/arm/armsse.h @@ -0,0 +1,158 @@ +/* + * 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. + */ + +/* + * This is a model of the Arm "Subsystems for Embedded" family of + * hardware, which include the IoT Kit and the SSE-050, SSE-100 and + * SSE-200. Currently we model only the Arm IoT Kit which is documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * It contains: + * a Cortex-M33 + * the IDAU + * some timers and watchdogs + * two peripheral protection controllers + * a memory protection controller + * a security controller + * a bus fabric which arranges that some parts of the address + * space are secure and non-secure aliases of each other + * + * QEMU interface: + * + QOM property "memory" is a MemoryRegion containing the devices provided + * by the board model. + * + QOM property "MAINCLK" is the frequency of the main system clock + * + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts + * + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts, which + * are wired to the NVIC lines 32 .. n+32 + * + sysbus MMIO region 0 is the "AHB Slave Expansion" which allows + * bus master devices in the board model to make transactions into + * all the devices and memory areas in the IoTKit + * Controlling up to 4 AHB expansion PPBs which a system using the IoTKit + * might provide: + * + named GPIO outputs apb_ppcexp{0,1,2,3}_nonsec[0..15] + * + named GPIO outputs apb_ppcexp{0,1,2,3}_ap[0..15] + * + named GPIO outputs apb_ppcexp{0,1,2,3}_irq_enable + * + named GPIO outputs apb_ppcexp{0,1,2,3}_irq_clear + * + named GPIO inputs apb_ppcexp{0,1,2,3}_irq_status + * Controlling each of the 4 expansion AHB PPCs which a system using the IoTKit + * might provide: + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_nonsec[0..15] + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_ap[0..15] + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear + * + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status + * Controlling each of the 16 expansion MPCs which a system using the IoTKit + * might provide: + * + named GPIO inputs mpcexp_status[0..15] + * Controlling each of the 16 expansion MSCs which a system using the IoTKit + * might provide: + * + named GPIO inputs mscexp_status[0..15] + * + named GPIO outputs mscexp_clear[0..15] + * + named GPIO outputs mscexp_ns[0..15] + */ + +#ifndef ARMSSE_H +#define ARMSSE_H + +#include "hw/sysbus.h" +#include "hw/arm/armv7m.h" +#include "hw/misc/iotkit-secctl.h" +#include "hw/misc/tz-ppc.h" +#include "hw/misc/tz-mpc.h" +#include "hw/timer/cmsdk-apb-timer.h" +#include "hw/timer/cmsdk-apb-dualtimer.h" +#include "hw/watchdog/cmsdk-apb-watchdog.h" +#include "hw/misc/iotkit-sysctl.h" +#include "hw/misc/iotkit-sysinfo.h" +#include "hw/or-irq.h" +#include "hw/core/split-irq.h" + +#define TYPE_ARMSSE "arm-sse" +#define ARMSSE(obj) OBJECT_CHECK(ARMSSE, (obj), TYPE_ARMSSE) + +/* + * These type names are for specific IoTKit subsystems; other than + * instantiating them, code using these devices should always handle + * them via the ARMSSE base class, so they have no IOTKIT() etc macros. + */ +#define TYPE_IOTKIT "iotkit" + +/* We have an IRQ splitter and an OR gate input for each external PPC + * and the 2 internal PPCs + */ +#define NUM_EXTERNAL_PPCS (IOTS_NUM_AHB_EXP_PPC + IOTS_NUM_APB_EXP_PPC) +#define NUM_PPCS (NUM_EXTERNAL_PPCS + 2) + +typedef struct ARMSSE { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + ARMv7MState armv7m; + IoTKitSecCtl secctl; + TZPPC apb_ppc0; + TZPPC apb_ppc1; + TZMPC mpc; + CMSDKAPBTIMER timer0; + CMSDKAPBTIMER timer1; + CMSDKAPBTIMER s32ktimer; + qemu_or_irq ppc_irq_orgate; + SplitIRQ sec_resp_splitter; + SplitIRQ ppc_irq_splitter[NUM_PPCS]; + SplitIRQ mpc_irq_splitter[IOTS_NUM_EXP_MPC + IOTS_NUM_MPC]; + qemu_or_irq mpc_irq_orgate; + qemu_or_irq nmi_orgate; + + CMSDKAPBDualTimer dualtimer; + + CMSDKAPBWatchdog s32kwatchdog; + CMSDKAPBWatchdog nswatchdog; + CMSDKAPBWatchdog swatchdog; + + IoTKitSysCtl sysctl; + IoTKitSysCtl sysinfo; + + MemoryRegion container; + MemoryRegion alias1; + MemoryRegion alias2; + MemoryRegion alias3; + MemoryRegion sram0; + + qemu_irq *exp_irqs; + qemu_irq ppc0_irq; + qemu_irq ppc1_irq; + qemu_irq sec_resp_cfg; + qemu_irq sec_resp_cfg_in; + qemu_irq nsc_cfg_in; + + qemu_irq irq_status_in[NUM_EXTERNAL_PPCS]; + qemu_irq mpcexp_status_in[IOTS_NUM_EXP_MPC]; + + uint32_t nsccfg; + + /* Properties */ + MemoryRegion *board_memory; + uint32_t exp_numirq; + uint32_t mainclk_frq; +} ARMSSE; + +typedef struct ARMSSEInfo ARMSSEInfo; + +typedef struct ARMSSEClass { + DeviceClass parent_class; + const ARMSSEInfo *info; +} ARMSSEClass; + +#define ARMSSE_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMSSEClass, (klass), TYPE_ARMSSE) +#define ARMSSE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMSSEClass, (obj), TYPE_ARMSSE) + +#endif diff --git a/include/hw/arm/iotkit.h b/include/hw/arm/iotkit.h deleted file mode 100644 index 521d1f7375..0000000000 --- a/include/hw/arm/iotkit.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * 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. - */ - -/* - * This is a model of the Arm "Subsystems for Embedded" family of - * hardware, which include the IoT Kit and the SSE-050, SSE-100 and - * SSE-200. Currently we model only the Arm IoT Kit which is documented in - * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html - * It contains: - * a Cortex-M33 - * the IDAU - * some timers and watchdogs - * two peripheral protection controllers - * a memory protection controller - * a security controller - * a bus fabric which arranges that some parts of the address - * space are secure and non-secure aliases of each other - * - * QEMU interface: - * + QOM property "memory" is a MemoryRegion containing the devices provided - * by the board model. - * + QOM property "MAINCLK" is the frequency of the main system clock - * + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts - * + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts, which - * are wired to the NVIC lines 32 .. n+32 - * + sysbus MMIO region 0 is the "AHB Slave Expansion" which allows - * bus master devices in the board model to make transactions into - * all the devices and memory areas in the IoTKit - * Controlling up to 4 AHB expansion PPBs which a system using the IoTKit - * might provide: - * + named GPIO outputs apb_ppcexp{0,1,2,3}_nonsec[0..15] - * + named GPIO outputs apb_ppcexp{0,1,2,3}_ap[0..15] - * + named GPIO outputs apb_ppcexp{0,1,2,3}_irq_enable - * + named GPIO outputs apb_ppcexp{0,1,2,3}_irq_clear - * + named GPIO inputs apb_ppcexp{0,1,2,3}_irq_status - * Controlling each of the 4 expansion AHB PPCs which a system using the IoTKit - * might provide: - * + named GPIO outputs ahb_ppcexp{0,1,2,3}_nonsec[0..15] - * + named GPIO outputs ahb_ppcexp{0,1,2,3}_ap[0..15] - * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable - * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear - * + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status - * Controlling each of the 16 expansion MPCs which a system using the IoTKit - * might provide: - * + named GPIO inputs mpcexp_status[0..15] - * Controlling each of the 16 expansion MSCs which a system using the IoTKit - * might provide: - * + named GPIO inputs mscexp_status[0..15] - * + named GPIO outputs mscexp_clear[0..15] - * + named GPIO outputs mscexp_ns[0..15] - */ - -#ifndef IOTKIT_H -#define IOTKIT_H - -#include "hw/sysbus.h" -#include "hw/arm/armv7m.h" -#include "hw/misc/iotkit-secctl.h" -#include "hw/misc/tz-ppc.h" -#include "hw/misc/tz-mpc.h" -#include "hw/timer/cmsdk-apb-timer.h" -#include "hw/timer/cmsdk-apb-dualtimer.h" -#include "hw/watchdog/cmsdk-apb-watchdog.h" -#include "hw/misc/iotkit-sysctl.h" -#include "hw/misc/iotkit-sysinfo.h" -#include "hw/or-irq.h" -#include "hw/core/split-irq.h" - -#define TYPE_ARMSSE "arm-sse" -#define ARMSSE(obj) OBJECT_CHECK(ARMSSE, (obj), TYPE_ARMSSE) - -/* - * These type names are for specific IoTKit subsystems; other than - * instantiating them, code using these devices should always handle - * them via the ARMSSE base class, so they have no IOTKIT() etc macros. - */ -#define TYPE_IOTKIT "iotkit" - -/* We have an IRQ splitter and an OR gate input for each external PPC - * and the 2 internal PPCs - */ -#define NUM_EXTERNAL_PPCS (IOTS_NUM_AHB_EXP_PPC + IOTS_NUM_APB_EXP_PPC) -#define NUM_PPCS (NUM_EXTERNAL_PPCS + 2) - -typedef struct ARMSSE { - /*< private >*/ - SysBusDevice parent_obj; - - /*< public >*/ - ARMv7MState armv7m; - IoTKitSecCtl secctl; - TZPPC apb_ppc0; - TZPPC apb_ppc1; - TZMPC mpc; - CMSDKAPBTIMER timer0; - CMSDKAPBTIMER timer1; - CMSDKAPBTIMER s32ktimer; - qemu_or_irq ppc_irq_orgate; - SplitIRQ sec_resp_splitter; - SplitIRQ ppc_irq_splitter[NUM_PPCS]; - SplitIRQ mpc_irq_splitter[IOTS_NUM_EXP_MPC + IOTS_NUM_MPC]; - qemu_or_irq mpc_irq_orgate; - qemu_or_irq nmi_orgate; - - CMSDKAPBDualTimer dualtimer; - - CMSDKAPBWatchdog s32kwatchdog; - CMSDKAPBWatchdog nswatchdog; - CMSDKAPBWatchdog swatchdog; - - IoTKitSysCtl sysctl; - IoTKitSysCtl sysinfo; - - MemoryRegion container; - MemoryRegion alias1; - MemoryRegion alias2; - MemoryRegion alias3; - MemoryRegion sram0; - - qemu_irq *exp_irqs; - qemu_irq ppc0_irq; - qemu_irq ppc1_irq; - qemu_irq sec_resp_cfg; - qemu_irq sec_resp_cfg_in; - qemu_irq nsc_cfg_in; - - qemu_irq irq_status_in[NUM_EXTERNAL_PPCS]; - qemu_irq mpcexp_status_in[IOTS_NUM_EXP_MPC]; - - uint32_t nsccfg; - - /* Properties */ - MemoryRegion *board_memory; - uint32_t exp_numirq; - uint32_t mainclk_frq; -} ARMSSE; - -typedef struct ARMSSEInfo ARMSSEInfo; - -typedef struct ARMSSEClass { - DeviceClass parent_class; - const ARMSSEInfo *info; -} ARMSSEClass; - -#define ARMSSE_CLASS(klass) \ - OBJECT_CLASS_CHECK(ARMSSEClass, (klass), TYPE_ARMSSE) -#define ARMSSE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(ARMSSEClass, (obj), TYPE_ARMSSE) - -#endif -- cgit v1.2.3-70-g09d2 From 0a78d7ebf8524fdcf701e6e228d8a5720a0ffd1e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:42 +0000 Subject: hw/misc/iotkit-secctl: Support 4 internal MPCs The SSE-200 has 4 banks of SRAM, each with its own internal Memory Protection Controller. The interrupt status for these extra MPCs appears in the same security controller SECMPCINTSTATUS register as the MPC for the IoTKit's single SRAM bank. Enhance the iotkit-secctl device to allow 4 MPCs. (If the particular IoTKit/SSE variant in use does not have all 4 MPCs then the unused inputs will simply result in the SECMPCINTSTATUS bits being zero as required.) The hardcoded constant "1"s in armsse.c indicate the actual number of SRAM MPCs the IoTKit has, and will be replaced in the following commit. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-9-peter.maydell@linaro.org --- hw/arm/armsse.c | 6 +++--- hw/misc/iotkit-secctl.c | 5 +++-- include/hw/misc/iotkit-secctl.h | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 8554be1412..074c1d3a6c 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -138,7 +138,7 @@ static void armsse_init(Object *obj) sizeof(s->mpc_irq_orgate), TYPE_OR_IRQ, &error_abort, NULL); - for (i = 0; i < ARRAY_SIZE(s->mpc_irq_splitter); i++) { + for (i = 0; i < IOTS_NUM_EXP_MPC + 1; i++) { char *name = g_strdup_printf("mpc-irq-splitter-%d", i); SplitIRQ *splitter = &s->mpc_irq_splitter[i]; @@ -363,7 +363,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) /* 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); + IOTS_NUM_EXP_MPC + 1, "num-lines", &err); if (err) { error_propagate(errp, err); return; @@ -636,7 +636,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) } /* Wire up the splitters for the MPC IRQs */ - for (i = 0; i < IOTS_NUM_EXP_MPC + IOTS_NUM_MPC; i++) { + for (i = 0; i < IOTS_NUM_EXP_MPC + 1; i++) { SplitIRQ *splitter = &s->mpc_irq_splitter[i]; DeviceState *dev_splitter = DEVICE(splitter); diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c index 2222b3e147..537601cd53 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/include/hw/misc/iotkit-secctl.h b/include/hw/misc/iotkit-secctl.h index 1a193b306f..bcb0437be5 100644 --- a/include/hw/misc/iotkit-secctl.h +++ b/include/hw/misc/iotkit-secctl.h @@ -40,8 +40,8 @@ * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear * + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status - * Controlling the MPC in the IoTKit: - * + named GPIO input mpc_status + * Controlling the (up to) 4 MPCs in the IoTKit/SSE: + * + named GPIO inputs mpc_status[0..3] * Controlling each of the 16 expansion MPCs which a system using the IoTKit * might provide: * + named GPIO inputs mpcexp_status[0..15] @@ -67,7 +67,7 @@ #define IOTS_NUM_APB_EXP_PPC 4 #define IOTS_NUM_AHB_EXP_PPC 4 #define IOTS_NUM_EXP_MPC 16 -#define IOTS_NUM_MPC 1 +#define IOTS_NUM_MPC 4 #define IOTS_NUM_EXP_MSC 16 typedef struct IoTKitSecCtl IoTKitSecCtl; -- cgit v1.2.3-70-g09d2 From f0cab7fe88e1751209d6f3d8b9bac04b09b2e7ea Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:42 +0000 Subject: hw/arm/armsse: Make number of SRAM banks parameterised The SSE-200 has four banks of SRAM, each with its own Memory Protection Controller, where the IoTKit has only one. Make the number of SRAM banks a field in ARMSSEInfo. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-10-peter.maydell@linaro.org --- hw/arm/armsse.c | 78 +++++++++++++++++++++++++++++++------------------ include/hw/arm/armsse.h | 9 ++++-- 2 files changed, 56 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 074c1d3a6c..b639b54e0d 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -20,11 +20,13 @@ struct ARMSSEInfo { const char *name; + int sram_banks; }; static const ARMSSEInfo armsse_variants[] = { { .name = TYPE_IOTKIT, + .sram_banks = 1, }, }; @@ -118,8 +120,12 @@ static void armsse_forward_sec_resp_cfg(ARMSSE *s) 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); + memory_region_init(&s->container, obj, "armsse-container", UINT64_MAX); sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), @@ -133,12 +139,17 @@ static void armsse_init(Object *obj) 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); + 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 + 1; i++) { + 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]; @@ -199,6 +210,8 @@ static void armsse_mpcexp_status(void *opaque, int n, int level) 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; @@ -335,35 +348,41 @@ static void armsse_realize(DeviceState *dev, Error **errp) 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, "armsse.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; + /* 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; + + memory_region_init_ram(&s->sram[i], NULL, ramname, 0x00008000, &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 * 0x8000, + 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)); } - /* 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 + 1, "num-lines", &err); + IOTS_NUM_EXP_MPC + info->sram_banks, + "num-lines", &err); if (err) { error_propagate(errp, err); return; @@ -636,7 +655,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) } /* Wire up the splitters for the MPC IRQs */ - for (i = 0; i < IOTS_NUM_EXP_MPC + 1; i++) { + for (i = 0; i < IOTS_NUM_EXP_MPC + info->sram_banks; i++) { SplitIRQ *splitter = &s->mpc_irq_splitter[i]; DeviceState *dev_splitter = DEVICE(splitter); @@ -659,7 +678,8 @@ static void armsse_realize(DeviceState *dev, Error **errp) "mpcexp_status", i)); } else { /* Splitter input is from our own MPC */ - qdev_connect_gpio_out_named(DEVICE(&s->mpc), "irq", 0, + 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, diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h index ff51205498..99714aa63c 100644 --- a/include/hw/arm/armsse.h +++ b/include/hw/arm/armsse.h @@ -90,6 +90,11 @@ #define NUM_EXTERNAL_PPCS (IOTS_NUM_AHB_EXP_PPC + IOTS_NUM_APB_EXP_PPC) #define NUM_PPCS (NUM_EXTERNAL_PPCS + 2) +#define MAX_SRAM_BANKS 4 +#if MAX_SRAM_BANKS > IOTS_NUM_MPC +#error Too many SRAM banks +#endif + typedef struct ARMSSE { /*< private >*/ SysBusDevice parent_obj; @@ -99,7 +104,7 @@ typedef struct ARMSSE { IoTKitSecCtl secctl; TZPPC apb_ppc0; TZPPC apb_ppc1; - TZMPC mpc; + TZMPC mpc[IOTS_NUM_MPC]; CMSDKAPBTIMER timer0; CMSDKAPBTIMER timer1; CMSDKAPBTIMER s32ktimer; @@ -123,7 +128,7 @@ typedef struct ARMSSE { MemoryRegion alias1; MemoryRegion alias2; MemoryRegion alias3; - MemoryRegion sram0; + MemoryRegion sram[MAX_SRAM_BANKS]; qemu_irq *exp_irqs; qemu_irq ppc0_irq; -- cgit v1.2.3-70-g09d2 From 4b635cf7a95e5012113570a87e134962a0271a27 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:42 +0000 Subject: hw/arm/armsse: Make SRAM bank size configurable For the IoTKit the SRAM bank size is always 32K (15 bits); for the SSE-200 this is a configurable parameter, which defaults to 32K but can be changed when it is built into a particular SoC. For instance the Musca-B1 board sets it to 128K (17 bits). Make the bank size a QOM property. We follow the SSE-200 hardware in naming the parameter SRAM_ADDR_WIDTH, which specifies the number of address bits of a single SRAM bank. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-11-peter.maydell@linaro.org --- hw/arm/armsse.c | 18 ++++++++++++++++-- include/hw/arm/armsse.h | 1 + 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index b639b54e0d..a2ae5d3c4b 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -221,6 +221,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) 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"); @@ -232,6 +233,15 @@ static void armsse_realize(DeviceState *dev, Error **errp) 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, @@ -352,8 +362,10 @@ static void armsse_realize(DeviceState *dev, Error **errp) 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, 0x00008000, &err); + memory_region_init_ram(&s->sram[i], NULL, ramname, + sram_bank_size, &err); g_free(ramname); if (err) { error_propagate(errp, err); @@ -372,7 +384,8 @@ static void armsse_realize(DeviceState *dev, Error **errp) } /* 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 * 0x8000, + 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, @@ -748,6 +761,7 @@ static Property armsse_properties[] = { 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() }; diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h index 99714aa63c..e4a0501331 100644 --- a/include/hw/arm/armsse.h +++ b/include/hw/arm/armsse.h @@ -146,6 +146,7 @@ typedef struct ARMSSE { MemoryRegion *board_memory; uint32_t exp_numirq; uint32_t mainclk_frq; + uint32_t sram_addr_width; } ARMSSE; typedef struct ARMSSEInfo ARMSSEInfo; -- cgit v1.2.3-70-g09d2 From 91c1e9fcbd7548db3687db946a778b8f34d1343c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:42 +0000 Subject: hw/arm/armsse: Support dual-CPU configuration The SSE-200 has two Cortex-M33 CPUs. These see the same view of memory, with the exception of the "private CPU region" which has per-CPU devices. Internal device interrupts for SSE-200 devices are mostly wired up to both CPUs, with the exception of a few per-CPU devices. External GPIO inputs on the SSE-200 device are provided for the second CPU's interrupts above 32, as is already the case for the first CPU. Refactor the code to support creation of multiple CPUs. For the moment we leave all CPUs with the same view of memory: this will not work in the multiple-CPU case, but we will fix this in the following commit. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-12-peter.maydell@linaro.org --- hw/arm/armsse.c | 206 ++++++++++++++++++++++++++++++++++++++---------- include/hw/arm/armsse.h | 21 +++-- 2 files changed, 180 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index a2ae5d3c4b..5cb2b78b1f 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -21,18 +21,35 @@ struct ARMSSEInfo { const char *name; int sram_banks; + int num_cpus; }; static const ARMSSEInfo armsse_variants[] = { { .name = TYPE_IOTKIT, .sram_banks = 1, + .num_cpus = 1, }, }; /* 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. */ @@ -125,13 +142,18 @@ static void armsse_init(Object *obj) 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); - 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")); + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("armv7m%d", i); + sysbus_init_child_obj(obj, 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); + } sysbus_init_child_obj(obj, "secctl", &s->secctl, sizeof(s->secctl), TYPE_IOTKIT_SECCTL); @@ -192,13 +214,25 @@ static void armsse_init(Object *obj) 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) { - ARMSSE *s = ARMSSE(opaque); + qemu_irq *irqarray = opaque; - qemu_set_irq(s->exp_irqs[n], level); + qemu_set_irq(irqarray[n], level); } static void armsse_mpcexp_status(void *opaque, int n, int level) @@ -207,6 +241,26 @@ static void armsse_mpcexp_status(void *opaque, int n, int level) 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 armsse_realize(DeviceState *dev, Error **errp) { ARMSSE *s = ARMSSE(dev); @@ -280,37 +334,105 @@ static void armsse_realize(DeviceState *dev, Error **errp) 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; + 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; + } + } + object_property_set_link(cpuobj, OBJECT(&s->container), "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; + } + + /* 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); } - /* 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); + /* 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)); + } + } + } } - qdev_init_gpio_in_named(dev, armsse_exp_irq, "EXP_IRQ", s->exp_numirq); /* Set up the big aliases first */ make_alias(s, &s->alias1, "alias 1", 0x10000000, 0x10000000, 0x00000000); @@ -407,7 +529,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } qdev_connect_gpio_out(DEVICE(&s->mpc_irq_orgate), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 9)); + armsse_get_common_irq_in(s, 9)); /* Devices behind APB PPC0: * 0x40000000: timer0 @@ -424,7 +546,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer0), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 3)); + 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) { @@ -439,7 +561,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer1), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 4)); + 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) { @@ -455,7 +577,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->dualtimer), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 5)); + 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) { @@ -513,7 +635,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } qdev_connect_gpio_out(DEVICE(&s->ppc_irq_orgate), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 10)); + armsse_get_common_irq_in(s, 10)); /* 0x40010000 .. 0x4001ffff: private CPU region: unused in IoTKit */ @@ -528,7 +650,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32ktimer), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 2)); + 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) { @@ -609,7 +731,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->nswatchdog), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 1)); + 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); @@ -715,7 +837,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) 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)); + armsse_get_common_irq_in(s, 11)); /* * Expose our container region to the board model; this corresponds diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h index e4a0501331..faf5dfed25 100644 --- a/include/hw/arm/armsse.h +++ b/include/hw/arm/armsse.h @@ -28,9 +28,16 @@ * + QOM property "memory" is a MemoryRegion containing the devices provided * by the board model. * + QOM property "MAINCLK" is the frequency of the main system clock - * + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts - * + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts, which - * are wired to the NVIC lines 32 .. n+32 + * + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts. + * (In hardware, the SSE-200 permits the number of expansion interrupts + * for the two CPUs to be configured separately, but we restrict it to + * being the same for both, to avoid having to have separate Property + * lists for different variants. This restriction can be relaxed later + * if necessary.) + * + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts for CPU 0, + * which are wired to its NVIC lines 32 .. n+32 + * + Named GPIO inputs "EXP_CPU1_IRQ" 0..n are the expansion interrupts for + * CPU 1, which are wired to its NVIC lines 32 .. n+32 * + sysbus MMIO region 0 is the "AHB Slave Expansion" which allows * bus master devices in the board model to make transactions into * all the devices and memory areas in the IoTKit @@ -95,12 +102,14 @@ #error Too many SRAM banks #endif +#define SSE_MAX_CPUS 2 + typedef struct ARMSSE { /*< private >*/ SysBusDevice parent_obj; /*< public >*/ - ARMv7MState armv7m; + ARMv7MState armv7m[SSE_MAX_CPUS]; IoTKitSecCtl secctl; TZPPC apb_ppc0; TZPPC apb_ppc1; @@ -115,6 +124,8 @@ typedef struct ARMSSE { qemu_or_irq mpc_irq_orgate; qemu_or_irq nmi_orgate; + SplitIRQ cpu_irq_splitter[32]; + CMSDKAPBDualTimer dualtimer; CMSDKAPBWatchdog s32kwatchdog; @@ -130,7 +141,7 @@ typedef struct ARMSSE { MemoryRegion alias3; MemoryRegion sram[MAX_SRAM_BANKS]; - qemu_irq *exp_irqs; + qemu_irq *exp_irqs[SSE_MAX_CPUS]; qemu_irq ppc0_irq; qemu_irq ppc1_irq; qemu_irq sec_resp_cfg; -- cgit v1.2.3-70-g09d2 From d847ca5128351ea3be3a92be74d7bac74e59f048 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:42 +0000 Subject: hw/arm/armsse: Give each CPU its own view of memory Give each CPU its own container memory region. This is necessary for two reasons: * some devices are instantiated one per CPU and the CPU sees only its own device * since a memory region can only be put into one container, we must give each armv7m object a different MemoryRegion as its 'memory' property, or a dual-CPU configuration will assert on realize when the second armv7m object tries to put the MR into a container when it is already in the first armv7m object's container Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-13-peter.maydell@linaro.org --- hw/arm/armsse.c | 22 ++++++++++++++++++++-- include/hw/arm/armsse.h | 10 ++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 5cb2b78b1f..2472dfef3a 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -153,6 +153,15 @@ static void armsse_init(Object *obj) 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), @@ -332,7 +341,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) * 0x50000000..0x5fffffff alias of 0x40000000..0x4fffffff */ - memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); + 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]); @@ -373,7 +382,16 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } } - object_property_set_link(cpuobj, OBJECT(&s->container), "memory", &err); + + 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; diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h index faf5dfed25..89f19a971f 100644 --- a/include/hw/arm/armsse.h +++ b/include/hw/arm/armsse.h @@ -135,7 +135,17 @@ typedef struct ARMSSE { IoTKitSysCtl sysctl; IoTKitSysCtl sysinfo; + /* + * 'container' holds all devices seen by all CPUs. + * 'cpu_container[i]' is the view that CPU i has: this has the + * per-CPU devices of that CPU, plus as the background 'container' + * (or an alias of it, since we can only use it directly once). + * container_alias[i] is the alias of 'container' used by CPU i+1; + * CPU 0 can use 'container' directly. + */ MemoryRegion container; + MemoryRegion container_alias[SSE_MAX_CPUS - 1]; + MemoryRegion cpu_container[SSE_MAX_CPUS]; MemoryRegion alias1; MemoryRegion alias2; MemoryRegion alias3; -- cgit v1.2.3-70-g09d2 From 7cd3a2e0d53ea0dc5e2811082a4f64b52c220ded Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:42 +0000 Subject: hw/arm/armsse: Put each CPU in its own cluster object Create a cluster object to hold each CPU in the SSE. They are logically distinct and may be configured differently (for instance one may not have an FPU where the other does). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-14-peter.maydell@linaro.org --- hw/arm/armsse.c | 31 ++++++++++++++++++++++++++++--- include/hw/arm/armsse.h | 2 ++ 2 files changed, 30 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 2472dfef3a..2eb4ea3bfe 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -147,9 +147,22 @@ static void armsse_init(Object *obj) memory_region_init(&s->container, obj, "armsse-container", UINT64_MAX); for (i = 0; i < info->num_cpus; i++) { - char *name = g_strdup_printf("armv7m%d", i); - sysbus_init_child_obj(obj, name, &s->armv7m[i], sizeof(s->armv7m), - TYPE_ARMV7M); + /* + * 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); @@ -406,6 +419,18 @@ static void armsse_realize(DeviceState *dev, Error **errp) 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); diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h index 89f19a971f..999c2e4f7e 100644 --- a/include/hw/arm/armsse.h +++ b/include/hw/arm/armsse.h @@ -80,6 +80,7 @@ #include "hw/misc/iotkit-sysinfo.h" #include "hw/or-irq.h" #include "hw/core/split-irq.h" +#include "hw/cpu/cluster.h" #define TYPE_ARMSSE "arm-sse" #define ARMSSE(obj) OBJECT_CHECK(ARMSSE, (obj), TYPE_ARMSSE) @@ -110,6 +111,7 @@ typedef struct ARMSSE { /*< public >*/ ARMv7MState armv7m[SSE_MAX_CPUS]; + CPUClusterState cluster[SSE_MAX_CPUS]; IoTKitSecCtl secctl; TZPPC apb_ppc0; TZPPC apb_ppc1; -- cgit v1.2.3-70-g09d2 From dde0c4910395445da6b2b756193f89ab578d31a1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:42 +0000 Subject: iotkit-sysinfo: Make SYS_VERSION and SYS_CONFIG configurable The SYS_VERSION and SYS_CONFIG register values differ between the IoTKit and SSE-200. Make them configurable via QOM properties rather than hard-coded, and set them appropriately in the ARMSSE code that instantiates the IOTKIT_SYSINFO device. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-15-peter.maydell@linaro.org --- hw/arm/armsse.c | 51 ++++++++++++++++++++++++++++++++++++++++ hw/misc/iotkit-sysinfo.c | 15 ++++++++++-- include/hw/misc/iotkit-sysinfo.h | 6 +++++ 3 files changed, 70 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 2eb4ea3bfe..19cae77e77 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -18,10 +18,18 @@ #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; }; static const ARMSSEInfo armsse_variants[] = { @@ -29,9 +37,39 @@ static const ARMSSEInfo armsse_variants[] = { .name = TYPE_IOTKIT, .sram_banks = 1, .num_cpus = 1, + .sys_version = 0x41743, + .sys_config_format = IoTKitFormat, }, }; +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) @@ -726,6 +764,19 @@ static void armsse_realize(DeviceState *dev, Error **errp) 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); diff --git a/hw/misc/iotkit-sysinfo.c b/hw/misc/iotkit-sysinfo.c index 78955bc45f..026ba94261 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/include/hw/misc/iotkit-sysinfo.h b/include/hw/misc/iotkit-sysinfo.h index 7b2e1a5e48..d84eb203b9 100644 --- a/include/hw/misc/iotkit-sysinfo.h +++ b/include/hw/misc/iotkit-sysinfo.h @@ -14,6 +14,8 @@ * Arm IoTKit and documented in * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html * QEMU interface: + * + QOM property "SYS_VERSION": value to use for SYS_VERSION register + * + QOM property "SYS_CONFIG": value to use for SYS_CONFIG register * + sysbus MMIO region 0: the system information register bank */ @@ -32,6 +34,10 @@ typedef struct IoTKitSysInfo { /*< public >*/ MemoryRegion iomem; + + /* Properties */ + uint32_t sys_version; + uint32_t sys_config; } IoTKitSysInfo; #endif -- cgit v1.2.3-70-g09d2 From f8574705f62b38a610d398573828b57da24b1adb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:43 +0000 Subject: hw/arm/armsse: Add unimplemented-device stubs for MHUs The SSE-200 has two Message Handling Units (MHUs), which sit behind the APB PPC0. Wire up some unimplemented-device stubs for these, since we don't yet implement a real model of this device. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-16-peter.maydell@linaro.org --- hw/arm/armsse.c | 41 +++++++++++++++++++++++++++++++++++++++++ include/hw/arm/armsse.h | 3 +++ 2 files changed, 44 insertions(+) (limited to 'include') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 19cae77e77..1f3dc89c8e 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -30,6 +30,7 @@ struct ARMSSEInfo { int num_cpus; uint32_t sys_version; SysConfigFormat sys_config_format; + bool has_mhus; }; static const ARMSSEInfo armsse_variants[] = { @@ -39,6 +40,7 @@ static const ARMSSEInfo armsse_variants[] = { .num_cpus = 1, .sys_version = 0x41743, .sys_config_format = IoTKitFormat, + .has_mhus = false, }, }; @@ -257,6 +259,12 @@ static void armsse_init(Object *obj) 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); + } object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate, sizeof(s->nmi_orgate), TYPE_OR_IRQ, &error_abort, NULL); @@ -616,6 +624,8 @@ static void armsse_realize(DeviceState *dev, Error **errp) * 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. @@ -666,6 +676,31 @@ static void armsse_realize(DeviceState *dev, Error **errp) 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); @@ -681,6 +716,12 @@ static void armsse_realize(DeviceState *dev, Error **errp) 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, diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h index 999c2e4f7e..dbfcb28060 100644 --- a/include/hw/arm/armsse.h +++ b/include/hw/arm/armsse.h @@ -78,6 +78,7 @@ #include "hw/watchdog/cmsdk-apb-watchdog.h" #include "hw/misc/iotkit-sysctl.h" #include "hw/misc/iotkit-sysinfo.h" +#include "hw/misc/unimp.h" #include "hw/or-irq.h" #include "hw/core/split-irq.h" #include "hw/cpu/cluster.h" @@ -137,6 +138,8 @@ typedef struct ARMSSE { IoTKitSysCtl sysctl; IoTKitSysCtl sysinfo; + UnimplementedDeviceState mhu[2]; + /* * 'container' holds all devices seen by all CPUs. * 'cpu_container[i]' is the view that CPU i has: this has the -- cgit v1.2.3-70-g09d2 From e0b00f1b92d700171cfe39fac39de9fa75c1aecd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:43 +0000 Subject: hw/arm/armsse: Add unimplemented-device stubs for PPUs Add unimplemented-device stubs for the various Power Policy Unit devices that the SSE-200 has. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-17-peter.maydell@linaro.org --- hw/arm/armsse.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ include/hw/arm/armsse.h | 11 ++++++++++ 2 files changed, 69 insertions(+) (limited to 'include') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 1f3dc89c8e..280ba5c78b 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -31,6 +31,7 @@ struct ARMSSEInfo { uint32_t sys_version; SysConfigFormat sys_config_format; bool has_mhus; + bool has_ppus; }; static const ARMSSEInfo armsse_variants[] = { @@ -41,6 +42,7 @@ static const ARMSSEInfo armsse_variants[] = { .sys_version = 0x41743, .sys_config_format = IoTKitFormat, .has_mhus = false, + .has_ppus = false, }, }; @@ -265,6 +267,29 @@ static void armsse_init(Object *obj) 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); + } + } object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate, sizeof(s->nmi_orgate), TYPE_OR_IRQ, &error_abort, NULL); @@ -329,6 +354,17 @@ static qemu_irq armsse_get_common_irq_in(ARMSSE *s, int irqno) } } +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); @@ -833,6 +869,28 @@ static void armsse_realize(DeviceState *dev, Error **errp) } 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) { diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h index dbfcb28060..9855ec5f26 100644 --- a/include/hw/arm/armsse.h +++ b/include/hw/arm/armsse.h @@ -106,6 +106,16 @@ #define SSE_MAX_CPUS 2 +/* These define what each PPU in the ppu[] index is for */ +#define CPU0CORE_PPU 0 +#define CPU1CORE_PPU 1 +#define DBG_PPU 2 +#define RAM0_PPU 3 +#define RAM1_PPU 4 +#define RAM2_PPU 5 +#define RAM3_PPU 6 +#define NUM_PPUS 7 + typedef struct ARMSSE { /*< private >*/ SysBusDevice parent_obj; @@ -139,6 +149,7 @@ typedef struct ARMSSE { IoTKitSysCtl sysinfo; UnimplementedDeviceState mhu[2]; + UnimplementedDeviceState ppu[NUM_PPUS]; /* * 'container' holds all devices seen by all CPUs. -- cgit v1.2.3-70-g09d2 From 2357bca5328e9f6b1e0f14a3ac62a7f8b1aef557 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:43 +0000 Subject: hw/arm/armsse: Add unimplemented-device stub for cache control registers The SSE-200 gives each CPU a register bank to use to control its L1 instruction cache. Put in an unimplemented-device stub for this. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-18-peter.maydell@linaro.org --- hw/arm/armsse.c | 39 ++++++++++++++++++++++++++++++++++++++- include/hw/arm/armsse.h | 1 + 2 files changed, 39 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 280ba5c78b..41e4a781e1 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -32,6 +32,7 @@ struct ARMSSEInfo { SysConfigFormat sys_config_format; bool has_mhus; bool has_ppus; + bool has_cachectrl; }; static const ARMSSEInfo armsse_variants[] = { @@ -43,6 +44,7 @@ static const ARMSSEInfo armsse_variants[] = { .sys_config_format = IoTKitFormat, .has_mhus = false, .has_ppus = false, + .has_cachectrl = false, }, }; @@ -290,6 +292,16 @@ static void armsse_init(Object *obj) 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); + } + } object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate, sizeof(s->nmi_orgate), TYPE_OR_IRQ, &error_abort, NULL); @@ -795,7 +807,32 @@ static void armsse_realize(DeviceState *dev, Error **errp) qdev_connect_gpio_out(DEVICE(&s->ppc_irq_orgate), 0, armsse_get_common_irq_in(s, 10)); - /* 0x40010000 .. 0x4001ffff: private CPU region: unused in IoTKit */ + /* + * 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); + } + } /* 0x40020000 .. 0x4002ffff : ARMSSE system control peripheral region */ /* Devices behind APB PPC1: diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h index 9855ec5f26..9d830057d5 100644 --- a/include/hw/arm/armsse.h +++ b/include/hw/arm/armsse.h @@ -150,6 +150,7 @@ typedef struct ARMSSE { UnimplementedDeviceState mhu[2]; UnimplementedDeviceState ppu[NUM_PPUS]; + UnimplementedDeviceState cachectrl[SSE_MAX_CPUS]; /* * 'container' holds all devices seen by all CPUs. -- cgit v1.2.3-70-g09d2 From c1f572579eefe18e56c7135e2a7c0698f0488b92 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:43 +0000 Subject: hw/arm/armsse: Add unimplemented-device stub for CPU local control registers The SSE-200 has a "CPU local security control" register bank; add an unimplemented-device stub for it. (The register bank has only one interesting register, which allows the guest to lock down changes to various CPU registers so they cannot be modified further. We don't support that in our Cortex-M33 model anyway.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-19-peter.maydell@linaro.org --- hw/arm/armsse.c | 31 +++++++++++++++++++++++++++++++ include/hw/arm/armsse.h | 1 + 2 files changed, 32 insertions(+) (limited to 'include') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 41e4a781e1..9c111ac6a4 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -33,6 +33,7 @@ struct ARMSSEInfo { bool has_mhus; bool has_ppus; bool has_cachectrl; + bool has_cpusecctrl; }; static const ARMSSEInfo armsse_variants[] = { @@ -45,6 +46,7 @@ static const ARMSSEInfo armsse_variants[] = { .has_mhus = false, .has_ppus = false, .has_cachectrl = false, + .has_cpusecctrl = false, }, }; @@ -302,6 +304,16 @@ static void armsse_init(Object *obj) 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); + } + } object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate, sizeof(s->nmi_orgate), TYPE_OR_IRQ, &error_abort, NULL); @@ -833,6 +845,25 @@ static void armsse_realize(DeviceState *dev, Error **errp) 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); + } + } /* 0x40020000 .. 0x4002ffff : ARMSSE system control peripheral region */ /* Devices behind APB PPC1: diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h index 9d830057d5..961dbb3032 100644 --- a/include/hw/arm/armsse.h +++ b/include/hw/arm/armsse.h @@ -151,6 +151,7 @@ typedef struct ARMSSE { UnimplementedDeviceState mhu[2]; UnimplementedDeviceState ppu[NUM_PPUS]; UnimplementedDeviceState cachectrl[SSE_MAX_CPUS]; + UnimplementedDeviceState cpusecctrl[SSE_MAX_CPUS]; /* * 'container' holds all devices seen by all CPUs. -- cgit v1.2.3-70-g09d2 From 5aeb36896600ff92aee1083ed17e80f069befb93 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:43 +0000 Subject: hw/misc/armsse-cpuid: Implement SSE-200 CPU_IDENTITY register block The SSE-200 has a CPU_IDENTITY register block, which is a set of read-only registers. As well as the usual PID/CID registers, there is a single CPUID register which indicates whether the CPU is CPU 0 or CPU 1. Implement a model of this register block. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-20-peter.maydell@linaro.org --- MAINTAINERS | 2 + default-configs/arm-softmmu.mak | 1 + hw/misc/Makefile.objs | 1 + hw/misc/armsse-cpuid.c | 134 ++++++++++++++++++++++++++++++++++++++++ hw/misc/trace-events | 4 ++ include/hw/misc/armsse-cpuid.h | 41 ++++++++++++ 6 files changed, 183 insertions(+) create mode 100644 hw/misc/armsse-cpuid.c create mode 100644 include/hw/misc/armsse-cpuid.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index c2ad4e52c9..a70ecb1a69 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -631,6 +631,8 @@ F: hw/misc/iotkit-sysctl.c F: include/hw/misc/iotkit-sysctl.h F: hw/misc/iotkit-sysinfo.c F: include/hw/misc/iotkit-sysinfo.h +F: hw/misc/armsse-cpuid.c +F: include/hw/misc/armsse-cpuid.h Musicpal M: Jan Kiszka diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 3f20015787..be88870799 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -118,6 +118,7 @@ CONFIG_ARMSSE=y CONFIG_IOTKIT_SECCTL=y CONFIG_IOTKIT_SYSCTL=y CONFIG_IOTKIT_SYSINFO=y +CONFIG_ARMSSE_CPUID=y CONFIG_VERSATILE=y CONFIG_VERSATILE_PCI=y diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 04f3bfa516..74c91d250c 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 0000000000..7788f6ced6 --- /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/trace-events b/hw/misc/trace-events index 52466c77c4..b0701bddd3 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/include/hw/misc/armsse-cpuid.h b/include/hw/misc/armsse-cpuid.h new file mode 100644 index 0000000000..0ef33fcaba --- /dev/null +++ b/include/hw/misc/armsse-cpuid.h @@ -0,0 +1,41 @@ +/* + * 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 + * + * QEMU interface: + * + QOM property "CPUID": the value to use for the CPUID register + * + sysbus MMIO region 0: the system information register bank + */ + +#ifndef HW_MISC_ARMSSE_CPUID_H +#define HW_MISC_ARMSSE_CPUID_H + +#include "hw/sysbus.h" + +#define TYPE_ARMSSE_CPUID "armsse-cpuid" +#define ARMSSE_CPUID(obj) OBJECT_CHECK(ARMSSECPUID, (obj), TYPE_ARMSSE_CPUID) + +typedef struct ARMSSECPUID { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + + /* Properties */ + uint32_t cpuid; +} ARMSSECPUID; + +#endif -- cgit v1.2.3-70-g09d2 From ade67dcd4ac1786637d25bb04a13c836357f44fc Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:43 +0000 Subject: hw/arm/armsse: Add CPU_IDENTITY block to SSE-200 Instantiate a copy of the CPU_IDENTITY register block for each CPU in an SSE-200. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-21-peter.maydell@linaro.org --- hw/arm/armsse.c | 28 ++++++++++++++++++++++++++++ include/hw/arm/armsse.h | 3 +++ 2 files changed, 31 insertions(+) (limited to 'include') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 9c111ac6a4..eb691faf72 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -34,6 +34,7 @@ struct ARMSSEInfo { bool has_ppus; bool has_cachectrl; bool has_cpusecctrl; + bool has_cpuid; }; static const ARMSSEInfo armsse_variants[] = { @@ -47,6 +48,7 @@ static const ARMSSEInfo armsse_variants[] = { .has_ppus = false, .has_cachectrl = false, .has_cpusecctrl = false, + .has_cpuid = false, }, }; @@ -314,6 +316,16 @@ static void armsse_init(Object *obj) 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); @@ -864,6 +876,22 @@ static void armsse_realize(DeviceState *dev, Error **errp) 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: diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h index 961dbb3032..3914e8e4bf 100644 --- a/include/hw/arm/armsse.h +++ b/include/hw/arm/armsse.h @@ -78,6 +78,7 @@ #include "hw/watchdog/cmsdk-apb-watchdog.h" #include "hw/misc/iotkit-sysctl.h" #include "hw/misc/iotkit-sysinfo.h" +#include "hw/misc/armsse-cpuid.h" #include "hw/misc/unimp.h" #include "hw/or-irq.h" #include "hw/core/split-irq.h" @@ -153,6 +154,8 @@ typedef struct ARMSSE { UnimplementedDeviceState cachectrl[SSE_MAX_CPUS]; UnimplementedDeviceState cpusecctrl[SSE_MAX_CPUS]; + ARMSSECPUID cpuid[SSE_MAX_CPUS]; + /* * 'container' holds all devices seen by all CPUs. * 'cpu_container[i]' is the view that CPU i has: this has the -- cgit v1.2.3-70-g09d2 From 0829d24e6646e23507917859eea96f163eb62637 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:43 +0000 Subject: hw/arm/armsse: Add SSE-200 model Add a model of the SSE-200, now we have put in all the code that lets us make it different from the IoTKit. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-22-peter.maydell@linaro.org --- hw/arm/armsse.c | 12 ++++++++++++ include/hw/arm/armsse.h | 19 ++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index eb691faf72..5d53071a5a 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -50,6 +50,18 @@ static const ARMSSEInfo armsse_variants[] = { .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) diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h index 3914e8e4bf..f800bafb14 100644 --- a/include/hw/arm/armsse.h +++ b/include/hw/arm/armsse.h @@ -1,5 +1,5 @@ /* - * ARM SSE (Subsystems for Embedded): IoTKit + * ARM SSE (Subsystems for Embedded): IoTKit, SSE-200 * * Copyright (c) 2018 Linaro Limited * Written by Peter Maydell @@ -12,9 +12,13 @@ /* * This is a model of the Arm "Subsystems for Embedded" family of * hardware, which include the IoT Kit and the SSE-050, SSE-100 and - * SSE-200. Currently we model only the Arm IoT Kit which is documented in + * SSE-200. Currently we model: + * - the Arm IoT Kit which is documented in * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html - * It contains: + * - the SSE-200 which is 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 + * + * The IoTKit contains: * a Cortex-M33 * the IDAU * some timers and watchdogs @@ -23,6 +27,14 @@ * a security controller * a bus fabric which arranges that some parts of the address * space are secure and non-secure aliases of each other + * The SSE-200 additionally contains: + * a second Cortex-M33 + * two Message Handling Units (MHUs) + * an optional CryptoCell (which we do not model) + * more SRAM banks with associated MPCs + * multiple Power Policy Units (PPUs) + * a control interface for an icache for each CPU + * per-CPU identity and control register blocks * * QEMU interface: * + QOM property "memory" is a MemoryRegion containing the devices provided @@ -93,6 +105,7 @@ * them via the ARMSSE base class, so they have no IOTKIT() etc macros. */ #define TYPE_IOTKIT "iotkit" +#define TYPE_SSE200 "sse-200" /* We have an IRQ splitter and an OR gate input for each external PPC * and the 2 internal PPCs -- cgit v1.2.3-70-g09d2 From 42f6ed919325413392bea247a1e6f135deb469cd Mon Sep 17 00:00:00 2001 From: Julia Suvorova Date: Fri, 1 Feb 2019 14:55:46 +0000 Subject: arm: Clarify the logic of set_pc() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until now, the set_pc logic was unclear, which raised questions about whether it should be used directly, applying a value to PC or adding additional checks, for example, set the Thumb bit in Arm cpu. Let's set the set_pc logic for “Configure the PC, as was done in the ELF file” and implement synchronize_with_tb hook for preserving PC to cpu_tb_exec. Signed-off-by: Julia Suvorova Acked-by: Stefan Hajnoczi Message-id: 20190129121817.7109-1-jusual@mail.ru Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/boot.c | 4 ---- include/qom/cpu.h | 16 ++++++++++++++-- target/arm/arm-powerctl.c | 3 --- target/arm/cpu.c | 26 +++++++++++++++++++++++++- target/arm/cpu64.c | 15 --------------- 5 files changed, 39 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/hw/arm/boot.c b/hw/arm/boot.c index c7a67af7a9..05762d0fc1 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/include/qom/cpu.h b/include/qom/cpu.h index 4c2feb9c17..1d6099e5d4 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -103,9 +103,21 @@ struct TranslationBlock; * @get_arch_id: Callback for getting architecture-dependent CPU ID. * @get_paging_enabled: Callback for inquiring whether paging is enabled. * @get_memory_mapping: Callback for obtaining the memory mappings. - * @set_pc: Callback for setting the Program Counter register. + * @set_pc: Callback for setting the Program Counter register. This + * should have the semantics used by the target architecture when + * setting the PC from a source such as an ELF file entry point; + * for example on Arm it will also set the Thumb mode bit based + * on the least significant bit of the new PC value. + * If the target behaviour here is anything other than "set + * the PC register to the value passed in" then the target must + * also implement the synchronize_from_tb hook. * @synchronize_from_tb: Callback for synchronizing state from a TCG - * #TranslationBlock. + * #TranslationBlock. This is called when we abandon execution + * of a TB before starting it, and must set all parts of the CPU + * state which the previous TB in the chain may not have updated. + * This always includes at least the program counter; some targets + * will need to do more. If this hook is not implemented then the + * default is to call @set_pc(tb->pc). * @handle_mmu_fault: Callback for handling an MMU fault. * @get_phys_page_debug: Callback for obtaining a physical address. * @get_phys_page_attrs_debug: Callback for obtaining a physical address and the diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c index 2b856930fb..f9de5164e5 100644 --- a/target/arm/arm-powerctl.c +++ b/target/arm/arm-powerctl.c @@ -120,11 +120,8 @@ static void arm_set_cpu_on_async_work(CPUState *target_cpu_state, if (info->target_aa64) { target_cpu->env.xregs[0] = info->context_id; - target_cpu->env.thumb = false; } else { target_cpu->env.regs[0] = info->context_id; - target_cpu->env.thumb = info->entry & 1; - info->entry &= 0xfffffffe; } /* Start the new CPU at the requested address */ diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 8a9cd0900d..f00d450d0b 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -40,8 +40,31 @@ static void arm_cpu_set_pc(CPUState *cs, vaddr value) { ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + if (is_a64(env)) { + env->pc = value; + env->thumb = 0; + } else { + env->regs[15] = value & ~1; + env->thumb = value & 1; + } +} - cpu->env.regs[15] = value; +static void arm_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + /* + * It's OK to look at env for the current mode here, because it's + * never possible for an AArch64 TB to chain to an AArch32 TB. + */ + if (is_a64(env)) { + env->pc = tb->pc; + } else { + env->regs[15] = tb->pc; + } } static bool arm_cpu_has_work(CPUState *cs) @@ -2099,6 +2122,7 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) cc->cpu_exec_interrupt = arm_cpu_exec_interrupt; cc->dump_state = arm_cpu_dump_state; cc->set_pc = arm_cpu_set_pc; + cc->synchronize_from_tb = arm_cpu_synchronize_from_tb; cc->gdb_read_register = arm_cpu_gdb_read_register; cc->gdb_write_register = arm_cpu_gdb_write_register; #ifdef CONFIG_USER_ONLY diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index e9bc461c36..8653cecd03 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -480,20 +480,6 @@ static void aarch64_cpu_finalizefn(Object *obj) { } -static void aarch64_cpu_set_pc(CPUState *cs, vaddr value) -{ - ARMCPU *cpu = ARM_CPU(cs); - /* It's OK to look at env for the current mode here, because it's - * never possible for an AArch64 TB to chain to an AArch32 TB. - * (Otherwise we would need to use synchronize_from_tb instead.) - */ - if (is_a64(&cpu->env)) { - cpu->env.pc = value; - } else { - cpu->env.regs[15] = value; - } -} - static gchar *aarch64_gdb_arch_name(CPUState *cs) { return g_strdup("aarch64"); @@ -504,7 +490,6 @@ static void aarch64_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); cc->cpu_exec_interrupt = arm_cpu_exec_interrupt; - cc->set_pc = aarch64_cpu_set_pc; cc->gdb_read_register = aarch64_cpu_gdb_read_register; cc->gdb_write_register = aarch64_cpu_gdb_write_register; cc->gdb_num_core_regs = 34; -- cgit v1.2.3-70-g09d2 From c0d4eb83526e2ba5a8def0710d183a9387090ab6 Mon Sep 17 00:00:00 2001 From: Steffen Görtz Date: Fri, 1 Feb 2019 10:33:55 +0800 Subject: hw/nvram/nrf51_nvm: Add nRF51 non-volatile memories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nRF51 contains three regions of non-volatile memory (NVM): - CODE (R/W): contains code - FICR (R): Factory information like code size, chip id etc. - UICR (R/W): Changeable configuration data. Lock bits, Code protection configuration, Bootloader address, Nordic SoftRadio configuration, Firmware configuration. Read and write access to the memories is managed by the Non-volatile memory controller. Memory schema: [ CPU ] -+- [ NVM, either FICR, UICR or CODE ] | | \- [ NVMC ] Signed-off-by: Steffen Görtz Signed-off-by: Stefan Hajnoczi Tested-by: Joel Stanley Reviewed-by: Peter Maydell Message-id: 20190201023357.22596-2-stefanha@redhat.com Signed-off-by: Peter Maydell --- hw/nvram/Makefile.objs | 1 + hw/nvram/nrf51_nvm.c | 388 +++++++++++++++++++++++++++++++++++++++++++ include/hw/nvram/nrf51_nvm.h | 64 +++++++ 3 files changed, 453 insertions(+) create mode 100644 hw/nvram/nrf51_nvm.c create mode 100644 include/hw/nvram/nrf51_nvm.h (limited to 'include') diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs index b318e53a43..26f7b4ca35 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 0000000000..7d94cef1db --- /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 + * + * 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) diff --git a/include/hw/nvram/nrf51_nvm.h b/include/hw/nvram/nrf51_nvm.h new file mode 100644 index 0000000000..3792e4a9fe --- /dev/null +++ b/include/hw/nvram/nrf51_nvm.h @@ -0,0 +1,64 @@ +/* + * 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. + * + * QEMU interface: + * + sysbus MMIO regions 0: NVMC peripheral registers + * + sysbus MMIO regions 1: FICR peripheral registers + * + sysbus MMIO regions 2: UICR peripheral registers + * + flash-size property: flash size in bytes. + * + * Accuracy of the peripheral model: + * + Code regions (MPU configuration) are disregarded. + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + */ +#ifndef NRF51_NVM_H +#define NRF51_NVM_H + +#include "hw/sysbus.h" +#define TYPE_NRF51_NVM "nrf51_soc.nvm" +#define NRF51_NVM(obj) OBJECT_CHECK(NRF51NVMState, (obj), TYPE_NRF51_NVM) + +#define NRF51_UICR_FIXTURE_SIZE 64 + +#define NRF51_NVMC_SIZE 0x1000 + +#define NRF51_NVMC_READY 0x400 +#define NRF51_NVMC_READY_READY 0x01 +#define NRF51_NVMC_CONFIG 0x504 +#define NRF51_NVMC_CONFIG_MASK 0x03 +#define NRF51_NVMC_CONFIG_WEN 0x01 +#define NRF51_NVMC_CONFIG_EEN 0x02 +#define NRF51_NVMC_ERASEPCR1 0x508 +#define NRF51_NVMC_ERASEPCR0 0x510 +#define NRF51_NVMC_ERASEALL 0x50C +#define NRF51_NVMC_ERASEUICR 0x514 +#define NRF51_NVMC_ERASE 0x01 + +#define NRF51_UICR_SIZE 0x100 + +typedef struct NRF51NVMState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + MemoryRegion ficr; + MemoryRegion uicr; + MemoryRegion flash; + + uint32_t uicr_content[NRF51_UICR_FIXTURE_SIZE]; + uint32_t flash_size; + uint8_t *storage; + + uint32_t config; + +} NRF51NVMState; + + +#endif -- cgit v1.2.3-70-g09d2 From 4d744b25d37804279f94800b62f3d765177d6493 Mon Sep 17 00:00:00 2001 From: Steffen Görtz Date: Fri, 1 Feb 2019 10:33:56 +0800 Subject: arm: Instantiate NRF51 special NVM's and NVMC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instantiates UICR, FICR, FLASH and NVMC in nRF51 SOC. Signed-off-by: Steffen Görtz Reviewed-by: Peter Maydell Reviewed-by: Stefan Hajnoczi Signed-off-by: Stefan Hajnoczi Message-id: 20190201023357.22596-3-stefanha@redhat.com Signed-off-by: Peter Maydell --- hw/arm/nrf51_soc.c | 41 +++++++++++++++++++++++++++++------------ include/hw/arm/nrf51_soc.h | 2 ++ 2 files changed, 31 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index 3a1c7e200c..bbaf050103 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,14 +83,6 @@ 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, OBJECT(s), "nrf51.sram", s->sram_size, &err); if (err) { @@ -122,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) { @@ -159,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); } @@ -187,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/include/hw/arm/nrf51_soc.h b/include/hw/arm/nrf51_soc.h index fbdefc07e4..fd7fcc71a5 100644 --- a/include/hw/arm/nrf51_soc.h +++ b/include/hw/arm/nrf51_soc.h @@ -15,6 +15,7 @@ #include "hw/char/nrf51_uart.h" #include "hw/misc/nrf51_rng.h" #include "hw/gpio/nrf51_gpio.h" +#include "hw/nvram/nrf51_nvm.h" #include "hw/timer/nrf51_timer.h" #define TYPE_NRF51_SOC "nrf51-soc" @@ -32,6 +33,7 @@ typedef struct NRF51State { NRF51UARTState uart; NRF51RNGState rng; + NRF51NVMState nvm; NRF51GPIOState gpio; NRF51TimerState timer[NRF51_NUM_TIMERS]; -- cgit v1.2.3-70-g09d2