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)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub enum BaudRate {
|
pub enum BaudRate {
|
||||||
|
#[default]
|
||||||
Baud115200,
|
Baud115200,
|
||||||
Baud57600,
|
Baud57600,
|
||||||
Baud38400,
|
Baud38400,
|
||||||
Baud19200,
|
Baud19200,
|
||||||
#[default]
|
|
||||||
Baud9600,
|
Baud9600,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,3 +67,29 @@ pub struct Registers {
|
||||||
pub rcx: u64,
|
pub rcx: u64,
|
||||||
pub rax: 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 bit_field::BitField;
|
||||||
|
|
||||||
use crate::{serial_println, x86_64::Registers};
|
use crate::{
|
||||||
|
serial_println,
|
||||||
|
x86_64::{RFlags, Registers},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -201,21 +204,36 @@ impl EntryOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct InterruptStackFrameInner {
|
pub struct InterruptStackFrameInner {
|
||||||
pub instruction_pointer: u64,
|
pub instruction_pointer: u64,
|
||||||
pub code_segment: u16,
|
pub code_segment: u16,
|
||||||
_reserved: [u16; 3],
|
_reserved: [u16; 3],
|
||||||
|
pub flags: RFlags,
|
||||||
pub stack_pointer: u64,
|
pub stack_pointer: u64,
|
||||||
pub stack_segment: u16,
|
pub stack_segment: u16,
|
||||||
_reserved2: [u16; 3],
|
_reserved2: [u16; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct InterruptStackFrame(InterruptStackFrameInner);
|
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 {
|
impl Deref for InterruptStackFrame {
|
||||||
type Target = InterruptStackFrameInner;
|
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] {
|
pub unsafe fn as_type_erased(&self) -> &[Entry; 256] {
|
||||||
unsafe { core::mem::transmute::<&Self, &[Entry; 256]>(self) }
|
unsafe { core::mem::transmute::<&Self, &[Entry; 256]>(self) }
|
||||||
}
|
}
|
||||||
|
|
@ -424,6 +429,7 @@ extern "C" fn interrupt_dispatcher() {
|
||||||
"pop rdx",
|
"pop rdx",
|
||||||
"pop rcx",
|
"pop rcx",
|
||||||
"pop rax",
|
"pop rax",
|
||||||
|
"add rsp, 16", // pop the interrupt index and error code
|
||||||
"iretq",
|
"iretq",
|
||||||
registers_size = const { core::mem::size_of::<Registers>() },
|
registers_size = const { core::mem::size_of::<Registers>() },
|
||||||
global_handler = sym global_interrupt_handler,
|
global_handler = sym global_interrupt_handler,
|
||||||
|
|
@ -437,7 +443,7 @@ extern "C" fn global_interrupt_handler(
|
||||||
registers: &Registers,
|
registers: &Registers,
|
||||||
) {
|
) {
|
||||||
crate::serial_println!(
|
crate::serial_println!(
|
||||||
"Interrupt {} occurred! Error code: {:#x}, Frame: {:?}, Registers: {:?}",
|
"Interrupt {} occurred! Error code: {:#x}, Frame: {:#?}, Registers: {:#?}",
|
||||||
index,
|
index,
|
||||||
error_code,
|
error_code,
|
||||||
frame,
|
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)]
|
#[cfg_attr(test, test_case)]
|
||||||
fn test_interrupt_handler_trait() {
|
fn test_interrupt_handler_trait() {
|
||||||
let mut idt = InterruptDescriptorTable::new_empty();
|
use crate::sync::LazyLock;
|
||||||
idt.set_interrupt::<3, _>(my_handler_a);
|
static IDT: LazyLock<InterruptDescriptorTable> =
|
||||||
let addr_a = unsafe { idt.as_type_erased() }[3].handler_address();
|
crate::sync::LazyLock::new(InterruptDescriptorTable::new_default);
|
||||||
|
|
||||||
serial_println!("idt: {:#?}", idt);
|
unsafe { IDT.load() };
|
||||||
|
|
||||||
unsafe {
|
super::instructions::int3(); // Trigger a breakpoint interrupt (interrupt 3)
|
||||||
idt.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
super::instructions::int3();
|
serial_println!("Breakpoint interrupt handled successfully.");
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue