cleaned up interrupts

This commit is contained in:
janis 2026-07-19 23:18:00 +02:00
parent eccb202d04
commit 8212e794f3
Signed by: janis
SSH key fingerprint: SHA256:bB1qbbqmDXZNT0KKD5c2Dfjg53JGhj7B3CFcLIzSqq8
3 changed files with 56 additions and 213 deletions

View file

@ -300,11 +300,11 @@ pub mod uart_16550 {
#[derive(Debug, Clone, Copy, Default)]
pub enum BaudRate {
#[default]
Baud115200,
Baud57600,
Baud38400,
Baud19200,
#[default]
Baud9600,
}

View file

@ -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;
}
}

View file

@ -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(); // Trigger a breakpoint interrupt (interrupt 3)
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();
serial_println!("Breakpoint interrupt handled successfully.");
}