1 #include "pico/runtime.h"
2
3 #if !PICO_RUNTIME_NO_INIT_PER_CORE_INSTALL_STACK_GUARD
4 #if PICO_RP2040
5 #include "hardware/structs/mpu.h"
6 #elif defined(__riscv)
7 #include "hardware/riscv.h"
8 #endif
9 // this is called for each thread since they have their own MPU
runtime_init_per_core_install_stack_guard(void * stack_bottom)10 void runtime_init_per_core_install_stack_guard(void *stack_bottom) {
11 // this is called b4 runtime_init is complete, so beware printf or assert
12
13 uintptr_t addr = (uintptr_t) stack_bottom;
14 // the minimum we can protect is 32 bytes on a 32 byte boundary, so round up which will
15 // just shorten the valid stack range a tad
16 addr = (addr + 31u) & ~31u;
17
18 #if PICO_RP2040
19 // Armv6-M MPU
20 // make sure no one is using the MPU yet
21 if (mpu_hw->ctrl) {
22 // Note that it would be tempting to change this to a panic, but it happens so early, printing is not a good idea
23 __breakpoint();
24 }
25 // mask is 1 bit per 32 bytes of the 256 byte range... clear the bit for the segment we want
26 uint32_t subregion_select = 0xffu ^ (1u << ((addr >> 5u) & 7u));
27 mpu_hw->ctrl = 5; // enable mpu with background default map
28 mpu_hw->rbar = (addr & (uint)~0xff) | M0PLUS_MPU_RBAR_VALID_BITS | 0;
29 mpu_hw->rasr = 1 // enable region
30 | (0x7 << 1) // size 2^(7 + 1) = 256
31 | (subregion_select << 8)
32 | 0x10000000; // XN = disable instruction fetch; no other bits means no permissions
33
34 #elif defined(__riscv)
35 #if !PICO_RP2350
36 #error "Check PMP configuration for new platform"
37 #endif
38 // RISC-V PMP, RP2350 configuration of Hazard3: 8 non-hardwired regions,
39 // NAPOT only, 32-byte granule, with nonstandard PMPCFGM0 register to
40 // apply regions to M-mode without locking them.
41 // Make sure no one is using the PMP yet
42 bool dirty_pmp =
43 riscv_read_csr(pmpcfg0) != 0 ||
44 riscv_read_csr(pmpcfg1) != 0 ||
45 riscv_read_csr(RVCSR_PMPCFGM0_OFFSET) != 0;
46
47 if (dirty_pmp) {
48 __breakpoint();
49 }
50
51 // Note pmpaddr is in units of 4 bytes, so right-shift 2.
52 riscv_write_csr(pmpaddr0, (addr | 0x0fu) >> 2);
53 // Make this region inaccessible in both M-mode and U-mode (but don't lock it)
54 riscv_write_csr(RVCSR_PMPCFGM0_OFFSET, 0x1u);
55 riscv_write_csr(pmpcfg0, RVCSR_PMPCFG0_R0_A_VALUE_NAPOT << RVCSR_PMPCFG0_R0_A_LSB);
56
57 #else
58 // // Armv8-M MPU
59 // // make sure no one is using the MPU yet
60 // if (mpu_hw->ctrl) {
61 // __breakpoint();
62 // }
63 // mpu_hw->rnr = 0;
64 // // Read-only, privileged-only, nonexecutable. (Good enough because stack
65 // // is usually written first, on a stack push)
66 // mpu_hw->rbar = addr | (2u << M33_MPU_RBAR_AP_LSB) | (M33_MPU_RBAR_XN_BITS);
67 // mpu_hw->rlar = addr | M33_MPU_RLAR_EN_BITS;
68 // // Enable MPU (and leave default attributes applied even for privileged software)
69 // mpu_hw->ctrl = M33_MPU_CTRL_PRIVDEFENA_BITS | M33_MPU_CTRL_ENABLE_BITS;
70 pico_default_asm_volatile(
71 "msr msplim, %0"
72 :
73 : "r" (stack_bottom));
74 #endif
75 }
76
77 #endif