 0eb6b0ad16
			
		
	
	
		0eb6b0ad16
		
	
	
	
	
		
			
			The versions of the Secure Access Configuration Register Block and Non-secure Access Configuration Register Block in the SSE-300 are the same as those in the SSE-200, but the CIDR/PIDR ID register values are different. Plumb through the sse-version property and use it to select the correct ID register values. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20210219144617.4782-8-peter.maydell@linaro.org
		
			
				
	
	
		
			846 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			846 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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 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 = (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 = (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 = (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 = (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*[]) {
 | |
|         &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);
 |