|  | /* | 
|  | * 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 "qemu/module.h" | 
|  | #include "qapi/error.h" | 
|  | #include "trace.h" | 
|  | #include "hw/sysbus.h" | 
|  | #include "migration/vmstate.h" | 
|  | #include "hw/registerfields.h" | 
|  | #include "hw/irq.h" | 
|  | #include "hw/misc/iotkit-secctl.h" | 
|  | #include "hw/arm/armsse-version.h" | 
|  | #include "hw/qdev-properties.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, | 
|  | }; | 
|  |  | 
|  | static const uint8_t iotkit_secctl_s_sse300_idregs[] = { | 
|  | 0x04, 0x00, 0x00, 0x00, | 
|  | 0x52, 0xb8, 0x2b, 0x00, | 
|  | 0x0d, 0xf0, 0x05, 0xb1, | 
|  | }; | 
|  |  | 
|  | static const uint8_t iotkit_secctl_ns_sse300_idregs[] = { | 
|  | 0x04, 0x00, 0x00, 0x00, | 
|  | 0x53, 0xb8, 0x2b, 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 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_SECMPCINTSTATUS: | 
|  | r = s->mpcintstatus; | 
|  | 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_SECMSCINTSTAT: | 
|  | r = s->secmscintstat; | 
|  | break; | 
|  | case A_SECMSCINTEN: | 
|  | r = s->secmscinten; | 
|  | break; | 
|  | case A_NSMSCEXP: | 
|  | r = s->nsmscexp; | 
|  | 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: | 
|  | switch (s->sse_version) { | 
|  | case ARMSSE_SSE300: | 
|  | r = iotkit_secctl_s_sse300_idregs[(offset - A_PID4) / 4]; | 
|  | break; | 
|  | default: | 
|  | r = iotkit_secctl_s_idregs[(offset - A_PID4) / 4]; | 
|  | break; | 
|  | } | 
|  | 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 void iotkit_secctl_update_mscexp_irqs(qemu_irq *msc_irqs, uint32_t value) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < IOTS_NUM_EXP_MSC; i++) { | 
|  | qemu_set_irq(msc_irqs[i], extract32(value, i + 16, 1)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void iotkit_secctl_update_msc_irq(IoTKitSecCtl *s) | 
|  | { | 
|  | /* Update the combined MSC IRQ, based on S_MSCEXP_STATUS and S_MSCEXP_EN */ | 
|  | bool level = s->secmscintstat & s->secmscinten; | 
|  |  | 
|  | qemu_set_irq(s->msc_irq, level); | 
|  | } | 
|  |  | 
|  | 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: | 
|  | s->secppcintstat &= ~(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: | 
|  | iotkit_secctl_update_mscexp_irqs(s->mscexp_clear, value); | 
|  | break; | 
|  | case A_SECMSCINTEN: | 
|  | s->secmscinten = value; | 
|  | iotkit_secctl_update_msc_irq(s); | 
|  | break; | 
|  | case A_NSMSCEXP: | 
|  | s->nsmscexp = value; | 
|  | iotkit_secctl_update_mscexp_irqs(s->mscexp_ns, value); | 
|  | break; | 
|  | case A_SECMPCINTSTATUS: | 
|  | case A_SECPPCINTSTAT: | 
|  | case A_SECMSCINTSTAT: | 
|  | case A_BRGINTSTAT: | 
|  | case A_AHBNSPPC0: | 
|  | case A_AHBSPPPC0: | 
|  | 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: | 
|  | switch (s->sse_version) { | 
|  | case ARMSSE_SSE300: | 
|  | r = iotkit_secctl_ns_sse300_idregs[(offset - A_PID4) / 4]; | 
|  | break; | 
|  | default: | 
|  | r = iotkit_secctl_ns_idregs[(offset - A_PID4) / 4]; | 
|  | break; | 
|  | } | 
|  | 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_mpc_status(void *opaque, int n, int level) | 
|  | { | 
|  | IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); | 
|  |  | 
|  | s->mpcintstatus = deposit32(s->mpcintstatus, n, 1, !!level); | 
|  | } | 
|  |  | 
|  | static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level) | 
|  | { | 
|  | IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); | 
|  |  | 
|  | s->mpcintstatus = deposit32(s->mpcintstatus, n + 16, 1, !!level); | 
|  | } | 
|  |  | 
|  | static void iotkit_secctl_mscexp_status(void *opaque, int n, int level) | 
|  | { | 
|  | IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); | 
|  |  | 
|  | s->secmscintstat = deposit32(s->secmscintstat, n + 16, 1, !!level); | 
|  | iotkit_secctl_update_msc_irq(s); | 
|  | } | 
|  |  | 
|  | 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); | 
|  |  | 
|  | 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); | 
|  |  | 
|  | qdev_init_gpio_in_named(dev, iotkit_secctl_mscexp_status, | 
|  | "mscexp_status", IOTS_NUM_EXP_MSC); | 
|  | qdev_init_gpio_out_named(dev, s->mscexp_clear, "mscexp_clear", | 
|  | IOTS_NUM_EXP_MSC); | 
|  | qdev_init_gpio_out_named(dev, s->mscexp_ns, "mscexp_ns", | 
|  | IOTS_NUM_EXP_MSC); | 
|  | qdev_init_gpio_out_named(dev, &s->msc_irq, "msc_irq", 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 void iotkit_secctl_realize(DeviceState *dev, Error **errp) | 
|  | { | 
|  | IoTKitSecCtl *s = IOTKIT_SECCTL(dev); | 
|  |  | 
|  | if (!armsse_version_valid(s->sse_version)) { | 
|  | error_setg(errp, "invalid sse-version value %d", s->sse_version); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | static const VMStateDescription iotkit_secctl_ppc_vmstate = { | 
|  | .name = "iotkit-secctl-ppc", | 
|  | .version_id = 1, | 
|  | .minimum_version_id = 1, | 
|  | .fields = (const VMStateField[]) { | 
|  | VMSTATE_UINT32(ns, IoTKitSecCtlPPC), | 
|  | VMSTATE_UINT32(sp, IoTKitSecCtlPPC), | 
|  | VMSTATE_UINT32(nsp, IoTKitSecCtlPPC), | 
|  | VMSTATE_END_OF_LIST() | 
|  | } | 
|  | }; | 
|  |  | 
|  | static const VMStateDescription iotkit_secctl_mpcintstatus_vmstate = { | 
|  | .name = "iotkit-secctl-mpcintstatus", | 
|  | .version_id = 1, | 
|  | .minimum_version_id = 1, | 
|  | .fields = (const VMStateField[]) { | 
|  | VMSTATE_UINT32(mpcintstatus, IoTKitSecCtl), | 
|  | VMSTATE_END_OF_LIST() | 
|  | } | 
|  | }; | 
|  |  | 
|  | static bool needed_always(void *opaque) | 
|  | { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static const VMStateDescription iotkit_secctl_msc_vmstate = { | 
|  | .name = "iotkit-secctl/msc", | 
|  | .version_id = 1, | 
|  | .minimum_version_id = 1, | 
|  | .needed = needed_always, | 
|  | .fields = (const VMStateField[]) { | 
|  | VMSTATE_UINT32(secmscintstat, IoTKitSecCtl), | 
|  | VMSTATE_UINT32(secmscinten, IoTKitSecCtl), | 
|  | VMSTATE_UINT32(nsmscexp, IoTKitSecCtl), | 
|  | VMSTATE_END_OF_LIST() | 
|  | } | 
|  | }; | 
|  |  | 
|  | static const VMStateDescription iotkit_secctl_vmstate = { | 
|  | .name = "iotkit-secctl", | 
|  | .version_id = 1, | 
|  | .minimum_version_id = 1, | 
|  | .fields = (const 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() | 
|  | }, | 
|  | .subsections = (const VMStateDescription * const []) { | 
|  | &iotkit_secctl_mpcintstatus_vmstate, | 
|  | &iotkit_secctl_msc_vmstate, | 
|  | NULL | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static Property iotkit_secctl_props[] = { | 
|  | DEFINE_PROP_UINT32("sse-version", IoTKitSecCtl, sse_version, 0), | 
|  | DEFINE_PROP_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; | 
|  | dc->realize = iotkit_secctl_realize; | 
|  | device_class_set_props(dc, iotkit_secctl_props); | 
|  | } | 
|  |  | 
|  | 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); |