1use alloc::boxed::Box;
6use core::arch::x86_64::{_fxrstor64, _fxsave64, _xrstor64, _xsave64};
7
8use bitflags::bitflags;
9use cfg_if::cfg_if;
10use ostd_pod::{FromZeros, IntoBytes};
11use spin::Once;
12use x86::bits64::segmentation::{rdfsbase, rdgsbase, swapgs, wrfsbase, wrgsbase};
13use x86_64::registers::{
14 control::{Cr0, Cr0Flags},
15 rflags::RFlags,
16 xcontrol::XCr0,
17};
18
19use crate::{
20 arch::{
21 irq::HwIrqLine,
22 trap::{RawUserContext, TrapFrame},
23 },
24 cpu::PrivilegeLevel,
25 debug,
26 irq::{DisabledLocalIrqGuard, call_irq_callback_functions},
27 mm::Vaddr,
28 user::{ReturnReason, UserContextApi, UserContextApiInternal},
29};
30
31cfg_if! {
32 if #[cfg(feature = "cvm_guest")] {
33 mod tdx;
34
35 use tdx::VirtualizationExceptionHandler;
36 }
37}
38
39#[repr(C)]
41#[derive(Clone, Debug, Default)]
42pub struct UserContext {
43 user_context: RawUserContext,
44 exception: Option<CpuException>,
45}
46
47#[expect(missing_docs)]
49#[repr(C)]
50#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
51pub struct GeneralRegs {
52 pub rax: usize,
53 pub rbx: usize,
54 pub rcx: usize,
55 pub rdx: usize,
56 pub rsi: usize,
57 pub rdi: usize,
58 pub rbp: usize,
59 pub rsp: usize,
60 pub r8: usize,
61 pub r9: usize,
62 pub r10: usize,
63 pub r11: usize,
64 pub r12: usize,
65 pub r13: usize,
66 pub r14: usize,
67 pub r15: usize,
68 pub rip: usize,
69 pub rflags: usize,
70}
71
72#[derive(Clone, Copy, Debug, Default)]
74pub struct FsBase(usize);
75
76impl FsBase {
77 pub fn new(addr: usize) -> Self {
79 Self(addr)
80 }
81
82 pub fn addr(&self) -> usize {
84 self.0
85 }
86
87 pub fn save(&mut self) {
89 self.0 = unsafe { rdfsbase() as usize };
91 }
92
93 pub fn load(&self) {
95 unsafe { wrfsbase(self.0 as u64) }
97 }
98}
99
100#[derive(Clone, Copy, Debug, Default)]
102pub struct GsBase(usize);
103
104impl GsBase {
105 pub fn new(addr: usize) -> Self {
107 Self(addr)
108 }
109
110 pub fn addr(&self) -> usize {
112 self.0
113 }
114
115 pub fn save(&mut self, _guard: &DisabledLocalIrqGuard) {
117 unsafe {
121 swapgs();
122 self.0 = rdgsbase() as usize;
123 swapgs();
124 }
125 }
126
127 pub fn load(&self, _guard: &DisabledLocalIrqGuard) {
129 unsafe {
133 swapgs();
134 wrgsbase(self.0 as u64);
135 swapgs();
136 }
137 }
138}
139
140#[derive(Clone, Copy, Debug, Eq, PartialEq)]
157pub enum CpuException {
158 DivisionError,
160 Debug,
162 NonMaskableInterrupt,
164 BreakPoint,
166 Overflow,
168 BoundRangeExceeded,
170 InvalidOpcode,
172 DeviceNotAvailable,
174 DoubleFault,
176 CoprocessorSegmentOverrun,
178 InvalidTss(SelectorErrorCode),
180 SegmentNotPresent(SelectorErrorCode),
182 StackSegmentFault(SelectorErrorCode),
184 GeneralProtectionFault(Option<SelectorErrorCode>),
186 PageFault(RawPageFaultInfo),
188 X87FloatingPointException,
191 AlignmentCheck,
193 MachineCheck,
195 SIMDFloatingPointException,
197 VirtualizationException,
199 ControlProtectionException,
201 HypervisorInjectionException,
204 VMMCommunicationException,
206 SecurityException,
208 Reserved,
211}
212
213impl CpuException {
214 pub(crate) fn new(trap_num: usize, error_code: usize) -> Option<Self> {
215 let exception = match trap_num {
216 0 => Self::DivisionError,
217 1 => Self::Debug,
218 2 => Self::NonMaskableInterrupt,
219 3 => Self::BreakPoint,
220 4 => Self::Overflow,
221 5 => Self::BoundRangeExceeded,
222 6 => Self::InvalidOpcode,
223 7 => Self::DeviceNotAvailable,
224 8 => {
225 debug_assert_eq!(error_code, 0);
227 Self::DoubleFault
228 }
229 9 => Self::CoprocessorSegmentOverrun,
230 10 => Self::InvalidTss(SelectorErrorCode(error_code)),
231 11 => Self::SegmentNotPresent(SelectorErrorCode(error_code)),
232 12 => Self::StackSegmentFault(SelectorErrorCode(error_code)),
233 13 => {
234 let error_code = if error_code == 0 {
235 None
236 } else {
237 Some(SelectorErrorCode(error_code))
238 };
239 Self::GeneralProtectionFault(error_code)
240 }
241 14 => {
242 let page_fault_addr = x86_64::registers::control::Cr2::read_raw() as usize;
243 Self::PageFault(RawPageFaultInfo {
244 error_code: PageFaultErrorCode::from_bits(error_code).unwrap(),
245 addr: page_fault_addr,
246 })
247 }
248 16 => Self::X87FloatingPointException,
250 17 => Self::AlignmentCheck,
251 18 => Self::MachineCheck,
252 19 => Self::SIMDFloatingPointException,
253 20 => Self::VirtualizationException,
254 21 => Self::ControlProtectionException,
255 28 => Self::HypervisorInjectionException,
257 29 => Self::VMMCommunicationException,
258 30 => Self::SecurityException,
259 15 | 22..=27 | 31 => Self::Reserved,
261 _ => return None,
262 };
263
264 Some(exception)
265 }
266
267 const fn type_(&self) -> CpuExceptionType {
268 match self {
269 Self::Debug => CpuExceptionType::FaultOrTrap,
270 Self::NonMaskableInterrupt => CpuExceptionType::Interrupt,
271 Self::BreakPoint | Self::Overflow => CpuExceptionType::Trap,
272 Self::DoubleFault | Self::MachineCheck => CpuExceptionType::Abort,
273 Self::Reserved => CpuExceptionType::Reserved,
274 _ => CpuExceptionType::Fault,
275 }
276 }
277
278 pub(crate) const fn is_cpu_exception(trap_num: usize) -> bool {
279 trap_num <= 31
280 }
281}
282
283#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
287pub struct SelectorErrorCode(usize);
288
289impl UserContext {
290 pub fn general_regs(&self) -> &GeneralRegs {
292 &self.user_context.general
293 }
294
295 pub fn general_regs_mut(&mut self) -> &mut GeneralRegs {
297 &mut self.user_context.general
298 }
299
300 pub fn take_exception(&mut self) -> Option<CpuException> {
302 self.exception.take()
303 }
304}
305
306impl UserContextApiInternal for UserContext {
307 fn execute<F>(&mut self, mut has_kernel_event: F) -> ReturnReason
308 where
309 F: FnMut() -> bool,
310 {
311 self.user_context.general.rflags |= (RFlags::INTERRUPT_FLAG | RFlags::ID).bits() as usize;
314
315 const SYSCALL_TRAPNUM: usize = 0x100;
316
317 loop {
319 crate::task::scheduler::might_preempt();
320 self.user_context.run();
321
322 let exception =
323 CpuException::new(self.user_context.trap_num, self.user_context.error_code);
324 match exception {
325 #[cfg(feature = "cvm_guest")]
326 Some(CpuException::VirtualizationException) => {
327 let ve_handler = VirtualizationExceptionHandler::new();
328 crate::arch::irq::enable_local();
331 ve_handler.handle(self);
332 }
333 Some(exception) if exception.type_().is_fault_or_trap() => {
334 crate::arch::irq::enable_local();
335 self.exception = Some(exception);
336 return ReturnReason::UserException;
337 }
338 Some(exception) => {
339 panic!(
340 "cannot handle user CPU exception: {:?}, trapframe: {:?}",
341 exception,
342 self.as_trap_frame()
343 );
344 }
345 None if self.user_context.trap_num == SYSCALL_TRAPNUM => {
346 crate::arch::irq::enable_local();
347 return ReturnReason::UserSyscall;
348 }
349 None => {
350 call_irq_callback_functions(
351 &self.as_trap_frame(),
352 &HwIrqLine::new(self.as_trap_frame().trap_num as u8),
353 PrivilegeLevel::User,
354 );
355 crate::arch::irq::enable_local();
356 }
357 }
358
359 if has_kernel_event() {
360 break ReturnReason::KernelEvent;
361 }
362 }
363 }
364
365 fn as_trap_frame(&self) -> TrapFrame {
366 TrapFrame {
367 rax: self.user_context.general.rax,
368 rbx: self.user_context.general.rbx,
369 rcx: self.user_context.general.rcx,
370 rdx: self.user_context.general.rdx,
371 rsi: self.user_context.general.rsi,
372 rdi: self.user_context.general.rdi,
373 rbp: self.user_context.general.rbp,
374 r8: self.user_context.general.r8,
375 r9: self.user_context.general.r9,
376 r10: self.user_context.general.r10,
377 r11: self.user_context.general.r11,
378 r12: self.user_context.general.r12,
379 r13: self.user_context.general.r13,
380 r14: self.user_context.general.r14,
381 r15: self.user_context.general.r15,
382 trap_num: self.user_context.trap_num,
383 error_code: self.user_context.error_code,
384 rip: self.user_context.general.rip,
385 cs: 0,
386 rflags: self.user_context.general.rflags,
387 rsp: self.user_context.general.rsp,
388 ss: 0,
389 }
390 }
391}
392
393#[derive(Clone, Copy, Debug, Eq, PartialEq)]
405pub enum CpuExceptionType {
406 Fault,
408 Trap,
410 FaultOrTrap,
412 Interrupt,
414 Abort,
416 Reserved,
418}
419
420impl CpuExceptionType {
421 pub fn is_fault_or_trap(self) -> bool {
423 match self {
424 CpuExceptionType::Trap | CpuExceptionType::Fault | CpuExceptionType::FaultOrTrap => {
425 true
426 }
427 CpuExceptionType::Abort | CpuExceptionType::Interrupt | CpuExceptionType::Reserved => {
428 false
429 }
430 }
431 }
432}
433
434#[derive(Clone, Copy, Debug, Eq, PartialEq)]
436pub struct RawPageFaultInfo {
437 pub error_code: PageFaultErrorCode,
439 pub addr: Vaddr,
441}
442
443bitflags! {
444 pub struct PageFaultErrorCode : usize{
446 const PRESENT = 1 << 0;
448 const WRITE = 1 << 1;
450 const USER = 1 << 2;
452 const RESERVED = 1 << 3;
455 const INSTRUCTION = 1 << 4;
457 const PROTECTION = 1 << 5;
460 const SHADOW_STACK = 1 << 6;
462 const HLAT = 1 << 7;
464 const SGX = 1 << 15;
467 }
468}
469
470impl UserContextApi for UserContext {
471 fn trap_number(&self) -> usize {
472 self.user_context.trap_num
473 }
474
475 fn trap_error_code(&self) -> usize {
476 self.user_context.error_code
477 }
478
479 fn set_instruction_pointer(&mut self, ip: usize) {
480 self.set_rip(ip);
481 }
482
483 fn set_stack_pointer(&mut self, sp: usize) {
484 self.set_rsp(sp)
485 }
486
487 fn stack_pointer(&self) -> usize {
488 self.rsp()
489 }
490
491 fn instruction_pointer(&self) -> usize {
492 self.rip()
493 }
494}
495
496macro_rules! cpu_context_impl_getter_setter {
497 ( $( [ $field: ident, $setter_name: ident] ),*) => {
498 impl UserContext {
499 $(
500 #[doc = concat!("Gets the value of ", stringify!($field))]
501 #[inline(always)]
502 pub fn $field(&self) -> usize {
503 self.user_context.general.$field
504 }
505
506 #[doc = concat!("Sets the value of ", stringify!(field))]
507 #[inline(always)]
508 pub fn $setter_name(&mut self, $field: usize) {
509 self.user_context.general.$field = $field;
510 }
511 )*
512 }
513 };
514}
515
516cpu_context_impl_getter_setter!(
517 [rax, set_rax],
518 [rbx, set_rbx],
519 [rcx, set_rcx],
520 [rdx, set_rdx],
521 [rsi, set_rsi],
522 [rdi, set_rdi],
523 [rbp, set_rbp],
524 [rsp, set_rsp],
525 [r8, set_r8],
526 [r9, set_r9],
527 [r10, set_r10],
528 [r11, set_r11],
529 [r12, set_r12],
530 [r13, set_r13],
531 [r14, set_r14],
532 [r15, set_r15],
533 [rip, set_rip],
534 [rflags, set_rflags]
535);
536
537#[derive(Debug)]
541pub struct FpuContext {
542 xsave_area: Box<XSaveArea>,
543 area_size: usize,
544}
545
546impl FpuContext {
547 pub fn new() -> Self {
549 let mut area_size = size_of::<FxSaveArea>();
550 if let Some(xsave_area_size) = XSAVE_AREA_SIZE.get() {
551 area_size = area_size.max(*xsave_area_size);
552 }
553
554 Self {
555 xsave_area: Box::new(XSaveArea::new()),
556 area_size,
557 }
558 }
559
560 pub fn save(&mut self) {
562 let mem_addr = self.as_bytes_mut().as_mut_ptr();
563
564 if XSTATE_MAX_FEATURES.is_completed() {
565 unsafe { _xsave64(mem_addr, XFEATURE_MASK_USER_RESTORE) };
566 } else {
567 unsafe { _fxsave64(mem_addr) };
568 }
569
570 debug!("Save FPU context");
571 }
572
573 pub fn load(&self) {
575 let mem_addr = self.as_bytes().as_ptr();
576
577 if let Some(xstate_max_features) = XSTATE_MAX_FEATURES.get() {
578 let rs_mask = XFEATURE_MASK_USER_RESTORE & *xstate_max_features;
579
580 unsafe { _xrstor64(mem_addr, rs_mask) };
581 } else {
582 unsafe { _fxrstor64(mem_addr) };
583 }
584
585 debug!("Load FPU context");
586 }
587
588 pub fn as_bytes(&self) -> &[u8] {
590 &self.xsave_area.as_bytes()[..self.area_size]
591 }
592
593 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
595 &mut self.xsave_area.as_mut_bytes()[..self.area_size]
596 }
597}
598
599impl Default for FpuContext {
600 fn default() -> Self {
601 Self::new()
602 }
603}
604
605impl Clone for FpuContext {
606 fn clone(&self) -> Self {
607 let mut xsave_area = Box::new(XSaveArea::new());
608 xsave_area.fxsave_area = self.xsave_area.fxsave_area;
609 xsave_area.features = self.xsave_area.features;
610 xsave_area.compaction = self.xsave_area.compaction;
611 if self.area_size > size_of::<FxSaveArea>() {
612 let len = self.area_size - size_of::<FxSaveArea>() - 64;
613 xsave_area.extended_state_area[..len]
614 .copy_from_slice(&self.xsave_area.extended_state_area[..len]);
615 }
616
617 Self {
618 xsave_area,
619 area_size: self.area_size,
620 }
621 }
622}
623
624#[repr(C)]
626#[repr(align(64))]
627#[derive(Clone, Copy, Debug, Pod)]
628struct XSaveArea {
629 fxsave_area: FxSaveArea,
630 features: u64,
631 compaction: u64,
632 reserved: [u64; 6],
633 extended_state_area: [u8; MAX_XSAVE_AREA_SIZE - size_of::<FxSaveArea>() - 64],
634}
635
636impl XSaveArea {
637 fn new() -> Self {
638 let features = if let Some(xstate_max_features) = XSTATE_MAX_FEATURES.get() {
639 XCr0::read().bits() & *xstate_max_features
640 } else {
641 0
642 };
643
644 let mut xsave_area = Self::new_zeroed();
645 xsave_area.fxsave_area.control = 0x037F;
648 xsave_area.fxsave_area.tag = 0;
655 xsave_area.fxsave_area.mxcsr = 0x1F80;
656 xsave_area.features = features;
657
658 xsave_area
659 }
660}
661
662#[repr(C)]
664#[repr(align(16))]
665#[derive(Clone, Copy, Debug, Pod)]
666struct FxSaveArea {
667 control: u16, status: u16, tag: u8, reserved1: u8, op: u16, ip: u32, cs: u32, dp: u32, ds: u32, mxcsr: u32, mxcsr_mask: u32, st_space: [u32; 32], xmm_space: [u32; 64], reserved2: [u32; 12], reserved3: [u32; 12], }
683
684static XSTATE_MAX_FEATURES: Once<u64> = Once::new();
686
687const XFEATURE_MASK_USER_RESTORE: u64 = 0b1110_0111;
691
692static XSAVE_AREA_SIZE: Once<usize> = Once::new();
694
695const MAX_XSAVE_AREA_SIZE: usize = 4096;
697
698pub(in crate::arch) fn enable_essential_features() {
699 use super::extension::{IsaExtensions, has_extensions};
700
701 if has_extensions(IsaExtensions::XSAVE) {
702 XSTATE_MAX_FEATURES.call_once(|| super::cpuid::query_xstate_max_features().unwrap());
703 XSAVE_AREA_SIZE.call_once(|| {
704 let xsave_area_size = super::cpuid::query_xsave_area_size().unwrap() as usize;
705 assert!(xsave_area_size <= MAX_XSAVE_AREA_SIZE);
706 xsave_area_size
707 });
708 }
709
710 {
713 let mut cr0 = Cr0::read();
714 cr0.remove(Cr0Flags::TASK_SWITCHED | Cr0Flags::EMULATE_COPROCESSOR);
715
716 unsafe {
717 Cr0::write(cr0);
718 core::arch::asm!("fninit");
720 }
721 }
722}