mirror of
https://github.com/xomboverlord/xomb.git
synced 2026-01-11 10:16:36 +01:00
Add interrupt handling and kernel-space GDT
- Implement IDT with handlers for CPU exceptions (vectors 0-19) - Add kernel-space GDT module to support identity mapping removal - Add memory intrinsics (memcpy, memset, memmove, memcmp) for no_std - Add remove_identity_mapping() to paging module - Reload GDT to higher-half before removing identity mapping Exception handlers print full register state and halt on fault. Tested with divide-by-zero and INT3 breakpoint exceptions.
This commit is contained in:
137
src/arch/x86_64/gdt.rs
Normal file
137
src/arch/x86_64/gdt.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
//! Global Descriptor Table (GDT) for x86-64
|
||||
//!
|
||||
//! This module provides a kernel-space GDT that can be used after
|
||||
//! identity mapping is removed.
|
||||
|
||||
use core::arch::asm;
|
||||
use core::mem::size_of;
|
||||
|
||||
/// GDT entry (segment descriptor)
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GdtEntry {
|
||||
limit_low: u16,
|
||||
base_low: u16,
|
||||
base_mid: u8,
|
||||
access: u8,
|
||||
flags_limit_high: u8,
|
||||
base_high: u8,
|
||||
}
|
||||
|
||||
impl GdtEntry {
|
||||
/// Create a null descriptor
|
||||
pub const fn null() -> Self {
|
||||
Self {
|
||||
limit_low: 0,
|
||||
base_low: 0,
|
||||
base_mid: 0,
|
||||
access: 0,
|
||||
flags_limit_high: 0,
|
||||
base_high: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a 64-bit code segment descriptor
|
||||
pub const fn code64() -> Self {
|
||||
Self {
|
||||
limit_low: 0xFFFF,
|
||||
base_low: 0,
|
||||
base_mid: 0,
|
||||
access: 0x9A, // Present, ring 0, code, exec/read
|
||||
flags_limit_high: 0xAF, // 64-bit, limit high nibble
|
||||
base_high: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a data segment descriptor
|
||||
pub const fn data() -> Self {
|
||||
Self {
|
||||
limit_low: 0xFFFF,
|
||||
base_low: 0,
|
||||
base_mid: 0,
|
||||
access: 0x92, // Present, ring 0, data, read/write
|
||||
flags_limit_high: 0xCF, // 32-bit, 4KB granularity
|
||||
base_high: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// GDT pointer for LGDT instruction
|
||||
#[repr(C, packed)]
|
||||
pub struct GdtPointer {
|
||||
limit: u16,
|
||||
base: u64,
|
||||
}
|
||||
|
||||
/// Number of GDT entries
|
||||
const GDT_ENTRIES: usize = 3;
|
||||
|
||||
/// Kernel GDT with null, code, and data segments
|
||||
#[repr(C, align(16))]
|
||||
pub struct Gdt {
|
||||
entries: [GdtEntry; GDT_ENTRIES],
|
||||
}
|
||||
|
||||
impl Gdt {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
entries: [
|
||||
GdtEntry::null(), // 0x00: Null descriptor
|
||||
GdtEntry::code64(), // 0x08: Kernel code segment
|
||||
GdtEntry::data(), // 0x10: Kernel data segment
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Static kernel GDT (in higher-half memory)
|
||||
static KERNEL_GDT: Gdt = Gdt::new();
|
||||
|
||||
/// Reload the GDT with the kernel-space GDT
|
||||
///
|
||||
/// This should be called before removing identity mapping to ensure
|
||||
/// the GDT is accessible after the low memory is unmapped.
|
||||
pub fn reload() {
|
||||
let pointer = GdtPointer {
|
||||
limit: (size_of::<[GdtEntry; GDT_ENTRIES]>() - 1) as u16,
|
||||
base: KERNEL_GDT.entries.as_ptr() as u64,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
// Load new GDT
|
||||
asm!("lgdt [{}]", in(reg) &pointer, options(nostack, preserves_flags));
|
||||
|
||||
// Reload code segment by doing a far return
|
||||
// Push SS, RSP, RFLAGS, CS, RIP and do IRETQ
|
||||
asm!(
|
||||
"push 0x10", // SS
|
||||
"push rsp", // RSP
|
||||
"add qword ptr [rsp], 8", // Adjust for the push
|
||||
"pushfq", // RFLAGS
|
||||
"push 0x08", // CS
|
||||
"lea rax, [rip + 2f]", // RIP (address of label 2)
|
||||
"push rax",
|
||||
"iretq",
|
||||
"2:",
|
||||
// Reload data segments
|
||||
"mov ax, 0x10",
|
||||
"mov ds, ax",
|
||||
"mov es, ax",
|
||||
"mov fs, ax",
|
||||
"mov gs, ax",
|
||||
// SS is already set by IRETQ
|
||||
out("rax") _,
|
||||
options(preserves_flags)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the kernel code segment selector
|
||||
pub const fn kernel_cs() -> u16 {
|
||||
0x08
|
||||
}
|
||||
|
||||
/// Get the kernel data segment selector
|
||||
pub const fn kernel_ds() -> u16 {
|
||||
0x10
|
||||
}
|
||||
452
src/arch/x86_64/interrupts.rs
Normal file
452
src/arch/x86_64/interrupts.rs
Normal file
@@ -0,0 +1,452 @@
|
||||
//! Interrupt Descriptor Table (IDT) and interrupt handling for x86-64
|
||||
//!
|
||||
//! This module sets up the IDT and provides handlers for CPU exceptions
|
||||
//! and hardware interrupts.
|
||||
|
||||
use core::arch::asm;
|
||||
use core::mem::size_of;
|
||||
|
||||
/// Number of IDT entries (0-255)
|
||||
pub const IDT_ENTRIES: usize = 256;
|
||||
|
||||
/// IDT Gate types
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum GateType {
|
||||
/// Interrupt gate - clears IF flag (disables interrupts)
|
||||
Interrupt = 0xE,
|
||||
/// Trap gate - does not clear IF flag
|
||||
Trap = 0xF,
|
||||
}
|
||||
|
||||
/// An entry in the Interrupt Descriptor Table (64-bit mode)
|
||||
#[repr(C, packed)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct IdtEntry {
|
||||
/// Offset bits 0-15
|
||||
offset_low: u16,
|
||||
/// Code segment selector
|
||||
selector: u16,
|
||||
/// Bits 0-2: IST index, bits 3-7: reserved (0)
|
||||
ist: u8,
|
||||
/// Type and attributes: P(1) DPL(2) 0(1) Type(4)
|
||||
type_attr: u8,
|
||||
/// Offset bits 16-31
|
||||
offset_mid: u16,
|
||||
/// Offset bits 32-63
|
||||
offset_high: u32,
|
||||
/// Reserved (must be 0)
|
||||
reserved: u32,
|
||||
}
|
||||
|
||||
impl IdtEntry {
|
||||
/// Create an empty (not present) IDT entry
|
||||
pub const fn empty() -> Self {
|
||||
Self {
|
||||
offset_low: 0,
|
||||
selector: 0,
|
||||
ist: 0,
|
||||
type_attr: 0,
|
||||
offset_mid: 0,
|
||||
offset_high: 0,
|
||||
reserved: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new IDT entry
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `handler` - Address of the interrupt handler function
|
||||
/// * `selector` - Code segment selector (usually 0x08 for kernel code)
|
||||
/// * `gate_type` - Type of gate (Interrupt or Trap)
|
||||
/// * `dpl` - Descriptor Privilege Level (0-3)
|
||||
/// * `ist` - Interrupt Stack Table index (0 = no IST)
|
||||
pub const fn new(handler: u64, selector: u16, gate_type: GateType, dpl: u8, ist: u8) -> Self {
|
||||
let type_attr = (1 << 7) // Present bit
|
||||
| ((dpl & 0x3) << 5) // DPL
|
||||
| (gate_type as u8); // Gate type
|
||||
|
||||
Self {
|
||||
offset_low: handler as u16,
|
||||
selector,
|
||||
ist: ist & 0x7,
|
||||
type_attr,
|
||||
offset_mid: (handler >> 16) as u16,
|
||||
offset_high: (handler >> 32) as u32,
|
||||
reserved: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the handler address
|
||||
pub fn set_handler(&mut self, handler: u64) {
|
||||
self.offset_low = handler as u16;
|
||||
self.offset_mid = (handler >> 16) as u16;
|
||||
self.offset_high = (handler >> 32) as u32;
|
||||
}
|
||||
|
||||
/// Check if this entry is present
|
||||
pub const fn is_present(&self) -> bool {
|
||||
self.type_attr & (1 << 7) != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// IDT descriptor for LIDT instruction
|
||||
#[repr(C, packed)]
|
||||
pub struct IdtDescriptor {
|
||||
/// Size of IDT minus 1
|
||||
limit: u16,
|
||||
/// Virtual address of the IDT
|
||||
base: u64,
|
||||
}
|
||||
|
||||
/// The Interrupt Descriptor Table
|
||||
#[repr(C, align(16))]
|
||||
pub struct Idt {
|
||||
entries: [IdtEntry; IDT_ENTRIES],
|
||||
}
|
||||
|
||||
impl Idt {
|
||||
/// Create a new IDT with all entries empty
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
entries: [IdtEntry::empty(); IDT_ENTRIES],
|
||||
}
|
||||
}
|
||||
|
||||
/// Set an interrupt handler
|
||||
pub fn set_handler(&mut self, vector: u8, handler: u64, gate_type: GateType) {
|
||||
self.entries[vector as usize] = IdtEntry::new(
|
||||
handler,
|
||||
0x08, // Kernel code segment
|
||||
gate_type,
|
||||
0, // DPL 0 (kernel)
|
||||
0, // No IST
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Interrupt stack frame pushed by CPU on interrupt/exception
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct InterruptStackFrame {
|
||||
/// Instruction pointer
|
||||
pub rip: u64,
|
||||
/// Code segment
|
||||
pub cs: u64,
|
||||
/// CPU flags
|
||||
pub rflags: u64,
|
||||
/// Stack pointer
|
||||
pub rsp: u64,
|
||||
/// Stack segment
|
||||
pub ss: u64,
|
||||
}
|
||||
|
||||
/// Exception vector numbers
|
||||
pub mod vectors {
|
||||
pub const DIVIDE_ERROR: u8 = 0;
|
||||
pub const DEBUG: u8 = 1;
|
||||
pub const NMI: u8 = 2;
|
||||
pub const BREAKPOINT: u8 = 3;
|
||||
pub const OVERFLOW: u8 = 4;
|
||||
pub const BOUND_RANGE: u8 = 5;
|
||||
pub const INVALID_OPCODE: u8 = 6;
|
||||
pub const DEVICE_NOT_AVAILABLE: u8 = 7;
|
||||
pub const DOUBLE_FAULT: u8 = 8;
|
||||
pub const INVALID_TSS: u8 = 10;
|
||||
pub const SEGMENT_NOT_PRESENT: u8 = 11;
|
||||
pub const STACK_SEGMENT: u8 = 12;
|
||||
pub const GENERAL_PROTECTION: u8 = 13;
|
||||
pub const PAGE_FAULT: u8 = 14;
|
||||
pub const X87_FLOATING_POINT: u8 = 16;
|
||||
pub const ALIGNMENT_CHECK: u8 = 17;
|
||||
pub const MACHINE_CHECK: u8 = 18;
|
||||
pub const SIMD_FLOATING_POINT: u8 = 19;
|
||||
pub const VIRTUALIZATION: u8 = 20;
|
||||
pub const CONTROL_PROTECTION: u8 = 21;
|
||||
pub const HYPERVISOR_INJECTION: u8 = 28;
|
||||
pub const VMM_COMMUNICATION: u8 = 29;
|
||||
pub const SECURITY: u8 = 30;
|
||||
}
|
||||
|
||||
/// Exception names for debugging
|
||||
pub fn exception_name(vector: u8) -> &'static str {
|
||||
match vector {
|
||||
0 => "Divide Error",
|
||||
1 => "Debug",
|
||||
2 => "Non-Maskable Interrupt",
|
||||
3 => "Breakpoint",
|
||||
4 => "Overflow",
|
||||
5 => "Bound Range Exceeded",
|
||||
6 => "Invalid Opcode",
|
||||
7 => "Device Not Available",
|
||||
8 => "Double Fault",
|
||||
9 => "Coprocessor Segment Overrun",
|
||||
10 => "Invalid TSS",
|
||||
11 => "Segment Not Present",
|
||||
12 => "Stack-Segment Fault",
|
||||
13 => "General Protection Fault",
|
||||
14 => "Page Fault",
|
||||
16 => "x87 Floating-Point Exception",
|
||||
17 => "Alignment Check",
|
||||
18 => "Machine Check",
|
||||
19 => "SIMD Floating-Point Exception",
|
||||
20 => "Virtualization Exception",
|
||||
21 => "Control Protection Exception",
|
||||
28 => "Hypervisor Injection Exception",
|
||||
29 => "VMM Communication Exception",
|
||||
30 => "Security Exception",
|
||||
_ => "Unknown Exception",
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if an exception pushes an error code
|
||||
pub const fn has_error_code(vector: u8) -> bool {
|
||||
matches!(vector, 8 | 10 | 11 | 12 | 13 | 14 | 17 | 21 | 29 | 30)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Global IDT and Initialization
|
||||
// ============================================================================
|
||||
|
||||
use core::cell::UnsafeCell;
|
||||
|
||||
/// Wrapper for static IDT that can be safely shared (single-threaded init)
|
||||
#[repr(transparent)]
|
||||
struct StaticIdt(UnsafeCell<Idt>);
|
||||
|
||||
// SAFETY: IDT is only mutated during single-threaded init
|
||||
unsafe impl Sync for StaticIdt {}
|
||||
|
||||
/// Static IDT for loading (must be 'static for LIDT)
|
||||
static STATIC_IDT: StaticIdt = StaticIdt(UnsafeCell::new(Idt::new()));
|
||||
|
||||
/// Initialize the IDT with default exception handlers
|
||||
pub fn init() {
|
||||
use core::fmt::Write;
|
||||
use crate::serial::SerialPort;
|
||||
|
||||
let mut serial = unsafe { SerialPort::new(0x3F8) };
|
||||
|
||||
// SAFETY: Single-threaded initialization
|
||||
let idt = unsafe { &mut *STATIC_IDT.0.get() };
|
||||
|
||||
// Set up exception handlers (vectors 0-31)
|
||||
idt.set_handler(vectors::DIVIDE_ERROR, divide_error_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::DEBUG, debug_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::NMI, nmi_handler as *const () as u64, GateType::Interrupt);
|
||||
idt.set_handler(vectors::BREAKPOINT, breakpoint_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::OVERFLOW, overflow_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::BOUND_RANGE, bound_range_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::INVALID_OPCODE, invalid_opcode_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::DEVICE_NOT_AVAILABLE, device_not_available_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::DOUBLE_FAULT, double_fault_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::INVALID_TSS, invalid_tss_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::SEGMENT_NOT_PRESENT, segment_not_present_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::STACK_SEGMENT, stack_segment_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::GENERAL_PROTECTION, general_protection_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::PAGE_FAULT, page_fault_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::X87_FLOATING_POINT, x87_fp_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::ALIGNMENT_CHECK, alignment_check_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::MACHINE_CHECK, machine_check_handler as *const () as u64, GateType::Trap);
|
||||
idt.set_handler(vectors::SIMD_FLOATING_POINT, simd_fp_handler as *const () as u64, GateType::Trap);
|
||||
|
||||
// Load the IDT
|
||||
// SAFETY: STATIC_IDT lives for 'static
|
||||
unsafe {
|
||||
load_idt(STATIC_IDT.0.get());
|
||||
}
|
||||
|
||||
writeln!(serial, " IDT initialized with {} entries", IDT_ENTRIES).ok();
|
||||
}
|
||||
|
||||
/// Load IDT from a raw pointer
|
||||
unsafe fn load_idt(idt: *const Idt) {
|
||||
let descriptor = IdtDescriptor {
|
||||
limit: (size_of::<[IdtEntry; IDT_ENTRIES]>() - 1) as u16,
|
||||
base: unsafe { (*idt).entries.as_ptr() as u64 },
|
||||
};
|
||||
|
||||
unsafe {
|
||||
asm!("lidt [{}]", in(reg) &descriptor, options(nostack, preserves_flags));
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Exception Handlers
|
||||
// ============================================================================
|
||||
|
||||
// Macro to generate exception handler stubs
|
||||
macro_rules! exception_handler {
|
||||
($name:ident, $vector:expr, no_error_code) => {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn $name() {
|
||||
core::arch::naked_asm!(
|
||||
// Push dummy error code for uniform stack frame
|
||||
"push 0",
|
||||
// Push vector number
|
||||
"push {vector}",
|
||||
// Jump to common handler
|
||||
"jmp {common}",
|
||||
vector = const $vector,
|
||||
common = sym exception_common,
|
||||
);
|
||||
}
|
||||
};
|
||||
($name:ident, $vector:expr, error_code) => {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn $name() {
|
||||
core::arch::naked_asm!(
|
||||
// Error code already on stack
|
||||
// Push vector number
|
||||
"push {vector}",
|
||||
// Jump to common handler
|
||||
"jmp {common}",
|
||||
vector = const $vector,
|
||||
common = sym exception_common,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Common exception handler - saves state and calls Rust handler
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn exception_common() {
|
||||
core::arch::naked_asm!(
|
||||
// Save all general-purpose registers
|
||||
"push rax",
|
||||
"push rbx",
|
||||
"push rcx",
|
||||
"push rdx",
|
||||
"push rsi",
|
||||
"push rdi",
|
||||
"push rbp",
|
||||
"push r8",
|
||||
"push r9",
|
||||
"push r10",
|
||||
"push r11",
|
||||
"push r12",
|
||||
"push r13",
|
||||
"push r14",
|
||||
"push r15",
|
||||
|
||||
// First argument (rdi) = pointer to saved state on stack
|
||||
"mov rdi, rsp",
|
||||
// Call Rust exception handler
|
||||
"call {handler}",
|
||||
|
||||
// Restore all general-purpose registers
|
||||
"pop r15",
|
||||
"pop r14",
|
||||
"pop r13",
|
||||
"pop r12",
|
||||
"pop r11",
|
||||
"pop r10",
|
||||
"pop r9",
|
||||
"pop r8",
|
||||
"pop rbp",
|
||||
"pop rdi",
|
||||
"pop rsi",
|
||||
"pop rdx",
|
||||
"pop rcx",
|
||||
"pop rbx",
|
||||
"pop rax",
|
||||
|
||||
// Remove vector number and error code
|
||||
"add rsp, 16",
|
||||
|
||||
// Return from interrupt
|
||||
"iretq",
|
||||
|
||||
handler = sym rust_exception_handler,
|
||||
);
|
||||
}
|
||||
|
||||
/// Saved CPU state during exception
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct ExceptionState {
|
||||
// Pushed by exception_common (in reverse order)
|
||||
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 rbp: u64,
|
||||
pub rdi: u64,
|
||||
pub rsi: u64,
|
||||
pub rdx: u64,
|
||||
pub rcx: u64,
|
||||
pub rbx: u64,
|
||||
pub rax: u64,
|
||||
// Pushed by handler stub
|
||||
pub vector: u64,
|
||||
pub error_code: u64,
|
||||
// Pushed by CPU
|
||||
pub rip: u64,
|
||||
pub cs: u64,
|
||||
pub rflags: u64,
|
||||
pub rsp: u64,
|
||||
pub ss: u64,
|
||||
}
|
||||
|
||||
/// Rust exception handler - called from assembly
|
||||
extern "C" fn rust_exception_handler(state: &ExceptionState) {
|
||||
use core::fmt::Write;
|
||||
use crate::serial::SerialPort;
|
||||
|
||||
let mut serial = unsafe { SerialPort::new(0x3F8) };
|
||||
|
||||
writeln!(serial, "").ok();
|
||||
writeln!(serial, "!!! EXCEPTION: {} (vector {})",
|
||||
exception_name(state.vector as u8), state.vector).ok();
|
||||
writeln!(serial, " Error code: {:#x}", state.error_code).ok();
|
||||
writeln!(serial, " RIP: {:#x} CS: {:#x}", state.rip, state.cs).ok();
|
||||
writeln!(serial, " RSP: {:#x} SS: {:#x}", state.rsp, state.ss).ok();
|
||||
writeln!(serial, " RFLAGS: {:#x}", state.rflags).ok();
|
||||
writeln!(serial, " RAX: {:#018x} RBX: {:#018x}", state.rax, state.rbx).ok();
|
||||
writeln!(serial, " RCX: {:#018x} RDX: {:#018x}", state.rcx, state.rdx).ok();
|
||||
writeln!(serial, " RSI: {:#018x} RDI: {:#018x}", state.rsi, state.rdi).ok();
|
||||
writeln!(serial, " RBP: {:#018x} R8: {:#018x}", state.rbp, state.r8).ok();
|
||||
writeln!(serial, " R9: {:#018x} R10: {:#018x}", state.r9, state.r10).ok();
|
||||
writeln!(serial, " R11: {:#018x} R12: {:#018x}", state.r11, state.r12).ok();
|
||||
writeln!(serial, " R13: {:#018x} R14: {:#018x}", state.r13, state.r14).ok();
|
||||
writeln!(serial, " R15: {:#018x}", state.r15).ok();
|
||||
|
||||
// For page faults, also print CR2 (faulting address)
|
||||
if state.vector == vectors::PAGE_FAULT as u64 {
|
||||
let cr2: u64;
|
||||
unsafe { asm!("mov {}, cr2", out(reg) cr2, options(nostack, preserves_flags)); }
|
||||
writeln!(serial, " CR2 (fault addr): {:#x}", cr2).ok();
|
||||
}
|
||||
|
||||
writeln!(serial, "").ok();
|
||||
writeln!(serial, "System halted.").ok();
|
||||
|
||||
// Halt the system
|
||||
loop {
|
||||
unsafe { asm!("cli; hlt", options(nostack, nomem)); }
|
||||
}
|
||||
}
|
||||
|
||||
// Generate exception handlers
|
||||
exception_handler!(divide_error_handler, 0, no_error_code);
|
||||
exception_handler!(debug_handler, 1, no_error_code);
|
||||
exception_handler!(nmi_handler, 2, no_error_code);
|
||||
exception_handler!(breakpoint_handler, 3, no_error_code);
|
||||
exception_handler!(overflow_handler, 4, no_error_code);
|
||||
exception_handler!(bound_range_handler, 5, no_error_code);
|
||||
exception_handler!(invalid_opcode_handler, 6, no_error_code);
|
||||
exception_handler!(device_not_available_handler, 7, no_error_code);
|
||||
exception_handler!(double_fault_handler, 8, error_code);
|
||||
exception_handler!(invalid_tss_handler, 10, error_code);
|
||||
exception_handler!(segment_not_present_handler, 11, error_code);
|
||||
exception_handler!(stack_segment_handler, 12, error_code);
|
||||
exception_handler!(general_protection_handler, 13, error_code);
|
||||
exception_handler!(page_fault_handler, 14, error_code);
|
||||
exception_handler!(x87_fp_handler, 16, no_error_code);
|
||||
exception_handler!(alignment_check_handler, 17, error_code);
|
||||
exception_handler!(machine_check_handler, 18, no_error_code);
|
||||
exception_handler!(simd_fp_handler, 19, no_error_code);
|
||||
@@ -1,5 +1,8 @@
|
||||
//! x86_64 architecture support
|
||||
|
||||
pub mod interrupts;
|
||||
pub mod gdt;
|
||||
|
||||
/// Halt the CPU until the next interrupt
|
||||
#[inline]
|
||||
pub fn hlt() {
|
||||
|
||||
90
src/lib.rs
90
src/lib.rs
@@ -9,6 +9,72 @@
|
||||
#[cfg(test)]
|
||||
extern crate std;
|
||||
|
||||
// Compiler-required memory intrinsics for no_std environments
|
||||
#[cfg(not(test))]
|
||||
mod intrinsics {
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 {
|
||||
let mut i = 0;
|
||||
while i < n {
|
||||
unsafe {
|
||||
*dest.add(i) = *src.add(i);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
dest
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 {
|
||||
if src < dest as *const u8 {
|
||||
// Copy backwards to handle overlapping regions
|
||||
let mut i = n;
|
||||
while i > 0 {
|
||||
i -= 1;
|
||||
unsafe {
|
||||
*dest.add(i) = *src.add(i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Copy forwards
|
||||
let mut i = 0;
|
||||
while i < n {
|
||||
unsafe {
|
||||
*dest.add(i) = *src.add(i);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
dest
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn memset(dest: *mut u8, c: i32, n: usize) -> *mut u8 {
|
||||
let mut i = 0;
|
||||
while i < n {
|
||||
unsafe {
|
||||
*dest.add(i) = c as u8;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
dest
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 {
|
||||
let mut i = 0;
|
||||
while i < n {
|
||||
let a = unsafe { *s1.add(i) };
|
||||
let b = unsafe { *s2.add(i) };
|
||||
if a != b {
|
||||
return a as i32 - b as i32;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
// Re-export alloc for heap allocations (available after boot services)
|
||||
#[cfg(any(feature = "uefi", test))]
|
||||
extern crate alloc;
|
||||
@@ -80,6 +146,11 @@ pub fn kernel_init(info: &BootInfo) -> ! {
|
||||
free_mem / (1024 * 1024),
|
||||
total_mem / (1024 * 1024)).ok();
|
||||
|
||||
// Initialize interrupt handling
|
||||
writeln!(serial, "").ok();
|
||||
writeln!(serial, ">>> Initializing interrupt handling...").ok();
|
||||
arch::x86_64::interrupts::init();
|
||||
|
||||
// Test allocating a few frames
|
||||
writeln!(serial, "").ok();
|
||||
writeln!(serial, ">>> Testing frame allocator...").ok();
|
||||
@@ -193,6 +264,25 @@ pub fn kernel_init(info: &BootInfo) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
// Reload GDT to higher-half address before removing identity mapping
|
||||
writeln!(serial, "").ok();
|
||||
writeln!(serial, ">>> Reloading GDT to higher-half...").ok();
|
||||
arch::x86_64::gdt::reload();
|
||||
writeln!(serial, " GDT reloaded").ok();
|
||||
|
||||
// Remove identity mapping - no longer needed now that we're in higher-half
|
||||
writeln!(serial, "").ok();
|
||||
writeln!(serial, ">>> Removing identity mapping...").ok();
|
||||
memory::paging::remove_identity_mapping();
|
||||
|
||||
// Verify PML4[0] is now empty
|
||||
let pml4_0 = memory::paging::read_pml4(0);
|
||||
if pml4_0.is_present() {
|
||||
writeln!(serial, " WARNING: PML4[0] still present!").ok();
|
||||
} else {
|
||||
writeln!(serial, " Identity mapping removed (PML4[0] cleared)").ok();
|
||||
}
|
||||
|
||||
writeln!(serial, "").ok();
|
||||
writeln!(serial, "Kernel initialization complete.").ok();
|
||||
writeln!(serial, "Halting CPU.").ok();
|
||||
|
||||
@@ -427,6 +427,18 @@ pub fn flush_tlb() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove the identity mapping at PML4[0]
|
||||
///
|
||||
/// This should be called after the kernel is fully running in the higher-half
|
||||
/// and no longer needs the identity mapping set up during boot.
|
||||
pub fn remove_identity_mapping() {
|
||||
// Clear PML4[0]
|
||||
write_pml4(0, PageTableEntry::empty());
|
||||
|
||||
// Flush TLB to ensure the change takes effect
|
||||
flush_tlb();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Page Table Creation and Mapping
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user