use patina::{
bit,
error::EfiError,
pi::protocol::cpu_arch::EfiSystemContext,
{UEFI_PAGE_MASK, UEFI_PAGE_SIZE},
};
#[cfg(target_arch = "x86_64")]
use patina_mtrr::Mtrr;
use patina_paging::PageTable;
use patina_stacktrace::{StackFrame, StackTrace};
use crate::interrupts::{EfiExceptionInfoDump, HandlerType, InterruptManager, x64::ExceptionContextX64};
#[derive(Default, Copy, Clone)]
pub struct InterruptsX64 {}
#[allow(dead_code)]
impl InterruptsX64 {
pub const fn new() -> Self {
Self {}
}
pub fn initialize(&mut self) -> Result<(), EfiError> {
#[cfg(target_os = "uefi")]
crate::interrupts::x64::idt::initialize_idt();
self.register_exception_handler(8, HandlerType::UefiRoutine(double_fault_handler))
.expect("Failed to install default exception handler!");
self.register_exception_handler(13, HandlerType::UefiRoutine(general_protection_fault_handler))
.expect("Failed to install default exception handler!");
self.register_exception_handler(14, HandlerType::UefiRoutine(page_fault_handler))
.expect("Failed to install default exception handler!");
Ok(())
}
}
impl InterruptManager for InterruptsX64 {}
#[cfg_attr(coverage, coverage(off))]
extern "efiapi" fn double_fault_handler(_exception_type: isize, context: EfiSystemContext) {
let x64_context = unsafe { context.system_context_x64.as_ref().unwrap() };
log::error!("EXCEPTION: DOUBLE FAULT");
log::error!("Instruction Pointer: {:#X?}", x64_context.rip);
log::error!("Code Segment: {:#X?}", x64_context.cs);
log::error!("RFLAGS: {:#X?}", x64_context.rflags);
log::error!("Stack Segment: {:#X?}", x64_context.ss);
log::error!("Stack Pointer: {:#X?}", x64_context.rsp);
log::error!("Data Segment: {:#X?}", x64_context.ds);
log::error!("Paging Enable: {}", x64_context.cr0 & 0x80000000 != 0);
log::error!("Protection Enable: {}", x64_context.cr0 & 0x00000001 != 0);
log::error!("Page Directory Base: {:#X?}", x64_context.cr3);
log::error!("Control Flags (cr4): {:#X?}", x64_context.cr4);
log::error!("");
(x64_context as &ExceptionContextX64).dump_system_context_registers();
panic!("EXCEPTION: Double Fault");
}
#[cfg_attr(coverage, coverage(off))]
extern "efiapi" fn general_protection_fault_handler(_exception_type: isize, context: EfiSystemContext) {
let x64_context = unsafe { context.system_context_x64.as_ref().unwrap() };
log::error!("EXCEPTION: GP FAULT");
log::error!("Instruction Pointer: {:#X?}", x64_context.rip);
log::error!("Code Segment: {:#X?}", x64_context.cs);
log::error!("RFLAGS: {:#X?}", x64_context.rflags);
log::error!("Stack Segment: {:#X?}", x64_context.ss);
log::error!("Stack Pointer: {:#X?}", x64_context.rsp);
log::error!("Data Segment: {:#X?}", x64_context.ds);
log::error!("Paging Enable: {}", x64_context.cr0 & 0x80000000 != 0);
log::error!("Protection Enable: {}", x64_context.cr0 & 0x00000001 != 0);
log::error!("Page Directory Base: {:#X?}", x64_context.cr3);
log::error!("Control Flags (cr4): {:#X?}", x64_context.cr4);
interpret_gp_fault_exception_data(x64_context.exception_data);
log::error!("");
(x64_context as &ExceptionContextX64).dump_system_context_registers();
log::error!("Dumping Exception Stack Trace:");
let stack_frame = StackFrame { pc: x64_context.rip, sp: x64_context.rsp, fp: x64_context.rbp };
if let Err(err) = unsafe { StackTrace::dump_with(stack_frame) } {
log::error!("StackTrace: {err}");
}
panic!("EXCEPTION: GP FAULT");
}
#[cfg_attr(coverage, coverage(off))]
extern "efiapi" fn page_fault_handler(_exception_type: isize, context: EfiSystemContext) {
let x64_context = unsafe { context.system_context_x64.as_ref().unwrap() };
let rsp_page = x64_context.rsp & !UEFI_PAGE_MASK as u64;
let cr2_page = x64_context.cr2 & !UEFI_PAGE_MASK as u64;
let same_or_below = (cr2_page == rsp_page) || (cr2_page == (rsp_page.wrapping_sub(UEFI_PAGE_SIZE as u64)));
let data_access = (x64_context.exception_data & bit!(4)) == 0;
if same_or_below && data_access {
log::error!("EXCEPTION: PAGE FAULT STACK OVERFLOW");
} else {
log::error!("EXCEPTION: PAGE FAULT");
}
log::error!("Accessed Address: {:#X?}", x64_context.cr2);
log::error!("Paging Enabled: {}", x64_context.cr0 & 0x80000000 != 0);
log::error!("Instruction Pointer: {:#X?}", x64_context.rip);
log::error!("Code Segment: {:#X?}", x64_context.cs);
log::error!("RFLAGS: {:#X?}", x64_context.rflags);
log::error!("Stack Segment: {:#X?}", x64_context.ss);
log::error!("Data Segment: {:#X?}", x64_context.ds);
log::error!("Stack Pointer: {:#X?}", x64_context.rsp);
log::error!("Page Directory Base: {:#X?}", x64_context.cr3);
log::error!("Paging Features (cr4): {:#X?}", x64_context.cr4);
interpret_page_fault_exception_data(x64_context.exception_data);
log::error!("");
(x64_context as &ExceptionContextX64).dump_system_context_registers();
dump_pte(x64_context.cr2);
log::error!("Dumping Exception Stack Trace:");
let stack_frame = StackFrame { pc: x64_context.rip, sp: x64_context.rsp, fp: x64_context.rbp };
if let Err(err) = unsafe { StackTrace::dump_with(stack_frame) } {
log::error!("StackTrace: {err}");
}
panic!("EXCEPTION: PAGE FAULT");
}
#[cfg_attr(coverage, coverage(off))]
fn interpret_page_fault_exception_data(exception_data: u64) {
log::error!("Error Code: {exception_data:#X?}");
if (exception_data & bit!(0)) == 0 {
log::error!("Page not present");
} else {
log::error!("Page-level protection violation");
}
if (exception_data & bit!(1)) == 0 {
log::error!("R/W: Read");
} else {
log::error!("R/W: Write");
}
if (exception_data & bit!(2)) != 0 {
log::error!("User-mode access violation");
} else {
log::error!("Supervisor-mode access violation");
}
if (exception_data & bit!(3)) != 0 {
log::error!("Reserved bit violation");
}
if (exception_data & bit!(4)) == 0 {
log::error!("Data access");
} else {
log::error!("Instruction fetch access");
}
}
#[cfg_attr(coverage, coverage(off))]
fn interpret_gp_fault_exception_data(exception_data: u64) {
if exception_data != 0 {
log::error!("Segment descriptor or IDT vector number: {exception_data:#X?}");
} else {
log::error!("Not from loading a segment descriptor");
}
}
#[cfg_attr(coverage, coverage(off))]
fn dump_pte(cr2: u64) {
if let Ok(pt) =
unsafe {
patina_paging::x64::X64PageTable::open_active(patina_paging::page_allocator::PageAllocatorStub)
}
{
let _ = pt.dump_page_tables(cr2 & !(UEFI_PAGE_MASK as u64), UEFI_PAGE_SIZE as u64);
}
#[cfg(target_arch = "x86_64")]
{
let mtrr = patina_mtrr::create_mtrr_lib(0);
log::error!("");
log::error!("MTRR Cache Attribute: {}", mtrr.get_memory_attribute(cr2));
log::error!("");
}
}
#[cfg_attr(coverage, coverage(off))]
#[cfg(test)]
mod test {
extern crate std;
use serial_test::serial;
use super::*;
#[test]
#[serial(exception_handlers)]
fn test_interrupts_x64() {
let mut interrupts = InterruptsX64::new();
assert!(interrupts.initialize().is_ok());
assert!(interrupts.unregister_exception_handler(13).is_ok());
assert!(interrupts.unregister_exception_handler(14).is_ok());
}
}