Add focused execution tests for the optional Armv6-M MPU exposed by the RP2040 Cortex-M0+ cores.
Check register reset and readback, privileged and unprivileged accesses, the 256-byte minimum region size, overlapping-region priority, subregion disabling, and escalation of an MPU violation to HardFault. The test runs after the generic Armv6-M CONTROL.nPRIV prerequisite and the RP2040 machine have both been introduced. Signed-off-by: gilles grimaud <[email protected]> --- tests/tcg/arm/system/meson.build | 5 + tests/tcg/arm/system/rp2040-mpu.S | 356 ++++++++++++++++++++++++++++ tests/tcg/arm/system/rp2040-mpu.ref | 10 + 3 files changed, 371 insertions(+) create mode 100644 tests/tcg/arm/system/rp2040-mpu.S create mode 100644 tests/tcg/arm/system/rp2040-mpu.ref diff --git a/tests/tcg/arm/system/meson.build b/tests/tcg/arm/system/meson.build index 42e7614a11..a959830b1c 100644 --- a/tests/tcg/arm/system/meson.build +++ b/tests/tcg/arm/system/meson.build @@ -65,6 +65,11 @@ tests += { 'qemu_args': ['-serial', 'stdio', rp2040_qemu_args], 'expected_output': 'rp2040-uart.ref', }, + 'rp2040-mpu.S': { + 'cflags': rp2040_cflags, + 'qemu_args': ['-serial', 'stdio', rp2040_qemu_args], + 'expected_output': 'rp2040-mpu.ref', + }, 'semiconsole.c': { 'cflags': cflags, 'qemu_args': ['-serial', 'none', '-chardev', 'stdio,mux=on,id=stdio0', diff --git a/tests/tcg/arm/system/rp2040-mpu.S b/tests/tcg/arm/system/rp2040-mpu.S new file mode 100644 index 0000000000..5cc98f3d0e --- /dev/null +++ b/tests/tcg/arm/system/rp2040-mpu.S @@ -0,0 +1,356 @@ +/* + * Raspberry Pi Pico Cortex-M0+ MPU test. + * + * Copyright 2026 Gilles Grimaud + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +.syntax unified +.cpu cortex-m0plus +.thumb + +#define SRAM_END 0x20042000 +#define TEST_ADDRESS 0x20001000 +#define TEST_VALUE 0x12345678 +#define UART0_DR 0x40034000 +#define MPU_TYPE 0xe000ed90 +#define MPU_CTRL 0xe000ed94 +#define MPU_RNR 0xe000ed98 +#define MPU_RBAR 0xe000ed9c +#define MPU_RASR 0xe000eda0 +#define MPU_TYPE_DREGION_MASK 0x0000ff00 +#define MPU_TYPE_DREGION_SHIFT 8 +#define MPU_CTRL_ENABLE_PRIVDEFENA 0x05 +#define MPU_RASR_SRAM_FULL_ACCESS 0x03000025 +#define MPU_RASR_XIP_READ_ONLY 0x06000039 +#define MPU_RASR_TEST_PRIV_RW 0x0100000f +#define MPU_RASR_TEST_NO_ACCESS_32B 0x00000009 +#define MPU_RASR_TEST_NO_ACCESS_256B 0x0000000f +#define MPU_RASR_TEST_SUBREGION0_OFF 0x0000010f +#define SYS_EXIT 0x18 +#define ADP_STOPPED_APPLICATION_EXIT 0x20026 +#define ADP_STOPPED_RUNTIME_ERROR 0x20023 + +.section .vectors, "a", %progbits +vector_table: + .word SRAM_END + .word reset_handler + 1 + .word default_handler + 1 + .word hardfault_handler + 1 + +.section .text, "ax", %progbits +.thumb_func +.global reset_handler +reset_handler: + ldr r0, =start_message + bl puts + + ldr r0, =MPU_TYPE + ldr r0, [r0] + ldr r1, =MPU_TYPE_DREGION_MASK + ands r0, r1 + lsrs r0, r0, MPU_TYPE_DREGION_SHIFT + cmp r0, 8 + beq 1f + b fail_type +1: + + ldr r0, =type_message + bl puts + + ldr r0, =MPU_CTRL + ldr r1, [r0] + cmp r1, 0 + beq 1f + b fail_reset +1: + ldr r0, =MPU_RNR + ldr r1, [r0] + cmp r1, 0 + beq 1f + b fail_reset +1: + ldr r0, =MPU_RBAR + ldr r1, [r0] + cmp r1, 0 + beq 1f + b fail_reset +1: + ldr r0, =MPU_RASR + ldr r1, [r0] + cmp r1, 0 + beq 1f + b fail_reset +1: + + ldr r0, =reset_message + bl puts + + ldr r4, =TEST_ADDRESS + ldr r5, =TEST_VALUE + str r5, [r4] + ldr r0, [r4] + cmp r0, r5 + beq 1f + b fail_plain +1: + + ldr r0, =plain_message + bl puts + + movs r0, 0 + ldr r1, =0x20000000 + ldr r2, =MPU_RASR_SRAM_FULL_ACCESS + bl configure_region + + movs r0, 1 + movs r1, 0 + ldr r2, =MPU_RASR_XIP_READ_ONLY + bl configure_region + + movs r0, 2 + ldr r1, =TEST_ADDRESS + ldr r2, =MPU_RASR_TEST_PRIV_RW + bl configure_region + + ldr r0, =MPU_RNR + ldr r0, [r0] + cmp r0, 2 + beq 1f + b fail_registers +1: + ldr r0, =MPU_RBAR + ldr r0, [r0] + ldr r1, =(TEST_ADDRESS | 2) + cmp r0, r1 + beq 1f + b fail_registers +1: + ldr r0, =MPU_RASR + ldr r0, [r0] + ldr r1, =MPU_RASR_TEST_PRIV_RW + cmp r0, r1 + beq 1f + b fail_registers +1: + + ldr r0, =registers_message + bl puts + + ldr r0, =MPU_CTRL + movs r1, MPU_CTRL_ENABLE_PRIVDEFENA + str r1, [r0] + dsb + isb + + ldr r0, [r4] + cmp r0, r5 + beq 1f + b fail_privileged +1: + + ldr r0, =privileged_message + bl puts + + movs r0, 3 + ldr r1, =TEST_ADDRESS + ldr r2, =MPU_RASR_TEST_NO_ACCESS_32B + bl configure_region + dsb + isb + ldr r0, [r4] + cmp r0, r5 + beq 1f + b fail_min_size +1: + + ldr r0, =min_size_message + bl puts + + ldr r0, =fault_phase + movs r1, 1 + str r1, [r0] + movs r0, 3 + ldr r1, =TEST_ADDRESS + ldr r2, =MPU_RASR_TEST_NO_ACCESS_256B + bl configure_region + dsb + isb + ldr r0, [r4] + ldr r1, =fault_phase + ldr r1, [r1] + cmp r1, 0 + beq 1f + b fail_priority +1: + + ldr r0, =priority_message + bl puts + + movs r0, 3 + ldr r1, =TEST_ADDRESS + ldr r2, =MPU_RASR_TEST_SUBREGION0_OFF + bl configure_region + dsb + isb + ldr r0, [r4] + cmp r0, r5 + beq 1f + b fail_subregion +1: + + ldr r0, =subregion_message + bl puts + + movs r0, 1 + msr control, r0 + isb + + ldr r0, [r4] + ldr r0, =fail_no_fault_message + b test_failed + +.thumb_func +configure_region: + ldr r3, =MPU_RNR + str r0, [r3] + ldr r3, =MPU_RBAR + str r1, [r3] + ldr r3, =MPU_RASR + str r2, [r3] + bx lr + +fail_type: + ldr r0, =fail_type_message + b test_failed + +fail_reset: + ldr r0, =fail_reset_message + b test_failed + +fail_registers: + ldr r0, =fail_registers_message + b test_failed + +fail_plain: + ldr r0, =fail_plain_message + b test_failed + +fail_privileged: + ldr r0, =fail_privileged_message + b test_failed + +fail_min_size: + ldr r0, =fail_min_size_message + b test_failed + +fail_priority: + ldr r0, =fail_priority_message + b test_failed + +fail_subregion: + ldr r0, =fail_subregion_message + +test_failed: + bl puts + movs r0, SYS_EXIT + ldr r1, =ADP_STOPPED_RUNTIME_ERROR + bkpt 0xab + +1: + b 1b + +.thumb_func +hardfault_handler: + ldr r0, =fault_phase + ldr r1, [r0] + cmp r1, 0 + beq hardfault_done + movs r1, 0 + str r1, [r0] + ldr r0, =MPU_RNR + movs r1, 3 + str r1, [r0] + ldr r0, =MPU_RASR + movs r1, 0 + str r1, [r0] + dsb + isb + bx lr + +hardfault_done: + ldr r0, =ok_message + bl puts + movs r0, SYS_EXIT + ldr r1, =ADP_STOPPED_APPLICATION_EXIT + bkpt 0xab + +2: + b 2b + +.thumb_func +default_handler: + ldr r0, =fail_exception_message + b test_failed + +.thumb_func +puts: + ldr r2, =UART0_DR + +3: + ldrb r1, [r0] + cmp r1, 0 + beq 4f + str r1, [r2] + adds r0, 1 + b 3b + +4: + bx lr + +.section .rodata, "a", %progbits +start_message: + .asciz "PICO TCG MPU START\n" +type_message: + .asciz "PICO TCG MPU TYPE\n" +reset_message: + .asciz "PICO TCG MPU RESET\n" +plain_message: + .asciz "PICO TCG MPU PLAIN\n" +registers_message: + .asciz "PICO TCG MPU REGISTERS\n" +privileged_message: + .asciz "PICO TCG MPU PRIV\n" +min_size_message: + .asciz "PICO TCG MPU MIN SIZE\n" +priority_message: + .asciz "PICO TCG MPU PRIORITY\n" +subregion_message: + .asciz "PICO TCG MPU SUBREGION\n" +ok_message: + .asciz "PICO TCG MPU OK\n" +fail_type_message: + .asciz "PICO TCG MPU FAIL: TYPE\n" +fail_reset_message: + .asciz "PICO TCG MPU FAIL: RESET\n" +fail_plain_message: + .asciz "PICO TCG MPU FAIL: PLAIN\n" +fail_registers_message: + .asciz "PICO TCG MPU FAIL: REGISTERS\n" +fail_privileged_message: + .asciz "PICO TCG MPU FAIL: PRIV\n" +fail_min_size_message: + .asciz "PICO TCG MPU FAIL: MIN SIZE\n" +fail_priority_message: + .asciz "PICO TCG MPU FAIL: PRIORITY\n" +fail_subregion_message: + .asciz "PICO TCG MPU FAIL: SUBREGION\n" +fail_no_fault_message: + .asciz "PICO TCG MPU FAIL: NOFAULT\n" +fail_exception_message: + .asciz "PICO TCG MPU FAIL: EXCEPTION\n" + +.section .bss, "aw", %nobits +.align 2 +fault_phase: + .word 0 diff --git a/tests/tcg/arm/system/rp2040-mpu.ref b/tests/tcg/arm/system/rp2040-mpu.ref new file mode 100644 index 0000000000..d3938add02 --- /dev/null +++ b/tests/tcg/arm/system/rp2040-mpu.ref @@ -0,0 +1,10 @@ +PICO TCG MPU START +PICO TCG MPU TYPE +PICO TCG MPU RESET +PICO TCG MPU PLAIN +PICO TCG MPU REGISTERS +PICO TCG MPU PRIV +PICO TCG MPU MIN SIZE +PICO TCG MPU PRIORITY +PICO TCG MPU SUBREGION +PICO TCG MPU OK -- 2.55.0
