aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/intc/armv7m_nvic.c53
-rw-r--r--include/hw/intc/armv7m_nvic.h14
2 files changed, 66 insertions, 1 deletions
diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index d3e2056..8793f75 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -47,7 +47,7 @@
* For historical reasons QEMU tends to use "interrupt" and
* "exception" more or less interchangeably.
*/
-#define NVIC_FIRST_IRQ 16
+#define NVIC_FIRST_IRQ NVIC_INTERNAL_VECTORS
#define NVIC_MAX_IRQ (NVIC_MAX_VECTORS - NVIC_FIRST_IRQ)
/* Effective running priority of the CPU when no exception is active
@@ -1158,6 +1158,43 @@ static const VMStateDescription vmstate_VecInfo = {
}
};
+static bool nvic_security_needed(void *opaque)
+{
+ NVICState *s = opaque;
+
+ return arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY);
+}
+
+static int nvic_security_post_load(void *opaque, int version_id)
+{
+ NVICState *s = opaque;
+ int i;
+
+ /* Check for out of range priority settings */
+ if (s->sec_vectors[ARMV7M_EXCP_HARD].prio != -1) {
+ return 1;
+ }
+ for (i = ARMV7M_EXCP_MEM; i < ARRAY_SIZE(s->sec_vectors); i++) {
+ if (s->sec_vectors[i].prio & ~0xff) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static const VMStateDescription vmstate_nvic_security = {
+ .name = "nvic/m-security",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = nvic_security_needed,
+ .post_load = &nvic_security_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT_ARRAY(sec_vectors, NVICState, NVIC_INTERNAL_VECTORS, 1,
+ vmstate_VecInfo, VecInfo),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_nvic = {
.name = "armv7m_nvic",
.version_id = 4,
@@ -1168,6 +1205,10 @@ static const VMStateDescription vmstate_nvic = {
vmstate_VecInfo, VecInfo),
VMSTATE_UINT32(prigroup, NVICState),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_nvic_security,
+ NULL
}
};
@@ -1195,6 +1236,16 @@ static void armv7m_nvic_reset(DeviceState *dev)
s->vectors[ARMV7M_EXCP_NMI].prio = -2;
s->vectors[ARMV7M_EXCP_HARD].prio = -1;
+ if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY)) {
+ s->sec_vectors[ARMV7M_EXCP_HARD].enabled = 1;
+ s->sec_vectors[ARMV7M_EXCP_SVC].enabled = 1;
+ s->sec_vectors[ARMV7M_EXCP_PENDSV].enabled = 1;
+ s->sec_vectors[ARMV7M_EXCP_SYSTICK].enabled = 1;
+
+ /* AIRCR.BFHFNMINS resets to 0 so Secure HF is priority -1 (R_CMTC) */
+ s->sec_vectors[ARMV7M_EXCP_HARD].prio = -1;
+ }
+
/* Strictly speaking the reset handler should be enabled.
* However, we don't simulate soft resets through the NVIC,
* and the reset vector should never be pended.
diff --git a/include/hw/intc/armv7m_nvic.h b/include/hw/intc/armv7m_nvic.h
index 1a4cce7..317601e 100644
--- a/include/hw/intc/armv7m_nvic.h
+++ b/include/hw/intc/armv7m_nvic.h
@@ -21,6 +21,8 @@
/* Highest permitted number of exceptions (architectural limit) */
#define NVIC_MAX_VECTORS 512
+/* Number of internal exceptions */
+#define NVIC_INTERNAL_VECTORS 16
typedef struct VecInfo {
/* Exception priorities can range from -3 to 255; only the unmodifiable
@@ -41,6 +43,18 @@ typedef struct NVICState {
ARMCPU *cpu;
VecInfo vectors[NVIC_MAX_VECTORS];
+ /* If the v8M security extension is implemented, some of the internal
+ * exceptions are banked between security states (ie there exists both
+ * a Secure and a NonSecure version of the exception and its state):
+ * HardFault, MemManage, UsageFault, SVCall, PendSV, SysTick (R_PJHV)
+ * The rest (including all the external exceptions) are not banked, though
+ * they may be configurable to target either Secure or NonSecure state.
+ * We store the secure exception state in sec_vectors[] for the banked
+ * exceptions, and otherwise use only vectors[] (including for exceptions
+ * like SecureFault that unconditionally target Secure state).
+ * Entries in sec_vectors[] for non-banked exception numbers are unused.
+ */
+ VecInfo sec_vectors[NVIC_INTERNAL_VECTORS];
uint32_t prigroup;
/* vectpending and exception_prio are both cached state that can