Commit d0ec544d6b for qemu.org
commit d0ec544d6b3c2ed01dc82f53e5898e12b6cafa5f
Author: Gilles Grimaud <gilles.grimaud@univ-lille.fr>
Date: Mon Sep 14 21:23:17 2026 +0200
target/arm: enforce Armv6-M NVIC limits
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 <gilles.grimaud@univ-lille.fr>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index a7651f831e..b1d27dfb32 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -2728,6 +2728,11 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
return;
}
+ if (!arm_feature(&s->cpu->env, ARM_FEATURE_V7) && 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