diff --git a/kernel/.cargo/config.toml b/kernel/.cargo/config.toml index c3220c8..952962c 100644 --- a/kernel/.cargo/config.toml +++ b/kernel/.cargo/config.toml @@ -4,7 +4,7 @@ codegen-backend = "llvm" [unstable] json-target-spec = true # lets us specify a custom target specification file build-std-features = ["compiler-builtins-mem"] -build-std = ["core", "compiler_builtins"] +build-std = ["core", "alloc", "compiler_builtins"] [build] target = "x86_64-unknown-kernel.json" diff --git a/kernel/run.sh b/kernel/run.sh index 91795d7..0c2b294 100755 --- a/kernel/run.sh +++ b/kernel/run.sh @@ -71,6 +71,7 @@ set +e qemu-system-x86_64 \ -drive file="$image",format=raw \ -machine q35,accel=kvm -enable-kvm \ + -m 2G \ -drive if=pflash,format=raw,readonly=on,file="$OVMF_PATH/FV/OVMF_CODE.fd" \ -chardev stdio,id=serial0,logfile=qemu.log,signal=on \ -serial chardev:serial0 \ diff --git a/kernel/src/boot.rs b/kernel/src/boot.rs new file mode 100644 index 0000000..74f9799 --- /dev/null +++ b/kernel/src/boot.rs @@ -0,0 +1,122 @@ +use crate::{limine, sync::OnceLock}; + +#[repr(u8)] +#[derive(Debug, Clone, Copy)] +pub enum MemoryRegionType { + Usable = 0, + Reserved = 1, + AcpiReclaimable = 2, + AcpiNvs = 3, + BadMemory = 4, + Framebuffer = 5, + FirmwareReserved = 6, + FirmwareReclaimable = 7, + KernelAndModules = 8, +} + +impl From for MemoryRegionType { + fn from(kind: limine::MemMapEntryKind) -> Self { + match kind { + limine::MemMapEntryKind::Usable => Self::Usable, + limine::MemMapEntryKind::Reserved => Self::Reserved, + limine::MemMapEntryKind::AcpiReclaimable => Self::AcpiReclaimable, + limine::MemMapEntryKind::AcpiNvs => Self::AcpiNvs, + limine::MemMapEntryKind::BadMemory => Self::BadMemory, + limine::MemMapEntryKind::BootloaderReclaimable => Self::FirmwareReclaimable, + limine::MemMapEntryKind::KernelAndModules => Self::KernelAndModules, + limine::MemMapEntryKind::Framebuffer => Self::Framebuffer, + limine::MemMapEntryKind::ReservedMapped => Self::FirmwareReserved, + limine::MemMapEntryKind::Unknown => Self::Reserved, + } + } +} + +impl MemoryRegionType { + pub fn should_map(&self) -> bool { + !matches!(self, Self::Reserved | Self::BadMemory) + } + + pub fn is_usable(&self) -> bool { + matches!(self, Self::Usable | Self::AcpiReclaimable) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct MemoryRegion { + pub start: u64, + pub length: u64, + pub region_type: MemoryRegionType, +} + +impl From for MemoryRegion { + fn from(entry: limine::MemMapEntry) -> Self { + Self { + start: entry.base, + length: entry.length, + region_type: entry.kind().into(), + } + } +} + +impl MemoryRegion { + pub fn iter_pages(&self, page_size: usize) -> MemoryRegionIter<'_> { + MemoryRegionIter { + region: self, + cursor: 0, + page_size, + } + } +} + +pub struct MemoryRegionIter<'a> { + region: &'a MemoryRegion, + cursor: u64, + page_size: usize, +} + +impl Iterator for MemoryRegionIter<'_> { + type Item = u64; + + fn next(&mut self) -> Option { + if self.cursor >= self.region.length { + return None; + } + let addr = self.region.start + self.cursor; + self.cursor += self.page_size as u64; + Some(addr) + } +} + +const impl Default for MemoryRegion { + fn default() -> Self { + Self { + start: 0, + length: 0, + region_type: MemoryRegionType::Reserved, + } + } +} + +pub struct BootInfo { + pub hhdm_base: u64, + pub memory_map: &'static [MemoryRegion], +} + +pub static BOOT_INFO: OnceLock = OnceLock::new(); +static mut MEMORY_MAP: [MemoryRegion; 128] = [MemoryRegion::default(); 128]; + +pub fn init_boot_info>(hhdm_base: u64, memory_map: I) { + BOOT_INFO.initialize(|| { + for (i, region) in memory_map.enumerate() { + assert!(i < 128, "Memory map has more than 128 entries"); + unsafe { + MEMORY_MAP[i] = region; + } + } + + Ok::<_, !>(BootInfo { + hhdm_base, + memory_map: unsafe { (&raw const MEMORY_MAP).as_ref_unchecked() }, + }) + }); +} diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 97e36e5..710921b 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -1,16 +1,30 @@ #![no_std] -#![feature(const_trait_impl, const_default, const_range, debug_closure_helpers)] +#![feature( + const_trait_impl, + const_default, + const_range, + debug_closure_helpers, + allocator_api, + ptr_cast_slice, + likely_unlikely, + never_type +)] #![cfg_attr(test, feature(custom_test_frameworks))] #![cfg_attr(test, test_runner(crate::tests::test_runner))] #![cfg_attr(test, no_main)] #![cfg_attr(test, reexport_test_harness_main = "test_main")] +extern crate alloc; + pub mod bits; pub mod limine; pub mod serial; pub mod sync; pub mod x86_64; +pub mod boot; +pub mod memory; + pub mod testing; #[cfg(test)] mod tests; @@ -38,7 +52,7 @@ macro_rules! drop_guard { fn forget(self) { let mut this = ::core::mem::ManuallyDrop::new(self); unsafe { - ManuallyDrop::drop(&mut this.0); + ::core::mem::ManuallyDrop::drop(&mut this.0); } } } diff --git a/kernel/src/limine.rs b/kernel/src/limine.rs index 3b8d51e..e64b4f1 100644 --- a/kernel/src/limine.rs +++ b/kernel/src/limine.rs @@ -1,4 +1,4 @@ -use core::{cell::UnsafeCell, ptr::NonNull}; +use core::{cell::UnsafeCell, fmt::Debug, ptr::NonNull}; #[repr(C)] pub struct BaseRevision(UnsafeCell<[u64; 3]>); @@ -44,7 +44,7 @@ pub struct Request { request: U, } -unsafe impl Sync for Request{} +unsafe impl Sync for Request {} #[repr(C)] pub struct Response { @@ -165,3 +165,110 @@ impl FramebufferRequest { .unwrap_or(&[]) } } + +#[repr(C)] +pub struct MemMapResponse { + count: u64, + entries: *const *const MemMapEntry, +} + +unsafe impl Send for MemMapResponse {} +unsafe impl Sync for MemMapResponse {} + +pub type MemMapRequest = Request; + +impl MemMapRequest { + const ID: [u64; 2] = [0x67cf3d9d378a806f, 0xe304acdfc50c3c62]; + + pub const fn new() -> Self { + Self::new_raw(Self::ID, 0, ()) + } + + pub fn entries<'a>(&self) -> &'a [&'a MemMapEntry] { + // SAFETY: limine responses are guaranteed to be valid for the lifetime of the memory mapping. + self.response() + .map(|response| unsafe { + core::slice::from_raw_parts( + response.response.entries.cast::<&'a MemMapEntry>(), + response.response.count as usize, + ) + }) + .unwrap_or(&[]) + } +} + +#[repr(u64)] +#[derive(Debug)] +pub enum MemMapEntryKind { + Usable = 0, + Reserved = 1, + AcpiReclaimable = 2, + AcpiNvs = 3, + BadMemory = 4, + BootloaderReclaimable = 5, + KernelAndModules = 6, + Framebuffer = 7, + ReservedMapped = 8, + Unknown = u64::MAX, +} + +impl MemMapEntryKind { + pub fn from_u64(value: u64) -> Self { + match value { + 0 => Self::Usable, + 1 => Self::Reserved, + 2 => Self::AcpiReclaimable, + 3 => Self::AcpiNvs, + 4 => Self::BadMemory, + 5 => Self::BootloaderReclaimable, + 6 => Self::KernelAndModules, + 7 => Self::Framebuffer, + 8 => Self::ReservedMapped, + _ => Self::Unknown, + } + } +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct MemMapEntry { + pub base: u64, + pub length: u64, + pub kind: u64, +} + +impl MemMapEntry { + pub fn kind(&self) -> MemMapEntryKind { + MemMapEntryKind::from_u64(self.kind) + } +} + +impl Debug for MemMapEntry { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("MemMapEntry") + .field("base", &format_args!("{:#x}", self.base)) + .field("length", &format_args!("{:#x}", self.length)) + .field("kind", &self.kind()) + .finish() + } +} + +#[repr(C)] +#[derive(Debug)] +pub struct HhdmResponse { + pub offset: u64, +} + +pub type HhdmRequest = Request; + +impl HhdmRequest { + const ID: [u64; 2] = [0x48dcf1cb8ad2b852, 0x63984e959a98244b]; + + pub const fn new() -> Self { + Self::new_raw(Self::ID, 0, ()) + } + + pub fn offset(&self) -> Option { + self.response().map(|response| response.response.offset) + } +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 626adbc..585c52f 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -1,18 +1,75 @@ #![no_std] #![no_main] +use kernel::{ + memory::VirtAddr, + sync::LazyLock, + x86_64::{gdt::GlobalDescriptorTable, idt::InterruptDescriptorTable}, +}; + #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { kernel::serial_println!("[PANIC] {}", _info); kernel::x86_64::halt_loop() } +static IDT: LazyLock = + LazyLock::new(InterruptDescriptorTable::new_default); + +static GDT: LazyLock = LazyLock::new(GlobalDescriptorTable::new); + /// Entry point for the kernel #[unsafe(no_mangle)] extern "C" fn _start() -> ! { kernel::serial_println!("Hello, world!"); assert!(limine_requests::LIMINE_BASE_REVISION.is_supported()); + _ = kernel::memory::HHDM_BASE + .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!( + "HHDM offset: 0x{:#x}", + kernel::memory::HHDM_BASE.get().unwrap() + ); + + kernel::serial_println!( + "Memory map: {:#?}", + limine_requests::MEMMAP_REQUEST.entries() + ); + + GDT.load(); + IDT.load(); + + kernel::serial_println!("entry point: 0x{:x}", _start as *const () as usize); + kernel::serial_println!( + "entry point phy: {:?}", + kernel::x86_64::paging::get_physical_addr(VirtAddr(_start as *const () as u64)) + ); + + let pmm = kernel::memory::PhysicalMemoryAllocator::from_memory_map( + kernel::boot::BOOT_INFO + .get() + .expect("Boot info not initialized") + .memory_map, + ); + + kernel::serial_println!("PMM: {pmm:#?}"); + let fb = limine_requests::FRAMEBUFFER_REQUEST .framebuffers() .first() @@ -52,6 +109,14 @@ mod limine_requests { #[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; diff --git a/kernel/src/memory.rs b/kernel/src/memory.rs new file mode 100644 index 0000000..2e541ad --- /dev/null +++ b/kernel/src/memory.rs @@ -0,0 +1,1066 @@ +use core::{alloc::Allocator, fmt::Debug}; + +use crate::{memory::page_tree::PageTree, serial_println, sync::OnceLock, x86_64::PAGE_SIZE}; + +pub static HHDM_BASE: OnceLock = OnceLock::new(); + +pub trait VirtAddrTranslationExt { + fn into_phy_addr(self) -> Option; +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct PhyAddr(pub u64); + +impl PhyAddr { + pub fn into_hhdm_virt(&self) -> VirtAddr { + VirtAddr(self.0 + unsafe { crate::memory::HHDM_BASE.get().unwrap_unchecked() }) + } + pub fn page_add(&self, page_count: usize) -> PhyAddr { + PhyAddr(self.0 + (page_count as u64 * PAGE_SIZE as u64)) + } + pub fn byte_add(&self, offset: usize) -> PhyAddr { + PhyAddr(self.0 + offset as u64) + } +} + +impl Debug for PhyAddr { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "phy#{:#x}", self.0) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct VirtAddr(pub u64); + +impl Debug for VirtAddr { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "vm#{:#x}", self.0) + } +} + +impl VirtAddr { + pub fn as_ptr(&self) -> *const T { + core::ptr::with_exposed_provenance(self.0 as usize) + } + pub fn as_mut(&self) -> *mut T { + core::ptr::with_exposed_provenance_mut(self.0 as usize) + } +} + +pub struct PageChunk { + pub next: PhyAddr, + pub count: usize, +} + +pub struct PhysicalMemoryAllocator { + tree: PageTree, +} + +impl Debug for PhysicalMemoryAllocator { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("PhysicalMemoryAllocator") + .field_with("tree", |f| { + let iter = self.tree.iter(); + write!(f, "[")?; + for info in iter { + writeln!(f, "({:?}..{:?}), ", info.phy, info.end())?; + } + write!(f, "]") + }) + .finish() + } +} + +impl PhysicalMemoryAllocator { + 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 mut pmm = PhysicalMemoryAllocator { + tree: PageTree::from_root(usize::MAX), + }; + + 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 + } + + pub fn free_region(&mut self, page_index: usize, count: usize) { + serial_println!( + "Freeing region: page_index = {}, count = {}", + page_index, + count + ); + self.tree.free_region(page_index, count); + } +} + +#[derive(Debug, Default)] +pub struct PanicingAllocator; + +#[global_allocator] +static GLOBAL_ALLOCATOR: PanicingAllocator = PanicingAllocator; + +unsafe impl core::alloc::GlobalAllocator for PanicingAllocator {} + +unsafe impl Allocator for PanicingAllocator { + fn allocate( + &self, + layout: core::alloc::Layout, + ) -> Result, core::alloc::AllocError> { + match layout.size() { + 0 => Ok(core::ptr::NonNull::slice_from_raw_parts( + core::ptr::NonNull::dangling(), + 0, + )), + _ => panic!("PanicingAllocator cannot allocate memory"), + } + } + + unsafe fn deallocate(&self, _ptr: core::ptr::NonNull, _layout: core::alloc::Layout) {} +} + +pub mod bump { + use core::{ + alloc::{Allocator, Layout}, + cell::Cell, + marker::PhantomData, + ops::{Deref, DerefMut}, + ptr::{self, NonNull}, + }; + + #[repr(align(16))] + struct Chunk { + next: Option>, + size: usize, + } + + const EMPTY_CHUNK: Chunk = Chunk { + next: None, + size: 0, + }; + + struct RestorePoint { + top: *mut u8, + chunk: NonNull, + } + + pub struct Bump { + top: Cell<*mut u8>, + bottom: Cell<*mut Chunk>, + next_chunk: Cell>>, + backing_alloc: A, + } + + unsafe impl Send for Bump {} + + unsafe impl Allocator for BumpScope<'_, '_, A> { + fn allocate( + &self, + layout: Layout, + ) -> Result, core::alloc::AllocError> { + let virt = unsafe { self.alloc_layout(layout).ok_or(core::alloc::AllocError) }?; + + Ok(virt.cast_slice(layout.size())) + } + + unsafe fn deallocate(&self, _ptr: core::ptr::NonNull, _layout: Layout) {} + } + + impl Bump { + pub fn new_in(backing_alloc: A) -> Self { + let chunk = NonNull::from(&EMPTY_CHUNK); + let bottom = unsafe { chunk.as_ptr().add(1) }; + + Self { + top: Cell::new(bottom.cast::()), + bottom: Cell::new(bottom), + next_chunk: Cell::new(None), + backing_alloc, + } + } + + /// # Safety + /// The caller must ensure that the chunk is aligned to `Chunk` alignment. + pub unsafe fn from_raw_chunk_in(chunk: NonNull<[u8]>, backing_alloc: A) -> Self { + assert!( + chunk.as_ptr().addr().is_multiple_of(align_of::()), + "Chunk must be aligned to Chunk alignment" + ); + assert!( + chunk.len() >= size_of::(), + "Chunk must be at least the size of Chunk" + ); + + let len = chunk.len(); + let chunk = chunk.cast::(); + let bottom = unsafe { chunk.as_ptr().add(1) }; + let top = unsafe { bottom.byte_add(len - size_of::()) }; + + Self { + top: Cell::new(top.cast::()), + bottom: Cell::new(bottom), + next_chunk: Cell::new(None), + backing_alloc, + } + } + + pub fn as_scope<'env>(&'env mut self) -> BumpScope<'env, 'env, A> { + BumpScope { + bump: self, + _env: PhantomData, + _scope: PhantomData, + } + } + + pub fn scope<'env, F, R>(&'env mut self, f: F) -> R + where + F: for<'scope> FnOnce(&'scope mut BumpScope<'env, 'scope, A>) -> R + 'env, + R: 'env, + { + let restore = self.restore_point(); + + let mut scope = BumpScope { + bump: self, + _env: PhantomData, + _scope: PhantomData, + }; + + let result = f(&mut scope); + + unsafe { self.restore(restore) }; + + result + } + + fn restore_point(&self) -> RestorePoint { + RestorePoint { + top: self.top.get(), + chunk: NonNull::from(self.chunk()), + } + } + + unsafe fn restore(&self, restore_point: RestorePoint) { + // get current chunk and next chunk + let mut chunk = Some(NonNull::from(self.chunk())); + let mut head = self.next_chunk.get(); + + // walk the linked list of chunks used since the restore point and + // re-link them onto the free list. + while let Some(cnk) = chunk + && cnk != restore_point.chunk + { + let next = unsafe { ptr::replace(&raw mut (*cnk.as_ptr()).next, head) }; + + head = Some(cnk); + chunk = next; + } + + self.top.set(restore_point.top); + let bot = unsafe { restore_point.chunk.as_ptr().add(1) }; + self.bottom.set(bot); + self.next_chunk.set(head); + } + + #[inline] + pub unsafe fn alloc_layout(&self, layout: Layout) -> Option> { + let top = self.top.get(); + let bottom = self.bottom.get().addr(); + let extra = Self::extra_bytes(top, layout.align()); + + let slow_path = Self::out_of_mem(layout, top, bottom, false); + + if slow_path { + self.alloc_layout_slow_cold(layout) + } else { + let new_top = unsafe { top.byte_sub(extra).byte_sub(layout.size()) }; + self.top.set(new_top); + + Some(unsafe { NonNull::new_unchecked(new_top) }) + } + } + + #[inline(never)] + fn try_alloc_slow_with_no_inline(&self, f: F) -> Option> + where + F: FnOnce() -> T, + { + let p = self.alloc_layout_slow(Layout::new::())?; + let p = p.cast::(); + unsafe { p.write(f()) }; + Some(p) + } + + #[cold] + fn alloc_layout_slow_cold(&self, layout: Layout) -> Option> { + self.alloc_layout_slow(layout) + } + + // #[inline(never)] + // fn alloc_layout_slow_no_inline(&self, layout: Layout) -> Option> { + // self.alloc_layout_slow(layout) + // } + + fn alloc_layout_slow(&self, layout: Layout) -> Option> { + let extra_with_chunk = { + assert!(size_of::() == align_of::()); + // if the allocation is greater than the size of the chunk header, we need more bytes past the chunk header to align the allocation. + if layout.align() > size_of::() { + layout.align() - size_of::() + } else { + 0 + } + }; + + let min_size = layout.size() + extra_with_chunk; + + while let Some(mut chunk) = self.next_chunk.get() { + // SAFETY: `Bump` is not `Sync` and `alloc_layout_slow is not + // reentrant, we have exclusive access to `chunk` + let chunk = unsafe { chunk.as_mut() }; + let cap = chunk.size; + + if cap >= min_size { + let prev = self.chunk(); + self.next_chunk.set(chunk.next); + + // add the current chunk to the linked list of chunks, so it can be freed later. + chunk.next = Some(NonNull::from(prev)); + + unsafe { + // bottom is just past the chunk header + let bot = (&raw const *chunk).add(1).cast_mut(); + self.bottom.set(bot); + + // calculate top of chunk + let top = bot.byte_add(cap).cast::(); + + // sub allocation + let extra = Self::extra_bytes(top, layout.align()); + let top = top.byte_sub(extra).byte_sub(layout.size()); + self.top.set(top); + + return Some(NonNull::new_unchecked(top)); + }; + } else { + // chunk is too small, try the next one + self.next_chunk.set(chunk.next); + unsafe { + let layout = Layout::from_size_align_unchecked( + cap + size_of::(), + align_of::(), + ); + self.backing_alloc + .deallocate(NonNull::from(chunk).cast(), layout); + } + } + } + + let chunk = self.chunk(); + let prev = if chunk.size == 0 { + None + } else { + Some(NonNull::from(chunk)) + }; + + let size = self.next_chunk_size().max(min_size); + let new_chunk = self + .backing_alloc + .allocate(unsafe { + Layout::from_size_align_unchecked( + size + size_of::(), + align_of::(), + ) + }) + .ok()? + .cast::(); + + unsafe { + new_chunk.write(Chunk { next: prev, size }); + + let bot = new_chunk.add(1).as_ptr(); + self.bottom.set(bot); + + let top = bot.byte_add(size).cast::(); + let extra = Self::extra_bytes(top, layout.align()); + let top = top.byte_sub(extra).byte_sub(layout.size()); + self.top.set(top); + + Some(NonNull::new_unchecked(top)) + } + } + + fn next_chunk_size(&self) -> usize { + let chunk = self.chunk(); + let cap = (chunk.size + size_of::()).saturating_mul(2) - size_of::(); + + cap.max(0x1000) + } + + pub unsafe fn alloc_raw(&self) -> Option> { + let layout = core::alloc::Layout::new::(); + unsafe { self.alloc_layout(layout).map(|p| p.cast::()) } + } + + fn out_of_mem(layout: Layout, top: *mut u8, bottom: usize, comptime: bool) -> bool { + let extra = Self::extra_bytes(top, layout.align()); + + let max_padding = layout.align() - 1; + let max_size = layout.size() + max_padding; + let top = top.addr(); + + // biggest possible virtual address on x86_64 is 2^57 - 1 + const MAX_ADDR: usize = ((1u64 << 57) - 1) as usize; + // offsets of < SAFE_SIZE are guaranteed not to overflow the address space. + const SAFE_SIZE: usize = usize::MAX - MAX_ADDR; + + if comptime && max_size < SAFE_SIZE { + if max_size <= 16 { + let top = top - extra; + let top = top - layout.size(); + bottom > top + } else if max_padding < 16 { + let top = top - extra; + bottom + layout.size() > top + } else { + bottom + layout.size() + max_padding > top + } + } else if max_padding + .checked_add(isize::MAX as usize) + .is_some_and(|p| p < SAFE_SIZE) + { + if max_size < 16 { + let top = top - extra; + bottom + layout.size() > top + } else { + bottom + extra + layout.size() > top + } + } else { + if max_padding < 16 { + let top = top - extra; + top.checked_sub(layout.size()).is_none_or(|t| t < bottom) + } else { + top.checked_sub(extra) + .and_then(|t| t.checked_sub(layout.size())) + .is_none_or(|t| t < bottom) + } + } + } + + fn extra_bytes(ptr: *mut u8, align: usize) -> usize { + assert!(align.is_power_of_two(), "Alignment must be a power of two"); + ptr.addr() & (align - 1) + } + + fn chunk(&self) -> &Chunk { + unsafe { self.bottom.get().sub(1).as_ref_unchecked() } + } + } + + #[repr(transparent)] + pub struct BumpScope<'env, 'scope, A: Allocator> { + bump: &'scope mut Bump, + _env: PhantomData<&'env &'env mut ()>, + _scope: PhantomData<&'scope &'scope mut ()>, + } + + impl<'env, 'scope, A: Allocator> Deref for BumpScope<'env, 'scope, A> { + type Target = Bump; + + fn deref(&self) -> &Self::Target { + self.bump + } + } + + impl<'env, 'scope, A: Allocator> DerefMut for BumpScope<'env, 'scope, A> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.bump + } + } + + impl<'env, 'scope, A: Allocator> BumpScope<'env, 'scope, A> { + pub fn alloc_with(&self, f: F) -> &'scope mut T + where + F: FnOnce() -> T, + { + self.try_alloc_with(f).unwrap_or_else(|| { + panic!( + "Bump allocator out of memory when allocating {} bytes with alignment {}", + size_of::(), + align_of::() + ) + }) + } + + pub fn try_alloc_with(&self, f: F) -> Option<&'scope mut T> + where + F: FnOnce() -> T, + { + let layout = Layout::new::(); + let top = self.top.get(); + let bottom = self.bottom.get().addr(); + let extra = Bump::::extra_bytes(top, layout.align()); + + let slow_path = Bump::::out_of_mem(layout, top, bottom, false); + + let mut ptr = if layout.size() <= 16 { + if slow_path { + self.try_alloc_slow_with_no_inline(f)? + } else { + let new_top = unsafe { top.byte_sub(extra).byte_sub(layout.size()) }; + self.top.set(new_top); + unsafe { + ptr::write(new_top.cast::(), f()); + NonNull::new_unchecked(new_top.cast::()) + } + } + } else { + // in-place + let ptr = if slow_path { + self.alloc_layout_slow_cold(layout)? + } else { + let new_top = unsafe { top.byte_sub(extra).byte_sub(layout.size()) }; + self.top.set(new_top); + + unsafe { NonNull::new_unchecked(new_top) } + }; + + let ptr = ptr.cast::(); + unsafe { + ptr.write(f()); + } + + ptr + }; + + Some(unsafe { ptr.as_mut() }) + } + } + + fn asdf(bump: &mut Bump) { + let mut bump = bump.as_scope(); + let x = bump.alloc_with(|| 3u64); + bump.scope(|bump| { + let _y = bump.alloc_with(|| 4u64); + let _z = bump.alloc_with(|| 5u64); + }); + + assert!(*x == 3); + } +} + +mod page_tree { + use crate::{ + memory::{PAGE_SIZE, PhyAddr}, + serial_println, + }; + + use core::ops::{Index, IndexMut}; + + enum SearchResult { + Found(T), + NotFound(T), + } + struct Page { + left_idx: usize, + right_idx: usize, + parent: usize, + count: isize, + } + + impl Page { + fn new_red(_idx: usize, count: usize) -> Self { + Self { + left_idx: usize::MAX, + right_idx: usize::MAX, + parent: usize::MAX, + count: -(count as isize), + } + } + fn child(&self, left: bool) -> usize { + if left { self.left_idx } else { self.right_idx } + } + fn red(&self) -> bool { + self.count.is_negative() + } + fn set_color(&mut self, red: bool) { + if red { + self.count = -(self.count.unsigned_abs() as isize); + } else { + self.count = self.count.unsigned_abs() as isize; + } + } + fn count(&self) -> usize { + self.count.unsigned_abs() + } + fn set_count(&mut self, count: usize) { + let red = self.red(); + self.count = if red { + -(count as isize) + } else { + count as isize + }; + } + } + + #[repr(transparent)] + pub struct PageTree { + root: usize, + pages: Pages, + } + + struct Pages; + + impl Pages { + fn get_disjoint_mut(&mut self, indices: [usize; N]) -> [&mut Page; N] { + indices.map(|idx| unsafe { + PhyAddr(idx as u64 * PAGE_SIZE as u64) + .into_hhdm_virt() + .as_mut::() + .as_mut_unchecked() + }) + } + + fn get_ptr(&self, idx: usize) -> *const Page { + PhyAddr(idx as u64 * PAGE_SIZE as u64) + .into_hhdm_virt() + .as_ptr::() + } + + fn get_mut(&mut self, idx: usize) -> &mut Page { + unsafe { + PhyAddr(idx as u64 * PAGE_SIZE as u64) + .into_hhdm_virt() + .as_mut::() + .as_mut_unchecked() + } + } + } + + impl Index for Pages { + type Output = Page; + fn index(&self, idx: usize) -> &Self::Output { + unsafe { + PhyAddr(idx as u64 * PAGE_SIZE as u64) + .into_hhdm_virt() + .as_ptr::() + .as_ref_unchecked() + } + } + } + + impl IndexMut for Pages { + fn index_mut(&mut self, idx: usize) -> &mut Self::Output { + unsafe { + PhyAddr(idx as u64 * PAGE_SIZE as u64) + .into_hhdm_virt() + .as_mut::() + .as_mut_unchecked() + } + } + } + + impl PageTree { + pub fn from_root(root: usize) -> Self { + Self { root, pages: Pages } + } + pub fn free_region(&mut self, page_index: usize, count: usize) { + let pre = match self.find(page_index) { + SearchResult::Found(_) => panic!("Page index already exists in free list"), + SearchResult::NotFound(Self::INVALID_IDX) => Self::INVALID_IDX, + SearchResult::NotFound(idx) => { + if idx > page_index { + self.next_back_of(idx) + } else { + idx + } + } + }; + + match pre { + Self::INVALID_IDX => { + self.insert(page_index, count); + } + _ => { + if self.pages[pre].count() + pre == page_index { + serial_println!( + "Found previous page: pre = {}, pre_count = {}", + pre, + self.pages[pre].count() + ); + + let new_count = self.pages[pre].count() + count; + self.pages[pre].set_count(new_count); + } else { + self.insert(page_index, count); + } + } + } + } + + // rb-tree impl + + const INVALID_IDX: usize = usize::MAX; + fn rotate(&mut self, x: usize, left: bool) { + let (y, b, x_hole, y_hole) = if left { + let y = self.pages[x].right_idx; + let b = self.pages[y].left_idx; + + let [x_page, y_page] = self.pages.get_disjoint_mut([x, y]); + let x_hole = &mut x_page.right_idx; + let y_hole = &mut y_page.left_idx; + + (y, b, x_hole, y_hole) + } else { + let y = self.pages[x].left_idx; + let b = self.pages[y].right_idx; + + let [x_page, y_page] = self.pages.get_disjoint_mut([x, y]); + let x_hole = &mut x_page.left_idx; + let y_hole = &mut y_page.right_idx; + + (y, b, x_hole, y_hole) + }; + + *x_hole = b; + *y_hole = x; + + if b != usize::MAX { + self.pages[b].parent = x; + } + + let x_parent = self.pages[x].parent; + self.pages[y].parent = x_parent; + if self.pages[x].parent == usize::MAX { + self.root = y; + } else if x == self.pages[x_parent].left_idx { + self.pages[x_parent].left_idx = y; + } else { + self.pages[x_parent].right_idx = y; + } + + self.pages[x].parent = y; + } + + pub fn find(&self, idx: usize) -> SearchResult { + let mut node = self.root; + let mut parent = usize::MAX; + while node != usize::MAX { + parent = node; + if idx < node { + node = self.pages[node].left_idx; + } else if idx > node { + node = self.pages[node].right_idx; + } else { + return SearchResult::Found(node); + } + } + + SearchResult::NotFound(parent) + } + + pub fn insert(&mut self, idx: usize, count: usize) { + let SearchResult::NotFound(parent) = self.find(idx) else { + panic!("Page index already exists in free list"); + }; + + let page = &mut self.pages[idx]; + *page = Page::new_red(idx, count); + + page.parent = parent; + if parent == usize::MAX { + self.root = idx; + } else if idx < parent { + self.set_left_child_of(parent, idx); + } else { + self.set_right_child_of(parent, idx); + } + + self.fixup_insert(idx); + } + + fn fixup_insert(&mut self, mut idx: usize) { + // idx is red; fixup the tree while the parent is red + loop { + let parent = self.parent_of(idx); + if parent == usize::MAX || !self.color_of(parent) { + break; + } + + // gp is guaranteed to exist because the parent is red and the root is black + let gp = self.pages[parent].parent; + let (uncle, uncle_left) = if parent == self.pages[gp].left_idx { + (self.pages[gp].right_idx, false) + } else { + (self.pages[gp].left_idx, true) + }; + + if self.color_of(uncle) { + self.pages[parent].set_color(false); + self.pages[uncle].set_color(false); + self.pages[gp].set_color(true); + idx = gp; + } else { + // uncle is black + if idx == self.pages[parent].child(uncle_left) { + // idx is on the same side as uncle + self.rotate(parent, !uncle_left); + idx = parent; + } + + self.pages[parent].set_color(false); + self.pages[gp].set_color(true); + self.rotate(gp, uncle_left); + } + } + + self.pages[self.root].set_color(false); + } + + /// replaces the node at `at_idx` with the node at `with_idx`. + fn replace(&mut self, at_idx: usize, with_idx: usize) { + let at_parent = self.parent_of(at_idx); + + match at_parent { + Self::INVALID_IDX => {} + _ if at_idx == self.left_child_of(at_parent) => { + self.set_left_child_of(at_parent, with_idx); + } + _ => { + self.set_right_child_of(at_parent, with_idx); + } + } + + if let Self::INVALID_IDX = with_idx { + } else { + self.set_parent_of(with_idx, at_parent); + } + } + + /// returns the next node in the tree after `idx`, or usize::MAX if there is no next node. + fn minimum_of(&self, mut idx: usize) -> usize { + loop { + let next = self.left_child_of(idx); + if next == usize::MAX { + break; + } + idx = next; + } + + idx + } + + fn maximum_of(&self, mut idx: usize) -> usize { + loop { + let next = self.right_child_of(idx); + if next == usize::MAX { + break; + } + idx = next; + } + + idx + } + + fn next_back_of(&self, mut idx: usize) -> usize { + match self.left_child_of(idx) { + Self::INVALID_IDX => { + let mut p = self.parent_of(idx); + while p != Self::INVALID_IDX && idx == self.left_child_of(p) { + idx = p; + p = self.parent_of(idx); + } + p + } + left => self.maximum_of(left), + } + } + + fn next_of(&self, mut idx: usize) -> usize { + match self.right_child_of(idx) { + Self::INVALID_IDX => { + let mut p = self.parent_of(idx); + while p != Self::INVALID_IDX && idx == self.right_child_of(p) { + idx = p; + p = self.parent_of(idx); + } + p + } + right => self.minimum_of(right), + } + } + + fn left_child_of(&self, idx: usize) -> usize { + self.pages[idx].left_idx + } + fn right_child_of(&self, idx: usize) -> usize { + self.pages[idx].right_idx + } + fn child_of(&self, idx: usize, left: bool) -> usize { + if left { + self.pages[idx].left_idx + } else { + self.pages[idx].right_idx + } + } + fn children_of(&self, idx: usize) -> (usize, usize) { + (self.pages[idx].left_idx, self.pages[idx].right_idx) + } + fn parent_of(&self, idx: usize) -> usize { + self.pages[idx].parent + } + fn set_parent_of(&mut self, idx: usize, parent: usize) { + if idx != Self::INVALID_IDX { + self.pages[idx].parent = parent; + } + } + fn set_color_of(&mut self, idx: usize, red: bool) { + self.pages[idx].set_color(red); + } + fn color_of(&self, idx: usize) -> bool { + // leaves are implicitly black + idx != Self::INVALID_IDX && self.pages[idx].red() + } + fn set_left_child_of(&mut self, idx: usize, left: usize) { + self.pages[idx].left_idx = left; + } + fn set_right_child_of(&mut self, idx: usize, right: usize) { + self.pages[idx].right_idx = right; + } + + pub fn remove(&mut self, z: usize) { + let y = match self.children_of(z) { + (Self::INVALID_IDX, _) | (_, Self::INVALID_IDX) => z, + _ => self.next_of(z), + }; + + let x = match self.left_child_of(y) { + Self::INVALID_IDX => self.right_child_of(y), + _ => self.left_child_of(y), + }; + + let p_y = self.parent_of(y); + self.set_parent_of(x, p_y); + + match p_y { + Self::INVALID_IDX => { + self.root = x; + } + _ if y == self.left_child_of(p_y) => { + self.set_left_child_of(p_y, x); + } + _ => { + self.set_right_child_of(p_y, x); + } + } + + if y != z { + self.replace(z, y); + } + + if !self.color_of(y) { + self.fixup_remove(x); + } + } + + fn fixup_remove(&mut self, mut x: usize) { + loop { + if x == self.root || self.color_of(x) { + break; + } + + let p_x = self.parent_of(x); + let (mut w, w_left) = if x == self.left_child_of(p_x) { + (self.right_child_of(p_x), false) + } else { + (self.left_child_of(p_x), true) + }; + + if self.color_of(w) { + self.set_color_of(w, false); + self.set_color_of(p_x, true); + + self.rotate(p_x, !w_left); + + w = self.child_of(p_x, w_left); + } + + // w is now black + + match ( + self.color_of(self.left_child_of(w)), + self.color_of(self.right_child_of(w)), + ) { + (false, false) => { + self.set_color_of(w, true); + x = p_x; + continue; + } + (left @ true, false) | (left @ false, true) => { + if left != w_left { + self.set_color_of(self.child_of(w, left), false); + self.set_color_of(w, true); + self.rotate(w, w_left); + } + + self.set_color_of(w, self.color_of(p_x)); + self.set_color_of(p_x, false); + self.set_color_of(self.child_of(w, w_left), false); + self.rotate(p_x, !w_left); + x = self.root; + } + (true, true) => { + x = self.root; + } + } + } + } + + pub fn iter(&self) -> PageTreeIter<'_> { + PageTreeIter { + tree: self, + next: self.minimum_of(self.root), + } + } + } + + pub struct PageTreeIter<'a> { + tree: &'a PageTree, + next: usize, + } + + pub struct PageInfo { + pub phy: PhyAddr, + pub count: usize, + } + + impl PageInfo { + pub fn end(&self) -> PhyAddr { + self.phy.page_add(self.count) + } + } + + impl<'a> Iterator for PageTreeIter<'a> { + type Item = PageInfo; + + fn next(&mut self) -> Option { + if self.next == usize::MAX { + return None; + } + + let page_info = PageInfo { + phy: PhyAddr(self.next as u64 * PAGE_SIZE as u64), + count: self.tree.pages[self.next].count(), + }; + self.next = self.tree.next_of(self.next); + + Some(page_info) + } + } +} diff --git a/kernel/src/x86_64.rs b/kernel/src/x86_64.rs index bff17f3..bfeff7b 100644 --- a/kernel/src/x86_64.rs +++ b/kernel/src/x86_64.rs @@ -1,13 +1,18 @@ #![cfg(target_arch = "x86_64")] +pub mod cpuid; pub mod gdt; pub mod idt; pub mod instructions; pub mod paging; pub mod registers; +use core::{arch::asm, fmt::Debug}; + pub use instructions::hlt; +use crate::memory::VirtAddr; + pub const PAGE_SIZE: usize = 4096; pub fn halt_loop() -> ! { @@ -82,3 +87,128 @@ bitflags::bitflags! { const ID_FLAG = 1 << 21; } } + +pub trait VirtAddrExt { + const LEVEL5: u8 = 4; + const LEVEL4: u8 = 3; + const LEVEL3: u8 = 2; + const LEVEL2: u8 = 1; + const LEVEL1: u8 = 0; + + const PT: u8 = Self::LEVEL1; + const PD: u8 = Self::LEVEL2; + const PDPT: u8 = Self::LEVEL3; + const PML4: u8 = Self::LEVEL4; + const PML5: u8 = Self::LEVEL5; + + fn is_canonical(&self) -> bool; + fn page_table_index(&self) -> u16; + fn offset_4k(&self) -> u16; + fn offset_2m(&self) -> u32; + fn offset_1g(&self) -> u32; + fn into_parts(self) -> (E::Entries, E::Offset); +} + +pub struct L4Entries4K; +pub struct L4Entries2M; +pub struct L4Entries1G; + +pub struct U12(pub u16); +pub struct U21(pub u32); +pub struct U30(pub u32); + +pub(crate) mod sealed { + use super::{L4Entries1G, L4Entries2M, L4Entries4K, U12, U21, U30}; + + pub trait PageSize { + type Entries; + type Offset; + fn decompose(addr_bits: u64) -> (Self::Entries, Self::Offset); + } + + impl PageSize for L4Entries4K { + type Entries = [u16; 4]; + type Offset = U12; + + fn decompose(addr_bits: u64) -> (Self::Entries, Self::Offset) { + let entries = [ + ((addr_bits >> 39) & 0x1FF) as u16, + ((addr_bits >> 30) & 0x1FF) as u16, + ((addr_bits >> 21) & 0x1FF) as u16, + ((addr_bits >> 12) & 0x1FF) as u16, + ]; + let offset = U12((addr_bits & 0xFFF) as u16); + (entries, offset) + } + } + + impl PageSize for L4Entries2M { + type Entries = [u16; 3]; + type Offset = U21; + + fn decompose(addr_bits: u64) -> (Self::Entries, Self::Offset) { + let entries = [ + ((addr_bits >> 39) & 0x1FF) as u16, + ((addr_bits >> 30) & 0x1FF) as u16, + ((addr_bits >> 21) & 0x1FF) as u16, + ]; + let offset = U21((addr_bits & 0x1FFFFF) as u32); + (entries, offset) + } + } + + impl PageSize for L4Entries1G { + type Entries = [u16; 2]; + type Offset = U30; + + fn decompose(addr_bits: u64) -> (Self::Entries, Self::Offset) { + let entries = [ + ((addr_bits >> 39) & 0x1FF) as u16, + ((addr_bits >> 30) & 0x1FF) as u16, + ]; + let offset = U30((addr_bits & 0x3FFFFFFF) as u32); + (entries, offset) + } + } +} + +impl VirtAddrExt for VirtAddr { + fn is_canonical(&self) -> bool { + // sign bits are bits 48-63, and they must all be the same as bit 47 + // + // shift right 48 bits puts the 47th bit into the carry flag. + // sign-bits + CF should be 0 + let canonical: u8; + unsafe { + asm!( + "shr {bits}, 48", + "adc {bits}, 0", + "setz {canonical}", + bits = inout(reg) self.0 => _, + canonical = out(reg_byte) canonical, + ); + } + + canonical == 1 + } + + fn page_table_index(&self) -> u16 { + assert!(LEVEL <= 4, "LEVEL must be in the range 0..=4"); + let shift = 12 + (LEVEL * 9); + ((self.0 >> shift) & 0x1FF) as u16 + } + + fn offset_4k(&self) -> u16 { + (self.0 & 0xFFF) as u16 + } + fn offset_2m(&self) -> u32 { + (self.0 & 0x1FFFFF) as u32 + } + fn offset_1g(&self) -> u32 { + (self.0 & 0x3FFFFFFF) as u32 + } + + fn into_parts(self) -> (E::Entries, E::Offset) { + E::decompose(self.0) + } +} diff --git a/kernel/src/x86_64/cpuid.rs b/kernel/src/x86_64/cpuid.rs new file mode 100644 index 0000000..fed9655 --- /dev/null +++ b/kernel/src/x86_64/cpuid.rs @@ -0,0 +1,425 @@ +#![allow(clippy::identity_op)] + +use bit_field::BitField; +use bitflags::bitflags; + +pub struct CpuidResult { + pub eax: u32, + pub ebx: u32, + pub ecx: u32, + pub edx: u32, +} + +pub fn cpuid(eax: u32, ecx: u32) -> CpuidResult { + let result = core::arch::x86_64::__cpuid_count(eax, ecx); + + CpuidResult { + eax: result.eax, + ebx: result.ebx, + ecx: result.ecx, + edx: result.edx, + } +} + +pub struct CpuId { + max_eax: u32, + vendor_id: [u8; 12], +} + +pub struct Leaf1(CpuidResult); + +impl Leaf1 { + pub fn get() -> Self { + let result = cpuid(1, 0); + Self(result) + } + pub fn brand_index(&self) -> u8 { + self.0.ebx.get_bits(0..8) as u8 + } + pub fn clflush_line_size(&self) -> u8 { + self.0.ebx.get_bits(8..16) as u8 + } + pub fn max_logical_processors(&self) -> u8 { + self.0.ebx.get_bits(16..24) as u8 + } + pub fn initial_apic_id(&self) -> u8 { + self.0.ebx.get_bits(24..32) as u8 + } + + pub fn family_id(&self) -> u8 { + self.0.eax.get_bits(8..12) as u8 | ((self.0.eax.get_bits(20..28) as u8) << 4) + } + pub fn model_id(&self) -> u8 { + self.0.eax.get_bits(4..8) as u8 | ((self.0.eax.get_bits(16..20) as u8) << 4) + } + pub fn stepping_id(&self) -> u8 { + self.0.eax.get_bits(0..4) as u8 + } + pub fn processor_type(&self) -> u8 { + self.0.eax.get_bits(12..14) as u8 + } + pub fn flags(&self) -> Leaf1Flags { + Leaf1Flags::from_bits_truncate(self.0.ecx as u64 | ((self.0.edx as u64) << 32)) + } +} + +bitflags! { + pub struct Leaf1Flags: u64 { + const SSE3 = 1 << 0; + const PCLMULQDQ = 1 << 1; + const DTES64 = 1 << 2; + const MONITOR = 1 << 3; + const DS_CPL = 1 << 4; + const VMX = 1 << 5; + const SMX = 1 << 6; + const EIST = 1 << 7; + const TM2 = 1 << 8; + const SSSE3 = 1 << 9; + const CNXT_ID = 1 << 10; + const SDBG = 1 << 11; + const FMA = 1 << 12; + const CMPXCHG16B = 1 << 13; + const XTPR_UPDATE_CONTROL = 1 << 14; + const PDCM = 1 << 15; + const PCID = 1 << 17; + const DCA = 1 << 18; + const SSE4_1 = 1 << 19; + const SSE4_2 = 1 << 20; + const X2APIC = 1 << 21; + const MOVBE = 1 << 22; + const POPCNT = 1 << 23; + const TSC_DEADLINE_TIMER = 1 << 24; + const AESNI = 1 << 25; + const XSAVE = 1 << 26; + const OSXSAVE = 1 << 27; + const AVX = 1 <<28 ; + const F16C = 1 << 29; + const RDRAND = 1 << 30; + + const FPU = 1 << (0 + u32::BITS); + const VME = 1 << (1 + u32::BITS); + const DE = 1 << (2 + u32::BITS); + const PSE = 1 << (3 + u32::BITS); + const TSC = 1 << (4 + u32::BITS); + const MSR = 1 << (5 + u32::BITS); + const PAE = 1 << (6 + u32::BITS); + const MCE = 1 << (7 + u32::BITS); + const CX8 = 1 << (8 + u32::BITS); + const APIC = 1 << (9 + u32::BITS); + const SEP = 1 << (11 + u32::BITS); + const MTRR = 1 << (12 + u32::BITS); + const PGE = 1 << (13 + u32::BITS); + const MCA = 1 << (14 + u32::BITS); + const CMOV = 1 << (15 + u32::BITS); + const PAT = 1 << (16 + u32::BITS); + const PSE36 = 1 << (17 + u32::BITS); + const PSN = 1 << (18 + u32::BITS); + const CLFSH = 1 << (19 + u32::BITS); + const DS = 1 << (21 + u32::BITS); + const ACPI = 1 << (22 + u32::BITS); + const MMX = 1 << (23 + u32::BITS); + const FXSR = 1 << (24 + u32::BITS); + const SSE = 1 << (25 + u32::BITS); + const SSE2 = 1 << (26 + u32::BITS); + const SS = 1 << (27 + u32::BITS); + const HTT = 1 << (28 + u32::BITS); + const TM = 1 << (29 + u32::BITS); + const PBE = 1 << (31 + u32::BITS); + } +} + +pub struct Leaf7(CpuidResult); + +impl Leaf7 { + pub fn get() -> Self { + let result = cpuid(7, 0); + Self(result) + } + + pub fn subleaf1(&self) -> Option { + if self.max_subleaf() >= 1 { + let result = cpuid(7, 1); + Some(Leaf7Subleaf1(result)) + } else { + None + } + } + + pub fn max_subleaf(&self) -> u32 { + self.0.eax + } + pub fn flags(&self) -> Leaf7Flags { + Leaf7Flags::from_bits_truncate(unsafe { + core::mem::transmute::<[[u8; 4]; 4], u128>( + [self.0.ebx, self.0.ecx, self.0.edx, 0].map(u32::to_ne_bytes), + ) + }) + } + pub fn mawau(&self) -> u8 { + self.0.ecx.get_bits(17..22) as u8 + } +} + +bitflags! { + pub struct Leaf7Flags: u128{ + const FSGSBASE = 1 << 0; + const IA32_TSC_ADJUST_MSR = 1 << 1; + const SGX = 1 << 2; + const BMI1 = 1 << 3; + const HLE = 1 << 4; + const AVX2 = 1 << 5; + const SMEP = 1 << 7; + const BMI2 = 1 << 8; + const ERMS = 1 << 9; + const INVPCID = 1 << 10; + const RTM = 1 << 11; + const PQM = 1 << 12; + const FPU_CS_DS_DEPRECATION = 1 << 13; + const MPX = 1 << 14; + const PQE = 1 << 15; + const AVX512F = 1 << 16; + const AVX512DQ = 1 << 17; + const RDSEED = 1 << 18; + const ADX = 1 << 19; + const SMAP = 1 << 20; + const AVX512IFMA = 1 << 21; + const PCOMMIT = 1 << 22; + const CLFLUSHOPT = 1 << 23; + const CLWB = 1 <<24 ; + const INTEL_PT = 1 << 25; + const AVX512PF = 1 << 26; + const AVX512ER = 1 << 27; + const AVX512CD = 1 << 28; + const SHA = 1 << 29; + const AVX512BW = 1 << 30; + const AVX512VL = 1 << 31; + + const PREFETCHWT1 = 1 << (0 + u32::BITS); + const AVX512VBMI = 1 << (1 + u32::BITS); + const UMIP = 1 << (2 + u32::BITS); + const PKU = 1 << (3 + u32::BITS); + const OSPKE = 1 << (4 + u32::BITS); + const RDPID = 1 << (22 + u32::BITS); + const SGX_LC = 1 << (30 + u32::BITS); + + const SGX_KEYS = 1 << (1 + 2 * u32::BITS); + const AVX512_4VNNIW = 1 << (2 + 2 * u32::BITS); + const AVX512_4FMAPS = 1 << (3 + 2 * u32::BITS); + const FSRM = 1 << (4 + 2 * u32::BITS); + const UINTR = 1 << (5 + 2 * u32::BITS); + } +} + +pub struct Leaf7Subleaf1(CpuidResult); + +impl Leaf7Subleaf1 { + pub fn flags(&self) -> Leaf7Subleaf1Flags { + Leaf7Subleaf1Flags::from_bits_truncate(unsafe { + core::mem::transmute::<[u32; 4], u128>([self.0.eax, self.0.ebx, self.0.ecx, self.0.edx]) + }) + } +} + +// in eax:ebx:ecx:edx order +bitflags! { + pub struct Leaf7Subleaf1Flags: u128 { + const SHA512 = 1 << 0; + const SM3 = 1 << 1; + const SM4 = 1 << 2; + const RAO_INT = 1 << 3; + const AVX_VNNI = 1 << 4; + const AVX512_BF16 = 1 << 5; + const LASS = 1 << 6; + const CMPCCXADD = 1 << 7; + const ARCHPERFMONTEXT = 1 << 8; + const FZRM = 1 << 10; + const FSRS = 1 << 11; + const RSRCS = 1 << 12; + const FRED = 1 << 17; + const LKGS = 1 << 18; + const WRMSRNS = 1 << 19; + const NMI_SRC = 1 << 20; + const AMX_FP16 = 1 << 21; + const HRESET = 1 << 22; + const AVX_IFMA = 1 << 23; + const LAM = 1 << 26; + const MSRLIST = 1 << 27; + const INVD_DISABLE_POST_BIOS_DONE = 1 << 30; + const MOVRS = 1 << 31; + + const PPINSTR = 1 << (0 + u32::BITS); + const PBNDKB = 1 << (1 + u32::BITS); + const CPUIDMAXVAL_LIM_RMV = 1 << (2 + u32::BITS); + + const RDT_M_ASYM = 1 << (0 + 2 * u32::BITS); + const RDT_A_ASYM = 1 << (1 + 2 * u32::BITS); + const MSR_IMM = 1 << (2 + 2 * u32::BITS); + const ACE = 1 << (3 + 2 * u32::BITS); + + const AVX_VNNI_INT8 = 1 << (4 + 3 * u32::BITS); + const AVX_NE_CONVERT = 1 << (5 + 3 * u32::BITS); + const AMX_COMPLEX = 1 << (8 + 3 * u32::BITS); + const AVX_VNNI_INT16 = 1 << (10 + 3 * u32::BITS); + const UTMR = 1 << (13 + 3 * u32::BITS); + const PREFETCHI = 1 << (14 + 3 * u32::BITS); + const USER_MRS = 1 << (15 + 3 * u32::BITS); + const UIRET_UIF_FROM_RFLAGS = 1 << (17 + 3 * u32::BITS); + const CET_SSS = 1 << (18 + 3 * u32::BITS); + const AVX10 = 1 << (19 + 3 * u32::BITS); + const APX_F = 1 << (21 + 3 * u32::BITS); + const SEC_TEE_ATTESTATION = 1 << (22 + 3 * u32::BITS); + const MWAIT = 1 << (23 + 3 * u32::BITS); + const SLSM = 1 << (24 + 3 * u32::BITS); + } +} + +bitflags! { + pub struct Leaf8000001Flags: u64 { + const FPU = 1 << 0; + const VME = 1 << 1; + const DE = 1 << 2; + const PSE = 1 << 3; + const TSC = 1 << 4; + const MSR = 1 << 5; + const PAE = 1 << 6; + const MCE = 1 << 7; + const CX8 = 1 << 8; + const APIC = 1 << 9; + const SYSCALL_K9 = 1 << 10; + const SYSCALL = 1 << 11; + const MTRR = 1 << 12; + const PGE = 1 << 13; + const MCA = 1 << 14; + const CMOV = 1 << 15; + const PAT = 1 << 16; + const PSE36 = 1 << 17; + const ECC_K9 = 1 << 18; + const ECC = 1 << 19; + const NX = 1 << 20; + const MMXEXT = 1 << 22; + const MMX = 1 << 23; + const FXSR_OPT = 1 << 24; + const PDPE1GB = 1 << 26; + const RDTSCP = 1 << 27; + const REX32 = 1 << 28; + const LM = 1 << 29; + const _3DNOWEXT = 1 << 30; + const _3DNOW = 1 << 31; + + const LAHF_LM = 1 << (0 + u32::BITS); + const CMP_LEGACY = 1 << (1 + u32::BITS); + const SVM = 1 << (2 + u32::BITS); + const EXTAPIC = 1 << (3 + u32::BITS); + const CR8_LEGACY = 1 << (4 + u32::BITS); + const LZCNT = 1 << (5 + u32::BITS); + const SSE4A = 1 << (6 + u32::BITS); + const MISALIGNSSE = 1 << (7 + u32::BITS); + const _3DNOWPREFETCH = 1 << (8 + u32::BITS); + const OSVW = 1 << (9 + u32::BITS); + const IBS = 1 << (10 + u32::BITS); + const XOP = 1 << (11 + u32::BITS); + const SKINIT = 1 << (12 + u32::BITS); + const WDT = 1 << (13 + u32::BITS); + const LWP = 1 << (15 + u32::BITS); + const FMA4 = 1 << (16 + u32::BITS); + const TCE = 1 << (17 + u32::BITS); + const NODEID_MSR = 1 << (19 + u32::BITS); + const TBM = 1 << (21 + u32::BITS); + const TOPOEXT = 1 << (22 + u32::BITS); + const PERFCTR_CORE = 1 << (23 + u32::BITS); + const PERFCTR_NB = 1 << (24 + u32::BITS); + const DBX = 1 << (26 + u32::BITS); + const PERFTSC = 1 << (27 + u32::BITS); + const PCX_L2I = 1 << (28 + u32::BITS); + const MONITORX = 1 << (29 + u32::BITS); + const ADDR_MASK_EXT = 1 << (30 + u32::BITS); + } +} + +pub struct Leaf8000008(CpuidResult); + +impl Leaf8000008 { + pub fn physical_address_bits(&self) -> u8 { + self.0.eax.get_bits(0..8) as u8 + } + pub fn num_linear_address_bits(&self) -> u8 { + self.0.eax.get_bits(8..16) as u8 + } + pub fn gest_physical_address_bits(&self) -> u8 { + self.0.eax.get_bits(16..24) as u8 + } + + pub fn num_physical_threads(&self) -> u8 { + self.0.ecx.get_bits(0..8) as u8 - 1 + } + pub fn apic_id_size(&self) -> u8 { + self.0.ecx.get_bits(12..16) as u8 + } + pub fn performance_timestamp_counter_size(&self) -> u8 { + self.0.ecx.get_bits(16..18) as u8 + } + pub fn max_invlpgb_page_count(&self) -> u16 { + self.0.edx.get_bits(0..16) as u16 + } + + pub fn max_rdpru_ecx(&self) -> u16 { + self.0.edx.get_bits(16..32) as u16 + } + + pub fn flags(&self) -> Leaf8000008Flags { + Leaf8000008Flags::from_bits_truncate(self.0.ebx) + } +} + +bitflags! { + pub struct Leaf8000008Flags: u32 { + const CLZERO = 1 << 0; + const RETIRED_INSTR = 1 << 1; + const XRSTOR_FP_ERR = 1 << 2; + const INVLPGB = 1 << 3; + const RDPRU = 1 << 4; + const XOTEXT = 1 << 5; + const MBE = 1 << 6; + const MCOMMIT = 1 << 8; + const WBNOINVD = 1 << 9; + const LBR_EXT_V1 = 1 << 10; + const IBPB = 1 << 12; + const WBINVD_INT = 1 << 13; + const IBRS = 1 << 14; + const STIBP = 1 << 15; + const IBRS_ALWAYS = 1 << 16; + const STIBP_ALWAYS = 1 << 17; + const IBRS_PREFERRED = 1 << 18; + const IBRS_SAME_MODE_PROT = 1 << 19; + const NO_EFER_LMSLE = 1 << 20; + const INVLPGB_NESTED = 1 << 21; + const LBR_TSX = 1 << 22; + const PPIN = 1 << 23; + const SSBD = 1 << 24; + const SSBD_LEGACY = 1 << 25; + const SSBD_NO = 1 << 26; + const CPPC = 1 << 27; + const PSFD = 1 << 28; + const BTC_NO = 1 << 29; + const IBPB_RET = 1 << 30; + const BRANCH_SAPLING = 1 << 31; + } +} + +impl CpuId { + pub fn get() -> Self { + let result = cpuid(0, 0); + let vendor_id = unsafe { + core::mem::transmute::<[u32; 3], [u8; 12]>([result.ebx, result.edx, result.ecx]) + }; + + Self { + max_eax: result.eax, + vendor_id, + } + } + + pub fn vendor_id(&self) -> &[u8; 12] { + &self.vendor_id + } +} diff --git a/kernel/src/x86_64/gdt.rs b/kernel/src/x86_64/gdt.rs index 86d7b1c..8ed2a14 100644 --- a/kernel/src/x86_64/gdt.rs +++ b/kernel/src/x86_64/gdt.rs @@ -514,5 +514,3 @@ pub static TSS: LazyLock = LazyLock::new(|| { tss }); - -pub static GDT: LazyLock = LazyLock::new(GlobalDescriptorTable::new); diff --git a/kernel/src/x86_64/idt.rs b/kernel/src/x86_64/idt.rs index 6bcb3db..7bb6c33 100644 --- a/kernel/src/x86_64/idt.rs +++ b/kernel/src/x86_64/idt.rs @@ -4,7 +4,7 @@ use bit_field::BitField; use crate::{ serial_println, - x86_64::{RFlags, Registers}, + x86_64::{RFlags, Registers, halt_loop}, }; #[derive(Clone, Copy)] @@ -507,13 +507,24 @@ extern "C" fn global_interrupt_handler( error_code: u64, registers: &Registers, ) { - crate::serial_println!( + serial_println!( "Interrupt {} occurred! Error code: {:#x}, Frame: {:#?}, Registers: {:#?}", index, error_code, frame, registers ); + + match index { + ExceptionVector::BREAKPOINT => { + serial_println!("Breakpoint interrupt handled successfully."); + } + ExceptionVector::PAGE_FAULT => { + serial_println!("Page fault occurred! Error code: {:#x}", error_code); + halt_loop() + } + _ => {} + } } #[cfg(test)] diff --git a/kernel/src/x86_64/paging.rs b/kernel/src/x86_64/paging.rs index fcd7321..640e0bf 100644 --- a/kernel/src/x86_64/paging.rs +++ b/kernel/src/x86_64/paging.rs @@ -1,35 +1,241 @@ +use core::{ + borrow::Borrow, + fmt::Debug, + hint::unlikely, + ops::{Deref, Index}, +}; + use bit_field::BitField; +use crate::{ + memory::{PhyAddr, VirtAddr, VirtAddrTranslationExt}, + serial_println, + x86_64::{VirtAddrExt, registers::Cr4}, +}; + #[repr(C, align(4096))] -pub struct PageTable { - entries: [Entry; 512], +pub struct PageTable { + entries: [PageTableEntry; 512], +} + +impl PageTable { + pub fn get(&self, index: u16) -> Option { + if index < 512 { + let entry = self.entries[index as usize]; + + if entry.present() { Some(entry) } else { None } + } else { + None + } + } + pub fn get_unchecked(&self, index: u16) -> PageTableEntry { + assert!(index < 512, "Page table index out of bounds"); + self.entries[index as usize] + } +} + +impl Index for PageTable { + type Output = PageTableEntry; + + fn index(&self, index: u16) -> &Self::Output { + assert!(index < 512, "Page table index out of bounds"); + &self.entries[index as usize] + } +} + +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct PageTableEntry(PageTableEntryFlags); + +impl Debug for PageTableEntry { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("PageTableEntry") + .field( + "flags", + &PageTableEntryFlags::from_bits_truncate(self.as_raw()), + ) + .field("phy", &self.phy()) + .field("pk", &self.pk()) + .field("pat_index", &self.pat_index()) + .field("free_bits", &format_args!("{:#b}", self.free_bits())) + .finish() + } +} + +impl AsRef for PageTableEntry { + fn as_ref(&self) -> &PageTableEntryFlags { + &self.0 + } +} + +impl Borrow for PageTableEntry { + fn borrow(&self) -> &PageTableEntryFlags { + &self.0 + } +} + +impl Deref for PageTableEntry { + type Target = PageTableEntryFlags; + + fn deref(&self) -> &Self::Target { + &self.0 + } } bitflags::bitflags! { #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct PageTableEntryFlags: u64 { const PRESENT = 1 << 0; const WRITABLE = 1 << 1; const USER_ACCESSIBLE = 1 << 2; - const WRITE_THROUGH = 1 << 3; - const NO_CACHE = 1 << 4; + const WRITE_THROUGH = 1 << 3; // PWT + const NO_CACHE = 1 << 4; // PCD const ACCESSED = 1 << 5; const DIRTY = 1 << 6; const HUGE_PAGE = 1 << 7; const GLOBAL = 1 << 8; const NO_EXECUTE = 1 << 63; + const PAT_4K = 1 << 7; + const PAT_HUGE = 1 << 12; } } -impl PageTableEntryFlags { +impl PageTableEntry { pub fn from_raw(bits: u64) -> Self { - Self::from_bits_retain(bits) + Self(PageTableEntryFlags::from_bits_retain(bits)) } pub fn as_raw(&self) -> u64 { - self.bits() + self.0.bits() + } + pub fn as_mut_raw(&mut self) -> &mut u64 { + self.0.0.bits_mut() + } + pub fn present(&self) -> bool { + self.contains(PageTableEntryFlags::PRESENT) + } + pub fn phy(&self) -> PhyAddr { + PhyAddr(self.as_raw().get_bits(12..52) << 12) + } + pub fn try_as_page_table(&self) -> Option<&PageTable> { + if !self.contains(PageTableEntryFlags::PRESENT) { + return None; + } + if self.contains(PageTableEntryFlags::HUGE_PAGE) { + return None; + } + + unsafe { + Some( + self.phy() + .into_hhdm_virt() + .as_ptr::() + .as_ref() + .unwrap_unchecked(), + ) + } + } + pub unsafe fn as_page_table(&self) -> &PageTable { + // entry must be present + assert!(self.contains(PageTableEntryFlags::PRESENT)); + // if the entry is a huge page, it does not point to a deeper page table. + assert!(!self.contains(PageTableEntryFlags::HUGE_PAGE)); + + unsafe { + self.phy() + .into_hhdm_virt() + .as_ptr::() + .as_ref() + .unwrap_unchecked() + } + } + pub fn pk(&self) -> u8 { + self.as_raw().get_bits(59..63) as u8 + } + pub fn pat_index(&self) -> u8 { + let pat = if self.contains(PageTableEntryFlags::HUGE_PAGE) { + self.as_raw().get_bit(12) as u8 + } else { + self.as_raw().get_bit(7) as u8 + }; + + self.as_raw().get_bits(3..=4) as u8 | (pat << 2) } - pub fn phy(&self) -> u64 { - self.bits().get_bits(12..52) << 12 + /// Returns the free bits in the page table entry, which are bits 9-11 and + /// 52-58, combined into a single 10-bit value. + pub fn free_bits(&self) -> u16 { + let low = self.bits().get_bits(9..12) as u16; + let high = self.bits().get_bits(52..59) as u16; + low | (high << 3) + } + pub fn set_free_bits(&mut self, value: u16) { + let low = (value & 0b111) as u64; + let high = (value >> 3) as u64; + self.as_mut_raw().set_bits(9..12, low); + self.as_mut_raw().set_bits(52..59, high); } } + +impl VirtAddrTranslationExt for VirtAddr { + fn into_phy_addr(self) -> Option { + get_physical_addr(self) + } +} + +pub fn get_physical_addr(virt: VirtAddr) -> Option { + let cr3 = crate::x86_64::registers::Cr3::read(); + let cr4 = crate::x86_64::registers::Cr4::read(); + serial_println!("cr3: {:?}", cr3); + serial_println!("cr4: {:?}", cr4); + + let frame = cr3.phy(); + let page_table = unsafe { + frame + .into_hhdm_virt() + .as_ptr::() + .as_ref() + .unwrap_unchecked() + }; + + let pml4_entry = if unlikely(cr4.contains(Cr4::LA57)) { + let l5_entry = page_table[virt.page_table_index::<{ VirtAddr::PML5 }>()]; + + l5_entry + .try_as_page_table()? + .get_unchecked(virt.page_table_index::<{ VirtAddr::PML4 }>()) + } else { + page_table[virt.page_table_index::<{ VirtAddr::PML4 }>()] + }; + + serial_println!("pml4_entry: {:?}", pml4_entry); + + let pdpt_entry = pml4_entry.try_as_page_table()?[virt.page_table_index::<{ VirtAddr::PDPT }>()]; + serial_println!("pdpt_entry: {:?}", pdpt_entry); + + if pdpt_entry.contains(PageTableEntryFlags::HUGE_PAGE) { + const PHY_MASK_1G: u64 = !((1 << 30) - 1); + let phys_addr = (pdpt_entry.phy().0 & PHY_MASK_1G) + virt.offset_1g() as u64; + return Some(PhyAddr(phys_addr)); + } + + let pd_entry = pdpt_entry.try_as_page_table()?[virt.page_table_index::<{ VirtAddr::PD }>()]; + serial_println!("pd_entry: {:?}", pd_entry); + + if pd_entry.contains(PageTableEntryFlags::HUGE_PAGE) { + const PHY_MASK_2M: u64 = !((1 << 21) - 1); + let phys_addr = (pd_entry.phy().0 & PHY_MASK_2M) + virt.offset_2m() as u64; + return Some(PhyAddr(phys_addr)); + } + + let pt_entry = pd_entry.try_as_page_table()?[virt.page_table_index::<{ VirtAddr::PT }>()]; + serial_println!("pt_entry: {:?}", pt_entry); + + if !pt_entry.contains(PageTableEntryFlags::PRESENT) { + return None; + } + + let phy_addr = pt_entry.phy().into_hhdm_virt().0 + virt.offset_4k() as u64; + + Some(PhyAddr(phy_addr)) +} diff --git a/kernel/src/x86_64/registers.rs b/kernel/src/x86_64/registers.rs index 08e2854..8b08d45 100644 --- a/kernel/src/x86_64/registers.rs +++ b/kernel/src/x86_64/registers.rs @@ -1,4 +1,9 @@ -use core::{arch::asm, ops::Index}; +use core::{arch::asm, fmt::Debug, ops::Index}; + +use bit_field::BitField; +use bitflags::bitflags; + +use crate::memory::PhyAddr; bitflags::bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -35,30 +40,31 @@ impl Cr0Flags { bitflags::bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub struct Cr4Flags: u64 { + pub struct Cr4: u64 { const VIRTUAL_8086_MODE_EXTENSIONS = 1 << 0; const PROTECTED_MODE_VIRTUAL_INTERRUPTS = 1 << 1; const TIME_STAMP_DISABLE = 1 << 2; const DEBUGGING_EXTENSIONS = 1 << 3; const PAGE_SIZE_EXTENSIONS = 1 << 4; const PHYSICAL_ADDRESS_EXTENSION = 1 << 5; - const MACHINE_CHECK_ENABLE = 1 << 6; - const PAGE_GLOBAL_ENABLE = 1 << 7; - const PERFORMANCE_MONITOR_COUNTER_ENABLE = 1 << 8; + const MACHINE_CHECK = 1 << 6; + const PAGE_GLOBAL = 1 << 7; + const PERFORMANCE_MONITOR_COUNTER = 1 << 8; const OSFXSR_SUPPORT = 1 << 9; const OSXMMEXCPT_SUPPORT = 1 << 10; const USER_MODE_INSTRUCTION_PREVENTION = 1 << 11; - const VMX_ENABLE = 1 << 13; - const SMX_ENABLE = 1 << 14; - const FSGSBASE_ENABLE = 1 << 16; - const PCID_ENABLE = 1 << 17; - const OSXSAVE_ENABLE = 1 << 18; - const SMEP_ENABLE = 1 << 20; - const SMAP_ENABLE = 1 << 21; + const LA57 = 1 << 12; + const VMX = 1 << 13; + const SMX = 1 << 14; + const FSGSBASE = 1 << 16; + const PCID = 1 << 17; + const OSXSAVE = 1 << 18; + const SMEP = 1 << 20; + const SMAP = 1 << 21; } } -impl Cr4Flags { +impl Cr4 { pub fn read() -> Self { let value: u64; unsafe { @@ -100,13 +106,44 @@ impl IA32EferFlags { pub struct IA32Pat(u64); +const impl Default for IA32Pat { + fn default() -> Self { + Self::from_entries([ + IA32PatEntry::WRITE_BACK, + IA32PatEntry::WRITE_THROUGH, + IA32PatEntry::UNCACHEABLE, + IA32PatEntry::UNCACHED, + IA32PatEntry::WRITE_BACK, + IA32PatEntry::WRITE_THROUGH, + IA32PatEntry::UNCACHEABLE, + IA32PatEntry::UNCACHED, + ]) + } +} + impl IA32Pat { - pub fn from_entries(entries: [IA32PatEntry; 8]) -> Self { - let mut value = 0u64; - for (i, entry) in entries.iter().enumerate() { - value |= (entry.0 as u64) << (i * 8); - } - Self(value) + pub const fn from_entries(entries: [IA32PatEntry; 8]) -> Self { + let [ + IA32PatEntry(e0), + IA32PatEntry(e1), + IA32PatEntry(e2), + IA32PatEntry(e3), + IA32PatEntry(e4), + IA32PatEntry(e5), + IA32PatEntry(e6), + IA32PatEntry(e7), + ] = entries; + + let val = e0 as u64 + | ((e1 as u64) << 8) + | ((e2 as u64) << 16) + | ((e3 as u64) << 24) + | ((e4 as u64) << 32) + | ((e5 as u64) << 40) + | ((e6 as u64) << 48) + | ((e7 as u64) << 56); + + Self(val) } pub fn get(&self, index: u8) -> IA32PatEntry { assert!(index < 8, "Index out of bounds for IA32Pat"); @@ -131,3 +168,60 @@ impl IA32PatEntry { pub const WRITE_BACK: Self = Self(6); pub const UNCACHED: Self = Self(7); } + +pub struct Cr3(u64); + +impl Debug for Cr3 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Cr3") + .field("phy", &self.phy()) + .field("flags", &self.flags()) + .field("free_bits", &format_args!("{:#x}", self.free_bits())) + .finish() + } +} + +bitflags! { + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct Cr3Flags: u64 { + const PAGE_LEVEL_WRITE_THROUGH = 1 << 3; + const PAGE_LEVEL_CACHE_DISABLE = 1 << 4; + } +} +impl Cr3 { + pub fn read() -> Self { + let value: u64; + unsafe { + asm!("mov {}, cr3", out(reg) value, options(nomem, nostack, preserves_flags)); + } + Self(value) + } + pub unsafe fn write(&self) { + unsafe { + asm!("mov cr3, {}", in(reg) self.0, options(nomem, nostack, preserves_flags)); + } + } + + pub fn flags(&self) -> Cr3Flags { + Cr3Flags::from_bits_truncate(self.0) + } + pub fn set_flags(&mut self, flags: Cr3Flags) { + const MASK: u64 = Cr3Flags::all().bits(); + self.0 = (self.0 & !MASK) | flags.bits(); + } + pub fn phy(&self) -> PhyAddr { + PhyAddr(self.0.get_bits(12..52) << 12) + } + pub fn free_bits(&self) -> u16 { + let low = (self.0 & 0x7) as u16; + let high = self.0.get_bits(5..12) as u16; + low | (high << 3) + } + pub fn set_free_bits(&mut self, value: u16) { + let low = (value & 0x7) as u64; + let high = (value >> 3) as u64; + self.0.set_bits(0..3, low); + self.0.set_bits(5..12, high); + } +} diff --git a/kernel/tests/stack_overflow.rs b/kernel/tests/stack_overflow.rs index ac84138..a38b14a 100644 --- a/kernel/tests/stack_overflow.rs +++ b/kernel/tests/stack_overflow.rs @@ -7,11 +7,13 @@ use core::{cell::UnsafeCell, mem::offset_of}; use kernel::{ sync::LazyLock, x86_64::{ - gdt::{DF_STACK, GDT, GlobalDescriptorTable, RING0}, + gdt::{DF_STACK, GlobalDescriptorTable, RING0}, idt::{self, Entry, InterruptDescriptorTable}, }, }; +pub static GDT: LazyLock = LazyLock::new(GlobalDescriptorTable::new); + #[unsafe(export_name = "_start")] pub extern "C" fn main() -> ! { kernel::serial_println!("Hello, world!");