pub(super) mod gdt;
mod idt;
mod syscall;
use cfg_if::cfg_if;
use spin::Once;
use super::cpu::context::GeneralRegs;
use crate::{
arch::{
cpu::context::CpuException,
irq::{HwIrqLine, disable_local, enable_local},
},
cpu::PrivilegeLevel,
ex_table::ExTable,
irq::call_irq_callback_functions,
mm::MAX_USERSPACE_VADDR,
};
cfg_if! {
if #[cfg(feature = "cvm_guest")] {
use tdx_guest::{tdcall, handle_virtual_exception};
use crate::arch::tdx_guest::TrapFrameWrapper;
}
}
#[expect(missing_docs)]
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct TrapFrame {
pub rax: usize,
pub rbx: usize,
pub rcx: usize,
pub rdx: usize,
pub rsi: usize,
pub rdi: usize,
pub rbp: usize,
pub rsp: usize,
pub r8: usize,
pub r9: usize,
pub r10: usize,
pub r11: usize,
pub r12: usize,
pub r13: usize,
pub r14: usize,
pub r15: usize,
pub _pad: usize,
pub trap_num: usize,
pub error_code: usize,
pub rip: usize,
pub cs: usize,
pub rflags: usize,
}
pub(crate) unsafe fn init_on_cpu() {
unsafe { gdt::init_on_cpu() };
idt::init_on_cpu();
unsafe { syscall::init_on_cpu() };
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub(super) struct RawUserContext {
pub(super) general: GeneralRegs,
pub(super) trap_num: usize,
pub(super) error_code: usize,
}
#[unsafe(no_mangle)]
unsafe extern "sysv64" fn trap_handler(f: &mut TrapFrame) {
fn enable_local_if(cond: bool) {
if cond {
enable_local();
}
}
fn disable_local_if(cond: bool) {
if cond {
disable_local();
}
}
let was_irq_enabled =
f.rflags as u64 & x86_64::registers::rflags::RFlags::INTERRUPT_FLAG.bits() > 0;
let cpu_exception = CpuException::new(f.trap_num, f.error_code);
match cpu_exception {
#[cfg(feature = "cvm_guest")]
Some(CpuException::VirtualizationException) => {
let ve_info = tdcall::get_veinfo().expect("#VE handler: fail to get VE info\n");
enable_local_if(was_irq_enabled);
let mut trapframe_wrapper = TrapFrameWrapper(&mut *f);
handle_virtual_exception(&mut trapframe_wrapper, &ve_info);
*f = *trapframe_wrapper.0;
disable_local_if(was_irq_enabled);
}
Some(CpuException::PageFault(raw_page_fault_info)) => {
enable_local_if(was_irq_enabled);
if (0..MAX_USERSPACE_VADDR).contains(&raw_page_fault_info.addr) {
handle_user_page_fault(f, cpu_exception.as_ref().unwrap());
} else {
panic!(
"Cannot handle kernel page fault: {:#x?}; trapframe: {:#x?}",
raw_page_fault_info, f
);
}
disable_local_if(was_irq_enabled);
}
Some(exception) => {
enable_local_if(was_irq_enabled);
panic!(
"Cannot handle kernel CPU exception: {:#x?}; trapframe: {:#x?}",
exception, f
);
}
None => {
call_irq_callback_functions(
f,
&HwIrqLine::new(f.trap_num as u8),
PrivilegeLevel::Kernel,
);
}
}
}
#[expect(clippy::type_complexity)]
static USER_PAGE_FAULT_HANDLER: Once<fn(&CpuException) -> core::result::Result<(), ()>> =
Once::new();
pub fn inject_user_page_fault_handler(
handler: fn(info: &CpuException) -> core::result::Result<(), ()>,
) {
USER_PAGE_FAULT_HANDLER.call_once(|| handler);
}
fn handle_user_page_fault(f: &mut TrapFrame, exception: &CpuException) {
let handler = USER_PAGE_FAULT_HANDLER
.get()
.expect("a page fault handler is missing");
let res = handler(exception);
if res.is_ok() {
return;
}
if let Some(addr) = ExTable::find_recovery_inst_addr(f.rip) {
f.rip = addr;
} else {
panic!("Cannot handle user page fault; trapframe: {:#x?}", f);
}
}