const MPU_CTRL: *mut u32 = 0xE000_ED94 as *mut u32;
const MPU_RNR: *mut u32 = 0xE000_ED98 as *mut u32;
const MPU_RBAR: *mut u32 = 0xE000_ED9C as *mut u32;
const MPU_RASR: *mut u32 = 0xE000_EDA0 as *mut u32;
const RASR_XN: u32 = 1 << 28;
const RASR_AP_NO_ACCESS: u32 = 0b000 << 24;
const RASR_AP_RW: u32 = 0b011 << 24;
const RASR_ENABLE: u32 = 1 << 0;
const RBAR_VALID: u32 = 1 << 4;
const POOL_DENY_REGION: u32 = 6;
const CURRENT_STACK_REGION: u32 = 7;
fn write_region(region: u32, base: usize, size: usize, ap: u32, xn: bool) {
unsafe {
let size_field = size.trailing_zeros() - 1; core::ptr::write_volatile(MPU_RNR, region);
core::ptr::write_volatile(MPU_RBAR, (base as u32) | RBAR_VALID | region);
core::ptr::write_volatile(
MPU_RASR,
(if xn { RASR_XN } else { 0 }) | ap | (size_field << 1) | RASR_ENABLE,
);
}
}
pub(crate) fn init() {
let (base, len) = rivet::preempt::stack_pool::pool_bounds();
debug_assert!(len.is_power_of_two());
write_region(POOL_DENY_REGION, base, len, RASR_AP_NO_ACCESS, true);
unsafe {
core::ptr::write_volatile(MPU_CTRL, 0b101);
}
}
pub(crate) fn set_current_stack(base: usize, size: usize) {
if base == 0 {
return;
}
debug_assert!(size.is_power_of_two());
write_region(CURRENT_STACK_REGION, base, size, RASR_AP_RW, true);
}
pub(crate) fn disable_for_scope() -> u32 {
unsafe {
let saved = core::ptr::read_volatile(MPU_CTRL);
core::ptr::write_volatile(MPU_CTRL, saved & !1);
saved
}
}
pub(crate) fn restore_after_scope(saved: u32) {
unsafe { core::ptr::write_volatile(MPU_CTRL, saved) };
}
pub(crate) fn allow_scratch(_base: usize, _size: usize) {
unsafe {
core::ptr::write_volatile(MPU_RNR, POOL_DENY_REGION);
core::ptr::write_volatile(MPU_RASR, 0);
}
}
pub(crate) fn clear_scratch() {
let (base, len) = rivet::preempt::stack_pool::pool_bounds();
write_region(POOL_DENY_REGION, base, len, RASR_AP_NO_ACCESS, true);
}
const CFSR: *const u32 = 0xE000_ED28 as *const u32;
const MMFAR: *const u32 = 0xE000_ED34 as *const u32;
#[no_mangle]
unsafe extern "C" fn rivet_memmanage_rust(faulted_sp: usize) -> usize {
let cfsr = unsafe { core::ptr::read_volatile(CFSR) };
let mmfar = unsafe { core::ptr::read_volatile(MMFAR) };
let fault_pc = if faulted_sp != 0 {
unsafe { core::ptr::read_volatile((faulted_sp + 24) as *const u32) as usize }
} else {
0
};
let task_id = rivet::preempt::sched::current();
if let Some(id) = task_id {
if let Some(t) = rivet::preempt::tcb::get(id) {
t.sp.store(faulted_sp, rivet::sync::atomic::Ordering::Release);
}
}
let address = if cfsr & (1 << 7) != 0 {
mmfar as usize
} else {
0
};
let info = rivet::fault::FaultInfo {
task_id,
kind: rivet::fault::FaultKind::MemManage(cfsr),
address,
pc: fault_pc,
};
rivet::fault::on_fault(&info)
}
core::arch::global_asm!(
".section .text.MemManage",
".global MemManage",
".thumb_func",
"MemManage:",
" push {{lr}}", " mrs r0, psp", " bl rivet_memmanage_rust", " ldmia r0, {{r4-r11}}", " adds r0, r0, #32", " msr psp, r0", " pop {{lr}}", " bx lr", );