1use crate::thread::STACK_SIZE;
3use abyss::{
4 dev::x86_64::apic::{IPIDest, Mode, send_ipi},
5 interrupt::{InterruptGuard, NMI_EXPECTED_PANICKING},
6 unwind::{DwarfReader, Peeker, StackFrame, UnwindBacktrace},
7 x86_64::{intrinsics::cpuid, kernel_gs, pio::Pio},
8};
9use addr2line::{Context, Frame};
10use alloc::{borrow::Cow, sync::Arc};
11use core::mem::ManuallyDrop;
12use core::sync::atomic::Ordering;
13
14#[derive(Clone)]
15struct EhFrameReader;
16
17impl EhFrameReader {
18 fn start() -> usize {
19 unsafe extern "C" {
20 static __eh_frame_hdr_start: u8;
21 }
22 unsafe { &__eh_frame_hdr_start as *const _ as usize }
23 }
24
25 fn end() -> usize {
26 unsafe extern "C" {
27 static __eh_frame_end: u8;
28 }
29 unsafe { &__eh_frame_end as *const _ as usize }
30 }
31}
32
33impl Peeker for EhFrameReader {
34 fn read<T>(&self, ofs: usize) -> Option<T>
35 where
36 T: Copy,
37 {
38 let (start, end) = (Self::start(), Self::end());
39 if ofs >= start && ofs + core::mem::size_of::<T>() < end {
40 unsafe { (ofs as *const T).as_ref().cloned() }
41 } else {
42 None
43 }
44 }
45}
46
47struct BackTracePrinter<'a>(
48 Frame<'a, gimli::EndianArcSlice<gimli::LittleEndian>>,
49 Option<u64>,
50);
51
52impl core::fmt::Display for BackTracePrinter<'_> {
53 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54 if let Some(pc) = self.1 {
55 if let Some(Ok(name)) = self.0.function.as_ref().map(|n| n.demangle()) {
56 writeln!(formatter, "{name} [0x{pc:016x}]")?;
57 } else {
58 writeln!(formatter, "0x{pc:016x}")?;
59 }
60 }
61 if let Some(file) = self.0.location.as_ref().and_then(|n| n.file) {
62 write!(formatter, " at {file}:")?;
63 } else {
64 write!(formatter, " at ?:")?;
65 }
66 if let Some(line) = self.0.location.as_ref().and_then(|n| n.line) {
67 write!(formatter, "{line}:")?;
68 } else {
69 write!(formatter, "?:")?;
70 }
71 if let Some(col) = self.0.location.as_ref().and_then(|n| n.column) {
72 write!(formatter, "{col}")
73 } else {
74 write!(formatter, "?")
75 }
76 }
77}
78
79fn do_backtrace(state: &mut (isize, bool), frame: &StackFrame) {
80 let (depth, has_non_kernel_addr) = state;
81 let pc = frame.pc() as u64;
82 if pc >> 48 != 0xffff {
83 *has_non_kernel_addr = true;
84 }
85 *depth += 1;
86 if *depth == -1 {
87 return; }
89 if let Some(ctxt) = unsafe { DEBUG_CONTEXT.as_ref() }
90 && let Ok(mut frames) = ctxt.find_frames(pc)
91 && let Ok(Some(frame)) = frames.next()
92 {
93 println!(
94 " {:2}: {}",
95 depth,
96 BackTracePrinter(
97 frame,
98 Some(
99 pc + 1, )
105 ),
106 );
107 while let Ok(Some(frame)) = frames.next() {
108 println!("{}", BackTracePrinter(frame, None));
109 }
110 return;
111 }
112 println!(" {:2}: 0x{:016x} - ?", depth, pc);
113}
114
115static mut DEBUG_CONTEXT: Option<Context<gimli::EndianArcSlice<gimli::LittleEndian>>> = None;
116
117#[allow(dead_code)]
118#[allow(unreachable_code)]
119#[allow(clippy::empty_loop)]
120#[inline(never)]
121#[panic_handler]
122fn panic(info: &core::panic::PanicInfo) -> ! {
123 let _cli = ManuallyDrop::new(InterruptGuard::new());
125
126 match crate::PANIC_DEPTH.fetch_add(1, Ordering::SeqCst) {
127 0 => {}
128 1 => {
129 unsafe {
130 abyss::kprint::force_unlock_serial();
131 }
132 println!("*** PANIC recursed: Forcing the shutdown ***");
133 println!("{}", info);
134 unsafe {
135 abyss::x86_64::power_control::power_off();
136 }
137 }
138 _ => unsafe {
139 abyss::x86_64::power_control::power_off();
140 },
141 }
142 NMI_EXPECTED_PANICKING.store(true, Ordering::SeqCst);
147 for (cpuid, is_online) in abyss::boot::ONLINE_CPU
148 .iter()
149 .enumerate()
150 .filter(|(id, o)| o.load(Ordering::SeqCst) && *id != cpuid())
151 {
152 unsafe {
153 send_ipi(IPIDest::Cpu(cpuid), Mode::Nmi);
154 }
155 is_online.store(false, Ordering::SeqCst);
156 }
157 unsafe {
159 abyss::kprint::force_unlock_serial();
160 }
161 fn panic_internal_poweroff(has_non_kernel_addr: bool) -> ! {
162 if has_non_kernel_addr {
163 println!("\nWARNING: Non-kernel address detected in the backtrace.\n");
164 println!(
165 "It indicates that some of addresses above are either user program's or even completely wrong."
166 );
167 println!(
168 "For details, please refer to `Debugging a User Process` chapter of the KeOS documentation."
169 );
170 }
171 println!("\nShutting down the system in few moment...");
172 for _ in 0..50 {
173 fn init_pit(val: u16) {
174 let chan2_gate = Pio::new(0x61);
177 chan2_gate.write_u8((chan2_gate.read_u8() & !0x2) | 1);
178 Pio::new(0x43).write_u8(0xb0);
180 Pio::new(0x42).write_u8(val as u8); Pio::new(0x42).write_u8((val >> 8) as u8); }
183 init_pit(0xFFFF);
184
185 loop {
186 fn read_pit() -> u16 {
187 let low = Pio::new(0x42).read_u8() as u16;
188 let high = Pio::new(0x42).read_u8() as u16;
189 low | high << 8
190 }
191 if read_pit() == 0 {
192 break;
193 }
194 }
195 }
196 unsafe {
197 abyss::x86_64::power_control::power_off();
198 }
199 }
200
201 let frame = StackFrame::current();
202
203 if unsafe { ((frame.sp() & !(STACK_SIZE - 1)) as *mut crate::thread::ThreadStack).as_mut() }
204 .map(|stack| stack.magic == crate::thread::THREAD_MAGIC)
205 .unwrap_or(false)
206 {
207 crate::thread::with_current(|th| {
208 println!(
209 "\n\nKeOS thread '{}' [core #{}, tid #{}] {}\n",
210 th.name,
211 abyss::x86_64::intrinsics::cpuid(),
212 th.tid,
213 info
214 );
215 });
216 } else {
217 println!(
218 "\n\nKeOS thread '<unknown>' [core #{}, tid #?] {}\n",
219 abyss::x86_64::intrinsics::cpuid(),
220 info
221 );
222 }
223
224 println!("Stack Backtrace: ");
225 let mut state = (-2, false);
226 let sp_hi = frame.sp() & !(STACK_SIZE - 1);
227 if let Err(e) = unsafe {
228 UnwindBacktrace::new(
229 frame,
230 sp_hi..sp_hi + STACK_SIZE,
231 DwarfReader::from_peeker(EhFrameReader::start(), EhFrameReader),
232 )
233 .run(&mut state, |state, this, _| {
234 do_backtrace(state, &this.frame)
235 })
236 } {
237 println!("** Backtrace Failed: {:?}", e);
238 }
239 if let Some(trap_frame) = unsafe { kernel_gs::current().interrupt_frame.as_ref() } {
240 println!("Triggered by event at: ");
241 let frame = trap_frame.to_stack_frame();
242 let sp_hi = frame.sp() & !(STACK_SIZE - 1);
243 if let Err(e) = unsafe {
244 UnwindBacktrace::new(
245 frame,
246 sp_hi..sp_hi + STACK_SIZE,
247 DwarfReader::from_peeker(EhFrameReader::start(), EhFrameReader),
248 )
249 .run(&mut state, |depth, this, _| {
250 do_backtrace(depth, &this.frame)
251 })
252 } {
253 println!("** Backtrace Failed: {:?}", e);
254 }
255 }
256 panic_internal_poweroff(state.1)
257}
258
259#[allow(clippy::result_unit_err)]
261pub(crate) fn load_debug_infos() -> bool {
262 use object::{Object, ObjectSection};
263 let Some(kernel_disk) = abyss::dev::get_bdev(0) else {
264 return false;
265 };
266 let image_size = kernel_disk.block_cnt() * kernel_disk.block_size();
267 let mut kernel_image = alloc::vec![0u8; image_size].into_boxed_slice();
268 if kernel_disk
269 .read_bios(&mut Some((0, kernel_image.as_mut())).into_iter())
270 .is_err()
271 {
272 return false;
273 }
274 let Ok(kernel) = object::File::parse(kernel_image.as_ref()) else {
275 return false;
276 };
277 let Ok(dwarf): Result<_, ()> = gimli::Dwarf::load(|id| {
278 let data = kernel
279 .section_by_name(id.name())
280 .and_then(|section| section.uncompressed_data().ok())
281 .unwrap_or(Cow::Borrowed(&[]));
282 let data: Arc<[u8]> = Arc::from(data.as_ref());
283 Ok(gimli::EndianArcSlice::new(data, gimli::LittleEndian))
284 }) else {
285 return false;
286 };
287 unsafe {
288 DEBUG_CONTEXT = Context::from_dwarf(dwarf).ok();
289 DEBUG_CONTEXT.is_some()
290 }
291}