7a21bee2aa
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <20220707163720.1421716-5-berrange@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Thomas Huth <thuth@redhat.com>
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 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);
|