corevm_engine/
page_mapper.rs

1use corevm_host::{Range, RangeSet, PAGE_SIZE};
2use log::{debug, trace};
3
4/// Maps memory blocks of arbitrary sizes onto the heap address range.
5#[derive(Debug)]
6pub struct PageMapper {
7	/// Heap page range.
8	pub heap_page_range: Range,
9	/// Mapped page indices (i.e. address / 4096).
10	pub pages: RangeSet,
11}
12
13impl PageMapper {
14	pub fn new(pages: RangeSet, heap_page_range: Range) -> Self {
15		// Sanity checks.
16		if let Some(range) = pages.enclosing_range() {
17			let next_page_index = range.end;
18			assert!(
19				(heap_page_range.start..=heap_page_range.end).contains(&next_page_index),
20				"Heap page range {:?} doesn't contain the next page index {}",
21				heap_page_range,
22				next_page_index
23			);
24		}
25		Self { heap_page_range, pages }
26	}
27
28	pub fn map(&mut self, num_pages: u64) -> Option<Range> {
29		if num_pages == 0 {
30			return None;
31		}
32		let Some(page_range) = self.find_unmapped_page_range(num_pages) else {
33			debug!("Failed to map {num_pages} guest memory page(s): no next address");
34			return None;
35		};
36		self.pages.insert(page_range.clone());
37		trace!(
38			"Mapped guest memory pages {:#x}..{:#x}",
39			page_range.start * PAGE_SIZE,
40			page_range.end * PAGE_SIZE,
41		);
42		Some(page_range)
43	}
44
45	pub fn unmap(&mut self, start_page: u64, end_page: u64) {
46		self.pages.remove(&Range::new(start_page, end_page));
47	}
48
49	pub fn is_mapped(&self, page: u64) -> bool {
50		self.pages.as_ref().iter().any(|range| range.contains(page))
51	}
52
53	fn find_unmapped_page_range(&mut self, num_pages: u64) -> Option<Range> {
54		let start_page = match self.pages.enclosing_range() {
55			Some(range) => range.end,
56			None => self.heap_page_range.start,
57		};
58		let end_page = start_page.checked_add(num_pages)?;
59		if end_page > self.heap_page_range.end {
60			return None;
61		}
62		Some(Range::new(start_page, end_page))
63	}
64}
65
66#[cfg(test)]
67mod tests {
68	use super::*;
69
70	#[test]
71	fn map_works() {
72		let mut mapper = PageMapper::new(Default::default(), Range::new(1, 10));
73		assert_eq!(None, mapper.map(0));
74		assert_eq!(None, mapper.map(10));
75		assert_eq!(None, mapper.map(u64::MAX));
76		assert_eq!(Some(Range::new(1, 2)), mapper.map(1));
77		mapper.unmap(0, u64::MAX);
78		assert_eq!(0, mapper.pages.as_ref().len(), "{:?}", mapper);
79		assert_eq!(Some(Range::new(1, 10)), mapper.map(9));
80		mapper.unmap(0, u64::MAX);
81		assert_eq!(0, mapper.pages.as_ref().len(), "{:?}", mapper);
82		assert_eq!(Some(Range::new(1, 2)), mapper.map(1));
83	}
84}