summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS6
-rw-r--r--default-configs/arm-softmmu.mak3
-rw-r--r--exec.c10
-rw-r--r--hw/arm/Makefile.objs2
-rw-r--r--hw/arm/armsse.c1241
-rw-r--r--hw/arm/armv7m.c23
-rw-r--r--hw/arm/boot.c4
-rw-r--r--hw/arm/iotkit.c759
-rw-r--r--hw/arm/mps2-tz.c121
-rw-r--r--hw/arm/nrf51_soc.c44
-rw-r--r--hw/intc/armv7m_nvic.c3
-rw-r--r--hw/misc/Makefile.objs1
-rw-r--r--hw/misc/armsse-cpuid.c134
-rw-r--r--hw/misc/iotkit-secctl.c5
-rw-r--r--hw/misc/iotkit-sysinfo.c15
-rw-r--r--hw/misc/trace-events4
-rw-r--r--hw/nvram/Makefile.objs1
-rw-r--r--hw/nvram/nrf51_nvm.c388
-rw-r--r--include/hw/arm/armsse.h (renamed from include/hw/arm/iotkit.h)113
-rw-r--r--include/hw/arm/armv7m.h1
-rw-r--r--include/hw/arm/nrf51_soc.h2
-rw-r--r--include/hw/misc/armsse-cpuid.h41
-rw-r--r--include/hw/misc/iotkit-secctl.h6
-rw-r--r--include/hw/misc/iotkit-sysinfo.h6
-rw-r--r--include/hw/nvram/nrf51_nvm.h64
-rw-r--r--include/qom/cpu.h16
-rw-r--r--linux-user/aarch64/cpu_loop.c31
-rw-r--r--linux-user/aarch64/target_syscall.h2
-rw-r--r--linux-user/elfload.c10
-rw-r--r--target/arm/arm-powerctl.c3
-rw-r--r--target/arm/cpu.c41
-rw-r--r--target/arm/cpu.h12
-rw-r--r--target/arm/cpu64.c75
-rw-r--r--target/arm/helper.c139
-rw-r--r--target/arm/translate-a64.c59
-rw-r--r--tests/microbit-test.c108
36 files changed, 2552 insertions, 941 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 37ceda468f..612692b284 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -624,14 +624,16 @@ F: hw/arm/mps2.c
 F: hw/arm/mps2-tz.c
 F: hw/misc/mps2-*.c
 F: include/hw/misc/mps2-*.h
-F: hw/arm/iotkit.c
-F: include/hw/arm/iotkit.h
+F: hw/arm/armsse.c
+F: include/hw/arm/armsse.h
 F: hw/misc/iotkit-secctl.c
 F: include/hw/misc/iotkit-secctl.h
 F: hw/misc/iotkit-sysctl.c
 F: include/hw/misc/iotkit-sysctl.h
 F: hw/misc/iotkit-sysinfo.c
 F: include/hw/misc/iotkit-sysinfo.h
+F: hw/misc/armsse-cpuid.c
+F: include/hw/misc/armsse-cpuid.h
 
 Musicpal
 M: Jan Kiszka <jan.kiszka@web.de>
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 2420491aac..be88870799 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -114,10 +114,11 @@ CONFIG_MPS2_SCC=y
 CONFIG_TZ_MPC=y
 CONFIG_TZ_MSC=y
 CONFIG_TZ_PPC=y
-CONFIG_IOTKIT=y
+CONFIG_ARMSSE=y
 CONFIG_IOTKIT_SECCTL=y
 CONFIG_IOTKIT_SYSCTL=y
 CONFIG_IOTKIT_SYSINFO=y
+CONFIG_ARMSSE_CPUID=y
 
 CONFIG_VERSATILE=y
 CONFIG_VERSATILE_PCI=y
diff --git a/exec.c b/exec.c
index da3e635f91..25f3938a27 100644
--- a/exec.c
+++ b/exec.c
@@ -665,7 +665,7 @@ static void tcg_register_iommu_notifier(CPUState *cpu,
     int i;
 
     for (i = 0; i < cpu->iommu_notifiers->len; i++) {
-        notifier = &g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier, i);
+        notifier = g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier *, i);
         if (notifier->mr == mr && notifier->iommu_idx == iommu_idx) {
             break;
         }
@@ -673,7 +673,8 @@ static void tcg_register_iommu_notifier(CPUState *cpu,
     if (i == cpu->iommu_notifiers->len) {
         /* Not found, add a new entry at the end of the array */
         cpu->iommu_notifiers = g_array_set_size(cpu->iommu_notifiers, i + 1);
-        notifier = &g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier, i);
+        notifier = g_new0(TCGIOMMUNotifier, 1);
+        g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier *, i) = notifier;
 
         notifier->mr = mr;
         notifier->iommu_idx = iommu_idx;
@@ -705,8 +706,9 @@ static void tcg_iommu_free_notifier_list(CPUState *cpu)
     TCGIOMMUNotifier *notifier;
 
     for (i = 0; i < cpu->iommu_notifiers->len; i++) {
-        notifier = &g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier, i);
+        notifier = g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier *, i);
         memory_region_unregister_iommu_notifier(notifier->mr, &notifier->n);
+        g_free(notifier);
     }
     g_array_free(cpu->iommu_notifiers, true);
 }
@@ -976,7 +978,7 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp)
         vmstate_register(NULL, cpu->cpu_index, cc->vmsd, cpu);
     }
 
-    cpu->iommu_notifiers = g_array_new(false, true, sizeof(TCGIOMMUNotifier));
+    cpu->iommu_notifiers = g_array_new(false, true, sizeof(TCGIOMMUNotifier *));
 #endif
 }
 
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 50c7b4a927..22b7f0ed0b 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -34,7 +34,7 @@ obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o
 obj-$(CONFIG_MPS2) += mps2.o
 obj-$(CONFIG_MPS2) += mps2-tz.o
 obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o
-obj-$(CONFIG_IOTKIT) += iotkit.o
+obj-$(CONFIG_ARMSSE) += armsse.o
 obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o
 obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o smmuv3.o
 obj-$(CONFIG_FSL_IMX6UL) += fsl-imx6ul.o mcimx6ul-evk.o
diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c
new file mode 100644
index 0000000000..5d53071a5a
--- /dev/null
+++ b/hw/arm/armsse.c
@@ -0,0 +1,1241 @@
+/*
+ * Arm SSE (Subsystems for Embedded): IoTKit
+ *
+ * Copyright (c) 2018 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/arm/armsse.h"
+#include "hw/arm/arm.h"
+
+/* Format of the System Information block SYS_CONFIG register */
+typedef enum SysConfigFormat {
+    IoTKitFormat,
+    SSE200Format,
+} SysConfigFormat;
+
+struct ARMSSEInfo {
+    const char *name;
+    int sram_banks;
+    int num_cpus;
+    uint32_t sys_version;
+    SysConfigFormat sys_config_format;
+    bool has_mhus;
+    bool has_ppus;
+    bool has_cachectrl;
+    bool has_cpusecctrl;
+    bool has_cpuid;
+};
+
+static const ARMSSEInfo armsse_variants[] = {
+    {
+        .name = TYPE_IOTKIT,
+        .sram_banks = 1,
+        .num_cpus = 1,
+        .sys_version = 0x41743,
+        .sys_config_format = IoTKitFormat,
+        .has_mhus = false,
+        .has_ppus = false,
+        .has_cachectrl = false,
+        .has_cpusecctrl = false,
+        .has_cpuid = false,
+    },
+    {
+        .name = TYPE_SSE200,
+        .sram_banks = 4,
+        .num_cpus = 2,
+        .sys_version = 0x22041743,
+        .sys_config_format = SSE200Format,
+        .has_mhus = true,
+        .has_ppus = true,
+        .has_cachectrl = true,
+        .has_cpusecctrl = true,
+        .has_cpuid = true,
+    },
+};
+
+static uint32_t armsse_sys_config_value(ARMSSE *s, const ARMSSEInfo *info)
+{
+    /* Return the SYS_CONFIG value for this SSE */
+    uint32_t sys_config;
+
+    switch (info->sys_config_format) {
+    case IoTKitFormat:
+        sys_config = 0;
+        sys_config = deposit32(sys_config, 0, 4, info->sram_banks);
+        sys_config = deposit32(sys_config, 4, 4, s->sram_addr_width - 12);
+        break;
+    case SSE200Format:
+        sys_config = 0;
+        sys_config = deposit32(sys_config, 0, 4, info->sram_banks);
+        sys_config = deposit32(sys_config, 4, 5, s->sram_addr_width);
+        sys_config = deposit32(sys_config, 24, 4, 2);
+        if (info->num_cpus > 1) {
+            sys_config = deposit32(sys_config, 10, 1, 1);
+            sys_config = deposit32(sys_config, 20, 4, info->sram_banks - 1);
+            sys_config = deposit32(sys_config, 28, 4, 2);
+        }
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    return sys_config;
+}
+
+/* Clock frequency in HZ of the 32KHz "slow clock" */
+#define S32KCLK (32 * 1000)
+
+/* Is internal IRQ n shared between CPUs in a multi-core SSE ? */
+static bool irq_is_common[32] = {
+    [0 ... 5] = true,
+    /* 6, 7: per-CPU MHU interrupts */
+    [8 ... 12] = true,
+    /* 13: per-CPU icache interrupt */
+    /* 14: reserved */
+    [15 ... 20] = true,
+    /* 21: reserved */
+    [22 ... 26] = true,
+    /* 27: reserved */
+    /* 28, 29: per-CPU CTI interrupts */
+    /* 30, 31: reserved */
+};
+
+/* Create an alias region of @size bytes starting at @base
+ * which mirrors the memory starting at @orig.
+ */
+static void make_alias(ARMSSE *s, MemoryRegion *mr, const char *name,
+                       hwaddr base, hwaddr size, hwaddr orig)
+{
+    memory_region_init_alias(mr, NULL, name, &s->container, orig, size);
+    /* The alias is even lower priority than unimplemented_device regions */
+    memory_region_add_subregion_overlap(&s->container, base, mr, -1500);
+}
+
+static void irq_status_forwarder(void *opaque, int n, int level)
+{
+    qemu_irq destirq = opaque;
+
+    qemu_set_irq(destirq, level);
+}
+
+static void nsccfg_handler(void *opaque, int n, int level)
+{
+    ARMSSE *s = ARMSSE(opaque);
+
+    s->nsccfg = level;
+}
+
+static void armsse_forward_ppc(ARMSSE *s, const char *ppcname, int ppcnum)
+{
+    /* Each of the 4 AHB and 4 APB PPCs that might be present in a
+     * system using the ARMSSE has a collection of control lines which
+     * are provided by the security controller and which we want to
+     * expose as control lines on the ARMSSE device itself, so the
+     * code using the ARMSSE can wire them up to the PPCs.
+     */
+    SplitIRQ *splitter = &s->ppc_irq_splitter[ppcnum];
+    DeviceState *armssedev = DEVICE(s);
+    DeviceState *dev_secctl = DEVICE(&s->secctl);
+    DeviceState *dev_splitter = DEVICE(splitter);
+    char *name;
+
+    name = g_strdup_printf("%s_nonsec", ppcname);
+    qdev_pass_gpios(dev_secctl, armssedev, name);
+    g_free(name);
+    name = g_strdup_printf("%s_ap", ppcname);
+    qdev_pass_gpios(dev_secctl, armssedev, name);
+    g_free(name);
+    name = g_strdup_printf("%s_irq_enable", ppcname);
+    qdev_pass_gpios(dev_secctl, armssedev, name);
+    g_free(name);
+    name = g_strdup_printf("%s_irq_clear", ppcname);
+    qdev_pass_gpios(dev_secctl, armssedev, name);
+    g_free(name);
+
+    /* irq_status is a little more tricky, because we need to
+     * split it so we can send it both to the security controller
+     * and to our OR gate for the NVIC interrupt line.
+     * Connect up the splitter's outputs, and create a GPIO input
+     * which will pass the line state to the input splitter.
+     */
+    name = g_strdup_printf("%s_irq_status", ppcname);
+    qdev_connect_gpio_out(dev_splitter, 0,
+                          qdev_get_gpio_in_named(dev_secctl,
+                                                 name, 0));
+    qdev_connect_gpio_out(dev_splitter, 1,
+                          qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), ppcnum));
+    s->irq_status_in[ppcnum] = qdev_get_gpio_in(dev_splitter, 0);
+    qdev_init_gpio_in_named_with_opaque(armssedev, irq_status_forwarder,
+                                        s->irq_status_in[ppcnum], name, 1);
+    g_free(name);
+}
+
+static void armsse_forward_sec_resp_cfg(ARMSSE *s)
+{
+    /* Forward the 3rd output from the splitter device as a
+     * named GPIO output of the armsse object.
+     */
+    DeviceState *dev = DEVICE(s);
+    DeviceState *dev_splitter = DEVICE(&s->sec_resp_splitter);
+
+    qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1);
+    s->sec_resp_cfg_in = qemu_allocate_irq(irq_status_forwarder,
+                                           s->sec_resp_cfg, 1);
+    qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in);
+}
+
+static void armsse_init(Object *obj)
+{
+    ARMSSE *s = ARMSSE(obj);
+    ARMSSEClass *asc = ARMSSE_GET_CLASS(obj);
+    const ARMSSEInfo *info = asc->info;
+    int i;
+
+    assert(info->sram_banks <= MAX_SRAM_BANKS);
+    assert(info->num_cpus <= SSE_MAX_CPUS);
+
+    memory_region_init(&s->container, obj, "armsse-container", UINT64_MAX);
+
+    for (i = 0; i < info->num_cpus; i++) {
+        /*
+         * We put each CPU in its own cluster as they are logically
+         * distinct and may be configured differently.
+         */
+        char *name;
+
+        name = g_strdup_printf("cluster%d", i);
+        object_initialize_child(obj, name, &s->cluster[i],
+                                sizeof(s->cluster[i]), TYPE_CPU_CLUSTER,
+                                &error_abort, NULL);
+        qdev_prop_set_uint32(DEVICE(&s->cluster[i]), "cluster-id", i);
+        g_free(name);
+
+        name = g_strdup_printf("armv7m%d", i);
+        sysbus_init_child_obj(OBJECT(&s->cluster[i]), name,
+                              &s->armv7m[i], sizeof(s->armv7m), TYPE_ARMV7M);
+        qdev_prop_set_string(DEVICE(&s->armv7m[i]), "cpu-type",
+                             ARM_CPU_TYPE_NAME("cortex-m33"));
+        g_free(name);
+        name = g_strdup_printf("arm-sse-cpu-container%d", i);
+        memory_region_init(&s->cpu_container[i], obj, name, UINT64_MAX);
+        g_free(name);
+        if (i > 0) {
+            name = g_strdup_printf("arm-sse-container-alias%d", i);
+            memory_region_init_alias(&s->container_alias[i - 1], obj,
+                                     name, &s->container, 0, UINT64_MAX);
+            g_free(name);
+        }
+    }
+
+    sysbus_init_child_obj(obj, "secctl", &s->secctl, sizeof(s->secctl),
+                          TYPE_IOTKIT_SECCTL);
+    sysbus_init_child_obj(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0),
+                          TYPE_TZ_PPC);
+    sysbus_init_child_obj(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1),
+                          TYPE_TZ_PPC);
+    for (i = 0; i < info->sram_banks; i++) {
+        char *name = g_strdup_printf("mpc%d", i);
+        sysbus_init_child_obj(obj, name, &s->mpc[i],
+                              sizeof(s->mpc[i]), TYPE_TZ_MPC);
+        g_free(name);
+    }
+    object_initialize_child(obj, "mpc-irq-orgate", &s->mpc_irq_orgate,
+                            sizeof(s->mpc_irq_orgate), TYPE_OR_IRQ,
+                            &error_abort, NULL);
+
+    for (i = 0; i < IOTS_NUM_EXP_MPC + info->sram_banks; i++) {
+        char *name = g_strdup_printf("mpc-irq-splitter-%d", i);
+        SplitIRQ *splitter = &s->mpc_irq_splitter[i];
+
+        object_initialize_child(obj, name, splitter, sizeof(*splitter),
+                                TYPE_SPLIT_IRQ, &error_abort, NULL);
+        g_free(name);
+    }
+    sysbus_init_child_obj(obj, "timer0", &s->timer0, sizeof(s->timer0),
+                          TYPE_CMSDK_APB_TIMER);
+    sysbus_init_child_obj(obj, "timer1", &s->timer1, sizeof(s->timer1),
+                          TYPE_CMSDK_APB_TIMER);
+    sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer),
+                          TYPE_CMSDK_APB_TIMER);
+    sysbus_init_child_obj(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer),
+                          TYPE_CMSDK_APB_DUALTIMER);
+    sysbus_init_child_obj(obj, "s32kwatchdog", &s->s32kwatchdog,
+                          sizeof(s->s32kwatchdog), TYPE_CMSDK_APB_WATCHDOG);
+    sysbus_init_child_obj(obj, "nswatchdog", &s->nswatchdog,
+                          sizeof(s->nswatchdog), TYPE_CMSDK_APB_WATCHDOG);
+    sysbus_init_child_obj(obj, "swatchdog", &s->swatchdog,
+                          sizeof(s->swatchdog), TYPE_CMSDK_APB_WATCHDOG);
+    sysbus_init_child_obj(obj, "armsse-sysctl", &s->sysctl,
+                          sizeof(s->sysctl), TYPE_IOTKIT_SYSCTL);
+    sysbus_init_child_obj(obj, "armsse-sysinfo", &s->sysinfo,
+                          sizeof(s->sysinfo), TYPE_IOTKIT_SYSINFO);
+    if (info->has_mhus) {
+        sysbus_init_child_obj(obj, "mhu0", &s->mhu[0], sizeof(s->mhu[0]),
+                              TYPE_UNIMPLEMENTED_DEVICE);
+        sysbus_init_child_obj(obj, "mhu1", &s->mhu[1], sizeof(s->mhu[1]),
+                              TYPE_UNIMPLEMENTED_DEVICE);
+    }
+    if (info->has_ppus) {
+        for (i = 0; i < info->num_cpus; i++) {
+            char *name = g_strdup_printf("CPU%dCORE_PPU", i);
+            int ppuidx = CPU0CORE_PPU + i;
+
+            sysbus_init_child_obj(obj, name, &s->ppu[ppuidx],
+                                  sizeof(s->ppu[ppuidx]),
+                                  TYPE_UNIMPLEMENTED_DEVICE);
+            g_free(name);
+        }
+        sysbus_init_child_obj(obj, "DBG_PPU", &s->ppu[DBG_PPU],
+                              sizeof(s->ppu[DBG_PPU]),
+                              TYPE_UNIMPLEMENTED_DEVICE);
+        for (i = 0; i < info->sram_banks; i++) {
+            char *name = g_strdup_printf("RAM%d_PPU", i);
+            int ppuidx = RAM0_PPU + i;
+
+            sysbus_init_child_obj(obj, name, &s->ppu[ppuidx],
+                                  sizeof(s->ppu[ppuidx]),
+                                  TYPE_UNIMPLEMENTED_DEVICE);
+            g_free(name);
+        }
+    }
+    if (info->has_cachectrl) {
+        for (i = 0; i < info->num_cpus; i++) {
+            char *name = g_strdup_printf("cachectrl%d", i);
+
+            sysbus_init_child_obj(obj, name, &s->cachectrl[i],
+                                  sizeof(s->cachectrl[i]),
+                                  TYPE_UNIMPLEMENTED_DEVICE);
+            g_free(name);
+        }
+    }
+    if (info->has_cpusecctrl) {
+        for (i = 0; i < info->num_cpus; i++) {
+            char *name = g_strdup_printf("cpusecctrl%d", i);
+
+            sysbus_init_child_obj(obj, name, &s->cpusecctrl[i],
+                                  sizeof(s->cpusecctrl[i]),
+                                  TYPE_UNIMPLEMENTED_DEVICE);
+            g_free(name);
+        }
+    }
+    if (info->has_cpuid) {
+        for (i = 0; i < info->num_cpus; i++) {
+            char *name = g_strdup_printf("cpuid%d", i);
+
+            sysbus_init_child_obj(obj, name, &s->cpuid[i],
+                                  sizeof(s->cpuid[i]),
+                                  TYPE_ARMSSE_CPUID);
+            g_free(name);
+        }
+    }
+    object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate,
+                            sizeof(s->nmi_orgate), TYPE_OR_IRQ,
+                            &error_abort, NULL);
+    object_initialize_child(obj, "ppc-irq-orgate", &s->ppc_irq_orgate,
+                            sizeof(s->ppc_irq_orgate), TYPE_OR_IRQ,
+                            &error_abort, NULL);
+    object_initialize_child(obj, "sec-resp-splitter", &s->sec_resp_splitter,
+                            sizeof(s->sec_resp_splitter), TYPE_SPLIT_IRQ,
+                            &error_abort, NULL);
+    for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) {
+        char *name = g_strdup_printf("ppc-irq-splitter-%d", i);
+        SplitIRQ *splitter = &s->ppc_irq_splitter[i];
+
+        object_initialize_child(obj, name, splitter, sizeof(*splitter),
+                                TYPE_SPLIT_IRQ, &error_abort, NULL);
+        g_free(name);
+    }
+    if (info->num_cpus > 1) {
+        for (i = 0; i < ARRAY_SIZE(s->cpu_irq_splitter); i++) {
+            if (irq_is_common[i]) {
+                char *name = g_strdup_printf("cpu-irq-splitter%d", i);
+                SplitIRQ *splitter = &s->cpu_irq_splitter[i];
+
+                object_initialize_child(obj, name, splitter, sizeof(*splitter),
+                                        TYPE_SPLIT_IRQ, &error_abort, NULL);
+                g_free(name);
+            }
+        }
+    }
+}
+
+static void armsse_exp_irq(void *opaque, int n, int level)
+{
+    qemu_irq *irqarray = opaque;
+
+    qemu_set_irq(irqarray[n], level);
+}
+
+static void armsse_mpcexp_status(void *opaque, int n, int level)
+{
+    ARMSSE *s = ARMSSE(opaque);
+    qemu_set_irq(s->mpcexp_status_in[n], level);
+}
+
+static qemu_irq armsse_get_common_irq_in(ARMSSE *s, int irqno)
+{
+    /*
+     * Return a qemu_irq which can be used to signal IRQ n to
+     * all CPUs in the SSE.
+     */
+    ARMSSEClass *asc = ARMSSE_GET_CLASS(s);
+    const ARMSSEInfo *info = asc->info;
+
+    assert(irq_is_common[irqno]);
+
+    if (info->num_cpus == 1) {
+        /* Only one CPU -- just connect directly to it */
+        return qdev_get_gpio_in(DEVICE(&s->armv7m[0]), irqno);
+    } else {
+        /* Connect to the splitter which feeds all CPUs */
+        return qdev_get_gpio_in(DEVICE(&s->cpu_irq_splitter[irqno]), 0);
+    }
+}
+
+static void map_ppu(ARMSSE *s, int ppuidx, const char *name, hwaddr addr)
+{
+    /* Map a PPU unimplemented device stub */
+    DeviceState *dev = DEVICE(&s->ppu[ppuidx]);
+
+    qdev_prop_set_string(dev, "name", name);
+    qdev_prop_set_uint64(dev, "size", 0x1000);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->ppu[ppuidx]), 0, addr);
+}
+
+static void armsse_realize(DeviceState *dev, Error **errp)
+{
+    ARMSSE *s = ARMSSE(dev);
+    ARMSSEClass *asc = ARMSSE_GET_CLASS(dev);
+    const ARMSSEInfo *info = asc->info;
+    int i;
+    MemoryRegion *mr;
+    Error *err = NULL;
+    SysBusDevice *sbd_apb_ppc0;
+    SysBusDevice *sbd_secctl;
+    DeviceState *dev_apb_ppc0;
+    DeviceState *dev_apb_ppc1;
+    DeviceState *dev_secctl;
+    DeviceState *dev_splitter;
+    uint32_t addr_width_max;
+
+    if (!s->board_memory) {
+        error_setg(errp, "memory property was not set");
+        return;
+    }
+
+    if (!s->mainclk_frq) {
+        error_setg(errp, "MAINCLK property was not set");
+        return;
+    }
+
+    /* max SRAM_ADDR_WIDTH: 24 - log2(SRAM_NUM_BANK) */
+    assert(is_power_of_2(info->sram_banks));
+    addr_width_max = 24 - ctz32(info->sram_banks);
+    if (s->sram_addr_width < 1 || s->sram_addr_width > addr_width_max) {
+        error_setg(errp, "SRAM_ADDR_WIDTH must be between 1 and %d",
+                   addr_width_max);
+        return;
+    }
+
+    /* Handling of which devices should be available only to secure
+     * code is usually done differently for M profile than for A profile.
+     * Instead of putting some devices only into the secure address space,
+     * devices exist in both address spaces but with hard-wired security
+     * permissions that will cause the CPU to fault for non-secure accesses.
+     *
+     * The ARMSSE has an IDAU (Implementation Defined Access Unit),
+     * which specifies hard-wired security permissions for different
+     * areas of the physical address space. For the ARMSSE IDAU, the
+     * top 4 bits of the physical address are the IDAU region ID, and
+     * if bit 28 (ie the lowest bit of the ID) is 0 then this is an NS
+     * region, otherwise it is an S region.
+     *
+     * The various devices and RAMs are generally all mapped twice,
+     * once into a region that the IDAU defines as secure and once
+     * into a non-secure region. They sit behind either a Memory
+     * Protection Controller (for RAM) or a Peripheral Protection
+     * Controller (for devices), which allow a more fine grained
+     * configuration of whether non-secure accesses are permitted.
+     *
+     * (The other place that guest software can configure security
+     * permissions is in the architected SAU (Security Attribution
+     * Unit), which is entirely inside the CPU. The IDAU can upgrade
+     * the security attributes for a region to more restrictive than
+     * the SAU specifies, but cannot downgrade them.)
+     *
+     * 0x10000000..0x1fffffff  alias of 0x00000000..0x0fffffff
+     * 0x20000000..0x2007ffff  32KB FPGA block RAM
+     * 0x30000000..0x3fffffff  alias of 0x20000000..0x2fffffff
+     * 0x40000000..0x4000ffff  base peripheral region 1
+     * 0x40010000..0x4001ffff  CPU peripherals (none for ARMSSE)
+     * 0x40020000..0x4002ffff  system control element peripherals
+     * 0x40080000..0x400fffff  base peripheral region 2
+     * 0x50000000..0x5fffffff  alias of 0x40000000..0x4fffffff
+     */
+
+    memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -2);
+
+    for (i = 0; i < info->num_cpus; i++) {
+        DeviceState *cpudev = DEVICE(&s->armv7m[i]);
+        Object *cpuobj = OBJECT(&s->armv7m[i]);
+        int j;
+        char *gpioname;
+
+        qdev_prop_set_uint32(cpudev, "num-irq", s->exp_numirq + 32);
+        /*
+         * In real hardware the initial Secure VTOR is set from the INITSVTOR0
+         * register in the IoT Kit System Control Register block, and the
+         * initial value of that is in turn specifiable by the FPGA that
+         * instantiates the IoT Kit. In QEMU we don't implement this wrinkle,
+         * and simply set the CPU's init-svtor to the IoT Kit default value.
+         * In SSE-200 the situation is similar, except that the default value
+         * is a reset-time signal input. Typically a board using the SSE-200
+         * will have a system control processor whose boot firmware initializes
+         * the INITSVTOR* registers before powering up the CPUs in any case,
+         * so the hardware's default value doesn't matter. QEMU doesn't emulate
+         * the control processor, so instead we behave in the way that the
+         * firmware does. All boards currently known about have firmware that
+         * sets the INITSVTOR0 and INITSVTOR1 registers to 0x10000000, like the
+         * IoTKit default. We can make this more configurable if necessary.
+         */
+        qdev_prop_set_uint32(cpudev, "init-svtor", 0x10000000);
+        /*
+         * Start all CPUs except CPU0 powered down. In real hardware it is
+         * a configurable property of the SSE-200 which CPUs start powered up
+         * (via the CPUWAIT0_RST and CPUWAIT1_RST parameters), but since all
+         * the boards we care about start CPU0 and leave CPU1 powered off,
+         * we hard-code that for now. We can add QOM properties for this
+         * later if necessary.
+         */
+        if (i > 0) {
+            object_property_set_bool(cpuobj, true, "start-powered-off", &err);
+            if (err) {
+                error_propagate(errp, err);
+                return;
+            }
+        }
+
+        if (i > 0) {
+            memory_region_add_subregion_overlap(&s->cpu_container[i], 0,
+                                                &s->container_alias[i - 1], -1);
+        } else {
+            memory_region_add_subregion_overlap(&s->cpu_container[i], 0,
+                                                &s->container, -1);
+        }
+        object_property_set_link(cpuobj, OBJECT(&s->cpu_container[i]),
+                                 "memory", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+        object_property_set_link(cpuobj, OBJECT(s), "idau", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+        object_property_set_bool(cpuobj, true, "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+        /*
+         * The cluster must be realized after the armv7m container, as
+         * the container's CPU object is only created on realize, and the
+         * CPU must exist and have been parented into the cluster before
+         * the cluster is realized.
+         */
+        object_property_set_bool(OBJECT(&s->cluster[i]),
+                                 true, "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+
+        /* Connect EXP_IRQ/EXP_CPUn_IRQ GPIOs to the NVIC's lines 32 and up */
+        s->exp_irqs[i] = g_new(qemu_irq, s->exp_numirq);
+        for (j = 0; j < s->exp_numirq; j++) {
+            s->exp_irqs[i][j] = qdev_get_gpio_in(cpudev, i + 32);
+        }
+        if (i == 0) {
+            gpioname = g_strdup("EXP_IRQ");
+        } else {
+            gpioname = g_strdup_printf("EXP_CPU%d_IRQ", i);
+        }
+        qdev_init_gpio_in_named_with_opaque(dev, armsse_exp_irq,
+                                            s->exp_irqs[i],
+                                            gpioname, s->exp_numirq);
+        g_free(gpioname);
+    }
+
+    /* Wire up the splitters that connect common IRQs to all CPUs */
+    if (info->num_cpus > 1) {
+        for (i = 0; i < ARRAY_SIZE(s->cpu_irq_splitter); i++) {
+            if (irq_is_common[i]) {
+                Object *splitter = OBJECT(&s->cpu_irq_splitter[i]);
+                DeviceState *devs = DEVICE(splitter);
+                int cpunum;
+
+                object_property_set_int(splitter, info->num_cpus,
+                                        "num-lines", &err);
+                if (err) {
+                    error_propagate(errp, err);
+                    return;
+                }
+                object_property_set_bool(splitter, true, "realized", &err);
+                if (err) {
+                    error_propagate(errp, err);
+                    return;
+                }
+                for (cpunum = 0; cpunum < info->num_cpus; cpunum++) {
+                    DeviceState *cpudev = DEVICE(&s->armv7m[cpunum]);
+
+                    qdev_connect_gpio_out(devs, cpunum,
+                                          qdev_get_gpio_in(cpudev, i));
+                }
+            }
+        }
+    }
+
+    /* Set up the big aliases first */
+    make_alias(s, &s->alias1, "alias 1", 0x10000000, 0x10000000, 0x00000000);
+    make_alias(s, &s->alias2, "alias 2", 0x30000000, 0x10000000, 0x20000000);
+    /* The 0x50000000..0x5fffffff region is not a pure alias: it has
+     * a few extra devices that only appear there (generally the
+     * control interfaces for the protection controllers).
+     * We implement this by mapping those devices over the top of this
+     * alias MR at a higher priority.
+     */
+    make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000);
+
+
+    /* Security controller */
+    object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sbd_secctl = SYS_BUS_DEVICE(&s->secctl);
+    dev_secctl = DEVICE(&s->secctl);
+    sysbus_mmio_map(sbd_secctl, 0, 0x50080000);
+    sysbus_mmio_map(sbd_secctl, 1, 0x40080000);
+
+    s->nsc_cfg_in = qemu_allocate_irq(nsccfg_handler, s, 1);
+    qdev_connect_gpio_out_named(dev_secctl, "nsc_cfg", 0, s->nsc_cfg_in);
+
+    /* The sec_resp_cfg output from the security controller must be split into
+     * multiple lines, one for each of the PPCs within the ARMSSE and one
+     * that will be an output from the ARMSSE to the system.
+     */
+    object_property_set_int(OBJECT(&s->sec_resp_splitter), 3,
+                            "num-lines", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    object_property_set_bool(OBJECT(&s->sec_resp_splitter), true,
+                             "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    dev_splitter = DEVICE(&s->sec_resp_splitter);
+    qdev_connect_gpio_out_named(dev_secctl, "sec_resp_cfg", 0,
+                                qdev_get_gpio_in(dev_splitter, 0));
+
+    /* Each SRAM bank lives behind its own Memory Protection Controller */
+    for (i = 0; i < info->sram_banks; i++) {
+        char *ramname = g_strdup_printf("armsse.sram%d", i);
+        SysBusDevice *sbd_mpc;
+        uint32_t sram_bank_size = 1 << s->sram_addr_width;
+
+        memory_region_init_ram(&s->sram[i], NULL, ramname,
+                               sram_bank_size, &err);
+        g_free(ramname);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+        object_property_set_link(OBJECT(&s->mpc[i]), OBJECT(&s->sram[i]),
+                                 "downstream", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+        object_property_set_bool(OBJECT(&s->mpc[i]), true, "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+        /* Map the upstream end of the MPC into the right place... */
+        sbd_mpc = SYS_BUS_DEVICE(&s->mpc[i]);
+        memory_region_add_subregion(&s->container,
+                                    0x20000000 + i * sram_bank_size,
+                                    sysbus_mmio_get_region(sbd_mpc, 1));
+        /* ...and its register interface */
+        memory_region_add_subregion(&s->container, 0x50083000 + i * 0x1000,
+                                    sysbus_mmio_get_region(sbd_mpc, 0));
+    }
+
+    /* We must OR together lines from the MPC splitters to go to the NVIC */
+    object_property_set_int(OBJECT(&s->mpc_irq_orgate),
+                            IOTS_NUM_EXP_MPC + info->sram_banks,
+                            "num-lines", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    object_property_set_bool(OBJECT(&s->mpc_irq_orgate), true,
+                             "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    qdev_connect_gpio_out(DEVICE(&s->mpc_irq_orgate), 0,
+                          armsse_get_common_irq_in(s, 9));
+
+    /* Devices behind APB PPC0:
+     *   0x40000000: timer0
+     *   0x40001000: timer1
+     *   0x40002000: dual timer
+     *   0x40003000: MHU0 (SSE-200 only)
+     *   0x40004000: MHU1 (SSE-200 only)
+     * We must configure and realize each downstream device and connect
+     * it to the appropriate PPC port; then we can realize the PPC and
+     * map its upstream ends to the right place in the container.
+     */
+    qdev_prop_set_uint32(DEVICE(&s->timer0), "pclk-frq", s->mainclk_frq);
+    object_property_set_bool(OBJECT(&s->timer0), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer0), 0,
+                       armsse_get_common_irq_in(s, 3));
+    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer0), 0);
+    object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[0]", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+
+    qdev_prop_set_uint32(DEVICE(&s->timer1), "pclk-frq", s->mainclk_frq);
+    object_property_set_bool(OBJECT(&s->timer1), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer1), 0,
+                       armsse_get_common_irq_in(s, 4));
+    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer1), 0);
+    object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[1]", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+
+
+    qdev_prop_set_uint32(DEVICE(&s->dualtimer), "pclk-frq", s->mainclk_frq);
+    object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->dualtimer), 0,
+                       armsse_get_common_irq_in(s, 5));
+    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0);
+    object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+
+    if (info->has_mhus) {
+        for (i = 0; i < ARRAY_SIZE(s->mhu); i++) {
+            char *name = g_strdup_printf("MHU%d", i);
+            char *port = g_strdup_printf("port[%d]", i + 3);
+
+            qdev_prop_set_string(DEVICE(&s->mhu[i]), "name", name);
+            qdev_prop_set_uint64(DEVICE(&s->mhu[i]), "size", 0x1000);
+            object_property_set_bool(OBJECT(&s->mhu[i]), true,
+                                     "realized", &err);
+            if (err) {
+                error_propagate(errp, err);
+                return;
+            }
+            mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mhu[i]), 0);
+            object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr),
+                                     port, &err);
+            if (err) {
+                error_propagate(errp, err);
+                return;
+            }
+            g_free(name);
+            g_free(port);
+        }
+    }
+
+    object_property_set_bool(OBJECT(&s->apb_ppc0), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+
+    sbd_apb_ppc0 = SYS_BUS_DEVICE(&s->apb_ppc0);
+    dev_apb_ppc0 = DEVICE(&s->apb_ppc0);
+
+    mr = sysbus_mmio_get_region(sbd_apb_ppc0, 0);
+    memory_region_add_subregion(&s->container, 0x40000000, mr);
+    mr = sysbus_mmio_get_region(sbd_apb_ppc0, 1);
+    memory_region_add_subregion(&s->container, 0x40001000, mr);
+    mr = sysbus_mmio_get_region(sbd_apb_ppc0, 2);
+    memory_region_add_subregion(&s->container, 0x40002000, mr);
+    if (info->has_mhus) {
+        mr = sysbus_mmio_get_region(sbd_apb_ppc0, 3);
+        memory_region_add_subregion(&s->container, 0x40003000, mr);
+        mr = sysbus_mmio_get_region(sbd_apb_ppc0, 4);
+        memory_region_add_subregion(&s->container, 0x40004000, mr);
+    }
+    for (i = 0; i < IOTS_APB_PPC0_NUM_PORTS; i++) {
+        qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_nonsec", i,
+                                    qdev_get_gpio_in_named(dev_apb_ppc0,
+                                                           "cfg_nonsec", i));
+        qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_ap", i,
+                                    qdev_get_gpio_in_named(dev_apb_ppc0,
+                                                           "cfg_ap", i));
+    }
+    qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_enable", 0,
+                                qdev_get_gpio_in_named(dev_apb_ppc0,
+                                                       "irq_enable", 0));
+    qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_clear", 0,
+                                qdev_get_gpio_in_named(dev_apb_ppc0,
+                                                       "irq_clear", 0));
+    qdev_connect_gpio_out(dev_splitter, 0,
+                          qdev_get_gpio_in_named(dev_apb_ppc0,
+                                                 "cfg_sec_resp", 0));
+
+    /* All the PPC irq lines (from the 2 internal PPCs and the 8 external
+     * ones) are sent individually to the security controller, and also
+     * ORed together to give a single combined PPC interrupt to the NVIC.
+     */
+    object_property_set_int(OBJECT(&s->ppc_irq_orgate),
+                            NUM_PPCS, "num-lines", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    object_property_set_bool(OBJECT(&s->ppc_irq_orgate), true,
+                             "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    qdev_connect_gpio_out(DEVICE(&s->ppc_irq_orgate), 0,
+                          armsse_get_common_irq_in(s, 10));
+
+    /*
+     * 0x40010000 .. 0x4001ffff (and the 0x5001000... secure-only alias):
+     * private per-CPU region (all these devices are SSE-200 only):
+     *  0x50010000: L1 icache control registers
+     *  0x50011000: CPUSECCTRL (CPU local security control registers)
+     *  0x4001f000 and 0x5001f000: CPU_IDENTITY register block
+     */
+    if (info->has_cachectrl) {
+        for (i = 0; i < info->num_cpus; i++) {
+            char *name = g_strdup_printf("cachectrl%d", i);
+            MemoryRegion *mr;
+
+            qdev_prop_set_string(DEVICE(&s->cachectrl[i]), "name", name);
+            g_free(name);
+            qdev_prop_set_uint64(DEVICE(&s->cachectrl[i]), "size", 0x1000);
+            object_property_set_bool(OBJECT(&s->cachectrl[i]), true,
+                                     "realized", &err);
+            if (err) {
+                error_propagate(errp, err);
+                return;
+            }
+
+            mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cachectrl[i]), 0);
+            memory_region_add_subregion(&s->cpu_container[i], 0x50010000, mr);
+        }
+    }
+    if (info->has_cpusecctrl) {
+        for (i = 0; i < info->num_cpus; i++) {
+            char *name = g_strdup_printf("CPUSECCTRL%d", i);
+            MemoryRegion *mr;
+
+            qdev_prop_set_string(DEVICE(&s->cpusecctrl[i]), "name", name);
+            g_free(name);
+            qdev_prop_set_uint64(DEVICE(&s->cpusecctrl[i]), "size", 0x1000);
+            object_property_set_bool(OBJECT(&s->cpusecctrl[i]), true,
+                                     "realized", &err);
+            if (err) {
+                error_propagate(errp, err);
+                return;
+            }
+
+            mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cpusecctrl[i]), 0);
+            memory_region_add_subregion(&s->cpu_container[i], 0x50011000, mr);
+        }
+    }
+    if (info->has_cpuid) {
+        for (i = 0; i < info->num_cpus; i++) {
+            MemoryRegion *mr;
+
+            qdev_prop_set_uint32(DEVICE(&s->cpuid[i]), "CPUID", i);
+            object_property_set_bool(OBJECT(&s->cpuid[i]), true,
+                                     "realized", &err);
+            if (err) {
+                error_propagate(errp, err);
+                return;
+            }
+
+            mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cpuid[i]), 0);
+            memory_region_add_subregion(&s->cpu_container[i], 0x4001F000, mr);
+        }
+    }
+
+    /* 0x40020000 .. 0x4002ffff : ARMSSE system control peripheral region */
+    /* Devices behind APB PPC1:
+     *   0x4002f000: S32K timer
+     */
+    qdev_prop_set_uint32(DEVICE(&s->s32ktimer), "pclk-frq", S32KCLK);
+    object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32ktimer), 0,
+                       armsse_get_common_irq_in(s, 2));
+    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0);
+    object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+
+    object_property_set_bool(OBJECT(&s->apb_ppc1), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->apb_ppc1), 0);
+    memory_region_add_subregion(&s->container, 0x4002f000, mr);
+
+    dev_apb_ppc1 = DEVICE(&s->apb_ppc1);
+    qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_nonsec", 0,
+                                qdev_get_gpio_in_named(dev_apb_ppc1,
+                                                       "cfg_nonsec", 0));
+    qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_ap", 0,
+                                qdev_get_gpio_in_named(dev_apb_ppc1,
+                                                       "cfg_ap", 0));
+    qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_enable", 0,
+                                qdev_get_gpio_in_named(dev_apb_ppc1,
+                                                       "irq_enable", 0));
+    qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_clear", 0,
+                                qdev_get_gpio_in_named(dev_apb_ppc1,
+                                                       "irq_clear", 0));
+    qdev_connect_gpio_out(dev_splitter, 1,
+                          qdev_get_gpio_in_named(dev_apb_ppc1,
+                                                 "cfg_sec_resp", 0));
+
+    object_property_set_int(OBJECT(&s->sysinfo), info->sys_version,
+                            "SYS_VERSION", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    object_property_set_int(OBJECT(&s->sysinfo),
+                            armsse_sys_config_value(s, info),
+                            "SYS_CONFIG", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    object_property_set_bool(OBJECT(&s->sysinfo), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    /* System information registers */
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, 0x40020000);
+    /* System control registers */
+    object_property_set_bool(OBJECT(&s->sysctl), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctl), 0, 0x50021000);
+
+    if (info->has_ppus) {
+        /* CPUnCORE_PPU for each CPU */
+        for (i = 0; i < info->num_cpus; i++) {
+            char *name = g_strdup_printf("CPU%dCORE_PPU", i);
+
+            map_ppu(s, CPU0CORE_PPU + i, name, 0x50023000 + i * 0x2000);
+            /*
+             * We don't support CPU debug so don't create the
+             * CPU0DEBUG_PPU at 0x50024000 and 0x50026000.
+             */
+            g_free(name);
+        }
+        map_ppu(s, DBG_PPU, "DBG_PPU", 0x50029000);
+
+        for (i = 0; i < info->sram_banks; i++) {
+            char *name = g_strdup_printf("RAM%d_PPU", i);
+
+            map_ppu(s, RAM0_PPU + i, name, 0x5002a000 + i * 0x1000);
+            g_free(name);
+        }
+    }
+
+    /* This OR gate wires together outputs from the secure watchdogs to NMI */
+    object_property_set_int(OBJECT(&s->nmi_orgate), 2, "num-lines", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    object_property_set_bool(OBJECT(&s->nmi_orgate), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    qdev_connect_gpio_out(DEVICE(&s->nmi_orgate), 0,
+                          qdev_get_gpio_in_named(DEVICE(&s->armv7m), "NMI", 0));
+
+    qdev_prop_set_uint32(DEVICE(&s->s32kwatchdog), "wdogclk-frq", S32KCLK);
+    object_property_set_bool(OBJECT(&s->s32kwatchdog), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32kwatchdog), 0,
+                       qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 0));
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, 0x5002e000);
+
+    /* 0x40080000 .. 0x4008ffff : ARMSSE second Base peripheral region */
+
+    qdev_prop_set_uint32(DEVICE(&s->nswatchdog), "wdogclk-frq", s->mainclk_frq);
+    object_property_set_bool(OBJECT(&s->nswatchdog), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->nswatchdog), 0,
+                       armsse_get_common_irq_in(s, 1));
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->nswatchdog), 0, 0x40081000);
+
+    qdev_prop_set_uint32(DEVICE(&s->swatchdog), "wdogclk-frq", s->mainclk_frq);
+    object_property_set_bool(OBJECT(&s->swatchdog), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->swatchdog), 0,
+                       qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 1));
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->swatchdog), 0, 0x50081000);
+
+    for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) {
+        Object *splitter = OBJECT(&s->ppc_irq_splitter[i]);
+
+        object_property_set_int(splitter, 2, "num-lines", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+        object_property_set_bool(splitter, true, "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+    }
+
+    for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) {
+        char *ppcname = g_strdup_printf("ahb_ppcexp%d", i);
+
+        armsse_forward_ppc(s, ppcname, i);
+        g_free(ppcname);
+    }
+
+    for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) {
+        char *ppcname = g_strdup_printf("apb_ppcexp%d", i);
+
+        armsse_forward_ppc(s, ppcname, i + IOTS_NUM_AHB_EXP_PPC);
+        g_free(ppcname);
+    }
+
+    for (i = NUM_EXTERNAL_PPCS; i < NUM_PPCS; i++) {
+        /* Wire up IRQ splitter for internal PPCs */
+        DeviceState *devs = DEVICE(&s->ppc_irq_splitter[i]);
+        char *gpioname = g_strdup_printf("apb_ppc%d_irq_status",
+                                         i - NUM_EXTERNAL_PPCS);
+        TZPPC *ppc = (i == NUM_EXTERNAL_PPCS) ? &s->apb_ppc0 : &s->apb_ppc1;
+
+        qdev_connect_gpio_out(devs, 0,
+                              qdev_get_gpio_in_named(dev_secctl, gpioname, 0));
+        qdev_connect_gpio_out(devs, 1,
+                              qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), i));
+        qdev_connect_gpio_out_named(DEVICE(ppc), "irq", 0,
+                                    qdev_get_gpio_in(devs, 0));
+        g_free(gpioname);
+    }
+
+    /* Wire up the splitters for the MPC IRQs */
+    for (i = 0; i < IOTS_NUM_EXP_MPC + info->sram_banks; i++) {
+        SplitIRQ *splitter = &s->mpc_irq_splitter[i];
+        DeviceState *dev_splitter = DEVICE(splitter);
+
+        object_property_set_int(OBJECT(splitter), 2, "num-lines", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+        object_property_set_bool(OBJECT(splitter), true, "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+
+        if (i < IOTS_NUM_EXP_MPC) {
+            /* Splitter input is from GPIO input line */
+            s->mpcexp_status_in[i] = qdev_get_gpio_in(dev_splitter, 0);
+            qdev_connect_gpio_out(dev_splitter, 0,
+                                  qdev_get_gpio_in_named(dev_secctl,
+                                                         "mpcexp_status", i));
+        } else {
+            /* Splitter input is from our own MPC */
+            qdev_connect_gpio_out_named(DEVICE(&s->mpc[i - IOTS_NUM_EXP_MPC]),
+                                        "irq", 0,
+                                        qdev_get_gpio_in(dev_splitter, 0));
+            qdev_connect_gpio_out(dev_splitter, 0,
+                                  qdev_get_gpio_in_named(dev_secctl,
+                                                         "mpc_status", 0));
+        }
+
+        qdev_connect_gpio_out(dev_splitter, 1,
+                              qdev_get_gpio_in(DEVICE(&s->mpc_irq_orgate), i));
+    }
+    /* Create GPIO inputs which will pass the line state for our
+     * mpcexp_irq inputs to the correct splitter devices.
+     */
+    qdev_init_gpio_in_named(dev, armsse_mpcexp_status, "mpcexp_status",
+                            IOTS_NUM_EXP_MPC);
+
+    armsse_forward_sec_resp_cfg(s);
+
+    /* Forward the MSC related signals */
+    qdev_pass_gpios(dev_secctl, dev, "mscexp_status");
+    qdev_pass_gpios(dev_secctl, dev, "mscexp_clear");
+    qdev_pass_gpios(dev_secctl, dev, "mscexp_ns");
+    qdev_connect_gpio_out_named(dev_secctl, "msc_irq", 0,
+                                armsse_get_common_irq_in(s, 11));
+
+    /*
+     * Expose our container region to the board model; this corresponds
+     * to the AHB Slave Expansion ports which allow bus master devices
+     * (eg DMA controllers) in the board model to make transactions into
+     * devices in the ARMSSE.
+     */
+    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container);
+
+    system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq;
+}
+
+static void armsse_idau_check(IDAUInterface *ii, uint32_t address,
+                              int *iregion, bool *exempt, bool *ns, bool *nsc)
+{
+    /*
+     * For ARMSSE systems the IDAU responses are simple logical functions
+     * of the address bits. The NSC attribute is guest-adjustable via the
+     * NSCCFG register in the security controller.
+     */
+    ARMSSE *s = ARMSSE(ii);
+    int region = extract32(address, 28, 4);
+
+    *ns = !(region & 1);
+    *nsc = (region == 1 && (s->nsccfg & 1)) || (region == 3 && (s->nsccfg & 2));
+    /* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */
+    *exempt = (address & 0xeff00000) == 0xe0000000;
+    *iregion = region;
+}
+
+static const VMStateDescription armsse_vmstate = {
+    .name = "iotkit",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(nsccfg, ARMSSE),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property armsse_properties[] = {
+    DEFINE_PROP_LINK("memory", ARMSSE, board_memory, TYPE_MEMORY_REGION,
+                     MemoryRegion *),
+    DEFINE_PROP_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64),
+    DEFINE_PROP_UINT32("MAINCLK", ARMSSE, mainclk_frq, 0),
+    DEFINE_PROP_UINT32("SRAM_ADDR_WIDTH", ARMSSE, sram_addr_width, 15),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void armsse_reset(DeviceState *dev)
+{
+    ARMSSE *s = ARMSSE(dev);
+
+    s->nsccfg = 0;
+}
+
+static void armsse_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(klass);
+    ARMSSEClass *asc = ARMSSE_CLASS(klass);
+
+    dc->realize = armsse_realize;
+    dc->vmsd = &armsse_vmstate;
+    dc->props = armsse_properties;
+    dc->reset = armsse_reset;
+    iic->check = armsse_idau_check;
+    asc->info = data;
+}
+
+static const TypeInfo armsse_info = {
+    .name = TYPE_ARMSSE,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(ARMSSE),
+    .instance_init = armsse_init,
+    .abstract = true,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_IDAU_INTERFACE },
+        { }
+    }
+};
+
+static void armsse_register_types(void)
+{
+    int i;
+
+    type_register_static(&armsse_info);
+
+    for (i = 0; i < ARRAY_SIZE(armsse_variants); i++) {
+        TypeInfo ti = {
+            .name = armsse_variants[i].name,
+            .parent = TYPE_ARMSSE,
+            .class_init = armsse_class_init,
+            .class_data = (void *)&armsse_variants[i],
+        };
+        type_register(&ti);
+    }
+}
+
+type_init(armsse_register_types);
diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c
index f444652830..adae11e76e 100644
--- a/hw/arm/armv7m.c
+++ b/hw/arm/armv7m.c
@@ -158,7 +158,12 @@ static void armv7m_realize(DeviceState *dev, Error **errp)
 
     memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1);
 
