Extract x86-specific exception handling in aster-nix

This commit is contained in:
YanWQ-monad 2024-07-19 15:42:53 +08:00 committed by Tate, Hongliang Tian
parent a997d9f0b0
commit 4d36dd541f
7 changed files with 86 additions and 142 deletions

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use ostd::{ use ostd::{
cpu::{CpuExceptionInfo, RawGeneralRegs, UserContext, PAGE_FAULT}, cpu::{CpuException, CpuExceptionInfo, RawGeneralRegs, UserContext},
Pod, Pod,
}; };
@ -107,7 +107,7 @@ impl TryFrom<&CpuExceptionInfo> for PageFaultInfo {
type Error = (); type Error = ();
fn try_from(value: &CpuExceptionInfo) -> Result<Self, ()> { fn try_from(value: &CpuExceptionInfo) -> Result<Self, ()> {
if value.cpu_exception() != PAGE_FAULT { if value.cpu_exception() != CpuException::PAGE_FAULT {
return Err(()); return Err(());
} }

View File

@ -1,8 +1,10 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use ostd::cpu::UserContext; use ostd::cpu::{CpuException, CpuExceptionInfo, UserContext};
use crate::process::signal::{sig_num::SigNum, SignalContext}; use crate::process::signal::{
constants::*, sig_num::SigNum, signals::fault::FaultSignal, SignalContext,
};
impl SignalContext for UserContext { impl SignalContext for UserContext {
fn set_arguments(&mut self, sig_num: SigNum, siginfo_addr: usize, ucontext_addr: usize) { fn set_arguments(&mut self, sig_num: SigNum, siginfo_addr: usize, ucontext_addr: usize) {
@ -11,3 +13,30 @@ impl SignalContext for UserContext {
self.set_rdx(ucontext_addr); self.set_rdx(ucontext_addr);
} }
} }
impl From<&CpuExceptionInfo> for FaultSignal {
fn from(trap_info: &CpuExceptionInfo) -> Self {
let exception = CpuException::to_cpu_exception(trap_info.id as u16).unwrap();
let (num, code, addr) = match exception {
CpuException::DIVIDE_BY_ZERO => (SIGFPE, FPE_INTDIV, None),
CpuException::X87_FLOATING_POINT_EXCEPTION
| CpuException::SIMD_FLOATING_POINT_EXCEPTION => (SIGFPE, FPE_FLTDIV, None),
CpuException::BOUND_RANGE_EXCEEDED => (SIGSEGV, SEGV_BNDERR, None),
CpuException::ALIGNMENT_CHECK => (SIGBUS, BUS_ADRALN, None),
CpuException::INVALID_OPCODE => (SIGILL, ILL_ILLOPC, None),
CpuException::GENERAL_PROTECTION_FAULT => (SIGBUS, BUS_ADRERR, None),
CpuException::PAGE_FAULT => {
const PF_ERR_FLAG_PRESENT: usize = 1usize << 0;
let code = if trap_info.error_code & PF_ERR_FLAG_PRESENT != 0 {
SEGV_ACCERR
} else {
SEGV_MAPERR
};
let addr = Some(trap_info.page_fault_addr as u64);
(SIGSEGV, code, addr)
}
_ => panic!("Exception cannot be a signal"),
};
FaultSignal::new(num, code, addr)
}
}

View File

@ -11,7 +11,7 @@ use id_alloc::IdAlloc;
use ostd::{ use ostd::{
arch::{ arch::{
timer::{self, TIMER_FREQ}, timer::{self, TIMER_FREQ},
x86::trap::is_kernel_interrupted, trap::is_kernel_interrupted,
}, },
sync::Mutex, sync::Mutex,
}; };

View File

@ -1,16 +1,11 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use ostd::cpu::{
CpuException, CpuExceptionInfo, ALIGNMENT_CHECK, BOUND_RANGE_EXCEEDED, DIVIDE_BY_ZERO,
GENERAL_PROTECTION_FAULT, INVALID_OPCODE, PAGE_FAULT, SIMD_FLOATING_POINT_EXCEPTION,
X87_FLOATING_POINT_EXCEPTION,
};
use super::Signal; use super::Signal;
use crate::{ use crate::{
prelude::*, prelude::*,
process::signal::{c_types::siginfo_t, constants::*, sig_num::SigNum}, process::signal::{c_types::siginfo_t, sig_num::SigNum},
}; };
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct FaultSignal { pub struct FaultSignal {
num: SigNum, num: SigNum,
@ -19,30 +14,7 @@ pub struct FaultSignal {
} }
impl FaultSignal { impl FaultSignal {
pub fn new(trap_info: &CpuExceptionInfo) -> FaultSignal { pub fn new(num: SigNum, code: i32, addr: Option<u64>) -> FaultSignal {
debug!("Trap id: {}", trap_info.id);
let exception = CpuException::to_cpu_exception(trap_info.id as u16).unwrap();
let (num, code, addr) = match *exception {
DIVIDE_BY_ZERO => (SIGFPE, FPE_INTDIV, None),
X87_FLOATING_POINT_EXCEPTION | SIMD_FLOATING_POINT_EXCEPTION => {
(SIGFPE, FPE_FLTDIV, None)
}
BOUND_RANGE_EXCEEDED => (SIGSEGV, SEGV_BNDERR, None),
ALIGNMENT_CHECK => (SIGBUS, BUS_ADRALN, None),
INVALID_OPCODE => (SIGILL, ILL_ILLOPC, None),
GENERAL_PROTECTION_FAULT => (SIGBUS, BUS_ADRERR, None),
PAGE_FAULT => {
const PF_ERR_FLAG_PRESENT: usize = 1usize << 0;
let code = if trap_info.error_code & PF_ERR_FLAG_PRESENT != 0 {
SEGV_ACCERR
} else {
SEGV_MAPERR
};
let addr = Some(trap_info.page_fault_addr as u64);
(SIGSEGV, code, addr)
}
_ => panic!("Exception cannot be a signal"),
};
FaultSignal { num, code, addr } FaultSignal { num, code, addr }
} }
} }

View File

@ -73,61 +73,19 @@ pub(crate) fn handle_page_fault_from_vmar(
/// generate a fault signal for current process. /// generate a fault signal for current process.
fn generate_fault_signal(trap_info: &CpuExceptionInfo, ctx: &Context) { fn generate_fault_signal(trap_info: &CpuExceptionInfo, ctx: &Context) {
let signal = FaultSignal::new(trap_info); let signal = FaultSignal::from(trap_info);
ctx.posix_thread.enqueue_signal(Box::new(signal)); ctx.posix_thread.enqueue_signal(Box::new(signal));
} }
macro_rules! log_trap_common {
($exception_name: ident, $trap_info: ident) => {
trace!(
"[Trap][{}][err = {}]",
stringify!($exception_name),
$trap_info.error_code
)
};
}
fn log_trap_info(trap_info: &CpuExceptionInfo) { fn log_trap_info(trap_info: &CpuExceptionInfo) {
match trap_info.cpu_exception() { if let Ok(page_fault_info) = PageFaultInfo::try_from(trap_info) {
DIVIDE_BY_ZERO => log_trap_common!(DIVIDE_BY_ZERO, trap_info), trace!(
DEBUG => log_trap_common!(DEBUG, trap_info), "[Trap][PAGE_FAULT][page fault addr = 0x{:x}, err = {}]",
NON_MASKABLE_INTERRUPT => log_trap_common!(NON_MASKABLE_INTERRUPT, trap_info), trap_info.page_fault_addr,
BREAKPOINT => log_trap_common!(BREAKPOINT, trap_info), trap_info.error_code
OVERFLOW => log_trap_common!(OVERFLOW, trap_info), );
BOUND_RANGE_EXCEEDED => log_trap_common!(BOUND_RANGE_EXCEEDED, trap_info), } else {
INVALID_OPCODE => log_trap_common!(INVALID_OPCODE, trap_info), let exception = trap_info.cpu_exception();
DEVICE_NOT_AVAILABLE => log_trap_common!(DEVICE_NOT_AVAILABLE, trap_info), trace!("[Trap][{exception:?}][err = {}]", trap_info.error_code)
DOUBLE_FAULT => log_trap_common!(DOUBLE_FAULT, trap_info),
COPROCESSOR_SEGMENT_OVERRUN => log_trap_common!(COPROCESSOR_SEGMENT_OVERRUN, trap_info),
INVALID_TSS => log_trap_common!(INVALID_TSS, trap_info),
SEGMENT_NOT_PRESENT => log_trap_common!(SEGMENT_NOT_PRESENT, trap_info),
STACK_SEGMENT_FAULT => log_trap_common!(STACK_SEGMENT_FAULT, trap_info),
GENERAL_PROTECTION_FAULT => log_trap_common!(GENERAL_PROTECTION_FAULT, trap_info),
PAGE_FAULT => {
trace!(
"[Trap][{}][page fault addr = 0x{:x}, err = {}]",
stringify!(PAGE_FAULT),
trap_info.page_fault_addr,
trap_info.error_code
);
}
// 15 reserved
X87_FLOATING_POINT_EXCEPTION => log_trap_common!(X87_FLOATING_POINT_EXCEPTION, trap_info),
ALIGNMENT_CHECK => log_trap_common!(ALIGNMENT_CHECK, trap_info),
MACHINE_CHECK => log_trap_common!(MACHINE_CHECK, trap_info),
SIMD_FLOATING_POINT_EXCEPTION => log_trap_common!(SIMD_FLOATING_POINT_EXCEPTION, trap_info),
VIRTUALIZATION_EXCEPTION => log_trap_common!(VIRTUALIZATION_EXCEPTION, trap_info),
CONTROL_PROTECTION_EXCEPTION => log_trap_common!(CONTROL_PROTECTION_EXCEPTION, trap_info),
HYPERVISOR_INJECTION_EXCEPTION => {
log_trap_common!(HYPERVISOR_INJECTION_EXCEPTION, trap_info)
}
VMM_COMMUNICATION_EXCEPTION => log_trap_common!(VMM_COMMUNICATION_EXCEPTION, trap_info),
SECURITY_EXCEPTION => log_trap_common!(SECURITY_EXCEPTION, trap_info),
_ => {
info!(
"[Trap][Unknown trap type][id = {}, err = {}]",
trap_info.id, trap_info.error_code
);
}
} }
} }

View File

@ -12,6 +12,8 @@ use core::{
use bitflags::bitflags; use bitflags::bitflags;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use log::debug; use log::debug;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use x86::bits64::segmentation::wrfsbase; use x86::bits64::segmentation::wrfsbase;
use x86_64::registers::rflags::RFlags; use x86_64::registers::rflags::RFlags;
@ -109,41 +111,37 @@ impl UserContextApiInternal for UserContext {
// set ID flag which means cpu support CPUID instruction // set ID flag which means cpu support CPUID instruction
self.user_context.general.rflags |= (RFlags::INTERRUPT_FLAG | RFlags::ID).bits() as usize; self.user_context.general.rflags |= (RFlags::INTERRUPT_FLAG | RFlags::ID).bits() as usize;
let return_reason: ReturnReason;
const SYSCALL_TRAPNUM: u16 = 0x100; const SYSCALL_TRAPNUM: u16 = 0x100;
// return when it is syscall or cpu exception type is Fault or Trap. // return when it is syscall or cpu exception type is Fault or Trap.
loop { let return_reason = loop {
scheduler::might_preempt(); scheduler::might_preempt();
self.user_context.run(); self.user_context.run();
match CpuException::to_cpu_exception(self.user_context.trap_num as u16) { match CpuException::to_cpu_exception(self.user_context.trap_num as u16) {
Some(exception) => { Some(exception) => {
#[cfg(feature = "cvm_guest")] #[cfg(feature = "cvm_guest")]
if *exception == VIRTUALIZATION_EXCEPTION { if exception == CpuException::VIRTUALIZATION_EXCEPTION {
handle_virtualization_exception(self); handle_virtualization_exception(self);
continue; continue;
} }
if exception.typ == CpuExceptionType::FaultOrTrap match exception.typ() {
|| exception.typ == CpuExceptionType::Fault CpuExceptionType::FaultOrTrap
|| exception.typ == CpuExceptionType::Trap | CpuExceptionType::Trap
{ | CpuExceptionType::Fault => break ReturnReason::UserException,
return_reason = ReturnReason::UserException; _ => (),
break;
} }
} }
None => { None => {
if self.user_context.trap_num as u16 == SYSCALL_TRAPNUM { if self.user_context.trap_num as u16 == SYSCALL_TRAPNUM {
return_reason = ReturnReason::UserSyscall; break ReturnReason::UserSyscall;
break;
} }
} }
}; };
call_irq_callback_functions(&self.as_trap_frame(), self.as_trap_frame().trap_num); call_irq_callback_functions(&self.as_trap_frame(), self.as_trap_frame().trap_num);
if has_kernel_event() { if has_kernel_event() {
return_reason = ReturnReason::KernelEvent; break ReturnReason::KernelEvent;
break;
} }
} };
crate::arch::irq::enable_local(); crate::arch::irq::enable_local();
if return_reason == ReturnReason::UserException { if return_reason == ReturnReason::UserException {
@ -212,39 +210,26 @@ pub enum CpuExceptionType {
Reserved, Reserved,
} }
/// CPU exception.
#[derive(Debug, Copy, Clone, 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 { macro_rules! define_cpu_exception {
( $([ $name: ident = $exception_num:tt, $exception_type:tt]),* ) => { ( $([ $name: ident = $exception_id:tt, $exception_type:tt]),* ) => {
const EXCEPTION_LIST : [CpuException;count_tts!($($name)*)] = [ /// CPU exception.
$($name,)* #[allow(non_camel_case_types)]
]; #[derive(Debug, Copy, Clone, Eq, PartialEq, FromPrimitive)]
$( pub enum CpuException {
#[doc = concat!("The ", stringify!($name), " exception")] $(
pub const $name : CpuException = CpuException{ #[doc = concat!("The ", stringify!($name), " exception")]
number: $exception_num, $name = $exception_id,
typ: CpuExceptionType::$exception_type, )*
}; }
)*
impl CpuException {
/// The type of the CPU exception.
pub fn typ(&self) -> CpuExceptionType {
match self {
$( CpuException::$name => CpuExceptionType::$exception_type, )*
}
}
}
} }
} }
@ -314,19 +299,19 @@ bitflags! {
impl CpuException { impl CpuException {
/// Checks if the given `trap_num` is a valid CPU exception. /// Checks if the given `trap_num` is a valid CPU exception.
pub fn is_cpu_exception(trap_num: u16) -> bool { pub fn is_cpu_exception(trap_num: u16) -> bool {
trap_num < EXCEPTION_LIST.len() as u16 Self::to_cpu_exception(trap_num).is_some()
} }
/// Maps a `trap_num` to its corresponding CPU exception. /// Maps a `trap_num` to its corresponding CPU exception.
pub fn to_cpu_exception(trap_num: u16) -> Option<&'static CpuException> { pub fn to_cpu_exception(trap_num: u16) -> Option<CpuException> {
EXCEPTION_LIST.get(trap_num as usize) FromPrimitive::from_u16(trap_num)
} }
} }
impl CpuExceptionInfo { impl CpuExceptionInfo {
/// Get corresponding CPU exception /// Get corresponding CPU exception
pub fn cpu_exception(&self) -> CpuException { pub fn cpu_exception(&self) -> CpuException {
EXCEPTION_LIST[self.id] CpuException::to_cpu_exception(self.id as u16).unwrap()
} }
} }

View File

@ -26,7 +26,7 @@ use log::debug;
use super::ex_table::ExTable; use super::ex_table::ExTable;
use crate::{ use crate::{
cpu::{CpuException, CpuExceptionInfo, PageFaultErrorCode, PAGE_FAULT}, cpu::{CpuException, CpuExceptionInfo, PageFaultErrorCode},
cpu_local_cell, cpu_local_cell,
mm::{ mm::{
kspace::{KERNEL_PAGE_TABLE, LINEAR_MAPPING_BASE_VADDR, LINEAR_MAPPING_VADDR_RANGE}, kspace::{KERNEL_PAGE_TABLE, LINEAR_MAPPING_BASE_VADDR, LINEAR_MAPPING_VADDR_RANGE},
@ -40,7 +40,7 @@ use crate::{
cfg_if! { cfg_if! {
if #[cfg(feature = "cvm_guest")] { if #[cfg(feature = "cvm_guest")] {
use tdx_guest::{tdcall, tdx_is_enabled, handle_virtual_exception}; use tdx_guest::{tdcall, tdx_is_enabled, handle_virtual_exception};
use crate::arch::{cpu::VIRTUALIZATION_EXCEPTION, tdx_guest::TrapFrameWrapper}; use crate::arch::tdx_guest::TrapFrameWrapper;
} }
} }
@ -223,13 +223,13 @@ extern "sysv64" fn trap_handler(f: &mut TrapFrame) {
if CpuException::is_cpu_exception(f.trap_num as u16) { if CpuException::is_cpu_exception(f.trap_num as u16) {
match CpuException::to_cpu_exception(f.trap_num as u16).unwrap() { match CpuException::to_cpu_exception(f.trap_num as u16).unwrap() {
#[cfg(feature = "cvm_guest")] #[cfg(feature = "cvm_guest")]
&VIRTUALIZATION_EXCEPTION => { CpuException::VIRTUALIZATION_EXCEPTION => {
let ve_info = tdcall::get_veinfo().expect("#VE handler: fail to get VE info\n"); let ve_info = tdcall::get_veinfo().expect("#VE handler: fail to get VE info\n");
let mut trapframe_wrapper = TrapFrameWrapper(&mut *f); let mut trapframe_wrapper = TrapFrameWrapper(&mut *f);
handle_virtual_exception(&mut trapframe_wrapper, &ve_info); handle_virtual_exception(&mut trapframe_wrapper, &ve_info);
*f = *trapframe_wrapper.0; *f = *trapframe_wrapper.0;
} }
&PAGE_FAULT => { CpuException::PAGE_FAULT => {
let page_fault_addr = x86_64::registers::control::Cr2::read_raw(); let page_fault_addr = x86_64::registers::control::Cr2::read_raw();
// The actual user space implementation should be responsible // The actual user space implementation should be responsible
// for providing mechanism to treat the 0 virtual address. // for providing mechanism to treat the 0 virtual address.