Something went wrong. Try again.
A custom OS written in Zig
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824use x86_64::{PhysAddr, VirtAddr, registers::{control::{Cr3, Cr3Flags}, model_specific::Msr}, structures::paging::{FrameAllocator, PageTable, PageTableFlags, PageTableIndex, PhysFrame, Size4KiB, page_table::PageTableEntry}, instructions::tlb::flush};use core::{ops::{Deref, RangeInclusive}, ptr::NonNull};use raw_cpuid::CpuId;
use crate::{hhdm_offset::HhdmOffset, translate_addr::OffsetMappedPhysAddr};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]pub enum PageSize { _4KiB, _2MiB, _1GiB}
impl PageSize { pub const fn byte_len(self) -> usize { self.byte_len_u64() as usize }
pub const fn byte_len_u64(self) -> u64 { match self { Self::_4KiB => 0x1000, Self::_2MiB => 512 * 0x1000, Self::_1GiB => 512 * 512 * 0x1000 } }}
#[derive(Debug, Clone, Copy)]pub struct Frame { start_addr: PhysAddr, size: PageSize}
#[derive(Debug)]pub enum NewFrameError { NotAligned}
impl Frame { pub fn new(start_addr: PhysAddr, size: PageSize) -> Result<Self, NewFrameError> { if start_addr.is_aligned(size.byte_len_u64()) { Ok(Self { start_addr, size }) } else { Err(NewFrameError::NotAligned) } }
pub fn start_addr(&self) -> PhysAddr { self.start_addr }
pub fn size(&self) -> PageSize { self.size }
pub fn offset(&self, page_count: u64) -> Option<Self> { let bytes_offset = page_count.checked_mul(self.size.byte_len_u64())?; let offset_start_addr = self.start_addr.as_u64().checked_add(bytes_offset)?; Some(Self::new(PhysAddr::new(offset_start_addr), self.size).unwrap()) }}
/// A wrapper around [`PhysFrame`] that guarantees that it is "owned" by whatever owns it.#[derive(Debug)]pub struct Owned4KibFrame(pub(crate) PhysFrame);
impl Owned4KibFrame { /// # Safety /// - The phys frame must be valid physical memory /// - The memory cannot be used or referenced by anything else pub unsafe fn new(frame: PhysFrame) -> Self { Self(frame) }}
impl Deref for Owned4KibFrame { type Target = PhysFrame;
fn deref(&self) -> &Self::Target { &self.0 }}
impl From<Owned4KibFrame> for PhysFrame { fn from(value: Owned4KibFrame) -> Self { value.0 }}
#[derive(Debug, Clone, Copy)]pub struct PagingConfig { pub(crate) offset: VirtualOffset, pub(crate) pat: ManagedPat}
impl PagingConfig { pub fn new(pat: ManagedPat, offset: VirtualOffset) -> Self { Self { pat, offset } }}
impl PagingConfig { /// Create a new top level page table meant to only be accessed by the kernel. /// You will only be allowed to use the higher half of the virtual address space. /// /// This method also zeroes the frame. pub fn new_kernel(self, mut frame: Owned4KibFrame) -> ManagedL4PageTable { unsafe { init_page_table(&mut frame, &self) }; ManagedL4PageTable { frame, _type: L4Type::Kernel(KernelL4Data { is_referenced: false, }), config: self, } }}
#[derive(Debug, Clone, Copy)]pub struct VirtualOffset(u64);
impl VirtualOffset { /// # Safety /// All physical memory should be mapped starting at this virtual offset pub unsafe fn new(virtual_offset: u64) -> Self { Self(virtual_offset) }}
impl Deref for VirtualOffset { type Target = u64;
fn deref(&self) -> &Self::Target { &self.0 }}
impl From<HhdmOffset> for VirtualOffset { fn from(value: HhdmOffset) -> Self { Self(value.into()) }}
/// A guarantee that the PAT MSR won't be modified#[derive(Debug, Clone, Copy)]#[non_exhaustive]pub struct ManagedPat;
impl ManagedPat { /// # Safety /// Do not write to the PAT MSR after creating this pub const unsafe fn new() -> Self { Self {} }
/// GEt the necessary page table bits needed to set the caching memory type of a page. /// If for some reason there is no entry in the PAT MSR with the memory type, `None` is returned /// /// TODO: `memory_type` is supposed to be `PatMemoryType` pub fn get_page_table_flags(&self, memory_type: PatMemoryType, page_size: PageSize) -> Option<PageTableFlags> { let pat_msr_index = Pat::read().iter().position(|v| *v == memory_type)?; // Set Intel SDM -> Volume 3 -> 13.12.3 Selecting a Memory Type from the PAT let mut flags = PageTableFlags::empty();
if pat_msr_index & 0b001 != 0 { flags |= PageTableFlags::WRITE_THROUGH; }
if pat_msr_index & 0b010 != 0 { flags |= PageTableFlags::NO_CACHE; }
let mut flags: u64 = flags.bits();
if pat_msr_index & 0b100 != 0 { flags |= match page_size { PageSize::_1GiB | PageSize::_2MiB => /* PAT_HUGE_PAGE */ 1 << 12, PageSize::_4KiB => /* PAT_4KIB_PAGE */ 1 << 7 }; }
Some(PageTableFlags::from_bits_retain(flags)) }}
/// IA32_PAT: Page Attribute Table.#[derive(Debug)]pub struct Pat;
impl Pat { /// The underlying model specific register. pub const MSR: Msr = Msr::new(0x277); /// The default PAT configuration following a power up or reset of the processor. pub const DEFAULT: [PatMemoryType; 8] = [ PatMemoryType::WriteBack, PatMemoryType::WriteThrough, PatMemoryType::Uncacheable, PatMemoryType::StrongUncacheable, PatMemoryType::WriteBack, PatMemoryType::WriteThrough, PatMemoryType::Uncacheable, PatMemoryType::StrongUncacheable, ];
/// Reads IA32_PAT. /// /// The PAT must be supported on the CPU, otherwise a general protection exception will /// occur. Support can be detected using the `cpuid` instruction. #[inline] pub fn read() -> [PatMemoryType; 8] { unsafe { Self::MSR.read() } .to_ne_bytes() .map(|bits| PatMemoryType::from_bits(bits).unwrap()) }
/// Writes IA32_PAT. /// /// The PAT must be supported on the CPU, otherwise a general protection exception will /// occur. Support can be detected using the `cpuid` instruction. /// /// # Safety /// /// All affected pages must be flushed from the TLB. Processor caches may also need to be /// flushed. Additionally, all pages that map to a given frame must have the same memory /// type. #[inline] pub unsafe fn write(table: [PatMemoryType; 8]) { let bits = u64::from_ne_bytes(table.map(PatMemoryType::bits)); let mut msr = Self::MSR; unsafe { msr.write(bits); } }}
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]/// Memory types used in the [PAT](Pat).#[repr(u8)]pub enum PatMemoryType { /// Uncacheable (UC). StrongUncacheable = 0x00, /// Uses a write combining (WC) cache policy. WriteCombining = 0x01, /// Uses a write through (WT) cache policy. WriteThrough = 0x04, /// Uses a write protected (WP) cache policy. WriteProtected = 0x05, /// Uses a write back (WB) cache policy. WriteBack = 0x06, /// Same as strong uncacheable, but can be overridden to be write combining by MTRRs (UC-). Uncacheable = 0x07,}impl PatMemoryType { /// Converts from bits, returning `None` if the value is invalid. pub const fn from_bits(bits: u8) -> Option<Self> { match bits { 0x00 => Some(Self::StrongUncacheable), 0x01 => Some(Self::WriteCombining), 0x04 => Some(Self::WriteThrough), 0x05 => Some(Self::WriteProtected), 0x06 => Some(Self::WriteBack), 0x07 => Some(Self::Uncacheable), _ => None, } }
/// Gets the underlying bits. pub const fn bits(self) -> u8 { self as u8 }}
#[derive(Debug)]pub struct ManagedL4PageTable { pub frame: Owned4KibFrame, pub _type: L4Type, pub config: PagingConfig}
impl ManagedL4PageTable { /// Create a new top level page table meant to be accessed by a process. /// You will only be able to use the lower half of the virtual address space. /// /// This method also zeroes the frame. pub fn new_user(&mut self, mut frame: Owned4KibFrame) -> Self { match &mut self._type { L4Type::User => { panic!("self must be a kernel's l4 frame to copy from it") } L4Type::Kernel(KernelL4Data { is_referenced }) => { *is_referenced = true; } }; unsafe { init_page_table(&mut frame, &self.config) }; let mut lower_half = Self { frame, _type: L4Type::User, config: self.config, }; let range_to_copy = self._type.l4_managed_entry_range(); let kernel_page_table = unsafe { self.page_table().as_mut() }; let user_page_table = unsafe { lower_half.page_table().as_mut() }; for index in range_to_copy { user_page_table[index].clone_from(&kernel_page_table[index]); } lower_half }
/// If you choose to manually modify page table entries, be careful, because it could create valid page tables that will cause problems because this crate doesn't expect handle. pub fn page_table(&mut self) -> NonNull<PageTable> { NonNull::new( self.frame .start_address() .to_virt(&self.config) .as_mut_ptr::<PageTable>(), ) .unwrap() }
pub(super) fn table_mut(&mut self) -> PageTableWithLevelMut { PageTableWithLevelMut { page_table: self.page_table(), level: PageTableLevel::L4, l4: self, } }
/// # Safety /// Changes Cr3 value pub unsafe fn switch_to(&self, flags: Cr3Flags) { unsafe { Cr3::write(self.frame.0, flags) }; }
pub fn frame(&self) -> &Owned4KibFrame { &self.frame }}
#[derive(Debug)]pub enum L4Type { User, Kernel(KernelL4Data)}
impl L4Type { pub fn l4_managed_entry_range(&self) -> RangeInclusive<PageTableIndex> { match self { Self::User => PageTableIndex::new(0)..=PageTableIndex::new(255), Self::Kernel(_) => PageTableIndex::new(256)..=PageTableIndex::new(511), } }
pub fn can_create_new_l4_entries(&self) -> bool { match self { Self::User => true, Self::Kernel(KernelL4Data { is_referenced }) => !is_referenced, } }}
#[derive(Debug)]pub struct KernelL4Data { is_referenced: bool,}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]pub enum PageTableLevel { L1, L2, L3, L4,}
impl PageTableLevel { pub fn sub_level(self) -> Option<Self> { match self { PageTableLevel::L1 => None, PageTableLevel::L2 => Some(PageTableLevel::L1), PageTableLevel::L3 => Some(PageTableLevel::L2), PageTableLevel::L4 => Some(PageTableLevel::L3), } }
pub fn target_frame_size(self) -> Option<PageSize> { match self { PageTableLevel::L1 => Some(PageSize::_4KiB), PageTableLevel::L2 => Some(PageSize::_2MiB), PageTableLevel::L3 => Some(PageSize::_1GiB), PageTableLevel::L4 => None, } }}
#[derive(Debug)]pub struct PageTableWithLevelMut<'a> { pub(super) l4: &'a ManagedL4PageTable, pub(super) page_table: NonNull<PageTable>, pub(super) level: PageTableLevel,}
impl<'a> PageTableWithLevelMut<'a> { pub fn entry_mut(mut self, index: PageTableIndex) -> PageTableEntryWithLevelMut<'a> { if self.level == PageTableLevel::L4 { let range = self.l4._type.l4_managed_entry_range(); if !range.contains(&index) { panic!( "Cannot access L4 entry {index:?} because it is outside of the range managed by this page table ({range:?})" ) } } PageTableEntryWithLevelMut { entry: { let mut ptr = NonNull::from_mut(&mut unsafe { self.page_table.as_mut() }[index]); // Safety: We are still capturing a &mut to the managed L4 table unsafe { ptr.as_mut() } }, level: self.level, l4: self.l4, } }}
#[derive(Debug)]pub struct PageTableEntryWithLevelMut<'a> { pub(super) entry: &'a mut PageTableEntry, pub(super) level: PageTableLevel, pub(super) l4: &'a ManagedL4PageTable,}
#[derive(Debug)]pub enum SetFrameError { /// Either the page table is a L4 table (you can't map a 512 GiB frame) or the frame size is incompatible with the table level. NotAllowed, /// This CPU cannot have 1 GiB page sizes PageSizeNotSupported,}
#[derive(Debug)]pub enum SetTableError { /// This page table is a L1 table and L1 entries don't point to another page table. IsL1,}
#[derive(Debug)]pub enum GetTableError { /// This is a L1 table and cannot point to another table IsL1, /// This entry is not mapped NotMapped, /// This entry is mapped, but not mapped to a table MappedToFrame,}
#[derive(Debug)]pub enum UnmapFrameError { IsL4, NotPresent, IsPageTable,}
#[derive(Debug)]pub enum SetFlagsError { IsL4, NotPresent, IsPageTable,}
impl PageTableEntryWithLevelMut<'_> { pub fn is_empty(&self) -> bool { self.entry.is_unused() }
fn page_size(&self) -> Option<PageSize> { match self.level { PageTableLevel::L1 => Some(PageSize::_4KiB), PageTableLevel::L2 => Some(PageSize::_2MiB), PageTableLevel::L3 => Some(PageSize::_1GiB), PageTableLevel::L4 => None, } }
fn generate_flags(&self, configurable_flags: ConfigurableFlags) -> PageTableFlags { let mut flags = PageTableFlags::PRESENT | self.l4.config.pat .get_page_table_flags(configurable_flags.pat_memory_type, self.page_size().unwrap()) .expect("There are only 6 memory types and 8 slots, so all memory types should be present in the slots"); if !matches!(self.level, PageTableLevel::L1) { flags |= PageTableFlags::HUGE_PAGE } if configurable_flags.writable { flags |= PageTableFlags::WRITABLE; } if !configurable_flags.executable { flags |= PageTableFlags::NO_EXECUTE; } match &self.l4._type { L4Type::User => { flags |= PageTableFlags::USER_ACCESSIBLE; } L4Type::Kernel(_) => { flags |= PageTableFlags::GLOBAL; } }; flags }
pub fn set_frame( &mut self, frame: Frame, flags: ConfigurableFlags, ) -> Result<(), SetFrameError> { let level_frame_match = match self.level { PageTableLevel::L1 => matches!(frame.size(), PageSize::_4KiB), PageTableLevel::L2 => matches!(frame.size(), PageSize::_2MiB), PageTableLevel::L3 => matches!(frame.size(), PageSize::_1GiB), PageTableLevel::L4 => false, }; if !level_frame_match { return Err(SetFrameError::NotAllowed); } if frame.size() > max_page_size() { return Err(SetFrameError::PageSizeNotSupported); } self.entry .set_addr(frame.start_addr(), self.generate_flags(flags)); Ok(()) }
/// Returns the frame that was unmapped pub fn unmap_frame(&mut self) -> Result<Frame, UnmapFrameError> { let frame_size = self .level .target_frame_size() .ok_or(UnmapFrameError::IsL4)?; if !self.entry.flags().contains(PageTableFlags::PRESENT) { return Err(UnmapFrameError::NotPresent); } if !self.entry.flags().contains(PageTableFlags::HUGE_PAGE) && !matches!(frame_size, PageSize::_4KiB) { return Err(UnmapFrameError::IsPageTable); } let start_addr = self.entry.addr(); self.entry.set_unused(); Ok(Frame::new(start_addr, frame_size).unwrap()) }
/// Only sets flags for pointing to a frame, not pointing to a table pub fn set_flags(&mut self, flags: ConfigurableFlags) -> Result<(), SetFlagsError> { let page_size = self.level.target_frame_size().ok_or(SetFlagsError::IsL4)?; if !self.entry.flags().contains(PageTableFlags::PRESENT) { return Err(SetFlagsError::NotPresent); } if !self.entry.flags().contains(PageTableFlags::HUGE_PAGE) && !matches!(page_size, PageSize::_4KiB) { return Err(SetFlagsError::IsPageTable); } self.entry.set_flags(self.generate_flags(flags)); Ok(()) }}
impl<'a> PageTableEntryWithLevelMut<'a> { /// This method also zeroes the frame pub fn set_page_table( self, frame: PhysFrame, ) -> Result<PageTableWithLevelMut<'a>, SetTableError> { let page_table_level = self.level.sub_level().ok_or(SetTableError::IsL1)?; if self.level == PageTableLevel::L4 && !self.l4._type.can_create_new_l4_entries() { panic!( "Cannot create new L3 pages because the kernel page table would be out of sync with user page tables" ) } let ptr = NonNull::new( frame .start_address() .to_virt(&self.l4.config) .as_mut_ptr::<PageTable>(), ) .unwrap(); unsafe { ptr.write_bytes(0, 1) };
self.entry.set_frame( frame, PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::USER_ACCESSIBLE, ); Ok(PageTableWithLevelMut { page_table: ptr, level: page_table_level, l4: self.l4, }) }
pub fn get_page_table_mut(self) -> Result<PageTableWithLevelMut<'a>, GetTableError> { let page_table_level = self.level.sub_level().ok_or(GetTableError::IsL1)?; if self.entry.is_unused() { return Err(GetTableError::NotMapped); } if self.entry.flags().contains(PageTableFlags::HUGE_PAGE) { return Err(GetTableError::MappedToFrame); } let frame = self.entry.frame().unwrap(); let ptr = NonNull::new( frame .start_address() .to_virt(&self.l4.config) .as_mut_ptr::<PageTable>(), ) .unwrap(); Ok(PageTableWithLevelMut { page_table: ptr, level: page_table_level, l4: self.l4, }) }}
/// All mappings are readable because they require the PRESENT flag./// Some flags are also used as flags for sub-pages./// For a page to be writable and user accessible, all parent flags must also have WRITABLE and USER_ACCESSIBLE./// For a page to be executable, the flags and all parent flags should **not** have the NO_EXECUTE flag./// The GLOBAL flag only exists for the lowest level page table. It does not exist in higher page tables, so the mapper does not need to handle setting the GLOBAL flag in parent page tables.#[derive(Debug, Clone, Copy)]pub struct ConfigurableFlags { pub writable: bool, pub executable: bool, pub pat_memory_type: PatMemoryType,}
/// # Safety/// Frame will be zeroedunsafe fn init_page_table(frame: &mut Owned4KibFrame, config: &PagingConfig) { let ptr = NonNull::new( frame .start_address() .to_virt(config) .as_mut_ptr::<PageTable>(), ) .unwrap(); // We use `write_bytes` so that we don't put the 4 KiB page table on the stack, which causes stack overflows. unsafe { ptr.write_bytes(0, 1); }}
pub trait TranslateToVirt { fn to_virt(self, paging: &PagingConfig) -> VirtAddr;}
impl TranslateToVirt for PhysAddr { fn to_virt(self, paging: &PagingConfig) -> VirtAddr { VirtAddr::new(self.as_u64() + paging.offset.deref()) }}
/// Get the largest page size that this CPU supports.pub fn max_page_size() -> PageSize { if CpuId::new() .get_extended_processor_and_feature_identifiers() .is_some_and(|info| info.has_1gib_pages()) { PageSize::_1GiB } else { PageSize::_2MiB }}
impl ManagedL4PageTable { /// Maps a page to a phys frame. /// To avoid bugs, it is expected that the page is currently unmapped. It will error if the entry is not completely 0. /// /// PRESENT and HUGE_PAGE flags are automatically added as needed. /// /// # Safety /// Don't mess up page tables, don't give user mode access to things it shouldn't access, don't accidentally create multiple &mut T to the same data. pub unsafe fn map_page( &mut self, page: Page, frame: Frame, flags: ConfigurableFlags, frame_allocator: &mut impl FrameAllocator<Size4KiB>, ) -> Result<(), MapPageError> { let l4 = self.table_mut(); let l3 = get_or_create_page_table(l4.entry_mut(page.start_addr().p4_index()), frame_allocator)?; if let PageSize::_1GiB = page.size() { l3.entry_mut(page.start_addr().p3_index()) .set_frame(frame, flags) .map_err(MapPageError::SetFrame)?; return Ok(()); } let l2 = get_or_create_page_table(l3.entry_mut(page.start_addr().p3_index()), frame_allocator)?; if let PageSize::_2MiB = page.size() { l2.entry_mut(page.start_addr().p2_index()) .set_frame(frame, flags) .map_err(MapPageError::SetFrame)?; return Ok(()); } let l1 = get_or_create_page_table(l2.entry_mut(page.start_addr().p2_index()), frame_allocator)?; l1.entry_mut(page.start_addr().p1_index()) .set_frame(frame, flags) .map_err(MapPageError::SetFrame)?; Ok(()) }}
fn get_or_create_page_table<'a>( page_table_entry: PageTableEntryWithLevelMut<'a>, frame_allocator: &mut impl FrameAllocator<Size4KiB>,) -> Result<PageTableWithLevelMut<'a>, MapPageError> { Ok({ if page_table_entry.is_empty() { let frame = frame_allocator .allocate_frame() .ok_or(MapPageError::FrameAllocationFailed)?; page_table_entry .set_page_table(frame) .map_err(MapPageError::SetTable)? } else { page_table_entry .get_page_table_mut() .map_err(MapPageError::GetTable)? } })}
#[derive(Debug)]pub enum MapPageError { FrameAllocationFailed, SetTable(SetTableError), GetTable(GetTableError), SetFrame(SetFrameError),}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]pub struct Page { start_addr: VirtAddr, size: PageSize,}
#[derive(Debug)]pub enum NewPageError { NotAligned,}
impl Page { pub fn new(start_addr: VirtAddr, size: PageSize) -> Result<Self, NewPageError> { if start_addr.is_aligned(size.byte_len_u64()) { Ok(Self { start_addr, size }) } else { Err(NewPageError::NotAligned) } }
pub fn start_addr(&self) -> VirtAddr { self.start_addr }
pub fn size(&self) -> PageSize { self.size }
pub fn offset(&self, page_count: u64) -> Option<Self> { let bytes_offset = page_count.checked_mul(self.size.byte_len_u64())?; let offset_start_addr = self.start_addr.as_u64().checked_add(bytes_offset)?; Some(Self::new(VirtAddr::new(offset_start_addr), self.size).unwrap()) }}
impl Frame { pub fn offset_mapped(&self) -> Page { Page::new( self.start_addr.offset_mapped(), self.size ).unwrap() }}
#[derive(Debug)]pub enum UnmapPageError { GetTable(GetTableError), UnmapFrame(UnmapFrameError)}
impl ManagedL4PageTable { /// Also does `invlpg` after successfully un-mapping. /// Returns the entry that was removed. /// /// # Safety /// Don't unmap the wrong thing. It can cause page faults. pub unsafe fn unmap_page(&mut self, page: Page) -> Result<Frame, UnmapPageError> { let l4 = self.table_mut(); let l3 = l4 .entry_mut(page.start_addr().p4_index()) .get_page_table_mut() .map_err(UnmapPageError::GetTable)?; let l3_entry = l3.entry_mut(page.start_addr().p3_index());
let mut entry = if let PageSize::_1GiB = page.size() { l3_entry } else { let l2 = l3_entry .get_page_table_mut() .map_err(UnmapPageError::GetTable)?; let l2_entry = l2.entry_mut(page.start_addr().p2_index()); if let PageSize::_2MiB = page.size() { l2_entry } else { let l1 = l2_entry .get_page_table_mut() .map_err(UnmapPageError::GetTable)?; l1.entry_mut(page.start_addr().p1_index()) } }; let frame = entry.unmap_frame().map_err(UnmapPageError::UnmapFrame)?; flush(page.start_addr()); Ok(frame) }}