curiOS/kernel/src/x86_64/idt.rs
2026-07-26 04:32:21 +02:00

547 lines
16 KiB
Rust

use core::{arch::naked_asm, fmt::Debug, marker::PhantomData, ops::Deref, ptr::NonNull};
use bit_field::BitField;
use crate::{
serial_println,
x86_64::{RFlags, Registers, halt_loop},
};
#[derive(Clone, Copy)]
#[repr(C)]
pub struct Entry {
pub offset_low: u16,
pub selector: u16,
pub options: EntryOptions,
pub offset_middle: u16,
pub offset_high: u32,
pub reserved: u32,
}
impl core::fmt::Debug for Entry {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Entry")
.field("handler", &self.handler_address())
.field("selector", &self.selector)
.field("options", &self.options)
.finish()
}
}
pub struct ExceptionVector;
impl ExceptionVector {
pub const DIVIDE_BY_ZERO: u8 = 0;
pub const DEBUG: u8 = 1;
pub const NON_MASKABLE_INTERRUPT: u8 = 2;
pub const BREAKPOINT: u8 = 3;
pub const OVERFLOW: u8 = 4;
pub const BOUND_RANGE_EXCEEDED: u8 = 5;
pub const INVALID_OPCODE: u8 = 6;
pub const DEVICE_NOT_AVAILABLE: u8 = 7;
pub const DOUBLE_FAULT: u8 = 8;
pub const COPROCESSOR_SEGMENT_OVERRUN: u8 = 9;
pub const INVALID_TSS: u8 = 10;
pub const SEGMENT_NOT_PRESENT: u8 = 11;
pub const STACK_SEGMENT_FAULT: u8 = 12;
pub const GENERAL_PROTECTION_FAULT: u8 = 13;
pub const PAGE_FAULT: u8 = 14;
pub const X87_FLOATING_POINT_EXCEPTION: u8 = 16;
pub const ALIGNMENT_CHECK: u8 = 17;
pub const MACHINE_CHECK: u8 = 18;
pub const SIMD_FLOATING_POINT_EXCEPTION: u8 = 19;
pub const VIRTUALIZATION_EXCEPTION: u8 = 20;
pub const CONTROL_PROTECTION_EXCEPTION: u8 = 21;
pub const HYPERVISOR_EXCEPTION: u8 = 28;
pub const VMM_COMMUNICATION_EXCEPTION: u8 = 29;
pub const SECURITY_EXCEPTION: u8 = 30;
}
impl Entry {
pub unsafe fn new<T: ToAddress>(handler: T, selector: u16, options: EntryOptions) -> Self {
let handler_addr = handler.to_address();
Self {
offset_low: handler_addr as u16,
selector,
options,
offset_middle: (handler_addr >> 16) as u16,
offset_high: (handler_addr >> 32) as u32,
reserved: 0,
}
}
pub unsafe fn new_default_interrupt<const IDX: u8>() -> Self {
let cs = unsafe { super::instructions::get_cs() };
let mut options = EntryOptions::empty_interrupt_gate();
options.set_present(true);
let handler = match IDX {
8 | 10 | 11 | 12 | 13 | 14 | 17 | 21 | 29 | 30 => {
interrupt_trampoline_with_err::<IDX> as *const () as u64
}
_ => interrupt_trampoline_no_err::<IDX> as *const () as u64,
};
match IDX {
ExceptionVector::DOUBLE_FAULT => {
options.set_interrupt_stack_table_index(super::gdt::DF_STACK);
}
ExceptionVector::NON_MASKABLE_INTERRUPT => {
options.set_interrupt_stack_table_index(super::gdt::NMI_STACK);
}
ExceptionVector::MACHINE_CHECK => {
options.set_interrupt_stack_table_index(super::gdt::MC_STACK);
}
_ => {}
}
unsafe { Self::new(handler, cs, options) }
}
pub const fn missing_interrupt() -> Self {
Self {
offset_low: 0,
selector: 0,
options: EntryOptions::empty_interrupt_gate(),
offset_middle: 0,
offset_high: 0,
reserved: 0,
}
}
pub fn handler_address(&self) -> u64 {
let low = self.offset_low as u64;
let middle = self.offset_middle as u64;
let high = self.offset_high as u64;
(high << 32) | (middle << 16) | low
}
pub fn set_handler<T>(&mut self, handler: T)
where
T: ToAddress,
{
let handler_addr = handler.to_address();
self.offset_low = handler_addr as u16;
self.offset_middle = (handler_addr >> 16) as u16;
self.offset_high = (handler_addr >> 32) as u32;
self.options = EntryOptions::empty_interrupt_gate();
unsafe {
self.selector = super::instructions::get_cs();
}
self.options.set_present(true);
}
}
pub trait ToAddress {
fn to_address(&self) -> u64;
}
impl ToAddress for u64 {
fn to_address(&self) -> u64 {
*self
}
}
impl<T> ToAddress for *const T {
fn to_address(&self) -> u64 {
*self as u64
}
}
impl<T> ToAddress for *mut T {
fn to_address(&self) -> u64 {
*self as u64
}
}
impl<T> ToAddress for NonNull<T> {
fn to_address(&self) -> u64 {
self.as_ptr() as u64
}
}
impl<T> ToAddress for &T {
fn to_address(&self) -> u64 {
*self as *const T as u64
}
}
impl<T> ToAddress for &mut T {
fn to_address(&self) -> u64 {
*self as *const T as u64
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct EntryOptions(u16);
impl Debug for EntryOptions {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("EntryOptions")
.field("present", &self.present())
.field("privilege_level", &self.privilege_level())
.field(
"interrupt_stack_table_index",
&self.interrupt_stack_table_index(),
)
.field("kind", &self.kind())
.finish()
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum EntryType {
#[default]
InterruptGate = 0b1110,
TrapGate = 0b1111,
}
impl EntryType {
pub fn into_u8(self) -> u8 {
self as u8
}
/// # Safety
/// The caller must ensure that `value` is either `0b1110` or `0b1111`, or
/// the result of calling `into_u8` on an `EntryType`.
pub unsafe fn from_u8_unchecked(value: u8) -> Self {
unsafe { core::mem::transmute(value) }
}
}
impl EntryOptions {
pub const fn empty_interrupt_gate() -> Self {
Self(0b1110_0000_0000)
}
pub const fn empty_trap_gate() -> Self {
Self(0b1111_0000_0000)
}
pub fn with_present(mut self, present: bool) -> Self {
self.set_present(present);
self
}
pub fn with_privilege_level(mut self, level: u8) -> Self {
self.set_privilege_level(level);
self
}
pub fn with_interrupt_stack_table_index(mut self, index: u8) -> Self {
self.set_interrupt_stack_table_index(index);
self
}
pub fn with_kind(mut self, kind: EntryType) -> Self {
self.set_kind(kind);
self
}
pub fn present(&self) -> bool {
self.0.get_bit(15)
}
pub fn set_present(&mut self, present: bool) {
self.0.set_bit(15, present);
}
pub fn privilege_level(&self) -> u8 {
self.0.get_bits(13..15) as u8
}
pub fn set_privilege_level(&mut self, level: u8) {
self.0.set_bits(13..15, level as u16);
}
pub fn interrupt_stack_table_index(&self) -> Option<u8> {
let index = self.0.get_bits(0..3) as u8;
if index == 0 { None } else { Some(index - 1) }
}
pub fn set_interrupt_stack_table_index(&mut self, index: u8) {
self.0.set_bits(0..3, index as u16 + 1);
}
pub fn kind(&self) -> EntryType {
unsafe { EntryType::from_u8_unchecked(self.0.get_bits(8..12) as u8) }
}
pub fn set_kind(&mut self, kind: EntryType) {
self.0.set_bits(8..12, kind.into_u8() as u16);
}
}
#[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],
}
#[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;
fn deref(&self) -> &Self::Target {
&self.0
}
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct PageFaultErrorCode: u64 {
const PRESENT = 1 << 0;
const WRITE = 1 << 1;
const USER = 1 << 2;
const RESERVED = 1 << 3;
const INSTRUCTION_FETCH = 1 << 4;
const PROTECTION_KEY = 1 << 5;
const SGX = 1 << 15;
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C, packed(2))]
pub struct IdtRegister<'idt> {
pub limit: u16,
pub base: u64,
_pd: PhantomData<&'idt ()>,
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct InterruptDescriptorTable {
pub divide_by_zero: Entry,
pub debug: Entry,
pub non_maskable_interrupt: Entry,
pub breakpoint: Entry,
pub overflow: Entry,
pub bound_range_exceeded: Entry,
pub invalid_opcode: Entry,
pub device_not_available: Entry,
pub double_fault: Entry,
pub coprocessor_segment_overrun: Entry,
pub invalid_tss: Entry,
pub segment_not_present: Entry,
pub stack_segment_fault: Entry,
pub general_protection_fault: Entry,
pub page_fault: Entry,
_reserved1: Entry,
pub x87_floating_point: Entry,
pub alignment_check: Entry,
pub machine_check: Entry,
pub simd_floating_point: Entry,
pub virtualization: Entry,
pub cp_protection: Entry,
_reserved2: [Entry; 6],
pub hypervisor: Entry,
pub vmm_communication_exception: Entry,
pub security_exception: Entry,
_reserved3: Entry,
pub interrupts: [Entry; 256 - 32],
}
const impl Default for InterruptDescriptorTable {
fn default() -> Self {
Self::new_empty()
}
}
impl InterruptDescriptorTable {
pub const fn new_empty() -> Self {
Self {
divide_by_zero: Entry::missing_interrupt(),
debug: Entry::missing_interrupt(),
non_maskable_interrupt: Entry::missing_interrupt(),
breakpoint: Entry::missing_interrupt(),
overflow: Entry::missing_interrupt(),
bound_range_exceeded: Entry::missing_interrupt(),
invalid_opcode: Entry::missing_interrupt(),
device_not_available: Entry::missing_interrupt(),
double_fault: Entry::missing_interrupt(),
coprocessor_segment_overrun: Entry::missing_interrupt(),
invalid_tss: Entry::missing_interrupt(),
segment_not_present: Entry::missing_interrupt(),
stack_segment_fault: Entry::missing_interrupt(),
general_protection_fault: Entry::missing_interrupt(),
page_fault: Entry::missing_interrupt(),
_reserved1: Entry::missing_interrupt(),
x87_floating_point: Entry::missing_interrupt(),
alignment_check: Entry::missing_interrupt(),
machine_check: Entry::missing_interrupt(),
simd_floating_point: Entry::missing_interrupt(),
virtualization: Entry::missing_interrupt(),
cp_protection: Entry::missing_interrupt(),
_reserved2: [Entry::missing_interrupt(); 6],
hypervisor: Entry::missing_interrupt(),
vmm_communication_exception: Entry::missing_interrupt(),
security_exception: Entry::missing_interrupt(),
_reserved3: Entry::missing_interrupt(),
interrupts: [Entry::missing_interrupt(); 256 - 32],
}
}
pub fn new_default() -> Self {
let mut idt = Self::new_empty();
let slice = unsafe { idt.as_mut_type_erased() };
seq_macro::seq!(
N in 0..=255 {
slice[N] = unsafe { Entry::new_default_interrupt::<N>() };
}
);
idt
}
pub unsafe fn register(&self) -> IdtRegister<'_> {
IdtRegister {
limit: (core::mem::size_of::<Self>() - 1) as u16,
base: self as *const _ as u64,
_pd: PhantomData,
}
}
pub fn load(&'static self) {
unsafe { Self::load_unsafe(self) };
}
pub unsafe fn load_unsafe(&self) {
unsafe {
super::instructions::lidt(&self.register());
}
}
pub unsafe fn as_type_erased(&self) -> &[Entry; 256] {
unsafe { core::mem::transmute::<&Self, &[Entry; 256]>(self) }
}
pub unsafe fn as_mut_type_erased(&mut self) -> &mut [Entry; 256] {
unsafe { core::mem::transmute::<&mut Self, &mut [Entry; 256]>(self) }
}
}
#[unsafe(naked)]
extern "C" fn interrupt_trampoline_with_err<const IDX: u8>() {
naked_asm!(
"push {idx}",
"jmp {dispatcher}",
idx = const { IDX },
dispatcher = sym interrupt_dispatcher,
)
}
#[unsafe(naked)]
extern "C" fn interrupt_trampoline_no_err<const IDX: u8>() {
naked_asm!(
"push 0", // push a dummy error code of 0
"push {idx}",
"jmp {dispatcher}",
idx = const { IDX },
dispatcher = sym interrupt_dispatcher,
)
}
#[unsafe(naked)]
extern "C" fn interrupt_dispatcher() {
naked_asm!(
"push rax",
"push rcx",
"push rdx",
"push rbx",
"push rbp",
"push rsi",
"push rdi",
"push r8",
"push r9",
"push r10",
"push r11",
"push r12",
"push r13",
"push r14",
"push r15",
"lea rdi, [rsp + {registers_size} + 16]", // interrupt frame
"mov rdx, [rsp + {registers_size} + 8]", // error code
"mov rsi, [rsp + {registers_size}]", // interrupt index
"mov rcx, rsp", // pass the current stack pointer as the registers pointer
"call {global_handler}",
"pop r15",
"pop r14",
"pop r13",
"pop r12",
"pop r11",
"pop r10",
"pop r9",
"pop r8",
"pop rdi",
"pop rsi",
"pop rbp",
"pop rbx",
"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,
)
}
extern "C" fn global_interrupt_handler(
frame: &InterruptStackFrame,
index: u8,
error_code: u64,
registers: &Registers,
) {
serial_println!(
"Interrupt {} occurred! Error code: {:#x}, Frame: {:#?}, Registers: {:#?}",
index,
error_code,
frame,
registers
);
match index {
ExceptionVector::BREAKPOINT => {
serial_println!("Breakpoint interrupt handled successfully.");
}
ExceptionVector::PAGE_FAULT => {
serial_println!("Page fault occurred! Error code: {:#x}", error_code);
halt_loop()
}
_ => {}
}
}
#[cfg(test)]
mod tests {
use super::super::instructions;
use super::*;
#[test_case]
fn test_interrupt_handler_trait() {
use crate::sync::LazyLock;
static IDT: LazyLock<InterruptDescriptorTable> =
crate::sync::LazyLock::new(InterruptDescriptorTable::new_default);
unsafe { IDT.load_unsafe() };
instructions::int3(); // Trigger a breakpoint interrupt (interrupt 3)
serial_println!("Breakpoint interrupt handled successfully.");
}
}