1use core::sync::atomic::{AtomicU8, Ordering};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum FaultKind {
21 InstructionAccess,
23 LoadAccess,
25 StoreAccess,
27 MemManage(u32),
29 StackOverflow,
31 BudgetExceeded,
34}
35
36#[derive(Debug, Clone, Copy)]
38pub struct FaultInfo {
39 pub task_id: Option<usize>,
41 pub kind: FaultKind,
42 pub address: usize,
44 pub pc: usize,
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum FaultPolicy {
51 Panic,
53 IsolateTask,
55}
56
57const POLICY_PANIC: u8 = 0;
58const POLICY_ISOLATE: u8 = 1;
59static POLICY: AtomicU8 = AtomicU8::new(POLICY_PANIC);
60
61pub type OnTaskFault = fn(usize, &FaultInfo);
64static HOOK: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
65
66pub fn set_policy(policy: FaultPolicy) {
68 POLICY.store(
69 match policy {
70 FaultPolicy::Panic => POLICY_PANIC,
71 FaultPolicy::IsolateTask => POLICY_ISOLATE,
72 },
73 Ordering::Release,
74 );
75}
76
77pub fn set_on_task_fault(hook: OnTaskFault) {
79 HOOK.store(hook as usize, Ordering::Release);
80}
81
82pub fn on_fault(info: &FaultInfo) -> usize {
86 #[cfg(feature = "trace")]
87 {
88 let reason = match info.kind {
89 FaultKind::InstructionAccess => 0,
90 FaultKind::LoadAccess => 1,
91 FaultKind::StoreAccess => 2,
92 FaultKind::MemManage(_) => 3,
93 FaultKind::StackOverflow => 4,
94 FaultKind::BudgetExceeded => 5,
95 };
96 crate::trace::fault(info.task_id.map(|id| id as u16).unwrap_or(0xffff), reason, info.pc as u32);
97 }
98 if POLICY.load(Ordering::Acquire) == POLICY_ISOLATE {
99 isolate(info)
100 } else {
101 panic_policy(info)
102 }
103}
104
105fn dump(info: &FaultInfo) {
106 crate::console::write_str("\nRIVET FAULT: ");
107 match info.kind {
108 FaultKind::InstructionAccess => crate::console::write_str("instruction-access"),
109 FaultKind::LoadAccess => crate::console::write_str("load-access"),
110 FaultKind::StoreAccess => crate::console::write_str("store-access"),
111 FaultKind::MemManage(_) => crate::console::write_str("memmanage"),
112 FaultKind::StackOverflow => crate::console::write_str("stack-overflow"),
113 FaultKind::BudgetExceeded => crate::console::write_str("budget-exceeded"),
114 }
115 crate::console::write_str(" addr=0x");
116 print_hex(info.address);
117 crate::console::write_str(" pc=0x");
118 print_hex(info.pc);
119 if let Some(id) = info.task_id {
120 crate::console::write_str(" task=");
121 print_dec(id);
122 }
123 crate::console::write_str("\n");
124}
125
126fn panic_policy(info: &FaultInfo) -> ! {
127 dump(info);
128 for (id, t) in crate::preempt::tcb::TASKS.iter().enumerate() {
130 if t.used.load(Ordering::Acquire) {
131 let base = t.stack_base.load(Ordering::Acquire);
132 let size = t.stack_size.load(Ordering::Acquire);
133 if base != 0 && size != 0 {
134 let used = crate::preempt::stack_usage(unsafe {
137 core::slice::from_raw_parts(base as *const u8, size)
138 });
139 crate::console::write_str(" task ");
140 print_dec(id);
141 crate::console::write_str(" stack ");
142 print_dec(used);
143 crate::console::write_str("/");
144 print_dec(size);
145 crate::console::write_str("\n");
146 }
147 }
148 }
149 crate::port::board::exit(0xFA)
152}
153
154fn isolate(info: &FaultInfo) -> usize {
155 dump(info);
156
157 let faulting = info.task_id.unwrap_or_else(|| panic_policy(info));
161
162 if let Some(t) = crate::preempt::tcb::get(faulting) {
165 for slot in &t.held {
166 let ptr = slot.ptr.load(Ordering::Acquire);
167 if !ptr.is_null() {
168 unsafe {
171 crate::preempt::mutex::poison_mutex(ptr);
172 }
173 }
174 }
175 t.set_state(faulting, crate::preempt::tcb::TaskState::Blocked);
176 }
177
178 let hook = HOOK.load(Ordering::Acquire);
180 if hook != 0 {
181 unsafe {
183 let f: OnTaskFault = core::mem::transmute(hook);
184 f(faulting, info);
185 }
186 }
187
188 match crate::preempt::sched::schedule() {
190 Some(next) => {
191 if let Some(nt) = crate::preempt::tcb::get(next) {
192 nt.set_state(next, crate::preempt::tcb::TaskState::Running);
193 crate::preempt::sched::set_current(next);
194 crate::port::arch::on_switch_to(
195 nt.stack_base.load(Ordering::Acquire),
196 nt.stack_size.load(Ordering::Acquire),
197 );
198 }
199 nt_sp(next)
200 }
201 None => panic_policy(info),
202 }
203}
204
205fn nt_sp(id: usize) -> usize {
206 crate::preempt::tcb::get(id)
207 .map(|t| t.sp.load(Ordering::Acquire))
208 .unwrap_or(0)
209}
210
211fn print_hex(mut n: usize) {
212 let mut buf = [0u8; 8];
213 for i in (0..8).rev() {
214 let d = (n & 0xF) as u8;
215 buf[i] = if d < 10 { b'0' + d } else { b'a' + d - 10 };
216 n >>= 4;
217 }
218 if let Ok(s) = core::str::from_utf8(&buf) {
219 crate::console::write_str(s);
220 }
221}
222
223fn print_dec(mut n: usize) {
224 if n == 0 {
225 crate::console::write_str("0");
226 return;
227 }
228 let mut digits = [0u8; 10];
229 let mut i = 0;
230 while n > 0 {
231 digits[i] = b'0' + (n % 10) as u8;
232 n /= 10;
233 i += 1;
234 }
235 let mut out = [0u8; 10];
236 for j in 0..i {
237 out[j] = digits[i - 1 - j];
238 }
239 if let Ok(s) = core::str::from_utf8(&out[..i]) {
240 crate::console::write_str(s);
241 }
242}