use crate::registers::rflags::RFlags;
use crate::{PrivilegeLevel, VirtAddr};
use bit_field::BitField;
use bitflags::bitflags;
use core::convert::TryFrom;
use core::fmt;
use core::marker::PhantomData;
use core::ops::Bound::{Excluded, Included, Unbounded};
use core::ops::{
Bound, Deref, Index, IndexMut, Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive,
RangeTo, RangeToInclusive,
};
use volatile::Volatile;
use super::gdt::SegmentSelector;
#[derive(Clone, Debug)]
#[repr(C)]
#[repr(align(16))]
pub struct InterruptDescriptorTable {
pub divide_error: Entry<HandlerFunc>,
pub debug: Entry<HandlerFunc>,
pub non_maskable_interrupt: Entry<HandlerFunc>,
pub breakpoint: Entry<HandlerFunc>,
pub overflow: Entry<HandlerFunc>,
pub bound_range_exceeded: Entry<HandlerFunc>,
pub invalid_opcode: Entry<HandlerFunc>,
pub device_not_available: Entry<HandlerFunc>,
pub double_fault: Entry<DivergingHandlerFuncWithErrCode>,
coprocessor_segment_overrun: Entry<HandlerFunc>,
pub invalid_tss: Entry<HandlerFuncWithErrCode>,
pub segment_not_present: Entry<HandlerFuncWithErrCode>,
pub stack_segment_fault: Entry<HandlerFuncWithErrCode>,
pub general_protection_fault: Entry<HandlerFuncWithErrCode>,
pub page_fault: Entry<PageFaultHandlerFunc>,
reserved_1: Entry<HandlerFunc>,
pub x87_floating_point: Entry<HandlerFunc>,
pub alignment_check: Entry<HandlerFuncWithErrCode>,
pub machine_check: Entry<DivergingHandlerFunc>,
pub simd_floating_point: Entry<HandlerFunc>,
pub virtualization: Entry<HandlerFunc>,
pub cp_protection_exception: Entry<HandlerFuncWithErrCode>,
reserved_2: [Entry<HandlerFunc>; 6],
pub hv_injection_exception: Entry<HandlerFunc>,
pub vmm_communication_exception: Entry<HandlerFuncWithErrCode>,
pub security_exception: Entry<HandlerFuncWithErrCode>,
reserved_3: Entry<HandlerFunc>,
interrupts: [Entry<HandlerFunc>; 256 - 32],
}
impl InterruptDescriptorTable {
#[inline]
#[rustversion::attr(since(1.61), const)]
pub fn new() -> InterruptDescriptorTable {
InterruptDescriptorTable {
divide_error: Entry::missing(),
debug: Entry::missing(),
non_maskable_interrupt: Entry::missing(),
breakpoint: Entry::missing(),
overflow: Entry::missing(),
bound_range_exceeded: Entry::missing(),
invalid_opcode: Entry::missing(),
device_not_available: Entry::missing(),
double_fault: Entry::missing(),
coprocessor_segment_overrun: Entry::missing(),
invalid_tss: Entry::missing(),
segment_not_present: Entry::missing(),
stack_segment_fault: Entry::missing(),
general_protection_fault: Entry::missing(),
page_fault: Entry::missing(),
reserved_1: Entry::missing(),
x87_floating_point: Entry::missing(),
alignment_check: Entry::missing(),
machine_check: Entry::missing(),
simd_floating_point: Entry::missing(),
virtualization: Entry::missing(),
cp_protection_exception: Entry::missing(),
reserved_2: [Entry::missing(); 6],
hv_injection_exception: Entry::missing(),
vmm_communication_exception: Entry::missing(),
security_exception: Entry::missing(),
reserved_3: Entry::missing(),
interrupts: [Entry::missing(); 256 - 32],
}
}
#[inline]
pub fn reset(&mut self) {
*self = Self::new();
}
#[cfg(all(feature = "instructions", target_arch = "x86_64"))]
#[inline]
pub fn load(&'static self) {
unsafe { self.load_unsafe() }
}
#[cfg(all(feature = "instructions", target_arch = "x86_64"))]
#[inline]
pub unsafe fn load_unsafe(&self) {
use crate::instructions::tables::lidt;
unsafe {
lidt(&self.pointer());
}
}
#[cfg(all(feature = "instructions", target_arch = "x86_64"))]
fn pointer(&self) -> crate::structures::DescriptorTablePointer {
use core::mem::size_of;
crate::structures::DescriptorTablePointer {
base: VirtAddr::new(self as *const _ as u64),
limit: (size_of::<Self>() - 1) as u16,
}
}
fn condition_slice_bounds(&self, bounds: impl RangeBounds<u8>) -> (usize, usize) {
let lower_idx = match bounds.start_bound() {
Included(start) => usize::from(*start),
Excluded(start) => usize::from(*start) + 1,
Unbounded => 0,
};
let upper_idx = match bounds.end_bound() {
Included(end) => usize::from(*end) + 1,
Excluded(end) => usize::from(*end),
Unbounded => 256,
};
if lower_idx < 32 {
panic!("Cannot return slice from traps, faults, and exception handlers");
}
(lower_idx, upper_idx)
}
#[inline]
pub fn slice(&self, bounds: impl RangeBounds<u8>) -> &[Entry<HandlerFunc>] {
let (lower_idx, upper_idx) = self.condition_slice_bounds(bounds);
&self.interrupts[(lower_idx - 32)..(upper_idx - 32)]
}
#[inline]
pub fn slice_mut(&mut self, bounds: impl RangeBounds<u8>) -> &mut [Entry<HandlerFunc>] {
let (lower_idx, upper_idx) = self.condition_slice_bounds(bounds);
&mut self.interrupts[(lower_idx - 32)..(upper_idx - 32)]
}
}
impl Default for InterruptDescriptorTable {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Index<u8> for InterruptDescriptorTable {
type Output = Entry<HandlerFunc>;
#[inline]
fn index(&self, index: u8) -> &Self::Output {
match index {
0 => &self.divide_error,
1 => &self.debug,
2 => &self.non_maskable_interrupt,
3 => &self.breakpoint,
4 => &self.overflow,
5 => &self.bound_range_exceeded,
6 => &self.invalid_opcode,
7 => &self.device_not_available,
9 => &self.coprocessor_segment_overrun,
16 => &self.x87_floating_point,
19 => &self.simd_floating_point,
20 => &self.virtualization,
28 => &self.hv_injection_exception,
i @ 32..=255 => &self.interrupts[usize::from(i) - 32],
i @ 15 | i @ 31 | i @ 22..=27 => panic!("entry {} is reserved", i),
i @ 8 | i @ 10..=14 | i @ 17 | i @ 21 | i @ 29 | i @ 30 => {
panic!("entry {} is an exception with error code", i)
}
i @ 18 => panic!("entry {} is an diverging exception (must not return)", i),
}
}
}
impl IndexMut<u8> for InterruptDescriptorTable {
#[inline]
fn index_mut(&mut self, index: u8) -> &mut Self::Output {
match index {
0 => &mut self.divide_error,
1 => &mut self.debug,
2 => &mut self.non_maskable_interrupt,
3 => &mut self.breakpoint,
4 => &mut self.overflow,
5 => &mut self.bound_range_exceeded,
6 => &mut self.invalid_opcode,
7 => &mut self.device_not_available,
9 => &mut self.coprocessor_segment_overrun,
16 => &mut self.x87_floating_point,
19 => &mut self.simd_floating_point,
20 => &mut self.virtualization,
28 => &mut self.hv_injection_exception,
i @ 32..=255 => &mut self.interrupts[usize::from(i) - 32],
i @ 15 | i @ 31 | i @ 22..=27 => panic!("entry {} is reserved", i),
i @ 8 | i @ 10..=14 | i @ 17 | i @ 21 | i @ 29 | i @ 30 => {
panic!("entry {} is an exception with error code", i)
}
i @ 18 => panic!("entry {} is an diverging exception (must not return)", i),
}
}
}
macro_rules! impl_index_for_idt {
($ty:ty) => {
impl Index<$ty> for InterruptDescriptorTable {
type Output = [Entry<HandlerFunc>];
#[inline]
fn index(&self, index: $ty) -> &Self::Output {
self.slice(index)
}
}
impl IndexMut<$ty> for InterruptDescriptorTable {
#[inline]
fn index_mut(&mut self, index: $ty) -> &mut Self::Output {
self.slice_mut(index)
}
}
};
}
impl_index_for_idt!((Bound<&u8>, Bound<&u8>));
impl_index_for_idt!((Bound<u8>, Bound<u8>));
impl_index_for_idt!(Range<&u8>);
impl_index_for_idt!(Range<u8>);
impl_index_for_idt!(RangeFrom<&u8>);
impl_index_for_idt!(RangeFrom<u8>);
impl_index_for_idt!(RangeInclusive<&u8>);
impl_index_for_idt!(RangeInclusive<u8>);
impl_index_for_idt!(RangeTo<u8>);
impl_index_for_idt!(RangeTo<&u8>);
impl_index_for_idt!(RangeToInclusive<&u8>);
impl_index_for_idt!(RangeToInclusive<u8>);
impl_index_for_idt!(RangeFull);
#[derive(Clone, Copy)]
#[repr(C)]
pub struct Entry<F> {
pointer_low: u16,
options: EntryOptions,
pointer_middle: u16,
pointer_high: u32,
reserved: u32,
phantom: PhantomData<F>,
}
impl<T> fmt::Debug for Entry<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Entry")
.field("handler_addr", &format_args!("{:#x}", self.handler_addr()))
.field("options", &self.options)
.finish()
}
}
impl<T> PartialEq for Entry<T> {
fn eq(&self, other: &Self) -> bool {
self.pointer_low == other.pointer_low
&& self.options == other.options
&& self.pointer_middle == other.pointer_middle
&& self.pointer_high == other.pointer_high
&& self.reserved == other.reserved
}
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "abi_x86_interrupt"
))]
pub type HandlerFunc = extern "x86-interrupt" fn(InterruptStackFrame);
#[cfg(not(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "abi_x86_interrupt"
)))]
#[derive(Copy, Clone, Debug)]
pub struct HandlerFunc(());
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "abi_x86_interrupt"
))]
pub type HandlerFuncWithErrCode = extern "x86-interrupt" fn(InterruptStackFrame, error_code: u64);
#[cfg(not(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "abi_x86_interrupt"
)))]
#[derive(Copy, Clone, Debug)]
pub struct HandlerFuncWithErrCode(());
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "abi_x86_interrupt"
))]
pub type PageFaultHandlerFunc =
extern "x86-interrupt" fn(InterruptStackFrame, error_code: PageFaultErrorCode);
#[cfg(not(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "abi_x86_interrupt"
)))]
#[derive(Copy, Clone, Debug)]
pub struct PageFaultHandlerFunc(());
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "abi_x86_interrupt"
))]
pub type DivergingHandlerFunc = extern "x86-interrupt" fn(InterruptStackFrame) -> !;
#[cfg(not(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "abi_x86_interrupt"
)))]
#[derive(Copy, Clone, Debug)]
pub struct DivergingHandlerFunc(());
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "abi_x86_interrupt"
))]
pub type DivergingHandlerFuncWithErrCode =
extern "x86-interrupt" fn(InterruptStackFrame, error_code: u64) -> !;
#[cfg(not(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "abi_x86_interrupt"
)))]
#[derive(Copy, Clone, Debug)]
pub struct DivergingHandlerFuncWithErrCode(());
pub type GeneralHandlerFunc = fn(InterruptStackFrame, index: u8, error_code: Option<u64>);
impl<F> Entry<F> {
#[inline]
pub const fn missing() -> Self {
Entry {
pointer_low: 0,
pointer_middle: 0,
pointer_high: 0,
options: EntryOptions::minimal(),
reserved: 0,
phantom: PhantomData,
}
}
#[cfg(all(feature = "instructions", target_arch = "x86_64"))]
#[inline]
pub unsafe fn set_handler_addr(&mut self, addr: VirtAddr) -> &mut EntryOptions {
use crate::instructions::segmentation::{Segment, CS};
let addr = addr.as_u64();
self.pointer_low = addr as u16;
self.pointer_middle = (addr >> 16) as u16;
self.pointer_high = (addr >> 32) as u32;
self.options = EntryOptions::minimal();
unsafe { self.options.set_code_selector(CS::get_reg()) };
self.options.set_present(true);
&mut self.options
}
#[inline]
pub fn handler_addr(&self) -> VirtAddr {
let addr = self.pointer_low as u64
| ((self.pointer_middle as u64) << 16)
| ((self.pointer_high as u64) << 32);
VirtAddr::new_truncate(addr)
}
}
#[cfg(all(feature = "instructions", target_arch = "x86_64"))]
impl<F: HandlerFuncType> Entry<F> {
#[inline]
pub fn set_handler_fn(&mut self, handler: F) -> &mut EntryOptions {
unsafe { self.set_handler_addr(handler.to_virt_addr()) }
}
}
pub unsafe trait HandlerFuncType {
fn to_virt_addr(self) -> VirtAddr;
}
macro_rules! impl_handler_func_type {
($f:ty) => {
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "abi_x86_interrupt"
))]
unsafe impl HandlerFuncType for $f {
#[inline]
fn to_virt_addr(self) -> VirtAddr {
#[cfg_attr(
any(target_pointer_width = "32", target_pointer_width = "64"),
allow(clippy::fn_to_numeric_cast)
)]
VirtAddr::new(self as u64)
}
}
};
}
impl_handler_func_type!(HandlerFunc);
impl_handler_func_type!(HandlerFuncWithErrCode);
impl_handler_func_type!(PageFaultHandlerFunc);
impl_handler_func_type!(DivergingHandlerFunc);
impl_handler_func_type!(DivergingHandlerFuncWithErrCode);
#[repr(C)]
#[derive(Clone, Copy, PartialEq)]
pub struct EntryOptions {
cs: SegmentSelector,
bits: u16,
}
impl fmt::Debug for EntryOptions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("EntryOptions")
.field("code_selector", &self.cs)
.field("stack_index", &self.stack_index())
.field("type", &format_args!("{:#04b}", self.bits.get_bits(8..12)))
.field("privilege_level", &self.privilege_level())
.field("present", &self.present())
.finish()
}
}
impl EntryOptions {
#[inline]
const fn minimal() -> Self {
EntryOptions {
cs: SegmentSelector(0),
bits: 0b1110_0000_0000, }
}
pub unsafe fn set_code_selector(&mut self, cs: SegmentSelector) -> &mut Self {
self.cs = cs;
self
}
#[inline]
pub fn set_present(&mut self, present: bool) -> &mut Self {
self.bits.set_bit(15, present);
self
}
fn present(&self) -> bool {
self.bits.get_bit(15)
}
#[inline]
pub fn disable_interrupts(&mut self, disable: bool) -> &mut Self {
self.bits.set_bit(8, !disable);
self
}
#[inline]
pub fn set_privilege_level(&mut self, dpl: PrivilegeLevel) -> &mut Self {
self.bits.set_bits(13..15, dpl as u16);
self
}
fn privilege_level(&self) -> PrivilegeLevel {
PrivilegeLevel::from_u16(self.bits.get_bits(13..15))
}
#[inline]
pub unsafe fn set_stack_index(&mut self, index: u16) -> &mut Self {
self.bits.set_bits(0..3, index + 1);
self
}
fn stack_index(&self) -> Option<u16> {
self.bits.get_bits(0..3).checked_sub(1)
}
}
#[repr(transparent)]
pub struct InterruptStackFrame(InterruptStackFrameValue);
impl InterruptStackFrame {
#[inline]
pub fn new(
instruction_pointer: VirtAddr,
code_segment: SegmentSelector,
cpu_flags: RFlags,
stack_pointer: VirtAddr,
stack_segment: SegmentSelector,
) -> Self {
Self(InterruptStackFrameValue::new(
instruction_pointer,
code_segment,
cpu_flags,
stack_pointer,
stack_segment,
))
}
#[inline]
pub unsafe fn as_mut(&mut self) -> Volatile<&mut InterruptStackFrameValue> {
Volatile::new(&mut self.0)
}
}
impl Deref for InterruptStackFrame {
type Target = InterruptStackFrameValue;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl fmt::Debug for InterruptStackFrame {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct InterruptStackFrameValue {
pub instruction_pointer: VirtAddr,
pub code_segment: SegmentSelector,
_reserved1: [u8; 6],
pub cpu_flags: RFlags,
pub stack_pointer: VirtAddr,
pub stack_segment: SegmentSelector,
_reserved2: [u8; 6],
}
impl InterruptStackFrameValue {
#[inline]
pub fn new(
instruction_pointer: VirtAddr,
code_segment: SegmentSelector,
cpu_flags: RFlags,
stack_pointer: VirtAddr,
stack_segment: SegmentSelector,
) -> Self {
Self {
instruction_pointer,
code_segment,
_reserved1: Default::default(),
cpu_flags,
stack_pointer,
stack_segment,
_reserved2: Default::default(),
}
}
#[inline(always)]
#[cfg(all(feature = "instructions", target_arch = "x86_64"))]
pub unsafe fn iretq(&self) -> ! {
unsafe {
core::arch::asm!(
"push {stack_segment:r}",
"push {new_stack_pointer}",
"push {rflags}",
"push {code_segment:r}",
"push {new_instruction_pointer}",
"iretq",
rflags = in(reg) self.cpu_flags.bits(),
new_instruction_pointer = in(reg) self.instruction_pointer.as_u64(),
new_stack_pointer = in(reg) self.stack_pointer.as_u64(),
code_segment = in(reg) self.code_segment.0,
stack_segment = in(reg) self.stack_segment.0,
options(noreturn)
)
}
}
}
impl fmt::Debug for InterruptStackFrameValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = f.debug_struct("InterruptStackFrame");
s.field("instruction_pointer", &self.instruction_pointer);
s.field("code_segment", &self.code_segment);
s.field("cpu_flags", &self.cpu_flags);
s.field("stack_pointer", &self.stack_pointer);
s.field("stack_segment", &self.stack_segment);
s.finish()
}
}
bitflags! {
#[repr(transparent)]
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct PageFaultErrorCode: u64 {
const PROTECTION_VIOLATION = 1;
const CAUSED_BY_WRITE = 1 << 1;
const USER_MODE = 1 << 2;
const MALFORMED_TABLE = 1 << 3;
const INSTRUCTION_FETCH = 1 << 4;
const PROTECTION_KEY = 1 << 5;
const SHADOW_STACK = 1 << 6;
const HLAT = 1 << 7;
const SGX = 1 << 15;
const RMP = 1 << 31;
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct SelectorErrorCode {
flags: u64,
}
impl SelectorErrorCode {
pub const fn new(value: u64) -> Option<Self> {
if value > u16::MAX as u64 {
None
} else {
Some(Self { flags: value })
}
}
pub const fn new_truncate(value: u64) -> Self {
Self {
flags: (value as u16) as u64,
}
}
pub fn external(&self) -> bool {
self.flags.get_bit(0)
}
pub fn descriptor_table(&self) -> DescriptorTable {
match self.flags.get_bits(1..3) {
0b00 => DescriptorTable::Gdt,
0b01 => DescriptorTable::Idt,
0b10 => DescriptorTable::Ldt,
0b11 => DescriptorTable::Idt,
_ => unreachable!(),
}
}
pub fn index(&self) -> u64 {
self.flags.get_bits(3..16)
}
pub fn is_null(&self) -> bool {
self.flags == 0
}
}
impl fmt::Debug for SelectorErrorCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = f.debug_struct("Selector Error");
s.field("external", &self.external());
s.field("descriptor table", &self.descriptor_table());
s.field("index", &self.index());
s.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DescriptorTable {
Gdt,
Idt,
Ldt,
}
#[repr(u8)]
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ExceptionVector {
Division = 0x00,
Debug = 0x01,
NonMaskableInterrupt = 0x02,
Breakpoint = 0x03,
Overflow = 0x04,
BoundRange = 0x05,
InvalidOpcode = 0x06,
DeviceNotAvailable = 0x07,
Double = 0x08,
InvalidTss = 0x0A,
SegmentNotPresent = 0x0B,
Stack = 0x0C,
GeneralProtection = 0x0D,
Page = 0x0E,
X87FloatingPoint = 0x10,
AlignmentCheck = 0x11,
MachineCheck = 0x12,
SimdFloatingPoint = 0x13,
Virtualization = 0x14,
ControlProtection = 0x15,
HypervisorInjection = 0x1C,
VmmCommunication = 0x1D,
Security = 0x1E,
}
#[derive(Debug)]
pub struct InvalidExceptionVectorNumber(u8);
impl fmt::Display for InvalidExceptionVectorNumber {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} is not a valid exception vector", self.0)
}
}
impl TryFrom<u8> for ExceptionVector {
type Error = InvalidExceptionVectorNumber;
fn try_from(exception_vector_number: u8) -> Result<Self, Self::Error> {
match exception_vector_number {
0x00 => Ok(Self::Division),
0x01 => Ok(Self::Debug),
0x02 => Ok(Self::NonMaskableInterrupt),
0x03 => Ok(Self::Breakpoint),
0x04 => Ok(Self::Overflow),
0x05 => Ok(Self::BoundRange),
0x06 => Ok(Self::InvalidOpcode),
0x07 => Ok(Self::DeviceNotAvailable),
0x08 => Ok(Self::Double),
0x0A => Ok(Self::InvalidTss),
0x0B => Ok(Self::SegmentNotPresent),
0x0C => Ok(Self::Stack),
0x0D => Ok(Self::GeneralProtection),
0x0E => Ok(Self::Page),
0x10 => Ok(Self::X87FloatingPoint),
0x11 => Ok(Self::AlignmentCheck),
0x12 => Ok(Self::MachineCheck),
0x13 => Ok(Self::SimdFloatingPoint),
0x14 => Ok(Self::Virtualization),
0x15 => Ok(Self::ControlProtection),
0x1C => Ok(Self::HypervisorInjection),
0x1D => Ok(Self::VmmCommunication),
0x1E => Ok(Self::Security),
_ => Err(InvalidExceptionVectorNumber(exception_vector_number)),
}
}
}
#[cfg(all(
feature = "instructions",
feature = "abi_x86_interrupt",
target_arch = "x86_64"
))]
#[macro_export]
macro_rules! set_general_handler {
($idt:expr, $handler:ident) => {
$crate::set_general_handler!($idt, $handler, 0..=255);
};
($idt:expr, $handler:ident, $idx:literal) => {
$crate::set_general_handler!($idt, $handler, $idx..=$idx);
};
($idt:expr, $handler:ident, $range:expr) => {{
const GENERAL_HANDLER: $crate::structures::idt::GeneralHandlerFunc = $handler;
{
fn set_general_handler(
idt: &mut $crate::structures::idt::InterruptDescriptorTable,
range: impl ::core::ops::RangeBounds<u8>,
) {
$crate::set_general_handler_recursive_bits!(idt, GENERAL_HANDLER, range);
}
set_general_handler($idt, $range);
}
}};
}
#[cfg(all(
feature = "instructions",
feature = "abi_x86_interrupt",
target_arch = "x86_64"
))]
#[macro_export]
#[doc(hidden)]
macro_rules! set_general_handler_recursive_bits {
($idt:expr, $handler:ident, $range:expr, $bit7:tt, $bit6:tt, $bit5:tt, $bit4:tt, $bit3:tt, $bit2:tt, $bit1:tt, $bit0:tt) => {{
const IDX: u8 = $bit0 | ($bit1 << 1) | ($bit2 << 2) | ($bit3 << 3) | ($bit4 << 4) | ($bit5 << 5) | ($bit6 << 6) | ($bit7 << 7);
#[allow(unreachable_code)]
if $range.contains(&IDX) {
$crate::set_general_handler_entry!($idt, $handler, IDX, $bit7, $bit6, $bit5, $bit4, $bit3, $bit2, $bit1, $bit0);
}
}};
($idt:expr, $handler:ident, $range:expr $(, $bits:tt)*) => {
$crate::set_general_handler_recursive_bits!($idt, $handler, $range $(, $bits)*, 0);
$crate::set_general_handler_recursive_bits!($idt, $handler, $range $(, $bits)*, 1);
};
}
#[cfg(all(
feature = "instructions",
feature = "abi_x86_interrupt",
target_arch = "x86_64"
))]
#[macro_export]
#[doc(hidden)]
macro_rules! set_general_handler_entry {
($idt:expr, $handler:ident, $idx:expr, 0, 0, 0, 0, 1, 0, 0, 0) => {{
extern "x86-interrupt" fn handler(
frame: $crate::structures::idt::InterruptStackFrame,
error_code: u64,
) -> ! {
$handler(frame, $idx.into(), Some(error_code));
panic!("General handler returned on double fault");
}
$idt.double_fault.set_handler_fn(handler);
}};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 0, 1, 0) => {{
extern "x86-interrupt" fn handler(
frame: $crate::structures::idt::InterruptStackFrame,
error_code: u64,
) {
$handler(frame, $idx.into(), Some(error_code));
}
$idt.invalid_tss.set_handler_fn(handler);
}};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 0, 1, 1) => {{
extern "x86-interrupt" fn handler(
frame: $crate::structures::idt::InterruptStackFrame,
error_code: u64,
) {
$handler(frame, $idx.into(), Some(error_code));
}
$idt.segment_not_present.set_handler_fn(handler);
}};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 1, 0, 0) => {{
extern "x86-interrupt" fn handler(
frame: $crate::structures::idt::InterruptStackFrame,
error_code: u64,
) {
$handler(frame, $idx.into(), Some(error_code));
}
$idt.stack_segment_fault.set_handler_fn(handler);
}};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 1, 0, 1) => {{
extern "x86-interrupt" fn handler(
frame: $crate::structures::idt::InterruptStackFrame,
error_code: u64,
) {
$handler(frame, $idx.into(), Some(error_code));
}
$idt.general_protection_fault.set_handler_fn(handler);
}};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 1, 1, 0) => {{
extern "x86-interrupt" fn handler(
frame: $crate::structures::idt::InterruptStackFrame,
error_code: $crate::structures::idt::PageFaultErrorCode,
) {
$handler(frame, IDX.into(), Some(error_code.bits()));
}
$idt.page_fault.set_handler_fn(handler);
}};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 0, 0, 1) => {{
extern "x86-interrupt" fn handler(
frame: $crate::structures::idt::InterruptStackFrame,
error_code: u64,
) {
$handler(frame, $idx.into(), Some(error_code));
}
$idt.alignment_check.set_handler_fn(handler);
}};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 0, 1, 0) => {{
extern "x86-interrupt" fn handler(
frame: $crate::structures::idt::InterruptStackFrame,
) -> ! {
$handler(frame, $idx.into(), None);
panic!("General handler returned on machine check exception");
}
$idt.machine_check.set_handler_fn(handler);
}};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 1, 0, 1) => {{
extern "x86-interrupt" fn handler(
frame: $crate::structures::idt::InterruptStackFrame,
error_code: u64,
) {
$handler(frame, $idx.into(), Some(error_code));
}
$idt.cp_protection_exception.set_handler_fn(handler);
}};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 0, 1) => {
extern "x86-interrupt" fn handler(
frame: $crate::structures::idt::InterruptStackFrame,
error_code: u64,
) {
$handler(frame, $idx.into(), Some(error_code));
}
$idt.vmm_communication_exception.set_handler_fn(handler);
};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 1, 0) => {{
extern "x86-interrupt" fn handler(
frame: $crate::structures::idt::InterruptStackFrame,
error_code: u64,
) {
$handler(frame, $idx.into(), Some(error_code));
}
$idt.security_exception.set_handler_fn(handler);
}};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 1, 1, 1) => {};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 1, 1, 0) => {};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 1, 1, 1) => {};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 0, 0, 0) => {};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 0, 0, 1) => {};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 0, 1, 0) => {};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 0, 1, 1) => {};
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 1, 1) => {};
($idt:expr, $handler:ident, $idx:ident $(, $_bits:tt)*) => {{
extern "x86-interrupt" fn handler(frame: $crate::structures::idt::InterruptStackFrame) {
$handler(frame, $idx.into(), None);
}
$idt[$idx].set_handler_fn(handler);
}};
}
#[cfg(test)]
mod test {
use super::*;
#[allow(dead_code)]
fn entry_present(idt: &InterruptDescriptorTable, index: u8) -> bool {
let options = match index {
8 => &idt.double_fault.options,
10 => &idt.invalid_tss.options,
11 => &idt.segment_not_present.options,
12 => &idt.stack_segment_fault.options,
13 => &idt.general_protection_fault.options,
14 => &idt.page_fault.options,
15 => &idt.reserved_1.options,
17 => &idt.alignment_check.options,
18 => &idt.machine_check.options,
21 => &idt.cp_protection_exception.options,
i @ 22..=27 => &idt.reserved_2[usize::from(i) - 22].options,
28 => &idt.hv_injection_exception.options,
29 => &idt.vmm_communication_exception.options,
30 => &idt.security_exception.options,
31 => &idt.reserved_3.options,
other => &idt[other].options,
};
options.bits.get_bit(15)
}
#[test]
fn size_test() {
use core::mem::size_of;
assert_eq!(size_of::<Entry<HandlerFunc>>(), 16);
assert_eq!(size_of::<InterruptDescriptorTable>(), 256 * 16);
assert_eq!(size_of::<InterruptStackFrame>(), 40);
assert_eq!(size_of::<InterruptStackFrameValue>(), 40);
}
#[cfg(all(
feature = "instructions",
feature = "abi_x86_interrupt",
target_arch = "x86_64"
))]
#[cfg(not(windows))]
#[test]
fn default_handlers() {
fn general_handler(
_stack_frame: InterruptStackFrame,
_index: u8,
_error_code: Option<u64>,
) {
}
let mut idt = InterruptDescriptorTable::new();
set_general_handler!(&mut idt, general_handler, 0);
for i in 0..=255 {
if i == 0 {
assert!(entry_present(&idt, i));
} else {
assert!(!entry_present(&idt, i));
}
}
set_general_handler!(&mut idt, general_handler, 14);
for i in 0..=255 {
if i == 0 || i == 14 {
assert!(entry_present(&idt, i));
} else {
assert!(!entry_present(&idt, i));
}
}
set_general_handler!(&mut idt, general_handler, 32..64);
for i in 1..=255 {
if i == 0 || i == 14 || (32..64).contains(&i) {
assert!(entry_present(&idt, i), "{}", i);
} else {
assert!(!entry_present(&idt, i));
}
}
set_general_handler!(&mut idt, general_handler);
for i in 0..=255 {
if i == 15 || i == 31 || (22..=27).contains(&i) {
assert!(!entry_present(&idt, i));
} else {
assert!(entry_present(&idt, i));
}
}
}
#[test]
fn idt_fmt_debug() {
dbg!(InterruptDescriptorTable::new());
}
#[test]
fn entry_derive_test() {
fn foo(_: impl Copy + PartialEq + fmt::Debug) {}
foo(Entry::<HandlerFuncWithErrCode> {
pointer_low: 0,
options: EntryOptions::minimal(),
pointer_middle: 0,
pointer_high: 0,
reserved: 0,
phantom: PhantomData,
})
}
#[test]
fn isr_frame_manipulation() {
let mut frame = InterruptStackFrame(InterruptStackFrameValue {
instruction_pointer: VirtAddr::new(0x1000),
code_segment: SegmentSelector(0),
cpu_flags: RFlags::empty(),
stack_pointer: VirtAddr::new(0x2000),
stack_segment: SegmentSelector(0),
_reserved1: Default::default(),
_reserved2: Default::default(),
});
unsafe {
frame.as_mut().update(|f| f.instruction_pointer += 2u64);
}
}
}