Skip to main content

hermit/arch/x86_64/mm/
paging.rs

1use core::{fmt, ptr};
2
3use free_list::PageLayout;
4use memory_addresses::arch::x86_64::{PhysAddr, VirtAddr};
5use x86_64::registers::control::{Cr0, Cr0Flags, Cr2, Cr3};
6#[cfg(feature = "common-os")]
7use x86_64::registers::segmentation::SegmentSelector;
8pub use x86_64::structures::idt::InterruptStackFrame as ExceptionStackFrame;
9use x86_64::structures::idt::PageFaultErrorCode;
10pub use x86_64::structures::paging::PageTableFlags as PageTableEntryFlags;
11use x86_64::structures::paging::frame::PhysFrameRange;
12use x86_64::structures::paging::mapper::{MapToError, MappedFrame, TranslateResult, UnmapError};
13use x86_64::structures::paging::page::PageRange;
14use x86_64::structures::paging::{
15	FrameAllocator, Mapper, OffsetPageTable, Page, PageTable, PhysFrame, Size4KiB, Translate,
16};
17
18use crate::arch::kernel::processor;
19use crate::mm::{FrameAlloc, PageRangeAllocator};
20use crate::scheduler;
21
22unsafe impl FrameAllocator<Size4KiB> for FrameAlloc {
23	fn allocate_frame(&mut self) -> Option<PhysFrame<Size4KiB>> {
24		let size = usize::try_from(Size4KiB::SIZE).unwrap();
25		let layout = PageLayout::from_size(size).unwrap();
26
27		let range = FrameAlloc::allocate(layout).ok()?;
28
29		let phys_addr = PhysAddr::from(range.start());
30		Some(PhysFrame::from_start_address(phys_addr.into()).unwrap())
31	}
32}
33
34pub trait PageTableEntryFlagsExt {
35	/// Cached, writable, non-executable memory.
36	fn normal() -> Self;
37
38	/// Uncached, writable, non-executable memory.
39	#[cfg_attr(not(any(feature = "pci", feature = "vga")), expect(dead_code))]
40	fn device() -> Self;
41
42	#[allow(dead_code)]
43	fn set_writable(&mut self, writable: bool) -> &mut Self;
44
45	#[allow(dead_code)]
46	fn set_execute(&mut self, execute: bool) -> &mut Self;
47
48	#[cfg(feature = "common-os")]
49	fn user(&mut self) -> &mut Self;
50
51	#[expect(dead_code)]
52	#[cfg(feature = "common-os")]
53	fn kernel(&mut self) -> &mut Self;
54}
55
56impl PageTableEntryFlagsExt for PageTableEntryFlags {
57	fn normal() -> Self {
58		PageTableEntryFlags::WRITABLE | PageTableEntryFlags::NO_EXECUTE
59	}
60
61	fn device() -> Self {
62		PageTableEntryFlags::WRITABLE
63			| PageTableEntryFlags::NO_EXECUTE
64			| PageTableEntryFlags::NO_CACHE
65	}
66
67	fn set_writable(&mut self, writable: bool) -> &mut Self {
68		if writable {
69			self.insert(PageTableEntryFlags::WRITABLE);
70		} else {
71			self.remove(PageTableEntryFlags::WRITABLE);
72		}
73		self
74	}
75
76	fn set_execute(&mut self, execute: bool) -> &mut Self {
77		if execute {
78			self.remove(PageTableEntryFlags::NO_EXECUTE);
79		} else {
80			self.insert(PageTableEntryFlags::NO_EXECUTE);
81		}
82		self
83	}
84
85	#[cfg(feature = "common-os")]
86	fn user(&mut self) -> &mut Self {
87		self.insert(PageTableEntryFlags::USER_ACCESSIBLE);
88		self
89	}
90
91	#[cfg(feature = "common-os")]
92	fn kernel(&mut self) -> &mut Self {
93		self.remove(PageTableEntryFlags::USER_ACCESSIBLE);
94		self
95	}
96}
97
98pub use x86_64::structures::paging::{
99	PageSize, Size1GiB as HugePageSize, Size2MiB as LargePageSize, Size4KiB as BasePageSize,
100};
101
102/// Returns a mapping of the physical memory where physical address is equal to the virtual address (no offset)
103pub unsafe fn identity_mapped_page_table() -> OffsetPageTable<'static> {
104	let level_4_table_addr = Cr3::read().0.start_address().as_u64();
105	let level_4_table_ptr =
106		ptr::with_exposed_provenance_mut::<PageTable>(level_4_table_addr.try_into().unwrap());
107	unsafe {
108		let level_4_table = level_4_table_ptr.as_mut().unwrap();
109		OffsetPageTable::new(level_4_table, x86_64::addr::VirtAddr::new(0x0))
110	}
111}
112
113/// Returns true if the level 4 page table has a recursive entry.
114///
115/// This is useful for compatibility with the Hermit loader version 0.5.6.
116// FIXME: Remove once we drop support for loader 0.5.6
117#[cfg(feature = "hermit-entry")]
118pub fn is_recursive() -> bool {
119	use x86_64::structures::paging::PageTableIndex;
120
121	let identity_mapped_page_table = unsafe { identity_mapped_page_table() };
122	let level_4_table = identity_mapped_page_table.level_4_table();
123
124	let recursive_index = PageTableIndex::new(511);
125	let level_4_table_virt_addr = ptr::from_ref(level_4_table).addr();
126	let recursive_index_phys_addr = level_4_table[recursive_index].addr().as_u64() as usize;
127
128	level_4_table_virt_addr == recursive_index_phys_addr
129}
130
131/// Translate a virtual memory address to a physical one.
132pub fn virtual_to_physical(virtual_address: VirtAddr) -> Option<PhysAddr> {
133	let addr = x86_64::VirtAddr::from(virtual_address);
134
135	let translate_result = unsafe { identity_mapped_page_table() }.translate(addr);
136
137	match translate_result {
138		TranslateResult::NotMapped | TranslateResult::InvalidFrameAddress(_) => {
139			trace!("Unable to determine the physical address of 0x{virtual_address:X}");
140			None
141		}
142		TranslateResult::Mapped { frame, offset, .. } => {
143			Some(PhysAddr::new((frame.start_address() + offset).as_u64()))
144		}
145	}
146}
147
148/// Maps a continuous range of pages.
149///
150/// # Arguments
151///
152/// * `physical_address` - First physical address to map these pages to
153/// * `flags` - Flags from PageTableEntryFlags to set for the page table entry (e.g. WRITABLE or NO_EXECUTE).
154///   The PRESENT flags is set automatically.
155pub fn map<S>(
156	virtual_address: VirtAddr,
157	physical_address: PhysAddr,
158	count: usize,
159	flags: PageTableEntryFlags,
160) where
161	S: PageSize + fmt::Debug,
162	for<'a> OffsetPageTable<'a>: Mapper<S>,
163{
164	let pages = {
165		let start = Page::<S>::containing_address(virtual_address.into());
166		let end = start + count as u64;
167		Page::range(start, end)
168	};
169
170	let frames = {
171		let start = PhysFrame::<S>::containing_address(physical_address.into());
172		let end = start + count as u64;
173		PhysFrame::range(start, end)
174	};
175
176	let flags = flags | PageTableEntryFlags::PRESENT;
177
178	trace!("Mapping {pages:?} to {frames:?} with {flags:?}");
179
180	unsafe fn map_pages<M, S>(
181		mapper: &mut M,
182		pages: PageRange<S>,
183		frames: PhysFrameRange<S>,
184		flags: PageTableEntryFlags,
185	) -> bool
186	where
187		M: Mapper<S>,
188		S: PageSize + fmt::Debug,
189	{
190		let mut unmapped = false;
191		for (page, frame) in pages.zip(frames) {
192			// TODO: Require explicit unmaps
193			let unmap = mapper.unmap(page);
194			if let Ok((_frame, flush)) = unmap {
195				unmapped = true;
196				flush.flush();
197				debug!("Had to unmap page {page:?} before mapping.");
198			}
199			let map = unsafe { mapper.map_to(page, frame, flags, &mut FrameAlloc) };
200			match map {
201				Ok(mapper_flush) => mapper_flush.flush(),
202				Err(err) => panic!("Could not map {page:?} to {frame:?}: {err:?}"),
203			}
204		}
205		unmapped
206	}
207
208	let unmapped = unsafe { map_pages(&mut identity_mapped_page_table(), pages, frames, flags) };
209
210	if unmapped {
211		#[cfg(feature = "smp")]
212		crate::arch::kernel::apic::ipi_tlb_flush();
213	}
214}
215
216/// Maps `count` pages at address `virt_addr`. If the allocation of a physical memory failed,
217/// the number of successful mapped pages are returned as error value.
218pub fn map_heap<S>(virt_addr: VirtAddr, count: usize) -> Result<(), usize>
219where
220	S: PageSize + fmt::Debug,
221	for<'a> OffsetPageTable<'a>: Mapper<S>,
222{
223	let flags = PageTableEntryFlags::normal();
224
225	let virt_addrs = (0..count).map(|n| virt_addr + n as u64 * S::SIZE);
226
227	for (map_counter, virt_addr) in virt_addrs.enumerate() {
228		let layout = PageLayout::from_size_align(S::SIZE as usize, S::SIZE as usize).unwrap();
229		let frame_range = FrameAlloc::allocate(layout).map_err(|_| map_counter)?;
230		let phys_addr = PhysAddr::from(frame_range.start());
231		map::<S>(virt_addr, phys_addr, 1, flags);
232	}
233
234	Ok(())
235}
236
237pub fn identity_map<S>(phys_addr: PhysAddr)
238where
239	S: PageSize + fmt::Debug,
240	for<'a> OffsetPageTable<'a>: Mapper<S>,
241{
242	let frame = PhysFrame::<S>::from_start_address(phys_addr.into()).unwrap();
243	let flags = PageTableEntryFlags::PRESENT
244		| PageTableEntryFlags::WRITABLE
245		| PageTableEntryFlags::NO_EXECUTE;
246	let mapper_result =
247		unsafe { identity_mapped_page_table().identity_map(frame, flags, &mut FrameAlloc) };
248
249	match mapper_result {
250		Ok(mapper_flush) => mapper_flush.flush(),
251		Err(MapToError::PageAlreadyMapped(current_frame)) => assert_eq!(current_frame, frame),
252		Err(MapToError::ParentEntryHugePage) => {
253			let page_table = unsafe { identity_mapped_page_table() };
254			let virt_addr = VirtAddr::new(frame.start_address().as_u64()).into();
255			let phys_addr = frame.start_address();
256			assert_eq!(page_table.translate_addr(virt_addr), Some(phys_addr));
257		}
258		Err(err) => panic!("could not identity-map {frame:?}: {err:?}"),
259	}
260}
261
262pub fn unmap<S>(virtual_address: VirtAddr, count: usize)
263where
264	S: PageSize + fmt::Debug,
265	for<'a> OffsetPageTable<'a>: Mapper<S>,
266{
267	trace!("Unmapping virtual address {virtual_address:p} ({count} pages)");
268
269	let first_page = Page::<S>::containing_address(virtual_address.into());
270	let last_page = first_page + count as u64;
271	let range = Page::range(first_page, last_page);
272
273	for page in range {
274		let unmap_result = unsafe { identity_mapped_page_table() }.unmap(page);
275		match unmap_result {
276			Ok((_frame, flush)) => flush.flush(),
277			// FIXME: Some sentinel pages around stacks are supposed to be unmapped.
278			// We should handle this case there instead of here.
279			Err(UnmapError::PageNotMapped) => {
280				debug!("Tried to unmap {page:?}, which was not mapped.");
281			}
282			Err(err) => panic!("{err:?}"),
283		}
284	}
285}
286
287#[cfg(not(feature = "common-os"))]
288pub(crate) extern "x86-interrupt" fn page_fault_handler(
289	stack_frame: ExceptionStackFrame,
290	error_code: PageFaultErrorCode,
291) {
292	error!("Page fault (#PF)!");
293	error!("page_fault_linear_address = {:p}", Cr2::read().unwrap());
294	error!("error_code = {error_code:?}");
295	error!("fs = {:#X}", processor::readfs());
296	error!("gs = {:#X}", processor::readgs());
297	error!("stack_frame = {stack_frame:#?}");
298	scheduler::abort();
299}
300
301#[cfg(feature = "common-os")]
302pub(crate) extern "x86-interrupt" fn page_fault_handler(
303	mut stack_frame: ExceptionStackFrame,
304	error_code: PageFaultErrorCode,
305) {
306	unsafe {
307		if stack_frame.as_mut().read().code_segment != SegmentSelector(0x08) {
308			core::arch::asm!("swapgs", options(nostack));
309		}
310	}
311	error!("Page fault (#PF)!");
312	error!("page_fault_linear_address = {:p}", Cr2::read().unwrap());
313	error!("error_code = {error_code:?}");
314	error!("fs = {:#X}", processor::readfs());
315	error!("gs = {:#X}", processor::readgs());
316	error!("stack_frame = {stack_frame:#?}");
317	scheduler::abort();
318}
319
320pub unsafe fn init() {
321	unsafe {
322		log_page_tables();
323	}
324
325	ensure_p4_writable();
326
327	#[cfg(feature = "common-os")]
328	{
329		let (frame, _flags) = Cr3::read();
330		scheduler::BOOT_ROOT_PAGE_TABLE
331			.set(frame.start_address().as_u64().try_into().unwrap())
332			.unwrap();
333	}
334}
335
336/// Makes the level 4 page table writable.
337///
338/// This is useful when reusing UEFI's page tables which might not be writable.
339fn ensure_p4_writable() {
340	let mut pt = unsafe { identity_mapped_page_table() };
341
342	let p4_page = {
343		let (p4_frame, _) = Cr3::read_raw();
344		let p4_addr = x86_64::VirtAddr::new(p4_frame.start_address().as_u64());
345		Page::<Size4KiB>::from_start_address(p4_addr).unwrap()
346	};
347
348	let TranslateResult::Mapped { frame, flags, .. } = pt.translate(p4_page.start_address()) else {
349		unreachable!()
350	};
351
352	if flags.contains(PageTableEntryFlags::WRITABLE) {
353		return;
354	}
355
356	debug!("Making P4 table writable...");
357
358	let make_writable = || unsafe {
359		let flags = flags | PageTableEntryFlags::WRITABLE;
360		match frame {
361			MappedFrame::Size1GiB(_) => pt.set_flags_p3_entry(p4_page, flags).unwrap().ignore(),
362			MappedFrame::Size2MiB(_) => pt.set_flags_p2_entry(p4_page, flags).unwrap().ignore(),
363			MappedFrame::Size4KiB(_) => pt.update_flags(p4_page, flags).unwrap().ignore(),
364		}
365	};
366
367	unsafe fn without_protect<F, R>(f: F) -> R
368	where
369		F: FnOnce() -> R,
370	{
371		let cr0 = Cr0::read();
372		if cr0.contains(Cr0Flags::WRITE_PROTECT) {
373			unsafe { Cr0::write(cr0 - Cr0Flags::WRITE_PROTECT) }
374		}
375		let ret = f();
376		if cr0.contains(Cr0Flags::WRITE_PROTECT) {
377			unsafe { Cr0::write(cr0) }
378		}
379		ret
380	}
381
382	unsafe { without_protect(make_writable) }
383}
384
385#[cfg(feature = "common-os")]
386pub fn create_new_root_page_table() -> usize {
387	use core::slice;
388
389	use x86_64::structures::paging::Size4KiB as BasePageSize;
390
391	use crate::mm::MappedPageBox;
392
393	let layout = PageLayout::from_size(BasePageSize::SIZE as usize).unwrap();
394	let frame_range = FrameAlloc::allocate(layout).unwrap();
395	let physaddr = PhysAddr::from(frame_range.start());
396
397	let flags = PageTableEntryFlags::normal();
398
399	let entry: u64 = unsafe {
400		let (frame, _flags) = Cr3::read();
401		let page_range =
402			MappedPageBox::map_phys(frame.start_address().into(), layout, flags).unwrap();
403		let entry: &u64 = &*VirtAddr::from(page_range.pages().start()).as_ptr();
404
405		*entry
406	};
407
408	let page_range = unsafe { MappedPageBox::map_phys(physaddr, layout, flags).unwrap() };
409	let slice_addr = VirtAddr::from(page_range.pages().start());
410
411	unsafe {
412		let pml4 = slice::from_raw_parts_mut(slice_addr.as_mut_ptr(), 512);
413
414		// clear PML4
415		for elem in pml4.iter_mut() {
416			*elem = 0;
417		}
418
419		// copy first element and the self reference
420		pml4[0] = entry;
421		// create self reference
422		pml4[511] = physaddr.as_u64() + 0x3; // PG_PRESENT | PG_RW
423	};
424
425	physaddr.as_usize()
426}
427
428pub unsafe fn log_page_tables() {
429	use log::Level;
430
431	use self::mapped_page_range_display::OffsetPageTableExt;
432
433	if !log_enabled!(Level::Trace) {
434		return;
435	}
436
437	let page_table = unsafe { identity_mapped_page_table() };
438	trace!("Page tables:\n{}", page_table.display());
439}
440
441pub mod mapped_page_range_display {
442	use core::fmt::{self, Write};
443
444	use x86_64::structures::paging::mapper::PageTableFrameMapping;
445	use x86_64::structures::paging::{MappedPageTable, OffsetPageTable, PageSize};
446
447	use super::mapped_page_table_iter::{
448		self, MappedPageRangeInclusive, MappedPageRangeInclusiveItem,
449		MappedPageTableRangeInclusiveIter,
450	};
451	use super::offset_page_table::PhysOffset;
452
453	#[expect(dead_code)]
454	pub trait MappedPageTableExt<P: PageTableFrameMapping + Clone> {
455		fn display(&self) -> MappedPageTableDisplay<'_, &P>;
456	}
457
458	impl<P: PageTableFrameMapping + Clone> MappedPageTableExt<P> for MappedPageTable<'_, P> {
459		fn display(&self) -> MappedPageTableDisplay<'_, &P> {
460			MappedPageTableDisplay {
461				inner: mapped_page_table_iter::mapped_page_table_range_iter(self),
462			}
463		}
464	}
465
466	pub trait OffsetPageTableExt {
467		fn display(&self) -> MappedPageTableDisplay<'_, PhysOffset>;
468	}
469
470	impl OffsetPageTableExt for OffsetPageTable<'_> {
471		fn display(&self) -> MappedPageTableDisplay<'_, PhysOffset> {
472			MappedPageTableDisplay {
473				inner: mapped_page_table_iter::offset_page_table_range_iter(self),
474			}
475		}
476	}
477
478	pub struct MappedPageTableDisplay<'a, P: PageTableFrameMapping + Clone> {
479		inner: MappedPageTableRangeInclusiveIter<'a, P>,
480	}
481
482	impl<P: PageTableFrameMapping + Clone> fmt::Display for MappedPageTableDisplay<'_, P> {
483		fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
484			let mut has_fields = false;
485
486			for mapped_page_range in self.inner.clone() {
487				if has_fields {
488					f.write_char('\n')?;
489				}
490				write!(f, "{}", mapped_page_range.display())?;
491
492				has_fields = true;
493			}
494
495			Ok(())
496		}
497	}
498
499	pub trait MappedPageRangeInclusiveItemExt {
500		fn display(&self) -> MappedPageRangeInclusiveItemDisplay<'_>;
501	}
502
503	impl MappedPageRangeInclusiveItemExt for MappedPageRangeInclusiveItem {
504		fn display(&self) -> MappedPageRangeInclusiveItemDisplay<'_> {
505			MappedPageRangeInclusiveItemDisplay { inner: self }
506		}
507	}
508
509	pub struct MappedPageRangeInclusiveItemDisplay<'a> {
510		inner: &'a MappedPageRangeInclusiveItem,
511	}
512
513	impl fmt::Display for MappedPageRangeInclusiveItemDisplay<'_> {
514		fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
515			match self.inner {
516				MappedPageRangeInclusiveItem::Size4KiB(range) => range.display().fmt(f),
517				MappedPageRangeInclusiveItem::Size2MiB(range) => range.display().fmt(f),
518				MappedPageRangeInclusiveItem::Size1GiB(range) => range.display().fmt(f),
519			}
520		}
521	}
522
523	pub trait MappedPageRangeInclusiveExt<S: PageSize> {
524		fn display(&self) -> MappedPageRangeInclusiveDisplay<'_, S>;
525	}
526
527	impl<S: PageSize> MappedPageRangeInclusiveExt<S> for MappedPageRangeInclusive<S> {
528		fn display(&self) -> MappedPageRangeInclusiveDisplay<'_, S> {
529			MappedPageRangeInclusiveDisplay { inner: self }
530		}
531	}
532
533	pub struct MappedPageRangeInclusiveDisplay<'a, S: PageSize> {
534		inner: &'a MappedPageRangeInclusive<S>,
535	}
536
537	impl<S: PageSize> fmt::Display for MappedPageRangeInclusiveDisplay<'_, S> {
538		fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
539			let size = S::DEBUG_STR;
540			let len = self.inner.page_range.len();
541			let page_start = self.inner.page_range.start.start_address();
542			let page_end = self.inner.page_range.end.start_address();
543			let frame_start = self.inner.frame_range.start.start_address();
544			let frame_end = self.inner.frame_range.end.start_address();
545			let flags = self.inner.flags;
546			let format_phys = if page_start.as_u64() == frame_start.as_u64() {
547				assert_eq!(page_end.as_u64(), frame_end.as_u64());
548				format_args!("{:>39}", "identity mapped")
549			} else {
550				format_args!("{frame_start:18p}..={frame_end:18p}")
551			};
552			write!(
553				f,
554				"size: {size}, len: {len:5}, virt: {page_start:18p}..={page_end:18p}, phys: {format_phys}, flags: {flags:?}"
555			)
556		}
557	}
558}
559
560pub mod mapped_page_table_iter {
561	//! TODO: try to upstream this to [`x86_64`].
562
563	use core::fmt;
564	use core::ops::{Add, AddAssign, Sub, SubAssign};
565
566	use x86_64::structures::paging::frame::PhysFrameRangeInclusive;
567	use x86_64::structures::paging::mapper::PageTableFrameMapping;
568	use x86_64::structures::paging::page::{AddressNotAligned, PageRangeInclusive};
569	use x86_64::structures::paging::{
570		MappedPageTable, OffsetPageTable, Page, PageSize, PageTable, PageTableFlags,
571		PageTableIndex, PhysFrame, Size1GiB, Size2MiB, Size4KiB,
572	};
573
574	use super::offset_page_table::PhysOffset;
575	use super::walker::{PageTableWalkError, PageTableWalker};
576
577	#[derive(Debug)]
578	pub struct MappedPageRangeInclusive<S: PageSize> {
579		pub page_range: PageRangeInclusive<S>,
580		pub frame_range: PhysFrameRangeInclusive<S>,
581		pub flags: PageTableFlags,
582	}
583
584	impl<S: PageSize> TryFrom<(MappedPage<S>, MappedPage<S>)> for MappedPageRangeInclusive<S> {
585		type Error = TryFromMappedPageError;
586
587		fn try_from((start, end): (MappedPage<S>, MappedPage<S>)) -> Result<Self, Self::Error> {
588			if start.flags != end.flags {
589				return Err(TryFromMappedPageError);
590			}
591
592			Ok(Self {
593				page_range: PageRangeInclusive {
594					start: start.page,
595					end: end.page,
596				},
597				frame_range: PhysFrameRangeInclusive {
598					start: start.frame,
599					end: end.frame,
600				},
601				flags: start.flags,
602			})
603		}
604	}
605
606	#[derive(Debug)]
607	pub enum MappedPageRangeInclusiveItem {
608		Size4KiB(MappedPageRangeInclusive<Size4KiB>),
609		Size2MiB(MappedPageRangeInclusive<Size2MiB>),
610		Size1GiB(MappedPageRangeInclusive<Size1GiB>),
611	}
612
613	impl TryFrom<(MappedPageItem, MappedPageItem)> for MappedPageRangeInclusiveItem {
614		type Error = TryFromMappedPageError;
615
616		fn try_from((start, end): (MappedPageItem, MappedPageItem)) -> Result<Self, Self::Error> {
617			match (start, end) {
618				(MappedPageItem::Size4KiB(start), MappedPageItem::Size4KiB(end)) => {
619					let range = MappedPageRangeInclusive::try_from((start, end))?;
620					Ok(Self::Size4KiB(range))
621				}
622				(MappedPageItem::Size2MiB(start), MappedPageItem::Size2MiB(end)) => {
623					let range = MappedPageRangeInclusive::try_from((start, end))?;
624					Ok(Self::Size2MiB(range))
625				}
626				(MappedPageItem::Size1GiB(start), MappedPageItem::Size1GiB(end)) => {
627					let range = MappedPageRangeInclusive::try_from((start, end))?;
628					Ok(Self::Size1GiB(range))
629				}
630				(_, _) => Err(TryFromMappedPageError),
631			}
632		}
633	}
634
635	#[derive(PartialEq, Eq, Clone, Debug)]
636	pub struct TryFromMappedPageError;
637
638	impl fmt::Display for TryFromMappedPageError {
639		fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
640			f.write_str("provided mapped pages were not compatible")
641		}
642	}
643
644	#[derive(Clone)]
645	pub struct MappedPageTableRangeInclusiveIter<'a, P: PageTableFrameMapping> {
646		inner: MappedPageTableIter<'a, P>,
647		start: Option<MappedPageItem>,
648		end: Option<MappedPageItem>,
649	}
650
651	pub fn mapped_page_table_range_iter<'a, P: PageTableFrameMapping>(
652		page_table: &'a MappedPageTable<'a, P>,
653	) -> MappedPageTableRangeInclusiveIter<'a, &'a P> {
654		MappedPageTableRangeInclusiveIter {
655			inner: mapped_page_table_iter(page_table),
656			start: None,
657			end: None,
658		}
659	}
660
661	pub fn offset_page_table_range_iter<'a>(
662		page_table: &'a OffsetPageTable<'a>,
663	) -> MappedPageTableRangeInclusiveIter<'a, PhysOffset> {
664		MappedPageTableRangeInclusiveIter {
665			inner: offset_page_table_iter(page_table),
666			start: None,
667			end: None,
668		}
669	}
670
671	impl<P: PageTableFrameMapping> Iterator for MappedPageTableRangeInclusiveIter<'_, P> {
672		type Item = MappedPageRangeInclusiveItem;
673
674		fn next(&mut self) -> Option<Self::Item> {
675			if self.start.is_none() {
676				self.start = self.inner.next();
677				self.end = self.start;
678			}
679
680			let Some(start) = &mut self.start else {
681				return None;
682			};
683			let end = self.end.as_mut().unwrap();
684
685			for mapped_page in self.inner.by_ref() {
686				if mapped_page == *end + 1 {
687					*end = mapped_page;
688					continue;
689				}
690
691				let range = MappedPageRangeInclusiveItem::try_from((*start, *end)).unwrap();
692				*start = mapped_page;
693				*end = mapped_page;
694				return Some(range);
695			}
696
697			let range = MappedPageRangeInclusiveItem::try_from((*start, *end)).unwrap();
698			self.start = None;
699			self.end = None;
700			Some(range)
701		}
702	}
703
704	#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
705	pub struct MappedPage<S: PageSize> {
706		pub page: Page<S>,
707		pub frame: PhysFrame<S>,
708		pub flags: PageTableFlags,
709	}
710
711	impl<S: PageSize> Add<u64> for MappedPage<S> {
712		type Output = Self;
713
714		fn add(self, rhs: u64) -> Self::Output {
715			Self {
716				page: self.page + rhs,
717				frame: self.frame + rhs,
718				flags: self.flags,
719			}
720		}
721	}
722
723	impl<S: PageSize> Sub<u64> for MappedPage<S> {
724		type Output = Self;
725
726		fn sub(self, rhs: u64) -> Self::Output {
727			Self {
728				page: self.page - rhs,
729				frame: self.frame - rhs,
730				flags: self.flags,
731			}
732		}
733	}
734
735	#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
736	pub enum MappedPageItem {
737		Size4KiB(MappedPage<Size4KiB>),
738		Size2MiB(MappedPage<Size2MiB>),
739		Size1GiB(MappedPage<Size1GiB>),
740	}
741
742	impl Add<u64> for MappedPageItem {
743		type Output = Self;
744
745		fn add(self, rhs: u64) -> Self::Output {
746			match self {
747				Self::Size4KiB(mapped_page) => Self::Size4KiB(mapped_page + rhs),
748				Self::Size2MiB(mapped_page) => Self::Size2MiB(mapped_page + rhs),
749				Self::Size1GiB(mapped_page) => Self::Size1GiB(mapped_page + rhs),
750			}
751		}
752	}
753
754	impl AddAssign<u64> for MappedPageItem {
755		fn add_assign(&mut self, rhs: u64) {
756			*self = *self + rhs;
757		}
758	}
759
760	impl Sub<u64> for MappedPageItem {
761		type Output = Self;
762
763		fn sub(self, rhs: u64) -> Self::Output {
764			match self {
765				Self::Size4KiB(mapped_page) => Self::Size4KiB(mapped_page - rhs),
766				Self::Size2MiB(mapped_page) => Self::Size2MiB(mapped_page - rhs),
767				Self::Size1GiB(mapped_page) => Self::Size1GiB(mapped_page - rhs),
768			}
769		}
770	}
771
772	impl SubAssign<u64> for MappedPageItem {
773		fn sub_assign(&mut self, rhs: u64) {
774			*self = *self - rhs;
775		}
776	}
777
778	#[derive(Clone)]
779	pub struct MappedPageTableIter<'a, P: PageTableFrameMapping> {
780		page_table_walker: PageTableWalker<P>,
781		level_4_table: &'a PageTable,
782		p4_index: u16,
783		p3_index: u16,
784		p2_index: u16,
785		p1_index: u16,
786	}
787
788	pub fn mapped_page_table_iter<'a, P: PageTableFrameMapping>(
789		page_table: &'a MappedPageTable<'a, P>,
790	) -> MappedPageTableIter<'a, &'a P> {
791		MappedPageTableIter {
792			page_table_walker: unsafe {
793				PageTableWalker::new(page_table.page_table_frame_mapping())
794			},
795			level_4_table: page_table.level_4_table(),
796			p4_index: 0,
797			p3_index: 0,
798			p2_index: 0,
799			p1_index: 0,
800		}
801	}
802
803	pub fn offset_page_table_iter<'a>(
804		page_table: &'a OffsetPageTable<'a>,
805	) -> MappedPageTableIter<'a, PhysOffset> {
806		MappedPageTableIter {
807			page_table_walker: unsafe {
808				PageTableWalker::new(PhysOffset {
809					offset: page_table.phys_offset(),
810				})
811			},
812			level_4_table: page_table.level_4_table(),
813			p4_index: 0,
814			p3_index: 0,
815			p2_index: 0,
816			p1_index: 0,
817		}
818	}
819
820	impl<P: PageTableFrameMapping> MappedPageTableIter<'_, P> {
821		fn p4_index(&self) -> Option<PageTableIndex> {
822			if self.p4_index >= 512 {
823				return None;
824			}
825
826			Some(PageTableIndex::new(self.p4_index))
827		}
828
829		fn p3_index(&self) -> Option<PageTableIndex> {
830			if self.p3_index >= 512 {
831				return None;
832			}
833
834			Some(PageTableIndex::new(self.p3_index))
835		}
836
837		fn p2_index(&self) -> Option<PageTableIndex> {
838			if self.p2_index >= 512 {
839				return None;
840			}
841
842			Some(PageTableIndex::new(self.p2_index))
843		}
844
845		fn p1_index(&self) -> Option<PageTableIndex> {
846			if self.p1_index >= 512 {
847				return None;
848			}
849
850			Some(PageTableIndex::new(self.p1_index))
851		}
852
853		fn increment_p4_index(&mut self) -> Option<()> {
854			if self.p4_index >= 511 {
855				self.p4_index += 1;
856				return None;
857			}
858
859			self.p4_index += 1;
860			self.p3_index = 0;
861			self.p2_index = 0;
862			self.p1_index = 0;
863			Some(())
864		}
865
866		fn increment_p3_index(&mut self) -> Option<()> {
867			if self.p3_index == 511 {
868				self.increment_p4_index()?;
869				return None;
870			}
871
872			self.p3_index += 1;
873			self.p2_index = 0;
874			self.p1_index = 0;
875			Some(())
876		}
877
878		fn increment_p2_index(&mut self) -> Option<()> {
879			if self.p2_index == 511 {
880				self.increment_p3_index()?;
881				return None;
882			}
883
884			self.p2_index += 1;
885			self.p1_index = 0;
886			Some(())
887		}
888
889		fn increment_p1_index(&mut self) -> Option<()> {
890			if self.p1_index == 511 {
891				self.increment_p2_index()?;
892				return None;
893			}
894
895			self.p1_index += 1;
896			Some(())
897		}
898
899		fn next_forward(&mut self) -> Option<MappedPageItem> {
900			let p4 = self.level_4_table;
901
902			let p3 = loop {
903				match self.page_table_walker.next_table(&p4[self.p4_index()?]) {
904					Ok(page_table) => break page_table,
905					Err(PageTableWalkError::NotMapped) => self.increment_p4_index()?,
906					Err(PageTableWalkError::MappedToHugePage) => {
907						panic!("level 4 entry has huge page bit set")
908					}
909				}
910			};
911
912			let p2 = loop {
913				match self.page_table_walker.next_table(&p3[self.p3_index()?]) {
914					Ok(page_table) => break page_table,
915					Err(PageTableWalkError::NotMapped) => self.increment_p3_index()?,
916					Err(PageTableWalkError::MappedToHugePage) => {
917						let page =
918							Page::from_page_table_indices_1gib(self.p4_index()?, self.p3_index()?);
919						let entry = &p3[self.p3_index()?];
920						let frame = PhysFrame::containing_address(entry.addr());
921						let flags = entry.flags();
922						let mapped_page =
923							MappedPageItem::Size1GiB(MappedPage { page, frame, flags });
924
925						self.increment_p3_index();
926						return Some(mapped_page);
927					}
928				}
929			};
930
931			let p1 = loop {
932				match self.page_table_walker.next_table(&p2[self.p2_index()?]) {
933					Ok(page_table) => break page_table,
934					Err(PageTableWalkError::NotMapped) => self.increment_p2_index()?,
935					Err(PageTableWalkError::MappedToHugePage) => {
936						let page = Page::from_page_table_indices_2mib(
937							self.p4_index()?,
938							self.p3_index()?,
939							self.p2_index()?,
940						);
941						let entry = &p2[self.p2_index()?];
942						let frame = PhysFrame::containing_address(entry.addr());
943						let flags = entry.flags();
944						let mapped_page =
945							MappedPageItem::Size2MiB(MappedPage { page, frame, flags });
946
947						self.increment_p2_index();
948						return Some(mapped_page);
949					}
950				}
951			};
952
953			loop {
954				let p1_entry = &p1[self.p1_index()?];
955
956				if p1_entry.is_unused() {
957					self.increment_p1_index()?;
958					continue;
959				}
960
961				let frame = match PhysFrame::from_start_address(p1_entry.addr()) {
962					Ok(frame) => frame,
963					Err(AddressNotAligned) => {
964						warn!("Invalid frame address: {:p}", p1_entry.addr());
965						self.increment_p1_index()?;
966						continue;
967					}
968				};
969
970				let page = Page::from_page_table_indices(
971					self.p4_index()?,
972					self.p3_index()?,
973					self.p2_index()?,
974					self.p1_index()?,
975				);
976				let flags = p1_entry.flags();
977				let mapped_page = MappedPageItem::Size4KiB(MappedPage { page, frame, flags });
978
979				self.increment_p1_index();
980				return Some(mapped_page);
981			}
982		}
983	}
984
985	impl<P: PageTableFrameMapping> Iterator for MappedPageTableIter<'_, P> {
986		type Item = MappedPageItem;
987
988		fn next(&mut self) -> Option<Self::Item> {
989			self.next_forward().or_else(|| self.next_forward())
990		}
991	}
992}
993
994mod walker {
995	//! Taken from [`x86_64`]
996
997	use x86_64::structures::paging::PageTable;
998	use x86_64::structures::paging::mapper::PageTableFrameMapping;
999	use x86_64::structures::paging::page_table::{FrameError, PageTableEntry};
1000
1001	#[derive(Clone, Debug)]
1002	pub(super) struct PageTableWalker<P: PageTableFrameMapping> {
1003		page_table_frame_mapping: P,
1004	}
1005
1006	impl<P: PageTableFrameMapping> PageTableWalker<P> {
1007		#[inline]
1008		pub unsafe fn new(page_table_frame_mapping: P) -> Self {
1009			Self {
1010				page_table_frame_mapping,
1011			}
1012		}
1013
1014		/// Internal helper function to get a reference to the page table of the next level.
1015		///
1016		/// Returns `PageTableWalkError::NotMapped` if the entry is unused. Returns
1017		/// `PageTableWalkError::MappedToHugePage` if the `HUGE_PAGE` flag is set
1018		/// in the passed entry.
1019		#[inline]
1020		pub(super) fn next_table<'b>(
1021			&self,
1022			entry: &'b PageTableEntry,
1023		) -> Result<&'b PageTable, PageTableWalkError> {
1024			let page_table_ptr = self
1025				.page_table_frame_mapping
1026				.frame_to_pointer(entry.frame()?);
1027			let page_table: &PageTable = unsafe { &*page_table_ptr };
1028
1029			Ok(page_table)
1030		}
1031	}
1032
1033	#[derive(Debug)]
1034	pub(super) enum PageTableWalkError {
1035		NotMapped,
1036		MappedToHugePage,
1037	}
1038
1039	impl From<FrameError> for PageTableWalkError {
1040		#[inline]
1041		fn from(err: FrameError) -> Self {
1042			match err {
1043				FrameError::HugeFrame => PageTableWalkError::MappedToHugePage,
1044				FrameError::FrameNotPresent => PageTableWalkError::NotMapped,
1045			}
1046		}
1047	}
1048}
1049
1050mod offset_page_table {
1051	//! Taken from [`x86_64`]
1052
1053	use x86_64::VirtAddr;
1054	use x86_64::structures::paging::mapper::PageTableFrameMapping;
1055	use x86_64::structures::paging::{PageTable, PhysFrame};
1056
1057	#[derive(Clone, Debug)]
1058	pub struct PhysOffset {
1059		pub offset: VirtAddr,
1060	}
1061
1062	unsafe impl PageTableFrameMapping for PhysOffset {
1063		fn frame_to_pointer(&self, frame: PhysFrame) -> *mut PageTable {
1064			let virt = self.offset + frame.start_address().as_u64();
1065			virt.as_mut_ptr()
1066		}
1067	}
1068}