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; 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");
}
}
+80 -4
View File
@@ -1,5 +1,7 @@
use limine::request::FramebufferRequest; use ::limine as limine_api;
use limine::{BaseRevision, RequestsEndMarker, RequestsStartMarker};
use limine_api::request::{ExecutableAddressRequest, HhdmRequest, MemmapRequest};
use limine_api::{BaseRevision, RequestsEndMarker, RequestsStartMarker};
/// Sets the base revision to the latest revision supported by the crate. /// Sets the base revision to the latest revision supported by the crate.
/// See specification for further info. /// See specification for further info.
@@ -7,11 +9,19 @@ use limine::{BaseRevision, RequestsEndMarker, RequestsStartMarker};
#[used] #[used]
// The .requests section allows limine to find the requests faster and more safely. // The .requests section allows limine to find the requests faster and more safely.
#[unsafe(link_section = ".requests")] #[unsafe(link_section = ".requests")]
pub static BASE_REVISION: BaseRevision = BaseRevision::new(); static BASE_REVISION: BaseRevision = BaseRevision::new();
#[used] #[used]
#[unsafe(link_section = ".requests")] #[unsafe(link_section = ".requests")]
pub static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new(); static KERNEL_ADDRESS_REQUEST: ExecutableAddressRequest = ExecutableAddressRequest::new();
#[used]
#[unsafe(link_section = ".requests")]
static HHDM_REQUEST: HhdmRequest = HhdmRequest::new();
#[used]
#[unsafe(link_section = ".requests")]
static MEMMAP_REQUEST: MemmapRequest = MemmapRequest::new();
/// Define the stand and end markers for Limine requests. /// Define the stand and end markers for Limine requests.
#[used] #[used]
@@ -20,3 +30,69 @@ static _START_MARKER: RequestsStartMarker = RequestsStartMarker::new();
#[used] #[used]
#[unsafe(link_section = ".requests_end_marker")] #[unsafe(link_section = ".requests_end_marker")]
static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new(); static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new();
pub struct BootInfo {
pub kernel_address: crate::memory::PhysicalAddr,
pub hhdm_offset: u64,
entries: &'static [&'static limine_api::memmap::Entry],
}
impl BootInfo {
pub fn memory_regions(&self) -> impl Iterator<Item = crate::memory::MemoryRegion> + '_ {
use crate::memory::{MemoryRegion, MemoryRegionKind};
self.entries.iter().map(|&entry| MemoryRegion {
start: crate::memory::PhysicalAddr::new(entry.base),
length: entry.length,
kind: match entry.type_ {
limine_api::memmap::MEMMAP_USABLE => MemoryRegionKind::Usable,
limine_api::memmap::MEMMAP_RESERVED => MemoryRegionKind::Reserved,
limine_api::memmap::MEMMAP_ACPI_RECLAIMABLE => MemoryRegionKind::AcpiReclaimable,
limine_api::memmap::MEMMAP_ACPI_NVS => MemoryRegionKind::AcpiNvs,
limine_api::memmap::MEMMAP_BAD_MEMORY => MemoryRegionKind::BadMemory,
limine_api::memmap::MEMMAP_BOOTLOADER_RECLAIMABLE => {
MemoryRegionKind::BootloaderReclaimable
}
limine_api::memmap::MEMMAP_EXECUTABLE_AND_MODULES => {
MemoryRegionKind::KernelAndModules
}
limine_api::memmap::MEMMAP_FRAMEBUFFER => MemoryRegionKind::Framebuffer,
limine_api::memmap::MEMMAP_MAPPED_RESERVED => MemoryRegionKind::MappedReserved,
_ => MemoryRegionKind::Reserved,
},
})
}
}
#[derive(Debug)]
pub enum BootError {
UnsupportedBaseRevision,
FailedToGetKernelAddress,
FailedToGetHHDMAddress,
FailedToGetMemmap,
}
pub fn load_boot_info() -> Result<BootInfo, BootError> {
if !BASE_REVISION.is_supported() {
return Err(BootError::UnsupportedBaseRevision);
}
let kernel_address = KERNEL_ADDRESS_REQUEST
.response()
.ok_or(BootError::FailedToGetKernelAddress)?
.physical_base;
let hhdm_offset = HHDM_REQUEST
.response()
.ok_or(BootError::FailedToGetHHDMAddress)?
.offset;
let memmap = MEMMAP_REQUEST
.response()
.ok_or(BootError::FailedToGetMemmap)?
.entries();
Ok(BootInfo {
kernel_address: crate::memory::PhysicalAddr::new(kernel_address),
hhdm_offset,
entries: memmap,
})
}
+3 -1
View File
@@ -1 +1,3 @@
pub mod limine; mod limine;
pub use limine::{BootError, BootInfo, load_boot_info};
@@ -169,11 +169,11 @@ pub fn print(args: core::fmt::Arguments) {
#[macro_export] #[macro_export]
macro_rules! print { macro_rules! print {
($($arg:tt)*) => ($crate::drivers::serial::print(format_args!($($arg)*))); ($($arg:tt)*) => ($crate::debug::serial::print(format_args!($($arg)*)));
} }
#[macro_export] #[macro_export]
macro_rules! println { macro_rules! println {
() => ($crate::drivers::serial::print(format_args!("\n"))); () => ($crate::debug::serial::print(format_args!("\n")));
($($arg:tt)*) => ($crate::drivers::serial::print(format_args!("{}\n", format_args!($($arg)*)))); ($($arg:tt)*) => ($crate::debug::serial::print(format_args!("{}\n", format_args!($($arg)*))));
} }
+15 -41
View File
@@ -1,66 +1,40 @@
#![feature(abi_x86_interrupt, negative_impls)] #![feature(abi_x86_interrupt)]
#![allow(clippy::needless_return)] #![allow(clippy::needless_return)]
#![no_std] #![no_std]
#![no_main] #![no_main]
mod arch; mod arch;
mod boot; mod boot;
mod drivers; mod debug;
mod memory;
use boot::limine::{BASE_REVISION, FRAMEBUFFER_REQUEST}; use crate::debug::serial;
use crate::drivers::serial;
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn _start() -> ! { pub extern "C" fn _start() -> ! {
serial::init().unwrap(); serial::init().unwrap();
assert!(BASE_REVISION.is_supported()); arch::init();
let _boot_info = boot::load_boot_info().unwrap();
draw_gradient(); let addr = 0xDEADBEEF as *mut u32;
unsafe {
*addr = 0xDEADBEEF;
}
hcf(); hcf();
} }
fn draw_gradient() {
if let Some(framebuffer_response) = FRAMEBUFFER_REQUEST.response() {
if let Some(&framebuffer) = framebuffer_response.framebuffers().first() {
let buffer = unsafe {
core::slice::from_raw_parts_mut(
framebuffer.address().cast::<u32>(),
framebuffer.size() / 4,
)
};
for y in 0..framebuffer.height {
for x in 0..framebuffer.width {
let r = (255 * x) / (framebuffer.width - 1);
let g = (255 * y) / (framebuffer.height - 1);
let b = 255 - r;
let pixel = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
buffer
[(((y * framebuffer.pitch) / (framebuffer.bpp as u64 / 8)) + x) as usize] =
pixel
}
}
}
}
}
#[panic_handler] #[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! { fn panic(info: &core::panic::PanicInfo) -> ! {
println!("Uh oh, something went wrong!");
println!("{}", info);
hcf(); hcf();
} }
pub fn hcf() -> ! { pub fn hcf() -> ! {
loop { loop {
unsafe { arch::halt();
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
core::arch::asm!("hlt");
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
core::arch::asm!("wfi");
}
} }
} }
+51
View File
@@ -0,0 +1,51 @@
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PhysicalAddr(u64);
impl PhysicalAddr {
pub fn new(addr: u64) -> Self {
Self(addr)
}
pub const fn as_u64(self) -> u64 {
self.0
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct VirtualAddr(u64);
impl VirtualAddr {
pub fn new(addr: u64) -> Self {
Self(addr)
}
pub const fn as_u64(self) -> u64 {
self.0
}
pub unsafe fn as_mut_ptr<T>(self) -> *mut T {
self.as_u64() as *mut T
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MemoryRegionKind {
Usable,
Reserved,
AcpiReclaimable,
AcpiNvs,
BadMemory,
BootloaderReclaimable,
KernelAndModules,
Framebuffer,
MappedReserved,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MemoryRegion {
pub start: PhysicalAddr,
pub length: u64,
pub kind: MemoryRegionKind,
}