cleaned up interrupts
This commit is contained in:
parent
eccb202d04
commit
8212e794f3
|
|
@ -300,11 +300,11 @@ pub mod uart_16550 {
|
|||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub enum BaudRate {
|
||||
#[default]
|
||||
Baud115200,
|
||||
Baud57600,
|
||||
Baud38400,
|
||||
Baud19200,
|
||||
#[default]
|
||||
Baud9600,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,3 +67,29 @@ pub struct Registers {
|
|||
pub rcx: u64,
|
||||
pub rax: u64,
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct RFlags: u64 {
|
||||
const CARRY = 1 << 0;
|
||||
const DEFAULT = 1 << 1;
|
||||
const PARITY = 1 << 2;
|
||||
const AUXILIARY_CARRY = 1 << 4;
|
||||
const ZERO = 1 << 6;
|
||||
const SIGN = 1 << 7;
|
||||
const TRAP = 1 << 8;
|
||||
const INTERRUPT_ENABLE = 1 << 9;
|
||||
const DIRECTION = 1 << 10;
|
||||
const OVERFLOW = 1 << 11;
|
||||
const IOPL_LOW = 1 << 12;
|
||||
const IOPL_HIGH = 1 << 13;
|
||||
const NESTED_TASK = 1 << 14;
|
||||
const RESUME = 1 << 16;
|
||||
const VIRTUAL_8086_MODE = 1 << 17;
|
||||
const ALIGNMENT_CHECK = 1 << 18;
|
||||
const VIRTUAL_INTERRUPT_FLAG = 1 << 19;
|
||||
const VIRTUAL_INTERRUPT_PENDING = 1 << 20;
|
||||
const ID_FLAG = 1 << 21;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ use core::{arch::naked_asm, fmt::Debug, marker::PhantomData, ops::Deref, ptr::No
|
|||
|
||||
use bit_field::BitField;
|
||||
|
||||
use crate::{serial_println, x86_64::Registers};
|
||||
use crate::{
|
||||
serial_println,
|
||||
x86_64::{RFlags, Registers},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
|
|
@ -201,21 +204,36 @@ impl EntryOptions {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct InterruptStackFrameInner {
|
||||
pub instruction_pointer: u64,
|
||||
pub code_segment: u16,
|
||||
_reserved: [u16; 3],
|
||||
pub flags: RFlags,
|
||||
pub stack_pointer: u64,
|
||||
pub stack_segment: u16,
|
||||
_reserved2: [u16; 3],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct InterruptStackFrame(InterruptStackFrameInner);
|
||||
|
||||
impl Debug for InterruptStackFrame {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("InterruptStackFrame")
|
||||
.field(
|
||||
"instruction_pointer",
|
||||
&format_args!("{:#x}", self.instruction_pointer),
|
||||
)
|
||||
.field("code_segment", &format_args!("{:#x}", self.code_segment))
|
||||
.field("flags", &self.flags)
|
||||
.field("stack_pointer", &format_args!("{:#x}", self.stack_pointer))
|
||||
.field("stack_segment", &format_args!("{:#x}", self.stack_segment))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for InterruptStackFrame {
|
||||
type Target = InterruptStackFrameInner;
|
||||
|
||||
|
|
@ -344,19 +362,6 @@ impl InterruptDescriptorTable {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_interrupt<const N: u8, T: Fn(InterruptStackFrame, u8, Option<u64>)>(
|
||||
&mut self,
|
||||
_handler: T,
|
||||
) {
|
||||
let wrapper = InterruptHandlerWrapper::<T>(PhantomData);
|
||||
let handler_addr = handler_entry_by_index::<N, T>(wrapper);
|
||||
|
||||
unsafe {
|
||||
let entry = self.as_mut_type_erased().get_unchecked_mut(N as usize);
|
||||
entry.set_handler(handler_addr);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn as_type_erased(&self) -> &[Entry; 256] {
|
||||
unsafe { core::mem::transmute::<&Self, &[Entry; 256]>(self) }
|
||||
}
|
||||
|
|
@ -424,6 +429,7 @@ extern "C" fn interrupt_dispatcher() {
|
|||
"pop rdx",
|
||||
"pop rcx",
|
||||
"pop rax",
|
||||
"add rsp, 16", // pop the interrupt index and error code
|
||||
"iretq",
|
||||
registers_size = const { core::mem::size_of::<Registers>() },
|
||||
global_handler = sym global_interrupt_handler,
|
||||
|
|
@ -437,7 +443,7 @@ extern "C" fn global_interrupt_handler(
|
|||
registers: &Registers,
|
||||
) {
|
||||
crate::serial_println!(
|
||||
"Interrupt {} occurred! Error code: {:#x}, Frame: {:?}, Registers: {:?}",
|
||||
"Interrupt {} occurred! Error code: {:#x}, Frame: {:#?}, Registers: {:#?}",
|
||||
index,
|
||||
error_code,
|
||||
frame,
|
||||
|
|
@ -445,204 +451,15 @@ extern "C" fn global_interrupt_handler(
|
|||
);
|
||||
}
|
||||
|
||||
pub type InterruptHandler = extern "x86-interrupt" fn(InterruptStackFrame);
|
||||
pub type InterruptWithErrorCodeHandler = extern "x86-interrupt" fn(InterruptStackFrame, u64);
|
||||
pub type PageFaultHandler = extern "x86-interrupt" fn(InterruptStackFrame, PageFaultErrorCode);
|
||||
pub type DoubleFaultHandler = extern "x86-interrupt" fn(InterruptStackFrame, u64) -> !;
|
||||
pub type DivergingHandler = extern "x86-interrupt" fn(InterruptStackFrame) -> !;
|
||||
pub type GeneralHandler = fn(InterruptStackFrame, index: u8, error_code: Option<u64>);
|
||||
|
||||
macro_rules! impl_handler_type {
|
||||
($($handler_type:ty),*) => {
|
||||
$(
|
||||
impl ToAddress for $handler_type {
|
||||
fn to_address(&self) -> u64 {
|
||||
*self as *const () as u64
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_handler_type!(
|
||||
InterruptHandler,
|
||||
InterruptWithErrorCodeHandler,
|
||||
PageFaultHandler,
|
||||
DoubleFaultHandler,
|
||||
DivergingHandler
|
||||
);
|
||||
|
||||
pub trait HandleGeneratorTrait<const IDX: u8> {
|
||||
fn get_handler_addr(&self) -> u64;
|
||||
}
|
||||
|
||||
fn handler_entry_by_index<const IDX: u8, T>(handler: InterruptHandlerWrapper<T>) -> u64
|
||||
where
|
||||
T: Fn(InterruptStackFrame, u8, Option<u64>) + Sized,
|
||||
{
|
||||
if IDX == 8 {
|
||||
// Double fault
|
||||
DoubleFaultHandlerTrait::<IDX, T>::get_handler_addr(&handler)
|
||||
} else if IDX == 10 {
|
||||
// Invalid TSS
|
||||
InterruptWithErrorCodeHandlerTrait::<IDX, T>::get_handler_addr(&handler)
|
||||
} else if IDX == 11 {
|
||||
// Segment not present
|
||||
InterruptWithErrorCodeHandlerTrait::<IDX, T>::get_handler_addr(&handler)
|
||||
} else if IDX == 12 {
|
||||
// Stack segment fault
|
||||
InterruptWithErrorCodeHandlerTrait::<IDX, T>::get_handler_addr(&handler)
|
||||
} else if IDX == 13 {
|
||||
// General protection fault
|
||||
InterruptWithErrorCodeHandlerTrait::<IDX, T>::get_handler_addr(&handler)
|
||||
} else if IDX == 14 {
|
||||
// Page fault
|
||||
PageFaultHandlerTrait::<IDX, T>::get_handler_addr(&handler)
|
||||
} else if IDX == 17 {
|
||||
// Alignment check
|
||||
InterruptWithErrorCodeHandlerTrait::<IDX, T>::get_handler_addr(&handler)
|
||||
} else if IDX == 18 {
|
||||
// Machine check
|
||||
DivergingHandlerTrait::<IDX, T>::get_handler_addr(&handler)
|
||||
} else if IDX == 29 {
|
||||
// VMM communication exception
|
||||
InterruptWithErrorCodeHandlerTrait::<IDX, T>::get_handler_addr(&handler)
|
||||
} else if IDX == 30 {
|
||||
// Security exception
|
||||
InterruptWithErrorCodeHandlerTrait::<IDX, T>::get_handler_addr(&handler)
|
||||
} else {
|
||||
// All other interrupts
|
||||
InterruptHandlerTrait::<IDX, T>::get_handler_addr(&handler)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InterruptHandlerWrapper<T>(PhantomData<fn() -> T>);
|
||||
|
||||
pub trait InterruptErrorCode: Sized {
|
||||
fn as_u64(&self) -> u64;
|
||||
}
|
||||
|
||||
impl InterruptErrorCode for u64 {
|
||||
fn as_u64(&self) -> u64 {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptErrorCode for PageFaultErrorCode {
|
||||
fn as_u64(&self) -> u64 {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! interrupt_error_param {
|
||||
( $err:ty) => {
|
||||
$err
|
||||
};
|
||||
() => {
|
||||
()
|
||||
};
|
||||
($val:ident: $err:ty) => {
|
||||
Some(InterruptErrorCode::as_u64(&$val))
|
||||
};
|
||||
(err:) => {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! interrupt_return_expr {
|
||||
( $expr:ty) => {
|
||||
$crate::serial_println!("halt looping after unrecoverable interrupt");
|
||||
$crate::x86_64::halt_loop()
|
||||
};
|
||||
() => {
|
||||
()
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_interrupt_handler_trait {
|
||||
(trait $trait_name:ident (InterruptStackFrame $(, $err:ty)?) $(-> $ret:ty)?) => {
|
||||
pub trait $trait_name<const IDX: u8, T>
|
||||
where
|
||||
T: Fn(InterruptStackFrame, u8, Option<u64>),
|
||||
{
|
||||
extern "x86-interrupt" fn handler(
|
||||
frame: InterruptStackFrame,
|
||||
$( err: interrupt_error_param!{$err})*)
|
||||
$(-> $ret)* {
|
||||
assert_eq!(
|
||||
core::mem::size_of::<T>(),
|
||||
0,
|
||||
"Handler type must be zero-sized"
|
||||
);
|
||||
let f: T = unsafe { core::mem::transmute_copy::<(), T>(&()) };
|
||||
$crate::serial_println!("calling interrupt handler {} with {:?}", IDX, core::any::type_name::<T>());
|
||||
f(frame, IDX, interrupt_error_param!{err: $($err)*});
|
||||
$crate::serial_println!("returning from interrupt handler {} with {:?}", IDX, core::any::type_name::<T>());
|
||||
interrupt_return_expr!{$($ret)*}
|
||||
}
|
||||
|
||||
fn get_handler_addr(&self) -> u64 {
|
||||
Self::handler as *const () as u64
|
||||
}
|
||||
}
|
||||
|
||||
impl<const IDX: u8, T> $trait_name<IDX, T> for InterruptHandlerWrapper<T>
|
||||
where
|
||||
T: Fn(InterruptStackFrame, u8, Option<u64>),
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_interrupt_handler_trait!(trait InterruptHandlerTrait(InterruptStackFrame));
|
||||
impl_interrupt_handler_trait!(trait DivergingHandlerTrait(InterruptStackFrame) -> !);
|
||||
impl_interrupt_handler_trait!(trait InterruptWithErrorCodeHandlerTrait(InterruptStackFrame, u64));
|
||||
impl_interrupt_handler_trait!(trait PageFaultHandlerTrait(InterruptStackFrame, PageFaultErrorCode));
|
||||
impl_interrupt_handler_trait!(trait DoubleFaultHandlerTrait(InterruptStackFrame, u64) -> !);
|
||||
|
||||
fn my_handler(frame: InterruptStackFrame, index: u8) {
|
||||
// Handle the interrupt here
|
||||
crate::serial_println!("Interrupt {} occurred!", index);
|
||||
}
|
||||
|
||||
// fn asdf() {
|
||||
// let wrapper = InterruptHandlerWrapper::<>(PhantomData);
|
||||
// let handler = InterruptHandlerTrait::<32, _>::get_handler(&wrapper);
|
||||
// }
|
||||
|
||||
#[cfg_attr(test, test_case)]
|
||||
fn test_interrupt_handler_trait() {
|
||||
let mut idt = InterruptDescriptorTable::new_empty();
|
||||
idt.set_interrupt::<3, _>(my_handler_a);
|
||||
let addr_a = unsafe { idt.as_type_erased() }[3].handler_address();
|
||||
use crate::sync::LazyLock;
|
||||
static IDT: LazyLock<InterruptDescriptorTable> =
|
||||
crate::sync::LazyLock::new(InterruptDescriptorTable::new_default);
|
||||
|
||||
serial_println!("idt: {:#?}", idt);
|
||||
unsafe { IDT.load() };
|
||||
|
||||
unsafe {
|
||||
idt.load();
|
||||
}
|
||||
|
||||
super::instructions::int3();
|
||||
|
||||
serial_println!("back from int3");
|
||||
|
||||
idt.set_interrupt::<3, _>(my_handler_b);
|
||||
let addr_b = unsafe { idt.as_type_erased() }[3].handler_address();
|
||||
assert_ne!(addr_a, addr_b, "Handler addresses should be different");
|
||||
|
||||
unsafe {
|
||||
idt.load();
|
||||
}
|
||||
|
||||
super::instructions::int3();
|
||||
|
||||
idt.set_interrupt::<3, _>(|frame, index, error_code| {
|
||||
crate::serial_println!("handler c!");
|
||||
});
|
||||
|
||||
unsafe {
|
||||
idt.load();
|
||||
}
|
||||
|
||||
super::instructions::int3();
|
||||
super::instructions::int3(); // Trigger a breakpoint interrupt (interrupt 3)
|
||||
|
||||
serial_println!("Breakpoint interrupt handled successfully.");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue