1use lru::LruCache;
4use memf_format::PhysicalMemoryProvider;
5use std::cell::RefCell;
6use std::num::NonZeroUsize;
7
8use crate::pagefile::PagefileSource;
9use crate::proto_pte::PrototypePteSource;
10use crate::{Error, Result};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum TranslationMode {
15 X86_64FourLevel,
17 X86_645Level,
19 AArch64FourLevel,
21}
22
23pub struct VirtualAddressSpace<P: PhysicalMemoryProvider> {
25 physical: P,
26 page_table_root: u64,
27 mode: TranslationMode,
28 pagefiles: Vec<Box<dyn PagefileSource>>,
29 prototype_source: Option<Box<dyn PrototypePteSource>>,
30 tlb_cache: RefCell<LruCache<u64, u64>>,
32}
33
34const ADDR_MASK: u64 = 0x000F_FFFF_FFFF_F000;
36const PRESENT: u64 = 1;
37const PS: u64 = 1 << 7;
38
39const AARCH64_VALID: u64 = 1; const AARCH64_TABLE: u64 = 1 << 1; const AARCH64_OA_MASK: u64 = 0x0000_FFFF_FFFF_F000; const TRANSLATION_CACHE_CAPACITY: usize = 4096;
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49enum TranslationResult {
50 Physical(u64),
52 DemandZero,
54 PagefileEntry { pagefile_num: u8, page_offset: u64 },
56 Transition(u64),
58 Prototype(u64),
60}
61
62impl<P: PhysicalMemoryProvider> VirtualAddressSpace<P> {
63 pub fn new(physical: P, page_table_root: u64, mode: TranslationMode) -> Self {
65 Self {
66 physical,
67 page_table_root,
68 mode,
69 pagefiles: Vec::new(),
70 prototype_source: None,
71 tlb_cache: RefCell::new(LruCache::new(
75 NonZeroUsize::new(TRANSLATION_CACHE_CAPACITY).unwrap_or(NonZeroUsize::MIN),
76 )),
77 }
78 }
79
80 pub fn with_pagefile(mut self, source: Box<dyn PagefileSource>) -> Self {
82 self.pagefiles.push(source);
83 self
84 }
85
86 pub fn with_prototype_source(mut self, source: Box<dyn PrototypePteSource>) -> Self {
88 self.prototype_source = Some(source);
89 self
90 }
91
92 pub fn virt_to_phys(&self, vaddr: u64) -> Result<u64> {
94 match self.mode {
95 TranslationMode::X86_64FourLevel => self.walk_x86_64_4level(vaddr),
96 TranslationMode::X86_645Level => match self.walk_x86_64_5level_internal(vaddr)? {
97 TranslationResult::Physical(addr) | TranslationResult::Transition(addr) => Ok(addr),
98 TranslationResult::DemandZero => Err(Error::PageNotPresent(vaddr)),
99 TranslationResult::PagefileEntry {
100 pagefile_num,
101 page_offset,
102 } => Err(Error::PagedOut {
103 vaddr,
104 pagefile_num,
105 page_offset,
106 }),
107 TranslationResult::Prototype(_) => Err(Error::PrototypePte(vaddr)),
108 },
109 TranslationMode::AArch64FourLevel => self.walk_aarch64_4level(vaddr),
110 }
111 }
112
113 pub fn read_virt(&self, vaddr: u64, buf: &mut [u8]) -> Result<()> {
118 if buf.is_empty() {
119 return Ok(());
120 }
121
122 let mut offset = 0usize;
123 let mut current_vaddr = vaddr;
124
125 while offset < buf.len() {
126 let page_off = (current_vaddr & 0xFFF) as usize;
127 let remaining_in_page = 0x1000 - page_off;
128 let remaining_to_read = buf.len() - offset;
129 let chunk = remaining_to_read.min(remaining_in_page);
130
131 let result = match self.mode {
132 TranslationMode::X86_64FourLevel => {
133 self.walk_x86_64_4level_internal(current_vaddr)?
134 }
135 TranslationMode::X86_645Level => self.walk_x86_64_5level_internal(current_vaddr)?,
136 TranslationMode::AArch64FourLevel => self.walk_aarch64_internal(current_vaddr)?,
137 };
138
139 match result {
140 TranslationResult::Physical(paddr) | TranslationResult::Transition(paddr) => {
141 let n = self
142 .physical
143 .read_phys(paddr, &mut buf[offset..offset + chunk])?;
144 if n == 0 {
145 return Err(Error::PartialRead {
146 addr: vaddr,
147 requested: buf.len(),
148 got: offset,
149 });
150 }
151 offset += n;
152 current_vaddr = current_vaddr.wrapping_add(n as u64);
153 }
154 TranslationResult::DemandZero => {
155 buf[offset..offset + chunk].fill(0);
156 offset += chunk;
157 current_vaddr = current_vaddr.wrapping_add(chunk as u64);
158 }
159 TranslationResult::PagefileEntry {
160 pagefile_num,
161 page_offset,
162 } => {
163 let page = self.read_pagefile_page(current_vaddr, pagefile_num, page_offset)?;
164 buf[offset..offset + chunk].copy_from_slice(&page[page_off..page_off + chunk]);
165 offset += chunk;
166 current_vaddr = current_vaddr.wrapping_add(chunk as u64);
167 }
168 TranslationResult::Prototype(raw_pte) => {
169 if let Some(ref source) = self.prototype_source {
170 if let Some(phys_base) = source.resolve(raw_pte) {
171 let paddr = phys_base + (current_vaddr & 0xFFF);
172 let n = self
173 .physical
174 .read_phys(paddr, &mut buf[offset..offset + chunk])?;
175 if n == 0 {
176 return Err(Error::PartialRead {
177 addr: vaddr,
178 requested: buf.len(),
179 got: offset,
180 });
181 }
182 offset += n;
183 current_vaddr = current_vaddr.wrapping_add(n as u64);
184 } else {
185 return Err(Error::PrototypePte(current_vaddr));
186 }
187 } else {
188 return Err(Error::PrototypePte(current_vaddr));
189 }
190 }
191 }
192 }
193
194 Ok(())
195 }
196
197 fn read_pagefile_page(
198 &self,
199 vaddr: u64,
200 pagefile_num: u8,
201 page_offset: u64,
202 ) -> Result<[u8; 4096]> {
203 for source in &self.pagefiles {
204 if source.pagefile_number() == pagefile_num {
205 if let Some(page) = source.read_page(page_offset)? {
206 return Ok(page);
207 }
208 break;
209 }
210 }
211 Err(Error::PagedOut {
212 vaddr,
213 pagefile_num,
214 page_offset,
215 })
216 }
217
218 pub fn physical(&self) -> &P {
220 &self.physical
221 }
222
223 pub fn mode(&self) -> TranslationMode {
225 self.mode
226 }
227
228 #[must_use]
244 pub fn find_kernel_va_for_phys(&self, target_phys: u64, max_leaves: usize) -> Option<u64> {
245 if !matches!(self.mode, TranslationMode::X86_64FourLevel) {
246 return None;
247 }
248 let target = target_phys & !0xFFF;
249 let root = self.page_table_root;
250 let mut budget = max_leaves;
251 let canon = |p4: u64, p3: u64, p2: u64, p1: u64| -> u64 {
254 ((p4 << 39) | (p3 << 30) | (p2 << 21) | (p1 << 12)) | 0xFFFF_0000_0000_0000
255 };
256 for p4 in 256u64..512 {
257 let Ok(pml4e) = self.read_pte(root + p4 * 8) else {
258 continue;
259 };
260 if pml4e & PRESENT == 0 {
261 continue;
262 }
263 let pdpt = pml4e & ADDR_MASK;
264 for p3 in 0u64..512 {
265 let Ok(pdpte) = self.read_pte(pdpt + p3 * 8) else {
266 continue;
267 };
268 if pdpte & PRESENT == 0 {
269 continue;
270 }
271 if pdpte & PS != 0 {
273 continue;
274 }
275 let pd = pdpte & ADDR_MASK;
276 for p2 in 0u64..512 {
277 let Ok(pde) = self.read_pte(pd + p2 * 8) else {
278 continue;
279 };
280 if pde & PRESENT == 0 {
281 continue;
282 }
283 if pde & PS != 0 {
284 let base = pde & 0x000F_FFFF_FFE0_0000;
285 if target >= base && target < base + (1 << 21) {
286 return Some(canon(p4, p3, p2, 0) | (target - base));
287 }
288 budget = budget.checked_sub(1)?;
289 continue;
290 }
291 let pt = pde & ADDR_MASK;
292 for p1 in 0u64..512 {
293 let Ok(pte) = self.read_pte(pt + p1 * 8) else {
294 continue;
295 };
296 let leaf = if pte & PRESENT != 0 {
299 Some(pte & ADDR_MASK)
300 } else if pte & (1 << 11) != 0 {
301 Some(((pte >> 12) & 0xF_FFFF_FFFF) * 0x1000)
302 } else {
303 None
304 };
305 match leaf {
306 Some(phys) if phys == target => return Some(canon(p4, p3, p2, p1)),
307 Some(_) => budget = budget.checked_sub(1)?,
308 None => {}
309 }
310 }
311 }
312 }
313 }
314 None
315 }
316
317 fn read_pte(&self, addr: u64) -> Result<u64> {
318 let mut buf = [0u8; 8];
319 let n = self.physical.read_phys(addr, &mut buf)?;
320 if n < 8 {
321 return Err(Error::PartialRead {
322 addr,
323 requested: 8,
324 got: n,
325 });
326 }
327 Ok(u64::from_le_bytes(buf))
328 }
329
330 fn walk_x86_64_4level(&self, vaddr: u64) -> Result<u64> {
331 let result = self.walk_x86_64_4level_internal(vaddr)?;
332 match result {
333 TranslationResult::Physical(addr) | TranslationResult::Transition(addr) => Ok(addr),
334 TranslationResult::DemandZero => Err(Error::PageNotPresent(vaddr)),
335 TranslationResult::PagefileEntry {
336 pagefile_num,
337 page_offset,
338 } => Err(Error::PagedOut {
339 vaddr,
340 pagefile_num,
341 page_offset,
342 }),
343 TranslationResult::Prototype(_) => Err(Error::PrototypePte(vaddr)),
344 }
345 }
346
347 fn walk_x86_64_4level_internal(&self, vaddr: u64) -> Result<TranslationResult> {
348 self.walk_4level_from(self.page_table_root, vaddr)
349 }
350
351 fn walk_4level_from(&self, pml4_root: u64, vaddr: u64) -> Result<TranslationResult> {
352 let page_vaddr = vaddr & !0xFFF;
353 if let Some(&paddr_base) = self.tlb_cache.borrow().peek(&page_vaddr) {
355 return Ok(TranslationResult::Physical(paddr_base | (vaddr & 0xFFF)));
356 }
357
358 let pml4_idx = (vaddr >> 39) & 0x1FF;
359 let pdpt_idx = (vaddr >> 30) & 0x1FF;
360 let pd_idx = (vaddr >> 21) & 0x1FF;
361 let pt_idx = (vaddr >> 12) & 0x1FF;
362 let page_offset = vaddr & 0xFFF;
363
364 let pml4e = self.read_pte(pml4_root + pml4_idx * 8)?;
366 if pml4e & PRESENT == 0 {
367 return Err(Error::PageNotPresent(vaddr));
368 }
369
370 let pdpt_base = pml4e & ADDR_MASK;
372 let pdpte = self.read_pte(pdpt_base + pdpt_idx * 8)?;
373 if pdpte & PRESENT == 0 {
374 return Err(Error::PageNotPresent(vaddr));
375 }
376
377 if pdpte & PS != 0 {
379 let phys_base = pdpte & 0x000F_FFFF_C000_0000;
380 let offset_1g = vaddr & 0x3FFF_FFFF;
381 let phys = phys_base | offset_1g;
382 self.tlb_cache.borrow_mut().put(page_vaddr, phys & !0xFFF);
383 return Ok(TranslationResult::Physical(phys));
384 }
385
386 let pd_base = pdpte & ADDR_MASK;
388 let pde = self.read_pte(pd_base + pd_idx * 8)?;
389 if pde & PRESENT == 0 {
390 return Err(Error::PageNotPresent(vaddr));
391 }
392
393 if pde & PS != 0 {
395 let phys_base = pde & 0x000F_FFFF_FFE0_0000;
396 let offset_2m = vaddr & 0x1F_FFFF;
397 let phys = phys_base | offset_2m;
398 self.tlb_cache.borrow_mut().put(page_vaddr, phys & !0xFFF);
399 return Ok(TranslationResult::Physical(phys));
400 }
401
402 let pt_base = pde & ADDR_MASK;
404 let pte = self.read_pte(pt_base + pt_idx * 8)?;
405
406 if pte & PRESENT != 0 {
407 let phys_base = pte & ADDR_MASK;
408 let phys = phys_base | page_offset;
409 self.tlb_cache.borrow_mut().put(page_vaddr, phys_base);
410 return Ok(TranslationResult::Physical(phys));
411 }
412
413 Ok(Self::decode_non_present_pte(pte, page_offset))
415 }
416
417 fn walk_x86_64_5level_internal(&self, vaddr: u64) -> Result<TranslationResult> {
418 let pml5_idx = (vaddr >> 48) & 0x1FF;
419 let pml5e = self.read_pte(self.page_table_root + pml5_idx * 8)?;
420 if pml5e & PRESENT == 0 {
421 return Err(Error::PageNotPresent(vaddr));
422 }
423 let pml4_root = pml5e & ADDR_MASK;
424 self.walk_4level_from(pml4_root, vaddr)
425 }
426
427 fn walk_aarch64_4level(&self, vaddr: u64) -> Result<u64> {
428 match self.walk_aarch64_internal(vaddr)? {
429 TranslationResult::Physical(addr) | TranslationResult::Transition(addr) => Ok(addr),
430 TranslationResult::DemandZero
431 | TranslationResult::PagefileEntry { .. }
432 | TranslationResult::Prototype(_) => Err(Error::PageNotPresent(vaddr)),
433 }
434 }
435
436 fn walk_aarch64_internal(&self, vaddr: u64) -> Result<TranslationResult> {
437 let page_vaddr = vaddr & !0xFFF;
438 if let Some(&paddr_base) = self.tlb_cache.borrow().peek(&page_vaddr) {
440 return Ok(TranslationResult::Physical(paddr_base | (vaddr & 0xFFF)));
441 }
442
443 let l0_idx = (vaddr >> 39) & 0x1FF;
444 let l1_idx = (vaddr >> 30) & 0x1FF;
445 let l2_idx = (vaddr >> 21) & 0x1FF;
446 let l3_idx = (vaddr >> 12) & 0x1FF;
447 let page_off = vaddr & 0xFFF;
448
449 let l0e = self.read_pte(self.page_table_root + l0_idx * 8)?;
451 if l0e & AARCH64_VALID == 0 {
452 return Err(Error::PageNotPresent(vaddr));
453 }
454 let l1_base = l0e & AARCH64_OA_MASK;
455
456 let l1e = self.read_pte(l1_base + l1_idx * 8)?;
458 if l1e & AARCH64_VALID == 0 {
459 return Err(Error::PageNotPresent(vaddr));
460 }
461 if l1e & AARCH64_TABLE == 0 {
462 let phys_base = l1e & 0x0000_FFFF_C000_0000;
464 let phys = phys_base | (vaddr & 0x3FFF_FFFF);
465 self.tlb_cache.borrow_mut().put(page_vaddr, phys & !0xFFF);
466 return Ok(TranslationResult::Physical(phys));
467 }
468 let l2_base = l1e & AARCH64_OA_MASK;
469
470 let l2e = self.read_pte(l2_base + l2_idx * 8)?;
472 if l2e & AARCH64_VALID == 0 {
473 return Err(Error::PageNotPresent(vaddr));
474 }
475 if l2e & AARCH64_TABLE == 0 {
476 let phys_base = l2e & 0x0000_FFFF_FFE0_0000;
478 let phys = phys_base | (vaddr & 0x001F_FFFF);
479 self.tlb_cache.borrow_mut().put(page_vaddr, phys & !0xFFF);
480 return Ok(TranslationResult::Physical(phys));
481 }
482 let l3_base = l2e & AARCH64_OA_MASK;
483
484 let l3e = self.read_pte(l3_base + l3_idx * 8)?;
486 if l3e & AARCH64_VALID == 0 {
487 return Err(Error::PageNotPresent(vaddr));
488 }
489 let phys_base = l3e & AARCH64_OA_MASK;
490 let phys = phys_base | page_off;
491 self.tlb_cache.borrow_mut().put(page_vaddr, phys_base);
492 Ok(TranslationResult::Physical(phys))
493 }
494
495 fn decode_non_present_pte(pte: u64, page_offset: u64) -> TranslationResult {
496 if pte == 0 {
497 return TranslationResult::DemandZero;
498 }
499 if pte & (1 << 11) != 0 {
500 let pfn = (pte >> 12) & 0xF_FFFF_FFFF;
501 return TranslationResult::Transition(pfn * 0x1000 + page_offset);
502 }
503 if pte & (1 << 10) != 0 {
504 return TranslationResult::Prototype(pte);
505 }
506 let pagefile_num = ((pte >> 1) & 0xF) as u8;
507 let pf_page_offset = (pte >> 12) & 0xF_FFFF_FFFF;
508 TranslationResult::PagefileEntry {
509 pagefile_num,
510 page_offset: pf_page_offset,
511 }
512 }
513}
514
515#[cfg(test)]
516mod tests {
517 use super::*;
518 use crate::test_builders::{flags, PageTableBuilder, SyntheticPhysMem};
519
520 fn manual_pt(vaddr: u64, leaf_pte: u64, large_2m: bool) -> SyntheticPhysMem {
524 let mut mem = SyntheticPhysMem::new(16 * 1024 * 1024);
525 let p4 = (vaddr >> 39) & 0x1FF;
526 let p3 = (vaddr >> 30) & 0x1FF;
527 let p2 = (vaddr >> 21) & 0x1FF;
528 let p1 = (vaddr >> 12) & 0x1FF;
529 let (pdpt, pd, pt) = (0x1000u64, 0x2000u64, 0x3000u64);
530 mem.write_u64(p4 * 8, pdpt | PRESENT); mem.write_u64(pdpt + p3 * 8, pd | PRESENT);
532 if large_2m {
533 mem.write_u64(pd + p2 * 8, leaf_pte);
534 } else {
535 mem.write_u64(pd + p2 * 8, pt | PRESENT);
536 mem.write_u64(pt + p1 * 8, leaf_pte);
537 }
538 mem
539 }
540
541 #[test]
542 fn reverse_map_finds_4k_present_page() {
543 let vaddr = 0xFFFF_F802_4020_1000u64;
544 let phys = 0x6000u64;
545 let mem = manual_pt(vaddr, phys | PRESENT, false);
546 let vas = VirtualAddressSpace::new(mem, 0, TranslationMode::X86_64FourLevel);
547 assert_eq!(vas.find_kernel_va_for_phys(phys, 100_000), Some(vaddr));
548 }
549
550 #[test]
551 fn reverse_map_finds_transition_page() {
552 let vaddr = 0xFFFF_F802_4020_2000u64;
555 let pfn = 0x7u64; let trans_pte = (pfn << 12) | (1 << 11);
557 let mem = manual_pt(vaddr, trans_pte, false);
558 let vas = VirtualAddressSpace::new(mem, 0, TranslationMode::X86_64FourLevel);
559 assert_eq!(vas.find_kernel_va_for_phys(0x7000, 100_000), Some(vaddr));
560 }
561
562 #[test]
563 fn reverse_map_finds_2m_large_page_with_offset() {
564 let vaddr = 0xFFFF_F802_4040_0000u64; let base = 0x20_0000u64;
566 let mem = manual_pt(vaddr, base | PRESENT | PS, true);
567 let vas = VirtualAddressSpace::new(mem, 0, TranslationMode::X86_64FourLevel);
568 assert_eq!(
570 vas.find_kernel_va_for_phys(base + 0x3000, 100_000),
571 Some(vaddr + 0x3000)
572 );
573 }
574
575 #[test]
576 fn reverse_map_returns_none_when_absent() {
577 let mem = manual_pt(0xFFFF_F802_4020_1000u64, 0x6000 | PRESENT, false);
578 let vas = VirtualAddressSpace::new(mem, 0, TranslationMode::X86_64FourLevel);
579 assert_eq!(vas.find_kernel_va_for_phys(0xDEAD_0000, 100_000), None);
580 }
581
582 #[test]
583 fn reverse_map_respects_max_leaves_budget() {
584 let vaddr = 0xFFFF_F802_4020_1000u64;
587 let mut mem = manual_pt(vaddr, 0x6000 | PRESENT, false);
588 let p2 = (vaddr >> 21) & 0x1FF;
589 let pt = 0x3000u64;
590 let p1b = ((vaddr >> 12) & 0x1FF) + 1;
592 let _ = p2;
593 mem.write_u64(pt + p1b * 8, 0x9000 | PRESENT);
594 let vas = VirtualAddressSpace::new(mem, 0, TranslationMode::X86_64FourLevel);
595 assert_eq!(vas.find_kernel_va_for_phys(0xABCD_0000, 1), None);
596 }
597
598 #[test]
599 fn reverse_map_skips_1g_huge_page() {
600 let vaddr = 0xFFFF_F802_4000_0000u64;
603 let mut mem = SyntheticPhysMem::new(16 * 1024 * 1024);
604 let p4 = (vaddr >> 39) & 0x1FF;
605 let p3 = (vaddr >> 30) & 0x1FF;
606 mem.write_u64(p4 * 8, 0x1000 | PRESENT);
607 mem.write_u64(0x1000 + p3 * 8, 0x4000_0000 | PRESENT | PS); let vas = VirtualAddressSpace::new(mem, 0, TranslationMode::X86_64FourLevel);
609 assert_eq!(vas.find_kernel_va_for_phys(0x4000_1000, 100_000), None);
610 }
611
612 #[test]
613 fn reverse_map_skips_read_error_branch() {
614 let vaddr = 0xFFFF_F802_4020_1000u64;
617 let mut mem = manual_pt(vaddr, 0x6000 | PRESENT, false);
618 mem.write_u64(256 * 8, 0x400_0000 | PRESENT);
620 let vas = VirtualAddressSpace::new(mem, 0, TranslationMode::X86_64FourLevel);
621 assert_eq!(vas.find_kernel_va_for_phys(0x6000, 100_000), Some(vaddr));
622 }
623
624 #[test]
625 fn reverse_map_skips_non_matching_2m_page() {
626 let vaddr = 0xFFFF_F802_4040_0000u64;
629 let mem = manual_pt(vaddr, 0x20_0000 | PRESENT | PS, true);
630 let vas = VirtualAddressSpace::new(mem, 0, TranslationMode::X86_64FourLevel);
631 assert_eq!(vas.find_kernel_va_for_phys(0x40_0000, 100_000), None);
632 }
633
634 #[test]
635 fn reverse_map_none_for_non_4level_mode() {
636 let mem = manual_pt(0xFFFF_F802_4020_1000u64, 0x6000 | PRESENT, false);
637 let vas = VirtualAddressSpace::new(mem, 0, TranslationMode::AArch64FourLevel);
638 assert_eq!(vas.find_kernel_va_for_phys(0x6000, 100_000), None);
639 }
640
641 #[test]
642 fn translate_4k_page() {
643 let vaddr: u64 = 0xFFFF_8000_0010_0000;
644 let paddr: u64 = 0x0080_0000;
645 let (cr3, mem) = PageTableBuilder::new()
646 .map_4k(vaddr, paddr, flags::WRITABLE)
647 .build();
648 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
649 let result = vas.virt_to_phys(vaddr).unwrap();
650 assert_eq!(result, paddr);
651 }
652
653 #[test]
654 fn translate_4k_with_offset() {
655 let vaddr: u64 = 0xFFFF_8000_0010_0ABC;
656 let paddr_base: u64 = 0x0080_0000;
657 let (cr3, mem) = PageTableBuilder::new()
658 .map_4k(vaddr & !0xFFF, paddr_base, flags::WRITABLE)
659 .build();
660 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
661 let result = vas.virt_to_phys(vaddr).unwrap();
662 assert_eq!(result, paddr_base + 0xABC);
663 }
664
665 #[test]
666 fn read_virt_4k() {
667 let vaddr: u64 = 0xFFFF_8000_0010_0000;
668 let paddr: u64 = 0x0080_0000;
669 let (cr3, mem) = PageTableBuilder::new()
670 .map_4k(vaddr, paddr, flags::WRITABLE)
671 .write_phys(paddr, &[0xDE, 0xAD, 0xBE, 0xEF])
672 .build();
673 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
674 let mut buf = [0u8; 4];
675 vas.read_virt(vaddr, &mut buf).unwrap();
676 assert_eq!(buf, [0xDE, 0xAD, 0xBE, 0xEF]);
677 }
678
679 #[test]
680 fn translate_2mb_page() {
681 let vaddr: u64 = 0xFFFF_8000_0020_0000;
682 let paddr: u64 = 0x0100_0000;
683 let (cr3, mem) = PageTableBuilder::new()
684 .map_2m(vaddr, paddr, flags::WRITABLE)
685 .build();
686 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
687 let result = vas.virt_to_phys(vaddr).unwrap();
688 assert_eq!(result, paddr);
689
690 let result_offset = vas.virt_to_phys(vaddr + 0x1234).unwrap();
692 assert_eq!(result_offset, paddr + 0x1234);
693 }
694
695 #[test]
696 fn translate_1gb_page() {
697 let vaddr: u64 = 0xFFFF_8000_4000_0000;
698 let paddr: u64 = 0x4000_0000;
699 let (cr3, mem) = PageTableBuilder::new()
700 .map_1g(vaddr, paddr, flags::WRITABLE)
701 .build();
702 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
703 let result = vas.virt_to_phys(vaddr).unwrap();
704 assert_eq!(result, paddr);
705
706 let result_offset = vas.virt_to_phys(vaddr + 0x12_3456).unwrap();
708 assert_eq!(result_offset, paddr + 0x12_3456);
709 }
710
711 #[test]
712 fn non_present_page_returns_error() {
713 let (cr3, mem) = PageTableBuilder::new().build();
714 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
715 let result = vas.virt_to_phys(0xFFFF_8000_0010_0000);
716 assert!(result.is_err());
717 match result.unwrap_err() {
718 Error::PageNotPresent(addr) => assert_eq!(addr, 0xFFFF_8000_0010_0000),
719 other => panic!("unexpected error: {other}"),
720 }
721 }
722
723 #[test]
724 fn read_virt_cross_page_boundary() {
725 let vaddr_page1: u64 = 0xFFFF_8000_0010_0000;
727 let vaddr_page2: u64 = 0xFFFF_8000_0010_1000;
728 let paddr1: u64 = 0x0080_0000;
729 let paddr2: u64 = 0x0090_0000;
730
731 let (cr3, mem) = PageTableBuilder::new()
733 .map_4k(vaddr_page1, paddr1, flags::WRITABLE)
734 .map_4k(vaddr_page2, paddr2, flags::WRITABLE)
735 .write_phys(paddr1 + 0xFFC, &[0xAA, 0xBB, 0xCC, 0xDD])
736 .write_phys(paddr2, &[0x11, 0x22, 0x33, 0x44])
737 .build();
738
739 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
740 let mut buf = [0u8; 8];
741 vas.read_virt(vaddr_page1 + 0xFFC, &mut buf).unwrap();
742 assert_eq!(buf, [0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22, 0x33, 0x44]);
743 }
744
745 #[test]
746 fn read_virt_empty_buffer() {
747 let (cr3, mem) = PageTableBuilder::new().build();
748 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
749 let mut buf = [];
750 vas.read_virt(0xFFFF_8000_0010_0000, &mut buf).unwrap();
751 }
752
753 #[test]
754 fn virt_to_phys_4k_direct() {
755 let vaddr: u64 = 0xFFFF_8000_0010_0000;
757 let paddr: u64 = 0x0080_0000;
758 let (cr3, mem) = PageTableBuilder::new()
759 .map_4k(vaddr, paddr, flags::WRITABLE)
760 .build();
761 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
762 assert_eq!(vas.virt_to_phys(vaddr).unwrap(), paddr);
763 assert_eq!(vas.virt_to_phys(vaddr + 0x42).unwrap(), paddr + 0x42);
764 }
765
766 #[test]
767 fn physical_accessor() {
768 let (cr3, mem) = PageTableBuilder::new()
769 .write_phys(0x5000, &[0xAB; 8])
770 .build();
771 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
772 let phys = vas.physical();
773 let mut buf = [0u8; 4];
774 let n = phys.read_phys(0x5000, &mut buf).unwrap();
775 assert_eq!(n, 4);
776 assert_eq!(buf, [0xAB; 4]);
777 }
778
779 #[test]
780 fn demand_zero_pte_returns_page_not_present() {
781 let vaddr: u64 = 0xFFFF_8000_0010_0000;
782 let (cr3, mem) = PageTableBuilder::new().map_demand_zero(vaddr).build();
783 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
784 let result = vas.virt_to_phys(vaddr);
785 assert!(result.is_err());
786 match result.unwrap_err() {
787 Error::PageNotPresent(addr) => assert_eq!(addr, vaddr),
788 other => panic!("expected PageNotPresent, got: {other}"),
789 }
790 }
791
792 #[test]
793 fn transition_pte_resolves_to_physical() {
794 let vaddr: u64 = 0xFFFF_8000_0010_0000;
795 let pfn: u64 = 0x800;
796 let (cr3, mem) = PageTableBuilder::new()
797 .map_transition(vaddr, pfn)
798 .write_phys(pfn * 0x1000, &[0xDE, 0xAD, 0xBE, 0xEF])
799 .build();
800 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
801 let paddr = vas.virt_to_phys(vaddr).unwrap();
802 assert_eq!(paddr, pfn * 0x1000);
803 }
804
805 #[test]
806 fn transition_pte_with_offset() {
807 let vaddr_base: u64 = 0xFFFF_8000_0010_0000;
808 let vaddr: u64 = vaddr_base + 0x42;
809 let pfn: u64 = 0x800;
810 let (cr3, mem) = PageTableBuilder::new()
811 .map_transition(vaddr_base, pfn)
812 .build();
813 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
814 let paddr = vas.virt_to_phys(vaddr).unwrap();
815 assert_eq!(paddr, pfn * 0x1000 + 0x42);
816 }
817
818 #[test]
819 fn prototype_pte_returns_error() {
820 let vaddr: u64 = 0xFFFF_8000_0010_0000;
821 let (cr3, mem) = PageTableBuilder::new().map_prototype(vaddr).build();
822 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
823 let result = vas.virt_to_phys(vaddr);
824 assert!(result.is_err());
825 match result.unwrap_err() {
826 Error::PrototypePte(addr) => assert_eq!(addr, vaddr),
827 other => panic!("expected PrototypePte, got: {other}"),
828 }
829 }
830
831 #[test]
832 fn pagefile_pte_returns_paged_out() {
833 let vaddr: u64 = 0xFFFF_8000_0010_0000;
834 let (cr3, mem) = PageTableBuilder::new()
835 .map_pagefile(vaddr, 0, 0x1234)
836 .build();
837 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
838 let result = vas.virt_to_phys(vaddr);
839 assert!(result.is_err());
840 match result.unwrap_err() {
841 Error::PagedOut {
842 vaddr: v,
843 pagefile_num,
844 page_offset,
845 } => {
846 assert_eq!(v, vaddr);
847 assert_eq!(pagefile_num, 0);
848 assert_eq!(page_offset, 0x1234);
849 }
850 other => panic!("expected PagedOut, got: {other}"),
851 }
852 }
853
854 #[test]
855 fn pagefile_pte_number_routing() {
856 let vaddr: u64 = 0xFFFF_8000_0010_0000;
857 let (cr3, mem) = PageTableBuilder::new()
858 .map_pagefile(vaddr, 2, 0xABCD)
859 .build();
860 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
861 let result = vas.virt_to_phys(vaddr);
862 match result.unwrap_err() {
863 Error::PagedOut {
864 pagefile_num,
865 page_offset,
866 ..
867 } => {
868 assert_eq!(pagefile_num, 2);
869 assert_eq!(page_offset, 0xABCD);
870 }
871 other => panic!("expected PagedOut, got: {other}"),
872 }
873 }
874
875 use crate::test_builders::{MockPagefileSource, MockPrototypePteSource};
876
877 #[test]
878 fn read_virt_demand_zero_returns_zeroes() {
879 let vaddr: u64 = 0xFFFF_8000_0010_0000;
880 let (cr3, mem) = PageTableBuilder::new().map_demand_zero(vaddr).build();
881 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
882 let mut buf = [0xFFu8; 4096];
883 vas.read_virt(vaddr, &mut buf).unwrap();
884 assert!(
885 buf.iter().all(|&b| b == 0),
886 "demand-zero page must be all zeroes"
887 );
888 }
889
890 #[test]
891 fn read_virt_transition_reads_physical() {
892 let vaddr: u64 = 0xFFFF_8000_0010_0000;
893 let pfn: u64 = 0x800;
894 let (cr3, mem) = PageTableBuilder::new()
895 .map_transition(vaddr, pfn)
896 .write_phys(pfn * 0x1000, &[0xCA, 0xFE, 0xBA, 0xBE])
897 .build();
898 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
899 let mut buf = [0u8; 4];
900 vas.read_virt(vaddr, &mut buf).unwrap();
901 assert_eq!(buf, [0xCA, 0xFE, 0xBA, 0xBE]);
902 }
903
904 #[test]
905 fn read_virt_pagefile_with_provider() {
906 let vaddr: u64 = 0xFFFF_8000_0010_0000;
907 let page_offset: u64 = 0x10;
908 let mut page_data = [0u8; 4096];
909 page_data[0..4].copy_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]);
910
911 let (cr3, mem) = PageTableBuilder::new()
912 .map_pagefile(vaddr, 0, page_offset)
913 .build();
914
915 let mock = MockPagefileSource::new(0, vec![(page_offset, page_data)]);
916 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel)
917 .with_pagefile(Box::new(mock));
918
919 let mut buf = [0u8; 4];
920 vas.read_virt(vaddr, &mut buf).unwrap();
921 assert_eq!(buf, [0xDE, 0xAD, 0xBE, 0xEF]);
922 }
923
924 #[test]
925 fn read_virt_pagefile_without_provider_errors() {
926 let vaddr: u64 = 0xFFFF_8000_0010_0000;
927 let (cr3, mem) = PageTableBuilder::new().map_pagefile(vaddr, 0, 0x10).build();
928 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
929 let mut buf = [0u8; 4];
930 let result = vas.read_virt(vaddr, &mut buf);
931 assert!(result.is_err());
932 match result.unwrap_err() {
933 Error::PagedOut {
934 pagefile_num: 0,
935 page_offset: 0x10,
936 ..
937 } => {}
938 other => panic!("expected PagedOut, got: {other}"),
939 }
940 }
941
942 #[test]
943 fn read_virt_prototype_pte_errors() {
944 let vaddr: u64 = 0xFFFF_8000_0010_0000;
945 let (cr3, mem) = PageTableBuilder::new().map_prototype(vaddr).build();
946 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
947 let mut buf = [0u8; 4];
948 let result = vas.read_virt(vaddr, &mut buf);
949 assert!(result.is_err());
950 match result.unwrap_err() {
951 Error::PrototypePte(addr) => assert_eq!(addr, vaddr),
952 other => panic!("expected PrototypePte, got: {other}"),
953 }
954 }
955
956 #[test]
957 fn read_virt_pagefile_number_routing() {
958 let vaddr1: u64 = 0xFFFF_8000_0010_0000;
959 let vaddr2: u64 = 0xFFFF_8000_0010_1000;
960
961 let mut page0_data = [0u8; 4096];
962 page0_data[0..4].copy_from_slice(&[0x11, 0x22, 0x33, 0x44]);
963 let mut page1_data = [0u8; 4096];
964 page1_data[0..4].copy_from_slice(&[0xAA, 0xBB, 0xCC, 0xDD]);
965
966 let (cr3, mem) = PageTableBuilder::new()
967 .map_pagefile(vaddr1, 0, 0x10)
968 .map_pagefile(vaddr2, 1, 0x20)
969 .build();
970
971 let mock0 = MockPagefileSource::new(0, vec![(0x10, page0_data)]);
972 let mock1 = MockPagefileSource::new(1, vec![(0x20, page1_data)]);
973
974 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel)
975 .with_pagefile(Box::new(mock0))
976 .with_pagefile(Box::new(mock1));
977
978 let mut buf1 = [0u8; 4];
979 vas.read_virt(vaddr1, &mut buf1).unwrap();
980 assert_eq!(buf1, [0x11, 0x22, 0x33, 0x44]);
981
982 let mut buf2 = [0u8; 4];
983 vas.read_virt(vaddr2, &mut buf2).unwrap();
984 assert_eq!(buf2, [0xAA, 0xBB, 0xCC, 0xDD]);
985 }
986
987 #[test]
988 fn read_virt_pagefile_out_of_range() {
989 let vaddr: u64 = 0xFFFF_8000_0010_0000;
990 let (cr3, mem) = PageTableBuilder::new()
991 .map_pagefile(vaddr, 0, 0x9999)
992 .build();
993 let mock = MockPagefileSource::new(0, vec![]);
994 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel)
995 .with_pagefile(Box::new(mock));
996 let mut buf = [0u8; 4];
997 let result = vas.read_virt(vaddr, &mut buf);
998 assert!(result.is_err());
999 match result.unwrap_err() {
1000 Error::PagedOut {
1001 page_offset: 0x9999,
1002 ..
1003 } => {}
1004 other => panic!("expected PagedOut, got: {other}"),
1005 }
1006 }
1007
1008 #[test]
1009 fn read_virt_mixed_pages_cross_boundary() {
1010 let vaddr1: u64 = 0xFFFF_8000_0010_0000;
1011 let vaddr2: u64 = 0xFFFF_8000_0010_1000;
1012 let vaddr3: u64 = 0xFFFF_8000_0010_2000;
1013 let paddr1: u64 = 0x0080_0000;
1014
1015 let mut pf_page = [0u8; 4096];
1016 pf_page[0..4].copy_from_slice(&[0xBB; 4]);
1017
1018 let (cr3, mem) = PageTableBuilder::new()
1019 .map_4k(vaddr1, paddr1, flags::WRITABLE)
1020 .write_phys(paddr1 + 0xFFC, &[0xAA; 4])
1021 .map_pagefile(vaddr2, 0, 0x10)
1022 .map_demand_zero(vaddr3)
1023 .build();
1024
1025 let mock = MockPagefileSource::new(0, vec![(0x10, pf_page)]);
1026 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel)
1027 .with_pagefile(Box::new(mock));
1028
1029 let mut buf = [0u8; 8];
1031 vas.read_virt(vaddr1 + 0xFFC, &mut buf).unwrap();
1032 assert_eq!(buf, [0xAA, 0xAA, 0xAA, 0xAA, 0xBB, 0xBB, 0xBB, 0xBB]);
1033
1034 let mut buf2 = [0u8; 8];
1036 vas.read_virt(vaddr2 + 0xFFC, &mut buf2).unwrap();
1037 assert_eq!(buf2, [0u8; 8]);
1038 }
1039
1040 #[test]
1041 fn read_virt_prototype_pte_resolves_when_source_provided() {
1042 let vaddr: u64 = 0xFFFF_8000_0010_0000;
1043 let resolved_paddr: u64 = 0x00A0_0000;
1044 let raw_pte: u64 = (1 << 10) | (0xABCu64 << 12);
1046
1047 let (cr3, mem) = PageTableBuilder::new()
1048 .map_prototype_raw(vaddr, raw_pte)
1049 .write_phys(resolved_paddr, &[0xDE, 0xAD, 0xBE, 0xEF])
1050 .build();
1051
1052 let mock = MockPrototypePteSource::new(vec![(raw_pte, resolved_paddr)]);
1053 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel)
1054 .with_prototype_source(Box::new(mock));
1055
1056 let mut buf = [0u8; 4];
1057 vas.read_virt(vaddr, &mut buf).unwrap();
1058 assert_eq!(buf, [0xDE, 0xAD, 0xBE, 0xEF]);
1059 }
1060
1061 #[test]
1062 fn read_virt_prototype_pte_errors_when_source_returns_none() {
1063 let vaddr: u64 = 0xFFFF_8000_0010_0000;
1064 let raw_pte: u64 = (1 << 10) | (0xDEFu64 << 12);
1065
1066 let (cr3, mem) = PageTableBuilder::new()
1067 .map_prototype_raw(vaddr, raw_pte)
1068 .build();
1069
1070 let mock = MockPrototypePteSource::new(vec![]);
1072 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel)
1073 .with_prototype_source(Box::new(mock));
1074
1075 let mut buf = [0u8; 4];
1076 let result = vas.read_virt(vaddr, &mut buf);
1077 assert!(result.is_err());
1078 match result.unwrap_err() {
1079 Error::PrototypePte(addr) => assert_eq!(addr, vaddr),
1080 other => panic!("expected PrototypePte, got: {other}"),
1081 }
1082 }
1083
1084 #[test]
1085 fn translation_cache_hit_returns_same_result() {
1086 let vaddr: u64 = 0xFFFF_8000_0010_0000;
1087 let paddr: u64 = 0x0080_0000;
1088 let (cr3, mem) = PageTableBuilder::new()
1089 .map_4k(vaddr, paddr, flags::WRITABLE)
1090 .build();
1091 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
1092 let first = vas.virt_to_phys(vaddr).unwrap();
1093 let second = vas.virt_to_phys(vaddr).unwrap();
1094 assert_eq!(first, second);
1095 assert_eq!(first, paddr);
1096 }
1097
1098 #[test]
1099 fn translate_5level_variant_compiles() {
1100 assert_ne!(
1102 std::mem::discriminant(&TranslationMode::X86_645Level),
1103 std::mem::discriminant(&TranslationMode::X86_64FourLevel),
1104 );
1105 }
1106
1107 #[test]
1108 fn translate_5level_4k_page() {
1109 let vaddr: u64 = 0xFFFF_8000_0010_0000;
1115 let paddr: u64 = 0x0090_0000;
1116 let (cr3, mem) = PageTableBuilder::new()
1117 .map_4k(vaddr, paddr, flags::WRITABLE)
1118 .build();
1119 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_645Level);
1120 let _ = vas.virt_to_phys(vaddr); }
1124
1125 #[test]
1126 fn translation_cache_capacity_100_distinct_pages() {
1127 use std::collections::HashSet;
1128 let base_vaddr: u64 = 0xFFFF_8000_0000_0000;
1129 let mut builder = PageTableBuilder::new();
1130 for i in 0..200u64 {
1131 builder = builder.map_4k(
1132 base_vaddr + i * 0x1000,
1133 0x1000 + i * 0x1000,
1134 flags::WRITABLE,
1135 );
1136 }
1137 let (cr3, mem) = builder.build();
1138 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
1139 let mut results = HashSet::new();
1140 for i in 0..200u64 {
1141 let paddr = vas.virt_to_phys(base_vaddr + i * 0x1000).unwrap();
1142 results.insert(paddr);
1143 }
1144 assert_eq!(
1145 results.len(),
1146 200,
1147 "each page must map to a distinct physical address"
1148 );
1149 }
1150
1151 #[test]
1152 fn read_virt_prototype_pte_with_page_offset() {
1153 let vaddr_base: u64 = 0xFFFF_8000_0010_0000;
1154 let vaddr: u64 = vaddr_base + 0x100;
1155 let resolved_paddr: u64 = 0x00B0_0000;
1156 let raw_pte: u64 = (1 << 10) | (0x123u64 << 12);
1157
1158 let (cr3, mem) = PageTableBuilder::new()
1159 .map_prototype_raw(vaddr_base, raw_pte)
1160 .write_phys(resolved_paddr + 0x100, &[0xCA, 0xFE])
1161 .build();
1162
1163 let mock = MockPrototypePteSource::new(vec![(raw_pte, resolved_paddr)]);
1164 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel)
1165 .with_prototype_source(Box::new(mock));
1166
1167 let mut buf = [0u8; 2];
1168 vas.read_virt(vaddr, &mut buf).unwrap();
1169 assert_eq!(buf, [0xCA, 0xFE]);
1170 }
1171
1172 #[test]
1173 fn aarch64_mode_distinct_from_x86() {
1174 assert_ne!(
1175 std::mem::discriminant(&TranslationMode::AArch64FourLevel),
1176 std::mem::discriminant(&TranslationMode::X86_64FourLevel),
1177 );
1178 }
1179
1180 #[test]
1181 fn aarch64_non_present_returns_error() {
1182 let (cr3, mem) = PageTableBuilder::new().build();
1184 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::AArch64FourLevel);
1185 let result = vas.virt_to_phys(0x1000);
1186 assert!(matches!(result, Err(Error::PageNotPresent(_))));
1187 }
1188
1189 #[test]
1190 fn aarch64_translate_4k_page() {
1191 let vaddr: u64 = 0x0000_0000_0010_0000; let paddr: u64 = 0x0080_0000;
1198 let (cr3, mem) = PageTableBuilder::new()
1199 .map_4k(vaddr, paddr, flags::WRITABLE)
1200 .build();
1201 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::AArch64FourLevel);
1202 let result = vas.virt_to_phys(vaddr);
1203 assert!(
1208 result.is_ok() || matches!(result, Err(Error::PageNotPresent(_))),
1209 "must not panic; got {result:?}"
1210 );
1211 }
1212
1213 #[test]
1214 fn multiple_mappings_same_pml4() {
1215 let vaddr1: u64 = 0xFFFF_8000_0010_0000;
1216 let vaddr2: u64 = 0xFFFF_8000_0010_1000;
1217 let paddr1: u64 = 0x0080_0000;
1218 let paddr2: u64 = 0x0090_0000;
1219
1220 let (cr3, mem) = PageTableBuilder::new()
1221 .map_4k(vaddr1, paddr1, flags::WRITABLE)
1222 .map_4k(vaddr2, paddr2, flags::WRITABLE)
1223 .write_phys(paddr1, &[0x11; 8])
1224 .write_phys(paddr2, &[0x22; 8])
1225 .build();
1226
1227 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
1228
1229 let mut buf1 = [0u8; 8];
1230 vas.read_virt(vaddr1, &mut buf1).unwrap();
1231 assert_eq!(buf1, [0x11; 8]);
1232
1233 let mut buf2 = [0u8; 8];
1234 vas.read_virt(vaddr2, &mut buf2).unwrap();
1235 assert_eq!(buf2, [0x22; 8]);
1236 }
1237}