diff --git a/Cargo.lock b/Cargo.lock index ea0aea6..7d62e49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,14 +20,28 @@ version = "0.1.0" dependencies = [ "bit_field", "bitflags", + "plain", "rbtree", + "rustc-demangle", "seq-macro", ] +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "rbtree" version = "0.1.0" +[[package]] +name = "rustc-demangle" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" + [[package]] name = "seq-macro" version = "0.3.6" diff --git a/kernel/.cargo/config.toml b/kernel/.cargo/config.toml index 6c2340e..1bdbcb2 100644 --- a/kernel/.cargo/config.toml +++ b/kernel/.cargo/config.toml @@ -7,4 +7,7 @@ build-std = ["core", "alloc", "compiler_builtins"] target = "x86_64-unknown-kernel.json" [target.x86_64-unknown-kernel] -runner = "./run.sh" \ No newline at end of file +runner = "./run.sh" +rustflags = [ + "-C", "force-frame-pointers=yes", +] \ No newline at end of file diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 00ccbbb..dddd093 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -26,4 +26,6 @@ bit_field = "0.10.3" bitflags = "2.13.1" seq-macro = "0.3.6" -rbtree = { path = "../crates/rbtree" } \ No newline at end of file +rbtree = { path = "../crates/rbtree" } +plain = "0.2.3" +rustc-demangle = "0.1.28" diff --git a/kernel/kernel.lds b/kernel/kernel.lds index c3318ef..79a36f9 100644 --- a/kernel/kernel.lds +++ b/kernel/kernel.lds @@ -10,6 +10,8 @@ PHDRS { SECTIONS { . = 0xffffffff80000000; + __kernel_start = .; + .limine_requests : { KEEP(*(.limine_requests_start)) KEEP(*(.limine_requests)) @@ -36,6 +38,16 @@ SECTIONS { *(COMMON) } :data + __kernel_end = .; + + .symtab 0: { + *(.symtab) + } + + .strtab 0: { + *(.strtab) + } + /DISCARD/ : { *(.eh_frame*) *(.note .note.*) diff --git a/kernel/src/limine.rs b/kernel/src/limine.rs index e64b4f1..64407ed 100644 --- a/kernel/src/limine.rs +++ b/kernel/src/limine.rs @@ -1,4 +1,4 @@ -use core::{cell::UnsafeCell, fmt::Debug, ptr::NonNull}; +use core::{cell::UnsafeCell, ffi::CStr, fmt::Debug, ptr::NonNull}; #[repr(C)] pub struct BaseRevision(UnsafeCell<[u64; 3]>); @@ -272,3 +272,60 @@ impl HhdmRequest { self.response().map(|response| response.response.offset) } } + +#[repr(C)] +#[derive(Debug)] +pub struct ExecutableFileResponse { + file: *const LimineFile, +} + +unsafe impl Send for ExecutableFileResponse {} +unsafe impl Sync for ExecutableFileResponse {} + +pub type ExecutableFileRequest = Request; + +impl ExecutableFileRequest { + const ID: [u64; 2] = [0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69]; + + pub const fn new() -> Self { + Self::new_raw(Self::ID, 0, ()) + } + + pub fn file(&self) -> Option<&LimineFile> { + self.response() + .and_then(|response| unsafe { response.response.file.as_ref() }) + } +} + +#[repr(C)] +#[derive(Debug)] +pub struct LimineFile { + pub revision: u64, + pub addr: *const u8, + pub length: u64, + pub path: *const i8, + pub string: *const i8, + pub media_type: u32, + pub _padding: [u8; 4], + pub tftp_ipv4_addr: [u8; 4], + pub tftp_port: u32, + pub partition_index: u32, + pub mbr_disk_id: u32, + pub gpt_disk_uuid: [u8; 16], + pub gpt_part_uuid: [u8; 16], + pub part_uuid: [u8; 16], +} + +impl LimineFile { + pub const MEDIA_TYPE_GENERIC: u32 = 0; + pub const MEDIA_TYPE_OPTICAL: u32 = 1; + pub const MEDIA_TYPE_TFTP: u32 = 2; + + pub fn bytes(&self) -> &'static [u8] { + unsafe { core::slice::from_raw_parts(self.addr, self.length as usize) } + } +} + +#[used] +#[unsafe(link_section = ".limine_requests")] +pub static EXECUTABLE_FILE_REQUEST: ExecutableFileRequest = ExecutableFileRequest::new(); diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 0e28023..9ba7f6d 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -56,6 +56,8 @@ extern "C" fn _start() -> ! { GDT.load(); IDT.load(); + unsafe { (0 as *mut u8).write_volatile(0x42) }; + kernel::serial_println!("entry point: 0x{:x}", _start as *const () as usize); kernel::serial_println!( "entry point phy: {:?}", diff --git a/kernel/src/memory.rs b/kernel/src/memory.rs index 9203bf0..1b55fd3 100644 --- a/kernel/src/memory.rs +++ b/kernel/src/memory.rs @@ -7,8 +7,11 @@ use crate::{serial_println, sync::OnceLock, x86_64::PAGE_SIZE}; pub static HHDM_BASE: OnceLock = OnceLock::new(); -pub trait VirtAddrTranslationExt { +pub trait VirtAddrTranslationExt: Sized { fn into_phy_addr(self) -> Option; + fn is_mapped(self) -> bool { + self.into_phy_addr().is_some() + } } #[derive(Clone, Copy, PartialEq, Eq)] @@ -51,6 +54,12 @@ impl Debug for PhyAddr { } } +impl From for VirtAddr { + fn from(value: u64) -> Self { + VirtAddr(value) + } +} + impl From<&T> for VirtAddr { fn from(ptr: &T) -> Self { VirtAddr(ptr as *const T as u64) diff --git a/kernel/src/x86_64.rs b/kernel/src/x86_64.rs index bfeff7b..7b9b121 100644 --- a/kernel/src/x86_64.rs +++ b/kernel/src/x86_64.rs @@ -1,5 +1,6 @@ #![cfg(target_arch = "x86_64")] +pub mod backtrace; pub mod cpuid; pub mod gdt; pub mod idt; @@ -212,3 +213,67 @@ impl VirtAddrExt for VirtAddr { E::decompose(self.0) } } + +pub struct Context { + pub registers: Registers, + pub rip: u64, + pub rflags: RFlags, + pub rsp: u64, + /// The code segment selector. + pub cs: u16, +} + +impl core::fmt::Display for Context { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + writeln!(f, "RIP - {:>#018x} \tRSP - {:>#018x}", self.rip, self.rsp)?; + writeln!( + f, + "FLAGS - {:>#018x} \tCS - {:>#018x}", + self.rflags.bits(), + self.cs + )?; + + writeln!( + f, + "RAX: {:>#018x} \tRCX: {:>#018x}", + self.registers.rax, self.registers.rcx + )?; + writeln!( + f, + "RDX: {:>#018x} \tRBX: {:>#018x}", + self.registers.rdx, self.registers.rbx + )?; + writeln!( + f, + "RSP: {:>#018x} \tRBP: {:>#018x}", + self.rsp, self.registers.rbp + )?; + writeln!( + f, + "RSI: {:>#018x} \tRDI: {:>#018x}", + self.registers.rsi, self.registers.rdi + )?; + writeln!( + f, + "R8 : {:>#018x} \tR9 : {:>#018x}", + self.registers.r8, self.registers.r9 + )?; + writeln!( + f, + "R10: {:>#018x} \tR11: {:>#018x}", + self.registers.r10, self.registers.r11 + )?; + writeln!( + f, + "R12: {:>#018x} \tR13: {:>#018x}", + self.registers.r12, self.registers.r13 + )?; + writeln!( + f, + "R14: {:>#018x} \tR15: {:>#018x}", + self.registers.r14, self.registers.r15 + )?; + + Ok(()) + } +} diff --git a/kernel/src/x86_64/backtrace.rs b/kernel/src/x86_64/backtrace.rs new file mode 100644 index 0000000..4b551d0 --- /dev/null +++ b/kernel/src/x86_64/backtrace.rs @@ -0,0 +1,495 @@ +use core::ffi::{CStr, c_void}; + +use crate::{ + limine::LimineFile, + memory::{VirtAddr, VirtAddrTranslationExt}, + serial_println, + sync::LazyLock, + x86_64::{Context, VirtAddrExt}, +}; + +unsafe extern "C" { + static __kernel_start: c_void; + static __kernel_end: c_void; +} + +pub struct Frame { + pub rip: u64, + pub rbp: u64, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Elf64SymBind { + Local = 0, + Global = 1, + Weak = 2, + Loos = 10, + Hios = 12, + Loproc = 13, + Hiproc = 15, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Elf64SymType { + Notype = 0, + Object = 1, + Func = 2, + Section = 3, + File = 4, + Common = 5, + Tls = 6, + Loos = 10, + Hios = 12, + Loproc = 13, + Hiproc = 15, +} + +#[repr(C)] +#[derive(Debug)] +struct Elf64Sym { + name: u32, + info: u8, + other: u8, + shndx: u16, + value: u64, + size: u64, +} + +unsafe impl plain::Plain for Elf64Sym {} + +#[repr(u32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum SectionHeaderType { + Null = 0, + Progbits = 1, + Symtab = 2, + Strtab = 3, + Rela = 4, + Hash = 5, + Dynamic = 6, + Note = 7, + Nobits = 8, + Rel = 9, + Shlib = 10, + Dynsym = 11, + InitArray = 14, + FiniArray = 15, + PreinitArray = 16, + Group = 17, + SymtabShndx = 18, + Num = 19, + GnuAttributes = 0x6ffffff5, + GnuHash = 0x6ffffff6, + GnuLiblist = 0x6ffffff7, + Checksum = 0x6ffffff8, + Losunwind = 0x6ffffffa, + Amd64Unwind = 0x7000001, + OsSpecific(u32), + ProcSpecific(u32), + UserSpecific(u32), + Unknown = 0xffffffff, +} + +impl SectionHeaderType { + fn from_u32(value: u32) -> Self { + match value { + 0 => Self::Null, + 1 => Self::Progbits, + 2 => Self::Symtab, + 3 => Self::Strtab, + 4 => Self::Rela, + 5 => Self::Hash, + 6 => Self::Dynamic, + 7 => Self::Note, + 8 => Self::Nobits, + 9 => Self::Rel, + 10 => Self::Shlib, + 11 => Self::Dynsym, + 14 => Self::InitArray, + 15 => Self::FiniArray, + 16 => Self::PreinitArray, + 17 => Self::Group, + 18 => Self::SymtabShndx, + 19 => Self::Num, + 0x6ffffff5 => Self::GnuAttributes, + 0x6ffffff6 => Self::GnuHash, + 0x6ffffff7 => Self::GnuLiblist, + 0x6ffffff8 => Self::Checksum, + 0x6ffffffa => Self::Losunwind, + 0x7000001 => Self::Amd64Unwind, + 0x60000000..=0x6fffffff => Self::OsSpecific(value), + 0x70000000..=0x7fffffff => Self::ProcSpecific(value), + 0x80000000..=0x8fffffff => Self::UserSpecific(value), + _ => Self::Unknown, + } + } + + fn into_u32(self) -> u32 { + match self { + Self::Null => 0, + Self::Progbits => 1, + Self::Symtab => 2, + Self::Strtab => 3, + Self::Rela => 4, + Self::Hash => 5, + Self::Dynamic => 6, + Self::Note => 7, + Self::Nobits => 8, + Self::Rel => 9, + Self::Shlib => 10, + Self::Dynsym => 11, + Self::InitArray => 14, + Self::FiniArray => 15, + Self::PreinitArray => 16, + Self::Group => 17, + Self::SymtabShndx => 18, + Self::Num => 19, + Self::GnuAttributes => 0x6ffffff5, + Self::GnuHash => 0x6ffffff6, + Self::GnuLiblist => 0x6ffffff7, + Self::Checksum => 0x6ffffff8, + Self::Losunwind => 0x6ffffffa, + Self::Amd64Unwind => 0x7000001, + Self::OsSpecific(value) | Self::ProcSpecific(value) | Self::UserSpecific(value) => { + value + } + Self::Unknown => 0xffffffff, + } + } +} + +#[repr(C)] +#[derive(Debug)] +struct SectionHeader { + name: u32, + sh_type: u32, + flags: u64, + addr: u64, + offset: u64, + size: u64, + link: u32, + info: u32, + addralign: u64, + entsize: u64, +} + +unsafe impl plain::Plain for SectionHeader {} + +impl SectionHeader { + fn bytes<'a>(&self, bytes: &'a [u8]) -> Option<&'a [u8]> { + let offset = self.offset as usize; + let size = self.size as usize; + + if offset + size > bytes.len() { + return None; + } + + Some(&bytes[offset..offset + size]) + } +} + +#[repr(C)] +#[derive(Debug)] +struct Elf64Ehdr { + ident: [u8; 16], + kind: u16, + machine: u16, + version: u32, + entry: u64, + phoff: u64, + shoff: u64, + flags: u32, + ehsize: u16, + phentsize: u16, + phnum: u16, + shentsize: u16, + shnum: u16, + shstrndx: u16, +} + +unsafe impl plain::Plain for Elf64Ehdr {} + +struct SectionHeaders<'a> { + headers: &'a [SectionHeader], +} + +impl<'a> SectionHeaders<'a> { + fn symtab_hdr(&self) -> Option<&'a SectionHeader> { + self.headers + .iter() + .find(|header| SectionHeaderType::from_u32(header.sh_type) == SectionHeaderType::Symtab) + } +} + +#[derive(Default)] +struct Strtab<'a> { + bytes: &'a [u8], +} + +impl<'a> Strtab<'a> { + fn len(&self) -> usize { + self.bytes.len() + } + fn get_str(&self, offset: u32) -> Option<&'a str> { + let offset = offset as usize; + + if offset >= self.bytes.len() { + return None; + } + + let bytes = &self.bytes[offset..]; + + let nul_pos = bytes.iter().position(|&b| b == 0)?; + + core::str::from_utf8(&bytes[..nul_pos]).ok() + } +} + +impl Elf64Ehdr { + fn section_headers<'a>(&self, bytes: &'a [u8]) -> SectionHeaders<'a> { + let shoff = self.shoff as usize; + // let shentsize = self.shentsize as usize; + let shnum = self.shnum as usize; + + unsafe { + SectionHeaders { + headers: core::slice::from_raw_parts( + bytes.as_ptr().byte_add(shoff).cast::(), + shnum, + ), + } + } + } + + fn shstrtab<'a>(&self, bytes: &'a [u8]) -> Option> { + let shstrndx = self.shstrndx as usize; + let section_headers = self.section_headers(bytes); + + if shstrndx >= section_headers.headers.len() { + return None; + } + + let shstrtab_header = §ion_headers.headers[shstrndx]; + serial_println!("shstrtab_header: {:#?}", shstrtab_header); + let offset = shstrtab_header.offset as usize; + let size = shstrtab_header.size as usize; + + if offset + size > bytes.len() { + return None; + } + + Some(Strtab { + bytes: &bytes[offset..offset + size], + }) + } + + fn find_shdr_by_name<'a>(&self, bytes: &'a [u8], name: &str) -> Option<&'a SectionHeader> { + let shstrtab = self.shstrtab(bytes)?; + let section_headers = self.section_headers(bytes); + + section_headers + .headers + .iter() + .find(|header| shstrtab.get_str(header.name) == Some(name)) + } + + fn strtab<'a>(&self, bytes: &'a [u8]) -> Option> { + let strtab = self.find_shdr_by_name(bytes, ".strtab")?; + + let offset = strtab.offset as usize; + let size = strtab.size as usize; + + if offset + size > bytes.len() { + return None; + } + + Some(Strtab { + bytes: &bytes[offset..offset + size], + }) + } + + fn symtab<'a>(&self, bytes: &'a [u8]) -> Option<&'a [Elf64Sym]> { + let section_headers = self.section_headers(bytes); + let symtab_header = section_headers.symtab_hdr()?; + + let offset = symtab_header.offset as usize; + let size = symtab_header.size as usize; + + if offset + size > bytes.len() { + return None; + } + + let num_symbols = size / core::mem::size_of::(); + + unsafe { + Some(core::slice::from_raw_parts( + bytes.as_ptr().byte_add(offset).cast::(), + num_symbols, + )) + } + } +} + +impl Elf64Sym { + fn symbols() -> &'static [Elf64Sym] { + static SYMBOLS: LazyLock<&'static [Elf64Sym]> = LazyLock::new(|| { + if let Some(bytes) = crate::limine::EXECUTABLE_FILE_REQUEST + .file() + .map(LimineFile::bytes) + { + if let Ok(ehdr) = plain::from_bytes::(bytes) { + if let Some(symtab) = ehdr.symtab(bytes) { + return symtab; + } + } + } + &[] + }); + + SYMBOLS.as_ref() + } + + fn find(addr: u64) -> Option<&'static Elf64Sym> { + Self::symbols() + .iter() + .find(|&sym| sym.range().contains(&addr)) + .map(|v| v as _) + } + + fn find_type(addr: u64, kind: Elf64SymType) -> Option<&'static Elf64Sym> { + Self::symbols() + .iter() + .find(|&sym| sym.kind() == kind && sym.range().contains(&addr)) + .map(|v| v as _) + } + + fn bind(&self) -> Elf64SymBind { + match self.info >> 4 { + 0 => Elf64SymBind::Local, + 1 => Elf64SymBind::Global, + 2 => Elf64SymBind::Weak, + 10 => Elf64SymBind::Loos, + 12 => Elf64SymBind::Hios, + 13 => Elf64SymBind::Loproc, + 15 => Elf64SymBind::Hiproc, + _ => panic!("Invalid bind value"), + } + } + fn kind(&self) -> Elf64SymType { + match self.info & 0xf { + 0 => Elf64SymType::Notype, + 1 => Elf64SymType::Object, + 2 => Elf64SymType::Func, + 3 => Elf64SymType::Section, + 4 => Elf64SymType::File, + 5 => Elf64SymType::Common, + 6 => Elf64SymType::Tls, + 10 => Elf64SymType::Loos, + 12 => Elf64SymType::Hios, + 13 => Elf64SymType::Loproc, + 15 => Elf64SymType::Hiproc, + _ => panic!("Invalid type value"), + } + } + + fn range(&self) -> core::ops::Range { + self.value..(self.value.saturating_add(self.size)) + } + + fn name(&self) -> Option<&str> { + static STRTAB: LazyLock> = LazyLock::new(|| { + if let Some(bytes) = crate::limine::EXECUTABLE_FILE_REQUEST + .file() + .map(LimineFile::bytes) + && let Ok(ehdr) = plain::from_bytes::(bytes) + { + serial_println!("ELF64 Header: {:#?}", ehdr); + return ehdr.strtab(bytes).unwrap_or_default(); + } + + Strtab::default() + }); + + let len = STRTAB.len(); + + if self.name == 0 || self.name as usize >= len { + return None; + } + + STRTAB.get_str(self.name) + } +} + +pub fn backtrace(ctx: &Context) -> impl Iterator { + Backtrace { + frame: Some(Frame { + rip: ctx.rip, + rbp: ctx.registers.rbp, + }), + } +} + +pub fn debug_backtrace(ctx: &Context) { + serial_println!("{ctx}"); + for frame in backtrace(ctx) { + if let Some(sym) = Elf64Sym::find(frame.rip) { + if sym.kind() == Elf64SymType::Func + && sym.shndx != 0 + && let Some(name) = sym.name() + { + serial_println!( + "0x{:016x} - {} + 0x{:x}", + frame.rip, + rustc_demangle::demangle(name), + frame.rip - sym.value + ); + } else { + serial_println!("{sym:#?}"); + serial_println!( + "0x{:016x} - + 0x{:x}", + frame.rip, + frame.rip - sym.value + ); + } + } else { + serial_println!("0x{:016x} - ", frame.rip); + } + } +} + +struct Backtrace { + frame: Option, +} + +impl Iterator for Backtrace { + type Item = Frame; + + fn next(&mut self) -> Option { + let frame = self.frame.take()?; + + let next_frame = { + let rsp = VirtAddr::from(frame.rbp); + if rsp.is_canonical() && rsp.is_mapped() { + // The stack frame layout is as follows: + // | Our Function | + // +-----------------+ + // | Previous RBP | <- RBP points here + // +-----------------+ + // | Return Address | <- RBP + 8 points here + // +-----------------+ + // | Parent Function | + // + let rip = unsafe { *(rsp.as_ptr::().add(1)) }; + let rbp = unsafe { *(rsp.as_ptr::()) }; + + Some(Frame { rip, rbp }) + } else { + None + } + }; + + self.frame = next_frame; + + Some(frame) + } +} diff --git a/kernel/src/x86_64/idt.rs b/kernel/src/x86_64/idt.rs index 7bb6c33..cf861eb 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, halt_loop}, + x86_64::{Context, RFlags, Registers, backtrace::debug_backtrace, halt_loop}, }; #[derive(Clone, Copy)] @@ -521,6 +521,13 @@ extern "C" fn global_interrupt_handler( } ExceptionVector::PAGE_FAULT => { serial_println!("Page fault occurred! Error code: {:#x}", error_code); + debug_backtrace(&Context { + rip: frame.instruction_pointer, + registers: *registers, + rsp: frame.stack_pointer, + rflags: frame.flags, + cs: frame.code_segment, + }); halt_loop() } _ => {} diff --git a/kernel/src/x86_64/paging.rs b/kernel/src/x86_64/paging.rs index 0f46dc9..983206b 100644 --- a/kernel/src/x86_64/paging.rs +++ b/kernel/src/x86_64/paging.rs @@ -9,7 +9,6 @@ use bit_field::BitField; use crate::{ memory::{PhyAddr, VirtAddr, VirtAddrTranslationExt}, - serial_println, x86_64::{VirtAddrExt, registers::Cr4}, }; @@ -186,8 +185,6 @@ impl VirtAddrTranslationExt for VirtAddr { 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 { @@ -208,10 +205,7 @@ pub fn get_physical_addr(virt: VirtAddr) -> Option { 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); @@ -220,7 +214,6 @@ pub fn get_physical_addr(virt: VirtAddr) -> Option { } 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); @@ -229,7 +222,6 @@ pub fn get_physical_addr(virt: VirtAddr) -> Option { } 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;