Armv6-M implements CONTROL.nPRIV even though it does not implement the Main Extension. The M-profile MSR helper currently gates writes to nPRIV on ARM_FEATURE_M_MAIN, preventing Cortex-M0 CPUs from entering unprivileged Thread mode.
Allow privileged code to write nPRIV on every M-profile CPU. The translator already ends the translation block and rebuilds the execution flags after an M-profile MSR, so this does not require explicit TLB maintenance. Add a check-tcg test using the generic microbit machine. It verifies entry into unprivileged Thread mode, that unprivileged code cannot clear nPRIV, and that an exception handler can restore privileged Thread mode. Signed-off-by: Gilles Grimaud <[email protected]> --- target/arm/tcg/m_helper.c | 2 +- tests/tcg/arm/system/meson.build | 6 ++ tests/tcg/arm/system/test-armv6m-control.S | 85 ++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/arm/system/test-armv6m-control.S diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index 33c9e7c55b..0cd51c52ba 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -2775,7 +2775,7 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) !arm_v7m_is_handler_mode(env))) { write_v7m_control_spsel(env, (val & R_V7M_CONTROL_SPSEL_MASK) != 0); } - if (cur_el > 0 && arm_feature(env, ARM_FEATURE_M_MAIN)) { + if (cur_el > 0) { env->v7m.control[env->v7m.secure] &= ~R_V7M_CONTROL_NPRIV_MASK; env->v7m.control[env->v7m.secure] |= val & R_V7M_CONTROL_NPRIV_MASK; } diff --git a/tests/tcg/arm/system/meson.build b/tests/tcg/arm/system/meson.build index 5286997670..e92a83a8d5 100644 --- a/tests/tcg/arm/system/meson.build +++ b/tests/tcg/arm/system/meson.build @@ -45,6 +45,12 @@ tests += { '-T', files('test-armv6m-undef.ld')], 'qemu_args': ['-M', 'microbit', qemu_base_args], }, + 'test-armv6m-control.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-control.S b/tests/tcg/arm/system/test-armv6m-control.S new file mode 100644 index 0000000000..4142618a94 --- /dev/null +++ b/tests/tcg/arm/system/test-armv6m-control.S @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Test Armv6-M CONTROL.nPRIV behavior. */ + +.syntax unified +.cpu cortex-m0 +.thumb + +#define SRAM_BASE 0x20000000 +#define SRAM_SIZE (16 * 1024) + +#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 7 + .word 0 + .endr + .word svc_handler + 1 + .word 0 + .word 0 + .word 0 + .word 0 + .rept 32 + .word 0 + .endr + +.equ exc_reset_thumb, reset + 1 +.global exc_reset_thumb +reset: + /* Thread mode starts privileged. */ + mrs r0, control + movs r1, 1 + tst r0, r1 + bne failure + + /* A privileged write can enter unprivileged Thread mode. */ + movs r0, 1 + msr control, r0 + isb + mrs r0, control + tst r0, r1 + beq failure + + /* An unprivileged write cannot clear nPRIV. */ + movs r0, 0 + msr control, r0 + isb + mrs r0, control + tst r0, r1 + beq failure + + /* The privileged handler restores privileged Thread mode. */ + svc 0 + mrs r0, control + tst r0, r1 + 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 + +svc_handler: + movs r0, 0 + msr control, r0 + bx lr -- 2.55.0
