Armv6-M supports no more than 32 external interrupts. Its ICTR and Active
Bit Register locations are reserved, and Cortex-M0 implements two priority
bits.

Reject Armv6-M NVIC configurations with more than 32 external interrupts.
Add a check-tcg test that verifies the reserved register behavior and
priority masking on the microbit machine.

Signed-off-by: Gilles Grimaud <[email protected]>
---
 hw/intc/armv7m_nvic.c                   |  7 +++
 tests/tcg/arm/system/meson.build        |  6 +++
 tests/tcg/arm/system/test-armv6m-nvic.S | 70 +++++++++++++++++++++++++
 3 files changed, 83 insertions(+)
 create mode 100644 tests/tcg/arm/system/test-armv6m-nvic.S

diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index a7651f831e..c1ceb7450f 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -2728,6 +2728,13 @@ static void armv7m_nvic_realize(DeviceState *dev, Error 
**errp)
         return;
     }
 
+    if (!arm_feature(&s->cpu->env, ARM_FEATURE_V7) &&
+        !arm_feature(&s->cpu->env, ARM_FEATURE_V8) &&
+        s->num_irq > 32) {
+        error_setg(errp, "Armv6-M NVIC cannot exceed 32 external IRQs");
+        return;
+    }
+
     qdev_init_gpio_in(dev, set_irq_level, s->num_irq);
 
     /* include space for internal exception vectors */
diff --git a/tests/tcg/arm/system/meson.build b/tests/tcg/arm/system/meson.build
index 4c77b9c3d6..5286997670 100644
--- a/tests/tcg/arm/system/meson.build
+++ b/tests/tcg/arm/system/meson.build
@@ -39,6 +39,12 @@ tests += {
                '-T', files('test-armv6m-undef.ld')],
     'qemu_args': ['-M', 'microbit', qemu_base_args],
   },
+  'test-armv6m-nvic.S': {
+    'cflags': ['-mcpu=cortex-m0', '-mfloat-abi=soft', '-nostdlib',
+               '-Wl,--build-id=none',
+               '-T', files('test-armv6m-undef.ld')],
+    'qemu_args': ['-M', 'microbit', qemu_base_args],
+  },
   'semiconsole.c': {
     'cflags': cflags,
     'qemu_args': ['-serial', 'none', '-chardev', 'stdio,mux=on,id=stdio0',
diff --git a/tests/tcg/arm/system/test-armv6m-nvic.S 
b/tests/tcg/arm/system/test-armv6m-nvic.S
new file mode 100644
index 0000000000..ecb29eaa3d
--- /dev/null
+++ b/tests/tcg/arm/system/test-armv6m-nvic.S
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Test Armv6-M NVIC register behavior. */
+
+.syntax unified
+.cpu cortex-m0
+.thumb
+
+#define SRAM_BASE 0x20000000
+#define SRAM_SIZE (16 * 1024)
+
+#define NVIC_ICTR  0xe000e004
+#define NVIC_IABR0 0xe000e300
+#define NVIC_IPR0  0xe000e400
+
+#define semihosting_call bkpt 0xab
+#define SYS_EXIT 0x18
+
+vector_table:
+    .word SRAM_BASE + SRAM_SIZE
+    .word reset + 1
+    .word 0
+    .word failure + 1
+    .rept 44
+    .word 0
+    .endr
+
+.equ exc_reset_thumb, reset + 1
+.global exc_reset_thumb
+reset:
+    /* ICTR is reserved and reads as zero on Armv6-M. */
+    ldr r0, =NVIC_ICTR
+    ldr r1, [r0]
+    cmp r1, 0
+    bne failure
+
+    /* The Active Bit Register is reserved and behaves as RAZ/WI. */
+    ldr r0, =NVIC_IABR0
+    movs r1, 1
+    str r1, [r0]
+    ldr r1, [r0]
+    cmp r1, 0
+    bne failure
+
+    /* Cortex-M0 implements the two most significant priority bits. */
+    ldr r0, =NVIC_IPR0
+    movs r1, 0xff
+    strb r1, [r0]
+    ldrb r1, [r0]
+    cmp r1, 0xc0
+    bne failure
+
+success:
+    movs r0, 1
+    b exit
+
+failure:
+    movs r0, 0
+
+exit:
+    movs r1, 0
+    cmp r0, 1
+    bne 1f
+    ldr r1, ADP_Stopped_ApplicationExit
+1:
+    movs r0, SYS_EXIT
+    semihosting_call
+
+.align 2
+ADP_Stopped_ApplicationExit:
+    .word 0x20026
-- 
2.55.0


Reply via email to