Compare commits

..

4 commits

12 changed files with 346 additions and 78 deletions

View file

@ -1,3 +1,4 @@
use core::fmt::Debug;
use core::marker::PhantomData; use core::marker::PhantomData;
use core::mem; use core::mem;
use core::ops::Not; use core::ops::Not;
@ -657,6 +658,15 @@ pub struct RBTree<N: UnsafeNode> {
root: Option<NonNull<N>>, root: Option<NonNull<N>>,
} }
impl<N: UnsafeNode> Debug for RBTree<N>
where
N::Key: Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_set().entries(self.iter()).finish()
}
}
impl<N: UnsafeNode> RBTree<N> { impl<N: UnsafeNode> RBTree<N> {
pub fn new() -> Self { pub fn new() -> Self {
Self { root: None } Self { root: None }
@ -1153,7 +1163,9 @@ impl<'a, N: UnsafeNode + 'a> Iterator for TreeIter<'a, N> {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.range self.range
.next() .next()
.map(|n| unsafe { n.node().unwrap().as_ref().key() }) .as_ref()
.and_then(Handle::node)
.map(|n| unsafe { n.as_ref().key() })
} }
} }
@ -1161,7 +1173,9 @@ impl<'a, N: UnsafeNode + 'a> DoubleEndedIterator for TreeIter<'a, N> {
fn next_back(&mut self) -> Option<Self::Item> { fn next_back(&mut self) -> Option<Self::Item> {
self.range self.range
.next_back() .next_back()
.map(|n| unsafe { n.node().unwrap().as_ref().key() }) .as_ref()
.and_then(Handle::node)
.map(|n| unsafe { n.as_ref().key() })
} }
} }

View file

@ -20,6 +20,7 @@ harness = false
[[test]] [[test]]
name = "stack_overflow" name = "stack_overflow"
harness = false harness = false
test = false # currently doesn't work reliably because the stack corrupts something
[dependencies] [dependencies]
bit_field = "0.10.3" bit_field = "0.10.3"

View file