-    s->cpu = ARM_CPU(object_new(s->cpu_type));
+    s->cpu = ARM_CPU(object_new_with_props(s->cpu_type, OBJECT(s), "cpu",
+                                           &err, NULL));
+    if (err != NULL) {
+        error_propagate(errp, err);
+        return;
+    }
 
     object_property_set_link(OBJECT(s->cpu), OBJECT(&s->container), "memory",
                              &error_abort);
@@ -177,11 +182,21 @@ static void armv7m_realize(DeviceState *dev, Error **errp)
             return;
         }
     }
+    if (object_property_find(OBJECT(s->cpu), "start-powered-off", NULL)) {
+        object_property_set_bool(OBJECT(s->cpu), s->start_powered_off,
+                                 "start-powered-off", &err);
+        if (err != NULL) {
+            error_propagate(errp, err);
+            return;
+        }
+    }
 
-    /* Tell the CPU where the NVIC is; it will fail realize if it doesn't
-     * have one.
+    /*
+     * Tell the CPU where the NVIC is; it will fail realize if it doesn't
+     * have one. Similarly, tell the NVIC where its CPU is.
      */
     s->cpu->env.nvic = &s->nvic;
+    s->nvic.cpu = s->cpu;
 
     object_property_set_bool(OBJECT(s->cpu), true, "realized", &err);
     if (err != NULL) {
@@ -243,6 +258,8 @@ static Property armv7m_properties[] = {
     DEFINE_PROP_LINK("idau", ARMv7MState, idau, TYPE_IDAU_INTERFACE, Object *),
     DEFINE_PROP_UINT32("init-svtor", ARMv7MState, init_svtor, 0),
     DEFINE_PROP_BOOL("enable-bitband", ARMv7MState, enable_bitband, false),
+    DEFINE_PROP_BOOL("start-powered-off", ARMv7MState, start_powered_off,
+                     false),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index c7a67af7a9..05762d0fc1 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -697,10 +697,6 @@ static void do_cpu_reset(void *opaque)
                 g_assert_not_reached();
             }
 
-            if (!env->aarch64) {
-                env->thumb = info->entry & 1;
-                entry &= 0xfffffffe;
-            }
             cpu_set_pc(cs, entry);
         } else {
             /* If we are booting Linux then we need to check whether we are
diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c
deleted file mode 100644
index 8742200fb4..0000000000
--- a/hw/arm/iotkit.c
+++ /dev/null
@@ -1,759 +0,0 @@
-/*
- * Arm IoT Kit
- *
- * Copyright (c) 2018 Linaro Limited
- * Written by Peter Maydell
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 or
- * (at your option) any later version.
- */
-
-#include "qemu/osdep.h"
-#include "qemu/log.h"
-#include "qapi/error.h"
-#include "trace.h"
-#include "hw/sysbus.h"
-#include "hw/registerfields.h"
-#include "hw/arm/iotkit.h"
-#include "hw/arm/arm.h"
-
-/* Clock frequency in HZ of the 32KHz "slow clock" */
-#define S32KCLK (32 * 1000)
-
-/* Create an alias region of @size bytes starting at @base
- * which mirrors the memory starting at @orig.
- */
-static void make_alias(IoTKit *s, MemoryRegion *mr, const char *name,
-                       hwaddr base, hwaddr size, hwaddr orig)
-{
-    memory_region_init_alias(mr, NULL, name, &s->container, orig, size);
-    /* The alias is even lower priority than unimplemented_device regions */
-    memory_region_add_subregion_overlap(&s->container, base, mr, -1500);
-}
-
-static void irq_status_forwarder(void *opaque, int n, int level)
-{
-    qemu_irq destirq = opaque;
-
-    qemu_set_irq(destirq, level);
-}
-
-static void nsccfg_handler(void *opaque, int n, int level)
-{
-    IoTKit *s = IOTKIT(opaque);
-
-    s->nsccfg = level;
-}
-
-static void iotkit_forward_ppc(IoTKit *s, const char *ppcname, int ppcnum)
-{
-    /* Each of the 4 AHB and 4 APB PPCs that might be present in a
-     * system using the IoTKit has a collection of control lines which
-     * are provided by the security controller and which we want to
-     * expose as control lines on the IoTKit device itself, so the
-     * code using the IoTKit can wire them up to the PPCs.
-     */
-    SplitIRQ *splitter = &s->ppc_irq_splitter[ppcnum];
-    DeviceState *iotkitdev = DEVICE(s);
-    DeviceState *dev_secctl = DEVICE(&s->secctl);
-    DeviceState *dev_splitter = DEVICE(splitter);
-    char *name;
-
-    name = g_strdup_printf("%s_nonsec", ppcname);
-    qdev_pass_gpios(dev_secctl, iotkitdev, name);
-    g_free(name);
-    name = g_strdup_printf("%s_ap", ppcname);
-    qdev_pass_gpios(dev_secctl, iotkitdev, name);
-    g_free(name);
-    name = g_strdup_printf("%s_irq_enable", ppcname);
-    qdev_pass_gpios(dev_secctl, iotkitdev, name);
-    g_free(name);
-    name = g_strdup_printf("%s_irq_clear", ppcname);
-    qdev_pass_gpios(dev_secctl, iotkitdev, name);
-    g_free(name);
-
-    /* irq_status is a little more tricky, because we need to
-     * split it so we can send it both to the security controller
-     * and to our OR gate for the NVIC interrupt line.
-     * Connect up the splitter's outputs, and create a GPIO input
-     * which will pass the line state to the input splitter.
-     */
-    name = g_strdup_printf("%s_irq_status", ppcname);
-    qdev_connect_gpio_out(dev_splitter, 0,
-                          qdev_get_gpio_in_named(dev_secctl,
-                                                 name, 0));
-    qdev_connect_gpio_out(dev_splitter, 1,
-                          qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), ppcnum));
-    s->irq_status_in[ppcnum] = qdev_get_gpio_in(dev_splitter, 0);
-    qdev_init_gpio_in_named_with_opaque(iotkitdev, irq_status_forwarder,
-                                        s->irq_status_in[ppcnum], name, 1);
-    g_free(name);
-}
-
-static void iotkit_forward_sec_resp_cfg(IoTKit *s)
-{
-    /* Forward the 3rd output from the splitter device as a
-     * named GPIO output of the iotkit object.
-     */
-    DeviceState *dev = DEVICE(s);
-    DeviceState *dev_splitter = DEVICE(&s->sec_resp_splitter);
-
-    qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1);
-    s->sec_resp_cfg_in = qemu_allocate_irq(irq_status_forwarder,
-                                           s->sec_resp_cfg, 1);
-    qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in);
-}
-
-static void iotkit_init(Object *obj)
-{
-    IoTKit *s = IOTKIT(obj);
-    int i;
-
-    memory_region_init(&s->container, obj, "iotkit-container", UINT64_MAX);
-
-    sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m),
-                          TYPE_ARMV7M);
-    qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type",
-                         ARM_CPU_TYPE_NAME("cortex-m33"));
-
-    sysbus_init_child_obj(obj, "secctl", &s->secctl, sizeof(s->secctl),
-                          TYPE_IOTKIT_SECCTL);
-    sysbus_init_child_obj(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0),
-                          TYPE_TZ_PPC);
-    sysbus_init_child_obj(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1),
-                          TYPE_TZ_PPC);
-    sysbus_init_child_obj(obj, "mpc", &s->mpc, sizeof(s->mpc), TYPE_TZ_MPC);
-    object_initialize_child(obj, "mpc-irq-orgate", &s->mpc_irq_orgate,
-                            sizeof(s->mpc_irq_orgate), TYPE_OR_IRQ,
-                            &error_abort, NULL);
-
-    for (i = 0; i < ARRAY_SIZE(s->mpc_irq_splitter); i++) {
-        char *name = g_strdup_printf("mpc-irq-splitter-%d", i);
-        SplitIRQ *splitter = &s->mpc_irq_splitter[i];
-
-        object_initialize_child(obj, name, splitter, sizeof(*splitter),
-                                TYPE_SPLIT_IRQ, &error_abort, NULL);
-        g_free(name);
-    }
-    sysbus_init_child_obj(obj, "timer0", &s->timer0, sizeof(s->timer0),
-                          TYPE_CMSDK_APB_TIMER);
-    sysbus_init_child_obj(obj, "timer1", &s->timer1, sizeof(s->timer1),
-                          TYPE_CMSDK_APB_TIMER);
-    sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer),
-                          TYPE_CMSDK_APB_TIMER);
-    sysbus_init_child_obj(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer),
-                          TYPE_CMSDK_APB_DUALTIMER);
-    sysbus_init_child_obj(obj, "s32kwatchdog", &s->s32kwatchdog,
-                          sizeof(s->s32kwatchdog), TYPE_CMSDK_APB_WATCHDOG);
-    sysbus_init_child_obj(obj, "nswatchdog", &s->nswatchdog,
-                          sizeof(s->nswatchdog), TYPE_CMSDK_APB_WATCHDOG);
-    sysbus_init_child_obj(obj, "swatchdog", &s->swatchdog,
-                          sizeof(s->swatchdog), TYPE_CMSDK_APB_WATCHDOG);
-    sysbus_init_child_obj(obj, "iotkit-sysctl", &s->sysctl,
-                          sizeof(s->sysctl), TYPE_IOTKIT_SYSCTL);
-    sysbus_init_child_obj(obj, "iotkit-sysinfo", &s->sysinfo,
-                          sizeof(s->sysinfo), TYPE_IOTKIT_SYSINFO);
-    object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate,
-                            sizeof(s->nmi_orgate), TYPE_OR_IRQ,
-                            &error_abort, NULL);
-    object_initialize_child(obj, "ppc-irq-orgate", &s->ppc_irq_orgate,
-                            sizeof(s->ppc_irq_orgate), TYPE_OR_IRQ,
-                            &error_abort, NULL);
-    object_initialize_child(obj, "sec-resp-splitter", &s->sec_resp_splitter,
-                            sizeof(s->sec_resp_splitter), TYPE_SPLIT_IRQ,
-                            &error_abort, NULL);
-    for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) {
-        char *name = g_strdup_printf("ppc-irq-splitter-%d", i);
-        SplitIRQ *splitter = &s->ppc_irq_splitter[i];
-
-        object_initialize_child(obj, name, splitter, sizeof(*splitter),
-                                TYPE_SPLIT_IRQ, &error_abort, NULL);
-        g_free(name);
-    }
-}
-
-static void iotkit_exp_irq(void *opaque, int n, int level)
-{
-    IoTKit *s = IOTKIT(opaque);
-
-    qemu_set_irq(s->exp_irqs[n], level);
-}
-
-static void iotkit_mpcexp_status(void *opaque, int n, int level)
-{
-    IoTKit *s = IOTKIT(opaque);
-    qemu_set_irq(s->mpcexp_status_in[n], level);
-}
-
-static void iotkit_realize(DeviceState *dev, Error **errp)
-{
-    IoTKit *s = IOTKIT(dev);
-    int i;
-    MemoryRegion *mr;
-    Error *err = NULL;
-    SysBusDevice *sbd_apb_ppc0;
-    SysBusDevice *sbd_secctl;
-    DeviceState *dev_apb_ppc0;
-    DeviceState *dev_apb_ppc1;
-    DeviceState *dev_secctl;
-    DeviceState *dev_splitter;
-
-    if (!s->board_memory) {
-        error_setg(errp, "memory property was not set");
-        return;
-    }
-
-    if (!s->mainclk_frq) {
-        error_setg(errp, "MAINCLK property was not set");
-        return;
-    }
-
-    /* Handling of which devices should be available only to secure
-     * code is usually done differently for M profile than for A profile.
-     * Instead of putting some devices only into the secure address space,
-     * devices exist in both address spaces but with hard-wired security
-     * permissions that will cause the CPU to fault for non-secure accesses.
-     *
-     * The IoTKit has an IDAU (Implementation Defined Access Unit),
-     * which specifies hard-wired security permissions for different
-     * areas of the physical address space. For the IoTKit IDAU, the
-     * top 4 bits of the physical address are the IDAU region ID, and
-     * if bit 28 (ie the lowest bit of the ID) is 0 then this is an NS
-     * region, otherwise it is an S region.
-     *
-     * The various devices and RAMs are generally all mapped twice,
-     * once into a region that the IDAU defines as secure and once
-     * into a non-secure region. They sit behind either a Memory
-     * Protection Controller (for RAM) or a Peripheral Protection
-     * Controller (for devices), which allow a more fine grained
-     * configuration of whether non-secure accesses are permitted.
-     *
-     * (The other place that guest software can configure security
-     * permissions is in the architected SAU (Security Attribution
-     * Unit), which is entirely inside the CPU. The IDAU can upgrade
-     * the security attributes for a region to more restrictive than
-     * the SAU specifies, but cannot downgrade them.)
-     *
-     * 0x10000000..0x1fffffff  alias of 0x00000000..0x0fffffff
-     * 0x20000000..0x2007ffff  32KB FPGA block RAM
-     * 0x30000000..0x3fffffff  alias of 0x20000000..0x2fffffff
-     * 0x40000000..0x4000ffff  base peripheral region 1
-     * 0x40010000..0x4001ffff  CPU peripherals (none for IoTKit)
-     * 0x40020000..0x4002ffff  system control element peripherals
-     * 0x40080000..0x400fffff  base peripheral region 2
-     * 0x50000000..0x5fffffff  alias of 0x40000000..0x4fffffff
-     */
-
-    memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1);
-
-    qdev_prop_set_uint32(DEVICE(&s->armv7m), "num-irq", s->exp_numirq + 32);
-    /* In real hardware the initial Secure VTOR is set from the INITSVTOR0
-     * register in the IoT Kit System Control Register block, and the
-     * initial value of that is in turn specifiable by the FPGA that
-     * instantiates the IoT Kit. In QEMU we don't implement this wrinkle,
-     * and simply set the CPU's init-svtor to the IoT Kit default value.
-     */
-    qdev_prop_set_uint32(DEVICE(&s->armv7m), "init-svtor", 0x10000000);
-    object_property_set_link(OBJECT(&s->armv7m), OBJECT(&s->container),
-                             "memory", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    object_property_set_link(OBJECT(&s->armv7m), OBJECT(s), "idau", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    object_property_set_bool(OBJECT(&s->armv7m), true, "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-
-    /* Connect our EXP_IRQ GPIOs to the NVIC's lines 32 and up. */
-    s->exp_irqs = g_new(qemu_irq, s->exp_numirq);
-    for (i = 0; i < s->exp_numirq; i++) {
-        s->exp_irqs[i] = qdev_get_gpio_in(DEVICE(&s->armv7m), i + 32);
-    }
-    qdev_init_gpio_in_named(dev, iotkit_exp_irq, "EXP_IRQ", s->exp_numirq);
-
-    /* Set up the big aliases first */
-    make_alias(s, &s->alias1, "alias 1", 0x10000000, 0x10000000, 0x00000000);
-    make_alias(s, &s->alias2, "alias 2", 0x30000000, 0x10000000, 0x20000000);
-    /* The 0x50000000..0x5fffffff region is not a pure alias: it has
-     * a few extra devices that only appear there (generally the
-     * control interfaces for the protection controllers).
-     * We implement this by mapping those devices over the top of this
-     * alias MR at a higher priority.
-     */
-    make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000);
-
-
-    /* Security controller */
-    object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    sbd_secctl = SYS_BUS_DEVICE(&s->secctl);
-    dev_secctl = DEVICE(&s->secctl);
-    sysbus_mmio_map(sbd_secctl, 0, 0x50080000);
-    sysbus_mmio_map(sbd_secctl, 1, 0x40080000);
-
-    s->nsc_cfg_in = qemu_allocate_irq(nsccfg_handler, s, 1);
-    qdev_connect_gpio_out_named(dev_secctl, "nsc_cfg", 0, s->nsc_cfg_in);
-
-    /* The sec_resp_cfg output from the security controller must be split into
-     * multiple lines, one for each of the PPCs within the IoTKit and one
-     * that will be an output from the IoTKit to the system.
-     */
-    object_property_set_int(OBJECT(&s->sec_resp_splitter), 3,
-                            "num-lines", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    object_property_set_bool(OBJECT(&s->sec_resp_splitter), true,
-                             "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    dev_splitter = DEVICE(&s->sec_resp_splitter);
-    qdev_connect_gpio_out_named(dev_secctl, "sec_resp_cfg", 0,
-                                qdev_get_gpio_in(dev_splitter, 0));
-
-    /* This RAM lives behind the Memory Protection Controller */
-    memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    object_property_set_link(OBJECT(&s->mpc), OBJECT(&s->sram0),
-                             "downstream", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    object_property_set_bool(OBJECT(&s->mpc), true, "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    /* Map the upstream end of the MPC into the right place... */
-    memory_region_add_subregion(&s->container, 0x20000000,
-                                sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc),
-                                                       1));
-    /* ...and its register interface */
-    memory_region_add_subregion(&s->container, 0x50083000,
-                                sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc),
-                                                       0));
-
-    /* We must OR together lines from the MPC splitters to go to the NVIC */
-    object_property_set_int(OBJECT(&s->mpc_irq_orgate),
-                            IOTS_NUM_EXP_MPC + IOTS_NUM_MPC, "num-lines", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    object_property_set_bool(OBJECT(&s->mpc_irq_orgate), true,
-                             "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    qdev_connect_gpio_out(DEVICE(&s->mpc_irq_orgate), 0,
-                          qdev_get_gpio_in(DEVICE(&s->armv7m), 9));
-
-    /* Devices behind APB PPC0:
-     *   0x40000000: timer0
-     *   0x40001000: timer1
-     *   0x40002000: dual timer
-     * We must configure and realize each downstream device and connect
-     * it to the appropriate PPC port; then we can realize the PPC and
-     * map its upstream ends to the right place in the container.
-     */
-    qdev_prop_set_uint32(DEVICE(&s->timer0), "pclk-frq", s->mainclk_frq);
-    object_property_set_bool(OBJECT(&s->timer0), true, "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer0), 0,
-                       qdev_get_gpio_in(DEVICE(&s->armv7m), 3));
-    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer0), 0);
-    object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[0]", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-
-    qdev_prop_set_uint32(DEVICE(&s->timer1), "pclk-frq", s->mainclk_frq);
-    object_property_set_bool(OBJECT(&s->timer1), true, "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer1), 0,
-                       qdev_get_gpio_in(DEVICE(&s->armv7m), 4));
-    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer1), 0);
-    object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[1]", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-
-
-    qdev_prop_set_uint32(DEVICE(&s->dualtimer), "pclk-frq", s->mainclk_frq);
-    object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    sysbus_connect_irq(SYS_BUS_DEVICE(&s->dualtimer), 0,
-                       qdev_get_gpio_in(DEVICE(&s->armv7m), 5));
-    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0);
-    object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-
-    object_property_set_bool(OBJECT(&s->apb_ppc0), true, "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-
-    sbd_apb_ppc0 = SYS_BUS_DEVICE(&s->apb_ppc0);
-    dev_apb_ppc0 = DEVICE(&s->apb_ppc0);
-
-    mr = sysbus_mmio_get_region(sbd_apb_ppc0, 0);
-    memory_region_add_subregion(&s->container, 0x40000000, mr);
-    mr = sysbus_mmio_get_region(sbd_apb_ppc0, 1);
-    memory_region_add_subregion(&s->container, 0x40001000, mr);
-    mr = sysbus_mmio_get_region(sbd_apb_ppc0, 2);
-    memory_region_add_subregion(&s->container, 0x40002000, mr);
-    for (i = 0; i < IOTS_APB_PPC0_NUM_PORTS; i++) {
-        qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_nonsec", i,
-                                    qdev_get_gpio_in_named(dev_apb_ppc0,
-                                                           "cfg_nonsec", i));
-        qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_ap", i,
-                                    qdev_get_gpio_in_named(dev_apb_ppc0,
-                                                           "cfg_ap", i));
-    }
-    qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_enable", 0,
-                                qdev_get_gpio_in_named(dev_apb_ppc0,
-                                                       "irq_enable", 0));
-    qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_clear", 0,
-                                qdev_get_gpio_in_named(dev_apb_ppc0,
-                                                       "irq_clear", 0));
-    qdev_connect_gpio_out(dev_splitter, 0,
-                          qdev_get_gpio_in_named(dev_apb_ppc0,
-                                                 "cfg_sec_resp", 0));
-
-    /* All the PPC irq lines (from the 2 internal PPCs and the 8 external
-     * ones) are sent individually to the security controller, and also
-     * ORed together to give a single combined PPC interrupt to the NVIC.
-     */
-    object_property_set_int(OBJECT(&s->ppc_irq_orgate),
-                            NUM_PPCS, "num-lines", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    object_property_set_bool(OBJECT(&s->ppc_irq_orgate), true,
-                             "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    qdev_connect_gpio_out(DEVICE(&s->ppc_irq_orgate), 0,
-                          qdev_get_gpio_in(DEVICE(&s->armv7m), 10));
-
-    /* 0x40010000 .. 0x4001ffff: private CPU region: unused in IoTKit */
-
-    /* 0x40020000 .. 0x4002ffff : IoTKit system control peripheral region */
-    /* Devices behind APB PPC1:
-     *   0x4002f000: S32K timer
-     */
-    qdev_prop_set_uint32(DEVICE(&s->s32ktimer), "pclk-frq", S32KCLK);
-    object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32ktimer), 0,
-                       qdev_get_gpio_in(DEVICE(&s->armv7m), 2));
-    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0);
-    object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-
-    object_property_set_bool(OBJECT(&s->apb_ppc1), true, "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->apb_ppc1), 0);
-    memory_region_add_subregion(&s->container, 0x4002f000, mr);
-
-    dev_apb_ppc1 = DEVICE(&s->apb_ppc1);
-    qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_nonsec", 0,
-                                qdev_get_gpio_in_named(dev_apb_ppc1,
-                                                       "cfg_nonsec", 0));
-    qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_ap", 0,
-                                qdev_get_gpio_in_named(dev_apb_ppc1,
-                                                       "cfg_ap", 0));
-    qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_enable", 0,
-                                qdev_get_gpio_in_named(dev_apb_ppc1,
-                                                       "irq_enable", 0));
-    qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_clear", 0,
-                                qdev_get_gpio_in_named(dev_apb_ppc1,
-                                                       "irq_clear", 0));
-    qdev_connect_gpio_out(dev_splitter, 1,
-                          qdev_get_gpio_in_named(dev_apb_ppc1,
-                                                 "cfg_sec_resp", 0));
-
-    object_property_set_bool(OBJECT(&s->sysinfo), true, "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    /* System information registers */
-    sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, 0x40020000);
-    /* System control registers */
-    object_property_set_bool(OBJECT(&s->sysctl), true, "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctl), 0, 0x50021000);
-
-    /* This OR gate wires together outputs from the secure watchdogs to NMI */
-    object_property_set_int(OBJECT(&s->nmi_orgate), 2, "num-lines", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    object_property_set_bool(OBJECT(&s->nmi_orgate), true, "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    qdev_connect_gpio_out(DEVICE(&s->nmi_orgate), 0,
-                          qdev_get_gpio_in_named(DEVICE(&s->armv7m), "NMI", 0));
-
-    qdev_prop_set_uint32(DEVICE(&s->s32kwatchdog), "wdogclk-frq", S32KCLK);
-    object_property_set_bool(OBJECT(&s->s32kwatchdog), true, "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32kwatchdog), 0,
-                       qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 0));
-    sysbus_mmio_map(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, 0x5002e000);
-
-    /* 0x40080000 .. 0x4008ffff : IoTKit second Base peripheral region */
-
-    qdev_prop_set_uint32(DEVICE(&s->nswatchdog), "wdogclk-frq", s->mainclk_frq);
-    object_property_set_bool(OBJECT(&s->nswatchdog), true, "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    sysbus_connect_irq(SYS_BUS_DEVICE(&s->nswatchdog), 0,
-                       qdev_get_gpio_in(DEVICE(&s->armv7m), 1));
-    sysbus_mmio_map(SYS_BUS_DEVICE(&s->nswatchdog), 0, 0x40081000);
-
-    qdev_prop_set_uint32(DEVICE(&s->swatchdog), "wdogclk-frq", s->mainclk_frq);
-    object_property_set_bool(OBJECT(&s->swatchdog), true, "realized", &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    sysbus_connect_irq(SYS_BUS_DEVICE(&s->swatchdog), 0,
-                       qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 1));
-    sysbus_mmio_map(SYS_BUS_DEVICE(&s->swatchdog), 0, 0x50081000);
-
-    for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) {
-        Object *splitter = OBJECT(&s->ppc_irq_splitter[i]);
-
-        object_property_set_int(splitter, 2, "num-lines", &err);
-        if (err) {
-            error_propagate(errp, err);
-            return;
-        }
-        object_property_set_bool(splitter, true, "realized", &err);
-        if (err) {
-            error_propagate(errp, err);
-            return;
-        }
-    }
-
-    for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) {
-        char *ppcname = g_strdup_printf("ahb_ppcexp%d", i);
-
-        iotkit_forward_ppc(s, ppcname, i);
-        g_free(ppcname);
-    }
-
-    for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) {
-        char *ppcname = g_strdup_printf("apb_ppcexp%d", i);
-
-        iotkit_forward_ppc(s, ppcname, i + IOTS_NUM_AHB_EXP_PPC);
-        g_free(ppcname);
-    }
-
-    for (i = NUM_EXTERNAL_PPCS; i < NUM_PPCS; i++) {
-        /* Wire up IRQ splitter for internal PPCs */
-        DeviceState *devs = DEVICE(&s->ppc_irq_splitter[i]);
-        char *gpioname = g_strdup_printf("apb_ppc%d_irq_status",
-                                         i - NUM_EXTERNAL_PPCS);
-        TZPPC *ppc = (i == NUM_EXTERNAL_PPCS) ? &s->apb_ppc0 : &s->apb_ppc1;
-
-        qdev_connect_gpio_out(devs, 0,
-                              qdev_get_gpio_in_named(dev_secctl, gpioname, 0));
-        qdev_connect_gpio_out(devs, 1,
-                              qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), i));
-        qdev_connect_gpio_out_named(DEVICE(ppc), "irq", 0,
-                                    qdev_get_gpio_in(devs, 0));
-        g_free(gpioname);
-    }
-
-    /* Wire up the splitters for the MPC IRQs */
-    for (i = 0; i < IOTS_NUM_EXP_MPC + IOTS_NUM_MPC; i++) {
-        SplitIRQ *splitter = &s->mpc_irq_splitter[i];
-        DeviceState *dev_splitter = DEVICE(splitter);
-
-        object_property_set_int(OBJECT(splitter), 2, "num-lines", &err);
-        if (err) {
-            error_propagate(errp, err);
-            return;
-        }
-        object_property_set_bool(OBJECT(splitter), true, "realized", &err);
-        if (err) {
-            error_propagate(errp, err);
-            return;
-        }
-
-        if (i < IOTS_NUM_EXP_MPC) {
-            /* Splitter input is from GPIO input line */
-            s->mpcexp_status_in[i] = qdev_get_gpio_in(dev_splitter, 0);
-            qdev_connect_gpio_out(dev_splitter, 0,
-                                  qdev_get_gpio_in_named(dev_secctl,
-                                                         "mpcexp_status", i));
-        } else {
-            /* Splitter input is from our own MPC */
-            qdev_connect_gpio_out_named(DEVICE(&s->mpc), "irq", 0,
-                                        qdev_get_gpio_in(dev_splitter, 0));
-            qdev_connect_gpio_out(dev_splitter, 0,
-                                  qdev_get_gpio_in_named(dev_secctl,
-                                                         "mpc_status", 0));
-        }
-
-        qdev_connect_gpio_out(dev_splitter, 1,
-                              qdev_get_gpio_in(DEVICE(&s->mpc_irq_orgate), i));
-    }
-    /* Create GPIO inputs which will pass the line state for our
-     * mpcexp_irq inputs to the correct splitter devices.
-     */
-    qdev_init_gpio_in_named(dev, iotkit_mpcexp_status, "mpcexp_status",
-                            IOTS_NUM_EXP_MPC);
-
-    iotkit_forward_sec_resp_cfg(s);
-
-    /* Forward the MSC related signals */
-    qdev_pass_gpios(dev_secctl, dev, "mscexp_status");
-    qdev_pass_gpios(dev_secctl, dev, "mscexp_clear");
-    qdev_pass_gpios(dev_secctl, dev, "mscexp_ns");
-    qdev_connect_gpio_out_named(dev_secctl, "msc_irq", 0,
-                                qdev_get_gpio_in(DEVICE(&s->armv7m), 11));
-
-    /*
-     * Expose our container region to the board model; this corresponds
-     * to the AHB Slave Expansion ports which allow bus master devices
-     * (eg DMA controllers) in the board model to make transactions into
-     * devices in the IoTKit.
-     */
-    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container);
-
-    system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq;
-}
-
-static void iotkit_idau_check(IDAUInterface *ii, uint32_t address,
-                              int *iregion, bool *exempt, bool *ns, bool *nsc)
-{
-    /* For IoTKit systems the IDAU responses are simple logical functions
-     * of the address bits. The NSC attribute is guest-adjustable via the
-     * NSCCFG register in the security controller.
-     */
-    IoTKit *s = IOTKIT(ii);
-    int region = extract32(address, 28, 4);
-
-    *ns = !(region & 1);
-    *nsc = (region == 1 && (s->nsccfg & 1)) || (region == 3 && (s->nsccfg & 2));
-    /* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */
-    *exempt = (address & 0xeff00000) == 0xe0000000;
-    *iregion = region;
-}
-
-static const VMStateDescription iotkit_vmstate = {
-    .name = "iotkit",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(nsccfg, IoTKit),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property iotkit_properties[] = {
-    DEFINE_PROP_LINK("memory", IoTKit, board_memory, TYPE_MEMORY_REGION,
-                     MemoryRegion *),
-    DEFINE_PROP_UINT32("EXP_NUMIRQ", IoTKit, exp_numirq, 64),
-    DEFINE_PROP_UINT32("MAINCLK", IoTKit, mainclk_frq, 0),
-    DEFINE_PROP_END_OF_LIST()
-};
-
-static void iotkit_reset(DeviceState *dev)
-{
-    IoTKit *s = IOTKIT(dev);
-
-    s->nsccfg = 0;
-}
-
-static void iotkit_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(klass);
-
-    dc->realize = iotkit_realize;
-    dc->vmsd = &iotkit_vmstate;
-    dc->props = iotkit_properties;
-    dc->reset = iotkit_reset;
-    iic->check = iotkit_idau_check;
-}
-
-static const TypeInfo iotkit_info = {
-    .name = TYPE_IOTKIT,
-    .parent = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(IoTKit),
-    .instance_init = iotkit_init,
-    .class_init = iotkit_class_init,
-    .interfaces = (InterfaceInfo[]) {
-        { TYPE_IDAU_INTERFACE },
-        { }
-    }
-};
-
-static void iotkit_register_types(void)
-{
-    type_register_static(&iotkit_info);
-}
-
-type_init(iotkit_register_types);
diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c
index 82b1d020a5..f5f0b0e0fa 100644
--- a/hw/arm/mps2-tz.c
+++ b/hw/arm/mps2-tz.c
@@ -15,6 +15,7 @@
  * as seen by the guest depend significantly on the FPGA image.
  * This source file covers the following FPGA images, for TrustZone cores:
  *  "mps2-an505" -- Cortex-M33 as documented in ARM Application Note AN505
