interrupts
This commit is contained in:
parent
bbe0551ac5
commit
eccb202d04
14
kernel/Cargo.lock
generated
14
kernel/Cargo.lock
generated
|
|
@ -2,6 +2,12 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.13.1"
|
||||
|
|
@ -12,5 +18,13 @@ checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
|
|||
name = "kernel"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"bitflags",
|
||||
"seq-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "seq-macro"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
|
||||
|
|
|
|||
|
|
@ -18,4 +18,6 @@ name = "simple"
|
|||
harness = false
|
||||
|
||||
[dependencies]
|
||||
bit_field = "0.10.3"
|
||||
bitflags = "2.13.1"
|
||||
seq-macro = "0.3.6"
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ qemu-system-x86_64 \
|
|||
-drive file="$image",format=raw \
|
||||
-machine q35,accel=kvm -enable-kvm \
|
||||
-drive if=pflash,format=raw,readonly=on,file="$OVMF_PATH/FV/OVMF_CODE.fd" \
|
||||
-chardev stdio,id=serial0,logfile=qemu.log,signal=off \
|
||||
-chardev stdio,id=serial0,logfile=qemu.log,signal=on \
|
||||
-serial chardev:serial0 \
|
||||
-vga std \
|
||||
${QEMU_TEST_ARGS[@]}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#![no_std]
|
||||
#![feature(const_trait_impl, const_default)]
|
||||
#![feature(const_trait_impl, const_default, abi_x86_interrupt, never_type)]
|
||||
#![cfg_attr(test, feature(custom_test_frameworks))]
|
||||
#![cfg_attr(test, test_runner(crate::tests::test_runner))]
|
||||
#![cfg_attr(test, no_main)]
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
#![cfg(target_arch = "x86_64")]
|
||||
|
||||
#[inline]
|
||||
pub fn hlt() {
|
||||
unsafe {
|
||||
core::arch::asm!("hlt", options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
}
|
||||
pub mod idt;
|
||||
pub mod instructions;
|
||||
|
||||
pub use instructions::hlt;
|
||||
|
||||
pub fn halt_loop() -> ! {
|
||||
loop {
|
||||
|
|
@ -47,3 +45,25 @@ impl PortU16 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(C)]
|
||||
// registers in the order they are pushed onto the stack:
|
||||
pub struct Registers {
|
||||
pub r15: u64,
|
||||
pub r14: u64,
|
||||
pub r13: u64,
|
||||
pub r12: u64,
|
||||
pub r11: u64,
|
||||
pub r10: u64,
|
||||
pub r9: u64,
|
||||
pub r8: u64,
|
||||
pub rdi: u64,
|
||||
pub rsi: u64,
|
||||
pub rbp: u64,
|
||||
// rsp is not included here
|
||||
pub rbx: u64,
|
||||
pub rdx: u64,
|
||||
pub rcx: u64,
|
||||
pub rax: u64,
|
||||
}
|
||||
|
|
|
|||
648
kernel/src/x86_64/idt.rs
Normal file
648
kernel/src/x86_64/idt.rs
Normal file
|
|
@ -0,0 +1,648 @@
|
|||
use core::{arch::naked_asm, fmt::Debug, marker::PhantomData, ops::Deref, ptr::NonNull};
|
||||
|
||||
use bit_field::BitField;
|
||||
|
||||
use crate::{serial_println, x86_64::Registers};
|
||||
|
||||
#[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()
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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 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(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct InterruptStackFrameInner {
|
||||
pub instruction_pointer: u64,
|
||||
pub code_segment: u16,
|
||||
_reserved: [u16; 3],
|
||||
pub stack_pointer: u64,
|
||||
pub stack_segment: u16,
|
||||
_reserved2: [u16; 3],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct InterruptStackFrame(InterruptStackFrameInner);
|
||||
|
||||
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 InterruptDescriptorTablePointer<'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 pointer(&self) -> InterruptDescriptorTablePointer<'_> {
|
||||
InterruptDescriptorTablePointer {
|
||||
limit: (core::mem::size_of::<Self>() - 1) as u16,
|
||||
base: self as *const _ as u64,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn load(&self) {
|
||||
unsafe {
|
||||
super::instructions::lidt(&self.pointer());
|
||||
}
|
||||
}
|
||||
|
||||
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) }
|
||||
}
|
||||
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",
|
||||
"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,
|
||||
) {
|
||||
crate::serial_println!(
|
||||
"Interrupt {} occurred! Error code: {:#x}, Frame: {:?}, Registers: {:?}",
|
||||
index,
|
||||
error_code,
|
||||
frame,
|
||||
registers
|
||||
);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
serial_println!("idt: {:#?}", idt);
|
||||
|
||||
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();
|
||||
}
|
||||
45
kernel/src/x86_64/instructions.rs
Normal file
45
kernel/src/x86_64/instructions.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#[inline]
|
||||
pub fn hlt() {
|
||||
unsafe {
|
||||
core::arch::asm!("hlt", options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn lidt(idt: &super::idt::InterruptDescriptorTablePointer) {
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"lidt [{}]",
|
||||
in(reg) idt,
|
||||
options(readonly, nostack, preserves_flags)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn int3() {
|
||||
unsafe {
|
||||
core::arch::asm!("int3", options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! read_segment {
|
||||
($segment:literal) => {
|
||||
{
|
||||
let value: u16;
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
concat!("mov {0:x}, ", $segment),
|
||||
out(reg) value,
|
||||
options(nomem, nostack, preserves_flags)
|
||||
);
|
||||
}
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn get_cs() -> u16 {
|
||||
read_segment!("cs")
|
||||
}
|
||||
Loading…
Reference in a new issue