add IDT and GDT initialization
This commit is contained in:
@@ -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]
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user