+ *  "mps2-an521" -- Dual Cortex-M33 as documented in Application Note AN521
  *
  * Links to the TRM for the board itself and to the various Application
  * Notes which document the FPGA images can be found here:
@@ -24,10 +25,16 @@
  * http://infocenter.arm.com/help/topic/com.arm.doc.100112_0200_06_en/versatile_express_cortex_m_prototyping_systems_v2m_mps2_and_v2m_mps2plus_technical_reference_100112_0200_06_en.pdf
  * Application Note AN505:
  * http://infocenter.arm.com/help/topic/com.arm.doc.dai0505b/index.html
+ * Application Note AN521:
+ * http://infocenter.arm.com/help/topic/com.arm.doc.dai0521c/index.html
  *
  * The AN505 defers to the Cortex-M33 processor ARMv8M IoT Kit FVP User Guide
  * (ARM ECM0601256) for the details of some of the device layout:
  *   http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html
+ * Similarly, the AN521 uses the SSE-200, and the SSE-200 TRM defines
+ * most of the device layout:
+ *  http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf
+ *
  */
 
 #include "qemu/osdep.h"
@@ -46,27 +53,31 @@
 #include "hw/misc/mps2-fpgaio.h"
 #include "hw/misc/tz-mpc.h"
 #include "hw/misc/tz-msc.h"
-#include "hw/arm/iotkit.h"
+#include "hw/arm/armsse.h"
 #include "hw/dma/pl080.h"
 #include "hw/ssi/pl022.h"
 #include "hw/devices.h"
 #include "net/net.h"
 #include "hw/core/split-irq.h"
 
+#define MPS2TZ_NUMIRQ 92
+
 typedef enum MPS2TZFPGAType {
     FPGA_AN505,
+    FPGA_AN521,
 } MPS2TZFPGAType;
 
 typedef struct {
     MachineClass parent;
     MPS2TZFPGAType fpga_type;
     uint32_t scc_id;
+    const char *armsse_type;
 } MPS2TZMachineClass;
 
 typedef struct {
     MachineState parent;
 
-    IoTKit iotkit;
+    ARMSSE iotkit;
     MemoryRegion psram;
     MemoryRegion ssram[3];
     MemoryRegion ssram1_m;
@@ -85,10 +96,12 @@ typedef struct {
     SplitIRQ sec_resp_splitter;
     qemu_or_irq uart_irq_orgate;
     DeviceState *lan9118;
+    SplitIRQ cpu_irq_splitter[MPS2TZ_NUMIRQ];
 } MPS2TZMachineState;
 
 #define TYPE_MPS2TZ_MACHINE "mps2tz"
 #define TYPE_MPS2TZ_AN505_MACHINE MACHINE_TYPE_NAME("mps2-an505")
+#define TYPE_MPS2TZ_AN521_MACHINE MACHINE_TYPE_NAME("mps2-an521")
 
 #define MPS2TZ_MACHINE(obj) \
     OBJECT_CHECK(MPS2TZMachineState, obj, TYPE_MPS2TZ_MACHINE)
@@ -111,6 +124,23 @@ static void make_ram_alias(MemoryRegion *mr, const char *name,
     memory_region_add_subregion(get_system_memory(), base, mr);
 }
 
+static qemu_irq get_sse_irq_in(MPS2TZMachineState *mms, int irqno)
+{
+    /* Return a qemu_irq which will signal IRQ n to all CPUs in the SSE. */
+    MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms);
+
+    assert(irqno < MPS2TZ_NUMIRQ);
+
+    switch (mmc->fpga_type) {
+    case FPGA_AN505:
+        return qdev_get_gpio_in_named(DEVICE(&mms->iotkit), "EXP_IRQ", irqno);
+    case FPGA_AN521:
+        return qdev_get_gpio_in(DEVICE(&mms->cpu_irq_splitter[irqno]), 0);
+    default:
+        g_assert_not_reached();
+    }
+}
+
 /* Most of the devices in the AN505 FPGA image sit behind
  * Peripheral Protection Controllers. These data structures
  * define the layout of which devices sit behind which PPCs.
@@ -161,7 +191,6 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque,
     int txirqno = i * 2 + 1;
     int combirqno = i + 10;
     SysBusDevice *s;
-    DeviceState *iotkitdev = DEVICE(&mms->iotkit);
     DeviceState *orgate_dev = DEVICE(&mms->uart_irq_orgate);
 
     sysbus_init_child_obj(OBJECT(mms), name, uart, sizeof(mms->uart[0]),
@@ -170,14 +199,11 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque,
     qdev_prop_set_uint32(DEVICE(uart), "pclk-frq", SYSCLK_FRQ);
     object_property_set_bool(OBJECT(uart), true, "realized", &error_fatal);
     s = SYS_BUS_DEVICE(uart);
-    sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev,
-                                                    "EXP_IRQ", txirqno));
-    sysbus_connect_irq(s, 1, qdev_get_gpio_in_named(iotkitdev,
-                                                    "EXP_IRQ", rxirqno));
+    sysbus_connect_irq(s, 0, get_sse_irq_in(mms, txirqno));
+    sysbus_connect_irq(s, 1, get_sse_irq_in(mms, rxirqno));
     sysbus_connect_irq(s, 2, qdev_get_gpio_in(orgate_dev, i * 2));
     sysbus_connect_irq(s, 3, qdev_get_gpio_in(orgate_dev, i * 2 + 1));
-    sysbus_connect_irq(s, 4, qdev_get_gpio_in_named(iotkitdev,
-                                                    "EXP_IRQ", combirqno));
+    sysbus_connect_irq(s, 4, get_sse_irq_in(mms, combirqno));
     return sysbus_mmio_get_region(SYS_BUS_DEVICE(uart), 0);
 }
 
@@ -213,7 +239,6 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque,
                                   const char *name, hwaddr size)
 {
     SysBusDevice *s;
-    DeviceState *iotkitdev = DEVICE(&mms->iotkit);
     NICInfo *nd = &nd_table[0];
 
     /* In hardware this is a LAN9220; the LAN9118 is software compatible
@@ -225,7 +250,7 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque,
     qdev_init_nofail(mms->lan9118);
 
     s = SYS_BUS_DEVICE(mms->lan9118);
-    sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 16));
+    sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 16));
     return sysbus_mmio_get_region(s, 0);
 }
 
@@ -315,12 +340,9 @@ static MemoryRegion *make_dma(MPS2TZMachineState *mms, void *opaque,
 
     s = SYS_BUS_DEVICE(dma);
     /* Wire up DMACINTR, DMACINTERR, DMACINTTC */
