summary refs log tree commit diff stats
path: root/hw/misc/iotkit-secctl.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2018-03-02 14:37:10 +0000
committerPeter Maydell <peter.maydell@linaro.org>2018-03-02 14:37:10 +0000
commit86f4c7e05b1c44dbe1b329a51f311f10aef6ff34 (patch)
tree6073147f05719812e5ecb14ffd6994a66fed9a7f /hw/misc/iotkit-secctl.c
parent2e7b766594e17f786a6b2e5be690bc5b43ce6036 (diff)
parente66a67bf28e1b4fce2e3d72a2610dbd48d9d3078 (diff)
downloadfocaccia-qemu-86f4c7e05b1c44dbe1b329a51f311f10aef6ff34.tar.gz
focaccia-qemu-86f4c7e05b1c44dbe1b329a51f311f10aef6ff34.zip
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20180302' into staging
target-arm queue:
 * implement FCMA and RDM v8.1 and v8.3 instructions
 * enable Cortex-M33 v8M core, and provide new mps2-an505 board model
   that uses it
 * decodetree: Propagate return value from translate subroutines
 * xlnx-zynqmp: Implement the RTC device

# gpg: Signature made Fri 02 Mar 2018 11:05:40 GMT
# gpg:                using RSA key 3C2525ED14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>"
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* remotes/pmaydell/tags/pull-target-arm-20180302: (39 commits)
  target/arm: Enable ARM_FEATURE_V8_FCMA
  target/arm: Decode t32 simd 3reg and 2reg_scalar extension
  target/arm: Decode aa32 armv8.3 2-reg-index
  target/arm: Decode aa32 armv8.3 3-same
  target/arm: Decode aa64 armv8.3 fcmla
  target/arm: Decode aa64 armv8.3 fcadd
  target/arm: Add ARM_FEATURE_V8_FCMA
  target/arm: Enable ARM_FEATURE_V8_RDM
  target/arm: Decode aa32 armv8.1 two reg and a scalar
  target/arm: Decode aa32 armv8.1 three same
  target/arm: Decode aa64 armv8.1 scalar/vector x indexed element
  target/arm: Decode aa64 armv8.1 three same extra
  target/arm: Decode aa64 armv8.1 scalar three same extra
  target/arm: Refactor disas_simd_indexed size checks
  target/arm: Refactor disas_simd_indexed decode
  target/arm: Add ARM_FEATURE_V8_RDM
  mps2-an505: New board model: MPS2 with AN505 Cortex-M33 FPGA image
  hw/arm/iotkit: Model Arm IOT Kit
  hw/misc/iotkit-secctl: Add remaining simple registers
  hw/misc/iotkit-secctl: Add handling for PPCs
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/misc/iotkit-secctl.c')
-rw-r--r--hw/misc/iotkit-secctl.c704
1 files changed, 704 insertions, 0 deletions
diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c
new file mode 100644
index 0000000000..ddd1584d34
--- /dev/null
+++ b/hw/misc/iotkit-secctl.c
@@ -0,0 +1,704 @@
+/*
+ * Arm IoT Kit security controller
+ *
+ * Copyright (c) 2018 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/misc/iotkit-secctl.h"
+
+/* Registers in the secure privilege control block */
+REG32(SECRESPCFG, 0x10)
+REG32(NSCCFG, 0x14)
+REG32(SECMPCINTSTATUS, 0x1c)
+REG32(SECPPCINTSTAT, 0x20)
+REG32(SECPPCINTCLR, 0x24)
+REG32(SECPPCINTEN, 0x28)
+REG32(SECMSCINTSTAT, 0x30)
+REG32(SECMSCINTCLR, 0x34)
+REG32(SECMSCINTEN, 0x38)
+REG32(BRGINTSTAT, 0x40)
+REG32(BRGINTCLR, 0x44)
+REG32(BRGINTEN, 0x48)
+REG32(AHBNSPPC0, 0x50)
+REG32(AHBNSPPCEXP0, 0x60)
+REG32(AHBNSPPCEXP1, 0x64)
+REG32(AHBNSPPCEXP2, 0x68)
+REG32(AHBNSPPCEXP3, 0x6c)
+REG32(APBNSPPC0, 0x70)
+REG32(APBNSPPC1, 0x74)
+REG32(APBNSPPCEXP0, 0x80)
+REG32(APBNSPPCEXP1, 0x84)
+REG32(APBNSPPCEXP2, 0x88)
+REG32(APBNSPPCEXP3, 0x8c)
+REG32(AHBSPPPC0, 0x90)
+REG32(AHBSPPPCEXP0, 0xa0)
+REG32(AHBSPPPCEXP1, 0xa4)
+REG32(AHBSPPPCEXP2, 0xa8)
+REG32(AHBSPPPCEXP3, 0xac)
+REG32(APBSPPPC0, 0xb0)
+REG32(APBSPPPC1, 0xb4)
+REG32(APBSPPPCEXP0, 0xc0)
+REG32(APBSPPPCEXP1, 0xc4)
+REG32(APBSPPPCEXP2, 0xc8)
+REG32(APBSPPPCEXP3, 0xcc)
+REG32(NSMSCEXP, 0xd0)
+REG32(PID4, 0xfd0)
+REG32(PID5, 0xfd4)
+REG32(PID6, 0xfd8)
+REG32(PID7, 0xfdc)
+REG32(PID0, 0xfe0)
+REG32(PID1, 0xfe4)
+REG32(PID2, 0xfe8)
+REG32(PID3, 0xfec)
+REG32(CID0, 0xff0)
+REG32(CID1, 0xff4)
+REG32(CID2, 0xff8)
+REG32(CID3, 0xffc)
+
+/* Registers in the non-secure privilege control block */
+REG32(AHBNSPPPC0, 0x90)
+REG32(AHBNSPPPCEXP0, 0xa0)
+REG32(AHBNSPPPCEXP1, 0xa4)
+REG32(AHBNSPPPCEXP2, 0xa8)
+REG32(AHBNSPPPCEXP3, 0xac)
+REG32(APBNSPPPC0, 0xb0)
+REG32(APBNSPPPC1, 0xb4)
+REG32(APBNSPPPCEXP0, 0xc0)
+REG32(APBNSPPPCEXP1, 0xc4)
+REG32(APBNSPPPCEXP2, 0xc8)
+REG32(APBNSPPPCEXP3, 0xcc)
+/* PID and CID registers are also present in the NS block */
+
+static const uint8_t iotkit_secctl_s_idregs[] = {
+    0x04, 0x00, 0x00, 0x00,
+    0x52, 0xb8, 0x0b, 0x00,
+    0x0d, 0xf0, 0x05, 0xb1,
+};
+
+static const uint8_t iotkit_secctl_ns_idregs[] = {
+    0x04, 0x00, 0x00, 0x00,
+    0x53, 0xb8, 0x0b, 0x00,
+    0x0d, 0xf0, 0x05, 0xb1,
+};
+
+/* The register sets for the various PPCs (AHB internal, APB internal,
+ * AHB expansion, APB expansion) are all set up so that they are
+ * in 16-aligned blocks so offsets 0xN0, 0xN4, 0xN8, 0xNC are PPCs
+ * 0, 1, 2, 3 of that type, so we can convert a register address offset
+ * into an an index into a PPC array easily.
+ */
+static inline int offset_to_ppc_idx(uint32_t offset)
+{
+    return extract32(offset, 2, 2);
+}
+
+typedef void PerPPCFunction(IoTKitSecCtlPPC *ppc);
+
+static void foreach_ppc(IoTKitSecCtl *s, PerPPCFunction *fn)
+{
+    int i;
+
+    for (i = 0; i < IOTS_NUM_APB_PPC; i++) {
+        fn(&s->apb[i]);
+    }
+    for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) {
+        fn(&s->apbexp[i]);
+    }
+    for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) {
+        fn(&s->ahbexp[i]);
+    }
+}
+
+static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr,
+                                        uint64_t *pdata,
+                                        unsigned size, MemTxAttrs attrs)
+{
+    uint64_t r;
+    uint32_t offset = addr & ~0x3;
+    IoTKitSecCtl *s = IOTKIT_SECCTL(opaque);
+
+    switch (offset) {
+    case A_AHBNSPPC0:
+    case A_AHBSPPPC0:
+        r = 0;
+        break;
+    case A_SECRESPCFG:
+        r = s->secrespcfg;
+        break;
+    case A_NSCCFG:
+        r = s->nsccfg;
+        break;
+    case A_SECPPCINTSTAT:
+        r = s->secppcintstat;
+        break;
+    case A_SECPPCINTEN:
+        r = s->secppcinten;
+        break;
+    case A_BRGINTSTAT:
+        /* QEMU's bus fabric can never report errors as it doesn't buffer
+         * writes, so we never report bridge interrupts.
+         */
+        r = 0;
+        break;
+    case A_BRGINTEN:
+        r = s->brginten;
+        break;
+    case A_AHBNSPPCEXP0:
+    case A_AHBNSPPCEXP1:
+    case A_AHBNSPPCEXP2:
+    case A_AHBNSPPCEXP3:
+        r = s->ahbexp[offset_to_ppc_idx(offset)].ns;
+        break;
+    case A_APBNSPPC0:
+    case A_APBNSPPC1:
+        r = s->apb[offset_to_ppc_idx(offset)].ns;
+        break;
+    case A_APBNSPPCEXP0:
+    case A_APBNSPPCEXP1:
+    case A_APBNSPPCEXP2:
+    case A_APBNSPPCEXP3:
+        r = s->apbexp[offset_to_ppc_idx(offset)].ns;
+        break;
+    case A_AHBSPPPCEXP0:
+    case A_AHBSPPPCEXP1:
+    case A_AHBSPPPCEXP2:
+    case A_AHBSPPPCEXP3:
+        r = s->apbexp[offset_to_ppc_idx(offset)].sp;
+        break;
+    case A_APBSPPPC0:
+    case A_APBSPPPC1:
+        r = s->apb[offset_to_ppc_idx(offset)].sp;
+        break;
+    case A_APBSPPPCEXP0:
+    case A_APBSPPPCEXP1:
+    case A_APBSPPPCEXP2:
+    case A_APBSPPPCEXP3:
+        r = s->apbexp[offset_to_ppc_idx(offset)].sp;
+        break;
+    case A_SECMPCINTSTATUS:
+    case A_SECMSCINTSTAT:
+    case A_SECMSCINTEN:
+    case A_NSMSCEXP:
+        qemu_log_mask(LOG_UNIMP,
+                      "IoTKit SecCtl S block read: "
+                      "unimplemented offset 0x%x\n", offset);
+        r = 0;
+        break;
+    case A_PID4:
+    case A_PID5:
+    case A_PID6:
+    case A_PID7:
+    case A_PID0:
+    case A_PID1:
+    case A_PID2:
+    case A_PID3:
+    case A_CID0:
+    case A_CID1:
+    case A_CID2:
+    case A_CID3:
+        r = iotkit_secctl_s_idregs[(offset - A_PID4) / 4];
+        break;
+    case A_SECPPCINTCLR:
+    case A_SECMSCINTCLR:
+    case A_BRGINTCLR:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "IotKit SecCtl S block read: write-only offset 0x%x\n",
+                      offset);
+        r = 0;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "IotKit SecCtl S block read: bad offset 0x%x\n", offset);
+        r = 0;
+        break;
+    }
+
+    if (size != 4) {
+        /* None of our registers are access-sensitive, so just pull the right
+         * byte out of the word read result.
+         */
+        r = extract32(r, (addr & 3) * 8, size * 8);
+    }
+
+    trace_iotkit_secctl_s_read(offset, r, size);
+    *pdata = r;
+    return MEMTX_OK;
+}
+
+static void iotkit_secctl_update_ppc_ap(IoTKitSecCtlPPC *ppc)
+{
+    int i;
+
+    for (i = 0; i < ppc->numports; i++) {
+        bool v;
+
+        if (extract32(ppc->ns, i, 1)) {
+            v = extract32(ppc->nsp, i, 1);
+        } else {
+            v = extract32(ppc->sp, i, 1);
+        }
+        qemu_set_irq(ppc->ap[i], v);
+    }
+}
+
+static void iotkit_secctl_ppc_ns_write(IoTKitSecCtlPPC *ppc, uint32_t value)
+{
+    int i;
+
+    ppc->ns = value & MAKE_64BIT_MASK(0, ppc->numports);
+    for (i = 0; i < ppc->numports; i++) {
+        qemu_set_irq(ppc->nonsec[i], extract32(ppc->ns, i, 1));
+    }
+    iotkit_secctl_update_ppc_ap(ppc);
+}
+
+static void iotkit_secctl_ppc_sp_write(IoTKitSecCtlPPC *ppc, uint32_t value)
+{
+    ppc->sp = value & MAKE_64BIT_MASK(0, ppc->numports);
+    iotkit_secctl_update_ppc_ap(ppc);
+}
+
+static void iotkit_secctl_ppc_nsp_write(IoTKitSecCtlPPC *ppc, uint32_t value)
+{
+    ppc->nsp = value & MAKE_64BIT_MASK(0, ppc->numports);
+    iotkit_secctl_update_ppc_ap(ppc);
+}
+
+static void iotkit_secctl_ppc_update_irq_clear(IoTKitSecCtlPPC *ppc)
+{
+    uint32_t value = ppc->parent->secppcintstat;
+
+    qemu_set_irq(ppc->irq_clear, extract32(value, ppc->irq_bit_offset, 1));
+}
+
+static void iotkit_secctl_ppc_update_irq_enable(IoTKitSecCtlPPC *ppc)
+{
+    uint32_t value = ppc->parent->secppcinten;
+
+    qemu_set_irq(ppc->irq_enable, extract32(value, ppc->irq_bit_offset, 1));
+}
+
+static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr,
+                                         uint64_t value,
+                                         unsigned size, MemTxAttrs attrs)
+{
+    IoTKitSecCtl *s = IOTKIT_SECCTL(opaque);
+    uint32_t offset = addr;
+    IoTKitSecCtlPPC *ppc;
+
+    trace_iotkit_secctl_s_write(offset, value, size);
+
+    if (size != 4) {
+        /* Byte and halfword writes are ignored */
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "IotKit SecCtl S block write: bad size, ignored\n");
+        return MEMTX_OK;
+    }
+
+    switch (offset) {
+    case A_NSCCFG:
+        s->nsccfg = value & 3;
+        qemu_set_irq(s->nsc_cfg_irq, s->nsccfg);
+        break;
+    case A_SECRESPCFG:
+        value &= 1;
+        s->secrespcfg = value;
+        qemu_set_irq(s->sec_resp_cfg, s->secrespcfg);
+        break;
+    case A_SECPPCINTCLR:
+        value &= 0x00f000f3;
+        foreach_ppc(s, iotkit_secctl_ppc_update_irq_clear);
+        break;
+    case A_SECPPCINTEN:
+        s->secppcinten = value & 0x00f000f3;
+        foreach_ppc(s, iotkit_secctl_ppc_update_irq_enable);
+        break;
+    case A_BRGINTCLR:
+        break;
+    case A_BRGINTEN:
+        s->brginten = value & 0xffff0000;
+        break;
+    case A_AHBNSPPCEXP0:
+    case A_AHBNSPPCEXP1:
+    case A_AHBNSPPCEXP2:
+    case A_AHBNSPPCEXP3:
+        ppc = &s->ahbexp[offset_to_ppc_idx(offset)];
+        iotkit_secctl_ppc_ns_write(ppc, value);
+        break;
+    case A_APBNSPPC0:
+    case A_APBNSPPC1:
+        ppc = &s->apb[offset_to_ppc_idx(offset)];
+        iotkit_secctl_ppc_ns_write(ppc, value);
+        break;
+    case A_APBNSPPCEXP0:
+    case A_APBNSPPCEXP1:
+    case A_APBNSPPCEXP2:
+    case A_APBNSPPCEXP3:
+        ppc = &s->apbexp[offset_to_ppc_idx(offset)];
+        iotkit_secctl_ppc_ns_write(ppc, value);
+        break;
+    case A_AHBSPPPCEXP0:
+    case A_AHBSPPPCEXP1:
+    case A_AHBSPPPCEXP2:
+    case A_AHBSPPPCEXP3:
+        ppc = &s->ahbexp[offset_to_ppc_idx(offset)];
+        iotkit_secctl_ppc_sp_write(ppc, value);
+        break;
+    case A_APBSPPPC0:
+    case A_APBSPPPC1:
+        ppc = &s->apb[offset_to_ppc_idx(offset)];
+        iotkit_secctl_ppc_sp_write(ppc, value);
+        break;
+    case A_APBSPPPCEXP0:
+    case A_APBSPPPCEXP1:
+    case A_APBSPPPCEXP2:
+    case A_APBSPPPCEXP3:
+        ppc = &s->apbexp[offset_to_ppc_idx(offset)];
+        iotkit_secctl_ppc_sp_write(ppc, value);
+        break;
+    case A_SECMSCINTCLR:
+    case A_SECMSCINTEN:
+        qemu_log_mask(LOG_UNIMP,
+                      "IoTKit SecCtl S block write: "
+                      "unimplemented offset 0x%x\n", offset);
+        break;
+    case A_SECMPCINTSTATUS:
+    case A_SECPPCINTSTAT:
+    case A_SECMSCINTSTAT:
+    case A_BRGINTSTAT:
+    case A_AHBNSPPC0:
+    case A_AHBSPPPC0:
+    case A_NSMSCEXP:
+    case A_PID4:
+    case A_PID5:
+    case A_PID6:
+    case A_PID7:
+    case A_PID0:
+    case A_PID1:
+    case A_PID2:
+    case A_PID3:
+    case A_CID0:
+    case A_CID1:
+    case A_CID2:
+    case A_CID3:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "IoTKit SecCtl S block write: "
+                      "read-only offset 0x%x\n", offset);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "IotKit SecCtl S block write: bad offset 0x%x\n",
+                      offset);
+        break;
+    }
+
+    return MEMTX_OK;
+}
+
+static MemTxResult iotkit_secctl_ns_read(void *opaque, hwaddr addr,
+                                         uint64_t *pdata,
+                                         unsigned size, MemTxAttrs attrs)
+{
+    IoTKitSecCtl *s = IOTKIT_SECCTL(opaque);
+    uint64_t r;
+    uint32_t offset = addr & ~0x3;
+
+    switch (offset) {
+    case A_AHBNSPPPC0:
+        r = 0;
+        break;
+    case A_AHBNSPPPCEXP0:
+    case A_AHBNSPPPCEXP1:
+    case A_AHBNSPPPCEXP2:
+    case A_AHBNSPPPCEXP3:
+        r = s->ahbexp[offset_to_ppc_idx(offset)].nsp;
+        break;
+    case A_APBNSPPPC0:
+    case A_APBNSPPPC1:
+        r = s->apb[offset_to_ppc_idx(offset)].nsp;
+        break;
+    case A_APBNSPPPCEXP0:
+    case A_APBNSPPPCEXP1:
+    case A_APBNSPPPCEXP2:
+    case A_APBNSPPPCEXP3:
+        r = s->apbexp[offset_to_ppc_idx(offset)].nsp;
+        break;
+    case A_PID4:
+    case A_PID5:
+    case A_PID6:
+    case A_PID7:
+    case A_PID0:
+    case A_PID1:
+    case A_PID2:
+    case A_PID3:
+    case A_CID0:
+    case A_CID1:
+    case A_CID2:
+    case A_CID3:
+        r = iotkit_secctl_ns_idregs[(offset - A_PID4) / 4];
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "IotKit SecCtl NS block write: bad offset 0x%x\n",
+                      offset);
+        r = 0;
+        break;
+    }
+
+    if (size != 4) {
+        /* None of our registers are access-sensitive, so just pull the right
+         * byte out of the word read result.
+         */
+        r = extract32(r, (addr & 3) * 8, size * 8);
+    }
+
+    trace_iotkit_secctl_ns_read(offset, r, size);
+    *pdata = r;
+    return MEMTX_OK;
+}
+
+static MemTxResult iotkit_secctl_ns_write(void *opaque, hwaddr addr,
+                                          uint64_t value,
+                                          unsigned size, MemTxAttrs attrs)
+{
+    IoTKitSecCtl *s = IOTKIT_SECCTL(opaque);
+    uint32_t offset = addr;
+    IoTKitSecCtlPPC *ppc;
+
+    trace_iotkit_secctl_ns_write(offset, value, size);
+
+    if (size != 4) {
+        /* Byte and halfword writes are ignored */
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "IotKit SecCtl NS block write: bad size, ignored\n");
+        return MEMTX_OK;
+    }
+
+    switch (offset) {
+    case A_AHBNSPPPCEXP0:
+    case A_AHBNSPPPCEXP1:
+    case A_AHBNSPPPCEXP2:
+    case A_AHBNSPPPCEXP3:
+        ppc = &s->ahbexp[offset_to_ppc_idx(offset)];
+        iotkit_secctl_ppc_nsp_write(ppc, value);
+        break;
+    case A_APBNSPPPC0:
+    case A_APBNSPPPC1:
+        ppc = &s->apb[offset_to_ppc_idx(offset)];
+        iotkit_secctl_ppc_nsp_write(ppc, value);
+        break;
+    case A_APBNSPPPCEXP0:
+    case A_APBNSPPPCEXP1:
+    case A_APBNSPPPCEXP2:
+    case A_APBNSPPPCEXP3:
+        ppc = &s->apbexp[offset_to_ppc_idx(offset)];
+        iotkit_secctl_ppc_nsp_write(ppc, value);
+        break;
+    case A_AHBNSPPPC0:
+    case A_PID4:
+    case A_PID5:
+    case A_PID6:
+    case A_PID7:
+    case A_PID0:
+    case A_PID1:
+    case A_PID2:
+    case A_PID3:
+    case A_CID0:
+    case A_CID1:
+    case A_CID2:
+    case A_CID3:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "IoTKit SecCtl NS block write: "
+                      "read-only offset 0x%x\n", offset);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "IotKit SecCtl NS block write: bad offset 0x%x\n",
+                      offset);
+        break;
+    }
+
+    return MEMTX_OK;
+}
+
+static const MemoryRegionOps iotkit_secctl_s_ops = {
+    .read_with_attrs = iotkit_secctl_s_read,
+    .write_with_attrs = iotkit_secctl_s_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 4,
+};
+
+static const MemoryRegionOps iotkit_secctl_ns_ops = {
+    .read_with_attrs = iotkit_secctl_ns_read,
+    .write_with_attrs = iotkit_secctl_ns_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 4,
+};
+
+static void iotkit_secctl_reset_ppc(IoTKitSecCtlPPC *ppc)
+{
+    ppc->ns = 0;
+    ppc->sp = 0;
+    ppc->nsp = 0;
+}
+
+static void iotkit_secctl_reset(DeviceState *dev)
+{
+    IoTKitSecCtl *s = IOTKIT_SECCTL(dev);
+
+    s->secppcintstat = 0;
+    s->secppcinten = 0;
+    s->secrespcfg = 0;
+    s->nsccfg = 0;
+    s->brginten = 0;
+
+    foreach_ppc(s, iotkit_secctl_reset_ppc);
+}
+
+static void iotkit_secctl_ppc_irqstatus(void *opaque, int n, int level)
+{
+    IoTKitSecCtlPPC *ppc = opaque;
+    IoTKitSecCtl *s = IOTKIT_SECCTL(ppc->parent);
+    int irqbit = ppc->irq_bit_offset + n;
+
+    s->secppcintstat = deposit32(s->secppcintstat, irqbit, 1, level);
+}
+
+static void iotkit_secctl_init_ppc(IoTKitSecCtl *s,
+                                   IoTKitSecCtlPPC *ppc,
+                                   const char *name,
+                                   int numports,
+                                   int irq_bit_offset)
+{
+    char *gpioname;
+    DeviceState *dev = DEVICE(s);
+
+    ppc->numports = numports;
+    ppc->irq_bit_offset = irq_bit_offset;
+    ppc->parent = s;
+
+    gpioname = g_strdup_printf("%s_nonsec", name);
+    qdev_init_gpio_out_named(dev, ppc->nonsec, gpioname, numports);
+    g_free(gpioname);
+    gpioname = g_strdup_printf("%s_ap", name);
+    qdev_init_gpio_out_named(dev, ppc->ap, gpioname, numports);
+    g_free(gpioname);
+    gpioname = g_strdup_printf("%s_irq_enable", name);
+    qdev_init_gpio_out_named(dev, &ppc->irq_enable, gpioname, 1);
+    g_free(gpioname);
+    gpioname = g_strdup_printf("%s_irq_clear", name);
+    qdev_init_gpio_out_named(dev, &ppc->irq_clear, gpioname, 1);
+    g_free(gpioname);
+    gpioname = g_strdup_printf("%s_irq_status", name);
+    qdev_init_gpio_in_named_with_opaque(dev, iotkit_secctl_ppc_irqstatus,
+                                        ppc, gpioname, 1);
+    g_free(gpioname);
+}
+
+static void iotkit_secctl_init(Object *obj)
+{
+    IoTKitSecCtl *s = IOTKIT_SECCTL(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    DeviceState *dev = DEVICE(obj);
+    int i;
+
+    iotkit_secctl_init_ppc(s, &s->apb[0], "apb_ppc0",
+                           IOTS_APB_PPC0_NUM_PORTS, 0);
+    iotkit_secctl_init_ppc(s, &s->apb[1], "apb_ppc1",
+                           IOTS_APB_PPC1_NUM_PORTS, 1);
+
+    for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) {
+        IoTKitSecCtlPPC *ppc = &s->apbexp[i];
+        char *ppcname = g_strdup_printf("apb_ppcexp%d", i);
+        iotkit_secctl_init_ppc(s, ppc, ppcname, IOTS_PPC_NUM_PORTS, 4 + i);
+        g_free(ppcname);
+    }
+    for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) {
+        IoTKitSecCtlPPC *ppc = &s->ahbexp[i];
+        char *ppcname = g_strdup_printf("ahb_ppcexp%d", i);
+        iotkit_secctl_init_ppc(s, ppc, ppcname, IOTS_PPC_NUM_PORTS, 20 + i);
+        g_free(ppcname);
+    }
+
+    qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1);
+    qdev_init_gpio_out_named(dev, &s->nsc_cfg_irq, "nsc_cfg", 1);
+
+    memory_region_init_io(&s->s_regs, obj, &iotkit_secctl_s_ops,
+                          s, "iotkit-secctl-s-regs", 0x1000);
+    memory_region_init_io(&s->ns_regs, obj, &iotkit_secctl_ns_ops,
+                          s, "iotkit-secctl-ns-regs", 0x1000);
+    sysbus_init_mmio(sbd, &s->s_regs);
+    sysbus_init_mmio(sbd, &s->ns_regs);
+}
+
+static const VMStateDescription iotkit_secctl_ppc_vmstate = {
+    .name = "iotkit-secctl-ppc",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(ns, IoTKitSecCtlPPC),
+        VMSTATE_UINT32(sp, IoTKitSecCtlPPC),
+        VMSTATE_UINT32(nsp, IoTKitSecCtlPPC),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription iotkit_secctl_vmstate = {
+    .name = "iotkit-secctl",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(secppcintstat, IoTKitSecCtl),
+        VMSTATE_UINT32(secppcinten, IoTKitSecCtl),
+        VMSTATE_UINT32(secrespcfg, IoTKitSecCtl),
+        VMSTATE_UINT32(nsccfg, IoTKitSecCtl),
+        VMSTATE_UINT32(brginten, IoTKitSecCtl),
+        VMSTATE_STRUCT_ARRAY(apb, IoTKitSecCtl, IOTS_NUM_APB_PPC, 1,
+                             iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC),
+        VMSTATE_STRUCT_ARRAY(apbexp, IoTKitSecCtl, IOTS_NUM_APB_EXP_PPC, 1,
+                             iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC),
+        VMSTATE_STRUCT_ARRAY(ahbexp, IoTKitSecCtl, IOTS_NUM_AHB_EXP_PPC, 1,
+                             iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void iotkit_secctl_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd = &iotkit_secctl_vmstate;
+    dc->reset = iotkit_secctl_reset;
+}
+
+static const TypeInfo iotkit_secctl_info = {
+    .name = TYPE_IOTKIT_SECCTL,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(IoTKitSecCtl),
+    .instance_init = iotkit_secctl_init,
+    .class_init = iotkit_secctl_class_init,
+};
+
+static void iotkit_secctl_register_types(void)
+{
+    type_register_static(&iotkit_secctl_info);
+}
+
+type_init(iotkit_secctl_register_types);