1use alloc::boxed::Box;
6use core::arch::x86_64::{_fxrstor64, _fxsave64, _xrstor64, _xsave64};
7
8use bitflags::bitflags;
9use ostd_pod::{FromZeros, IntoBytes};
10use spin::Once;
11use x86::bits64::segmentation::{rdfsbase, rdgsbase, swapgs, wrfsbase, wrgsbase};
12use x86_64::registers::{
13 control::{Cr0, Cr0Flags},
14 rflags::RFlags,
15 xcontrol::XCr0,
16};
17
18use crate::{
19 arch::{
20 irq::HwIrqLine,
21 trap::{RawUserContext, TrapFrame},
22 },
23 cpu::PrivilegeLevel,
24 debug,
25 irq::{DisabledLocalIrqGuard, call_irq_callback_functions},
26 mm::Vaddr,
27 user::{ReturnReason, UserContextApi, UserContextApiInternal, UserModeHooks},
28};
29
30cfg_select! {
31 feature = "cvm_guest" => {
32 mod tdx;
33
34 use tdx::VirtualizationExceptionHandler;
35 }
36}
37
38#[repr(C)]
40#[derive(Clone, Debug, Default)]
41pub struct UserContext {
42 user_context: RawUserContext,
43 exception: Option<CpuException>,
44}
45
46#[expect(missing_docs)]
48#[repr(C)]
49#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
50pub struct GeneralRegs {
51 pub rax: usize,
52 pub rbx: usize,
53 pub rcx: usize,
54 pub rdx: usize,
55 pub rsi: usize,
56 pub rdi: usize,
57 pub rbp: usize,
58 pub rsp: usize,
59 pub r8: usize,
60 pub r9: usize,
61 pub r10: usize,
62 pub r11: usize,
63 pub r12: usize,
64 pub r13: usize,
65 pub r14: usize,
66 pub r15: usize,
67 pub rip: usize,
68 pub rflags: usize,
69}
70
71#[derive(Clone, Copy, Debug, Default)]
73pub struct FsBase(usize);
74
75impl FsBase {
76 pub fn new(addr: usize) -> Self {
78 Self(addr)
79 }
80
81 pub fn addr(&self) -> usize {
83 self.0
84 }
85
86 pub fn save(&mut self) {
88 self.0 = unsafe { rdfsbase() as usize };
90 }
91
92 pub fn load(&self) {
94 unsafe { wrfsbase(self.0 as u64) }
96 }
97}
98
99#[derive(Clone, Copy, Debug, Default)]
101pub struct GsBase(usize);
102
103impl GsBase {
104 pub fn new(addr: usize) -> Self {
106 Self(addr)
107 }
108
109 pub fn addr(&self) -> usize {
111 self.0
112 }
113
114 pub fn save(&mut self, _guard: &DisabledLocalIrqGuard) {
116 unsafe {
120 swapgs();
121 self.0 = rdgsbase() as usize;
122 swapgs();
123 }
124 }
125
126 pub fn load(&self, _guard: &DisabledLocalIrqGuard) {
128 unsafe {
132 swapgs();
133 wrgsbase(self.0 as u64);
134 swapgs();
135 }
136 }
137}
138
139#[derive(Clone, Copy, Debug, Eq, PartialEq)]
156pub enum CpuException {
157 DivisionError,
159 Debug,
161 NonMaskableInterrupt,
163 BreakPoint,
165 Overflow,
167 BoundRangeExceeded,
169 InvalidOpcode,
171 DeviceNotAvailable,
173 DoubleFault,
175 CoprocessorSegmentOverrun,
177 InvalidTss(SelectorErrorCode),
179 SegmentNotPresent(SelectorErrorCode),
181 StackSegmentFault(SelectorErrorCode),
183 GeneralProtectionFault(Option<SelectorErrorCode>),
185 PageFault(RawPageFaultInfo),
187 X87FloatingPointException,
190 AlignmentCheck,
192 MachineCheck,
194 SIMDFloatingPointException,
196 VirtualizationException,
198 ControlProtectionException,
200 HypervisorInjectionException,
203 VMMCommunicationException,
205 SecurityException,
207 Reserved,
210}
211
212impl CpuException {
213 pub(in crate::arch) fn new(trap_num: usize, error_code: usize) -> Option<Self> {
214 let exception = match trap_num {
215 0 => Self::DivisionError,
216 1 => Self::Debug,
217 2 => Self::NonMaskableInterrupt,
218 3 => Self::BreakPoint,
219 4 => Self::Overflow,
220 5 => Self::BoundRangeExceeded,
221 6 => Self::InvalidOpcode,
222 7 => Self::DeviceNotAvailable,
223 8 => {
224 debug_assert_eq!(error_code, 0);
226 Self::DoubleFault
227 }
228 9 => Self::CoprocessorSegmentOverrun,
229 10 => Self::InvalidTss(SelectorErrorCode(error_code)),
230 11 => Self::SegmentNotPresent(SelectorErrorCode(error_code)),
231 12 => Self::StackSegmentFault(SelectorErrorCode(error_code)),
232 13 => {
233 let error_code = if error_code == 0 {
234 None
235 } else {
236 Some(SelectorErrorCode(error_code))
237 };
238 Self::GeneralProtectionFault(error_code)
239 }
240 14 => {
241 let page_fault_addr = x86_64::registers::control::Cr2::read_raw() as usize;
242 Self::PageFault(RawPageFaultInfo {
243 error_code: PageFaultErrorCode::from_bits(error_code).unwrap(),
244 addr: page_fault_addr,
245 })
246 }
247 16 => Self::X87FloatingPointException,
249 17 => Self::AlignmentCheck,
250 18 => Self::MachineCheck,
251 19 => Self::SIMDFloatingPointException,
252 20 => Self::VirtualizationException,
253 21 => Self::ControlProtectionException,
254 28 => Self::HypervisorInjectionException,
256 29 => Self::VMMCommunicationException,
257 30 => Self::SecurityException,
258 15 | 22..=27 | 31 => Self::Reserved,
260 _ => return None,
261 };
262
263 Some(exception)
264 }
265
266 const fn type_(&self) -> CpuExceptionType {
267 match self {
268 Self::Debug => CpuExceptionType::FaultOrTrap,
269 Self::NonMaskableInterrupt => CpuExceptionType::Interrupt,
270 Self::BreakPoint | Self::Overflow => CpuExceptionType::Trap,
271 Self::DoubleFault | Self::MachineCheck => CpuExceptionType::Abort,
272 Self::Reserved => CpuExceptionType::Reserved,
273 _ => CpuExceptionType::Fault,
274 }
275 }
276
277 pub(in crate::arch) const fn is_cpu_exception(trap_num: usize) -> bool {
278 trap_num <= 31
279 }
280}
281
282#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
286pub struct SelectorErrorCode(usize);
287
288impl UserContext {
289 pub fn general_regs(&self) -> &GeneralRegs {
291 &self.user_context.general
292 }
293
294 pub fn general_regs_mut(&mut self) -> &mut GeneralRegs {
296 &mut self.user_context.general
297 }
298
299 pub fn take_exception(&mut self) -> Option<CpuException> {
301 self.exception.take()
302 }
303}
304
305impl UserContextApiInternal for UserContext {
306 fn execute<T: UserModeHooks>(&mut self, hooks: &T) -> ReturnReason {
307 self.user_context.general.rflags |= (RFlags::INTERRUPT_FLAG | RFlags::ID).bits() as usize;
310
311 const SYSCALL_TRAPNUM: usize = 0x100;
312
313 loop {
315 crate::task::scheduler::might_preempt();
316
317 let guard = crate::irq::disable_local();
318 hooks.pre_user_run(&guard);
319 self.user_context.run(guard);
320
321 let exception =
322 CpuException::new(self.user_context.trap_num, self.user_context.error_code);
323 match exception {
324 #[cfg(feature = "cvm_guest")]
325 Some(CpuException::VirtualizationException) => {
326 let ve_handler = VirtualizationExceptionHandler::new();
327 crate::arch::irq::enable_local();
330 ve_handler.handle(self);
331 }
332 Some(exception) if exception.type_().is_fault_or_trap() => {
333 crate::arch::irq::enable_local();
334 self.exception = Some(exception);
335 return ReturnReason::UserException;
336 }
337 Some(exception) => {
338 panic!(
339 "Cannot handle user CPU exception: {:?}; trapframe: {:?}",
340 exception,
341 self.as_trap_frame()
342 );
343 }
344 None if self.user_context.trap_num == SYSCALL_TRAPNUM => {
345 crate::arch::irq::enable_local();
346 return ReturnReason::UserSyscall;
347 }
348 None => {
349 call_irq_callback_functions(
350 &self.as_trap_frame(),
351 &HwIrqLine::new(self.as_trap_frame().trap_num as u8),
352 PrivilegeLevel::User,
353 );
354 crate::arch::irq::enable_local();
355 }
356 }
357
358 if hooks.has_kernel_event() {
359 break ReturnReason::KernelEvent;
360 }
361 }
362 }
363
364 fn as_trap_frame(&self) -> TrapFrame {
365 TrapFrame {
366 rax: self.user_context.general.rax,
367 rbx: self.user_context.general.rbx,
368 rcx: self.user_context.general.rcx,
369 rdx: self.user_context.general.rdx,
370 rsi: self.user_context.general.rsi,
371 rdi: self.user_context.general.rdi,
372 rbp: self.user_context.general.rbp,
373 r8: self.user_context.general.r8,
374 r9: self.user_context.general.r9,
375 r10: self.user_context.general.r10,
376 r11: self.user_context.general.r11,
377 r12: self.user_context.general.r12,
378 r13: self.user_context.general.r13,
379 r14: self.user_context.general.r14,
380 r15: self.user_context.general.r15,
381 trap_num: self.user_context.trap_num,
382 error_code: self.user_context.error_code,
383 rip: self.user_context.general.rip,
384 cs: 0,
385 rflags: self.user_context.general.rflags,
386 rsp: self.user_context.general.rsp,
387 ss: 0,
388 }
389 }
390}
391
392#[derive(Clone, Copy, Debug, Eq, PartialEq)]
402enum CpuExceptionType {
403 Fault,
405 Trap,
407 FaultOrTrap,
409 Interrupt,
411 Abort,
413 Reserved,
415}
416
417impl CpuExceptionType {
418 fn is_fault_or_trap(self) -> bool {
420 match self {
421 CpuExceptionType::Trap | CpuExceptionType::Fault | CpuExceptionType::FaultOrTrap => {
422 true
423 }
424 CpuExceptionType::Abort | CpuExceptionType::Interrupt | CpuExceptionType::Reserved => {
425 false
426 }
427 }
428 }
429}
430
431#[derive(Clone, Copy, Debug, Eq, PartialEq)]
433pub struct RawPageFaultInfo {
434 pub error_code: PageFaultErrorCode,
436 pub addr: Vaddr,
438}
439
440bitflags! {
441 pub struct PageFaultErrorCode : usize{
443 const PRESENT = 1 << 0;
445 const WRITE = 1 << 1;
447 const USER = 1 << 2;
449 const RESERVED = 1 << 3;
452 const INSTRUCTION = 1 << 4;
454 const PROTECTION = 1 << 5;
457 const SHADOW_STACK = 1 << 6;
459 const HLAT = 1 << 7;
461 const SGX = 1 << 15;
464 }
465}
466
467impl UserContextApi for UserContext {
468 fn set_instruction_pointer(&mut self, ip: usize) {
469 self.set_rip(ip);
470 }
471
472 fn set_stack_pointer(&mut self, sp: usize) {
473 self.set_rsp(sp)
474 }
475
476 fn stack_pointer(&self) -> usize {
477 self.rsp()
478 }
479
480 fn instruction_pointer(&self) -> usize {
481 self.rip()
482 }
483}
484
485macro_rules! cpu_context_impl_getter_setter {
486 ( $( [ $field: ident, $setter_name: ident] ),*) => {
487 impl UserContext {
488 $(
489 #[doc = concat!("Gets the value of ", stringify!($field))]
490 #[inline(always)]
491 pub fn $field(&self) -> usize {
492 self.user_context.general.$field
493 }
494
495 #[doc = concat!("Sets the value of ", stringify!(field))]
496 #[inline(always)]
497 pub fn $setter_name(&mut self, $field: usize) {
498 self.user_context.general.$field = $field;
499 }
500 )*
501 }
502 };
503}
504
505cpu_context_impl_getter_setter!(
506 [rax, set_rax],
507 [rbx, set_rbx],
508 [rcx, set_rcx],
509 [rdx, set_rdx],
510 [rsi, set_rsi],
511 [rdi, set_rdi],
512 [rbp, set_rbp],
513 [rsp, set_rsp],
514 [r8, set_r8],
515 [r9, set_r9],
516 [r10, set_r10],
517 [r11, set_r11],
518 [r12, set_r12],
519 [r13, set_r13],
520 [r14, set_r14],
521 [r15, set_r15],
522 [rip, set_rip],
523 [rflags, set_rflags]
524);
525
526#[derive(Debug)]
530pub struct FpuContext {
531 xsave_area: Box<XSaveArea>,
532 area_size: usize,
533}
534
535impl FpuContext {
536 pub fn new() -> Self {
538 let mut area_size = size_of::<FxSaveArea>();
539 if let Some(xsave_area_size) = XSAVE_AREA_SIZE.get() {
540 area_size = area_size.max(*xsave_area_size);
541 }
542
543 Self {
544 xsave_area: Box::new(XSaveArea::new()),
545 area_size,
546 }
547 }
548
549 pub fn save(&mut self) {
551 let mem_addr = self.as_bytes_mut().as_mut_ptr();
552
553 if XSTATE_MAX_FEATURES.is_completed() {
554 unsafe { _xsave64(mem_addr, XFEATURE_MASK_USER_RESTORE) };
555 } else {
556 unsafe { _fxsave64(mem_addr) };
557 }
558
559 debug!("Save FPU context");
560 }
561
562 pub fn load(&self) {
564 let mem_addr = self.as_bytes().as_ptr();
565
566 if let Some(xstate_max_features) = XSTATE_MAX_FEATURES.get() {
567 let rs_mask = XFEATURE_MASK_USER_RESTORE & *xstate_max_features;
568
569 unsafe { _xrstor64(mem_addr, rs_mask) };
570 } else {
571 unsafe { _fxrstor64(mem_addr) };
572 }
573
574 debug!("Load FPU context");
575 }
576
577 pub fn as_bytes(&self) -> &[u8] {
579 &self.xsave_area.as_bytes()[..self.area_size]
580 }
581
582 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
584 &mut self.xsave_area.as_mut_bytes()[..self.area_size]
585 }
586}
587
588impl Default for FpuContext {
589 fn default() -> Self {
590 Self::new()
591 }
592}
593
594impl Clone for FpuContext {
595 fn clone(&self) -> Self {
596 let mut xsave_area = Box::new(XSaveArea::new());
597 xsave_area.fxsave_area = self.xsave_area.fxsave_area;
598 xsave_area.features = self.xsave_area.features;
599 xsave_area.compaction = self.xsave_area.compaction;
600 if self.area_size > size_of::<FxSaveArea>() {
601 let len = self.area_size - size_of::<FxSaveArea>() - 64;
602 xsave_area.extended_state_area[..len]
603 .copy_from_slice(&self.xsave_area.extended_state_area[..len]);
604 }
605
606 Self {
607 xsave_area,
608 area_size: self.area_size,
609 }
610 }
611}
612
613#[repr(C)]
615#[repr(align(64))]
616#[derive(Clone, Copy, Debug, Pod)]
617struct XSaveArea {
618 fxsave_area: FxSaveArea,
619 features: u64,
620 compaction: u64,
621 reserved: [u64; 6],
622 extended_state_area: [u8; MAX_XSAVE_AREA_SIZE - size_of::<FxSaveArea>() - 64],
623}
624
625impl XSaveArea {
626 fn new() -> Self {
627 let features = if let Some(xstate_max_features) = XSTATE_MAX_FEATURES.get() {
628 XCr0::read().bits() & *xstate_max_features
629 } else {
630 0
631 };
632
633 let mut xsave_area = Self::new_zeroed();
634 xsave_area.fxsave_area.control = 0x037F;
637 xsave_area.fxsave_area.tag = 0;
644 xsave_area.fxsave_area.mxcsr = 0x1F80;
645 xsave_area.features = features;
646
647 xsave_area
648 }
649}
650
651#[repr(C)]
653#[repr(align(16))]
654#[derive(Clone, Copy, Debug, Pod)]
655struct FxSaveArea {
656 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], }
672
673static XSTATE_MAX_FEATURES: Once<u64> = Once::new();
675
676const XFEATURE_MASK_USER_RESTORE: u64 = 0b1110_0111;
680
681static XSAVE_AREA_SIZE: Once<usize> = Once::new();
683
684const MAX_XSAVE_AREA_SIZE: usize = 4096;
686
687pub(in crate::arch) fn enable_essential_features() {
688 use super::extension::{IsaExtensions, has_extensions};
689
690 if has_extensions(IsaExtensions::XSAVE) {
691 XSTATE_MAX_FEATURES.call_once(|| super::cpuid::query_xstate_max_features().unwrap());
692 XSAVE_AREA_SIZE.call_once(|| {
693 let xsave_area_size = super::cpuid::query_xsave_area_size().unwrap() as usize;
694 assert!(xsave_area_size <= MAX_XSAVE_AREA_SIZE);
695 xsave_area_size
696 });
697 }
698
699 {
702 let mut cr0 = Cr0::read();
703 cr0.remove(Cr0Flags::TASK_SWITCHED | Cr0Flags::EMULATE_COPROCESSOR);
704
705 unsafe {
706 Cr0::write(cr0);
707 core::arch::asm!("fninit");
709 }
710 }
711}