From 287a7f6e39cfc44a39909862365fddfc90778968 Mon Sep 17 00:00:00 2001 From: kumar sourav Date: Fri, 1 Feb 2019 14:55:41 +0000 Subject: hw/arm/nrf51_soc: set object owner in memory_region_init_ram MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit set object owner in memory_region_init_ram() instead of NULL. Signed-off-by: kumar sourav Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Message-id: 20190125155630.17430-1-sourav.jb1988@gmail.com Signed-off-by: Peter Maydell --- hw/arm/nrf51_soc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'hw') diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index 1630c27..3a1c7e2 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -89,7 +89,8 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) } memory_region_add_subregion(&s->container, NRF51_FLASH_BASE, &s->flash); - memory_region_init_ram(&s->sram, NULL, "nrf51.sram", s->sram_size, &err); + memory_region_init_ram(&s->sram, OBJECT(s), "nrf51.sram", s->sram_size, + &err); if (err) { error_propagate(errp, err); return; -- cgit v1.1 From 3693f217d38f053fa5a7fcd2841c07926c026218 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:41 +0000 Subject: armv7m: Don't assume the NVIC's CPU is CPU 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the ARMv7M NVIC object's realize method assumes that the CPU the NVIC is attached to is CPU 0, because it thinks there can only ever be one CPU in the system. To allow a dual-Cortex-M33 setup we need to remove this assumption; instead the armv7m wrapper object tells the NVIC its CPU, in the same way that it already tells the CPU what the NVIC is. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-2-peter.maydell@linaro.org --- hw/arm/armv7m.c | 6 ++++-- hw/intc/armv7m_nvic.c | 3 +-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'hw') diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index f444652..f9aa83d 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -178,10 +178,12 @@ static void armv7m_realize(DeviceState *dev, Error **errp) } } - /* Tell the CPU where the NVIC is; it will fail realize if it doesn't - * have one. + /* + * Tell the CPU where the NVIC is; it will fail realize if it doesn't + * have one. Similarly, tell the NVIC where its CPU is. */ s->cpu->env.nvic = &s->nvic; + s->nvic.cpu = s->cpu; object_property_set_bool(OBJECT(s->cpu), true, "realized", &err); if (err != NULL) { diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 0beefb0..790a3d9 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -2274,8 +2274,7 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp) Error *err = NULL; int regionlen; - s->cpu = ARM_CPU(qemu_get_cpu(0)); - + /* The armv7m container object will have set our CPU pointer */ if (!s->cpu || !arm_feature(&s->cpu->env, ARM_FEATURE_M)) { error_setg(errp, "The NVIC can only be used with a Cortex-M CPU"); return; -- cgit v1.1 From e4c81e3a451f9ce692f2644b395facbbdc3891cb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:41 +0000 Subject: armv7m: Make cpu object a child of the armv7m container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than just creating the CPUs with object_new, make them child objects of the armv7m container. This will allow the cluster code to find the CPUs if an armv7m object is made a child of a cluster object. object_new_with_props() will do the parenting for us. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-3-peter.maydell@linaro.org --- hw/arm/armv7m.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'hw') diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index f9aa83d..0f2c8e0 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -158,7 +158,12 @@ static void armv7m_realize(DeviceState *dev, Error **errp) memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); - s->cpu = ARM_CPU(object_new(s->cpu_type)); + s->cpu = ARM_CPU(object_new_with_props(s->cpu_type, OBJECT(s), "cpu", + &err, NULL)); + if (err != NULL) { + error_propagate(errp, err); + return; + } object_property_set_link(OBJECT(s->cpu), OBJECT(&s->container), "memory", &error_abort); -- cgit v1.1 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 --- hw/arm/armv7m.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'hw') diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 0f2c8e0..adae11e 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -182,6 +182,14 @@ static void armv7m_realize(DeviceState *dev, Error **errp) return; } } + if (object_property_find(OBJECT(s->cpu), "start-powered-off", NULL)) { + object_property_set_bool(OBJECT(s->cpu), s->start_powered_off, + "start-powered-off", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + } /* * Tell the CPU where the NVIC is; it will fail realize if it doesn't @@ -250,6 +258,8 @@ static Property armv7m_properties[] = { DEFINE_PROP_LINK("idau", ARMv7MState, idau, TYPE_IDAU_INTERFACE, Object *), DEFINE_PROP_UINT32("init-svtor", ARMv7MState, init_svtor, 0), DEFINE_PROP_BOOL("enable-bitband", ARMv7MState, enable_bitband, false), + DEFINE_PROP_BOOL("start-powered-off", ARMv7MState, start_powered_off, + false), DEFINE_PROP_END_OF_LIST(), }; -- cgit v1.1 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 +- 2 files changed, 31 insertions(+), 30 deletions(-) (limited to 'hw') diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c index 8742200..9360053 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 82b1d02..5824335 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; -- cgit v1.1 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 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) (limited to 'hw') diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c index 9360053..d5b1729 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); -- cgit v1.1 From 13628891b397386a10a8e3288f31d6ee6ab0bfc3 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' local variables and functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename various internal uses of 'iotkit' in hw/arm/iotkit.c to 'armsse', for consistency. The remaining occurences are: * related to the devices TYPE_IOTKIT_SYSCTL, TYPE_IOTKIT_SYSINFO, etc, which this refactor is not touching * references that apply specifically to the IoTKit (like the lack of a private CPU region) * the vmstate, which keeps its old "iotkit" name for migration compatibility reasons Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-7-peter.maydell@linaro.org --- hw/arm/iotkit.c | 68 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) (limited to 'hw') diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c index d5b1729..7ff14fd5 100644 --- a/hw/arm/iotkit.c +++ b/hw/arm/iotkit.c @@ -56,7 +56,7 @@ static void nsccfg_handler(void *opaque, int n, int level) s->nsccfg = level; } -static void iotkit_forward_ppc(ARMSSE *s, const char *ppcname, int ppcnum) +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 @@ -65,22 +65,22 @@ static void iotkit_forward_ppc(ARMSSE *s, const char *ppcname, int ppcnum) * code using the ARMSSE can wire them up to the PPCs. */ SplitIRQ *splitter = &s->ppc_irq_splitter[ppcnum]; - DeviceState *iotkitdev = DEVICE(s); + 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, iotkitdev, name); + qdev_pass_gpios(dev_secctl, armssedev, name); g_free(name); name = g_strdup_printf("%s_ap", ppcname); - qdev_pass_gpios(dev_secctl, iotkitdev, name); + qdev_pass_gpios(dev_secctl, armssedev, name); g_free(name); name = g_strdup_printf("%s_irq_enable", ppcname); - qdev_pass_gpios(dev_secctl, iotkitdev, name); + qdev_pass_gpios(dev_secctl, armssedev, name); g_free(name); name = g_strdup_printf("%s_irq_clear", ppcname); - qdev_pass_gpios(dev_secctl, iotkitdev, name); + qdev_pass_gpios(dev_secctl, armssedev, name); g_free(name); /* irq_status is a little more tricky, because we need to @@ -96,15 +96,15 @@ static void iotkit_forward_ppc(ARMSSE *s, const char *ppcname, int ppcnum) qdev_connect_gpio_out(dev_splitter, 1, qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), ppcnum)); s->irq_status_in[ppcnum] = qdev_get_gpio_in(dev_splitter, 0); - qdev_init_gpio_in_named_with_opaque(iotkitdev, irq_status_forwarder, + qdev_init_gpio_in_named_with_opaque(armssedev, irq_status_forwarder, s->irq_status_in[ppcnum], name, 1); g_free(name); } -static void iotkit_forward_sec_resp_cfg(ARMSSE *s) +static void armsse_forward_sec_resp_cfg(ARMSSE *s) { /* Forward the 3rd output from the splitter device as a - * named GPIO output of the iotkit object. + * named GPIO output of the armsse object. */ DeviceState *dev = DEVICE(s); DeviceState *dev_splitter = DEVICE(&s->sec_resp_splitter); @@ -115,12 +115,12 @@ static void iotkit_forward_sec_resp_cfg(ARMSSE *s) qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in); } -static void iotkit_init(Object *obj) +static void armsse_init(Object *obj) { ARMSSE *s = ARMSSE(obj); int i; - memory_region_init(&s->container, obj, "iotkit-container", UINT64_MAX); + memory_region_init(&s->container, obj, "armsse-container", UINT64_MAX); sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), TYPE_ARMV7M); @@ -160,9 +160,9 @@ static void iotkit_init(Object *obj) sizeof(s->nswatchdog), TYPE_CMSDK_APB_WATCHDOG); sysbus_init_child_obj(obj, "swatchdog", &s->swatchdog, sizeof(s->swatchdog), TYPE_CMSDK_APB_WATCHDOG); - sysbus_init_child_obj(obj, "iotkit-sysctl", &s->sysctl, + sysbus_init_child_obj(obj, "armsse-sysctl", &s->sysctl, sizeof(s->sysctl), TYPE_IOTKIT_SYSCTL); - sysbus_init_child_obj(obj, "iotkit-sysinfo", &s->sysinfo, + 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, @@ -183,20 +183,20 @@ static void iotkit_init(Object *obj) } } -static void iotkit_exp_irq(void *opaque, int n, int level) +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 iotkit_mpcexp_status(void *opaque, int n, int 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 iotkit_realize(DeviceState *dev, Error **errp) +static void armsse_realize(DeviceState *dev, Error **errp) { ARMSSE *s = ARMSSE(dev); int i; @@ -287,7 +287,7 @@ static void iotkit_realize(DeviceState *dev, Error **errp) for (i = 0; i < s->exp_numirq; i++) { s->exp_irqs[i] = qdev_get_gpio_in(DEVICE(&s->armv7m), i + 32); } - qdev_init_gpio_in_named(dev, iotkit_exp_irq, "EXP_IRQ", s->exp_numirq); + 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); @@ -336,7 +336,7 @@ static void iotkit_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(dev_splitter, 0)); /* This RAM lives behind the Memory Protection Controller */ - memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err); + memory_region_init_ram(&s->sram0, NULL, "armsse.sram0", 0x00008000, &err); if (err) { error_propagate(errp, err); return; @@ -608,14 +608,14 @@ static void iotkit_realize(DeviceState *dev, Error **errp) for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) { char *ppcname = g_strdup_printf("ahb_ppcexp%d", i); - iotkit_forward_ppc(s, ppcname, i); + 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); - iotkit_forward_ppc(s, ppcname, i + IOTS_NUM_AHB_EXP_PPC); + armsse_forward_ppc(s, ppcname, i + IOTS_NUM_AHB_EXP_PPC); g_free(ppcname); } @@ -672,10 +672,10 @@ static void iotkit_realize(DeviceState *dev, Error **errp) /* Create GPIO inputs which will pass the line state for our * mpcexp_irq inputs to the correct splitter devices. */ - qdev_init_gpio_in_named(dev, iotkit_mpcexp_status, "mpcexp_status", + qdev_init_gpio_in_named(dev, armsse_mpcexp_status, "mpcexp_status", IOTS_NUM_EXP_MPC); - iotkit_forward_sec_resp_cfg(s); + armsse_forward_sec_resp_cfg(s); /* Forward the MSC related signals */ qdev_pass_gpios(dev_secctl, dev, "mscexp_status"); @@ -695,7 +695,7 @@ static void iotkit_realize(DeviceState *dev, Error **errp) system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq; } -static void iotkit_idau_check(IDAUInterface *ii, uint32_t address, +static void armsse_idau_check(IDAUInterface *ii, uint32_t address, int *iregion, bool *exempt, bool *ns, bool *nsc) { /* @@ -713,7 +713,7 @@ static void iotkit_idau_check(IDAUInterface *ii, uint32_t address, *iregion = region; } -static const VMStateDescription iotkit_vmstate = { +static const VMStateDescription armsse_vmstate = { .name = "iotkit", .version_id = 1, .minimum_version_id = 1, @@ -723,7 +723,7 @@ static const VMStateDescription iotkit_vmstate = { } }; -static Property iotkit_properties[] = { +static Property armsse_properties[] = { DEFINE_PROP_LINK("memory", ARMSSE, board_memory, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64), @@ -731,24 +731,24 @@ static Property iotkit_properties[] = { DEFINE_PROP_END_OF_LIST() }; -static void iotkit_reset(DeviceState *dev) +static void armsse_reset(DeviceState *dev) { ARMSSE *s = ARMSSE(dev); s->nsccfg = 0; } -static void iotkit_class_init(ObjectClass *klass, void *data) +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 = iotkit_realize; - dc->vmsd = &iotkit_vmstate; - dc->props = iotkit_properties; - dc->reset = iotkit_reset; - iic->check = iotkit_idau_check; + dc->realize = armsse_realize; + dc->vmsd = &armsse_vmstate; + dc->props = armsse_properties; + dc->reset = armsse_reset; + iic->check = armsse_idau_check; asc->info = data; } @@ -756,7 +756,7 @@ static const TypeInfo armsse_info = { .name = TYPE_ARMSSE, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ARMSSE), - .instance_init = iotkit_init, + .instance_init = armsse_init, .abstract = true, .interfaces = (InterfaceInfo[]) { { TYPE_IDAU_INTERFACE }, @@ -774,7 +774,7 @@ static void armsse_register_types(void) TypeInfo ti = { .name = armsse_variants[i].name, .parent = TYPE_ARMSSE, - .class_init = iotkit_class_init, + .class_init = armsse_class_init, .class_data = (void *)&armsse_variants[i], }; type_register(&ti); -- cgit v1.1 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 --- hw/arm/Makefile.objs | 2 +- hw/arm/armsse.c | 784 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/arm/iotkit.c | 784 --------------------------------------------------- hw/arm/mps2-tz.c | 2 +- 4 files changed, 786 insertions(+), 786 deletions(-) create mode 100644 hw/arm/armsse.c delete mode 100644 hw/arm/iotkit.c (limited to 'hw') diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 50c7b4a..22b7f0e 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -34,7 +34,7 @@ obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o obj-$(CONFIG_MPS2) += mps2.o obj-$(CONFIG_MPS2) += mps2-tz.o obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o -obj-$(CONFIG_IOTKIT) += iotkit.o +obj-$(CONFIG_ARMSSE) += armsse.o obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o smmuv3.o obj-$(CONFIG_FSL_IMX6UL) += fsl-imx6ul.o mcimx6ul-evk.o diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c new file mode 100644 index 0000000..8554be1 --- /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 7ff14fd5..0000000 --- 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 5824335..3859f17 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" -- cgit v1.1 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 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'hw') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 8554be1..074c1d3 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 2222b3e..537601c 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -600,7 +600,7 @@ static void iotkit_secctl_mpc_status(void *opaque, int n, int level) { IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); - s->mpcintstatus = deposit32(s->mpcintstatus, 0, 1, !!level); + s->mpcintstatus = deposit32(s->mpcintstatus, n, 1, !!level); } static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level) @@ -686,7 +686,8 @@ static void iotkit_secctl_init(Object *obj) qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); qdev_init_gpio_out_named(dev, &s->nsc_cfg_irq, "nsc_cfg", 1); - qdev_init_gpio_in_named(dev, iotkit_secctl_mpc_status, "mpc_status", 1); + qdev_init_gpio_in_named(dev, iotkit_secctl_mpc_status, "mpc_status", + IOTS_NUM_MPC); qdev_init_gpio_in_named(dev, iotkit_secctl_mpcexp_status, "mpcexp_status", IOTS_NUM_EXP_MPC); -- cgit v1.1 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 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 29 deletions(-) (limited to 'hw') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 074c1d3..b639b54 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, -- cgit v1.1 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 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'hw') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index b639b54..a2ae5d3 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() }; -- cgit v1.1 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 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 164 insertions(+), 42 deletions(-) (limited to 'hw') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index a2ae5d3..5cb2b78 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 -- cgit v1.1 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 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'hw') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 5cb2b78..2472dfe 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; -- cgit v1.1 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 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) (limited to 'hw') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 2472dfe..2eb4ea3 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); -- cgit v1.1 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 ++++++++++++-- 2 files changed, 64 insertions(+), 2 deletions(-) (limited to 'hw') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 2eb4ea3..19cae77 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 78955bc..026ba94 100644 --- a/hw/misc/iotkit-sysinfo.c +++ b/hw/misc/iotkit-sysinfo.c @@ -51,15 +51,16 @@ static const int sysinfo_id[] = { static uint64_t iotkit_sysinfo_read(void *opaque, hwaddr offset, unsigned size) { + IoTKitSysInfo *s = IOTKIT_SYSINFO(opaque); uint64_t r; switch (offset) { case A_SYS_VERSION: - r = 0x41743; + r = s->sys_version; break; case A_SYS_CONFIG: - r = 0x31; + r = s->sys_config; break; case A_PID4 ... A_CID3: r = sysinfo_id[(offset - A_PID4) / 4]; @@ -94,6 +95,12 @@ static const MemoryRegionOps iotkit_sysinfo_ops = { .valid.max_access_size = 4, }; +static Property iotkit_sysinfo_props[] = { + DEFINE_PROP_UINT32("SYS_VERSION", IoTKitSysInfo, sys_version, 0), + DEFINE_PROP_UINT32("SYS_CONFIG", IoTKitSysInfo, sys_config, 0), + DEFINE_PROP_END_OF_LIST() +}; + static void iotkit_sysinfo_init(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); @@ -106,10 +113,14 @@ static void iotkit_sysinfo_init(Object *obj) static void iotkit_sysinfo_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); + /* * This device has no guest-modifiable state and so it * does not need a reset function or VMState. */ + + dc->props = iotkit_sysinfo_props; } static const TypeInfo iotkit_sysinfo_info = { -- cgit v1.1 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 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'hw') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 19cae77..1f3dc89 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, -- cgit v1.1 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 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'hw') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 1f3dc89..280ba5c 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) { -- cgit v1.1 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 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) (limited to 'hw') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 280ba5c..41e4a78 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: -- cgit v1.1 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 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'hw') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 41e4a78..9c111ac 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: -- cgit v1.1 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 --- hw/misc/Makefile.objs | 1 + hw/misc/armsse-cpuid.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/misc/trace-events | 4 ++ 3 files changed, 139 insertions(+) create mode 100644 hw/misc/armsse-cpuid.c (limited to 'hw') diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 04f3bfa..74c91d2 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -69,6 +69,7 @@ obj-$(CONFIG_TZ_PPC) += tz-ppc.o obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o obj-$(CONFIG_IOTKIT_SYSCTL) += iotkit-sysctl.o obj-$(CONFIG_IOTKIT_SYSINFO) += iotkit-sysinfo.o +obj-$(CONFIG_ARMSSE_CPUID) += armsse-cpuid.o obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_AUX) += auxbus.o diff --git a/hw/misc/armsse-cpuid.c b/hw/misc/armsse-cpuid.c new file mode 100644 index 0000000..7788f6c --- /dev/null +++ b/hw/misc/armsse-cpuid.c @@ -0,0 +1,134 @@ +/* + * ARM SSE-200 CPU_IDENTITY register block + * + * Copyright (c) 2019 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "CPU_IDENTITY" register block which is part of the + * Arm SSE-200 and documented in + * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf + * + * It consists of one read-only CPUID register (set by QOM property), plus the + * usual ID registers. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/armsse-cpuid.h" + +REG32(CPUID, 0x0) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* PID/CID values */ +static const int sysinfo_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x58, 0xb8, 0x0b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static uint64_t armsse_cpuid_read(void *opaque, hwaddr offset, + unsigned size) +{ + ARMSSECPUID *s = ARMSSE_CPUID(opaque); + uint64_t r; + + switch (offset) { + case A_CPUID: + r = s->cpuid; + break; + case A_PID4 ... A_CID3: + r = sysinfo_id[(offset - A_PID4) / 4]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "SSE CPU_IDENTITY read: bad offset 0x%x\n", (int)offset); + r = 0; + break; + } + trace_armsse_cpuid_read(offset, r, size); + return r; +} + +static void armsse_cpuid_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + trace_armsse_cpuid_write(offset, value, size); + + qemu_log_mask(LOG_GUEST_ERROR, + "SSE CPU_IDENTITY: write to RO offset 0x%x\n", (int)offset); +} + +static const MemoryRegionOps armsse_cpuid_ops = { + .read = armsse_cpuid_read, + .write = armsse_cpuid_write, + .endianness = DEVICE_LITTLE_ENDIAN, + /* byte/halfword accesses are just zero-padded on reads and writes */ + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, +}; + +static Property armsse_cpuid_props[] = { + DEFINE_PROP_UINT32("CPUID", ARMSSECPUID, cpuid, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void armsse_cpuid_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + ARMSSECPUID *s = ARMSSE_CPUID(obj); + + memory_region_init_io(&s->iomem, obj, &armsse_cpuid_ops, + s, "armsse-cpuid", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void armsse_cpuid_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + /* + * This device has no guest-modifiable state and so it + * does not need a reset function or VMState. + */ + + dc->props = armsse_cpuid_props; +} + +static const TypeInfo armsse_cpuid_info = { + .name = TYPE_ARMSSE_CPUID, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ARMSSECPUID), + .instance_init = armsse_cpuid_init, + .class_init = armsse_cpuid_class_init, +}; + +static void armsse_cpuid_register_types(void) +{ + type_register_static(&armsse_cpuid_info); +} + +type_init(armsse_cpuid_register_types); diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 52466c7..b0701bd 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -132,3 +132,7 @@ iotkit_sysinfo_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysI iotkit_sysctl_read(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" iotkit_sysctl_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" iotkit_sysctl_reset(void) "IoTKit SysCtl: reset" + +# hw/misc/armsse-cpuid.c +armsse_cpuid_read(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +armsse_cpuid_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" -- cgit v1.1 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 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'hw') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 9c111ac..eb691fa 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: -- cgit v1.1 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 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'hw') diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index eb691fa..5d53071 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) -- cgit v1.1 From 4a30dc1c236888ffab1f5e30a5c76b35df917ab9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:43 +0000 Subject: hw/arm/mps2-tz: Add IRQ infrastructure to support SSE-200 In preparation for adding support for the AN521 MPS2 image, we need to handle wiring up the MPS2 device interrupt lines to both CPUs in the SSE-200, rather than just the one that the IoTKit has. Abstract out a "connect to the IoTKit interrupt line" function and make it connect to a splitter which feeds both sets of inputs for the SSE-200 case. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-23-peter.maydell@linaro.org --- hw/arm/mps2-tz.c | 79 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 20 deletions(-) (limited to 'hw') diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 3859f17..95adcd4 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -53,8 +53,11 @@ #include "net/net.h" #include "hw/core/split-irq.h" +#define MPS2TZ_NUMIRQ 92 + typedef enum MPS2TZFPGAType { FPGA_AN505, + FPGA_AN521, } MPS2TZFPGAType; typedef struct { @@ -85,6 +88,7 @@ typedef struct { SplitIRQ sec_resp_splitter; qemu_or_irq uart_irq_orgate; DeviceState *lan9118; + SplitIRQ cpu_irq_splitter[MPS2TZ_NUMIRQ]; } MPS2TZMachineState; #define TYPE_MPS2TZ_MACHINE "mps2tz" @@ -111,6 +115,23 @@ static void make_ram_alias(MemoryRegion *mr, const char *name, memory_region_add_subregion(get_system_memory(), base, mr); } +static qemu_irq get_sse_irq_in(MPS2TZMachineState *mms, int irqno) +{ + /* Return a qemu_irq which will signal IRQ n to all CPUs in the SSE. */ + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); + + assert(irqno < MPS2TZ_NUMIRQ); + + switch (mmc->fpga_type) { + case FPGA_AN505: + return qdev_get_gpio_in_named(DEVICE(&mms->iotkit), "EXP_IRQ", irqno); + case FPGA_AN521: + return qdev_get_gpio_in(DEVICE(&mms->cpu_irq_splitter[irqno]), 0); + default: + g_assert_not_reached(); + } +} + /* Most of the devices in the AN505 FPGA image sit behind * Peripheral Protection Controllers. These data structures * define the layout of which devices sit behind which PPCs. @@ -161,7 +182,6 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, int txirqno = i * 2 + 1; int combirqno = i + 10; SysBusDevice *s; - DeviceState *iotkitdev = DEVICE(&mms->iotkit); DeviceState *orgate_dev = DEVICE(&mms->uart_irq_orgate); sysbus_init_child_obj(OBJECT(mms), name, uart, sizeof(mms->uart[0]), @@ -170,14 +190,11 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, qdev_prop_set_uint32(DEVICE(uart), "pclk-frq", SYSCLK_FRQ); object_property_set_bool(OBJECT(uart), true, "realized", &error_fatal); s = SYS_BUS_DEVICE(uart); - sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev, - "EXP_IRQ", txirqno)); - sysbus_connect_irq(s, 1, qdev_get_gpio_in_named(iotkitdev, - "EXP_IRQ", rxirqno)); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, txirqno)); + sysbus_connect_irq(s, 1, get_sse_irq_in(mms, rxirqno)); sysbus_connect_irq(s, 2, qdev_get_gpio_in(orgate_dev, i * 2)); sysbus_connect_irq(s, 3, qdev_get_gpio_in(orgate_dev, i * 2 + 1)); - sysbus_connect_irq(s, 4, qdev_get_gpio_in_named(iotkitdev, - "EXP_IRQ", combirqno)); + sysbus_connect_irq(s, 4, get_sse_irq_in(mms, combirqno)); return sysbus_mmio_get_region(SYS_BUS_DEVICE(uart), 0); } @@ -213,7 +230,6 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size) { SysBusDevice *s; - DeviceState *iotkitdev = DEVICE(&mms->iotkit); NICInfo *nd = &nd_table[0]; /* In hardware this is a LAN9220; the LAN9118 is software compatible @@ -225,7 +241,7 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque, qdev_init_nofail(mms->lan9118); s = SYS_BUS_DEVICE(mms->lan9118); - sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 16)); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 16)); return sysbus_mmio_get_region(s, 0); } @@ -315,12 +331,9 @@ static MemoryRegion *make_dma(MPS2TZMachineState *mms, void *opaque, s = SYS_BUS_DEVICE(dma); /* Wire up DMACINTR, DMACINTERR, DMACINTTC */ - sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev, - "EXP_IRQ", 58 + i * 3)); - sysbus_connect_irq(s, 1, qdev_get_gpio_in_named(iotkitdev, - "EXP_IRQ", 56 + i * 3)); - sysbus_connect_irq(s, 2, qdev_get_gpio_in_named(iotkitdev, - "EXP_IRQ", 57 + i * 3)); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 58 + i * 3)); + sysbus_connect_irq(s, 1, get_sse_irq_in(mms, 56 + i * 3)); + sysbus_connect_irq(s, 2, get_sse_irq_in(mms, 57 + i * 3)); g_free(mscname); return sysbus_mmio_get_region(s, 0); @@ -339,21 +352,20 @@ static MemoryRegion *make_spi(MPS2TZMachineState *mms, void *opaque, */ PL022State *spi = opaque; int i = spi - &mms->spi[0]; - DeviceState *iotkitdev = DEVICE(&mms->iotkit); SysBusDevice *s; sysbus_init_child_obj(OBJECT(mms), name, spi, sizeof(mms->spi[0]), TYPE_PL022); object_property_set_bool(OBJECT(spi), true, "realized", &error_fatal); s = SYS_BUS_DEVICE(spi); - sysbus_connect_irq(s, 0, - qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 51 + i)); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 51 + i)); return sysbus_mmio_get_region(s, 0); } static void mps2tz_common_init(MachineState *machine) { MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine); + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); MachineClass *mc = MACHINE_GET_CLASS(machine); MemoryRegion *system_memory = get_system_memory(); DeviceState *iotkitdev; @@ -371,11 +383,38 @@ static void mps2tz_common_init(MachineState *machine) iotkitdev = DEVICE(&mms->iotkit); object_property_set_link(OBJECT(&mms->iotkit), OBJECT(system_memory), "memory", &error_abort); - qdev_prop_set_uint32(iotkitdev, "EXP_NUMIRQ", 92); + qdev_prop_set_uint32(iotkitdev, "EXP_NUMIRQ", MPS2TZ_NUMIRQ); qdev_prop_set_uint32(iotkitdev, "MAINCLK", SYSCLK_FRQ); object_property_set_bool(OBJECT(&mms->iotkit), true, "realized", &error_fatal); + /* + * The AN521 needs us to create splitters to feed the IRQ inputs + * for each CPU in the SSE-200 from each device in the board. + */ + if (mmc->fpga_type == FPGA_AN521) { + for (i = 0; i < MPS2TZ_NUMIRQ; i++) { + char *name = g_strdup_printf("mps2-irq-splitter%d", i); + SplitIRQ *splitter = &mms->cpu_irq_splitter[i]; + + object_initialize_child(OBJECT(machine), name, + splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_fatal, NULL); + g_free(name); + + object_property_set_int(OBJECT(splitter), 2, "num-lines", + &error_fatal); + object_property_set_bool(OBJECT(splitter), true, "realized", + &error_fatal); + qdev_connect_gpio_out(DEVICE(splitter), 0, + qdev_get_gpio_in_named(DEVICE(&mms->iotkit), + "EXP_IRQ", i)); + qdev_connect_gpio_out(DEVICE(splitter), 1, + qdev_get_gpio_in_named(DEVICE(&mms->iotkit), + "EXP_CPU1_IRQ", i)); + } + } + /* The sec_resp_cfg output from the IoTKit must be split into multiple * lines, one for each of the PPCs we create here, plus one per MSC. */ @@ -426,7 +465,7 @@ static void mps2tz_common_init(MachineState *machine) object_property_set_bool(OBJECT(&mms->uart_irq_orgate), true, "realized", &error_fatal); qdev_connect_gpio_out(DEVICE(&mms->uart_irq_orgate), 0, - qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 15)); + get_sse_irq_in(mms, 15)); /* Most of the devices in the FPGA are behind Peripheral Protection * Controllers. The required order for initializing things is: -- cgit v1.1 From 23f9242332baee5020daaa5c4f4ddc72e12bc9f0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 1 Feb 2019 14:55:44 +0000 Subject: hw/arm/mps2-tz: Add mps2-an521 model Add a model of the MPS2 FPGA image described in Application Note AN521. This is identical to the AN505 image, except that it uses the SSE-200 rather than the IoTKit and so has two Cortex-M33 CPUs. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190121185118.18550-24-peter.maydell@linaro.org --- hw/arm/mps2-tz.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) (limited to 'hw') diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 95adcd4..f5f0b0e 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -15,6 +15,7 @@ * as seen by the guest depend significantly on the FPGA image. * This source file covers the following FPGA images, for TrustZone cores: * "mps2-an505" -- Cortex-M33 as documented in ARM Application Note AN505 + * "mps2-an521" -- Dual Cortex-M33 as documented in Application Note AN521 * * Links to the TRM for the board itself and to the various Application * Notes which document the FPGA images can be found here: @@ -24,10 +25,16 @@ * http://infocenter.arm.com/help/topic/com.arm.doc.100112_0200_06_en/versatile_express_cortex_m_prototyping_systems_v2m_mps2_and_v2m_mps2plus_technical_reference_100112_0200_06_en.pdf * Application Note AN505: * http://infocenter.arm.com/help/topic/com.arm.doc.dai0505b/index.html + * Application Note AN521: + * http://infocenter.arm.com/help/topic/com.arm.doc.dai0521c/index.html * * The AN505 defers to the Cortex-M33 processor ARMv8M IoT Kit FVP User Guide * (ARM ECM0601256) for the details of some of the device layout: * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * Similarly, the AN521 uses the SSE-200, and the SSE-200 TRM defines + * most of the device layout: + * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf + * */ #include "qemu/osdep.h" @@ -64,6 +71,7 @@ typedef struct { MachineClass parent; MPS2TZFPGAType fpga_type; uint32_t scc_id; + const char *armsse_type; } MPS2TZMachineClass; typedef struct { @@ -93,6 +101,7 @@ typedef struct { #define TYPE_MPS2TZ_MACHINE "mps2tz" #define TYPE_MPS2TZ_AN505_MACHINE MACHINE_TYPE_NAME("mps2-an505") +#define TYPE_MPS2TZ_AN521_MACHINE MACHINE_TYPE_NAME("mps2-an521") #define MPS2TZ_MACHINE(obj) \ OBJECT_CHECK(MPS2TZMachineState, obj, TYPE_MPS2TZ_MACHINE) @@ -379,7 +388,7 @@ static void mps2tz_common_init(MachineState *machine) } sysbus_init_child_obj(OBJECT(machine), "iotkit", &mms->iotkit, - sizeof(mms->iotkit), TYPE_IOTKIT); + sizeof(mms->iotkit), mmc->armsse_type); iotkitdev = DEVICE(&mms->iotkit); object_property_set_link(OBJECT(&mms->iotkit), OBJECT(system_memory), "memory", &error_abort); @@ -632,7 +641,6 @@ static void mps2tz_class_init(ObjectClass *oc, void *data) IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(oc); mc->init = mps2tz_common_init; - mc->max_cpus = 1; iic->check = mps2_tz_idau_check; } @@ -642,9 +650,28 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data) MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); mc->desc = "ARM MPS2 with AN505 FPGA image for Cortex-M33"; + mc->default_cpus = 1; + mc->min_cpus = mc->default_cpus; + mc->max_cpus = mc->default_cpus; mmc->fpga_type = FPGA_AN505; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); mmc->scc_id = 0x41045050; + mmc->armsse_type = TYPE_IOTKIT; +} + +static void mps2tz_an521_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); + + mc->desc = "ARM MPS2 with AN521 FPGA image for dual Cortex-M33"; + mc->default_cpus = 2; + mc->min_cpus = mc->default_cpus; + mc->max_cpus = mc->default_cpus; + mmc->fpga_type = FPGA_AN521; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); + mmc->scc_id = 0x41045210; + mmc->armsse_type = TYPE_SSE200; } static const TypeInfo mps2tz_info = { @@ -666,10 +693,17 @@ static const TypeInfo mps2tz_an505_info = { .class_init = mps2tz_an505_class_init, }; +static const TypeInfo mps2tz_an521_info = { + .name = TYPE_MPS2TZ_AN521_MACHINE, + .parent = TYPE_MPS2TZ_MACHINE, + .class_init = mps2tz_an521_class_init, +}; + static void mps2tz_machine_init(void) { type_register_static(&mps2tz_info); type_register_static(&mps2tz_an505_info); + type_register_static(&mps2tz_an521_info); } type_init(mps2tz_machine_init); -- cgit v1.1 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 ---- 1 file changed, 4 deletions(-) (limited to 'hw') diff --git a/hw/arm/boot.c b/hw/arm/boot.c index c7a67af..05762d0 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -697,10 +697,6 @@ static void do_cpu_reset(void *opaque) g_assert_not_reached(); } - if (!env->aarch64) { - env->thumb = info->entry & 1; - entry &= 0xfffffffe; - } cpu_set_pc(cs, entry); } else { /* If we are booting Linux then we need to check whether we are -- cgit v1.1 From c0d4eb83526e2ba5a8def0710d183a9387090ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20G=C3=B6rtz?= 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 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 389 insertions(+) create mode 100644 hw/nvram/nrf51_nvm.c (limited to 'hw') diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs index b318e53..26f7b4c 100644 --- a/hw/nvram/Makefile.objs +++ b/hw/nvram/Makefile.objs @@ -5,3 +5,4 @@ common-obj-y += fw_cfg.o common-obj-y += chrp_nvram.o common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o obj-$(CONFIG_PSERIES) += spapr_nvram.o +obj-$(CONFIG_NRF51_SOC) += nrf51_nvm.o diff --git a/hw/nvram/nrf51_nvm.c b/hw/nvram/nrf51_nvm.c new file mode 100644 index 0000000..7d94cef --- /dev/null +++ b/hw/nvram/nrf51_nvm.c @@ -0,0 +1,388 @@ +/* + * Nordic Semiconductor nRF51 non-volatile memory + * + * It provides an interface to erase regions in flash memory. + * Furthermore it provides the user and factory information registers. + * + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf + * + * See nRF51 reference manual and product sheet sections: + * + Non-Volatile Memory Controller (NVMC) + * + Factory Information Configuration Registers (FICR) + * + User Information Configuration Registers (UICR) + * + * Copyright 2018 Steffen Görtz + * + * 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) -- cgit v1.1 From 4d744b25d37804279f94800b62f3d765177d6493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20G=C3=B6rtz?= 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 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) (limited to 'hw') diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index 3a1c7e2..bbaf050 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -29,8 +29,10 @@ * are supported in the future, add a sub-class of NRF51SoC for * the specific variants */ -#define NRF51822_FLASH_SIZE (256 * NRF51_PAGE_SIZE) -#define NRF51822_SRAM_SIZE (16 * NRF51_PAGE_SIZE) +#define NRF51822_FLASH_PAGES 256 +#define NRF51822_SRAM_PAGES 16 +#define NRF51822_FLASH_SIZE (NRF51822_FLASH_PAGES * NRF51_PAGE_SIZE) +#define NRF51822_SRAM_SIZE (NRF51822_SRAM_PAGES * NRF51_PAGE_SIZE) #define BASE_TO_IRQ(base) ((base >> 12) & 0x1F) @@ -81,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); -- cgit v1.1