81a75deb1a
The IoTKit does not have any Master Security Contollers itself, but it does provide registers in the secure privilege control block which allow control of MSCs in the external system. Add support for these registers. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Message-id: 20180820141116.9118-13-peter.maydell@linaro.org Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
796 lines
22 KiB
C
796 lines
22 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 "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_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:
|
|
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 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:
|
|
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:
|
|
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_mpc_status(void *opaque, int n, int level)
|
|
{
|
|
IoTKitSecCtl *s = IOTKIT_SECCTL(opaque);
|
|
|
|
s->mpcintstatus = deposit32(s->mpcintstatus, 0, 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", 1);
|
|
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 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 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);
|