add IDT and GDT initialization

This commit is contained in:
Zoe
2026-08-18 12:39:28 -05:00
parent 8dc3839ba4
commit 2a1bf17d8b
11 changed files with 599 additions and 49 deletions
+147
View File
@@ -0,0 +1,147 @@
use core::arch::asm;
#[repr(C, align(8))]
struct Gdt {
entries: [u64; 5],
}
impl Gdt {
pub const fn new() -> Self {
Self { entries: [0; 5] }
}
}
#[repr(C, packed)]
struct GdtPointer {
limit: u16,
base: u64,
}
#[repr(C, packed)]
struct TaskStateSegment {
reserved_0: u32,
privilege_stacks: [u64; 3],
reserved_1: u64,
interrupt_stacks: [u64; 7],
reserved_2: u64,
reserved_3: u16,
iomap_base: u16,
}
impl TaskStateSegment {
pub const fn new() -> Self {
Self {
reserved_0: 0,
privilege_stacks: [0; 3],
reserved_1: 0,
interrupt_stacks: [0; 7],
reserved_2: 0,
reserved_3: 0,
iomap_base: core::mem::size_of::<TaskStateSegment>() as u16,
}
}
}
// 16 KiB
const DOUBLE_FAULT_STACK_SIZE: usize = 16 * 1024;
pub(super) const KERNEL_CODE_SELECTOR: u16 = 1 * 8;
pub(super) const KERNEL_DATA_SELECTOR: u16 = 2 * 8;
pub(super) const TSS_SELECTOR: u16 = 3 * 8;
#[repr(align(16))]
struct ExceptionStack([u8; DOUBLE_FAULT_STACK_SIZE]);
static mut GDT: Gdt = Gdt::new();
static mut TSS: TaskStateSegment = TaskStateSegment::new();
static mut DOUBLE_FAULT_STACK: ExceptionStack = ExceptionStack([0; DOUBLE_FAULT_STACK_SIZE]);
pub fn init() {
const _: () = assert!(core::mem::size_of::<TaskStateSegment>() == 104);
const _: () = assert!(core::mem::size_of::<GdtPointer>() == 10);
unsafe {
let stack_bottom = core::ptr::addr_of_mut!(DOUBLE_FAULT_STACK) as u64;
let stack_top = stack_bottom + DOUBLE_FAULT_STACK_SIZE as u64;
TSS.interrupt_stacks[0] = stack_top;
let [tss_low, tss_high] = tss_descriptor(core::ptr::addr_of!(TSS));
let gdt = Gdt {
entries: [
0,
kernel_code_descriptor(),
kernel_data_descriptor(),
tss_low,
tss_high,
],
};
core::ptr::addr_of_mut!(GDT).write(gdt);
gdt_reload();
}
}
fn gdt_reload() {
unsafe {
asm!(
"lgdt [{gdt_pointer}]",
"push {code_selector}",
"lea rax, [rip + 2f]",
"push rax",
"retfq",
"2:",
"mov ax, {data_selector}",
"mov ds, ax",
"mov es, ax",
"mov fs, ax",
"mov gs, ax",
"mov ss, ax",
"mov ax, {tss_selector}",
"ltr ax",
gdt_pointer = in(reg) &GdtPointer {
limit: (core::mem::size_of::<Gdt>() - 1) as u16,
base: core::ptr::addr_of!(GDT) as u64,
},
code_selector = const KERNEL_CODE_SELECTOR,
data_selector = const KERNEL_DATA_SELECTOR,
tss_selector = const TSS_SELECTOR,
lateout("rax") _,
options(preserves_flags)
)
}
}
const PRESENT: u64 = 1 << 47;
const USER_DESCRIPTOR: u64 = 1 << 44;
const EXECUTABLE: u64 = 1 << 43;
const READ_WRITE: u64 = 1 << 41;
const LONG_MODE: u64 = 1 << 53;
fn kernel_code_descriptor() -> u64 {
PRESENT | USER_DESCRIPTOR | EXECUTABLE | READ_WRITE | LONG_MODE
}
fn kernel_data_descriptor() -> u64 {
PRESENT | USER_DESCRIPTOR | READ_WRITE
}
fn tss_descriptor(tss: *const TaskStateSegment) -> [u64; 2] {
let base = tss as u64;
let limit = (core::mem::size_of::<TaskStateSegment>() - 1) as u64;
let low = (limit & 0xFFFF)
| ((base & 0x00FF_FFFF) << 16)
| (0x09 << 40)
| (1 << 47)
| (((limit >> 16) & 0x0F) << 48)
| (((base >> 24) & 0xFF) << 56);
let high = base >> 32;
[low, high]
}
+151
View File
@@ -0,0 +1,151 @@
use core::arch::asm;
use super::idt::{self, InterruptStackFrame};
use crate::{hcf, println};
macro_rules! fatal_without_error_code {
($handler:ident, $name:literal) => {
extern "x86-interrupt" fn $handler(frame: InterruptStackFrame) {
fatal_exception($name, &frame, None);
}
};
}
macro_rules! fatal_with_error_code {
($handler:ident, $name:literal) => {
extern "x86-interrupt" fn $handler(frame: InterruptStackFrame, error_code: u64) {
fatal_exception($name, &frame, Some(error_code));
}
};
}
extern "x86-interrupt" fn debug_handler(frame: InterruptStackFrame) {
fatal_exception("DEBUG EXCEPTION", &frame, None);
}
extern "x86-interrupt" fn non_maskable_interrupt_handler(frame: InterruptStackFrame) {
fatal_exception("NON-MASKABLE INTERRUPT", &frame, None);
}
extern "x86-interrupt" fn breakpoint_handler(frame: InterruptStackFrame) {
report_exception("BREAKPOINT", &frame, None);
}
extern "x86-interrupt" fn double_fault_handler(frame: InterruptStackFrame, error_code: u64) {
fatal_exception("DOUBLE FAULT", &frame, Some(error_code));
}
extern "x86-interrupt" fn page_fault_handler(frame: InterruptStackFrame, error_code: u64) {
report_exception("PAGE FAULT", &frame, Some(error_code));
println!("Faulting address: {:#X}", read_cr2());
print_page_fault_error(error_code);
hcf();
}
fatal_without_error_code!(divide_error_handler, "DIVIDE ERROR");
fatal_without_error_code!(invalid_opcode_handler, "INVALID OPCODE");
fatal_without_error_code!(device_not_available_handler, "DEVICE NOT AVAILABLE");
fatal_without_error_code!(x87_floating_point_handler, "X87 FLOATING-POINT EXCEPTION");
fatal_without_error_code!(machine_check_handler, "MACHINE CHECK");
fatal_without_error_code!(simd_floating_point_handler, "SIMD FLOATING-POINT EXCEPTION");
fatal_with_error_code!(invalid_tss_handler, "INVALID TSS");
fatal_with_error_code!(segment_not_present_handler, "SEGMENT NOT PRESENT");
fatal_with_error_code!(stack_segment_fault_handler, "STACK-SEGMENT FAULT");
fatal_with_error_code!(general_protection_handler, "GENERAL PROTECTION FAULT");
fatal_with_error_code!(alignment_check_handler, "ALIGNMENT CHECK");
pub(super) fn install(idt: &mut idt::Idt) {
idt.set_handler(0, divide_error_handler, 0);
idt.set_handler(1, debug_handler, 0);
idt.set_handler(2, non_maskable_interrupt_handler, 0);
idt.set_handler(3, breakpoint_handler, 0);
idt.set_handler(6, invalid_opcode_handler, 0);
idt.set_handler(7, device_not_available_handler, 0);
idt.set_error_code_handler(8, double_fault_handler, 1);
idt.set_error_code_handler(10, invalid_tss_handler, 0);
idt.set_error_code_handler(11, segment_not_present_handler, 0);
idt.set_error_code_handler(12, stack_segment_fault_handler, 0);
idt.set_error_code_handler(13, general_protection_handler, 0);
idt.set_error_code_handler(14, page_fault_handler, 0);
idt.set_handler(16, x87_floating_point_handler, 0);
idt.set_error_code_handler(17, alignment_check_handler, 0);
idt.set_handler(18, machine_check_handler, 0);
idt.set_handler(19, simd_floating_point_handler, 0);
}
fn read_cr2() -> u64 {
let value: u64;
unsafe {
asm!(
"mov {}, cr2",
out(reg) value,
options(nomem, nostack, preserves_flags),
);
}
value
}
fn print_page_fault_error(error_code: u64) {
let present = error_code & (1 << 0) != 0;
let write = error_code & (1 << 1) != 0;
let user = error_code & (1 << 2) != 0;
println!(
"Cause: {}",
if present {
"protection violation"
} else {
"page not present"
}
);
println!("Access: {}", if write { "write" } else { "read" });
println!("Mode: {}", if user { "user" } else { "supervisor" });
if error_code & (1 << 3) != 0 {
println!("Reserved page-table bit was set");
}
if error_code & (1 << 4) != 0 {
println!("Access was an instruction fetch");
}
if error_code & (1 << 5) != 0 {
println!("Protection-key violation");
}
if error_code & (1 << 6) != 0 {
println!("Shadow-stack access");
}
if error_code & (1 << 15) != 0 {
println!("Software Guard Extensions violation");
}
}
fn report_exception(name: &'static str, frame: &InterruptStackFrame, error_code: Option<u64>) {
println!();
println!("========= {name} =========");
println!(
"Origin: {}",
if frame.code_segment & 0b11 == 3 {
"user"
} else {
"kernel"
}
);
println!("RIP: {:#X}", frame.instruction_pointer.as_u64());
println!("CS: {:#X}", frame.code_segment);
println!("FLAGS: {:#X}", frame.cpu_flags);
println!("RSP: {:#X}", frame.stack_pointer.as_u64());
println!("SS: {:#X}", frame.stack_segment);
if let Some(error_code) = error_code {
println!("ERROR: {:#X}", error_code);
}
}
fn fatal_exception(name: &'static str, frame: &InterruptStackFrame, error_code: Option<u64>) -> ! {
report_exception(name, frame, error_code);
hcf()
}
+115
View File
@@ -0,0 +1,115 @@
use super::exceptions;
use crate::{arch::x86_64::gdt::KERNEL_CODE_SELECTOR, memory::VirtualAddr};
#[repr(C, packed)]
#[derive(Clone, Copy)]
struct IdtEntry {
offset_low: u16,
code_selector: u16,
ist: u8,
attributes: u8,
offset_middle: u16,
offset_high: u32,
reserved: u32,
}
impl IdtEntry {
const fn missing() -> Self {
return Self {
offset_low: 0,
code_selector: 0x08,
ist: 0,
attributes: 0,
offset_middle: 0,
offset_high: 0,
reserved: 0,
};
}
}
#[repr(C, packed)]
struct IdtPointer {
limit: u16,
base: u64,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub(super) struct InterruptStackFrame {
pub instruction_pointer: VirtualAddr,
pub code_segment: u64,
pub cpu_flags: u64,
pub stack_pointer: VirtualAddr,
pub stack_segment: u64,
}
const INTERRUPT_GATE: u8 = 0b1110;
const PRESENT: u8 = 1 << 7;
const KERNEL_INTERRUPT_GATE: u8 = PRESENT | INTERRUPT_GATE;
pub(super) type Handler = extern "x86-interrupt" fn(InterruptStackFrame);
pub(super) type ErrorCodeHandler = extern "x86-interrupt" fn(InterruptStackFrame, u64);
pub(super) struct Idt {
entries: [IdtEntry; 256],
}
impl Idt {
pub const fn new() -> Self {
Self {
entries: [IdtEntry::missing(); 256],
}
}
pub(super) fn set_handler(&mut self, vector: u8, handler: Handler, ist: u8) {
self.set_handler_address(vector, handler as usize, ist);
}
pub(super) fn set_error_code_handler(
&mut self,
vector: u8,
handler: ErrorCodeHandler,
ist: u8,
) {
self.set_handler_address(vector, handler as usize, ist);
}
fn set_handler_address(&mut self, vector: u8, address: usize, ist: u8) {
self.entries[vector as usize] = IdtEntry {
offset_low: address as u16,
offset_middle: (address >> 16) as u16,
offset_high: (address >> 32) as u32,
code_selector: KERNEL_CODE_SELECTOR,
ist: ist & 0b111,
attributes: KERNEL_INTERRUPT_GATE,
reserved: 0,
};
}
}
static mut IDT: Idt = Idt::new();
pub fn idt_init() {
const _: () = assert!(core::mem::size_of::<IdtEntry>() == 16);
const _: () = assert!(core::mem::size_of::<IdtPointer>() == 10);
const _: () = assert!(core::mem::size_of::<InterruptStackFrame>() == 40);
let mut idt = Idt::new();
exceptions::install(&mut idt);
unsafe {
core::ptr::addr_of_mut!(IDT).write(idt);
let pointer = IdtPointer {
limit: (core::mem::size_of::<Idt>() - 1) as u16,
base: core::ptr::addr_of!(IDT) as u64,
};
core::arch::asm!(
"lidt [{}]",
in(reg) core::ptr::addr_of!(pointer),
options(readonly, nostack, preserves_flags),
);
}
}
+12
View File
@@ -0,0 +1,12 @@
use core::arch::asm;
mod exceptions;
mod idt;
pub use idt::idt_init as init;
pub fn disable_interrupts() {
unsafe {
asm!("cli");
}
}
+22
View File
@@ -1 +1,23 @@
mod gdt;
mod interrupts;
pub mod port;
use core::arch::asm;
pub use interrupts::disable_interrupts;
use crate::println;
pub fn init() {
disable_interrupts();
println!("Loading GDT...");
gdt::init();
println!("Loading IDT...");
interrupts::init();
}
pub fn halt() {
unsafe {
asm!("hlt");
}
}