Skip to main content

ostd/arch/x86/cpu/context/
mod.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! CPU execution context control.
4
5use 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/// Userspace CPU context, including general-purpose registers and exception information.
40#[repr(C)]
41#[derive(Clone, Debug, Default)]
42pub struct UserContext {
43    user_context: RawUserContext,
44    exception: Option<CpuException>,
45}
46
47/// General registers.
48#[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/// The user-mode FS base register.
73#[derive(Clone, Copy, Debug, Default)]
74pub struct FsBase(usize);
75
76impl FsBase {
77    /// Creates a new `FsBase` with the given address.
78    pub fn new(addr: usize) -> Self {
79        Self(addr)
80    }
81
82    /// Returns the stored address.
83    pub fn addr(&self) -> usize {
84        self.0
85    }
86
87    /// Saves the current CPU FS base into this struct.
88    pub fn save(&mut self) {
89        // SAFETY: Reading the user FS base does not affect kernel code.
90        self.0 = unsafe { rdfsbase() as usize };
91    }
92
93    /// Loads this struct's FS base onto the CPU.
94    pub fn load(&self) {
95        // SAFETY: Writing the user FS base does not affect kernel code.
96        unsafe { wrfsbase(self.0 as u64) }
97    }
98}
99
100/// The user-mode GS base register.
101#[derive(Clone, Copy, Debug, Default)]
102pub struct GsBase(usize);
103
104impl GsBase {
105    /// Creates a new `GsBase` with the given address.
106    pub fn new(addr: usize) -> Self {
107        Self(addr)
108    }
109
110    /// Returns the stored address.
111    pub fn addr(&self) -> usize {
112        self.0
113    }
114
115    /// Saves the current CPU GS base into this struct.
116    pub fn save(&mut self, _guard: &DisabledLocalIrqGuard) {
117        // SAFETY:
118        // 1. In these steps, we have disabled the IRQ and are not using the kernel GS base.
119        // 2. Reading the user GS base does not affect kernel code.
120        unsafe {
121            swapgs();
122            self.0 = rdgsbase() as usize;
123            swapgs();
124        }
125    }
126
127    /// Loads this struct's GS base onto the CPU.
128    pub fn load(&self, _guard: &DisabledLocalIrqGuard) {
129        // SAFETY:
130        // 1. In these steps, we have disabled the IRQ and are not using the kernel GS base.
131        // 2. Writing the user GS base does not affect kernel code.
132        unsafe {
133            swapgs();
134            wrgsbase(self.0 as u64);
135            swapgs();
136        }
137    }
138}
139
140/// Architectural CPU exceptions (x86-64 vectors 0-31).
141///
142/// For the authoritative specification of each vector, see the
143/// Intel® 64 and IA-32 Architectures Software Developer’s Manual,
144/// Volume 3 “System Programming Guide”, Chapter 6 “Interrupt and Exception
145/// Handling”, in particular Section 6.15 “Exception and Interrupt
146/// Reference”.
147///
148/// Every enum variant corresponds to one exception defined by the
149/// Intel/AMD architecture.
150/// Variants that naturally carry an error code (or other error information)
151/// expose it through their associated data fields.
152//
153// TODO: Some exceptions (like `AlignmentCheck`) also push an
154//       error code onto the stack, but that detail is not yet represented
155//       in this type definition.
156#[derive(Clone, Copy, Debug, Eq, PartialEq)]
157pub enum CpuException {
158    ///  0 – #DE  Divide-by-zero error.
159    DivisionError,
160    ///  1 – #DB  Debug.
161    Debug,
162    ///  2 – NMI  Non-maskable interrupt.
163    NonMaskableInterrupt,
164    ///  3 – #BP  Breakpoint (INT3).
165    BreakPoint,
166    ///  4 – #OF  Overflow.
167    Overflow,
168    ///  5 – #BR  Bound-range exceeded.
169    BoundRangeExceeded,
170    ///  6 – #UD  Invalid or undefined opcode.
171    InvalidOpcode,
172    ///  7 – #NM  Device not available (FPU/MMX/SSE disabled).
173    DeviceNotAvailable,
174    ///  8 – #DF  Double fault (always pushes an error code of 0).
175    DoubleFault,
176    ///  9 – Coprocessor segment overrun (reserved on modern CPUs).
177    CoprocessorSegmentOverrun,
178    /// 10 – #TS  Invalid TSS.
179    InvalidTss(SelectorErrorCode),
180    /// 11 – #NP  Segment not present.
181    SegmentNotPresent(SelectorErrorCode),
182    /// 12 – #SS  Stack-segment fault.
183    StackSegmentFault(SelectorErrorCode),
184    /// 13 – #GP  General protection fault
185    GeneralProtectionFault(Option<SelectorErrorCode>),
186    /// 14 – #PF  Page fault.
187    PageFault(RawPageFaultInfo),
188    // 15: Reserved
189    /// 16 – #MF  x87 floating-point exception.
190    X87FloatingPointException,
191    /// 17 – #AC  Alignment check.
192    AlignmentCheck,
193    /// 18 – #MC  Machine check.
194    MachineCheck,
195    /// 19 – #XM / #XF  SIMD/FPU floating-point exception.
196    SIMDFloatingPointException,
197    /// 20 – #VE  Virtualization exception.
198    VirtualizationException,
199    /// 21 – #CP  Control protection exception (CET).
200    ControlProtectionException,
201    // 22-27: Reserved
202    /// 28 – #HV  Hypervisor injection exception.
203    HypervisorInjectionException,
204    /// 29 – #VC  VMM communication exception (SEV-ES GHCB).
205    VMMCommunicationException,
206    /// 30 – #SX  Security exception.
207    SecurityException,
208    // 31: Reserved
209    /// Catch-all for reserved or undefined vector numbers.
210    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                // A double fault will always generate an error code with a value of zero.
226                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            // Reserved 15
249            16 => Self::X87FloatingPointException,
250            17 => Self::AlignmentCheck,
251            18 => Self::MachineCheck,
252            19 => Self::SIMDFloatingPointException,
253            20 => Self::VirtualizationException,
254            21 => Self::ControlProtectionException,
255            // Reserved 22-27
256            28 => Self::HypervisorInjectionException,
257            29 => Self::VMMCommunicationException,
258            30 => Self::SecurityException,
259            // Reserved 31
260            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/// Selector error code.
284///
285/// Reference: <https://wiki.osdev.org/Exceptions#Selector_Error_Code>.
286#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
287pub struct SelectorErrorCode(usize);
288
289impl UserContext {
290    /// Returns a reference to the general registers.
291    pub fn general_regs(&self) -> &GeneralRegs {
292        &self.user_context.general
293    }
294
295    /// Returns a mutable reference to the general registers
296    pub fn general_regs_mut(&mut self) -> &mut GeneralRegs {
297        &mut self.user_context.general
298    }
299
300    /// Takes the CPU exception out.
301    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        // set interrupt flag so that in user mode it can receive external interrupts
312        // set ID flag which means cpu support CPUID instruction
313        self.user_context.general.rflags |= (RFlags::INTERRUPT_FLAG | RFlags::ID).bits() as usize;
314
315        const SYSCALL_TRAPNUM: usize = 0x100;
316
317        // Return when it is syscall or cpu exception type is Fault or Trap.
318        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                    // Check out the doc of `VirtualizationExceptionHandler::new` to
329                    // see why IRQs must enabled _after_ instantiating a `VirtualizationExceptionHandler`.
330                    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            rsp: self.user_context.general.rsp,
375            r8: self.user_context.general.r8,
376            r9: self.user_context.general.r9,
377            r10: self.user_context.general.r10,
378            r11: self.user_context.general.r11,
379            r12: self.user_context.general.r12,
380            r13: self.user_context.general.r13,
381            r14: self.user_context.general.r14,
382            r15: self.user_context.general.r15,
383            trap_num: self.user_context.trap_num,
384            error_code: self.user_context.error_code,
385            rip: self.user_context.general.rip,
386            cs: 0,
387            rflags: self.user_context.general.rflags,
388        }
389    }
390}
391
392/// As Osdev Wiki defines(<https://wiki.osdev.org/Exceptions>):
393/// CPU exceptions are classified as:
394///
395/// Faults: These can be corrected and the program may continue as if nothing happened.
396///
397/// Traps: Traps are reported immediately after the execution of the trapping instruction.
398///
399/// Aborts: Some severe unrecoverable error.
400///
401/// But there exists some vector which are special. Vector 1 can be both fault or trap and vector 2 is interrupt.
402/// So here we also define FaultOrTrap and Interrupt
403#[derive(Clone, Copy, Debug, Eq, PartialEq)]
404pub enum CpuExceptionType {
405    /// CPU faults. Faults can be corrected, and the program may continue as if nothing happened.
406    Fault,
407    /// CPU traps. Traps are reported immediately after the execution of the trapping instruction
408    Trap,
409    /// Faults or traps
410    FaultOrTrap,
411    /// CPU interrupts
412    Interrupt,
413    /// Some severe unrecoverable error
414    Abort,
415    /// Reserved for future use
416    Reserved,
417}
418
419impl CpuExceptionType {
420    /// Returns whether this exception type is a fault or a trap.
421    pub fn is_fault_or_trap(self) -> bool {
422        match self {
423            CpuExceptionType::Trap | CpuExceptionType::Fault | CpuExceptionType::FaultOrTrap => {
424                true
425            }
426            CpuExceptionType::Abort | CpuExceptionType::Interrupt | CpuExceptionType::Reserved => {
427                false
428            }
429        }
430    }
431}
432
433/// Architecture-specific data reported with a page-fault exception.
434#[derive(Clone, Copy, Debug, Eq, PartialEq)]
435pub struct RawPageFaultInfo {
436    /// The error code pushed by the CPU for this page fault.
437    pub error_code: PageFaultErrorCode,
438    /// The linear (virtual) address that triggered the fault (contents of CR2).
439    pub addr: Vaddr,
440}
441
442bitflags! {
443    /// Page Fault error code. Following the Intel Architectures Software Developer's Manual Volume 3
444    pub struct PageFaultErrorCode : usize{
445        /// 0 if no translation for the linear address.
446        const PRESENT       = 1 << 0;
447        /// 1 if the access was a write.
448        const WRITE         = 1 << 1;
449        /// 1 if the access was a user-mode access.
450        const USER          = 1 << 2;
451        /// 1 if there is no translation for the linear address
452        /// because a reserved bit was set.
453        const RESERVED      = 1 << 3;
454        /// 1 if the access was an instruction fetch.
455        const INSTRUCTION   = 1 << 4;
456        /// 1 if the access was a data access to a linear address with a protection key for which
457        /// the protection-key rights registers disallow access.
458        const PROTECTION    = 1 << 5;
459        /// 1 if the access was a shadow-stack access.
460        const SHADOW_STACK  = 1 << 6;
461        /// 1 if there is no translation for the linear address using HLAT paging.
462        const HLAT          = 1 << 7;
463        /// 1 if the exception is unrelated to paging and resulted from violation of SGX-specific
464        /// access-control requirements.
465        const SGX           = 1 << 15;
466    }
467}
468
469impl UserContextApi for UserContext {
470    fn trap_number(&self) -> usize {
471        self.user_context.trap_num
472    }
473
474    fn trap_error_code(&self) -> usize {
475        self.user_context.error_code
476    }
477
478    fn set_instruction_pointer(&mut self, ip: usize) {
479        self.set_rip(ip);
480    }
481
482    fn set_stack_pointer(&mut self, sp: usize) {
483        self.set_rsp(sp)
484    }
485
486    fn stack_pointer(&self) -> usize {
487        self.rsp()
488    }
489
490    fn instruction_pointer(&self) -> usize {
491        self.rip()
492    }
493}
494
495macro_rules! cpu_context_impl_getter_setter {
496    ( $( [ $field: ident, $setter_name: ident] ),*) => {
497        impl UserContext {
498            $(
499                #[doc = concat!("Gets the value of ", stringify!($field))]
500                #[inline(always)]
501                pub fn $field(&self) -> usize {
502                    self.user_context.general.$field
503                }
504
505                #[doc = concat!("Sets the value of ", stringify!(field))]
506                #[inline(always)]
507                pub fn $setter_name(&mut self, $field: usize) {
508                    self.user_context.general.$field = $field;
509                }
510            )*
511        }
512    };
513}
514
515cpu_context_impl_getter_setter!(
516    [rax, set_rax],
517    [rbx, set_rbx],
518    [rcx, set_rcx],
519    [rdx, set_rdx],
520    [rsi, set_rsi],
521    [rdi, set_rdi],
522    [rbp, set_rbp],
523    [rsp, set_rsp],
524    [r8, set_r8],
525    [r9, set_r9],
526    [r10, set_r10],
527    [r11, set_r11],
528    [r12, set_r12],
529    [r13, set_r13],
530    [r14, set_r14],
531    [r15, set_r15],
532    [rip, set_rip],
533    [rflags, set_rflags]
534);
535
536/// The FPU context of user task.
537///
538/// This could be used for saving both legacy and modern state format.
539#[derive(Debug)]
540pub struct FpuContext {
541    xsave_area: Box<XSaveArea>,
542    area_size: usize,
543}
544
545impl FpuContext {
546    /// Creates a new FPU context.
547    pub fn new() -> Self {
548        let mut area_size = size_of::<FxSaveArea>();
549        if let Some(xsave_area_size) = XSAVE_AREA_SIZE.get() {
550            area_size = area_size.max(*xsave_area_size);
551        }
552
553        Self {
554            xsave_area: Box::new(XSaveArea::new()),
555            area_size,
556        }
557    }
558
559    /// Saves CPU's current FPU context to this instance.
560    pub fn save(&mut self) {
561        let mem_addr = self.as_bytes_mut().as_mut_ptr();
562
563        if XSTATE_MAX_FEATURES.is_completed() {
564            unsafe { _xsave64(mem_addr, XFEATURE_MASK_USER_RESTORE) };
565        } else {
566            unsafe { _fxsave64(mem_addr) };
567        }
568
569        debug!("Save FPU context");
570    }
571
572    /// Loads CPU's FPU context from this instance.
573    pub fn load(&self) {
574        let mem_addr = self.as_bytes().as_ptr();
575
576        if let Some(xstate_max_features) = XSTATE_MAX_FEATURES.get() {
577            let rs_mask = XFEATURE_MASK_USER_RESTORE & *xstate_max_features;
578
579            unsafe { _xrstor64(mem_addr, rs_mask) };
580        } else {
581            unsafe { _fxrstor64(mem_addr) };
582        }
583
584        debug!("Load FPU context");
585    }
586
587    /// Returns the FPU context as a byte slice.
588    pub fn as_bytes(&self) -> &[u8] {
589        &self.xsave_area.as_bytes()[..self.area_size]
590    }
591
592    /// Returns the FPU context as a mutable byte slice.
593    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
594        &mut self.xsave_area.as_mut_bytes()[..self.area_size]
595    }
596}
597
598impl Default for FpuContext {
599    fn default() -> Self {
600        Self::new()
601    }
602}
603
604impl Clone for FpuContext {
605    fn clone(&self) -> Self {
606        let mut xsave_area = Box::new(XSaveArea::new());
607        xsave_area.fxsave_area = self.xsave_area.fxsave_area;
608        xsave_area.features = self.xsave_area.features;
609        xsave_area.compaction = self.xsave_area.compaction;
610        if self.area_size > size_of::<FxSaveArea>() {
611            let len = self.area_size - size_of::<FxSaveArea>() - 64;
612            xsave_area.extended_state_area[..len]
613                .copy_from_slice(&self.xsave_area.extended_state_area[..len]);
614        }
615
616        Self {
617            xsave_area,
618            area_size: self.area_size,
619        }
620    }
621}
622
623/// The modern FPU context format (as saved and restored by the `XSAVE` and `XRSTOR` instructions).
624#[repr(C)]
625#[repr(align(64))]
626#[derive(Clone, Copy, Debug, Pod)]
627struct XSaveArea {
628    fxsave_area: FxSaveArea,
629    features: u64,
630    compaction: u64,
631    reserved: [u64; 6],
632    extended_state_area: [u8; MAX_XSAVE_AREA_SIZE - size_of::<FxSaveArea>() - 64],
633}
634
635impl XSaveArea {
636    fn new() -> Self {
637        let features = if let Some(xstate_max_features) = XSTATE_MAX_FEATURES.get() {
638            XCr0::read().bits() & *xstate_max_features
639        } else {
640            0
641        };
642
643        let mut xsave_area = Self::new_zeroed();
644        // Set the initial values for the FPU context. Refer to Intel SDM, Table 10-1:
645        // "IA-32 and Intel® 64 Processor States Following Power-up, Reset, or INIT (Contd.)".
646        xsave_area.fxsave_area.control = 0x037F;
647        // Refer to Intel SDM, Volume 1, Section "x87 State". In the FXSAVE/XSAVE image the
648        // `tag` field contains an abridged x87 Tag Word (FTW) - a compact (8-bit) encoding
649        // used in saved/restore images. In this format, a bit value of 0 indicates the
650        // corresponding x87 register is empty (this is the inverse of the legacy 16-bit
651        // tag-word semantics). The `fninit` instruction clears all x87 registers, so the
652        // abridged tag must be initialized to 0 to represent an empty register set.
653        xsave_area.fxsave_area.tag = 0;
654        xsave_area.fxsave_area.mxcsr = 0x1F80;
655        xsave_area.features = features;
656
657        xsave_area
658    }
659}
660
661/// The legacy SSE/MMX FPU context format (as saved and restored by the `FXSAVE` and `FXRSTOR` instructions).
662#[repr(C)]
663#[repr(align(16))]
664#[derive(Clone, Copy, Debug, Pod)]
665struct FxSaveArea {
666    control: u16,         // x87 FPU Control Word
667    status: u16,          // x87 FPU Status Word
668    tag: u8,              // x87 FPU Tag Byte (abridged format)
669    reserved1: u8,        // Reserved
670    op: u16,              // x87 FPU Last Instruction Opcode
671    ip: u32,              // x87 FPU Instruction Pointer Offset
672    cs: u32,              // x87 FPU Instruction Pointer Selector
673    dp: u32,              // x87 FPU Instruction Operand (Data) Pointer Offset
674    ds: u32,              // x87 FPU Instruction Operand (Data) Pointer Selector
675    mxcsr: u32,           // MXCSR Register State
676    mxcsr_mask: u32,      // MXCSR Mask
677    st_space: [u32; 32], // x87 FPU or MMX technology registers (ST0-ST7 or MM0-MM7, 128 bits per field)
678    xmm_space: [u32; 64], // XMM registers (XMM0-XMM15, 128 bits per field)
679    reserved2: [u32; 12], // Reserved
680    reserved3: [u32; 12], // Software reserved
681}
682
683/// The XSTATE features (user & supervisor) supported by the processor.
684static XSTATE_MAX_FEATURES: Once<u64> = Once::new();
685
686/// Mask features which are restored when returning to user space.
687///
688/// X87 | SSE | AVX | OPMASK | ZMM_HI256 | HI16_ZMM
689const XFEATURE_MASK_USER_RESTORE: u64 = 0b1110_0111;
690
691/// The real size in bytes of the XSAVE area containing all states enabled by XCRO | IA32_XSS.
692static XSAVE_AREA_SIZE: Once<usize> = Once::new();
693
694/// The max size in bytes of the XSAVE area.
695const MAX_XSAVE_AREA_SIZE: usize = 4096;
696
697pub(in crate::arch) fn enable_essential_features() {
698    use super::extension::{IsaExtensions, has_extensions};
699
700    if has_extensions(IsaExtensions::XSAVE) {
701        XSTATE_MAX_FEATURES.call_once(|| super::cpuid::query_xstate_max_features().unwrap());
702        XSAVE_AREA_SIZE.call_once(|| {
703            let xsave_area_size = super::cpuid::query_xsave_area_size().unwrap() as usize;
704            assert!(xsave_area_size <= MAX_XSAVE_AREA_SIZE);
705            xsave_area_size
706        });
707    }
708
709    // We now assume that all x86-64 CPUs should have the FPU. Otherwise, we should check
710    // `has_extensions(IsaExtensions::FPU)` here.
711    {
712        let mut cr0 = Cr0::read();
713        cr0.remove(Cr0Flags::TASK_SWITCHED | Cr0Flags::EMULATE_COPROCESSOR);
714
715        unsafe {
716            Cr0::write(cr0);
717            // Flush out any pending x87 state.
718            core::arch::asm!("fninit");
719        }
720    }
721}