1#![allow(clippy::unwrap_used, clippy::expect_used)]
7
8use memf_format::{PhysicalMemoryProvider, PhysicalRange};
9
10#[derive(Debug, Clone)]
12pub struct SyntheticPhysMem {
13 data: Vec<u8>,
14}
15
16impl SyntheticPhysMem {
17 pub fn new(size: usize) -> Self {
19 Self {
20 data: vec![0u8; size],
21 }
22 }
23
24 pub fn write_bytes(&mut self, addr: u64, bytes: &[u8]) {
26 let start = addr as usize;
27 self.data[start..start + bytes.len()].copy_from_slice(bytes);
28 }
29
30 pub fn write_u64(&mut self, addr: u64, value: u64) {
32 self.write_bytes(addr, &value.to_le_bytes());
33 }
34
35 pub fn read_u64(&self, addr: u64) -> u64 {
37 let start = addr as usize;
38 u64::from_le_bytes(self.data[start..start + 8].try_into().unwrap())
39 }
40
41 pub fn data(&self) -> &[u8] {
43 &self.data
44 }
45}
46
47impl PhysicalMemoryProvider for SyntheticPhysMem {
48 fn read_phys(&self, addr: u64, buf: &mut [u8]) -> memf_format::Result<usize> {
49 if buf.is_empty() {
50 return Ok(0);
51 }
52 let start = addr as usize;
53 if start >= self.data.len() {
54 return Ok(0);
55 }
56 let available = self.data.len() - start;
57 let to_read = buf.len().min(available);
58 buf[..to_read].copy_from_slice(&self.data[start..start + to_read]);
59 Ok(to_read)
60 }
61
62 fn ranges(&self) -> &[PhysicalRange] {
63 &[]
64 }
65 fn format_name(&self) -> &str {
66 "Synthetic"
67 }
68}
69
70pub mod flags {
72 pub const PRESENT: u64 = 1 << 0;
74 pub const WRITABLE: u64 = 1 << 1;
76 pub const USER: u64 = 1 << 2;
78 pub const PS: u64 = 1 << 7;
80}
81
82pub struct PageTableBuilder {
84 mem: SyntheticPhysMem,
85 next_page: u64,
86 cr3: u64,
87}
88
89impl PageTableBuilder {
90 pub const CR3: u64 = 0x0000_0000;
92 const ADDR_MASK: u64 = 0x000F_FFFF_FFFF_F000;
93
94 pub fn new() -> Self {
96 let mut mem = SyntheticPhysMem::new(16 * 1024 * 1024);
97 let cr3 = Self::CR3;
98 let next_page = 0x1000;
99 for i in 0..512 {
101 mem.write_u64(cr3 + i * 8, 0);
102 }
103 Self {
104 mem,
105 next_page,
106 cr3,
107 }
108 }
109
110 fn alloc_page(&mut self) -> u64 {
111 let addr = self.next_page;
112 self.next_page += 0x1000;
113 for i in 0..512 {
115 self.mem.write_u64(addr + i * 8, 0);
116 }
117 addr
118 }
119
120 pub fn map_4k(mut self, vaddr: u64, paddr: u64, page_flags: u64) -> Self {
122 let pml4_idx = (vaddr >> 39) & 0x1FF;
123 let pdpt_idx = (vaddr >> 30) & 0x1FF;
124 let pd_idx = (vaddr >> 21) & 0x1FF;
125 let pt_idx = (vaddr >> 12) & 0x1FF;
126
127 let pml4e_addr = self.cr3 + pml4_idx * 8;
129 let mut pml4e = self.mem.read_u64(pml4e_addr);
130 if pml4e & flags::PRESENT == 0 {
131 let pdpt_page = self.alloc_page();
132 pml4e = pdpt_page | flags::PRESENT | flags::WRITABLE;
133 self.mem.write_u64(pml4e_addr, pml4e);
134 }
135 let pdpt_base = pml4e & Self::ADDR_MASK;
136
137 let pdpte_addr = pdpt_base + pdpt_idx * 8;
139 let mut pdpte = self.mem.read_u64(pdpte_addr);
140 if pdpte & flags::PRESENT == 0 {
141 let pd_page = self.alloc_page();
142 pdpte = pd_page | flags::PRESENT | flags::WRITABLE;
143 self.mem.write_u64(pdpte_addr, pdpte);
144 }
145 let pd_base = pdpte & Self::ADDR_MASK;
146
147 let pde_addr = pd_base + pd_idx * 8;
149 let mut pde = self.mem.read_u64(pde_addr);
150 if pde & flags::PRESENT == 0 {
151 let pt_page = self.alloc_page();
152 pde = pt_page | flags::PRESENT | flags::WRITABLE;
153 self.mem.write_u64(pde_addr, pde);
154 }
155 let pt_base = pde & Self::ADDR_MASK;
156
157 let pte_addr = pt_base + pt_idx * 8;
159 let pte = (paddr & Self::ADDR_MASK) | page_flags | flags::PRESENT;
160 self.mem.write_u64(pte_addr, pte);
161 self
162 }
163
164 pub fn map_2m(mut self, vaddr: u64, paddr: u64, page_flags: u64) -> Self {
166 let pml4_idx = (vaddr >> 39) & 0x1FF;
167 let pdpt_idx = (vaddr >> 30) & 0x1FF;
168 let pd_idx = (vaddr >> 21) & 0x1FF;
169
170 let pml4e_addr = self.cr3 + pml4_idx * 8;
172 let mut pml4e = self.mem.read_u64(pml4e_addr);
173 if pml4e & flags::PRESENT == 0 {
174 let pdpt_page = self.alloc_page();
175 pml4e = pdpt_page | flags::PRESENT | flags::WRITABLE;
176 self.mem.write_u64(pml4e_addr, pml4e);
177 }
178 let pdpt_base = pml4e & Self::ADDR_MASK;
179
180 let pdpte_addr = pdpt_base + pdpt_idx * 8;
182 let mut pdpte = self.mem.read_u64(pdpte_addr);
183 if pdpte & flags::PRESENT == 0 {
184 let pd_page = self.alloc_page();
185 pdpte = pd_page | flags::PRESENT | flags::WRITABLE;
186 self.mem.write_u64(pdpte_addr, pdpte);
187 }
188 let pd_base = pdpte & Self::ADDR_MASK;
189
190 let pde_addr = pd_base + pd_idx * 8;
192 let pde = (paddr & 0x000F_FFFF_FFE0_0000) | page_flags | flags::PRESENT | flags::PS;
193 self.mem.write_u64(pde_addr, pde);
194 self
195 }
196
197 pub fn map_1g(mut self, vaddr: u64, paddr: u64, page_flags: u64) -> Self {
199 let pml4_idx = (vaddr >> 39) & 0x1FF;
200 let pdpt_idx = (vaddr >> 30) & 0x1FF;
201
202 let pml4e_addr = self.cr3 + pml4_idx * 8;
204 let mut pml4e = self.mem.read_u64(pml4e_addr);
205 if pml4e & flags::PRESENT == 0 {
206 let pdpt_page = self.alloc_page();
207 pml4e = pdpt_page | flags::PRESENT | flags::WRITABLE;
208 self.mem.write_u64(pml4e_addr, pml4e);
209 }
210 let pdpt_base = pml4e & Self::ADDR_MASK;
211
212 let pdpte_addr = pdpt_base + pdpt_idx * 8;
214 let pdpte = (paddr & 0x000F_FFFF_C000_0000) | page_flags | flags::PRESENT | flags::PS;
215 self.mem.write_u64(pdpte_addr, pdpte);
216 self
217 }
218
219 fn ensure_pt_entry(&mut self, vaddr: u64) -> u64 {
221 let pml4_idx = (vaddr >> 39) & 0x1FF;
222 let pdpt_idx = (vaddr >> 30) & 0x1FF;
223 let pd_idx = (vaddr >> 21) & 0x1FF;
224 let pt_idx = (vaddr >> 12) & 0x1FF;
225
226 let pml4e_addr = self.cr3 + pml4_idx * 8;
228 let mut pml4e = self.mem.read_u64(pml4e_addr);
229 if pml4e & flags::PRESENT == 0 {
230 let pdpt_page = self.alloc_page();
231 pml4e = pdpt_page | flags::PRESENT | flags::WRITABLE;
232 self.mem.write_u64(pml4e_addr, pml4e);
233 }
234 let pdpt_base = pml4e & Self::ADDR_MASK;
235
236 let pdpte_addr = pdpt_base + pdpt_idx * 8;
238 let mut pdpte = self.mem.read_u64(pdpte_addr);
239 if pdpte & flags::PRESENT == 0 {
240 let pd_page = self.alloc_page();
241 pdpte = pd_page | flags::PRESENT | flags::WRITABLE;
242 self.mem.write_u64(pdpte_addr, pdpte);
243 }
244 let pd_base = pdpte & Self::ADDR_MASK;
245
246 let pde_addr = pd_base + pd_idx * 8;
248 let mut pde = self.mem.read_u64(pde_addr);
249 if pde & flags::PRESENT == 0 {
250 let pt_page = self.alloc_page();
251 pde = pt_page | flags::PRESENT | flags::WRITABLE;
252 self.mem.write_u64(pde_addr, pde);
253 }
254 let pt_base = pde & Self::ADDR_MASK;
255
256 pt_base + pt_idx * 8
257 }
258
259 pub fn map_demand_zero(mut self, vaddr: u64) -> Self {
261 let pte_addr = self.ensure_pt_entry(vaddr);
262 self.mem.write_u64(pte_addr, 0);
264 self
265 }
266
267 pub fn map_transition(mut self, vaddr: u64, pfn: u64) -> Self {
269 let pte_addr = self.ensure_pt_entry(vaddr);
270 let pte = (pfn << 12) | (1 << 11);
271 self.mem.write_u64(pte_addr, pte);
272 self
273 }
274
275 pub fn map_pagefile(mut self, vaddr: u64, pagefile_num: u8, page_offset: u64) -> Self {
277 let pte_addr = self.ensure_pt_entry(vaddr);
278 let pte = ((u64::from(pagefile_num) & 0xF) << 1) | (page_offset << 12);
279 self.mem.write_u64(pte_addr, pte);
280 self
281 }
282
283 pub fn map_prototype(mut self, vaddr: u64) -> Self {
285 let pte_addr = self.ensure_pt_entry(vaddr);
286 let pte: u64 = 1 << 10;
287 self.mem.write_u64(pte_addr, pte);
288 self
289 }
290
291 pub fn map_prototype_raw(mut self, vaddr: u64, raw_pte: u64) -> Self {
293 assert!(raw_pte & (1 << 10) != 0, "prototype bit (10) must be set");
294 assert!(raw_pte & 1 == 0, "PRESENT bit must be clear");
295 let pte_addr = self.ensure_pt_entry(vaddr);
296 self.mem.write_u64(pte_addr, raw_pte);
297 self
298 }
299
300 pub fn write_phys(mut self, addr: u64, data: &[u8]) -> Self {
302 self.mem.write_bytes(addr, data);
303 self
304 }
305
306 pub fn write_phys_u64(mut self, addr: u64, value: u64) -> Self {
308 self.mem.write_u64(addr, value);
309 self
310 }
311
312 pub fn build(self) -> (u64, SyntheticPhysMem) {
314 (self.cr3, self.mem)
315 }
316}
317
318impl Default for PageTableBuilder {
319 fn default() -> Self {
320 Self::new()
321 }
322}
323
324pub struct MockPagefileSource {
326 pagefile_num: u8,
327 pages: std::collections::HashMap<u64, [u8; 4096]>,
328}
329
330impl MockPagefileSource {
331 pub fn new(pagefile_num: u8, pages: Vec<(u64, [u8; 4096])>) -> Self {
334 Self {
335 pagefile_num,
336 pages: pages.into_iter().collect(),
337 }
338 }
339}
340
341pub struct MockPrototypePteSource {
343 entries: std::collections::HashMap<u64, u64>,
345}
346
347impl MockPrototypePteSource {
348 pub fn new(entries: Vec<(u64, u64)>) -> Self {
350 Self {
351 entries: entries.into_iter().collect(),
352 }
353 }
354}
355
356impl crate::proto_pte::PrototypePteSource for MockPrototypePteSource {
357 fn resolve(&self, pte_value: u64) -> Option<u64> {
358 self.entries.get(&pte_value).copied()
359 }
360}
361
362impl crate::pagefile::PagefileSource for MockPagefileSource {
363 fn pagefile_number(&self) -> u8 {
364 self.pagefile_num
365 }
366
367 fn read_page(&self, page_offset: u64) -> crate::Result<Option<[u8; 4096]>> {
368 Ok(self.pages.get(&page_offset).copied())
369 }
370}
371
372pub fn make_reader(
377 isf: &memf_symbols::test_builders::IsfBuilder,
378) -> crate::object_reader::ObjectReader<SyntheticPhysMem> {
379 let json = isf.build_json();
380 let resolver = memf_symbols::isf::IsfResolver::from_value(&json).unwrap();
381 let (cr3, mem) = PageTableBuilder::new().build();
382 let vas = crate::vas::VirtualAddressSpace::new(
383 mem,
384 cr3,
385 crate::vas::TranslationMode::X86_64FourLevel,
386 );
387 crate::object_reader::ObjectReader::new(vas, Box::new(resolver))
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393
394 #[test]
395 fn synthetic_mem_read_write() {
396 let mut mem = SyntheticPhysMem::new(4096);
397 mem.write_bytes(0x100, &[0xAA, 0xBB, 0xCC, 0xDD]);
398 let mut buf = [0u8; 4];
399 let n = mem.read_phys(0x100, &mut buf).unwrap();
400 assert_eq!(n, 4);
401 assert_eq!(buf, [0xAA, 0xBB, 0xCC, 0xDD]);
402 }
403
404 #[test]
405 fn synthetic_mem_u64() {
406 let mut mem = SyntheticPhysMem::new(4096);
407 mem.write_u64(0x200, 0xDEAD_BEEF_CAFE_BABE);
408 assert_eq!(mem.read_u64(0x200), 0xDEAD_BEEF_CAFE_BABE);
409 }
410
411 #[test]
412 fn page_table_builder_creates_pml4() {
413 let (cr3, mem) = PageTableBuilder::new().build();
414 assert_eq!(cr3, 0);
415 for i in 0..512 {
416 assert_eq!(mem.read_u64(cr3 + i * 8), 0);
417 }
418 }
419
420 #[test]
421 fn page_table_builder_map_4k() {
422 let vaddr: u64 = 0xFFFF_8000_0010_0000;
423 let paddr: u64 = 0x0080_0000;
424 let (cr3, mem) = PageTableBuilder::new()
425 .map_4k(vaddr, paddr, flags::WRITABLE)
426 .write_phys(paddr, &[0x42; 64])
427 .build();
428 let pml4_idx = (vaddr >> 39) & 0x1FF;
429 let pml4e = mem.read_u64(cr3 + pml4_idx * 8);
430 assert_ne!(pml4e & flags::PRESENT, 0);
431 let mut buf = [0u8; 4];
432 mem.read_phys(paddr, &mut buf).unwrap();
433 assert_eq!(buf, [0x42; 4]);
434 }
435
436 #[test]
437 fn page_table_builder_map_2m() {
438 let vaddr: u64 = 0xFFFF_8000_0020_0000;
439 let paddr: u64 = 0x0100_0000;
440 let (cr3, mem) = PageTableBuilder::new()
441 .map_2m(vaddr, paddr, flags::WRITABLE)
442 .build();
443 let pml4_idx = (vaddr >> 39) & 0x1FF;
444 let pml4e = mem.read_u64(cr3 + pml4_idx * 8);
445 assert_ne!(pml4e & flags::PRESENT, 0);
446 }
447
448 #[test]
449 fn mock_pagefile_source_read_page() {
450 use crate::pagefile::PagefileSource;
451
452 let mut page_data = [0xABu8; 4096];
453 page_data[0] = 0x42;
454 let mock = MockPagefileSource::new(0, vec![(0x10, page_data)]);
455 assert_eq!(mock.pagefile_number(), 0);
456 let page = mock.read_page(0x10).unwrap().unwrap();
457 assert_eq!(page[0], 0x42);
458 assert_eq!(page[1], 0xAB);
459 }
460
461 #[test]
462 fn mock_pagefile_source_missing_page() {
463 use crate::pagefile::PagefileSource;
464
465 let mock = MockPagefileSource::new(1, vec![]);
466 assert_eq!(mock.pagefile_number(), 1);
467 assert!(mock.read_page(0x999).unwrap().is_none());
468 }
469
470 #[test]
471 fn page_table_builder_map_demand_zero() {
472 let vaddr: u64 = 0xFFFF_8000_0010_0000;
473 let (cr3, mem) = PageTableBuilder::new().map_demand_zero(vaddr).build();
474 let pml4_idx = (vaddr >> 39) & 0x1FF;
475 let pml4e = mem.read_u64(cr3 + pml4_idx * 8);
476 assert_ne!(pml4e & flags::PRESENT, 0, "PML4 entry should be present");
477 let pdpt_base = pml4e & PageTableBuilder::ADDR_MASK;
478 let pdpt_idx = (vaddr >> 30) & 0x1FF;
479 let pdpte = mem.read_u64(pdpt_base + pdpt_idx * 8);
480 assert_ne!(pdpte & flags::PRESENT, 0, "PDPT entry should be present");
481 let pd_base = pdpte & PageTableBuilder::ADDR_MASK;
482 let pd_idx = (vaddr >> 21) & 0x1FF;
483 let pde = mem.read_u64(pd_base + pd_idx * 8);
484 assert_ne!(pde & flags::PRESENT, 0, "PD entry should be present");
485 let pt_base = pde & PageTableBuilder::ADDR_MASK;
486 let pt_idx = (vaddr >> 12) & 0x1FF;
487 let pte = mem.read_u64(pt_base + pt_idx * 8);
488 assert_eq!(pte, 0, "demand-zero PTE must be all zeros");
489 }
490
491 #[test]
492 fn page_table_builder_map_transition_pte() {
493 let vaddr: u64 = 0xFFFF_8000_0010_0000;
494 let pfn: u64 = 0x800;
495 let (cr3, mem) = PageTableBuilder::new().map_transition(vaddr, pfn).build();
496 let pml4_idx = (vaddr >> 39) & 0x1FF;
497 let pml4e = mem.read_u64(cr3 + pml4_idx * 8);
498 let pdpt_base = pml4e & PageTableBuilder::ADDR_MASK;
499 let pdpt_idx = (vaddr >> 30) & 0x1FF;
500 let pdpte = mem.read_u64(pdpt_base + pdpt_idx * 8);
501 let pd_base = pdpte & PageTableBuilder::ADDR_MASK;
502 let pd_idx = (vaddr >> 21) & 0x1FF;
503 let pde = mem.read_u64(pd_base + pd_idx * 8);
504 let pt_base = pde & PageTableBuilder::ADDR_MASK;
505 let pt_idx = (vaddr >> 12) & 0x1FF;
506 let pte = mem.read_u64(pt_base + pt_idx * 8);
507 assert_eq!(pte & 1, 0, "PRESENT must be clear");
508 assert_ne!(pte & (1 << 11), 0, "TRANSITION bit must be set");
509 assert_eq!((pte >> 12) & 0xF_FFFF_FFFF, pfn, "PFN must match");
510 }
511
512 #[test]
513 fn page_table_builder_map_pagefile_pte() {
514 let vaddr: u64 = 0xFFFF_8000_0010_0000;
515 let pagefile_num: u8 = 0;
516 let page_offset: u64 = 0x5678;
517 let (cr3, mem) = PageTableBuilder::new()
518 .map_pagefile(vaddr, pagefile_num, page_offset)
519 .build();
520 let pml4_idx = (vaddr >> 39) & 0x1FF;
521 let pml4e = mem.read_u64(cr3 + pml4_idx * 8);
522 let pdpt_base = pml4e & PageTableBuilder::ADDR_MASK;
523 let pdpt_idx = (vaddr >> 30) & 0x1FF;
524 let pdpte = mem.read_u64(pdpt_base + pdpt_idx * 8);
525 let pd_base = pdpte & PageTableBuilder::ADDR_MASK;
526 let pd_idx = (vaddr >> 21) & 0x1FF;
527 let pde = mem.read_u64(pd_base + pd_idx * 8);
528 let pt_base = pde & PageTableBuilder::ADDR_MASK;
529 let pt_idx = (vaddr >> 12) & 0x1FF;
530 let pte = mem.read_u64(pt_base + pt_idx * 8);
531 assert_eq!(pte & 1, 0, "PRESENT must be clear");
532 assert_eq!((pte >> 1) & 0xF, u64::from(pagefile_num), "pagefile_num");
533 assert_eq!(pte & (1 << 10), 0, "prototype bit must be clear");
534 assert_eq!(pte & (1 << 11), 0, "transition bit must be clear");
535 assert_eq!((pte >> 12) & 0xF_FFFF_FFFF, page_offset, "page_offset");
536 }
537
538 #[test]
539 fn page_table_builder_map_prototype_pte() {
540 let vaddr: u64 = 0xFFFF_8000_0010_0000;
541 let (cr3, mem) = PageTableBuilder::new().map_prototype(vaddr).build();
542 let pml4_idx = (vaddr >> 39) & 0x1FF;
543 let pml4e = mem.read_u64(cr3 + pml4_idx * 8);
544 let pdpt_base = pml4e & PageTableBuilder::ADDR_MASK;
545 let pdpt_idx = (vaddr >> 30) & 0x1FF;
546 let pdpte = mem.read_u64(pdpt_base + pdpt_idx * 8);
547 let pd_base = pdpte & PageTableBuilder::ADDR_MASK;
548 let pd_idx = (vaddr >> 21) & 0x1FF;
549 let pde = mem.read_u64(pd_base + pd_idx * 8);
550 let pt_base = pde & PageTableBuilder::ADDR_MASK;
551 let pt_idx = (vaddr >> 12) & 0x1FF;
552 let pte = mem.read_u64(pt_base + pt_idx * 8);
553 assert_eq!(pte & 1, 0, "PRESENT must be clear");
554 assert_ne!(pte & (1 << 10), 0, "PROTOTYPE bit must be set");
555 }
556}