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            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/// As Osdev Wiki defines(<https://wiki.osdev.org/Exceptions>):
394/// CPU exceptions are classified as:
395///
396/// Faults: These can be corrected and the program may continue as if nothing happened.
397///
398/// Traps: Traps are reported immediately after the execution of the trapping instruction.
399///
400/// Aborts: Some severe unrecoverable error.
401///
402/// But there exists some vector which are special. Vector 1 can be both fault or trap and vector 2 is interrupt.
403/// So here we also define FaultOrTrap and Interrupt
404#[derive(Clone, Copy, Debug, Eq, PartialEq)]
405pub enum CpuExceptionType {
406    /// CPU faults. Faults can be corrected, and the program may continue as if nothing happened.
407    Fault,
408    /// CPU traps. Traps are reported immediately after the execution of the trapping instruction
409    Trap,
410    /// Faults or traps
411    FaultOrTrap,
412    /// CPU interrupts
413    Interrupt,
414    /// Some severe unrecoverable error
415    Abort,
416    /// Reserved for future use
417    Reserved,
418}
419
420impl CpuExceptionType {
421    /// Returns whether this exception type is a fault or a trap.
422    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/// Architecture-specific data reported with a page-fault exception.
435#[derive(Clone, Copy, Debug, Eq, PartialEq)]
436pub struct RawPageFaultInfo {
437    /// The error code pushed by the CPU for this page fault.
438    pub error_code: PageFaultErrorCode,
439    /// The linear (virtual) address that triggered the fault (contents of CR2).
440    pub addr: Vaddr,
441}
442
443bitflags! {
444    /// Page Fault error code. Following the Intel Architectures Software Developer's Manual Volume 3
445    pub struct PageFaultErrorCode : usize{
446        /// 0 if no translation for the linear address.
447        const PRESENT       = 1 << 0;
448        /// 1 if the access was a write.
449        const WRITE         = 1 << 1;
450        /// 1 if the access was a user-mode access.
451        const USER          = 1 << 2;
452        /// 1 if there is no translation for the linear address
453        /// because a reserved bit was set.
454        const RESERVED      = 1 << 3;
455        /// 1 if the access was an instruction fetch.
456        const INSTRUCTION   = 1 << 4;
457        /// 1 if the access was a data access to a linear address with a protection key for which
458        /// the protection-key rights registers disallow access.
459        const PROTECTION    = 1 << 5;
460        /// 1 if the access was a shadow-stack access.
461        const SHADOW_STACK  = 1 << 6;
462        /// 1 if there is no translation for the linear address using HLAT paging.
463        const HLAT          = 1 << 7;
464        /// 1 if the exception is unrelated to paging and resulted from violation of SGX-specific
465        /// access-control requirements.
466        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/// The FPU context of user task.
538///
539/// This could be used for saving both legacy and modern state format.
540#[derive(Debug)]
541pub struct FpuContext {
542    xsave_area: Box<XSaveArea>,
543    area_size: usize,
544}
545
546impl FpuContext {
547    /// Creates a new FPU context.
548    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    /// Saves CPU's current FPU context to this instance.
561    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    /// Loads CPU's FPU context from this instance.
574    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    /// Returns the FPU context as a byte slice.
589    pub fn as_bytes(&self) -> &[u8] {
590        &self.xsave_area.as_bytes()[..self.area_size]
591    }
592
593    /// Returns the FPU context as a mutable byte slice.
594    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/// The modern FPU context format (as saved and restored by the `XSAVE` and `XRSTOR` instructions).
625#[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        // Set the initial values for the FPU context. Refer to Intel SDM, Table 10-1:
646        // "IA-32 and Intel® 64 Processor States Following Power-up, Reset, or INIT (Contd.)".
647        xsave_area.fxsave_area.control = 0x037F;
648        // Refer to Intel SDM, Volume 1, Section "x87 State". In the FXSAVE/XSAVE image the
649        // `tag` field contains an abridged x87 Tag Word (FTW) - a compact (8-bit) encoding
650        // used in saved/restore images. In this format, a bit value of 0 indicates the
651        // corresponding x87 register is empty (this is the inverse of the legacy 16-bit
652        // tag-word semantics). The `fninit` instruction clears all x87 registers, so the
653        // abridged tag must be initialized to 0 to represent an empty register set.
654        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/// The legacy SSE/MMX FPU context format (as saved and restored by the `FXSAVE` and `FXRSTOR` instructions).
663#[repr(C)]
664#[repr(align(16))]
665#[derive(Clone, Copy, Debug, Pod)]
666struct FxSaveArea {
667    control: u16,         // x87 FPU Control Word
668    status: u16,          // x87 FPU Status Word
669    tag: u8,              // x87 FPU Tag Byte (abridged format)
670    reserved1: u8,        // Reserved
671    op: u16,              // x87 FPU Last Instruction Opcode
672    ip: u32,              // x87 FPU Instruction Pointer Offset
673    cs: u32,              // x87 FPU Instruction Pointer Selector
674    dp: u32,              // x87 FPU Instruction Operand (Data) Pointer Offset
675    ds: u32,              // x87 FPU Instruction Operand (Data) Pointer Selector
676    mxcsr: u32,           // MXCSR Register State
677    mxcsr_mask: u32,      // MXCSR Mask
678    st_space: [u32; 32], // x87 FPU or MMX technology registers (ST0-ST7 or MM0-MM7, 128 bits per field)
679    xmm_space: [u32; 64], // XMM registers (XMM0-XMM15, 128 bits per field)
680    reserved2: [u32; 12], // Reserved
681    reserved3: [u32; 12], // Software reserved
682}
683
684/// The XSTATE features (user & supervisor) supported by the processor.
685static XSTATE_MAX_FEATURES: Once<u64> = Once::new();
686
687/// Mask features which are restored when returning to user space.
688///
689/// X87 | SSE | AVX | OPMASK | ZMM_HI256 | HI16_ZMM
690const XFEATURE_MASK_USER_RESTORE: u64 = 0b1110_0111;
691
692/// The real size in bytes of the XSAVE area containing all states enabled by XCRO | IA32_XSS.
693static XSAVE_AREA_SIZE: Once<usize> = Once::new();
694
695/// The max size in bytes of the XSAVE area.
696const 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    // We now assume that all x86-64 CPUs should have the FPU. Otherwise, we should check
711    // `has_extensions(IsaExtensions::FPU)` here.
712    {
713        let mut cr0 = Cr0::read();
714        cr0.remove(Cr0Flags::TASK_SWITCHED | Cr0Flags::EMULATE_COPROCESSOR);
715
716        unsafe {
717            Cr0::write(cr0);
718            // Flush out any pending x87 state.
719            core::arch::asm!("fninit");
720        }
721    }
722}