-    sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev,
-                                                    "EXP_IRQ", 58 + i * 3));
-    sysbus_connect_irq(s, 1, qdev_get_gpio_in_named(iotkitdev,
-                                                    "EXP_IRQ", 56 + i * 3));
-    sysbus_connect_irq(s, 2, qdev_get_gpio_in_named(iotkitdev,
-                                                    "EXP_IRQ", 57 + i * 3));
+    sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 58 + i * 3));
+    sysbus_connect_irq(s, 1, get_sse_irq_in(mms, 56 + i * 3));
+    sysbus_connect_irq(s, 2, get_sse_irq_in(mms, 57 + i * 3));
 
     g_free(mscname);
     return sysbus_mmio_get_region(s, 0);
@@ -339,21 +361,20 @@ static MemoryRegion *make_spi(MPS2TZMachineState *mms, void *opaque,
      */
     PL022State *spi = opaque;
     int i = spi - &mms->spi[0];
-    DeviceState *iotkitdev = DEVICE(&mms->iotkit);
     SysBusDevice *s;
 
     sysbus_init_child_obj(OBJECT(mms), name, spi, sizeof(mms->spi[0]),
                           TYPE_PL022);
     object_property_set_bool(OBJECT(spi), true, "realized", &error_fatal);
     s = SYS_BUS_DEVICE(spi);
-    sysbus_connect_irq(s, 0,
-                       qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 51 + i));
+    sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 51 + i));
     return sysbus_mmio_get_region(s, 0);
 }
 
 static void mps2tz_common_init(MachineState *machine)
 {
     MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine);
+    MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms);
     MachineClass *mc = MACHINE_GET_CLASS(machine);
     MemoryRegion *system_memory = get_system_memory();
     DeviceState *iotkitdev;
@@ -367,15 +388,42 @@ static void mps2tz_common_init(MachineState *machine)
     }
 
     sysbus_init_child_obj(OBJECT(machine), "iotkit", &mms->iotkit,
-                          sizeof(mms->iotkit), TYPE_IOTKIT);
+                          sizeof(mms->iotkit), mmc->armsse_type);
     iotkitdev = DEVICE(&mms->iotkit);
     object_property_set_link(OBJECT(&mms->iotkit), OBJECT(system_memory),
                              "memory", &error_abort);
-    qdev_prop_set_uint32(iotkitdev, "EXP_NUMIRQ", 92);
+    qdev_prop_set_uint32(iotkitdev, "EXP_NUMIRQ", MPS2TZ_NUMIRQ);
     qdev_prop_set_uint32(iotkitdev, "MAINCLK", SYSCLK_FRQ);
     object_property_set_bool(OBJECT(&mms->iotkit), true, "realized",
                              &error_fatal);
 
+    /*
+     * The AN521 needs us to create splitters to feed the IRQ inputs
+     * for each CPU in the SSE-200 from each device in the board.
+     */
+    if (mmc->fpga_type == FPGA_AN521) {
+        for (i = 0; i < MPS2TZ_NUMIRQ; i++) {
+            char *name = g_strdup_printf("mps2-irq-splitter%d", i);
+            SplitIRQ *splitter = &mms->cpu_irq_splitter[i];
+
+            object_initialize_child(OBJECT(machine), name,
+                                    splitter, sizeof(*splitter),
+                                    TYPE_SPLIT_IRQ, &error_fatal, NULL);
+            g_free(name);
+
+            object_property_set_int(OBJECT(splitter), 2, "num-lines",
+                                    &error_fatal);
+            object_property_set_bool(OBJECT(splitter), true, "realized",
+                                     &error_fatal);
+            qdev_connect_gpio_out(DEVICE(splitter), 0,
+                                  qdev_get_gpio_in_named(DEVICE(&mms->iotkit),
+                                                         "EXP_IRQ", i));
+            qdev_connect_gpio_out(DEVICE(splitter), 1,
+                                  qdev_get_gpio_in_named(DEVICE(&mms->iotkit),
+                                                         "EXP_CPU1_IRQ", i));
+        }
+    }
+
     /* The sec_resp_cfg output from the IoTKit must be split into multiple
      * lines, one for each of the PPCs we create here, plus one per MSC.
      */
@@ -426,7 +474,7 @@ static void mps2tz_common_init(MachineState *machine)
     object_property_set_bool(OBJECT(&mms->uart_irq_orgate), true,
                              "realized", &error_fatal);
     qdev_connect_gpio_out(DEVICE(&mms->uart_irq_orgate), 0,
-                          qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 15));
+                          get_sse_irq_in(mms, 15));
 
     /* Most of the devices in the FPGA are behind Peripheral Protection
      * Controllers. The required order for initializing things is:
@@ -593,7 +641,6 @@ static void mps2tz_class_init(ObjectClass *oc, void *data)
     IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(oc);
 
     mc->init = mps2tz_common_init;
-    mc->max_cpus = 1;
     iic->check = mps2_tz_idau_check;
 }
 
@@ -603,9 +650,28 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data)
     MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc);
 
     mc->desc = "ARM MPS2 with AN505 FPGA image for Cortex-M33";
+    mc->default_cpus = 1;
+    mc->min_cpus = mc->default_cpus;
+    mc->max_cpus = mc->default_cpus;
     mmc->fpga_type = FPGA_AN505;
     mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33");
     mmc->scc_id = 0x41045050;
+    mmc->armsse_type = TYPE_IOTKIT;
+}
+
+static void mps2tz_an521_class_init(ObjectClass *oc, void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+    MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc);
+
+    mc->desc = "ARM MPS2 with AN521 FPGA image for dual Cortex-M33";
+    mc->default_cpus = 2;
+    mc->min_cpus = mc->default_cpus;
+    mc->max_cpus = mc->default_cpus;
+    mmc->fpga_type = FPGA_AN521;
+    mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33");
+    mmc->scc_id = 0x41045210;
+    mmc->armsse_type = TYPE_SSE200;
 }
 
 static const TypeInfo mps2tz_info = {
@@ -627,10 +693,17 @@ static const TypeInfo mps2tz_an505_info = {
     .class_init = mps2tz_an505_class_init,
 };
 
+static const TypeInfo mps2tz_an521_info = {
+    .name = TYPE_MPS2TZ_AN521_MACHINE,
+    .parent = TYPE_MPS2TZ_MACHINE,
+    .class_init = mps2tz_an521_class_init,
+};
+
 static void mps2tz_machine_init(void)
 {
     type_register_static(&mps2tz_info);
     type_register_static(&mps2tz_an505_info);
+    type_register_static(&mps2tz_an521_info);
 }
 
 type_init(mps2tz_machine_init);
diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c
index 1630c27594..bbaf050103 100644
--- a/hw/arm/nrf51_soc.c
+++ b/hw/arm/nrf51_soc.c
@@ -29,8 +29,10 @@
  * are supported in the future, add a sub-class of NRF51SoC for
  * the specific variants
  */
-#define NRF51822_FLASH_SIZE     (256 * NRF51_PAGE_SIZE)
-#define NRF51822_SRAM_SIZE      (16 * NRF51_PAGE_SIZE)
+#define NRF51822_FLASH_PAGES    256
+#define NRF51822_SRAM_PAGES     16
+#define NRF51822_FLASH_SIZE     (NRF51822_FLASH_PAGES * NRF51_PAGE_SIZE)
+#define NRF51822_SRAM_SIZE      (NRF51822_SRAM_PAGES * NRF51_PAGE_SIZE)
 
 #define BASE_TO_IRQ(base) ((base >> 12) & 0x1F)
 
@@ -81,15 +83,8 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp)
 
     memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1);
 
-    memory_region_init_rom(&s->flash, OBJECT(s), "nrf51.flash", s->flash_size,
-            &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-    memory_region_add_subregion(&s->container, NRF51_FLASH_BASE, &s->flash);
-
-    memory_region_init_ram(&s->sram, NULL, "nrf51.sram", s->sram_size, &err);
+    memory_region_init_ram(&s->sram, OBJECT(s), "nrf51.sram", s->sram_size,
+                           &err);
     if (err) {
         error_propagate(errp, err);
         return;
@@ -121,6 +116,29 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp)
                        qdev_get_gpio_in(DEVICE(&s->cpu),
                        BASE_TO_IRQ(NRF51_RNG_BASE)));
 
+    /* UICR, FICR, NVMC, FLASH */
+    object_property_set_uint(OBJECT(&s->nvm), s->flash_size, "flash-size",
+                             &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+
+    object_property_set_bool(OBJECT(&s->nvm), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+
+    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 0);
+    memory_region_add_subregion_overlap(&s->container, NRF51_NVMC_BASE, mr, 0);
+    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 1);
+    memory_region_add_subregion_overlap(&s->container, NRF51_FICR_BASE, mr, 0);
+    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 2);
+    memory_region_add_subregion_overlap(&s->container, NRF51_UICR_BASE, mr, 0);
+    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 3);
+    memory_region_add_subregion_overlap(&s->container, NRF51_FLASH_BASE, mr, 0);
+
     /* GPIO */
     object_property_set_bool(OBJECT(&s->gpio), true, "realized", &err);
     if (err) {
@@ -158,8 +176,6 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp)
 
     create_unimplemented_device("nrf51_soc.io", NRF51_IOMEM_BASE,
                                 NRF51_IOMEM_SIZE);
-    create_unimplemented_device("nrf51_soc.ficr", NRF51_FICR_BASE,
-                                NRF51_FICR_SIZE);
     create_unimplemented_device("nrf51_soc.private",
                                 NRF51_PRIVATE_BASE, NRF51_PRIVATE_SIZE);
 }
@@ -186,6 +202,8 @@ static void nrf51_soc_init(Object *obj)
     sysbus_init_child_obj(obj, "rng", &s->rng, sizeof(s->rng),
                            TYPE_NRF51_RNG);
 
+    sysbus_init_child_obj(obj, "nvm", &s->nvm, sizeof(s->nvm), TYPE_NRF51_NVM);
+
     sysbus_init_child_obj(obj, "gpio", &s->gpio, sizeof(s->gpio),
                           TYPE_NRF51_GPIO);
 
diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index 0beefb05d4..790a3d9584 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -2274,8 +2274,7 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
     Error *err = NULL;
     int regionlen;
 
-    s->cpu = ARM_CPU(qemu_get_cpu(0));
-
+    /* The armv7m container object will have set our CPU pointer */
     if (!s->cpu || !arm_feature(&s->cpu->env, ARM_FEATURE_M)) {
         error_setg(errp, "The NVIC can only be used with a Cortex-M CPU");
         return;
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 04f3bfa516..74c91d250c 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -69,6 +69,7 @@ obj-$(CONFIG_TZ_PPC) += tz-ppc.o
 obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o
 obj-$(CONFIG_IOTKIT_SYSCTL) += iotkit-sysctl.o
 obj-$(CONFIG_IOTKIT_SYSINFO) += iotkit-sysinfo.o
+obj-$(CONFIG_ARMSSE_CPUID) += armsse-cpuid.o
 
 obj-$(CONFIG_PVPANIC) += pvpanic.o
 obj-$(CONFIG_AUX) += auxbus.o
diff --git a/hw/misc/armsse-cpuid.c b/hw/misc/armsse-cpuid.c
new file mode 100644
index 0000000000..7788f6ced6
--- /dev/null
+++ b/hw/misc/armsse-cpuid.c
@@ -0,0 +1,134 @@
+/*
+ * ARM SSE-200 CPU_IDENTITY register block
+ *
+ * Copyright (c) 2019 Linaro Limited
+ * Written by Peter Maydell
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 or
+ *  (at your option) any later version.
+ */
+
+/*
+ * This is a model of the "CPU_IDENTITY" register block which is part of the
+ * Arm SSE-200 and documented in
+ * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf
+ *
+ * It consists of one read-only CPUID register (set by QOM property), plus the
+ * usual ID registers.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "qapi/error.h"
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/misc/armsse-cpuid.h"
+
+REG32(CPUID, 0x0)
+REG32(PID4, 0xfd0)
+REG32(PID5, 0xfd4)
+REG32(PID6, 0xfd8)
+REG32(PID7, 0xfdc)
+REG32(PID0, 0xfe0)
+REG32(PID1, 0xfe4)
+REG32(PID2, 0xfe8)
+REG32(PID3, 0xfec)
+REG32(CID0, 0xff0)
+REG32(CID1, 0xff4)
+REG32(CID2, 0xff8)
+REG32(CID3, 0xffc)
+
+/* PID/CID values */
+static const int sysinfo_id[] = {
+    0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
+    0x58, 0xb8, 0x0b, 0x00, /* PID0..PID3 */
+    0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
+};
+
+static uint64_t armsse_cpuid_read(void *opaque, hwaddr offset,
+                                    unsigned size)
+{
+    ARMSSECPUID *s = ARMSSE_CPUID(opaque);
+    uint64_t r;
+
+    switch (offset) {
+    case A_CPUID:
+        r = s->cpuid;
+        break;
+    case A_PID4 ... A_CID3:
+        r = sysinfo_id[(offset - A_PID4) / 4];
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "SSE CPU_IDENTITY read: bad offset 0x%x\n", (int)offset);
+        r = 0;
+        break;
+    }
+    trace_armsse_cpuid_read(offset, r, size);
+    return r;
+}
+
+static void armsse_cpuid_write(void *opaque, hwaddr offset,
+                                 uint64_t value, unsigned size)
+{
+    trace_armsse_cpuid_write(offset, value, size);
+
+    qemu_log_mask(LOG_GUEST_ERROR,
+                  "SSE CPU_IDENTITY: write to RO offset 0x%x\n", (int)offset);
+}
+
+static const MemoryRegionOps armsse_cpuid_ops = {
+    .read = armsse_cpuid_read,
+    .write = armsse_cpuid_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    /* byte/halfword accesses are just zero-padded on reads and writes */
+    .impl.min_access_size = 4,
+    .impl.max_access_size = 4,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+};
+
+static Property armsse_cpuid_props[] = {
+    DEFINE_PROP_UINT32("CPUID", ARMSSECPUID, cpuid, 0),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void armsse_cpuid_init(Object *obj)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    ARMSSECPUID *s = ARMSSE_CPUID(obj);
+
+    memory_region_init_io(&s->iomem, obj, &armsse_cpuid_ops,
+                          s, "armsse-cpuid", 0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void armsse_cpuid_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    /*
+     * This device has no guest-modifiable state and so it
+     * does not need a reset function or VMState.
+     */
+
+    dc->props = armsse_cpuid_props;
+}
+
+static const TypeInfo armsse_cpuid_info = {
+    .name = TYPE_ARMSSE_CPUID,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(ARMSSECPUID),
+    .instance_init = armsse_cpuid_init,
+    .class_init = armsse_cpuid_class_init,
+};
+
+static void armsse_cpuid_register_types(void)
+{
+    type_register_static(&armsse_cpuid_info);
+}
+
+type_init(armsse_cpuid_register_types);
diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c
index 2222b3e147..537601cd53 100644
--- a/hw/misc/iotkit-secctl.c
+++ b/hw/misc/iotkit-secctl.c
@@ -600,7 +600,7 @@ static void iotkit_secctl_mpc_status(void *opaque, int n, int level)
 {
     IoTKitSecCtl *s = IOTKIT_SECCTL(opaque);
 
-    s->mpcintstatus = deposit32(s->mpcintstatus, 0, 1, !!level);
+    s->mpcintstatus = deposit32(s->mpcintstatus, n, 1, !!level);
 }
 
 static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level)
@@ -686,7 +686,8 @@ static void iotkit_secctl_init(Object *obj)
     qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1);
     qdev_init_gpio_out_named(dev, &s->nsc_cfg_irq, "nsc_cfg", 1);
 
-    qdev_init_gpio_in_named(dev, iotkit_secctl_mpc_status, "mpc_status", 1);
+    qdev_init_gpio_in_named(dev, iotkit_secctl_mpc_status, "mpc_status",
+                            IOTS_NUM_MPC);
     qdev_init_gpio_in_named(dev, iotkit_secctl_mpcexp_status,
                             "mpcexp_status", IOTS_NUM_EXP_MPC);
 
