1use memf_format::PhysicalMemoryProvider;
4
5use crate::pagefile::PagefileSource;
6use crate::proto_pte::PrototypePteSource;
7use crate::{Error, Result};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum TranslationMode {
12 X86_64FourLevel,
14}
15
16pub struct VirtualAddressSpace<P: PhysicalMemoryProvider> {
18 physical: P,
19 page_table_root: u64,
20 mode: TranslationMode,
21 pagefiles: Vec<Box<dyn PagefileSource>>,
22 prototype_source: Option<Box<dyn PrototypePteSource>>,
23}
24
25const ADDR_MASK: u64 = 0x000F_FFFF_FFFF_F000;
27const PRESENT: u64 = 1;
28const PS: u64 = 1 << 7;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32enum TranslationResult {
33 Physical(u64),
35 DemandZero,
37 PagefileEntry { pagefile_num: u8, page_offset: u64 },
39 Transition(u64),
41 Prototype(u64),
43}
44
45impl<P: PhysicalMemoryProvider> VirtualAddressSpace<P> {
46 pub fn new(physical: P, page_table_root: u64, mode: TranslationMode) -> Self {
48 Self {
49 physical,
50 page_table_root,
51 mode,
52 pagefiles: Vec::new(),
53 prototype_source: None,
54 }
55 }
56
57 pub fn with_pagefile(mut self, source: Box<dyn PagefileSource>) -> Self {
59 self.pagefiles.push(source);
60 self
61 }
62
63 pub fn with_prototype_source(mut self, source: Box<dyn PrototypePteSource>) -> Self {
65 self.prototype_source = Some(source);
66 self
67 }
68
69 pub fn virt_to_phys(&self, vaddr: u64) -> Result<u64> {
71 match self.mode {
72 TranslationMode::X86_64FourLevel => self.walk_x86_64_4level(vaddr),
73 }
74 }
75
76 pub fn read_virt(&self, vaddr: u64, buf: &mut [u8]) -> Result<()> {
81 if buf.is_empty() {
82 return Ok(());
83 }
84
85 let mut offset = 0usize;
86 let mut current_vaddr = vaddr;
87
88 while offset < buf.len() {
89 let page_off = (current_vaddr & 0xFFF) as usize;
90 let remaining_in_page = 0x1000 - page_off;
91 let remaining_to_read = buf.len() - offset;
92 let chunk = remaining_to_read.min(remaining_in_page);
93
94 let result = match self.mode {
95 TranslationMode::X86_64FourLevel => {
96 self.walk_x86_64_4level_internal(current_vaddr)?
97 }
98 };
99
100 match result {
101 TranslationResult::Physical(paddr) | TranslationResult::Transition(paddr) => {
102 let n = self
103 .physical
104 .read_phys(paddr, &mut buf[offset..offset + chunk])?;
105 if n == 0 {
106 return Err(Error::PartialRead {
107 addr: vaddr,
108 requested: buf.len(),
109 got: offset,
110 });
111 }
112 offset += n;
113 current_vaddr = current_vaddr.wrapping_add(n as u64);
114 }
115 TranslationResult::DemandZero => {
116 buf[offset..offset + chunk].fill(0);
117 offset += chunk;
118 current_vaddr = current_vaddr.wrapping_add(chunk as u64);
119 }
120 TranslationResult::PagefileEntry {
121 pagefile_num,
122 page_offset,
123 } => {
124 let page = self.read_pagefile_page(current_vaddr, pagefile_num, page_offset)?;
125 buf[offset..offset + chunk].copy_from_slice(&page[page_off..page_off + chunk]);
126 offset += chunk;
127 current_vaddr = current_vaddr.wrapping_add(chunk as u64);
128 }
129 TranslationResult::Prototype(raw_pte) => {
130 if let Some(ref source) = self.prototype_source {
131 if let Some(phys_base) = source.resolve(raw_pte) {
132 let paddr = phys_base + (current_vaddr & 0xFFF);
133 let n = self
134 .physical
135 .read_phys(paddr, &mut buf[offset..offset + chunk])?;
136 if n == 0 {
137 return Err(Error::PartialRead {
138 addr: vaddr,
139 requested: buf.len(),
140 got: offset,
141 });
142 }
143 offset += n;
144 current_vaddr = current_vaddr.wrapping_add(n as u64);
145 } else {
146 return Err(Error::PrototypePte(current_vaddr));
147 }
148 } else {
149 return Err(Error::PrototypePte(current_vaddr));
150 }
151 }
152 }
153 }
154
155 Ok(())
156 }
157
158 fn read_pagefile_page(
159 &self,
160 vaddr: u64,
161 pagefile_num: u8,
162 page_offset: u64,
163 ) -> Result<[u8; 4096]> {
164 for source in &self.pagefiles {
165 if source.pagefile_number() == pagefile_num {
166 if let Some(page) = source.read_page(page_offset)? {
167 return Ok(page);
168 }
169 break;
170 }
171 }
172 Err(Error::PagedOut {
173 vaddr,
174 pagefile_num,
175 page_offset,
176 })
177 }
178
179 pub fn physical(&self) -> &P {
181 &self.physical
182 }
183
184 pub fn mode(&self) -> TranslationMode {
186 self.mode
187 }
188
189 fn read_pte(&self, addr: u64) -> Result<u64> {
190 let mut buf = [0u8; 8];
191 let n = self.physical.read_phys(addr, &mut buf)?;
192 if n < 8 {
193 return Err(Error::PartialRead {
194 addr,
195 requested: 8,
196 got: n,
197 });
198 }
199 Ok(u64::from_le_bytes(buf))
200 }
201
202 fn walk_x86_64_4level(&self, vaddr: u64) -> Result<u64> {
203 let result = self.walk_x86_64_4level_internal(vaddr)?;
204 match result {
205 TranslationResult::Physical(addr) | TranslationResult::Transition(addr) => Ok(addr),
206 TranslationResult::DemandZero => Err(Error::PageNotPresent(vaddr)),
207 TranslationResult::PagefileEntry {
208 pagefile_num,
209 page_offset,
210 } => Err(Error::PagedOut {
211 vaddr,
212 pagefile_num,
213 page_offset,
214 }),
215 TranslationResult::Prototype(_) => Err(Error::PrototypePte(vaddr)),
216 }
217 }
218
219 fn walk_x86_64_4level_internal(&self, vaddr: u64) -> Result<TranslationResult> {
220 let pml4_idx = (vaddr >> 39) & 0x1FF;
221 let pdpt_idx = (vaddr >> 30) & 0x1FF;
222 let pd_idx = (vaddr >> 21) & 0x1FF;
223 let pt_idx = (vaddr >> 12) & 0x1FF;
224 let page_offset = vaddr & 0xFFF;
225
226 let pml4e = self.read_pte(self.page_table_root + pml4_idx * 8)?;
228 if pml4e & PRESENT == 0 {
229 return Err(Error::PageNotPresent(vaddr));
230 }
231
232 let pdpt_base = pml4e & ADDR_MASK;
234 let pdpte = self.read_pte(pdpt_base + pdpt_idx * 8)?;
235 if pdpte & PRESENT == 0 {
236 return Err(Error::PageNotPresent(vaddr));
237 }
238
239 if pdpte & PS != 0 {
241 let phys_base = pdpte & 0x000F_FFFF_C000_0000;
242 let offset_1g = vaddr & 0x3FFF_FFFF;
243 return Ok(TranslationResult::Physical(phys_base | offset_1g));
244 }
245
246 let pd_base = pdpte & ADDR_MASK;
248 let pde = self.read_pte(pd_base + pd_idx * 8)?;
249 if pde & PRESENT == 0 {
250 return Err(Error::PageNotPresent(vaddr));
251 }
252
253 if pde & PS != 0 {
255 let phys_base = pde & 0x000F_FFFF_FFE0_0000;
256 let offset_2m = vaddr & 0x1F_FFFF;
257 return Ok(TranslationResult::Physical(phys_base | offset_2m));
258 }
259
260 let pt_base = pde & ADDR_MASK;
262 let pte = self.read_pte(pt_base + pt_idx * 8)?;
263
264 if pte & PRESENT != 0 {
265 let phys_base = pte & ADDR_MASK;
266 return Ok(TranslationResult::Physical(phys_base | page_offset));
267 }
268
269 Ok(Self::decode_non_present_pte(pte, page_offset))
271 }
272
273 fn decode_non_present_pte(pte: u64, page_offset: u64) -> TranslationResult {
274 if pte == 0 {
275 return TranslationResult::DemandZero;
276 }
277 if pte & (1 << 11) != 0 {
278 let pfn = (pte >> 12) & 0xF_FFFF_FFFF;
279 return TranslationResult::Transition(pfn * 0x1000 + page_offset);
280 }
281 if pte & (1 << 10) != 0 {
282 return TranslationResult::Prototype(pte);
283 }
284 let pagefile_num = ((pte >> 1) & 0xF) as u8;
285 let pf_page_offset = (pte >> 12) & 0xF_FFFF_FFFF;
286 TranslationResult::PagefileEntry {
287 pagefile_num,
288 page_offset: pf_page_offset,
289 }
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296 use crate::test_builders::{flags, PageTableBuilder};
297
298 #[test]
299 fn translate_4k_page() {
300 let vaddr: u64 = 0xFFFF_8000_0010_0000;
301 let paddr: u64 = 0x0080_0000;
302 let (cr3, mem) = PageTableBuilder::new()
303 .map_4k(vaddr, paddr, flags::WRITABLE)
304 .build();
305 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
306 let result = vas.virt_to_phys(vaddr).unwrap();
307 assert_eq!(result, paddr);
308 }
309
310 #[test]
311 fn translate_4k_with_offset() {
312 let vaddr: u64 = 0xFFFF_8000_0010_0ABC;
313 let paddr_base: u64 = 0x0080_0000;
314 let (cr3, mem) = PageTableBuilder::new()
315 .map_4k(vaddr & !0xFFF, paddr_base, flags::WRITABLE)
316 .build();
317 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
318 let result = vas.virt_to_phys(vaddr).unwrap();
319 assert_eq!(result, paddr_base + 0xABC);
320 }
321
322 #[test]
323 fn read_virt_4k() {
324 let vaddr: u64 = 0xFFFF_8000_0010_0000;
325 let paddr: u64 = 0x0080_0000;
326 let (cr3, mem) = PageTableBuilder::new()
327 .map_4k(vaddr, paddr, flags::WRITABLE)
328 .write_phys(paddr, &[0xDE, 0xAD, 0xBE, 0xEF])
329 .build();
330 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
331 let mut buf = [0u8; 4];
332 vas.read_virt(vaddr, &mut buf).unwrap();
333 assert_eq!(buf, [0xDE, 0xAD, 0xBE, 0xEF]);
334 }
335
336 #[test]
337 fn translate_2mb_page() {
338 let vaddr: u64 = 0xFFFF_8000_0020_0000;
339 let paddr: u64 = 0x0100_0000;
340 let (cr3, mem) = PageTableBuilder::new()
341 .map_2m(vaddr, paddr, flags::WRITABLE)
342 .build();
343 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
344 let result = vas.virt_to_phys(vaddr).unwrap();
345 assert_eq!(result, paddr);
346
347 let result_offset = vas.virt_to_phys(vaddr + 0x1234).unwrap();
349 assert_eq!(result_offset, paddr + 0x1234);
350 }
351
352 #[test]
353 fn translate_1gb_page() {
354 let vaddr: u64 = 0xFFFF_8000_4000_0000;
355 let paddr: u64 = 0x4000_0000;
356 let (cr3, mem) = PageTableBuilder::new()
357 .map_1g(vaddr, paddr, flags::WRITABLE)
358 .build();
359 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
360 let result = vas.virt_to_phys(vaddr).unwrap();
361 assert_eq!(result, paddr);
362
363 let result_offset = vas.virt_to_phys(vaddr + 0x12_3456).unwrap();
365 assert_eq!(result_offset, paddr + 0x12_3456);
366 }
367
368 #[test]
369 fn non_present_page_returns_error() {
370 let (cr3, mem) = PageTableBuilder::new().build();
371 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
372 let result = vas.virt_to_phys(0xFFFF_8000_0010_0000);
373 assert!(result.is_err());
374 match result.unwrap_err() {
375 Error::PageNotPresent(addr) => assert_eq!(addr, 0xFFFF_8000_0010_0000),
376 other => panic!("unexpected error: {other}"),
377 }
378 }
379
380 #[test]
381 fn read_virt_cross_page_boundary() {
382 let vaddr_page1: u64 = 0xFFFF_8000_0010_0000;
384 let vaddr_page2: u64 = 0xFFFF_8000_0010_1000;
385 let paddr1: u64 = 0x0080_0000;
386 let paddr2: u64 = 0x0090_0000;
387
388 let (cr3, mem) = PageTableBuilder::new()
390 .map_4k(vaddr_page1, paddr1, flags::WRITABLE)
391 .map_4k(vaddr_page2, paddr2, flags::WRITABLE)
392 .write_phys(paddr1 + 0xFFC, &[0xAA, 0xBB, 0xCC, 0xDD])
393 .write_phys(paddr2, &[0x11, 0x22, 0x33, 0x44])
394 .build();
395
396 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
397 let mut buf = [0u8; 8];
398 vas.read_virt(vaddr_page1 + 0xFFC, &mut buf).unwrap();
399 assert_eq!(buf, [0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22, 0x33, 0x44]);
400 }
401
402 #[test]
403 fn read_virt_empty_buffer() {
404 let (cr3, mem) = PageTableBuilder::new().build();
405 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
406 let mut buf = [];
407 vas.read_virt(0xFFFF_8000_0010_0000, &mut buf).unwrap();
408 }
409
410 #[test]
411 fn virt_to_phys_4k_direct() {
412 let vaddr: u64 = 0xFFFF_8000_0010_0000;
414 let paddr: u64 = 0x0080_0000;
415 let (cr3, mem) = PageTableBuilder::new()
416 .map_4k(vaddr, paddr, flags::WRITABLE)
417 .build();
418 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
419 assert_eq!(vas.virt_to_phys(vaddr).unwrap(), paddr);
420 assert_eq!(vas.virt_to_phys(vaddr + 0x42).unwrap(), paddr + 0x42);
421 }
422
423 #[test]
424 fn physical_accessor() {
425 let (cr3, mem) = PageTableBuilder::new()
426 .write_phys(0x5000, &[0xAB; 8])
427 .build();
428 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
429 let phys = vas.physical();
430 let mut buf = [0u8; 4];
431 let n = phys.read_phys(0x5000, &mut buf).unwrap();
432 assert_eq!(n, 4);
433 assert_eq!(buf, [0xAB; 4]);
434 }
435
436 #[test]
437 fn demand_zero_pte_returns_page_not_present() {
438 let vaddr: u64 = 0xFFFF_8000_0010_0000;
439 let (cr3, mem) = PageTableBuilder::new().map_demand_zero(vaddr).build();
440 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
441 let result = vas.virt_to_phys(vaddr);
442 assert!(result.is_err());
443 match result.unwrap_err() {
444 Error::PageNotPresent(addr) => assert_eq!(addr, vaddr),
445 other => panic!("expected PageNotPresent, got: {other}"),
446 }
447 }
448
449 #[test]
450 fn transition_pte_resolves_to_physical() {
451 let vaddr: u64 = 0xFFFF_8000_0010_0000;
452 let pfn: u64 = 0x800;
453 let (cr3, mem) = PageTableBuilder::new()
454 .map_transition(vaddr, pfn)
455 .write_phys(pfn * 0x1000, &[0xDE, 0xAD, 0xBE, 0xEF])
456 .build();
457 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
458 let paddr = vas.virt_to_phys(vaddr).unwrap();
459 assert_eq!(paddr, pfn * 0x1000);
460 }
461
462 #[test]
463 fn transition_pte_with_offset() {
464 let vaddr_base: u64 = 0xFFFF_8000_0010_0000;
465 let vaddr: u64 = vaddr_base + 0x42;
466 let pfn: u64 = 0x800;
467 let (cr3, mem) = PageTableBuilder::new()
468 .map_transition(vaddr_base, pfn)
469 .build();
470 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
471 let paddr = vas.virt_to_phys(vaddr).unwrap();
472 assert_eq!(paddr, pfn * 0x1000 + 0x42);
473 }
474
475 #[test]
476 fn prototype_pte_returns_error() {
477 let vaddr: u64 = 0xFFFF_8000_0010_0000;
478 let (cr3, mem) = PageTableBuilder::new().map_prototype(vaddr).build();
479 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
480 let result = vas.virt_to_phys(vaddr);
481 assert!(result.is_err());
482 match result.unwrap_err() {
483 Error::PrototypePte(addr) => assert_eq!(addr, vaddr),
484 other => panic!("expected PrototypePte, got: {other}"),
485 }
486 }
487
488 #[test]
489 fn pagefile_pte_returns_paged_out() {
490 let vaddr: u64 = 0xFFFF_8000_0010_0000;
491 let (cr3, mem) = PageTableBuilder::new()
492 .map_pagefile(vaddr, 0, 0x1234)
493 .build();
494 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
495 let result = vas.virt_to_phys(vaddr);
496 assert!(result.is_err());
497 match result.unwrap_err() {
498 Error::PagedOut {
499 vaddr: v,
500 pagefile_num,
501 page_offset,
502 } => {
503 assert_eq!(v, vaddr);
504 assert_eq!(pagefile_num, 0);
505 assert_eq!(page_offset, 0x1234);
506 }
507 other => panic!("expected PagedOut, got: {other}"),
508 }
509 }
510
511 #[test]
512 fn pagefile_pte_number_routing() {
513 let vaddr: u64 = 0xFFFF_8000_0010_0000;
514 let (cr3, mem) = PageTableBuilder::new()
515 .map_pagefile(vaddr, 2, 0xABCD)
516 .build();
517 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
518 let result = vas.virt_to_phys(vaddr);
519 match result.unwrap_err() {
520 Error::PagedOut {
521 pagefile_num,
522 page_offset,
523 ..
524 } => {
525 assert_eq!(pagefile_num, 2);
526 assert_eq!(page_offset, 0xABCD);
527 }
528 other => panic!("expected PagedOut, got: {other}"),
529 }
530 }
531
532 use crate::test_builders::{MockPagefileSource, MockPrototypePteSource};
533
534 #[test]
535 fn read_virt_demand_zero_returns_zeroes() {
536 let vaddr: u64 = 0xFFFF_8000_0010_0000;
537 let (cr3, mem) = PageTableBuilder::new().map_demand_zero(vaddr).build();
538 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
539 let mut buf = [0xFFu8; 4096];
540 vas.read_virt(vaddr, &mut buf).unwrap();
541 assert!(
542 buf.iter().all(|&b| b == 0),
543 "demand-zero page must be all zeroes"
544 );
545 }
546
547 #[test]
548 fn read_virt_transition_reads_physical() {
549 let vaddr: u64 = 0xFFFF_8000_0010_0000;
550 let pfn: u64 = 0x800;
551 let (cr3, mem) = PageTableBuilder::new()
552 .map_transition(vaddr, pfn)
553 .write_phys(pfn * 0x1000, &[0xCA, 0xFE, 0xBA, 0xBE])
554 .build();
555 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
556 let mut buf = [0u8; 4];
557 vas.read_virt(vaddr, &mut buf).unwrap();
558 assert_eq!(buf, [0xCA, 0xFE, 0xBA, 0xBE]);
559 }
560
561 #[test]
562 fn read_virt_pagefile_with_provider() {
563 let vaddr: u64 = 0xFFFF_8000_0010_0000;
564 let page_offset: u64 = 0x10;
565 let mut page_data = [0u8; 4096];
566 page_data[0..4].copy_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]);
567
568 let (cr3, mem) = PageTableBuilder::new()
569 .map_pagefile(vaddr, 0, page_offset)
570 .build();
571
572 let mock = MockPagefileSource::new(0, vec![(page_offset, page_data)]);
573 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel)
574 .with_pagefile(Box::new(mock));
575
576 let mut buf = [0u8; 4];
577 vas.read_virt(vaddr, &mut buf).unwrap();
578 assert_eq!(buf, [0xDE, 0xAD, 0xBE, 0xEF]);
579 }
580
581 #[test]
582 fn read_virt_pagefile_without_provider_errors() {
583 let vaddr: u64 = 0xFFFF_8000_0010_0000;
584 let (cr3, mem) = PageTableBuilder::new().map_pagefile(vaddr, 0, 0x10).build();
585 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
586 let mut buf = [0u8; 4];
587 let result = vas.read_virt(vaddr, &mut buf);
588 assert!(result.is_err());
589 match result.unwrap_err() {
590 Error::PagedOut {
591 pagefile_num: 0,
592 page_offset: 0x10,
593 ..
594 } => {}
595 other => panic!("expected PagedOut, got: {other}"),
596 }
597 }
598
599 #[test]
600 fn read_virt_prototype_pte_errors() {
601 let vaddr: u64 = 0xFFFF_8000_0010_0000;
602 let (cr3, mem) = PageTableBuilder::new().map_prototype(vaddr).build();
603 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
604 let mut buf = [0u8; 4];
605 let result = vas.read_virt(vaddr, &mut buf);
606 assert!(result.is_err());
607 match result.unwrap_err() {
608 Error::PrototypePte(addr) => assert_eq!(addr, vaddr),
609 other => panic!("expected PrototypePte, got: {other}"),
610 }
611 }
612
613 #[test]
614 fn read_virt_pagefile_number_routing() {
615 let vaddr1: u64 = 0xFFFF_8000_0010_0000;
616 let vaddr2: u64 = 0xFFFF_8000_0010_1000;
617
618 let mut page0_data = [0u8; 4096];
619 page0_data[0..4].copy_from_slice(&[0x11, 0x22, 0x33, 0x44]);
620 let mut page1_data = [0u8; 4096];
621 page1_data[0..4].copy_from_slice(&[0xAA, 0xBB, 0xCC, 0xDD]);
622
623 let (cr3, mem) = PageTableBuilder::new()
624 .map_pagefile(vaddr1, 0, 0x10)
625 .map_pagefile(vaddr2, 1, 0x20)
626 .build();
627
628 let mock0 = MockPagefileSource::new(0, vec![(0x10, page0_data)]);
629 let mock1 = MockPagefileSource::new(1, vec![(0x20, page1_data)]);
630
631 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel)
632 .with_pagefile(Box::new(mock0))
633 .with_pagefile(Box::new(mock1));
634
635 let mut buf1 = [0u8; 4];
636 vas.read_virt(vaddr1, &mut buf1).unwrap();
637 assert_eq!(buf1, [0x11, 0x22, 0x33, 0x44]);
638
639 let mut buf2 = [0u8; 4];
640 vas.read_virt(vaddr2, &mut buf2).unwrap();
641 assert_eq!(buf2, [0xAA, 0xBB, 0xCC, 0xDD]);
642 }
643
644 #[test]
645 fn read_virt_pagefile_out_of_range() {
646 let vaddr: u64 = 0xFFFF_8000_0010_0000;
647 let (cr3, mem) = PageTableBuilder::new()
648 .map_pagefile(vaddr, 0, 0x9999)
649 .build();
650 let mock = MockPagefileSource::new(0, vec![]);
651 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel)
652 .with_pagefile(Box::new(mock));
653 let mut buf = [0u8; 4];
654 let result = vas.read_virt(vaddr, &mut buf);
655 assert!(result.is_err());
656 match result.unwrap_err() {
657 Error::PagedOut {
658 page_offset: 0x9999,
659 ..
660 } => {}
661 other => panic!("expected PagedOut, got: {other}"),
662 }
663 }
664
665 #[test]
666 fn read_virt_mixed_pages_cross_boundary() {
667 let vaddr1: u64 = 0xFFFF_8000_0010_0000;
668 let vaddr2: u64 = 0xFFFF_8000_0010_1000;
669 let vaddr3: u64 = 0xFFFF_8000_0010_2000;
670 let paddr1: u64 = 0x0080_0000;
671
672 let mut pf_page = [0u8; 4096];
673 pf_page[0..4].copy_from_slice(&[0xBB; 4]);
674
675 let (cr3, mem) = PageTableBuilder::new()
676 .map_4k(vaddr1, paddr1, flags::WRITABLE)
677 .write_phys(paddr1 + 0xFFC, &[0xAA; 4])
678 .map_pagefile(vaddr2, 0, 0x10)
679 .map_demand_zero(vaddr3)
680 .build();
681
682 let mock = MockPagefileSource::new(0, vec![(0x10, pf_page)]);
683 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel)
684 .with_pagefile(Box::new(mock));
685
686 let mut buf = [0u8; 8];
688 vas.read_virt(vaddr1 + 0xFFC, &mut buf).unwrap();
689 assert_eq!(buf, [0xAA, 0xAA, 0xAA, 0xAA, 0xBB, 0xBB, 0xBB, 0xBB]);
690
691 let mut buf2 = [0u8; 8];
693 vas.read_virt(vaddr2 + 0xFFC, &mut buf2).unwrap();
694 assert_eq!(buf2, [0u8; 8]);
695 }
696
697 #[test]
698 fn read_virt_prototype_pte_resolves_when_source_provided() {
699 let vaddr: u64 = 0xFFFF_8000_0010_0000;
700 let resolved_paddr: u64 = 0x00A0_0000;
701 let raw_pte: u64 = (1 << 10) | (0xABCu64 << 12);
703
704 let (cr3, mem) = PageTableBuilder::new()
705 .map_prototype_raw(vaddr, raw_pte)
706 .write_phys(resolved_paddr, &[0xDE, 0xAD, 0xBE, 0xEF])
707 .build();
708
709 let mock = MockPrototypePteSource::new(vec![(raw_pte, resolved_paddr)]);
710 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel)
711 .with_prototype_source(Box::new(mock));
712
713 let mut buf = [0u8; 4];
714 vas.read_virt(vaddr, &mut buf).unwrap();
715 assert_eq!(buf, [0xDE, 0xAD, 0xBE, 0xEF]);
716 }
717
718 #[test]
719 fn read_virt_prototype_pte_errors_when_source_returns_none() {
720 let vaddr: u64 = 0xFFFF_8000_0010_0000;
721 let raw_pte: u64 = (1 << 10) | (0xDEFu64 << 12);
722
723 let (cr3, mem) = PageTableBuilder::new()
724 .map_prototype_raw(vaddr, raw_pte)
725 .build();
726
727 let mock = MockPrototypePteSource::new(vec![]);
729 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel)
730 .with_prototype_source(Box::new(mock));
731
732 let mut buf = [0u8; 4];
733 let result = vas.read_virt(vaddr, &mut buf);
734 assert!(result.is_err());
735 match result.unwrap_err() {
736 Error::PrototypePte(addr) => assert_eq!(addr, vaddr),
737 other => panic!("expected PrototypePte, got: {other}"),
738 }
739 }
740
741 #[test]
742 fn read_virt_prototype_pte_with_page_offset() {
743 let vaddr_base: u64 = 0xFFFF_8000_0010_0000;
744 let vaddr: u64 = vaddr_base + 0x100;
745 let resolved_paddr: u64 = 0x00B0_0000;
746 let raw_pte: u64 = (1 << 10) | (0x123u64 << 12);
747
748 let (cr3, mem) = PageTableBuilder::new()
749 .map_prototype_raw(vaddr_base, raw_pte)
750 .write_phys(resolved_paddr + 0x100, &[0xCA, 0xFE])
751 .build();
752
753 let mock = MockPrototypePteSource::new(vec![(raw_pte, resolved_paddr)]);
754 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel)
755 .with_prototype_source(Box::new(mock));
756
757 let mut buf = [0u8; 2];
758 vas.read_virt(vaddr, &mut buf).unwrap();
759 assert_eq!(buf, [0xCA, 0xFE]);
760 }
761
762 #[test]
763 fn multiple_mappings_same_pml4() {
764 let vaddr1: u64 = 0xFFFF_8000_0010_0000;
765 let vaddr2: u64 = 0xFFFF_8000_0010_1000;
766 let paddr1: u64 = 0x0080_0000;
767 let paddr2: u64 = 0x0090_0000;
768
769 let (cr3, mem) = PageTableBuilder::new()
770 .map_4k(vaddr1, paddr1, flags::WRITABLE)
771 .map_4k(vaddr2, paddr2, flags::WRITABLE)
772 .write_phys(paddr1, &[0x11; 8])
773 .write_phys(paddr2, &[0x22; 8])
774 .build();
775
776 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
777
778 let mut buf1 = [0u8; 8];
779 vas.read_virt(vaddr1, &mut buf1).unwrap();
780 assert_eq!(buf1, [0x11; 8]);
781
782 let mut buf2 = [0u8; 8];
783 vas.read_virt(vaddr2, &mut buf2).unwrap();
784 assert_eq!(buf2, [0x22; 8]);
785 }
786}