Files
RedBear-OS/src/arch/x86_shared/interrupt/exception.rs
T
bjorn3 38f36d406b Use cfg!() instead of #[cfg] for most profiling code
This makes it less likely that it breaks again in the future as most
code gets checked by the compiler even when profiling support is not
enabled.
2026-04-13 19:43:43 +02:00

295 lines
7.3 KiB
Rust

use syscall::Exception;
use x86::irq::PageFaultError;
use crate::{
arch::x86_shared::interrupt,
context::signal::excp_handler,
memory::{GenericPfFlags, VirtualAddress},
ptrace,
sync::CleanLockToken,
syscall::flag::*,
};
interrupt_stack!(divide_by_zero, |stack| {
println!("Divide by zero");
stack.trace();
excp_handler(Exception {
kind: 0,
..Default::default()
});
});
interrupt_stack!(debug, @paranoid, |stack| {
let mut handled = false;
// Disable singlestep before there is a breakpoint, since the breakpoint
// handler might end up setting it again but unless it does we want the
// default to be false.
#[cfg(target_arch = "x86")]
let had_singlestep = stack.iret.eflags & (1 << 8) == 1 << 8;
#[cfg(target_arch = "x86_64")]
let had_singlestep = stack.iret.rflags & (1 << 8) == 1 << 8;
stack.set_singlestep(false);
let mut token = unsafe { CleanLockToken::new() };
if ptrace::breakpoint_callback(PTRACE_STOP_SINGLESTEP, None, &mut token).is_some() {
handled = true;
} else {
// There was no breakpoint, restore original value
stack.set_singlestep(had_singlestep);
}
if !handled {
println!("Debug trap");
stack.dump();
excp_handler(Exception {
kind: 1,
..Default::default()
});
}
});
interrupt_stack!(non_maskable, @paranoid, |stack| {
#[cfg(target_arch = "x86_64")]
unsafe { crate::profiling::nmi_handler(stack) };
#[cfg(not(all(target_arch = "x86_64", feature = "profiling")))]
{
// TODO: This will likely deadlock
println!("Non-maskable interrupt");
stack.dump();
}
});
interrupt_stack!(breakpoint, |stack| {
// The processor lets EIP/RIP point to the instruction *after* int3, so
// unhandled breakpoint interrupt don't go in an infinite loop. But we
// throw SIGTRAP anyway, so that's not a problem.
//
// We have the following code to prevent
// - RIP from going out of sync with instructions
// - The user having to do 2 syscalls to replace the instruction at RIP
// - Having more compatibility glue for GDB than necessary
//
// Let's just follow Linux convention and let RIP be RIP-1, point to the
// int3 instruction. After all, it's the sanest thing to do.
#[cfg(target_arch = "x86")]
{
stack.iret.eip -= 1;
}
#[cfg(target_arch = "x86_64")]
{
stack.iret.rip -= 1;
}
let mut token = unsafe { CleanLockToken::new() };
if ptrace::breakpoint_callback(PTRACE_STOP_BREAKPOINT, None, &mut token).is_none() {
println!("Breakpoint trap");
stack.dump();
excp_handler(Exception {
kind: 3,
..Default::default()
});
}
});
interrupt_stack!(overflow, |stack| {
println!("Overflow trap");
stack.trace();
excp_handler(Exception {
kind: 4,
..Default::default()
});
});
interrupt_stack!(bound_range, |stack| {
println!("Bound range exceeded fault");
stack.trace();
excp_handler(Exception {
kind: 5,
..Default::default()
});
});
interrupt_stack!(invalid_opcode, |stack| {
println!("Invalid opcode fault");
stack.trace();
excp_handler(Exception {
kind: 6,
..Default::default()
});
});
interrupt_stack!(device_not_available, |stack| {
println!("Device not available fault");
stack.trace();
excp_handler(Exception {
kind: 7,
..Default::default()
});
});
interrupt_error!(double_fault, |stack, _code| {
println!("Double fault");
stack.trace();
unsafe {
loop {
interrupt::disable();
interrupt::halt();
}
}
});
interrupt_error!(invalid_tss, |stack, code| {
println!("Invalid TSS fault");
stack.trace();
excp_handler(Exception {
kind: 10,
code,
..Default::default()
});
});
interrupt_error!(segment_not_present, |stack, code| {
println!("Segment not present fault");
stack.trace();
excp_handler(Exception {
kind: 11,
code,
..Default::default()
});
});
interrupt_error!(stack_segment, |stack, code| {
println!("Stack segment fault");
stack.trace();
excp_handler(Exception {
kind: 12,
code,
..Default::default()
});
});
interrupt_error!(protection, |stack, code| {
println!("Protection fault code={:#0x}", code);
stack.trace();
excp_handler(Exception {
kind: 13,
code,
..Default::default()
});
});
interrupt_error!(page, |stack, code| {
let cr2 = VirtualAddress::new(unsafe { x86::controlregs::cr2() });
let arch_flags = PageFaultError::from_bits_truncate(code as u32);
let mut generic_flags = GenericPfFlags::empty();
generic_flags.set(
GenericPfFlags::PRESENT,
arch_flags.contains(PageFaultError::P),
);
generic_flags.set(
GenericPfFlags::INVOLVED_WRITE,
arch_flags.contains(PageFaultError::WR),
);
generic_flags.set(
GenericPfFlags::USER_NOT_SUPERVISOR,
arch_flags.contains(PageFaultError::US),
);
generic_flags.set(
GenericPfFlags::INVL,
arch_flags.contains(PageFaultError::RSVD),
);
generic_flags.set(
GenericPfFlags::INSTR_NOT_DATA,
arch_flags.contains(PageFaultError::ID),
);
#[cfg(target_arch = "x86")]
if crate::memory::page_fault_handler(&mut stack.inner, generic_flags, cr2).is_err() {
println!("Page fault: {:>08X} {:#?}", cr2.data(), arch_flags);
stack.trace();
excp_handler(Exception {
kind: 14,
code,
address: cr2.data(),
});
}
#[cfg(target_arch = "x86_64")]
if crate::memory::page_fault_handler(stack, generic_flags, cr2).is_err() {
println!("Page fault: {:>016X} {:#?}", cr2.data(), arch_flags);
stack.trace();
excp_handler(Exception {
kind: 14,
code,
address: cr2.data(),
});
}
});
interrupt_stack!(fpu_fault, |stack| {
println!("FPU floating point fault");
stack.trace();
excp_handler(Exception {
kind: 16,
..Default::default()
});
});
interrupt_error!(alignment_check, |stack, code| {
println!("Alignment check fault");
stack.trace();
excp_handler(Exception {
kind: 17,
code,
..Default::default()
});
});
interrupt_stack!(machine_check, @paranoid, |stack| {
println!("Machine check fault");
stack.trace();
unsafe {
loop {
interrupt::disable();
interrupt::halt();
}
}
});
interrupt_stack!(simd, |stack| {
println!("SIMD floating point fault");
let mut mxcsr = 0_usize;
unsafe { core::arch::asm!("stmxcsr [{}]", in(reg) core::ptr::addr_of_mut!(mxcsr)) };
println!("MXCSR {:#0x}", mxcsr);
stack.trace();
excp_handler(Exception {
kind: 19,
..Default::default()
});
});
interrupt_stack!(virtualization, |stack| {
println!("Virtualization fault");
stack.trace();
unsafe {
loop {
interrupt::disable();
interrupt::halt();
}
}
});
interrupt_error!(security, |stack, _code| {
println!("Security exception");
stack.trace();
unsafe {
loop {
interrupt::disable();
interrupt::halt();
}
}
});