From 5e6d681fcbb63528940791f5040f27b242d6bba2 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 10 Sep 2025 18:21:02 +0200 Subject: [PATCH] Share exception interrupt handlers between x86 and x86_64 --- src/arch/x86/interrupt/exception.rs | 265 ------------------ src/arch/x86/interrupt/handler.rs | 8 +- src/arch/x86/interrupt/mod.rs | 1 - src/arch/x86_64/interrupt/mod.rs | 1 - .../interrupt/exception.rs | 27 +- src/arch/x86_shared/interrupt/mod.rs | 1 + src/arch/x86_shared/mod.rs | 1 - 7 files changed, 29 insertions(+), 275 deletions(-) delete mode 100644 src/arch/x86/interrupt/exception.rs rename src/arch/{x86_64 => x86_shared}/interrupt/exception.rs (90%) diff --git a/src/arch/x86/interrupt/exception.rs b/src/arch/x86/interrupt/exception.rs deleted file mode 100644 index f5f06e8172..0000000000 --- a/src/arch/x86/interrupt/exception.rs +++ /dev/null @@ -1,265 +0,0 @@ -use syscall::Exception; -use x86::irq::PageFaultError; - -use crate::{ - context::signal::excp_handler, interrupt, interrupt_error, interrupt_stack, - memory::GenericPfFlags, paging::VirtualAddress, panic::stack_trace, ptrace, syscall::flag::*, -}; - -interrupt_stack!(divide_by_zero, |stack| { - println!("Divide by zero"); - stack.dump(); - 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. - let had_singlestep = stack.iret.eflags & (1 << 8) == 1 << 8; - stack.set_singlestep(false); - - if ptrace::breakpoint_callback(PTRACE_STOP_SINGLESTEP, None).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| { - println!("Non-maskable interrupt"); - stack.dump(); -}); - -interrupt_stack!(breakpoint, |stack| { - // The processor lets EIP 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 - // - EIP from going out of sync with instructions - // - The user having to do 2 syscalls to replace the instruction at EIP - // - Having more compatibility glue for GDB than necessary - // - // Let's just follow Linux convention and let EIP be EIP-1, point to the - // int3 instruction. After all, it's the sanest thing to do. - stack.iret.eip -= 1; - - if ptrace::breakpoint_callback(PTRACE_STOP_BREAKPOINT, None).is_none() { - println!("Breakpoint trap"); - stack.dump(); - excp_handler(Exception { - kind: 3, - ..Default::default() - }); - } -}); - -interrupt_stack!(overflow, |stack| { - println!("Overflow trap"); - stack.dump(); - stack_trace(); - excp_handler(Exception { - kind: 4, - ..Default::default() - }); -}); - -interrupt_stack!(bound_range, |stack| { - println!("Bound range exceeded fault"); - stack.dump(); - stack_trace(); - excp_handler(Exception { - kind: 5, - ..Default::default() - }); -}); - -interrupt_stack!(invalid_opcode, |stack| { - println!("Invalid opcode fault"); - stack.dump(); - stack_trace(); - excp_handler(Exception { - kind: 6, - ..Default::default() - }); -}); - -interrupt_stack!(device_not_available, |stack| { - println!("Device not available fault"); - stack.dump(); - stack_trace(); - excp_handler(Exception { - kind: 7, - ..Default::default() - }); -}); - -interrupt_error!(double_fault, |stack| { - println!("Double fault"); - stack.dump(); - stack_trace(); - loop { - interrupt::disable(); - interrupt::halt(); - } -}); - -interrupt_error!(invalid_tss, |stack| { - println!("Invalid TSS fault"); - stack.dump(); - stack_trace(); - excp_handler(Exception { - kind: 10, - code: stack.code, - ..Default::default() - }); -}); - -interrupt_error!(segment_not_present, |stack| { - println!("Segment not present fault"); - stack.dump(); - stack_trace(); - excp_handler(Exception { - kind: 11, - code: stack.code, - ..Default::default() - }); -}); - -interrupt_error!(stack_segment, |stack| { - println!("Stack segment fault"); - stack.dump(); - stack_trace(); - excp_handler(Exception { - kind: 12, - code: stack.code, - ..Default::default() - }); -}); - -interrupt_error!(protection, |stack| { - println!("Protection fault"); - stack.dump(); - stack_trace(); - excp_handler(Exception { - kind: 13, - code: stack.code, - ..Default::default() - }); -}); - -interrupt_error!(page, |stack| { - let cr2 = VirtualAddress::new(unsafe { x86::controlregs::cr2() }); - let arch_flags = PageFaultError::from_bits_truncate(stack.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), - ); - - if crate::memory::page_fault_handler(&mut stack.inner, generic_flags, cr2).is_err() { - println!("Page fault: {:>08X} {:#?}", cr2.data(), arch_flags); - stack.dump(); - stack_trace(); - excp_handler(Exception { - kind: 14, - code: stack.code, - address: cr2.data(), - }); - } -}); - -interrupt_stack!(fpu_fault, |stack| { - println!("FPU floating point fault"); - stack.dump(); - stack_trace(); - excp_handler(Exception { - kind: 16, - ..Default::default() - }); -}); - -interrupt_error!(alignment_check, |stack| { - println!("Alignment check fault"); - stack.dump(); - stack_trace(); - excp_handler(Exception { - kind: 17, - code: stack.code, - ..Default::default() - }); -}); - -interrupt_stack!(machine_check, @paranoid, |stack| { - println!("Machine check fault"); - stack.dump(); - stack_trace(); - loop { - interrupt::disable(); - interrupt::halt(); - } -}); - -interrupt_stack!(simd, |stack| { - println!("SIMD floating point fault"); - stack.dump(); - stack_trace(); - excp_handler(Exception { - kind: 19, - ..Default::default() - }); -}); - -interrupt_stack!(virtualization, |stack| { - println!("Virtualization fault"); - stack.dump(); - stack_trace(); - loop { - interrupt::disable(); - interrupt::halt(); - } -}); - -interrupt_error!(security, |stack| { - println!("Security exception"); - stack.dump(); - stack_trace(); - loop { - interrupt::disable(); - interrupt::halt(); - } -}); diff --git a/src/arch/x86/interrupt/handler.rs b/src/arch/x86/interrupt/handler.rs index 6083dd7767..01aee638eb 100644 --- a/src/arch/x86/interrupt/handler.rs +++ b/src/arch/x86/interrupt/handler.rs @@ -356,14 +356,12 @@ macro_rules! interrupt { #[macro_export] macro_rules! interrupt_error { - ($name:ident, |$stack:ident| $code:block) => { + ($name:ident, |$stack:ident, $error_code:ident| $code:block) => { #[naked] pub unsafe extern "C" fn $name() { unsafe { unsafe extern "C" fn inner($stack: &mut $crate::arch::x86::interrupt::handler::InterruptErrorStack) { - #[allow(unused_unsafe)] - unsafe { - $code - } + let $error_code: usize = $stack.code; + $code } core::arch::naked_asm!(concat!( diff --git a/src/arch/x86/interrupt/mod.rs b/src/arch/x86/interrupt/mod.rs index 430961dd9f..5ba2f7c67c 100644 --- a/src/arch/x86/interrupt/mod.rs +++ b/src/arch/x86/interrupt/mod.rs @@ -5,7 +5,6 @@ pub use crate::arch::x86_shared::interrupt::*; #[macro_use] pub mod handler; -pub mod exception; pub mod irq; pub mod syscall; diff --git a/src/arch/x86_64/interrupt/mod.rs b/src/arch/x86_64/interrupt/mod.rs index 430961dd9f..5ba2f7c67c 100644 --- a/src/arch/x86_64/interrupt/mod.rs +++ b/src/arch/x86_64/interrupt/mod.rs @@ -5,7 +5,6 @@ pub use crate::arch::x86_shared::interrupt::*; #[macro_use] pub mod handler; -pub mod exception; pub mod irq; pub mod syscall; diff --git a/src/arch/x86_64/interrupt/exception.rs b/src/arch/x86_shared/interrupt/exception.rs similarity index 90% rename from src/arch/x86_64/interrupt/exception.rs rename to src/arch/x86_shared/interrupt/exception.rs index 575fd997df..6e69a1f021 100644 --- a/src/arch/x86_64/interrupt/exception.rs +++ b/src/arch/x86_shared/interrupt/exception.rs @@ -22,6 +22,9 @@ interrupt_stack!(debug, @paranoid, |stack| { // 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); @@ -55,7 +58,7 @@ interrupt_stack!(non_maskable, @paranoid, |stack| { }); interrupt_stack!(breakpoint, |stack| { - // The processor lets RIP point to the instruction *after* int3, so + // 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. // @@ -66,7 +69,14 @@ interrupt_stack!(breakpoint, |stack| { // // 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. - stack.iret.rip -= 1; + #[cfg(target_arch = "x86")] + { + stack.iret.eip -= 1; + } + #[cfg(target_arch = "x86_64")] + { + stack.iret.rip -= 1; + } if ptrace::breakpoint_callback(PTRACE_STOP_BREAKPOINT, None).is_none() { println!("Breakpoint trap"); @@ -200,6 +210,19 @@ interrupt_error!(page, |stack, code| { 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.dump(); + unsafe { 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.dump(); diff --git a/src/arch/x86_shared/interrupt/mod.rs b/src/arch/x86_shared/interrupt/mod.rs index a01e7dbfd7..7a49ffb7fe 100644 --- a/src/arch/x86_shared/interrupt/mod.rs +++ b/src/arch/x86_shared/interrupt/mod.rs @@ -1,5 +1,6 @@ //! Interrupt instructions +pub mod exception; pub mod ipi; pub mod trace; diff --git a/src/arch/x86_shared/mod.rs b/src/arch/x86_shared/mod.rs index 21b6a208fb..ff818fcf77 100644 --- a/src/arch/x86_shared/mod.rs +++ b/src/arch/x86_shared/mod.rs @@ -11,7 +11,6 @@ pub mod device; pub mod idt; /// Interrupt instructions -#[macro_use] pub mod interrupt; /// Inter-processor interrupts