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