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