use core::sync::atomic::{AtomicU8, Ordering};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FaultKind {
InstructionAccess,
LoadAccess,
StoreAccess,
MemManage(u32),
StackOverflow,
BudgetExceeded,
}
#[derive(Debug, Clone, Copy)]
pub struct FaultInfo {
pub task_id: Option<usize>,
pub kind: FaultKind,
pub address: usize,
pub pc: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FaultPolicy {
Panic,
IsolateTask,
}
const POLICY_PANIC: u8 = 0;
const POLICY_ISOLATE: u8 = 1;
static POLICY: AtomicU8 = AtomicU8::new(POLICY_PANIC);
pub type OnTaskFault = fn(usize, &FaultInfo);
static HOOK: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
pub fn set_policy(policy: FaultPolicy) {
POLICY.store(
match policy {
FaultPolicy::Panic => POLICY_PANIC,
FaultPolicy::IsolateTask => POLICY_ISOLATE,
},
Ordering::Release,
);
}
pub fn set_on_task_fault(hook: OnTaskFault) {
HOOK.store(hook as usize, Ordering::Release);
}
pub fn on_fault(info: &FaultInfo) -> usize {
#[cfg(feature = "trace")]
{
let reason = match info.kind {
FaultKind::InstructionAccess => 0,
FaultKind::LoadAccess => 1,
FaultKind::StoreAccess => 2,
FaultKind::MemManage(_) => 3,
FaultKind::StackOverflow => 4,
FaultKind::BudgetExceeded => 5,
};
crate::trace::fault(info.task_id.map(|id| id as u16).unwrap_or(0xffff), reason, info.pc as u32);
}
if POLICY.load(Ordering::Acquire) == POLICY_ISOLATE {
isolate(info)
} else {
panic_policy(info)
}
}
fn dump(info: &FaultInfo) {
crate::console::write_str("\nRIVET FAULT: ");
match info.kind {
FaultKind::InstructionAccess => crate::console::write_str("instruction-access"),
FaultKind::LoadAccess => crate::console::write_str("load-access"),
FaultKind::StoreAccess => crate::console::write_str("store-access"),
FaultKind::MemManage(_) => crate::console::write_str("memmanage"),
FaultKind::StackOverflow => crate::console::write_str("stack-overflow"),
FaultKind::BudgetExceeded => crate::console::write_str("budget-exceeded"),
}
crate::console::write_str(" addr=0x");
print_hex(info.address);
crate::console::write_str(" pc=0x");
print_hex(info.pc);
if let Some(id) = info.task_id {
crate::console::write_str(" task=");
print_dec(id);
}
crate::console::write_str("\n");
}
fn panic_policy(info: &FaultInfo) -> ! {
dump(info);
for (id, t) in crate::preempt::tcb::TASKS.iter().enumerate() {
if t.used.load(Ordering::Acquire) {
let base = t.stack_base.load(Ordering::Acquire);
let size = t.stack_size.load(Ordering::Acquire);
if base != 0 && size != 0 {
let used = crate::preempt::stack_usage(unsafe {
core::slice::from_raw_parts(base as *const u8, size)
});
crate::console::write_str(" task ");
print_dec(id);
crate::console::write_str(" stack ");
print_dec(used);
crate::console::write_str("/");
print_dec(size);
crate::console::write_str("\n");
}
}
}
crate::port::board::exit(0xFA)
}
fn isolate(info: &FaultInfo) -> usize {
dump(info);
let faulting = info.task_id.unwrap_or_else(|| panic_policy(info));
if let Some(t) = crate::preempt::tcb::get(faulting) {
for slot in &t.held {
let ptr = slot.ptr.load(Ordering::Acquire);
if !ptr.is_null() {
unsafe {
crate::preempt::mutex::poison_mutex(ptr);
}
}
}
t.set_state(faulting, crate::preempt::tcb::TaskState::Blocked);
}
let hook = HOOK.load(Ordering::Acquire);
if hook != 0 {
unsafe {
let f: OnTaskFault = core::mem::transmute(hook);
f(faulting, info);
}
}
match crate::preempt::sched::schedule() {
Some(next) => {
if let Some(nt) = crate::preempt::tcb::get(next) {
nt.set_state(next, crate::preempt::tcb::TaskState::Running);
crate::preempt::sched::set_current(next);
crate::port::arch::on_switch_to(
nt.stack_base.load(Ordering::Acquire),
nt.stack_size.load(Ordering::Acquire),
);
}
nt_sp(next)
}
None => panic_policy(info),
}
}
fn nt_sp(id: usize) -> usize {
crate::preempt::tcb::get(id)
.map(|t| t.sp.load(Ordering::Acquire))
.unwrap_or(0)
}
fn print_hex(mut n: usize) {
let mut buf = [0u8; 8];
for i in (0..8).rev() {
let d = (n & 0xF) as u8;
buf[i] = if d < 10 { b'0' + d } else { b'a' + d - 10 };
n >>= 4;
}
if let Ok(s) = core::str::from_utf8(&buf) {
crate::console::write_str(s);
}
}
fn print_dec(mut n: usize) {
if n == 0 {
crate::console::write_str("0");
return;
}
let mut digits = [0u8; 10];
let mut i = 0;
while n > 0 {
digits[i] = b'0' + (n % 10) as u8;
n /= 10;
i += 1;
}
let mut out = [0u8; 10];
for j in 0..i {
out[j] = digits[i - 1 - j];
}
if let Ok(s) = core::str::from_utf8(&out[..i]) {
crate::console::write_str(s);
}
}