diff --git a/hw/misc/iotkit-sysinfo.c b/hw/misc/iotkit-sysinfo.c
index 78955bc45f..026ba94261 100644
--- a/hw/misc/iotkit-sysinfo.c
+++ b/hw/misc/iotkit-sysinfo.c
@@ -51,15 +51,16 @@ static const int sysinfo_id[] = {
 static uint64_t iotkit_sysinfo_read(void *opaque, hwaddr offset,
                                     unsigned size)
 {
+    IoTKitSysInfo *s = IOTKIT_SYSINFO(opaque);
     uint64_t r;
 
     switch (offset) {
     case A_SYS_VERSION:
-        r = 0x41743;
+        r = s->sys_version;
         break;
 
     case A_SYS_CONFIG:
-        r = 0x31;
+        r = s->sys_config;
         break;
     case A_PID4 ... A_CID3:
         r = sysinfo_id[(offset - A_PID4) / 4];
@@ -94,6 +95,12 @@ static const MemoryRegionOps iotkit_sysinfo_ops = {
     .valid.max_access_size = 4,
 };
 
+static Property iotkit_sysinfo_props[] = {
+    DEFINE_PROP_UINT32("SYS_VERSION", IoTKitSysInfo, sys_version, 0),
+    DEFINE_PROP_UINT32("SYS_CONFIG", IoTKitSysInfo, sys_config, 0),
+    DEFINE_PROP_END_OF_LIST()
+};
+
 static void iotkit_sysinfo_init(Object *obj)
 {
     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
@@ -106,10 +113,14 @@ static void iotkit_sysinfo_init(Object *obj)
 
 static void iotkit_sysinfo_class_init(ObjectClass *klass, void *data)
 {
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
     /*
      * This device has no guest-modifiable state and so it
      * does not need a reset function or VMState.
      */
+
+    dc->props = iotkit_sysinfo_props;
 }
 
 static const TypeInfo iotkit_sysinfo_info = {
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 52466c77c4..b0701bddd3 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -132,3 +132,7 @@ iotkit_sysinfo_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysI
 iotkit_sysctl_read(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
 iotkit_sysctl_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
 iotkit_sysctl_reset(void) "IoTKit SysCtl: reset"
+
+# hw/misc/armsse-cpuid.c
+armsse_cpuid_read(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+armsse_cpuid_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs
index b318e53a43..26f7b4ca35 100644
--- a/hw/nvram/Makefile.objs
+++ b/hw/nvram/Makefile.objs
@@ -5,3 +5,4 @@ common-obj-y += fw_cfg.o
 common-obj-y += chrp_nvram.o
 common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
 obj-$(CONFIG_PSERIES) += spapr_nvram.o
+obj-$(CONFIG_NRF51_SOC) += nrf51_nvm.o
diff --git a/hw/nvram/nrf51_nvm.c b/hw/nvram/nrf51_nvm.c
new file mode 100644
index 0000000000..7d94cef1db
--- /dev/null
+++ b/hw/nvram/nrf51_nvm.c
@@ -0,0 +1,388 @@
+/*
+ * Nordic Semiconductor nRF51 non-volatile memory
+ *
+ * It provides an interface to erase regions in flash memory.
+ * Furthermore it provides the user and factory information registers.
+ *
+ * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf
+ *
+ * See nRF51 reference manual and product sheet sections:
+ * + Non-Volatile Memory Controller (NVMC)
+ * + Factory Information Configuration Registers (FICR)
+ * + User Information Configuration Registers (UICR)
+ *
+ * Copyright 2018 Steffen Görtz <contrib@steffen-goertz.de>
+ *
+ * This code is licensed under the GPL version 2 or later.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "exec/address-spaces.h"
+#include "hw/arm/nrf51.h"
+#include "hw/nvram/nrf51_nvm.h"
+
+/*
+ * FICR Registers Assignments
+ * CODEPAGESIZE      0x010
+ * CODESIZE          0x014
+ * CLENR0            0x028
+ * PPFC              0x02C
+ * NUMRAMBLOCK       0x034
+ * SIZERAMBLOCKS     0x038
+ * SIZERAMBLOCK[0]   0x038
+ * SIZERAMBLOCK[1]   0x03C
+ * SIZERAMBLOCK[2]   0x040
+ * SIZERAMBLOCK[3]   0x044
+ * CONFIGID          0x05C
+ * DEVICEID[0]       0x060
+ * DEVICEID[1]       0x064
+ * ER[0]             0x080
+ * ER[1]             0x084
+ * ER[2]             0x088
+ * ER[3]             0x08C
+ * IR[0]             0x090
+ * IR[1]             0x094
+ * IR[2]             0x098
+ * IR[3]             0x09C
+ * DEVICEADDRTYPE    0x0A0
+ * DEVICEADDR[0]     0x0A4
+ * DEVICEADDR[1]     0x0A8
+ * OVERRIDEEN        0x0AC
+ * NRF_1MBIT[0]      0x0B0
+ * NRF_1MBIT[1]      0x0B4
+ * NRF_1MBIT[2]      0x0B8
+ * NRF_1MBIT[3]      0x0BC
+ * NRF_1MBIT[4]      0x0C0
+ * BLE_1MBIT[0]      0x0EC
+ * BLE_1MBIT[1]      0x0F0
+ * BLE_1MBIT[2]      0x0F4
+ * BLE_1MBIT[3]      0x0F8
+ * BLE_1MBIT[4]      0x0FC
+ */
+static const uint32_t ficr_content[64] = {
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000400,
+    0x00000100, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000002, 0x00002000,
+    0x00002000, 0x00002000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000003,
+    0x12345678, 0x9ABCDEF1, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static uint64_t ficr_read(void *opaque, hwaddr offset, unsigned int size)
+{
+    assert(offset < sizeof(ficr_content));
+    return ficr_content[offset / 4];
+}
+
+static void ficr_write(void *opaque, hwaddr offset, uint64_t value,
+        unsigned int size)
+{
+    /* Intentionally do nothing */
+}
+
+static const MemoryRegionOps ficr_ops = {
+    .read = ficr_read,
+    .write = ficr_write,
+    .impl.min_access_size = 4,
+    .impl.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN
+};
+
+/*
+ * UICR Registers Assignments
+ * CLENR0           0x000
+ * RBPCONF          0x004
+ * XTALFREQ         0x008
+ * FWID             0x010
+ * BOOTLOADERADDR   0x014
+ * NRFFW[0]         0x014
+ * NRFFW[1]         0x018
+ * NRFFW[2]         0x01C
+ * NRFFW[3]         0x020
+ * NRFFW[4]         0x024
+ * NRFFW[5]         0x028
+ * NRFFW[6]         0x02C
+ * NRFFW[7]         0x030
+ * NRFFW[8]         0x034
+ * NRFFW[9]         0x038
+ * NRFFW[10]        0x03C
+ * NRFFW[11]        0x040
+ * NRFFW[12]        0x044
+ * NRFFW[13]        0x048
+ * NRFFW[14]        0x04C
+ * NRFHW[0]         0x050
+ * NRFHW[1]         0x054
+ * NRFHW[2]         0x058
+ * NRFHW[3]         0x05C
+ * NRFHW[4]         0x060
+ * NRFHW[5]         0x064
+ * NRFHW[6]         0x068
+ * NRFHW[7]         0x06C
+ * NRFHW[8]         0x070
+ * NRFHW[9]         0x074
+ * NRFHW[10]        0x078
+ * NRFHW[11]        0x07C
+ * CUSTOMER[0]      0x080
+ * CUSTOMER[1]      0x084
+ * CUSTOMER[2]      0x088
+ * CUSTOMER[3]      0x08C
+ * CUSTOMER[4]      0x090
+ * CUSTOMER[5]      0x094
+ * CUSTOMER[6]      0x098
+ * CUSTOMER[7]      0x09C
+ * CUSTOMER[8]      0x0A0
+ * CUSTOMER[9]      0x0A4
+ * CUSTOMER[10]     0x0A8
+ * CUSTOMER[11]     0x0AC
+ * CUSTOMER[12]     0x0B0
+ * CUSTOMER[13]     0x0B4
+ * CUSTOMER[14]     0x0B8
+ * CUSTOMER[15]     0x0BC
+ * CUSTOMER[16]     0x0C0
+ * CUSTOMER[17]     0x0C4
+ * CUSTOMER[18]     0x0C8
+ * CUSTOMER[19]     0x0CC
+ * CUSTOMER[20]     0x0D0
+ * CUSTOMER[21]     0x0D4
+ * CUSTOMER[22]     0x0D8
+ * CUSTOMER[23]     0x0DC
+ * CUSTOMER[24]     0x0E0
+ * CUSTOMER[25]     0x0E4
+ * CUSTOMER[26]     0x0E8
+ * CUSTOMER[27]     0x0EC
+ * CUSTOMER[28]     0x0F0
+ * CUSTOMER[29]     0x0F4
+ * CUSTOMER[30]     0x0F8
+ * CUSTOMER[31]     0x0FC
+ */
+
+static uint64_t uicr_read(void *opaque, hwaddr offset, unsigned int size)
+{
+    NRF51NVMState *s = NRF51_NVM(opaque);
+
+    assert(offset < sizeof(s->uicr_content));
+    return s->uicr_content[offset / 4];
+}
+
+static void uicr_write(void *opaque, hwaddr offset, uint64_t value,
+        unsigned int size)
+{
+    NRF51NVMState *s = NRF51_NVM(opaque);
+
+    assert(offset < sizeof(s->uicr_content));
+    s->uicr_content[offset / 4] = value;
+}
+
+static const MemoryRegionOps uicr_ops = {
+    .read = uicr_read,
+    .write = uicr_write,
+    .impl.min_access_size = 4,
+    .impl.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN
+};
+
+
+static uint64_t io_read(void *opaque, hwaddr offset, unsigned int size)
+{
+    NRF51NVMState *s = NRF51_NVM(opaque);
+    uint64_t r = 0;
+
+    switch (offset) {
+    case NRF51_NVMC_READY:
+        r = NRF51_NVMC_READY_READY;
+        break;
+    case NRF51_NVMC_CONFIG:
+        r = s->config;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                "%s: bad read offset 0x%" HWADDR_PRIx "\n", __func__, offset);
+        break;
+    }
+
+    return r;
+}
+
+static void io_write(void *opaque, hwaddr offset, uint64_t value,
+        unsigned int size)
+{
+    NRF51NVMState *s = NRF51_NVM(opaque);
+
+    switch (offset) {
+    case NRF51_NVMC_CONFIG:
+        s->config = value & NRF51_NVMC_CONFIG_MASK;
+        break;
+    case NRF51_NVMC_ERASEPCR0:
+    case NRF51_NVMC_ERASEPCR1:
+        if (s->config & NRF51_NVMC_CONFIG_EEN) {
+            /* Mask in-page sub address */
+            value &= ~(NRF51_PAGE_SIZE - 1);
+            if (value <= (s->flash_size - NRF51_PAGE_SIZE)) {
+                memset(s->storage + value, 0xFF, NRF51_PAGE_SIZE);
+                memory_region_flush_rom_device(&s->flash, value,
+                                               NRF51_PAGE_SIZE);
+            }
+        } else {
+            qemu_log_mask(LOG_GUEST_ERROR,
+            "%s: Flash erase at 0x%" HWADDR_PRIx" while flash not erasable.\n",
+            __func__, offset);
+        }
+        break;
+    case NRF51_NVMC_ERASEALL:
+        if (value == NRF51_NVMC_ERASE) {
+            if (s->config & NRF51_NVMC_CONFIG_EEN) {
+                memset(s->storage, 0xFF, s->flash_size);
+                memory_region_flush_rom_device(&s->flash, 0, s->flash_size);
+                memset(s->uicr_content, 0xFF, sizeof(s->uicr_content));
+            } else {
+                qemu_log_mask(LOG_GUEST_ERROR, "%s: Flash not erasable.\n",
+                              __func__);
+            }
+        }
+        break;
+    case NRF51_NVMC_ERASEUICR:
+        if (value == NRF51_NVMC_ERASE) {
+            memset(s->uicr_content, 0xFF, sizeof(s->uicr_content));
+        }
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                "%s: bad write offset 0x%" HWADDR_PRIx "\n", __func__, offset);
+    }
+}
+
+static const MemoryRegionOps io_ops = {
+        .read = io_read,
+        .write = io_write,
+        .impl.min_access_size = 4,
+        .impl.max_access_size = 4,
+        .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+
+static void flash_write(void *opaque, hwaddr offset, uint64_t value,
+        unsigned int size)
+{
+    NRF51NVMState *s = NRF51_NVM(opaque);
+
+    if (s->config & NRF51_NVMC_CONFIG_WEN) {
+        uint32_t oldval;
+
+        assert(offset + size <= s->flash_size);
+
+        /* NOR Flash only allows bits to be flipped from 1's to 0's on write */
+        oldval = ldl_le_p(s->storage + offset);
+        oldval &= value;
+        stl_le_p(s->storage + offset, oldval);
+
+        memory_region_flush_rom_device(&s->flash, offset, size);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                "%s: Flash write 0x%" HWADDR_PRIx" while flash not writable.\n",
+                __func__, offset);
+    }
+}
+
+
+
+static const MemoryRegionOps flash_ops = {
+    .write = flash_write,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void nrf51_nvm_init(Object *obj)
+{
+    NRF51NVMState *s = NRF51_NVM(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+    memory_region_init_io(&s->mmio, obj, &io_ops, s, "nrf51_soc.nvmc",
+                          NRF51_NVMC_SIZE);
+    sysbus_init_mmio(sbd, &s->mmio);
+
+    memory_region_init_io(&s->ficr, obj, &ficr_ops, s, "nrf51_soc.ficr",
+                          sizeof(ficr_content));
+    sysbus_init_mmio(sbd, &s->ficr);
+
+    memory_region_init_io(&s->uicr, obj, &uicr_ops, s, "nrf51_soc.uicr",
+                          sizeof(s->uicr_content));
+    sysbus_init_mmio(sbd, &s->uicr);
+}
+
+static void nrf51_nvm_realize(DeviceState *dev, Error **errp)
+{
+    NRF51NVMState *s = NRF51_NVM(dev);
+    Error *err = NULL;
+
+    memory_region_init_rom_device(&s->flash, OBJECT(dev), &flash_ops, s,
+        "nrf51_soc.flash", s->flash_size, &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+
+    s->storage = memory_region_get_ram_ptr(&s->flash);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->flash);
+}
+
+static void nrf51_nvm_reset(DeviceState *dev)
+{
+    NRF51NVMState *s = NRF51_NVM(dev);
+
+    s->config = 0x00;
+    memset(s->uicr_content, 0xFF, sizeof(s->uicr_content));
+}
+
+static Property nrf51_nvm_properties[] = {
+    DEFINE_PROP_UINT32("flash-size", NRF51NVMState, flash_size, 0x40000),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription vmstate_nvm = {
+    .name = "nrf51_soc.nvm",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(uicr_content, NRF51NVMState,
+                NRF51_UICR_FIXTURE_SIZE),
+        VMSTATE_UINT32(config, NRF51NVMState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void nrf51_nvm_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->props = nrf51_nvm_properties;
+    dc->vmsd = &vmstate_nvm;
+    dc->realize = nrf51_nvm_realize;
+    dc->reset = nrf51_nvm_reset;
+}
+
+static const TypeInfo nrf51_nvm_info = {
+    .name = TYPE_NRF51_NVM,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(NRF51NVMState),
+    .instance_init = nrf51_nvm_init,
+    .class_init = nrf51_nvm_class_init
+};
+
+static void nrf51_nvm_register_types(void)
+{
+    type_register_static(&nrf51_nvm_info);
+}
+
+type_init(nrf51_nvm_register_types)
diff --git a/include/hw/arm/iotkit.h b/include/hw/arm/armsse.h
index 3a8ee63908..f800bafb14 100644
--- a/include/hw/arm/iotkit.h
+++ b/include/hw/arm/armsse.h
@@ -1,5 +1,5 @@
 /*
- * ARM IoT Kit
+ * ARM SSE (Subsystems for Embedded): IoTKit, SSE-200
  *
  * Copyright (c) 2018 Linaro Limited
  * Written by Peter Maydell
@@ -9,9 +9,16 @@
  * (at your option) any later version.
  */
 
-/* This is a model of the Arm IoT Kit which is documented in
+/*
+ * This is a model of the Arm "Subsystems for Embedded" family of
+ * hardware, which include the IoT Kit and the SSE-050, SSE-100 and
+ * SSE-200. Currently we model:
+ *  - the Arm IoT Kit which is documented in
  * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html
- * It contains:
+ *  - the SSE-200 which is documented in
+ * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf
+ *
+ * The IoTKit contains:
  *  a Cortex-M33
  *  the IDAU
  *  some timers and watchdogs
@@ -20,14 +27,29 @@
  *  a security controller
  *  a bus fabric which arranges that some parts of the address
  *  space are secure and non-secure aliases of each other
+ * The SSE-200 additionally contains:
+ *  a second Cortex-M33
+ *  two Message Handling Units (MHUs)
+ *  an optional CryptoCell (which we do not model)
+ *  more SRAM banks with associated MPCs
+ *  multiple Power Policy Units (PPUs)
+ *  a control interface for an icache for each CPU
+ *  per-CPU identity and control register blocks
  *
  * QEMU interface:
  *  + QOM property "memory" is a MemoryRegion containing the devices provided
  *    by the board model.
  *  + QOM property "MAINCLK" is the frequency of the main system clock
- *  + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts
- *  + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts, which
- *    are wired to the NVIC lines 32 .. n+32
+ *  + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts.
+ *    (In hardware, the SSE-200 permits the number of expansion interrupts
+ *    for the two CPUs to be configured separately, but we restrict it to
+ *    being the same for both, to avoid having to have separate Property
+ *    lists for different variants. This restriction can be relaxed later
+ *    if necessary.)
+ *  + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts for CPU 0,
+ *    which are wired to its NVIC lines 32 .. n+32
+ *  + Named GPIO inputs "EXP_CPU1_IRQ" 0..n are the expansion interrupts for
+ *    CPU 1, which are wired to its NVIC lines 32 .. n+32
  *  + sysbus MMIO region 0 is the "AHB Slave Expansion" which allows
  *    bus master devices in the board model to make transactions into
  *    all the devices and memory areas in the IoTKit
@@ -55,8 +77,8 @@
  *  + named GPIO outputs mscexp_ns[0..15]
  */
 
-#ifndef IOTKIT_H
-#define IOTKIT_H
+#ifndef ARMSSE_H
+#define ARMSSE_H
 
 #include "hw/sysbus.h"
 #include "hw/arm/armv7m.h"
@@ -68,11 +90,22 @@
 #include "hw/watchdog/cmsdk-apb-watchdog.h"
 #include "hw/misc/iotkit-sysctl.h"
 #include "hw/misc/iotkit-sysinfo.h"
+#include "hw/misc/armsse-cpuid.h"
+#include "hw/misc/unimp.h"
 #include "hw/or-irq.h"
 #include "hw/core/split-irq.h"
+#include "hw/cpu/cluster.h"
 
+#define TYPE_ARMSSE "arm-sse"
+#define ARMSSE(obj) OBJECT_CHECK(ARMSSE, (obj), TYPE_ARMSSE)
+
+/*
+ * These type names are for specific IoTKit subsystems; other than
+ * instantiating them, code using these devices should always handle
+ * them via the ARMSSE base class, so they have no IOTKIT() etc macros.
+ */
 #define TYPE_IOTKIT "iotkit"
-#define IOTKIT(obj) OBJECT_CHECK(IoTKit, (obj), TYPE_IOTKIT)
+#define TYPE_SSE200 "sse-200"
 
 /* We have an IRQ splitter and an OR gate input for each external PPC
  * and the 2 internal PPCs
@@ -80,16 +113,34 @@
 #define NUM_EXTERNAL_PPCS (IOTS_NUM_AHB_EXP_PPC + IOTS_NUM_APB_EXP_PPC)
 #define NUM_PPCS (NUM_EXTERNAL_PPCS + 2)
 
-typedef struct IoTKit {
+#define MAX_SRAM_BANKS 4
+#if MAX_SRAM_BANKS > IOTS_NUM_MPC
+#error Too many SRAM banks
+#endif
+
+#define SSE_MAX_CPUS 2
+
+/* These define what each PPU in the ppu[] index is for */
+#define CPU0CORE_PPU 0
+#define CPU1CORE_PPU 1
+#define DBG_PPU 2
+#define RAM0_PPU 3
+#define RAM1_PPU 4
+#define RAM2_PPU 5
+#define RAM3_PPU 6
+#define NUM_PPUS 7
+
+typedef struct ARMSSE {
     /*< private >*/
     SysBusDevice parent_obj;
 
     /*< public >*/
-    ARMv7MState armv7m;
+    ARMv7MState armv7m[SSE_MAX_CPUS];
+    CPUClusterState cluster[SSE_MAX_CPUS];
     IoTKitSecCtl secctl;
     TZPPC apb_ppc0;
     TZPPC apb_ppc1;
-    TZMPC mpc;
+    TZMPC mpc[IOTS_NUM_MPC];
     CMSDKAPBTIMER timer0;
     CMSDKAPBTIMER timer1;
     CMSDKAPBTIMER s32ktimer;
@@ -100,6 +151,8 @@ typedef struct IoTKit {
     qemu_or_irq mpc_irq_orgate;
     qemu_or_irq nmi_orgate;
 
+    SplitIRQ cpu_irq_splitter[32];
+
     CMSDKAPBDualTimer dualtimer;
 
     CMSDKAPBWatchdog s32kwatchdog;
@@ -109,13 +162,30 @@ typedef struct IoTKit {
     IoTKitSysCtl sysctl;
     IoTKitSysCtl sysinfo;
 
+    UnimplementedDeviceState mhu[2];
+    UnimplementedDeviceState ppu[NUM_PPUS];
+    UnimplementedDeviceState cachectrl[SSE_MAX_CPUS];
+    UnimplementedDeviceState cpusecctrl[SSE_MAX_CPUS];
+
+    ARMSSECPUID cpuid[SSE_MAX_CPUS];
+
+    /*
+     * 'container' holds all devices seen by all CPUs.
+     * 'cpu_container[i]' is the view that CPU i has: this has the
+     * per-CPU devices of that CPU, plus as the background 'container'
+     * (or an alias of it, since we can only use it directly once).
+     * container_alias[i] is the alias of 'container' used by CPU i+1;
+     * CPU 0 can use 'container' directly.
+     */
     MemoryRegion container;
+    MemoryRegion container_alias[SSE_MAX_CPUS - 1];
+    MemoryRegion cpu_container[SSE_MAX_CPUS];
     MemoryRegion alias1;
     MemoryRegion alias2;
     MemoryRegion alias3;
-    MemoryRegion sram0;
+    MemoryRegion sram[MAX_SRAM_BANKS];
 
-    qemu_irq *exp_irqs;
+    qemu_irq *exp_irqs[SSE_MAX_CPUS];
     qemu_irq ppc0_irq;
     qemu_irq ppc1_irq;
     qemu_irq sec_resp_cfg;
@@ -131,6 +201,19 @@ typedef struct IoTKit {
     MemoryRegion *board_memory;
     uint32_t exp_numirq;
     uint32_t mainclk_frq;
-} IoTKit;
+    uint32_t sram_addr_width;
+} ARMSSE;
+
+typedef struct ARMSSEInfo ARMSSEInfo;
+
+typedef struct ARMSSEClass {
+    DeviceClass parent_class;
+    const ARMSSEInfo *info;
+} ARMSSEClass;
+
+#define ARMSSE_CLASS(klass) \
+    OBJECT_CLASS_CHECK(ARMSSEClass, (klass), TYPE_ARMSSE)
+#define ARMSSE_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(ARMSSEClass, (obj), TYPE_ARMSSE)
 
 #endif
diff --git a/include/hw/arm/armv7m.h b/include/hw/arm/armv7m.h
index 2ba24953b6..e96a98f809 100644
--- a/include/hw/arm/armv7m.h
+++ b/include/hw/arm/armv7m.h
@@ -65,6 +65,7 @@ typedef struct ARMv7MState {
     Object *idau;
     uint32_t init_svtor;
     bool enable_bitband;
+    bool start_powered_off;
 } ARMv7MState;
 
 #endif
diff --git a/include/hw/arm/nrf51_soc.h b/include/hw/arm/nrf51_soc.h
index fbdefc07e4..fd7fcc71a5 100644
--- a/include/hw/arm/nrf51_soc.h
+++ b/include/hw/arm/nrf51_soc.h
@@ -15,6 +15,7 @@
 #include "hw/char/nrf51_uart.h"
 #include "hw/misc/nrf51_rng.h"
 #include "hw/gpio/nrf51_gpio.h"
+#include "hw/nvram/nrf51_nvm.h"
 #include "hw/timer/nrf51_timer.h"
 
 #define TYPE_NRF51_SOC "nrf51-soc"
@@ -32,6 +33,7 @@ typedef struct NRF51State {
 
     NRF51UARTState uart;
     NRF51RNGState rng;
+    NRF51NVMState nvm;
     NRF51GPIOState gpio;
     NRF51TimerState timer[NRF51_NUM_TIMERS];
 
diff --git a/include/hw/misc/armsse-cpuid.h b/include/hw/misc/armsse-cpuid.h
new file mode 100644
index 0000000000..0ef33fcaba
--- /dev/null
+++ b/include/hw/misc/armsse-cpuid.h
@@ -0,0 +1,41 @@
+/*
+ * ARM SSE-200 CPU_IDENTITY register block
+ *
+ * Copyright (c) 2019 Linaro Limited
+ * Written by Peter Maydell
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 or
+ *  (at your option) any later version.
+ */
+
+/*
+ * This is a model of the "CPU_IDENTITY" register block which is part of the
+ * Arm SSE-200 and documented in
+ * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf
+ *
+ * QEMU interface:
+ *  + QOM property "CPUID": the value to use for the CPUID register
+ *  + sysbus MMIO region 0: the system information register bank
+ */
+
+#ifndef HW_MISC_ARMSSE_CPUID_H
+#define HW_MISC_ARMSSE_CPUID_H
+
+#include "hw/sysbus.h"
+
+#define TYPE_ARMSSE_CPUID "armsse-cpuid"
+#define ARMSSE_CPUID(obj) OBJECT_CHECK(ARMSSECPUID, (obj), TYPE_ARMSSE_CPUID)
+
+typedef struct ARMSSECPUID {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion iomem;
+
+    /* Properties */
+    uint32_t cpuid;
+} ARMSSECPUID;
+
+#endif
diff --git a/include/hw/misc/iotkit-secctl.h b/include/hw/misc/iotkit-secctl.h
index 1a193b306f..bcb0437be5 100644
--- a/include/hw/misc/iotkit-secctl.h
+++ b/include/hw/misc/iotkit-secctl.h
@@ -40,8 +40,8 @@
  *  + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable
  *  + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear
  *  + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status
- * Controlling the MPC in the IoTKit:
- *  + named GPIO input mpc_status
+ * Controlling the (up to) 4 MPCs in the IoTKit/SSE:
+ *  + named GPIO inputs mpc_status[0..3]
  * Controlling each of the 16 expansion MPCs which a system using the IoTKit
  * might provide:
  *  + named GPIO inputs mpcexp_status[0..15]
@@ -67,7 +67,7 @@
 #define IOTS_NUM_APB_EXP_PPC 4
 #define IOTS_NUM_AHB_EXP_PPC 4
 #define IOTS_NUM_EXP_MPC 16
-#define IOTS_NUM_MPC 1
+#define IOTS_NUM_MPC 4
 #define IOTS_NUM_EXP_MSC 16
 
 typedef struct IoTKitSecCtl IoTKitSecCtl;
diff --git a/include/hw/misc/iotkit-sysinfo.h b/include/hw/misc/iotkit-sysinfo.h
index 7b2e1a5e48..d84eb203b9 100644
--- a/include/hw/misc/iotkit-sysinfo.h
+++ b/include/hw/misc/iotkit-sysinfo.h
@@ -14,6 +14,8 @@
  * Arm IoTKit and documented in
  * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html
  * QEMU interface:
+ *  + QOM property "SYS_VERSION": value to use for SYS_VERSION register
+ *  + QOM property "SYS_CONFIG": value to use for SYS_CONFIG register
  *  + sysbus MMIO region 0: the system information register bank
  */
 
@@ -32,6 +34,10 @@ typedef struct IoTKitSysInfo {
 
     /*< public >*/
     MemoryRegion iomem;
+
+    /* Properties */
+    uint32_t sys_version;
+    uint32_t sys_config;
 } IoTKitSysInfo;
 
 #endif
diff --git a/include/hw/nvram/nrf51_nvm.h b/include/hw/nvram/nrf51_nvm.h
new file mode 100644
index 0000000000..3792e4a9fe
--- /dev/null
+++ b/include/hw/nvram/nrf51_nvm.h
@@ -0,0 +1,64 @@
+/*
+ * Nordic Semiconductor nRF51 non-volatile memory
+ *
+ * It provides an interface to erase regions in flash memory.
+ * Furthermore it provides the user and factory information registers.
+ *
+ * QEMU interface:
+ * + sysbus MMIO regions 0: NVMC peripheral registers
+ * + sysbus MMIO regions 1: FICR peripheral registers
+ * + sysbus MMIO regions 2: UICR peripheral registers
+ * + flash-size property: flash size in bytes.
+ *
+ * Accuracy of the peripheral model:
+ * + Code regions (MPU configuration) are disregarded.
+ *
+ * Copyright 2018 Steffen Görtz <contrib@steffen-goertz.de>
+ *
+ * This code is licensed under the GPL version 2 or later.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#ifndef NRF51_NVM_H
+#define NRF51_NVM_H
+
+#include "hw/sysbus.h"
+#define TYPE_NRF51_NVM "nrf51_soc.nvm"
+#define NRF51_NVM(obj) OBJECT_CHECK(NRF51NVMState, (obj), TYPE_NRF51_NVM)
+
+#define NRF51_UICR_FIXTURE_SIZE 64
+
+#define NRF51_NVMC_SIZE         0x1000
+
+#define NRF51_NVMC_READY        0x400
+#define NRF51_NVMC_READY_READY  0x01
+#define NRF51_NVMC_CONFIG       0x504
+#define NRF51_NVMC_CONFIG_MASK  0x03
+#define NRF51_NVMC_CONFIG_WEN   0x01
+#define NRF51_NVMC_CONFIG_EEN   0x02
+#define NRF51_NVMC_ERASEPCR1    0x508
+#define NRF51_NVMC_ERASEPCR0    0x510
+#define NRF51_NVMC_ERASEALL     0x50C
+#define NRF51_NVMC_ERASEUICR    0x514
+#define NRF51_NVMC_ERASE        0x01
+
+#define NRF51_UICR_SIZE         0x100
+
+typedef struct NRF51NVMState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+    MemoryRegion ficr;
+    MemoryRegion uicr;
+    MemoryRegion flash;
+
+    uint32_t uicr_content[NRF51_UICR_FIXTURE_SIZE];
+    uint32_t flash_size;
+    uint8_t *storage;
+
+    uint32_t config;
+
+} NRF51NVMState;
+
+
+#endif
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index 4c2feb9c17..1d6099e5d4 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -103,9 +103,21 @@ struct TranslationBlock;
  * @get_arch_id: Callback for getting architecture-dependent CPU ID.
  * @get_paging_enabled: Callback for inquiring whether paging is enabled.
  * @get_memory_mapping: Callback for obtaining the memory mappings.
- * @set_pc: Callback for setting the Program Counter register.
+ * @set_pc: Callback for setting the Program Counter register. This
+ *       should have the semantics used by the target architecture when
+ *       setting the PC from a source such as an ELF file entry point;
+ *       for example on Arm it will also set the Thumb mode bit based
+ *       on the least significant bit of the new PC value.
+ *       If the target behaviour here is anything other than "set
+ *       the PC register to the value passed in" then the target must
+ *       also implement the synchronize_from_tb hook.
  * @synchronize_from_tb: Callback for synchronizing state from a TCG
- * #TranslationBlock.
+ *       #TranslationBlock. This is called when we abandon execution
+ *       of a TB before starting it, and must set all parts of the CPU
+ *       state which the previous TB in the chain may not have updated.
+ *       This always includes at least the program counter; some targets
+ *       will need to do more. If this hook is not implemented then the
+ *       default is to call @set_pc(tb->pc).
  * @handle_mmu_fault: Callback for handling an MMU fault.
  * @get_phys_page_debug: Callback for obtaining a physical address.
  * @get_phys_page_attrs_debug: Callback for obtaining a physical address and the
diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c
index 65d815f030..d75fd9d3e2 100644
--- a/linux-user/aarch64/cpu_loop.c
+++ b/linux-user/aarch64/cpu_loop.c
@@ -147,10 +147,29 @@ void cpu_loop(CPUARMState *env)
     }
 }
 
+static uint64_t arm_rand64(void)
+{
+    int shift = 64 - clz64(RAND_MAX);
+    int i, n = 64 / shift + (64 % shift != 0);
+    uint64_t ret = 0;
+
+    for (i = 0; i < n; i++) {
+        ret = (ret << shift) | rand();
+    }
+    return ret;
+}
+
+void arm_init_pauth_key(ARMPACKey *key)
+{
+    key->lo = arm_rand64();
+    key->hi = arm_rand64();
+}
+
 void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
 {
-    CPUState *cpu = ENV_GET_CPU(env);
-    TaskState *ts = cpu->opaque;
+    ARMCPU *cpu = arm_env_get_cpu(env);
+    CPUState *cs = CPU(cpu);
+    TaskState *ts = cs->opaque;
     struct image_info *info = ts->info;
     int i;
 
@@ -172,6 +191,14 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
     }
 #endif
 
+    if (cpu_isar_feature(aa64_pauth, cpu)) {
+        arm_init_pauth_key(&env->apia_key);
+        arm_init_pauth_key(&env->apib_key);
+        arm_init_pauth_key(&env->apda_key);
+        arm_init_pauth_key(&env->apdb_key);
+        arm_init_pauth_key(&env->apga_key);
+    }
+
     ts->stack_base = info->start_stack;
     ts->heap_base = info->brk;
     /* This will be filled in on the first SYS_HEAPINFO call.  */
diff --git a/linux-user/aarch64/target_syscall.h b/linux-user/aarch64/target_syscall.h
index 205265e619..937fd7989e 100644
--- a/linux-user/aarch64/target_syscall.h
+++ b/linux-user/aarch64/target_syscall.h
@@ -22,4 +22,6 @@ struct target_pt_regs {
 #define TARGET_PR_SVE_SET_VL  50
 #define TARGET_PR_SVE_GET_VL  51
 
+void arm_init_pauth_key(ARMPACKey *key);
+
 #endif /* AARCH64_TARGET_SYSCALL_H */
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 4cff9e1a31..775a36ccdd 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -560,6 +560,15 @@ enum {
     ARM_HWCAP_A64_ASIMDDP       = 1 << 20,
     ARM_HWCAP_A64_SHA512        = 1 << 21,
     ARM_HWCAP_A64_SVE           = 1 << 22,
+    ARM_HWCAP_A64_ASIMDFHM      = 1 << 23,
+    ARM_HWCAP_A64_DIT           = 1 << 24,
+    ARM_HWCAP_A64_USCAT         = 1 << 25,
+    ARM_HWCAP_A64_ILRCPC        = 1 << 26,
+    ARM_HWCAP_A64_FLAGM         = 1 << 27,
+    ARM_HWCAP_A64_SSBS          = 1 << 28,
+    ARM_HWCAP_A64_SB            = 1 << 29,
+    ARM_HWCAP_A64_PACA          = 1 << 30,
+    ARM_HWCAP_A64_PACG          = 1UL << 31,
 };
 
 #define ELF_HWCAP get_elf_hwcap()
@@ -591,6 +600,7 @@ static uint32_t get_elf_hwcap(void)
     GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP);
     GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA);
     GET_FEATURE_ID(aa64_sve, ARM_HWCAP_A64_SVE);
+    GET_FEATURE_ID(aa64_pauth, ARM_HWCAP_A64_PACA | ARM_HWCAP_A64_PACG);
 
 #undef GET_FEATURE_ID
 
diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c
index 2b856930fb..f9de5164e5 100644
--- a/target/arm/arm-powerctl.c
+++ b/target/arm/arm-powerctl.c
@@ -120,11 +120,8 @@ static void arm_set_cpu_on_async_work(CPUState *target_cpu_state,
 
     if (info->target_aa64) {
         target_cpu->env.xregs[0] = info->context_id;
-        target_cpu->env.thumb = false;
     } else {
         target_cpu->env.regs[0] = info->context_id;
-        target_cpu->env.thumb = info->entry & 1;
-        info->entry &= 0xfffffffe;
     }
 
     /* Start the new CPU at the requested address */
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index d6da3f4fed..3874dc9875 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -40,8 +40,31 @@
 static void arm_cpu_set_pc(CPUState *cs, vaddr value)
 {
     ARMCPU *cpu = ARM_CPU(cs);
+    CPUARMState *env = &cpu->env;
 
-    cpu->env.regs[15] = value;
+    if (is_a64(env)) {
+        env->pc = value;
+        env->thumb = 0;
+    } else {
+        env->regs[15] = value & ~1;
+        env->thumb = value & 1;
+    }
+}
+
+static void arm_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
+{
+    ARMCPU *cpu = ARM_CPU(cs);
+    CPUARMState *env = &cpu->env;
+
+    /*
+     * It's OK to look at env for the current mode here, because it's
+     * never possible for an AArch64 TB to chain to an AArch32 TB.
+     */
+    if (is_a64(env)) {
+        env->pc = tb->pc;
+    } else {
+        env->regs[15] = tb->pc;
+    }
 }
 
 static bool arm_cpu_has_work(CPUState *cs)
@@ -162,6 +185,9 @@ static void arm_cpu_reset(CPUState *s)
         env->pstate = PSTATE_MODE_EL0t;
         /* Userspace expects access to DC ZVA, CTL_EL0 and the cache ops */
         env->cp15.sctlr_el[1] |= SCTLR_UCT | SCTLR_UCI | SCTLR_DZE;
+        /* Enable all PAC keys.  */
+        env->cp15.sctlr_el[1] |= (SCTLR_EnIA | SCTLR_EnIB |
+                                  SCTLR_EnDA | SCTLR_EnDB);
         /* Enable all PAC instructions */
         env->cp15.hcr_el2 |= HCR_API;
         env->cp15.scr_el3 |= SCR_API;
@@ -836,6 +862,13 @@ static void arm_cpu_finalizefn(Object *obj)
         QLIST_REMOVE(hook, node);
         g_free(hook);
     }
+#ifndef CONFIG_USER_ONLY
+    if (cpu->pmu_timer) {
+        timer_del(cpu->pmu_timer);
+        timer_deinit(cpu->pmu_timer);
+        timer_free(cpu->pmu_timer);
+    }
+#endif
 }
 
 static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
@@ -1045,6 +1078,11 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
             arm_register_pre_el_change_hook(cpu, &pmu_pre_el_change, 0);
             arm_register_el_change_hook(cpu, &pmu_post_el_change, 0);
         }
+
+#ifndef CONFIG_USER_ONLY
+        cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, arm_pmu_timer_cb,
+                cpu);
+#endif
     } else {
         cpu->id_aa64dfr0 &= ~0xf00;
         cpu->pmceid0 = 0;
@@ -2087,6 +2125,7 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data)
     cc->cpu_exec_interrupt = arm_cpu_exec_interrupt;
     cc->dump_state = arm_cpu_dump_state;
     cc->set_pc = arm_cpu_set_pc;
+    cc->synchronize_from_tb = arm_cpu_synchronize_from_tb;
     cc->gdb_read_register = arm_cpu_gdb_read_register;
     cc->gdb_write_register = arm_cpu_gdb_write_register;
 #ifdef CONFIG_USER_ONLY
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index b8161cb6d7..a68bcc9fed 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -746,6 +746,11 @@ struct ARMCPU {
 
     /* Timers used by the generic (architected) timer */
     QEMUTimer *gt_timer[NUM_GTIMERS];
+    /*
+     * Timer used by the PMU. Its state is restored after migration by
+     * pmu_op_finish() - it does not need other handling during migration
+     */
+    QEMUTimer *pmu_timer;
     /* GPIO outputs for generic timer */
     qemu_irq gt_timer_outputs[NUM_GTIMERS];
     /* GPIO output for GICv3 maintenance interrupt signal */
@@ -1005,6 +1010,11 @@ void pmccntr_op_finish(CPUARMState *env);
 void pmu_op_start(CPUARMState *env);
 void pmu_op_finish(CPUARMState *env);
 
+/*
+ * Called when a PMU counter is due to overflow
+ */
+void arm_pmu_timer_cb(void *opaque);
+
 /**
  * Functions to register as EL change hooks for PMU mode filtering
  */
@@ -2502,7 +2512,7 @@ bool write_cpustate_to_list(ARMCPU *cpu);
 
 #if defined(TARGET_AARCH64)
 #  define TARGET_PHYS_ADDR_SPACE_BITS 48
-#  define TARGET_VIRT_ADDR_SPACE_BITS 64
+#  define TARGET_VIRT_ADDR_SPACE_BITS 48
 #else
 #  define TARGET_PHYS_ADDR_SPACE_BITS 40
 #  define TARGET_VIRT_ADDR_SPACE_BITS 32
diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index e9bc461c36..7107ec8d7e 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -281,38 +281,6 @@ static void cpu_max_set_sve_vq(Object *obj, Visitor *v, const char *name,
     error_propagate(errp, err);
 }
 
-#ifdef CONFIG_USER_ONLY
-static void cpu_max_get_packey(Object *obj, Visitor *v, const char *name,
-                               void *opaque, Error **errp)
-{
-    ARMCPU *cpu = ARM_CPU(obj);
-    const uint64_t *bit = opaque;
-    bool enabled = (cpu->env.cp15.sctlr_el[1] & *bit) != 0;
-
-    visit_type_bool(v, name, &enabled, errp);
-}
-
-static void cpu_max_set_packey(Object *obj, Visitor *v, const char *name,
-                               void *opaque, Error **errp)
-{
-    ARMCPU *cpu = ARM_CPU(obj);
-    Error *err = NULL;
-    const uint64_t *bit = opaque;
-    bool enabled;
-
-    visit_type_bool(v, name, &enabled, errp);
-
-    if (!err) {
-        if (enabled) {
-            cpu->env.cp15.sctlr_el[1] |= *bit;
-        } else {
-            cpu->env.cp15.sctlr_el[1] &= ~*bit;
-        }
-    }
-    error_propagate(errp, err);
-}
-#endif
-
 /* -cpu max: if KVM is enabled, like -cpu host (best possible with this host);
  * otherwise, a CPU with as many features enabled as our emulation supports.
  * The version of '-cpu max' for qemu-system-arm is defined in cpu.c;
@@ -388,34 +356,6 @@ static void aarch64_max_initfn(Object *obj)
          */
         cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */
         cpu->dcz_blocksize = 7; /*  512 bytes */
-
-        /*
-         * Note that Linux will enable enable all of the keys at once.
-         * But doing it this way will allow experimentation beyond that.
-         */
-        {
-            static const uint64_t apia_bit = SCTLR_EnIA;
-            static const uint64_t apib_bit = SCTLR_EnIB;
-            static const uint64_t apda_bit = SCTLR_EnDA;
-            static const uint64_t apdb_bit = SCTLR_EnDB;
-
-            object_property_add(obj, "apia", "bool", cpu_max_get_packey,
-                                cpu_max_set_packey, NULL,
-                                (void *)&apia_bit, &error_fatal);
-            object_property_add(obj, "apib", "bool", cpu_max_get_packey,
-                                cpu_max_set_packey, NULL,
-                                (void *)&apib_bit, &error_fatal);
-            object_property_add(obj, "apda", "bool", cpu_max_get_packey,
-                                cpu_max_set_packey, NULL,
-                                (void *)&apda_bit, &error_fatal);
-            object_property_add(obj, "apdb", "bool", cpu_max_get_packey,
-                                cpu_max_set_packey, NULL,
-                                (void *)&apdb_bit, &error_fatal);
-
-            /* Enable all PAC keys by default.  */
-            cpu->env.cp15.sctlr_el[1] |= SCTLR_EnIA | SCTLR_EnIB;
-            cpu->env.cp15.sctlr_el[1] |= SCTLR_EnDA | SCTLR_EnDB;
-        }
 #endif
 
         cpu->sve_max_vq = ARM_MAX_VQ;
@@ -480,20 +420,6 @@ static void aarch64_cpu_finalizefn(Object *obj)
 {
 }
 
-static void aarch64_cpu_set_pc(CPUState *cs, vaddr value)
-{
-    ARMCPU *cpu = ARM_CPU(cs);
-    /* It's OK to look at env for the current mode here, because it's
-     * never possible for an AArch64 TB to chain to an AArch32 TB.
-     * (Otherwise we would need to use synchronize_from_tb instead.)
-     */
-    if (is_a64(&cpu->env)) {
-        cpu->env.pc = value;
-    } else {
-        cpu->env.regs[15] = value;
-    }
-}
-
 static gchar *aarch64_gdb_arch_name(CPUState *cs)
 {
     return g_strdup("aarch64");
@@ -504,7 +430,6 @@ static void aarch64_cpu_class_init(ObjectClass *oc, void *data)
     CPUClass *cc = CPU_CLASS(oc);
 
     cc->cpu_exec_interrupt = arm_cpu_exec_interrupt;
-    cc->set_pc = aarch64_cpu_set_pc;
     cc->gdb_read_register = aarch64_cpu_gdb_read_register;
     cc->gdb_write_register = aarch64_cpu_gdb_write_register;
     cc->gdb_num_core_regs = 34;
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 66faebea8e..d070879894 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -977,6 +977,7 @@ static const ARMCPRegInfo v6_cp_reginfo[] = {
 /* Definitions for the PMU registers */
 #define PMCRN_MASK  0xf800
 #define PMCRN_SHIFT 11
+#define PMCRLC  0x40
 #define PMCRDP  0x10
 #define PMCRD   0x8
 #define PMCRC   0x4
@@ -1020,6 +1021,13 @@ typedef struct pm_event {
      * counters hold a difference from the return value from this function
      */
     uint64_t (*get_count)(CPUARMState *);
+    /*
+     * Return how many nanoseconds it will take (at a minimum) for count events
+     * to occur. A negative value indicates the counter will never overflow, or
+     * that the counter has otherwise arranged for the overflow bit to be set
+     * and the PMU interrupt to be raised on overflow.
+     */
+    int64_t (*ns_per_count)(uint64_t);
 } pm_event;
 
 static bool event_always_supported(CPUARMState *env)
@@ -1036,6 +1044,11 @@ static uint64_t swinc_get_count(CPUARMState *env)
     return 0;
 }
 
+static int64_t swinc_ns_per(uint64_t ignored)
+{
+    return -1;
+}
+
 /*
  * Return the underlying cycle count for the PMU cycle counters. If we're in
  * usermode, simply return 0.
@@ -1051,6 +1064,11 @@ static uint64_t cycles_get_count(CPUARMState *env)
 }
 
 #ifndef CONFIG_USER_ONLY
+static int64_t cycles_ns_per(uint64_t cycles)
+{
+    return (ARM_CPU_FREQ / NANOSECONDS_PER_SECOND) * cycles;
+}
+
 static bool instructions_supported(CPUARMState *env)
 {
     return use_icount == 1 /* Precise instruction counting */;
@@ -1060,21 +1078,29 @@ static uint64_t instructions_get_count(CPUARMState *env)
 {
     return (uint64_t)cpu_get_icount_raw();
 }
+
+static int64_t instructions_ns_per(uint64_t icount)
+{
+    return cpu_icount_to_ns((int64_t)icount);
+}
 #endif
 
 static const pm_event pm_events[] = {
     { .number = 0x000, /* SW_INCR */
       .supported = event_always_supported,
       .get_count = swinc_get_count,
+      .ns_per_count = swinc_ns_per,
     },
 #ifndef CONFIG_USER_ONLY
     { .number = 0x008, /* INST_RETIRED, Instruction architecturally executed */
       .supported = instructions_supported,
       .get_count = instructions_get_count,
+      .ns_per_count = instructions_ns_per,
     },
     { .number = 0x011, /* CPU_CYCLES, Cycle */
       .supported = event_always_supported,
       .get_count = cycles_get_count,
+      .ns_per_count = cycles_ns_per,
     }
 #endif
 };
@@ -1293,6 +1319,13 @@ static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter)
     return enabled && !prohibited && !filtered;
 }
 
+static void pmu_update_irq(CPUARMState *env)
+{
+    ARMCPU *cpu = arm_env_get_cpu(env);
+    qemu_set_irq(cpu->pmu_interrupt, (env->cp15.c9_pmcr & PMCRE) &&
+            (env->cp15.c9_pminten & env->cp15.c9_pmovsr));
+}
+
 /*
  * Ensure c15_ccnt is the guest-visible count so that operations such as
  * enabling/disabling the counter or filtering, modifying the count itself,
@@ -1310,7 +1343,16 @@ void pmccntr_op_start(CPUARMState *env)
             eff_cycles /= 64;
         }
 
-        env->cp15.c15_ccnt = eff_cycles - env->cp15.c15_ccnt_delta;
+        uint64_t new_pmccntr = eff_cycles - env->cp15.c15_ccnt_delta;
+
+        uint64_t overflow_mask = env->cp15.c9_pmcr & PMCRLC ? \
+                                 1ull << 63 : 1ull << 31;
+        if (env->cp15.c15_ccnt & ~new_pmccntr & overflow_mask) {
+            env->cp15.c9_pmovsr |= (1 << 31);
+            pmu_update_irq(env);
+        }
+
+        env->cp15.c15_ccnt = new_pmccntr;
     }
     env->cp15.c15_ccnt_delta = cycles;
 }
@@ -1323,13 +1365,27 @@ void pmccntr_op_start(CPUARMState *env)
 void pmccntr_op_finish(CPUARMState *env)
 {
     if (pmu_counter_enabled(env, 31)) {
-        uint64_t prev_cycles = env->cp15.c15_ccnt_delta;
+#ifndef CONFIG_USER_ONLY
+        /* Calculate when the counter will next overflow */
+        uint64_t remaining_cycles = -env->cp15.c15_ccnt;
+        if (!(env->cp15.c9_pmcr & PMCRLC)) {
+            remaining_cycles = (uint32_t)remaining_cycles;
+        }
+        int64_t overflow_in = cycles_ns_per(remaining_cycles);
+
+        if (overflow_in > 0) {
+            int64_t overflow_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+                overflow_in;
+            ARMCPU *cpu = arm_env_get_cpu(env);
+            timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at);
+        }
+#endif
 
+        uint64_t prev_cycles = env->cp15.c15_ccnt_delta;
         if (env->cp15.c9_pmcr & PMCRD) {
             /* Increment once every 64 processor clock cycles */
             prev_cycles /= 64;
         }
-
         env->cp15.c15_ccnt_delta = prev_cycles - env->cp15.c15_ccnt;
     }
 }
@@ -1345,8 +1401,13 @@ static void pmevcntr_op_start(CPUARMState *env, uint8_t counter)
     }
 
     if (pmu_counter_enabled(env, counter)) {
-        env->cp15.c14_pmevcntr[counter] =
-            count - env->cp15.c14_pmevcntr_delta[counter];
+        uint32_t new_pmevcntr = count - env->cp15.c14_pmevcntr_delta[counter];
+
+        if (env->cp15.c14_pmevcntr[counter] & ~new_pmevcntr & INT32_MIN) {
+            env->cp15.c9_pmovsr |= (1 << counter);
+            pmu_update_irq(env);
+        }
+        env->cp15.c14_pmevcntr[counter] = new_pmevcntr;
     }
     env->cp15.c14_pmevcntr_delta[counter] = count;
 }
@@ -1354,6 +1415,21 @@ static void pmevcntr_op_start(CPUARMState *env, uint8_t counter)
 static void pmevcntr_op_finish(CPUARMState *env, uint8_t counter)
 {
     if (pmu_counter_enabled(env, counter)) {
+#ifndef CONFIG_USER_ONLY
+        uint16_t event = env->cp15.c14_pmevtyper[counter] & PMXEVTYPER_EVTCOUNT;
+        uint16_t event_idx = supported_event_map[event];
+        uint64_t delta = UINT32_MAX -
+            (uint32_t)env->cp15.c14_pmevcntr[counter] + 1;
+        int64_t overflow_in = pm_events[event_idx].ns_per_count(delta);
+
+        if (overflow_in > 0) {
+            int64_t overflow_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+                overflow_in;
+            ARMCPU *cpu = arm_env_get_cpu(env);
+            timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at);
+        }
+#endif
+
         env->cp15.c14_pmevcntr_delta[counter] -=
             env->cp15.c14_pmevcntr[counter];
     }
@@ -1387,6 +1463,20 @@ void pmu_post_el_change(ARMCPU *cpu, void *ignored)
     pmu_op_finish(&cpu->env);
 }
 
+void arm_pmu_timer_cb(void *opaque)
+{
+    ARMCPU *cpu = opaque;
+
+    /*
+     * Update all the counter values based on the current underlying counts,
+     * triggering interrupts to be raised, if necessary. pmu_op_finish() also
+     * has the effect of setting the cpu->pmu_timer to the next earliest time a
+     * counter may expire.
+     */
+    pmu_op_start(&cpu->env);
+    pmu_op_finish(&cpu->env);
+}
+
 static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
                        uint64_t value)
 {
@@ -1423,7 +1513,20 @@ static void pmswinc_write(CPUARMState *env, const ARMCPRegInfo *ri,
                 /* counter is SW_INCR */
                 (env->cp15.c14_pmevtyper[i] & PMXEVTYPER_EVTCOUNT) == 0x0) {
             pmevcntr_op_start(env, i);
-            env->cp15.c14_pmevcntr[i]++;
+
+            /*
+             * Detect if this write causes an overflow since we can't predict
+             * PMSWINC overflows like we can for other events
+             */
+            uint32_t new_pmswinc = env->cp15.c14_pmevcntr[i] + 1;
+
+            if (env->cp15.c14_pmevcntr[i] & ~new_pmswinc & INT32_MIN) {
+                env->cp15.c9_pmovsr |= (1 << i);
+                pmu_update_irq(env);
+            }
+
+            env->cp15.c14_pmevcntr[i] = new_pmswinc;
+
             pmevcntr_op_finish(env, i);
         }
     }
@@ -1508,6 +1611,7 @@ static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
 {
     value &= pmu_counter_mask(env);
     env->cp15.c9_pmovsr &= ~value;
+    pmu_update_irq(env);
 }
 
 static void pmovsset_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -1515,6 +1619,7 @@ static void pmovsset_write(CPUARMState *env, const ARMCPRegInfo *ri,
 {
     value &= pmu_counter_mask(env);
     env->cp15.c9_pmovsr |= value;
+    pmu_update_irq(env);
 }
 
 static void pmevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -1701,6 +1806,7 @@ static void pmintenset_write(CPUARMState *env, const ARMCPRegInfo *ri,
     /* We have no event counters so only the C bit can be changed */
     value &= pmu_counter_mask(env);
     env->cp15.c9_pminten |= value;
+    pmu_update_irq(env);
 }
 
 static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -1708,6 +1814,7 @@ static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
 {
     value &= pmu_counter_mask(env);
     env->cp15.c9_pminten &= ~value;
+    pmu_update_irq(env);
 }
 
 static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -1752,6 +1859,9 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
     if (cpu_isar_feature(aa64_lor, cpu)) {
         valid_mask |= SCR_TLOR;
     }
+    if (cpu_isar_feature(aa64_pauth, cpu)) {
+        valid_mask |= SCR_API | SCR_APK;
+    }
 
     /* Clear all-context RES0 bits.  */
     value &= valid_mask;
@@ -1846,7 +1956,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
       .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
       .writefn = pmcntenclr_write },
     { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3,
-      .access = PL0_RW,
+      .access = PL0_RW, .type = ARM_CP_IO,
       .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr),
       .accessfn = pmreg_access,
       .writefn = pmovsr_write,
@@ -1854,16 +1964,18 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
     { .name = "PMOVSCLR_EL0", .state = ARM_CP_STATE_AA64,
       .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 3,
       .access = PL0_RW, .accessfn = pmreg_access,
-      .type = ARM_CP_ALIAS,
+      .type = ARM_CP_ALIAS | ARM_CP_IO,
       .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
       .writefn = pmovsr_write,
       .raw_writefn = raw_write },
     { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4,
-      .access = PL0_W, .accessfn = pmreg_access_swinc, .type = ARM_CP_NO_RAW,
+      .access = PL0_W, .accessfn = pmreg_access_swinc,
+      .type = ARM_CP_NO_RAW | ARM_CP_IO,
       .writefn = pmswinc_write },
     { .name = "PMSWINC_EL0", .state = ARM_CP_STATE_AA64,
       .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 4,
-      .access = PL0_W, .accessfn = pmreg_access_swinc, .type = ARM_CP_NO_RAW,
+      .access = PL0_W, .accessfn = pmreg_access_swinc,
+      .type = ARM_CP_NO_RAW | ARM_CP_IO,
       .writefn = pmswinc_write },
     { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5,
       .access = PL0_RW, .type = ARM_CP_ALIAS,
@@ -2050,14 +2162,14 @@ static const ARMCPRegInfo pmovsset_cp_reginfo[] = {
     /* PMOVSSET is not implemented in v7 before v7ve */
     { .name = "PMOVSSET", .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 3,
       .access = PL0_RW, .accessfn = pmreg_access,
-      .type = ARM_CP_ALIAS,
+      .type = ARM_CP_ALIAS | ARM_CP_IO,
       .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr),
       .writefn = pmovsset_write,
       .raw_writefn = raw_write },
     { .name = "PMOVSSET_EL0", .state = ARM_CP_STATE_AA64,
       .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 3,
       .access = PL0_RW, .accessfn = pmreg_access,
-      .type = ARM_CP_ALIAS,
+      .type = ARM_CP_ALIAS | ARM_CP_IO,
       .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
       .writefn = pmovsset_write,
       .raw_writefn = raw_write },
@@ -4449,6 +4561,9 @@ static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
     if (cpu_isar_feature(aa64_lor, cpu)) {
         valid_mask |= HCR_TLOR;
     }
+    if (cpu_isar_feature(aa64_pauth, cpu)) {
+        valid_mask |= HCR_API | HCR_APK;
+    }
 
     /* Clear RES0 bits.  */
     value &= valid_mask;
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index 4d28a27c3b..a1997e3ae2 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -2036,7 +2036,7 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn)
         if (!dc_isar_feature(aa64_pauth, s)) {
             goto do_unallocated;
         }
-        if (op3 != 2 || op3 != 3) {
+        if ((op3 & ~1) != 2) {
             goto do_unallocated;
         }
         if (s->pauth_active) {
@@ -2144,7 +2144,11 @@ static void disas_b_exc_sys(DisasContext *s, uint32_t insn)
         break;
     case 0x6a: /* Exception generation / System */
         if (insn & (1 << 24)) {
-            disas_system(s, insn);
+            if (extract32(insn, 22, 2) == 0) {
+                disas_system(s, insn);
+            } else {
+                unallocated_encoding(s);
+            }
         } else {
             disas_exc(s, insn);
         }
@@ -2799,7 +2803,7 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn,
     } else {
         if (size == 3 && opc == 2) {
             /* PRFM - prefetch */
-            if (is_unpriv) {
+            if (idx != 0) {
                 unallocated_encoding(s);
                 return;
             }
@@ -3245,6 +3249,7 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn)
 {
     int rt = extract32(insn, 0, 5);
     int rn = extract32(insn, 5, 5);
+    int rm = extract32(insn, 16, 5);
     int size = extract32(insn, 10, 2);
     int opcode = extract32(insn, 12, 4);
     bool is_store = !extract32(insn, 22, 1);
@@ -3264,6 +3269,11 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn)
         return;
     }
 
+    if (!is_postidx && rm != 0) {
+        unallocated_encoding(s);
+        return;
+    }
+
     /* From the shared decode logic */
     switch (opcode) {
     case 0x0:
@@ -3363,7 +3373,6 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn)
     }
 
     if (is_postidx) {
-        int rm = extract32(insn, 16, 5);
         if (rm == 31) {
             tcg_gen_mov_i64(tcg_rn, tcg_addr);
         } else {
@@ -3400,6 +3409,7 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn)
 {
     int rt = extract32(insn, 0, 5);
     int rn = extract32(insn, 5, 5);
+    int rm = extract32(insn, 16, 5);
     int size = extract32(insn, 10, 2);
     int S = extract32(insn, 12, 1);
     int opc = extract32(insn, 13, 3);
@@ -3415,6 +3425,15 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn)
     int ebytes, xs;
     TCGv_i64 tcg_addr, tcg_rn, tcg_ebytes;
 
+    if (extract32(insn, 31, 1)) {
+        unallocated_encoding(s);
+        return;
+    }
+    if (!is_postidx && rm != 0) {
+        unallocated_encoding(s);
+        return;
+    }
+
     switch (scale) {
     case 3:
         if (!is_load || S) {
@@ -3492,7 +3511,6 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn)
     }
 
     if (is_postidx) {
-        int rm = extract32(insn, 16, 5);
         if (rm == 31) {
             tcg_gen_mov_i64(tcg_rn, tcg_addr);
         } else {
@@ -4183,6 +4201,7 @@ static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn)
     int imm3 = extract32(insn, 10, 3);
     int option = extract32(insn, 13, 3);
     int rm = extract32(insn, 16, 5);
+    int opt = extract32(insn, 22, 2);
     bool setflags = extract32(insn, 29, 1);
     bool sub_op = extract32(insn, 30, 1);
     bool sf = extract32(insn, 31, 1);
@@ -4191,7 +4210,7 @@ static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn)
     TCGv_i64 tcg_rd;
     TCGv_i64 tcg_result;
 
-    if (imm3 > 4) {
+    if (imm3 > 4 || opt != 0) {
         unallocated_encoding(s);
         return;
     }
@@ -5617,11 +5636,17 @@ static void handle_fp_fcvt(DisasContext *s, int opcode,
  */
 static void disas_fp_1src(DisasContext *s, uint32_t insn)
 {
+    int mos = extract32(insn, 29, 3);
     int type = extract32(insn, 22, 2);
     int opcode = extract32(insn, 15, 6);
     int rn = extract32(insn, 5, 5);
     int rd = extract32(insn, 0, 5);
 
+    if (mos) {
+        unallocated_encoding(s);
+        return;
+    }
+
     switch (opcode) {
     case 0x4: case 0x5: case 0x7:
     {
@@ -5848,13 +5873,14 @@ static void handle_fp_2src_half(DisasContext *s, int opcode,
  */
 static void disas_fp_2src(DisasContext *s, uint32_t insn)
 {
+    int mos = extract32(insn, 29, 3);
     int type = extract32(insn, 22, 2);
     int rd = extract32(insn, 0, 5);
     int rn = extract32(insn, 5, 5);
     int rm = extract32(insn, 16, 5);
     int opcode = extract32(insn, 12, 4);
 
-    if (opcode > 8) {
+    if (opcode > 8 || mos) {
         unallocated_encoding(s);
         return;
     }
@@ -6009,6 +6035,7 @@ static void handle_fp_3src_half(DisasContext *s, bool o0, bool o1,
  */
 static void disas_fp_3src(DisasContext *s, uint32_t insn)
 {
+    int mos = extract32(insn, 29, 3);
     int type = extract32(insn, 22, 2);
     int rd = extract32(insn, 0, 5);
     int rn = extract32(insn, 5, 5);
@@ -6017,6 +6044,11 @@ static void disas_fp_3src(DisasContext *s, uint32_t insn)
     bool o0 = extract32(insn, 15, 1);
     bool o1 = extract32(insn, 21, 1);
 
+    if (mos) {
+        unallocated_encoding(s);
+        return;
+    }
+
     switch (type) {
     case 0:
         if (!fp_access_check(s)) {
@@ -6086,12 +6118,19 @@ uint64_t vfp_expand_imm(int size, uint8_t imm8)
 static void disas_fp_imm(DisasContext *s, uint32_t insn)
 {
     int rd = extract32(insn, 0, 5);
+    int imm5 = extract32(insn, 5, 5);
     int imm8 = extract32(insn, 13, 8);
     int type = extract32(insn, 22, 2);
+    int mos = extract32(insn, 29, 3);
     uint64_t imm;
     TCGv_i64 tcg_res;
     TCGMemOp sz;
 
+    if (mos || imm5) {
+        unallocated_encoding(s);
+        return;
+    }
+
     switch (type) {
     case 0:
         sz = MO_32;
@@ -12602,7 +12641,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
         break;
     case 0x0e: /* SDOT */
     case 0x1e: /* UDOT */
-        if (size != MO_32 || !dc_isar_feature(aa64_dp, s)) {
+        if (is_scalar || size != MO_32 || !dc_isar_feature(aa64_dp, s)) {
             unallocated_encoding(s);
             return;
         }
@@ -12611,7 +12650,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
     case 0x13: /* FCMLA #90 */
     case 0x15: /* FCMLA #180 */
     case 0x17: /* FCMLA #270 */
-        if (!dc_isar_feature(aa64_fcma, s)) {
+        if (is_scalar || !dc_isar_feature(aa64_fcma, s)) {
             unallocated_encoding(s);
             return;
         }
@@ -12641,7 +12680,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
 
     case 2: /* complex fp */
         /* Each indexable element is a complex pair.  */
-        size <<= 1;
+        size += 1;
         switch (size) {
         case MO_32:
             if (h && !is_q) {
diff --git a/tests/microbit-test.c b/tests/microbit-test.c
index 3bad947b6c..04e199ec33 100644
--- a/tests/microbit-test.c
+++ b/tests/microbit-test.c
@@ -21,6 +21,7 @@
 #include "hw/arm/nrf51.h"
 #include "hw/char/nrf51_uart.h"
 #include "hw/gpio/nrf51_gpio.h"
+#include "hw/nvram/nrf51_nvm.h"
 #include "hw/timer/nrf51_timer.h"
 #include "hw/i2c/microbit_i2c.h"
 
@@ -156,6 +157,112 @@ static void test_microbit_i2c(void)
     qtest_quit(qts);
 }
 
+#define FLASH_SIZE          (256 * NRF51_PAGE_SIZE)
+
+static void fill_and_erase(QTestState *qts, hwaddr base, hwaddr size,
+                           uint32_t address_reg)
+{
+    hwaddr i;
+
+    /* Erase Page */
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02);
+    qtest_writel(qts, NRF51_NVMC_BASE + address_reg, base);
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
+
+    /* Check memory */
+    for (i = 0; i < size / 4; i++) {
+        g_assert_cmpuint(qtest_readl(qts, base + i * 4), ==, 0xFFFFFFFF);
+    }
+
+    /* Fill memory */
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01);
+    for (i = 0; i < size / 4; i++) {
+        qtest_writel(qts, base + i * 4, i);
+        g_assert_cmpuint(qtest_readl(qts, base + i * 4), ==, i);
+    }
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
+}
+
+static void test_nrf51_nvmc(void)
+{
+    uint32_t value;
+    hwaddr i;
+    QTestState *qts = qtest_init("-M microbit");
+
+    /* Test always ready */
+    value = qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_READY);
+    g_assert_cmpuint(value & 0x01, ==, 0x01);
+
+    /* Test write-read config register */
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x03);
+    g_assert_cmpuint(qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG),
+                     ==, 0x03);
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
+    g_assert_cmpuint(qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG),
+                     ==, 0x00);
+
+    /* Test PCR0 */
+    fill_and_erase(qts, NRF51_FLASH_BASE, NRF51_PAGE_SIZE,
+                   NRF51_NVMC_ERASEPCR0);
+    fill_and_erase(qts, NRF51_FLASH_BASE + NRF51_PAGE_SIZE,
+                   NRF51_PAGE_SIZE, NRF51_NVMC_ERASEPCR0);
+
+    /* Test PCR1 */
+    fill_and_erase(qts, NRF51_FLASH_BASE, NRF51_PAGE_SIZE,
+                   NRF51_NVMC_ERASEPCR1);
+    fill_and_erase(qts, NRF51_FLASH_BASE + NRF51_PAGE_SIZE,
+                   NRF51_PAGE_SIZE, NRF51_NVMC_ERASEPCR1);
+
+    /* Erase all */
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02);
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEALL, 0x01);
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
+
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01);
+    for (i = 0; i < FLASH_SIZE / 4; i++) {
+        qtest_writel(qts, NRF51_FLASH_BASE + i * 4, i);
+        g_assert_cmpuint(qtest_readl(qts, NRF51_FLASH_BASE + i * 4), ==, i);
+    }
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
+
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02);
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEALL, 0x01);
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
+
+    for (i = 0; i < FLASH_SIZE / 4; i++) {
+        g_assert_cmpuint(qtest_readl(qts, NRF51_FLASH_BASE + i * 4),
+                         ==, 0xFFFFFFFF);
+    }
+
+    /* Erase UICR */
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02);
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEUICR, 0x01);
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
+
+    for (i = 0; i < NRF51_UICR_SIZE / 4; i++) {
+        g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4),
+                         ==, 0xFFFFFFFF);
+    }
+
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01);
+    for (i = 0; i < NRF51_UICR_SIZE / 4; i++) {
+        qtest_writel(qts, NRF51_UICR_BASE + i * 4, i);
+        g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4), ==, i);
+    }
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
+
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02);
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEUICR, 0x01);
+    qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
+
+    for (i = 0; i < NRF51_UICR_SIZE / 4; i++) {
+        g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4),
+                         ==, 0xFFFFFFFF);
+    }
+
+    qtest_quit(qts);
+}
+
 static void test_nrf51_gpio(void)
 {
     size_t i;
@@ -392,6 +499,7 @@ int main(int argc, char **argv)
 
     qtest_add_func("/microbit/nrf51/uart", test_nrf51_uart);
     qtest_add_func("/microbit/nrf51/gpio", test_nrf51_gpio);
+    qtest_add_func("/microbit/nrf51/nvmc", test_nrf51_nvmc);
     qtest_add_func("/microbit/nrf51/timer", test_nrf51_timer);
     qtest_add_func("/microbit/microbit/i2c", test_microbit_i2c);