466 lines
14 KiB
Rust

// SPDX-License-Identifier: MPL-2.0
//! CPU.
pub mod local;
use core::{
arch::x86_64::{_fxrstor, _fxsave},
fmt::Debug,
};
use bitflags::bitflags;
use cfg_if::cfg_if;
use log::debug;
pub use trapframe::GeneralRegs as RawGeneralRegs;
use trapframe::UserContext as RawUserContext;
use x86_64::registers::rflags::RFlags;
use crate::{
task::scheduler,
trap::call_irq_callback_functions,
user::{ReturnReason, UserContextApi, UserContextApiInternal},
};
cfg_if! {
if #[cfg(feature = "cvm_guest")] {
mod tdx;
use tdx::handle_virtualization_exception;
}
}
/// Cpu context, including both general-purpose registers and floating-point registers.
#[derive(Clone, Default, Copy, Debug)]
#[repr(C)]
pub struct UserContext {
user_context: RawUserContext,
fp_regs: FpRegs,
cpu_exception_info: CpuExceptionInfo,
}
/// CPU exception information.
#[derive(Clone, Default, Copy, Debug)]
#[repr(C)]
pub struct CpuExceptionInfo {
/// The ID of the exception.
pub id: usize,
/// The error code associated with the exception.
pub error_code: usize,
/// The virtual address where a page fault occurred.
pub page_fault_addr: usize,
}
impl UserContext {
/// Returns a reference to the general registers.
pub fn general_regs(&self) -> &RawGeneralRegs {
&self.user_context.general
}
/// Returns a mutable reference to the general registers
pub fn general_regs_mut(&mut self) -> &mut RawGeneralRegs {
&mut self.user_context.general
}
/// Returns the trap information.
pub fn trap_information(&self) -> &CpuExceptionInfo {
&self.cpu_exception_info
}
/// Returns a reference to the floating point registers
pub fn fp_regs(&self) -> &FpRegs {
&self.fp_regs
}
/// Returns a mutable reference to the floating point registers
pub fn fp_regs_mut(&mut self) -> &mut FpRegs {
&mut self.fp_regs
}
}
impl UserContextApiInternal for UserContext {
fn execute<F>(&mut self, mut has_kernel_event: F) -> ReturnReason
where
F: FnMut() -> bool,
{
// set interrupt flag so that in user mode it can receive external interrupts
// set ID flag which means cpu support CPUID instruction
self.user_context.general.rflags |= (RFlags::INTERRUPT_FLAG | RFlags::ID).bits() as usize;
let return_reason: ReturnReason;
const SYSCALL_TRAPNUM: u16 = 0x100;
// return when it is syscall or cpu exception type is Fault or Trap.
loop {
scheduler::might_preempt();
self.user_context.run();
match CpuException::to_cpu_exception(self.user_context.trap_num as u16) {
Some(exception) => {
#[cfg(feature = "cvm_guest")]
if *exception == VIRTUALIZATION_EXCEPTION {
handle_virtualization_exception(self);
continue;
}
if exception.typ == CpuExceptionType::FaultOrTrap
|| exception.typ == CpuExceptionType::Fault
|| exception.typ == CpuExceptionType::Trap
{
return_reason = ReturnReason::UserException;
break;
}
}
None => {
if self.user_context.trap_num as u16 == SYSCALL_TRAPNUM {
return_reason = ReturnReason::UserSyscall;
break;
}
}
};
call_irq_callback_functions(&self.as_trap_frame(), self.as_trap_frame().trap_num);
if has_kernel_event() {
return_reason = ReturnReason::KernelEvent;
break;
}
}
crate::arch::irq::enable_local();
if return_reason == ReturnReason::UserException {
self.cpu_exception_info = CpuExceptionInfo {
page_fault_addr: unsafe { x86::controlregs::cr2() },
id: self.user_context.trap_num,
error_code: self.user_context.error_code,
};
}
return_reason
}
fn as_trap_frame(&self) -> trapframe::TrapFrame {
trapframe::TrapFrame {
rax: self.user_context.general.rax,
rbx: self.user_context.general.rbx,
rcx: self.user_context.general.rcx,
rdx: self.user_context.general.rdx,
rsi: self.user_context.general.rsi,
rdi: self.user_context.general.rdi,
rbp: self.user_context.general.rbp,
rsp: self.user_context.general.rsp,
r8: self.user_context.general.r8,
r9: self.user_context.general.r9,
r10: self.user_context.general.r10,
r11: self.user_context.general.r11,
r12: self.user_context.general.r12,
r13: self.user_context.general.r13,
r14: self.user_context.general.r14,
r15: self.user_context.general.r15,
_pad: 0,
trap_num: self.user_context.trap_num,
error_code: self.user_context.error_code,
rip: self.user_context.general.rip,
cs: 0,
rflags: self.user_context.general.rflags,
}
}
}
/// As Osdev Wiki defines(<https://wiki.osdev.org/Exceptions>):
/// CPU exceptions are classified as:
///
/// Faults: These can be corrected and the program may continue as if nothing happened.
///
/// Traps: Traps are reported immediately after the execution of the trapping instruction.
///
/// Aborts: Some severe unrecoverable error.
///
/// But there exists some vector which are special. Vector 1 can be both fault or trap and vector 2 is interrupt.
/// So here we also define FaultOrTrap and Interrupt
#[derive(PartialEq, Eq, Debug)]
pub enum CpuExceptionType {
/// CPU faults. Faults can be corrected, and the program may continue as if nothing happened.
Fault,
/// CPU traps. Traps are reported immediately after the execution of the trapping instruction
Trap,
/// Faults or traps
FaultOrTrap,
/// CPU interrupts
Interrupt,
/// Some severe unrecoverable error
Abort,
/// Reserved for future use
Reserved,
}
/// CPU exception.
#[derive(Debug, Eq, PartialEq)]
pub struct CpuException {
/// The ID of the CPU exception.
pub number: u16,
/// The type of the CPU exception.
pub typ: CpuExceptionType,
}
/// Copy from: https://veykril.github.io/tlborm/decl-macros/building-blocks/counting.html#slice-length
macro_rules! replace_expr {
($_t:tt $sub:expr) => {
$sub
};
}
/// Copy from: https://veykril.github.io/tlborm/decl-macros/building-blocks/counting.html#slice-length
macro_rules! count_tts {
($($tts:tt)*) => {<[()]>::len(&[$(replace_expr!($tts ())),*])};
}
macro_rules! define_cpu_exception {
( $([ $name: ident = $exception_num:tt, $exception_type:tt]),* ) => {
const EXCEPTION_LIST : [CpuException;count_tts!($($name)*)] = [
$($name,)*
];
$(
#[doc = concat!("The ", stringify!($name), " exception")]
pub const $name : CpuException = CpuException{
number: $exception_num,
typ: CpuExceptionType::$exception_type,
};
)*
}
}
// We also defined the RESERVED Exception so that we can easily use the index of EXCEPTION_LIST to get the Exception
define_cpu_exception!(
[DIVIDE_BY_ZERO = 0, Fault],
[DEBUG = 1, FaultOrTrap],
[NON_MASKABLE_INTERRUPT = 2, Interrupt],
[BREAKPOINT = 3, Trap],
[OVERFLOW = 4, Trap],
[BOUND_RANGE_EXCEEDED = 5, Fault],
[INVALID_OPCODE = 6, Fault],
[DEVICE_NOT_AVAILABLE = 7, Fault],
[DOUBLE_FAULT = 8, Abort],
[COPROCESSOR_SEGMENT_OVERRUN = 9, Fault],
[INVALID_TSS = 10, Fault],
[SEGMENT_NOT_PRESENT = 11, Fault],
[STACK_SEGMENT_FAULT = 12, Fault],
[GENERAL_PROTECTION_FAULT = 13, Fault],
[PAGE_FAULT = 14, Fault],
[RESERVED_15 = 15, Reserved],
[X87_FLOATING_POINT_EXCEPTION = 16, Fault],
[ALIGNMENT_CHECK = 17, Fault],
[MACHINE_CHECK = 18, Abort],
[SIMD_FLOATING_POINT_EXCEPTION = 19, Fault],
[VIRTUALIZATION_EXCEPTION = 20, Fault],
[CONTROL_PROTECTION_EXCEPTION = 21, Fault],
[RESERVED_22 = 22, Reserved],
[RESERVED_23 = 23, Reserved],
[RESERVED_24 = 24, Reserved],
[RESERVED_25 = 25, Reserved],
[RESERVED_26 = 26, Reserved],
[RESERVED_27 = 27, Reserved],
[HYPERVISOR_INJECTION_EXCEPTION = 28, Fault],
[VMM_COMMUNICATION_EXCEPTION = 29, Fault],
[SECURITY_EXCEPTION = 30, Fault],
[RESERVED_31 = 31, Reserved]
);
bitflags! {
/// Page Fault error code. Following the Intel Architectures Software Developer's Manual Volume 3
pub struct PageFaultErrorCode : usize{
/// 0 if no translation for the linear address.
const PRESENT = 1 << 0;
/// 1 if the access was a write.
const WRITE = 1 << 1;
/// 1 if the access was a user-mode access.
const USER = 1 << 2;
/// 1 if there is no translation for the linear address
/// because a reserved bit was set.
const RESERVED = 1 << 3;
/// 1 if the access was an instruction fetch.
const INSTRUCTION = 1 << 4;
/// 1 if the access was a data access to a linear address with a protection key for which
/// the protection-key rights registers disallow access.
const PROTECTION = 1 << 5;
/// 1 if the access was a shadow-stack access.
const SHADOW_STACK = 1 << 6;
/// 1 if there is no translation for the linear address using HLAT paging.
const HLAT = 1 << 7;
/// 1 if the exception is unrelated to paging and resulted from violation of SGX-specific
/// access-control requirements.
const SGX = 1 << 15;
}
}
impl CpuException {
/// Checks if the given `trap_num` is a valid CPU exception.
pub fn is_cpu_exception(trap_num: u16) -> bool {
trap_num < EXCEPTION_LIST.len() as u16
}
/// Maps a `trap_num` to its corresponding CPU exception.
pub fn to_cpu_exception(trap_num: u16) -> Option<&'static CpuException> {
EXCEPTION_LIST.get(trap_num as usize)
}
}
impl UserContextApi for UserContext {
fn trap_number(&self) -> usize {
self.user_context.trap_num
}
fn trap_error_code(&self) -> usize {
self.user_context.error_code
}
fn set_instruction_pointer(&mut self, ip: usize) {
self.set_rip(ip);
}
fn set_stack_pointer(&mut self, sp: usize) {
self.set_rsp(sp)
}
fn stack_pointer(&self) -> usize {
self.rsp()
}
fn instruction_pointer(&self) -> usize {
self.rip()
}
}
macro_rules! cpu_context_impl_getter_setter {
( $( [ $field: ident, $setter_name: ident] ),*) => {
impl UserContext {
$(
#[doc = concat!("Gets the value of ", stringify!($field))]
#[inline(always)]
pub fn $field(&self) -> usize {
self.user_context.general.$field
}
#[doc = concat!("Sets the value of ", stringify!(field))]
#[inline(always)]
pub fn $setter_name(&mut self, $field: usize) {
self.user_context.general.$field = $field;
}
)*
}
};
}
cpu_context_impl_getter_setter!(
[rax, set_rax],
[rbx, set_rbx],
[rcx, set_rcx],
[rdx, set_rdx],
[rsi, set_rsi],
[rdi, set_rdi],
[rbp, set_rbp],
[rsp, set_rsp],
[r8, set_r8],
[r9, set_r9],
[r10, set_r10],
[r11, set_r11],
[r12, set_r12],
[r13, set_r13],
[r14, set_r14],
[r15, set_r15],
[rip, set_rip],
[rflags, set_rflags],
[fsbase, set_fsbase],
[gsbase, set_gsbase]
);
/// The floating-point state of CPU.
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct FpRegs {
buf: FxsaveArea,
is_valid: bool,
}
impl FpRegs {
/// Creates a new instance.
///
/// Note that a newly-created instance's floating point state is not
/// initialized, thus considered invalid (i.e., `self.is_valid() == false`).
pub fn new() -> Self {
// The buffer address requires 16bytes alignment.
Self {
buf: FxsaveArea { data: [0; 512] },
is_valid: false,
}
}
/// Save CPU's current floating pointer states into this instance.
pub fn save(&mut self) {
debug!("save fpregs");
debug!("write addr = 0x{:x}", (&mut self.buf) as *mut _ as usize);
let layout = alloc::alloc::Layout::for_value(&self.buf);
debug!("layout: {:?}", layout);
let ptr = unsafe { alloc::alloc::alloc(layout) } as usize;
debug!("ptr = 0x{:x}", ptr);
unsafe {
_fxsave(self.buf.data.as_mut_ptr());
}
debug!("save fpregs success");
self.is_valid = true;
}
/// Saves the floating state given by a slice of u8.
///
/// After calling this method, the state of the instance will be considered valid.
///
/// # Safety
///
/// It is the caller's responsibility to ensure that the source slice contains
/// data that is in xsave/xrstor format. The slice must have a length of 512 bytes.
pub unsafe fn save_from_slice(&mut self, src: &[u8]) {
self.buf.data.copy_from_slice(src);
self.is_valid = true;
}
/// Returns whether the instance can contains data in valid xsave/xrstor format.
pub fn is_valid(&self) -> bool {
self.is_valid
}
/// Clears the state of the instance.
///
/// This method does not reset the underlying buffer that contains the floating
/// point state; it only marks the buffer __invalid__.
pub fn clear(&mut self) {
self.is_valid = false;
}
/// Restores CPU's CPU floating pointer states from this instance.
///
/// # Panics
///
/// If the current state is invalid, the method will panic.
pub fn restore(&self) {
debug!("restore fpregs");
assert!(self.is_valid);
unsafe { _fxrstor(self.buf.data.as_ptr()) };
debug!("restore fpregs success");
}
/// Returns the floating point state as a slice.
///
/// Note that the slice may contain garbage if `self.is_valid() == false`.
pub fn as_slice(&self) -> &[u8] {
&self.buf.data
}
}
impl Default for FpRegs {
fn default() -> Self {
Self::new()
}
}
#[repr(C, align(16))]
#[derive(Debug, Clone, Copy)]
struct FxsaveArea {
data: [u8; 512], // 512 bytes
}