From 9777e9d3ea5bf74762d2173a5bf648adcee683f0 Mon Sep 17 00:00:00 2001 From: August Date: Tue, 30 Jun 2026 20:33:36 +0000 Subject: [PATCH] Look for local APIC, try to map it --- kernel/src/arch/x86_64/gdt.rs | 2 +- kernel/src/arch/x86_64/interrupts.rs | 30 +++++++++-- kernel/src/interrupt/mod.rs | 11 ++-- kernel/src/main.rs | 21 ++++++-- kernel/src/memory/mod.rs | 10 +--- kernel/src/memory/paging.rs | 77 ++++++++++++++++++++++++++-- kernel/src/process.rs | 2 +- 7 files changed, 129 insertions(+), 24 deletions(-) diff --git a/kernel/src/arch/x86_64/gdt.rs b/kernel/src/arch/x86_64/gdt.rs index dd2a0b9..413b509 100644 --- a/kernel/src/arch/x86_64/gdt.rs +++ b/kernel/src/arch/x86_64/gdt.rs @@ -9,8 +9,8 @@ use x86_64::structures::gdt; use x86_64::structures::gdt::*; use crate::log::*; -use crate::{log_info, log_trace}; use crate::memory::alloc::format; +use crate::{log_info, log_trace}; #[allow(dead_code)] pub trait GdtEntryRead { diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs index a7bf604..643ea91 100644 --- a/kernel/src/arch/x86_64/interrupts.rs +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -1,11 +1,19 @@ // Copyright (c) 2025 shibedrill // SPDX-License-Identifier: MIT -use crate::LogLevel; -use crate::{LOGGER, format, log_trace}; +use crate::{LOGGER, format, log_info, log_trace}; +use crate::{LogLevel, log_error}; use lazy_static::lazy_static; +use x86_64::PhysAddr; use x86_64::structures::idt::*; +pub fn get_lapic_addr() -> PhysAddr { + let apic_base = x86_64::registers::model_specific::ApicBase::read(); + let addr: PhysAddr = apic_base.0.start_address(); + log_info!("Found LAPIC at 0x{:x}, flags {:?}", addr, apic_base.1); + addr +} + lazy_static! { pub static ref IDT: InterruptDescriptorTable = { let mut idt = InterruptDescriptorTable::new(); @@ -14,6 +22,7 @@ lazy_static! { // approach will fall apart once multiarch support is re-added. idt.double_fault.set_handler_fn(double_fault); idt.page_fault.set_handler_fn(page_fault); + idt.general_protection_fault.set_handler_fn(gen_prot_fault); idt }; } @@ -37,7 +46,7 @@ extern "x86-interrupt" fn page_fault(info: InterruptStackFrame, errcode: PageFau ); if errcode.contains(PageFaultErrorCode::USER_MODE) { // Fault occurred in usermode. Non fatal. - todo!() + todo!("Page fault handler (usermode)") } else { // Fault occurred in kernel mode. This is possibly fatal. // This is recoverable if we simply hit an unavailable page, @@ -46,9 +55,20 @@ extern "x86-interrupt" fn page_fault(info: InterruptStackFrame, errcode: PageFau | errcode.contains(PageFaultErrorCode::MALFORMED_TABLE) { let info_formatted = format!("{info:#?}"); - crate::interrupt::page_fault(addr.unwrap().as_u64() as usize, info_formatted) + crate::interrupt::page_fault_fatal(addr.unwrap().as_u64() as usize, info_formatted) } else { - todo!() + log_error!("Page Fault, trying to recover..."); + todo!("Page fault handler (kernel mode)") } } } + +#[allow(dead_code)] +extern "x86-interrupt" fn gen_prot_fault(info: InterruptStackFrame, _: u64) { + crate::interrupt::gen_prot_fault(format!( + "RIP 0x{:x}, CS {} ({:?})", + info.instruction_pointer, + info.code_segment.index(), + info.code_segment.rpl() + )); +} diff --git a/kernel/src/interrupt/mod.rs b/kernel/src/interrupt/mod.rs index 8b2aa50..7581a18 100644 --- a/kernel/src/interrupt/mod.rs +++ b/kernel/src/interrupt/mod.rs @@ -1,12 +1,17 @@ // Copyright (c) 2025 shibedrill // SPDX-License-Identifier: MIT -use crate::memory::alloc::string::String; +use crate::log::*; +use crate::{format, log_error, memory::alloc::string::String}; pub fn double_fault(info: String) -> ! { panic!("Double fault: {}", info); } -pub fn page_fault(addr: usize, info: String) -> ! { - panic!("Page fault at 0x{:x}: {}", addr, info); +pub fn page_fault_fatal(addr: usize, info: String) -> ! { + panic!("Fatal page fault at 0x{:x}: {}", addr, info); +} + +pub fn gen_prot_fault(info: String) { + log_error!("General protection fault: {}", info); } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 61a368d..82e1419 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -31,6 +31,7 @@ use limine::firmware::{ FIRMWARE_TYPE_EFI32, FIRMWARE_TYPE_EFI64, FIRMWARE_TYPE_SBI, FIRMWARE_TYPE_X86BIOS, }; use spin::mutex::Mutex; +use x86_64::structures::paging::{PageTableFlags, page_table::PageTableEntry}; use crate::{ arch::{ @@ -38,9 +39,10 @@ use crate::{ x86_64::{ cpuid::{CPUID, virt_supported}, gdt::dump_gdt, + interrupts::get_lapic_addr, }, }, - memory::paging::{self, active_root_table}, + memory::paging::{self, active_root_table, map_page, phys_to_virt}, }; lazy_static! { @@ -147,7 +149,7 @@ unsafe extern "C" fn main() -> ! { None => log_info!("Multiprocessing response not received"), Some(resp) => { log_info!("Multiprocessing response received"); - log_trace!("{} CPUs found", resp.cpus().len()); + log_info!("{} CPUs found", resp.cpus().len()); } } @@ -183,11 +185,24 @@ unsafe extern "C" fn main() -> ! { root_ptable_info.1, addr_of!(*root_ptable_info.0) as usize ); - paging::walk_tables(); + // This is an expensive function. Only run it if we're actually logging the results. + let loglevel = LOGGER.lock().level; + if loglevel > LogLevel::Trace { + paging::walk_tables(); + } } log_info!("Long mode: {}", asm::long_mode()); dump_gdt(); + let lapic = get_lapic_addr(); + let mapping = { + let mut m = PageTableEntry::new(); + m.set_addr(lapic, PageTableFlags::PRESENT); + m + }; + let res = map_page(phys_to_virt(lapic), mapping); + log_info!("Map status: {:?}", res); + panic!("Finished boot, but cannot start init because processes not implemented!"); } diff --git a/kernel/src/memory/mod.rs b/kernel/src/memory/mod.rs index abe7af0..d87c32a 100644 --- a/kernel/src/memory/mod.rs +++ b/kernel/src/memory/mod.rs @@ -90,16 +90,10 @@ pub fn log_address() { if let Some(resp) = ADDRESS_REQUEST.response() { log_info!("Kernel physical start address: 0x{:x}", resp.physical_base); log_info!("Kernel virtual start address: 0x{:x}", resp.virtual_base); - log_info!( - "Testing virt_to_phys(): 0x{:x}", - resp.virtual_base - ); + log_info!("Testing virt_to_phys(): 0x{:x}", resp.virtual_base); let hhdm_vaddr = VirtAddr::new(resp.virtual_base); let hhdm_paddr = virt_to_phys(hhdm_vaddr); - log_info!( - "Result: 0x{:x}", - hhdm_paddr.unwrap() - ); + log_info!("Result: 0x{:x}", hhdm_paddr.unwrap()); } else { log_warning!("No kernel address response provided."); } diff --git a/kernel/src/memory/paging.rs b/kernel/src/memory/paging.rs index 06f110d..0e723e2 100644 --- a/kernel/src/memory/paging.rs +++ b/kernel/src/memory/paging.rs @@ -1,3 +1,4 @@ +use core::ops::DerefMut; use core::ops::Index; use core::ptr::addr_of; @@ -8,17 +9,83 @@ use x86_64::registers::control; use x86_64::registers::control::Cr4Flags; use x86_64::structures::paging::PageTable; use x86_64::structures::paging::PageTableFlags; +use x86_64::structures::paging::page_table::PageTableEntry; use crate::log::*; use crate::memory::HHDM_RESPONSE; use crate::memory::format; #[allow(dead_code)] -pub fn map_page(_virt: VirtAddr, _phys: PhysAddr) { +pub fn map_page(virt: VirtAddr, mapping: PageTableEntry) -> Option { // TODO: // This function should create necessary page tables until the // supplied virtual address maps to the supplied physical address. - todo!() + // Currently, it *will* give up if there are any missing tables on the path. + // It needs to be altered to support creating new intermediary tables. + // This will likely not be a necessary function in the future if VMM is left to processes. + log_info!( + "Mapping 0x{:x} to 0x{:x} with flags {:?}", + virt, + mapping.addr(), + mapping.flags() + ); + let root_table = unsafe { active_root_table() }; + let mut current_table = root_table; + let table_indices = [ + virt.as_u64().bits(0..=11) as usize, // Page offset + virt.as_u64().bits(12..=20) as usize, // Page Table index + virt.as_u64().bits(21..=29) as usize, // Page Directory index + virt.as_u64().bits(30..=38) as usize, // Page Directory Pointer Table index + virt.as_u64().bits(39..=47) as usize, // Page Map Level 4 index + virt.as_u64().bits(48..=56) as usize, // Page Map Level 5 index + ]; + while current_table.1 > 0 { + log_trace!( + "Searching through a level {} table at 0x{:x}", + current_table.1, + addr_of!(*current_table.0) as usize + ); + log_trace!("Index: {}", table_indices[current_table.1]); + let current_table_entry: &mut PageTableEntry = + &mut current_table.0.deref_mut()[table_indices[current_table.1]]; + if current_table_entry + .flags() + .contains(PageTableFlags::PRESENT) + && !current_table_entry.is_unused() + { + if current_table_entry + .flags() + .contains(PageTableFlags::HUGE_PAGE) + { + if current_table.1 == 3 { + // Huge page (1GiB), last 30 bits preserved + log_error!("Could not map: Entry on path is Huge Page!"); + return None; + } else if current_table.1 == 2 { + // Large page (2MiB), last 21 bits preserved + log_error!("Could not map: Entry on path is Large Page!"); + return None; + } + } else if current_table.1 > 1 { + current_table = ( + unsafe { &mut *phys_to_virt(current_table_entry.addr()).as_mut_ptr() } + as &mut PageTable, + current_table.1 - 1, + ); + } else { + current_table_entry.set_addr(mapping.addr(), mapping.flags()); + return Some(virt); + } + } else { + log_error!( + "Could not map: Level {} entry on path is not present!", + current_table.1 + ); + return None; + } + } + // Current table is level 1 + unreachable!() } #[allow(dead_code)] @@ -89,7 +156,11 @@ pub fn virt_to_phys(virt: VirtAddr) -> Option { virt.as_u64().bits(48..=56) as usize, // Page Map Level 5 index ]; while current_table.1 > 0 { - log_trace!("Searching through a level {} table at 0x{:x}", current_table.1, addr_of!(*current_table.0) as usize); + log_trace!( + "Searching through a level {} table at 0x{:x}", + current_table.1, + addr_of!(*current_table.0) as usize + ); log_trace!("Index: {}", table_indices[current_table.1]); let current_table_entry = current_table.0.index(table_indices[current_table.1]); if current_table_entry diff --git a/kernel/src/process.rs b/kernel/src/process.rs index 3e2972c..2689381 100644 --- a/kernel/src/process.rs +++ b/kernel/src/process.rs @@ -33,7 +33,7 @@ pub struct Process { #[allow(dead_code)] pub fn context_switch() -> ! { - todo!() + todo!("Context switch") } #[allow(dead_code)]