mirror of
https://github.com/xomboverlord/xomb.git
synced 2026-01-11 18:33:15 +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
|
//! x86_64 architecture support
|
||||||
|
|
||||||
|
pub mod interrupts;
|
||||||
|
pub mod gdt;
|
||||||
|
|
||||||
/// Halt the CPU until the next interrupt
|
/// Halt the CPU until the next interrupt
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hlt() {
|
pub fn hlt() {
|
||||||
|
|||||||
90
src/lib.rs
90
src/lib.rs
@@ -9,6 +9,72 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate std;
|
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)
|
// Re-export alloc for heap allocations (available after boot services)
|
||||||
#[cfg(any(feature = "uefi", test))]
|
#[cfg(any(feature = "uefi", test))]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
@@ -80,6 +146,11 @@ pub fn kernel_init(info: &BootInfo) -> ! {
|
|||||||
free_mem / (1024 * 1024),
|
free_mem / (1024 * 1024),
|
||||||
total_mem / (1024 * 1024)).ok();
|
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
|
// Test allocating a few frames
|
||||||
writeln!(serial, "").ok();
|
writeln!(serial, "").ok();
|
||||||
writeln!(serial, ">>> Testing frame allocator...").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, "").ok();
|
||||||
writeln!(serial, "Kernel initialization complete.").ok();
|
writeln!(serial, "Kernel initialization complete.").ok();
|
||||||
writeln!(serial, "Halting CPU.").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
|
// Page Table Creation and Mapping
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user