The Armv6-M Unprivileged/Privileged Extension is optional. Cortex-M0 does
not implement it, while Cortex-M0+ may implement it. The RP2040 Cortex-M0+
configuration provides the extension.

Add an ARM feature bit and a cortex-m0plus CPU type that enables it. Permit
privileged writes to CONTROL.nPRIV when either the Main Extension or the
optional privilege extension is present.

Update the microbit Cortex-M0 test to verify that writes to nPRIV are
ignored.

Signed-off-by: Gilles Grimaud <[email protected]>
---
 target/arm/cpu.h                           |  1 +
 target/arm/tcg/cpu-v7m.c                   | 11 ++++
 target/arm/tcg/m_helper.c                  |  4 +-
 tests/tcg/arm/system/meson.build           |  6 ++
 tests/tcg/arm/system/test-armv6m-control.S | 66 ++++++++++++++++++++++
 5 files changed, 87 insertions(+), 1 deletion(-)
 create mode 100644 tests/tcg/arm/system/test-armv6m-control.S

diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index e3f931dba2..66606b3444 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -2168,6 +2168,7 @@ enum arm_features {
     ARM_FEATURE_VBAR, /* has cp15 VBAR */
     ARM_FEATURE_M_SECURITY, /* M profile Security Extension */
     ARM_FEATURE_M_MAIN, /* M profile Main Extension */
+    ARM_FEATURE_M_UNPRIV, /* M profile Unprivileged/Privileged Extension */
     ARM_FEATURE_V8_1M, /* M profile extras only in v8.1M and later */
     /*
      * ARM_FEATURE_BACKCOMPAT_CNTFRQ makes the CPU default cntfrq be 62.5MHz
diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c
index 48b31d5eac..ac99c93ec3 100644
--- a/target/arm/tcg/cpu-v7m.c
+++ b/target/arm/tcg/cpu-v7m.c
@@ -76,6 +76,15 @@ static void cortex_m0_initfn(Object *obj)
     SET_IDREG(isar, ID_ISAR6, 0x00000000);
 }
 
+static void cortex_m0plus_initfn(Object *obj)
+{
+    ARMCPU *cpu = ARM_CPU(obj);
+
+    cortex_m0_initfn(obj);
+    set_feature(&cpu->env, ARM_FEATURE_M_UNPRIV);
+    cpu->midr = 0x410cc601;
+}
+
 static void cortex_m3_initfn(Object *obj)
 {
     ARMCPU *cpu = ARM_CPU(obj);
@@ -325,6 +334,8 @@ static void arm_v7m_class_init(ObjectClass *oc, const void 
*data)
 static const ARMCPUInfo arm_v7m_cpus[] = {
     { .name = "cortex-m0",   .initfn = cortex_m0_initfn,
                              .class_init = arm_v7m_class_init },
+    { .name = "cortex-m0plus", .initfn = cortex_m0plus_initfn,
+                                .class_init = arm_v7m_class_init },
     { .name = "cortex-m3",   .initfn = cortex_m3_initfn,
                              .class_init = arm_v7m_class_init },
     { .name = "cortex-m4",   .initfn = cortex_m4_initfn,
diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c
index 33c9e7c55b..f8461a4d66 100644
--- a/target/arm/tcg/m_helper.c
+++ b/target/arm/tcg/m_helper.c
@@ -2775,7 +2775,9 @@ 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 &&
+            (arm_feature(env, ARM_FEATURE_M_MAIN) ||
+             arm_feature(env, ARM_FEATURE_M_UNPRIV))) {
             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..42ce42db2b
--- /dev/null
+++ b/tests/tcg/arm/system/test-armv6m-control.S
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Test that Cortex-M0 does not implement CONTROL.nPRIV. */
+
+.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 0
+    .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
+
+    /* Cortex-M0 does not implement the optional privilege extension. */
+    movs r0, 1
+    msr control, r0
+    isb
+    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
-- 
2.55.0


Reply via email to