Initial work on paging, also complete ish interrupt registration

This commit is contained in:
ivajon
2026-05-08 11:40:51 +02:00
parent aa5ce32dce
commit c68d3fab14
11 changed files with 456 additions and 18 deletions

View File

@@ -159,7 +159,7 @@ impl Flags {
}
#[repr(transparent)]
#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
pub struct GdtSelector {
inner: u16,
}

View File

@@ -1,38 +1,68 @@
use core::sync::atomic::{AtomicBool, Ordering};
use crate::gdt::{DPL, GdtSelector};
pub mod vectors;
#[repr(C, packed)]
#[must_use]
pub struct InterruptTableDescriptor {
size: u16,
offset: u64,
}
#[repr(transparent)]
#[derive(Clone, Copy)]
#[must_use]
pub struct IdtTableEntry {
inner: u128,
}
pub type ISR = extern "x86-interrupt" fn(InterruptStackFrame) -> ();
pub type ISRWithIdx = extern "x86-interrupt" fn(InterruptStackFrame, u64) -> ();
pub type ISRWithIdxNeverReturns = extern "x86-interrupt" fn(InterruptStackFrame, u64) -> !;
#[repr(C)]
#[derive(Clone, Copy)]
#[must_use]
pub struct InterruptStackFrame {
ip: usize,
cs: usize,
flags: usize,
sp: usize,
ss: usize,
pub ip: usize,
pub cs: usize,
pub flags: usize,
pub sp: usize,
pub ss: usize,
}
#[repr(u8)]
#[must_use]
pub enum Gate {
Interrupt = 0b1110,
Trap = 0b1111,
}
impl IdtTableEntry {
pub fn new(isr: ISR, selector: GdtSelector, ist: u8, gate: Gate, priority: DPL) -> Self {
let mut inner = 0b0;
let offset = (isr as *const ()) as u64;
#[repr(u8)]
#[must_use]
pub enum Ist {
Idx0 = 0,
Idx1 = 1,
Idx2 = 2,
Idx3 = 3,
Idx4 = 4,
Idx5 = 5,
Idx6 = 6,
Idx7 = 7,
}
impl IdtTableEntry {
#[allow(clippy::cast_possible_truncation)]
const fn build(
offset: u64,
selector: GdtSelector,
ist: Ist,
gate: Gate,
priority: DPL,
) -> Self {
let mut inner = 0;
let bytes = (offset >> 32) as u32;
inner |= (bytes as u128) << 64;
@@ -44,8 +74,6 @@ impl IdtTableEntry {
inner |= (selector.raw() as u128) << 16;
assert!(ist <= 0b111);
inner |= (ist as u128) << 32;
inner |= (gate as u128) << 40;
inner |= (priority as u128) << 45;
@@ -53,4 +81,98 @@ impl IdtTableEntry {
Self { inner }
}
pub const fn null() -> Self {
Self { inner: 0 }
}
pub fn new(isr: ISR, selector: GdtSelector, ist: Ist, gate: Gate, priority: DPL) -> Self {
let offset = (isr as *const ()) as u64;
Self::build(offset, selector, ist, gate, priority)
}
// TODO: Make the type system check ist valid here.
pub fn with_idx(
isr: ISRWithIdx,
selector: GdtSelector,
ist: Ist,
gate: Gate,
priority: DPL,
) -> Self {
let offset = (isr as *const ()) as u64;
Self::build(offset, selector, ist, gate, priority)
}
}
static mut IDT: [IdtTableEntry; 256] = [IdtTableEntry::null(); _];
static ASSIGNED: [AtomicBool; 256] = [const { AtomicBool::new(false) }; 256];
#[allow(static_mut_refs)]
const IDT_SIZE: u16 = unsafe { (core::mem::size_of_val(&IDT) - 1) as u16 };
static IDT_INITIATED: AtomicBool = AtomicBool::new(false);
#[must_use]
pub struct Idt<const TABLE_SPECIFIED: bool>;
#[derive(Debug, Clone, Copy)]
#[must_use]
pub enum Error {
InvalidVectorIndex,
VectorAlreadyAssigned,
}
impl<const TABLE_SPECIFIED: bool> Idt<TABLE_SPECIFIED> {
/// Registers a new handler
///
/// # Safety
///
/// This function is safe assuming that this is the only [`Idt`] that exists. This assumption
/// holds given that the user does not forcibly create one.
pub fn register_handler(
&mut self,
vector: usize,
entry: IdtTableEntry,
) -> Result<&mut Self, Error> {
if vector >= ASSIGNED.len() {
return Err(Error::InvalidVectorIndex);
}
if ASSIGNED[vector].swap(true, Ordering::AcqRel) {
return Err(Error::VectorAlreadyAssigned);
}
unsafe { IDT[vector] = entry };
Ok(self)
}
#[allow(static_mut_refs)]
fn table(&self) -> InterruptTableDescriptor {
let addr = unsafe { IDT.as_ptr() as *const _ as u64 };
InterruptTableDescriptor {
size: IDT_SIZE,
offset: addr,
}
}
}
impl Idt<false> {
pub fn set_table(self) -> Idt<true> {
let ptr = self.table();
unsafe {
core::arch::asm!(
"lidt [{}]",
in(reg) &ptr,
options(readonly,nostack,preserves_flags)
)
}
Idt
}
pub fn new() -> Self {
assert!(
!IDT_INITIATED.swap(true, Ordering::AcqRel),
"IDT already initiated"
);
Self
}
}

View File

@@ -0,0 +1,73 @@
use crate::idt::{
DPL, Error, Gate, GdtSelector, ISR, ISRWithIdx, ISRWithIdxNeverReturns, Idt, IdtTableEntry, Ist,
};
macro_rules! vector {
($(
$(~$idx_:literal)?
$($idx:literal: $name:ident => $type:ty)?
);*) => {
pub trait Vector {
type FunctionSignature;
const IDX:usize;
fn register_handler<const REGISTERD:bool>(table:&mut Idt<REGISTERD>, isr: Self::FunctionSignature, selector: GdtSelector, ist: Ist, gate: Gate, priority: DPL) -> Result<&mut Idt<REGISTERD>,Error>;
}
pub mod named_isr {
$(
$(pub struct $name;)?
)*
}
$(
$(
impl Vector for named_isr::$name {
type FunctionSignature = $type;
const IDX:usize = $idx;
fn register_handler<const REGISTERD:bool>(table:&mut Idt<REGISTERD>, isr: Self::FunctionSignature, selector: GdtSelector, ist: Ist, gate: Gate, priority: DPL) -> Result<&mut Idt<REGISTERD>,Error> {
let offset = (isr as *const ()) as u64;
let desc = IdtTableEntry::build(offset, selector, ist, gate, priority);
table.register_handler(Self::IDX, desc)
}
}
)?
)*
}
}
vector!(
0: DivisionError => ISR;
1: Debug => ISR;
2: NonMaskableInterrupt => ISR;
3: Breakpoint => ISR;
4: Overflow => ISR;
5: BoundRangeExceeded => ISR;
6: InvalidOpcode => ISR;
7: DeviceNotAvailable => ISR;
8: DoubleFault => ISRWithIdxNeverReturns;
9: CoprocessorSegmentOverrun => ISR;
10: InvalidTSS => ISRWithIdx;
11: SegmentNotPresent => ISRWithIdx;
12: StackSegmentFault => ISRWithIdx;
13: GeneralProtectionFault => ISRWithIdx;
14: PageFault => ISRWithIdx;
~15;
16: X87FloatingPointException => ISR;
17: AlignmentCheck => ISRWithIdx;
18: MachineCheck => ISR;
19: SIMDFloatingPointException => ISR;
20: VirtualizationException => ISR;
21: ControlProtectionException => ISRWithIdx;
~22;
~23;
~24;
~25;
~26;
~27;
28: HypervisorInjectionException => ISR;
29: VMMCommunicationException => ISRWithIdx;
30: SecurityException => ISRWithIdx;
~31;
);

View File

@@ -23,7 +23,8 @@
clippy::unused_self,
clippy::must_use_candidate,
clippy::new_without_default,
clippy::cast_possible_truncation
clippy::cast_possible_truncation,
clippy::missing_errors_doc
)]
pub mod uart;
@@ -38,5 +39,7 @@ mod private {
pub mod prelude {
pub use crate::core::Core;
pub use crate::idt::vectors::*;
pub use crate::idt::*;
pub use crate::uart::{InitUart, Uart};
}