@ -100,12 +100,19 @@ const impl Default for MemoryRegion {
pub struct BootInfo { pub struct BootInfo {
pub hhdm_base: u64, pub hhdm_base: u64,
pub memory_map: &'static [MemoryRegion], pub memory_map: &'static [MemoryRegion],
pub executable_file: Option<&'static [u8]>,
} }
pub static BOOT_INFO: OnceLock<BootInfo> = OnceLock::new(); pub static BOOT_INFO: OnceLock<BootInfo> = OnceLock::new();
static mut MEMORY_MAP: [MemoryRegion; 128] = [MemoryRegion::default(); 128]; static mut MEMORY_MAP: [MemoryRegion; 128] = [MemoryRegion::default(); 128];
pub fn init_boot_info<I: Iterator<Item = MemoryRegion>>(hhdm_base: u64, memory_map: I) { pub fn init_boot_info<I: Iterator<Item = MemoryRegion>>(
hhdm_base: u64,
memory_map: I,
executable_file: Option<&'static [u8]>,
) {
_ = crate::memory::HHDM_BASE.try_insert(hhdm_base);
BOOT_INFO.initialize(|| { BOOT_INFO.initialize(|| {
for (i, region) in memory_map.enumerate() { for (i, region) in memory_map.enumerate() {
assert!(i < 128, "Memory map has more than 128 entries"); assert!(i < 128, "Memory map has more than 128 entries");
@ -117,6 +124,7 @@ pub fn init_boot_info<I: Iterator<Item = MemoryRegion>>(hhdm_base: u64, memory_m
Ok::<_, !>(BootInfo { Ok::<_, !>(BootInfo {
hhdm_base, hhdm_base,
memory_map: unsafe { (&raw const MEMORY_MAP).as_ref_unchecked() }, memory_map: unsafe { (&raw const MEMORY_MAP).as_ref_unchecked() },
executable_file,
}) })
}); });
} }

View file

@ -1,4 +1,6 @@
use core::{cell::UnsafeCell, ffi::CStr, fmt::Debug, ptr::NonNull}; use core::{cell::UnsafeCell, fmt::Debug, ptr::NonNull};
use crate::serial_println;
#[repr(C)] #[repr(C)]
pub struct BaseRevision(UnsafeCell<[u64; 3]>); pub struct BaseRevision(UnsafeCell<[u64; 3]>);
@ -326,6 +328,46 @@ impl LimineFile {
} }
} }
#[used]
#[unsafe(link_section = ".limine_requests_start")]
static LIMINE_REQUESTS_START: RequestsStartMarker = REQUESTS_START_MARKER;
#[used] #[used]
#[unsafe(link_section = ".limine_requests")] #[unsafe(link_section = ".limine_requests")]
pub static EXECUTABLE_FILE_REQUEST: ExecutableFileRequest = ExecutableFileRequest::new(); pub static EXECUTABLE_FILE_REQUEST: ExecutableFileRequest = ExecutableFileRequest::new();
#[used]
#[unsafe(link_section = ".limine_requests")]
pub static LIMINE_BASE_REVISION: BaseRevision = BaseRevision::from_revision(6);
#[used]
#[unsafe(link_section = ".limine_requests")]
pub static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new();
#[used]
#[unsafe(link_section = ".limine_requests")]
pub static HHDM_REQUEST: HhdmRequest = HhdmRequest::new();
#[used]
#[unsafe(link_section = ".limine_requests")]
pub static MEMMAP_REQUEST: MemMapRequest = MemMapRequest::new();
#[used]
#[unsafe(link_section = ".limine_requests_end")]
static LIMINE_REQUESTS_END: RequestsEndMarker = REQUESTS_END_MARKER;
pub fn init_limine_boot_info() {
crate::boot::init_boot_info(
HHDM_REQUEST
.offset()
.expect("HHDM offset not provided by bootloader"),
MEMMAP_REQUEST
.entries()
.iter()
.inspect(|e| {
serial_println!("{e:?}");
})
.map(|&&e| e.into()),
EXECUTABLE_FILE_REQUEST.file().map(|f| f.bytes()),
);
}

View file

@ -23,25 +23,9 @@ static GDT: LazyLock<GlobalDescriptorTable> = LazyLock::new(GlobalDescriptorTabl
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn _start() -> ! { extern "C" fn _start() -> ! {
kernel::logger::init(log::LevelFilter::Trace); kernel::logger::init(log::LevelFilter::Trace);
assert!(limine_requests::LIMINE_BASE_REVISION.is_supported()); assert!(kernel::limine::LIMINE_BASE_REVISION.is_supported());
_ = kernel::memory::HHDM_BASE kernel::limine::init_limine_boot_info();
.try_insert(
limine_requests::HHDM_REQUEST
.offset()
.expect("HHDM offset not provided by bootloader"),
)
.expect("HHDM offset already set");
kernel::boot::init_boot_info(
limine_requests::HHDM_REQUEST
.offset()
.expect("HHDM offset not provided by bootloader"),
limine_requests::MEMMAP_REQUEST
.entries()
.iter()
.map(|&&e| e.into()),
);
kernel::serial_println!( kernel::serial_println!(
"HHDM offset: 0x{:#x}", "HHDM offset: 0x{:#x}",
@ -50,20 +34,19 @@ extern "C" fn _start() -> ! {
kernel::serial_println!( kernel::serial_println!(
"Memory map: {:#?}", "Memory map: {:#?}",
limine_requests::MEMMAP_REQUEST.entries() kernel::limine::MEMMAP_REQUEST.entries()
); );
GDT.load(); GDT.load();
IDT.load(); IDT.load();
kernel::serial_println!("entry point: 0x{:x}", _start as *const () as usize); kernel::serial_println!("entry point: 0x{:x}", _start as *const () as usize);
kernel::serial_println!( kernel::serial_println!(
"entry point phy: {:?}", "entry point phy: {:?}",
kernel::x86_64::paging::get_physical_addr(VirtAddr(_start as *const () as u64)) kernel::x86_64::paging::get_physical_addr(VirtAddr(_start as *const () as u64))
); );
let pmm = kernel::memory::PhysicalMemoryAllocator::from_memory_map( let pmm = kernel::memory::PhysicalMemoryManager::from_memory_map(
kernel::boot::BOOT_INFO kernel::boot::BOOT_INFO
.get() .get()
.expect("Boot info not initialized") .expect("Boot info not initialized")
@ -75,7 +58,7 @@ extern "C" fn _start() -> ! {
kernel::serial_println!("PMM: {pmm:#?}"); kernel::serial_println!("PMM: {pmm:#?}");
let fb = limine_requests::FRAMEBUFFER_REQUEST let fb = kernel::limine::FRAMEBUFFER_REQUEST
.framebuffers() .framebuffers()
.first() .first()
.expect("No framebuffer found"); .expect("No framebuffer found");
@ -95,34 +78,3 @@ extern "C" fn _start() -> ! {
kernel::x86_64::halt_loop() kernel::x86_64::halt_loop()
} }
mod limine_requests {
use kernel::limine::{
BaseRevision, FramebufferRequest, REQUESTS_END_MARKER, REQUESTS_START_MARKER,
RequestsEndMarker, RequestsStartMarker,
};
#[used]
#[unsafe(link_section = ".limine_requests_start")]
static LIMINE_REQUESTS_START: RequestsStartMarker = REQUESTS_START_MARKER;
#[used]
#[unsafe(link_section = ".limine_requests")]
pub static LIMINE_BASE_REVISION: BaseRevision = BaseRevision::from_revision(6);
#[used]
#[unsafe(link_section = ".limine_requests")]
pub static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new();
#[used]
#[unsafe(link_section = ".limine_requests")]
pub static HHDM_REQUEST: kernel::limine::HhdmRequest = kernel::limine::HhdmRequest::new();
#[used]
#[unsafe(link_section = ".limine_requests")]
pub static MEMMAP_REQUEST: kernel::limine::MemMapRequest = kernel::limine::MemMapRequest::new();
#[used]
#[unsafe(link_section = ".limine_requests_end")]
static LIMINE_REQUESTS_END: RequestsEndMarker = REQUESTS_END_MARKER;
}

View file

@ -1,4 +1,10 @@
use core::{alloc::Allocator, cell::Cell, fmt::Debug, num::NonZeroUsize, ptr::NonNull}; use core::{
alloc::Allocator,
cell::Cell,
fmt::Debug,
num::{NonZero, NonZeroUsize},
ptr::NonNull,
};
use bit_field::BitField; use bit_field::BitField;
use rbtree::RBTree; use rbtree::RBTree;
@ -9,6 +15,8 @@ pub static HHDM_BASE: OnceLock<u64> = OnceLock::new();
pub trait VirtAddrTranslationExt: Sized { pub trait VirtAddrTranslationExt: Sized {
fn into_phy_addr(self) -> Option<PhyAddr>; fn into_phy_addr(self) -> Option<PhyAddr>;
#[expect(clippy::wrong_self_convention)]
fn is_mapped(self) -> bool { fn is_mapped(self) -> bool {
self.into_phy_addr().is_some() self.into_phy_addr().is_some()
} }
@ -129,7 +137,118 @@ pub struct PhysicalMemoryManager {
/// ///
/// When a range of pages is freed, we try to locate its buddy and merge /// When a range of pages is freed, we try to locate its buddy and merge
/// them into a larger chunk recursively. /// them into a larger chunk recursively.
buddies: [u64; 40], buddies: [RBTree<PhysicalPageNode>; 40],
}
impl Debug for PhysicalMemoryManager {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PhysicalMemoryManager")
.field_with("buddies", |f| {
f.debug_list()
.entries(self.buddies.iter().enumerate().map(|(i, tree)| {
struct BuddyDbg<'a> {
index: usize,
tree: &'a RBTree<PhysicalPageNode>,
}
impl Debug for BuddyDbg<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#x}: {:?}", 1u64 << self.index, self.tree)
}
}
BuddyDbg { index: i, tree }
}))
.finish()
})
.finish()
}
}
impl PhysicalMemoryManager {
pub fn from_memory_map(memory_map: &[crate::boot::MemoryRegion]) -> Self {
let usable_regions = memory_map
.iter()
.filter(|region| region.region_type.is_usable());
let buddies = [(); 40].map(|_| RBTree::default());
let mut pmm = PhysicalMemoryManager { buddies };
usable_regions.for_each(|region| {
let idx = region.start / crate::x86_64::PAGE_SIZE as u64;
let count = region.length / crate::x86_64::PAGE_SIZE as u64;
pmm.free_region(idx as usize, count as usize);
});
pmm
}
fn free_region(&mut self, page_index: usize, mut count: usize) {
serial_println!(
"Freeing region: page_index = {:#x}, count = {:#x}",
page_index,
count
);
while let Some(bit) = NonZero::new(count.isolate_lowest_one()).map(NonZero::get) {
count -= bit;
let mut bin = bit.trailing_zeros() as usize;
let mut index = page_index + count;
let mut buddy: NonNull<PhysicalPageNode>;
loop {
// each bin corresponds to a power-of-two number of pages.
// each chunk of pages has a buddy that is the same size (in the same bin)
// for example, the chunk [0, 4) has a buddy [4, 8).
let buddy_index = if index & bit == 0 {
// if the chunk is aligned to the size of the chunk, its buddy
// is located at page_index + bit.
index + bit
} else {
// if the chunk is not aligned to the size of the chunk, its
// buddy is located at page_index - bit.
index - bit
};
serial_println!(
"looking for buddy: page_index = {:#x}, buddy_index = {:#x}, bin = {}",
index,
buddy_index,
bin
);
match self.buddies[bin].remove(PhysicalPageNodeKey::from_page_index(buddy_index)) {
Some(chunk) => {
serial_println!(
"found buddy: page_index = {:#x}, buddy_index = {:#x}, bin = {}",
index,
buddy_index,
bin
);
buddy = chunk;
index = PhysicalPageNode::page_index(buddy).min(index);
bin += 1;
}
None => break,
}
}
let node_ptr = PhysicalPageNode::new_from_page_index(index);
serial_println!(
"inserting node: page_index = {:#x}, size = {:#x}, bin = {}",
index,
bit,
bin
);
let None = self.buddies[bin].insert_node(node_ptr) else {
panic!(
"Attempted to free a region that is already free: page_index = {}, count = {}",
page_index, bit
)
};
}
}
} }
pub struct PhysicalMemoryAllocator { pub struct PhysicalMemoryAllocator {
@ -699,6 +818,27 @@ impl Debug for PhysicalPageNode {
} }
impl PhysicalPageNode { impl PhysicalPageNode {
fn phy(&self) -> Option<PhyAddr> {
PhyAddr::from_hhdm_virt(self)
}
fn page_index(this: NonNull<Self>) -> usize {
let phy = PhyAddr::from_hhdm_virt(VirtAddr::from(this));
phy.expect("PhysicalPageNode is not in HHDM").page_index() as usize
}
fn new_from_page_index(page_index: usize) -> NonNull<Self> {
let phy = PhyAddr::from_page_index(page_index);
let virt = phy.as_hhdm_virt();
let ptr = virt.as_mut::<Self>();
unsafe {
ptr.write(Self::new_red());
}
NonNull::new(ptr).unwrap()
}
fn new_red() -> Self { fn new_red() -> Self {
PhysicalPageNode(Cell::new(1 << 120)) PhysicalPageNode(Cell::new(1 << 120))
} }
@ -757,6 +897,26 @@ impl PhysicalPageNode {
/// the key does not require any additional bits. /// the key does not require any additional bits.
struct PhysicalPageNodeKey; struct PhysicalPageNodeKey;
impl Debug for PhysicalPageNodeKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:?}", self.phy())
}
}
impl PhysicalPageNodeKey {
fn from_page_index(page_index: usize) -> &'static Self {
let phy = PhyAddr::from_page_index(page_index);
let virt = phy.as_hhdm_virt();
let ptr = virt.as_ptr::<Self>();
unsafe { &*ptr }
}
fn phy(&self) -> Option<PhyAddr> {
PhyAddr::from_hhdm_virt(self)
}
}
impl Eq for PhysicalPageNodeKey {} impl Eq for PhysicalPageNodeKey {}
impl PartialEq for PhysicalPageNodeKey { impl PartialEq for PhysicalPageNodeKey {

View file

@ -1,4 +1,5 @@
use core::ffi::{CStr, c_void}; #![allow(dead_code)]
use core::ffi::c_void;
use crate::{ use crate::{
limine::LimineFile, limine::LimineFile,
@ -336,12 +337,10 @@ impl Elf64Sym {
if let Some(bytes) = crate::limine::EXECUTABLE_FILE_REQUEST if let Some(bytes) = crate::limine::EXECUTABLE_FILE_REQUEST
.file() .file()
.map(LimineFile::bytes) .map(LimineFile::bytes)
&& let Ok(ehdr) = plain::from_bytes::<Elf64Ehdr>(bytes)
&& let Some(symtab) = ehdr.symtab(bytes)
{ {
if let Ok(ehdr) = plain::from_bytes::<Elf64Ehdr>(bytes) { return symtab;
if let Some(symtab) = ehdr.symtab(bytes) {
return symtab;
}
}
} }
&[] &[]
}); });
@ -375,6 +374,7 @@ impl Elf64Sym {
_ => panic!("Invalid bind value"), _ => panic!("Invalid bind value"),
} }
} }
fn kind(&self) -> Elf64SymType { fn kind(&self) -> Elf64SymType {
match self.info & 0xf { match self.info & 0xf {
0 => Elf64SymType::Notype, 0 => Elf64SymType::Notype,

View file

@ -1,7 +1,7 @@
use core::{ use core::{
arch::asm, arch::asm,
fmt::Debug, fmt::Debug,
mem::offset_of, mem::{MaybeUninit, offset_of},
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
}; };
@ -468,16 +468,44 @@ impl DerefMut for TssEntry {
} }
} }
const PRIVILEGE_STACK_TABLE_SIZE: usize = 3;
const INTERRUPT_STACK_TABLE_SIZE: usize = 7;
#[repr(C, packed(4))] #[repr(C, packed(4))]
pub struct TaskStateSegment { pub struct TaskStateSegment {
_reserved1: [u8; 4], _reserved1: [u8; 4],
pub privilege_stack_table: [u64; 3], pub privilege_stack_table: [u64; PRIVILEGE_STACK_TABLE_SIZE],
_reserved2: [u8; 8], _reserved2: [u8; 8],
pub interrupt_stack_table: [u64; 7], pub interrupt_stack_table: [u64; INTERRUPT_STACK_TABLE_SIZE],
_reserved3: [u8; 10], _reserved3: [u8; 10],
pub iomap_base: u16, pub iomap_base: u16,
} }
impl Debug for TaskStateSegment {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut dbg = f.debug_struct("TaskStateSegment");
let rsps = self.privilege_stack_table;
let ists = self.interrupt_stack_table;
dbg.field("RSP0", &format_args!("{:#x}", rsps[0]))
.field("RSP1", &format_args!("{:#x}", rsps[1]))
.field("RSP2", &format_args!("{:#x}", rsps[2]));
dbg.field("IST1", &format_args!("{:#x}", ists[0]))
.field("IST2", &format_args!("{:#x}", ists[1]))
.field("IST3", &format_args!("{:#x}", ists[2]))
.field("IST4", &format_args!("{:#x}", ists[3]))
.field("IST5", &format_args!("{:#x}", ists[4]))
.field("IST6", &format_args!("{:#x}", ists[5]))
.field("IST7", &format_args!("{:#x}", ists[6]));
dbg.field("iomap_base", &format_args!("{:#x}", self.iomap_base));
dbg.finish()
}
}
impl TaskStateSegment { impl TaskStateSegment {
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
@ -489,6 +517,13 @@ impl TaskStateSegment {
_reserved3: [0; 10], _reserved3: [0; 10],
} }
} }
pub fn set_stack(&mut self, index: u8, stack: &'static Stack) {
assert!(
(index as usize) < INTERRUPT_STACK_TABLE_SIZE,
"Interrupt stack table index out of bounds"
);
self.interrupt_stack_table[index as usize] = stack.top().to_address();
}
} }
const impl Default for TaskStateSegment { const impl Default for TaskStateSegment {
@ -500,17 +535,39 @@ const impl Default for TaskStateSegment {
pub const DF_STACK: u8 = 0; pub const DF_STACK: u8 = 0;
pub const NMI_STACK: u8 = 1; pub const NMI_STACK: u8 = 1;
pub const MC_STACK: u8 = 2; pub const MC_STACK: u8 = 2;
pub const PF_STACK: u8 = 3;
const STACK_SIZE: usize = super::PAGE_SIZE * 5;
#[repr(align(4096))]
pub struct Stack {
_bytes: MaybeUninit<[u8; STACK_SIZE]>,
}
impl Stack {
const fn new() -> Self {
Stack {
_bytes: MaybeUninit::uninit(),
}
}
fn top(&self) -> *const u8 {
unsafe {
(&raw const *self)
.cast::<u8>()
.byte_add(super::PAGE_SIZE * 5)
}
}
}
pub static TSS: LazyLock<TaskStateSegment> = LazyLock::new(|| { pub static TSS: LazyLock<TaskStateSegment> = LazyLock::new(|| {
let mut tss = TaskStateSegment::new(); let mut tss = TaskStateSegment::new();
const STACK_SIZE: usize = super::PAGE_SIZE * 5; static mut STACKS: [Stack; 4] = [const { Stack::new() }; 4];
static mut STACKS: [[u8; STACK_SIZE]; 3] = [[0; STACK_SIZE]; 3];
let stack_top = |n: usize| unsafe { STACKS[n].as_ptr().add(STACK_SIZE).to_address() }; tss.set_stack(DF_STACK, unsafe { &STACKS[DF_STACK as usize] });
tss.set_stack(NMI_STACK, unsafe { &STACKS[NMI_STACK as usize] });
tss.set_stack(MC_STACK, unsafe { &STACKS[MC_STACK as usize] });
tss.set_stack(PF_STACK, unsafe { &STACKS[PF_STACK as usize] });
tss.interrupt_stack_table[DF_STACK as usize] = stack_top(DF_STACK as usize); serial_println!("Initialized TSS {:#?}", tss);
tss.interrupt_stack_table[NMI_STACK as usize] = stack_top(NMI_STACK as usize);
tss.interrupt_stack_table[MC_STACK as usize] = stack_top(MC_STACK as usize);
tss tss
}); });

View file

@ -89,6 +89,9 @@ impl Entry {
ExceptionVector::NON_MASKABLE_INTERRUPT => { ExceptionVector::NON_MASKABLE_INTERRUPT => {
options.set_interrupt_stack_table_index(super::gdt::NMI_STACK); options.set_interrupt_stack_table_index(super::gdt::NMI_STACK);
} }
ExceptionVector::PAGE_FAULT => {
options.set_interrupt_stack_table_index(super::gdt::PF_STACK);
}
ExceptionVector::MACHINE_CHECK => { ExceptionVector::MACHINE_CHECK => {
options.set_interrupt_stack_table_index(super::gdt::MC_STACK); options.set_interrupt_stack_table_index(super::gdt::MC_STACK);
} }
@ -520,7 +523,12 @@ extern "C" fn global_interrupt_handler(
serial_println!("Breakpoint interrupt handled successfully."); serial_println!("Breakpoint interrupt handled successfully.");
} }
ExceptionVector::PAGE_FAULT => { ExceptionVector::PAGE_FAULT => {
serial_println!("Page fault occurred! Error code: {:#x}", error_code); let cr2 = super::registers::Cr2::read();
serial_println!(
"Page fault occurred! Error code: {:#x} CR2: {:#x}",
error_code,
cr2.0
);
debug_backtrace(&Context { debug_backtrace(&Context {
rip: frame.instruction_pointer, rip: frame.instruction_pointer,
registers: *registers, registers: *registers,

View file

@ -64,6 +64,17 @@ bitflags::bitflags! {
} }
} }
pub struct Cr2(pub u64);
impl Cr2 {
pub fn read() -> Self {
let value: u64;
unsafe {
asm!("mov {}, cr2", out(reg) value, options(nomem, nostack, preserves_flags));
}
Self(value)
}
}
impl Cr4 { impl Cr4 {
pub fn read() -> Self { pub fn read() -> Self {
let value: u64; let value: u64;

View file

@ -5,6 +5,7 @@
use core::{cell::UnsafeCell, mem::offset_of}; use core::{cell::UnsafeCell, mem::offset_of};
use kernel::{ use kernel::{
serial_println,
sync::LazyLock, sync::LazyLock,
x86_64::{ x86_64::{
gdt::{DF_STACK, GlobalDescriptorTable, RING0}, gdt::{DF_STACK, GlobalDescriptorTable, RING0},
@ -17,6 +18,7 @@ pub static GDT: LazyLock<GlobalDescriptorTable> = LazyLock::new(GlobalDescriptor
#[unsafe(export_name = "_start")] #[unsafe(export_name = "_start")]
pub extern "C" fn main() -> ! { pub extern "C" fn main() -> ! {
kernel::serial_println!("Hello, world!"); kernel::serial_println!("Hello, world!");
kernel::limine::init_limine_boot_info();
GDT.load(); GDT.load();
kernel::serial_println!("[ok] GDT loaded"); kernel::serial_println!("[ok] GDT loaded");
@ -25,12 +27,13 @@ pub extern "C" fn main() -> ! {
_stack_frame: &mut idt::InterruptStackFrame, _stack_frame: &mut idt::InterruptStackFrame,
_error_code: u64, _error_code: u64,
) -> ! { ) -> ! {
kernel::serial_println!("[ok] Double fault handler called"); kernel::serial_println!("[ok] Fault handler called");
kernel::testing::exit_qemu(kernel::testing::QemuExitCode::Success) kernel::testing::exit_qemu(kernel::testing::QemuExitCode::Success)
} }
static IDT: LazyLock<InterruptDescriptorTable> = LazyLock::new(|| { static IDT: LazyLock<InterruptDescriptorTable> = LazyLock::new(|| {
let mut idt = InterruptDescriptorTable::new_default(); let mut idt = InterruptDescriptorTable::new_default();
idt.double_fault = unsafe { idt.double_fault = unsafe {
Entry::new( Entry::new(
double_fault_handler as *const (), double_fault_handler as *const (),
@ -38,7 +41,19 @@ pub extern "C" fn main() -> ! {
idt::EntryOptions::empty_interrupt_gate() idt::EntryOptions::empty_interrupt_gate()
.with_present(true) .with_present(true)
.with_privilege_level(RING0) .with_privilege_level(RING0)
.with_interrupt_stack_table_index(DF_STACK), .with_interrupt_stack_table_index(kernel::x86_64::gdt::DF_STACK),
)
};
// this should fire #PF since we have it installed anways in the default idt
idt.page_fault = unsafe {
Entry::new(
double_fault_handler as *const (),
offset_of!(GlobalDescriptorTable, kernel_code) as u16,
idt::EntryOptions::empty_interrupt_gate()
.with_present(true)
.with_privilege_level(RING0)
.with_interrupt_stack_table_index(kernel::x86_64::gdt::PF_STACK),
) )
}; };