Skip to main content

ostd/arch/x86/trap/
mod.rs

1// SPDX-License-Identifier: MPL-2.0 OR MIT
2//
3// The original source code is from [trapframe-rs](https://github.com/rcore-os/trapframe-rs),
4// which is released under the following license:
5//
6// SPDX-License-Identifier: MIT
7//
8// Copyright (c) 2020 - 2024 Runji Wang
9//
10// We make the following new changes:
11// * Implement the `trap_handler` of Asterinas.
12//
13// These changes are released under the following license:
14//
15// SPDX-License-Identifier: MPL-2.0
16
17//! Handles trap.
18
19pub(super) mod gdt;
20mod idt;
21mod syscall;
22
23use super::cpu::context::GeneralRegs;
24use crate::{
25    arch::{
26        cpu::context::CpuException,
27        irq::{HwIrqLine, disable_local, enable_local},
28    },
29    cpu::PrivilegeLevel,
30    irq::call_irq_callback_functions,
31    mm::fault::TrapFrameApi,
32};
33
34cfg_select! {
35    feature = "cvm_guest" => {
36        use tdx_guest::{tdcall, handle_virtual_exception};
37        use crate::arch::tdx_guest::TrapFrameWrapper;
38    }
39}
40
41/// Trap frame of kernel interrupt
42///
43/// # Trap handler
44///
45/// You need to define a handler function like this:
46///
47/// ```
48/// // SAFETY: The name does not collide with other symbols.
49/// #[unsafe(no_mangle)]
50/// extern "sysv64" fn trap_handler(tf: &mut TrapFrame) {
51///     match tf.trap_num {
52///         3 => {
53///             println!("TRAP: BreakPoint");
54///             tf.rip += 1;
55///         }
56///         _ => panic!("TRAP: {:#x?}", tf),
57///     }
58/// }
59/// ```
60#[expect(missing_docs)]
61#[repr(C)]
62#[derive(Clone, Copy, Debug, Default)]
63pub struct TrapFrame {
64    // Pushed by 'trap.S'
65    pub rax: usize,
66    pub rbx: usize,
67    pub rcx: usize,
68    pub rdx: usize,
69    pub rsi: usize,
70    pub rdi: usize,
71    pub rbp: usize,
72    pub r8: usize,
73    pub r9: usize,
74    pub r10: usize,
75    pub r11: usize,
76    pub r12: usize,
77    pub r13: usize,
78    pub r14: usize,
79    pub r15: usize,
80
81    pub trap_num: usize,
82    pub error_code: usize,
83
84    // Pushed by CPU
85    pub rip: usize,
86    pub cs: usize,
87    pub rflags: usize,
88    pub rsp: usize,
89    pub ss: usize,
90}
91
92// Be careful: This assertion is a **soundness** requirement.
93//
94// According to the System V AMD64 ABI, the stack pointer should be aligned to
95// at least 16 bytes. The hardware will align the stack pointer to a 16-byte
96// boundary for exceptions and interrupts ("In IA-32e mode, the RSP is aligned
97// to a 16-byte boundary before pushing the stack frame"), so we only need to
98// ensure the size of a `TrapFrame` is also aligned.
99crate::const_assert!(size_of::<TrapFrame>().is_multiple_of(16));
100
101impl TrapFrameApi for TrapFrame {
102    fn set_instruction_pointer(&mut self, ip: usize) {
103        self.rip = ip;
104    }
105
106    fn instruction_pointer(&self) -> usize {
107        self.rip
108    }
109}
110
111/// Initializes interrupt handling on x86_64.
112///
113/// This function will:
114/// - Switch to a new, CPU-local [GDT].
115/// - Switch to a new, CPU-local [TSS].
116/// - Switch to a new, global [IDT].
117/// - Enable the [`syscall`] instruction.
118///
119/// [GDT]: https://wiki.osdev.org/GDT
120/// [IDT]: https://wiki.osdev.org/IDT
121/// [TSS]: https://wiki.osdev.org/Task_State_Segment
122/// [`syscall`]: https://www.felixcloutier.com/x86/syscall
123///
124/// # Safety
125///
126/// On the current CPU, this function must be called
127/// - only once and
128/// - before any trap can occur.
129pub(crate) unsafe fn init_on_cpu() {
130    // SAFETY: Since there's no traps, no preemption can occur.
131    unsafe { gdt::init_on_cpu() };
132
133    idt::init_on_cpu();
134
135    // SAFETY: `gdt::init_on_cpu` has been called before.
136    unsafe { syscall::init_on_cpu() };
137}
138
139/// Userspace context.
140#[repr(C)]
141#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
142pub(super) struct RawUserContext {
143    pub(super) general: GeneralRegs,
144    pub(super) trap_num: usize,
145    pub(super) error_code: usize,
146}
147
148/// Handle traps (only from kernel).
149// SAFETY: The name does not collide with other symbols.
150#[unsafe(no_mangle)]
151unsafe extern "sysv64" fn trap_handler(f: &mut TrapFrame) {
152    fn enable_local_if(cond: bool) {
153        if cond {
154            enable_local();
155        }
156    }
157
158    fn disable_local_if(cond: bool) {
159        if cond {
160            disable_local();
161        }
162    }
163
164    // The IRQ state before trapping. We need to ensure that the IRQ state
165    // during exception handling is consistent with the state before the trap.
166    let was_irq_enabled =
167        f.rflags as u64 & x86_64::registers::rflags::RFlags::INTERRUPT_FLAG.bits() > 0;
168
169    let cpu_exception = CpuException::new(f.trap_num, f.error_code);
170    match cpu_exception {
171        #[cfg(feature = "cvm_guest")]
172        Some(CpuException::VirtualizationException) => {
173            let ve_info = tdcall::get_veinfo().expect("#VE handler: fail to get VE info\n");
174            // We need to enable interrupts only after `tdcall::get_veinfo` is called
175            // to avoid nested `#VE`s.
176            enable_local_if(was_irq_enabled);
177            let mut trapframe_wrapper = TrapFrameWrapper(&mut *f);
178            handle_virtual_exception(&mut trapframe_wrapper, &ve_info);
179            *f = *trapframe_wrapper.0;
180            disable_local_if(was_irq_enabled);
181        }
182        Some(page_fault @ CpuException::PageFault(raw_page_fault_info)) => {
183            enable_local_if(was_irq_enabled);
184            crate::mm::fault::handle_user_page_fault(f, &page_fault, raw_page_fault_info.addr);
185            disable_local_if(was_irq_enabled);
186        }
187        Some(exception) => {
188            enable_local_if(was_irq_enabled);
189            panic!(
190                "Cannot handle kernel CPU exception: {:#x?}; trapframe: {:#x?}",
191                exception, f
192            );
193        }
194        None => {
195            call_irq_callback_functions(
196                f,
197                &HwIrqLine::new(f.trap_num as u8),
198                PrivilegeLevel::Kernel,
199            );
200        }
201    }
202}
203
204/// User-space code segment selector value.
205pub const USER_CS_VALUE: usize = gdt::USER_CS.0 as usize;
206/// User-space stack segment selector value.
207pub const USER_SS_VALUE: usize = gdt::USER_SS.0 as usize;