/src/hardfault.rs
use core::arch::global_asm;
use cortex_m::peripheral::SCB;
use crate::console::flush;
use crate::kprintf;
use crate::task::{with_task_manager, TaskRegisters};
fn print_regs(regs: &TaskRegisters) {
kprintf!(
"R0: {:08X} R1: {:08X} R2: {:08X} R3: {:08X}\r\n",
regs.r0,
regs.r1,
regs.r2,
regs.r3
);
kprintf!(
"R4: {:08X} R5: {:08X} R6: {:08X} R7: {:08X}\r\n",
regs.r4,
regs.r5,
regs.r6,
regs.r7
);
kprintf!(
"R8: {:08X} R9: {:08X} R10: {:08X} R11: {:08X}\r\n",
regs.r8,
regs.r9,
regs.r10,
regs.r11
);
kprintf!(
"R12: {:08X} SP: {:08X} LR: {:08X} PC: {:08X}\r\n",
regs.r12,
regs.sp,
regs.lr,
regs.pc
);
kprintf!("XPSR: {:08X}\r\n", regs.xpsr);
}
unsafe fn print_stack(regs: &TaskRegisters) {
let sp = regs.sp as *const u32;
kprintf!("SP: {:08X}\r\n", sp as u32);
for i in 0..4 {
kprintf!("{:08X}:", (sp as u32) + i * 32);
for j in 0..8 {
let d = *sp.offset((i * 8 + j) as isize);
kprintf!(" {:08X}", d);
}
kprintf!("\r\n");
}
}
unsafe fn print_mpu() {
let p = doa_hallonbrod::pac::Peripherals::steal();
let ctrl = p.PPB.mpu_ctrl.read();
kprintf!(
"MPU_CTRL: ENABLE: {}, HFIMENA: {}, PRIVDEFENA: {}\r\n",
ctrl.enable().bit(),
ctrl.hfnmiena().bit(),
ctrl.privdefena().bit(),
);
for i in 0..8 {
p.PPB.mpu_rnr.write(|w| w.region().bits(i));
let rbar = p.PPB.mpu_rbar.read();
let rasr = p.PPB.mpu_rasr.read();
kprintf!("MPU Region {} ", i);
if rasr.enable().bit() {
let attrs = rasr.attrs().bits();
kprintf!(
"ENABLED addr {:08X} size {:08X} srd {:08b} TEXSCB {:06b} {}",
rbar.addr().bits() << 8,
2_u32.pow(rasr.size().bits() as u32 + 1),
rasr.srd().bits(),
attrs & 0b111111,
match (attrs >> 8) & 0b111 {
0b000 => "none/none",
0b001 => "RW/none",
0b010 => "RW/RO",
0b011 => "RW/RW",
0b100 => "RESERVED",
0b101 => "RO/none",
0b110 => "RO/RO (110)",
0b111 => "RO/RO (111)",
_ => unreachable!(),
}
);
if attrs & (1 << 12) != 0 {
kprintf!(" XN");
}
} else {
kprintf!("DISABLED");
}
kprintf!("\r\n");
}
}
// This one is slightly different than the usual ExceptionEntry/Exit
// because we want to know the registers even if we came from handler
// mode.
global_asm!(
r#"
.section .HardFault, "ax"
.global HardFault
.type HardFault,function
HardFault:
// stack the registers
push {{r4, r5, r6, r7}} // push r4-r7
mov r0, r8
mov r1, r9
mov r2, r10
mov r3, r11
push {{r0, r1, r2, r3}} // push r8-r11
mov r3, lr
movs r1, #0x04 // Check which mode we just came from
ands r3, r1
beq 2f // if we came from thread mode,
mrs r0, PSP // use the process stack
b 3f
2: mrs r0, MSP // otherwise, use main stack
3:
push {{r0}} // push SP
sub sp, #32 // reserve space for the next eight registers
mov r1, sp // copy the stack pointer
ldm r0!, {{r4, r5, r6, r7}} // load r0-r3
stm r1!, {{r4, r5, r6, r7}} // store r0-r3
ldm r0!, {{r4, r5, r6, r7}} // load r12, lr, pc, xpsr
stm r1!, {{r4, r5, r6, r7}} // save r12, lr, pc, xpsr
mov r0, sp // get address of TaskRegisters struct
mov r1, r3
movs r2, 2 // shift the thread mode flag right so it's a
rors r1, r2 // bool and it's the second argument
push {{lr}} // save LR
bl hardfault_handler // call the handler
pop {{r0}}
add sp, #68 // pop everything off the stack. Restoring the
// registers is irrelevant; the process is crashing.
bx r0 // exit the handler
"#
);
#[no_mangle]
unsafe fn hardfault_handler(regs: &mut TaskRegisters, thread_mode: bool) {
kprintf!(
"\r\nHardFault! ({})\r\n",
if thread_mode { "thread" } else { "handler" }
);
print_regs(regs);
kprintf!("\r\n");
print_stack(regs);
kprintf!("\r\n");
print_mpu();
kprintf!("\r\n");
let scb = &(*cortex_m::peripheral::SCB::PTR);
kprintf!("ICSR: {:08X}\r\n", scb.icsr.read());
flush();
if thread_mode {
// kill the process and invoke the scheduler
with_task_manager(|tm| {
tm.exit_current_task();
});
SCB::set_pendsv();
} else {
// Can't go nowhere from here
cortex_m::interrupt::disable();
loop {}
}
}