1use crate::Error;
7use crate::IdentityMapSize;
8use core::ops::RangeInclusive;
9use zerocopy::FromBytes;
10use zerocopy::Immutable;
11use zerocopy::IntoBytes;
12use zerocopy::KnownLayout;
13
14const X64_PTE_PRESENT: u64 = 1;
15const X64_PTE_READ_WRITE: u64 = 1 << 1;
16const X64_PTE_ACCESSED: u64 = 1 << 5;
17const X64_PTE_DIRTY: u64 = 1 << 6;
18const X64_PTE_LARGE_PAGE: u64 = 1 << 7;
19
20const PAGE_TABLE_ENTRY_COUNT: usize = 512;
21const PAGE_TABLE_ENTRY_SIZE: usize = 8;
22
23const X64_PAGE_SHIFT: u64 = 12;
24const X64_PTE_BITS: u64 = 9;
25const X64_PTE_ADDRESS_MASK: u64 = 0x000f_ffff_ffff_f000;
26const X64_PTE_ADDRESS_LAST_BIT: u8 = (u64::BITS - X64_PTE_ADDRESS_MASK.leading_zeros() - 1) as u8;
27
28pub const X64_PTE_ADDRESS_BIT_RANGE: RangeInclusive<u8> =
31 X64_PAGE_SHIFT as u8..=X64_PTE_ADDRESS_LAST_BIT;
32
33pub const X64_PAGE_SIZE: u64 = 4096;
35
36pub const X64_LARGE_PAGE_SIZE: u64 = 0x200000;
38
39pub const X64_1GB_PAGE_SIZE: u64 = 0x40000000;
41
42pub const PAGE_TABLE_MAX_COUNT: usize = 20;
47
48static_assertions::const_assert_eq!(
49 PAGE_TABLE_ENTRY_SIZE * PAGE_TABLE_ENTRY_COUNT,
50 X64_PAGE_SIZE as usize
51);
52const PAGE_TABLE_SIZE: usize = PAGE_TABLE_ENTRY_COUNT * PAGE_TABLE_ENTRY_SIZE;
53
54pub const PAGE_TABLE_MAX_BYTES: usize = PAGE_TABLE_MAX_COUNT * X64_PAGE_SIZE as usize;
56
57#[derive(Copy, Clone, PartialEq, Eq, IntoBytes, Immutable, KnownLayout, FromBytes)]
58#[repr(transparent)]
59pub struct PageTableEntry {
61 pub(crate) entry: u64,
62}
63
64#[derive(Copy, Clone, Debug)]
67pub struct MappedRange {
68 start: u64,
69 end: u64,
70 permissions: u64,
71}
72
73impl MappedRange {
74 pub fn new(start: u64, end: u64) -> Self {
76 Self {
77 start,
78 end,
79 permissions: X64_PTE_PRESENT | X64_PTE_ACCESSED | X64_PTE_READ_WRITE,
80 }
81 }
82
83 pub fn start(&self) -> u64 {
85 self.start
86 }
87
88 pub fn end(&self) -> u64 {
90 self.end
91 }
92
93 pub fn read_only(mut self) -> Self {
95 self.permissions &= !X64_PTE_READ_WRITE;
96 self
97 }
98}
99
100impl core::fmt::Debug for PageTableEntry {
101 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
102 f.debug_struct("PageTableEntry")
103 .field("entry", &self.entry)
104 .field("is_present", &self.is_present())
105 .field("gpa", &self.gpa())
106 .finish()
107 }
108}
109
110#[derive(Debug, Copy, Clone)]
111pub enum PageTableEntryType {
112 Leaf1GbPage(u64),
114 Leaf2MbPage(u64),
116 Leaf4kPage(u64),
118 Pde(u64),
120}
121
122#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
124#[repr(u8)]
125pub enum EntryLevel {
126 Pml4 = 3,
127 Pdpt = 2,
128 Pd = 1,
129 Pt = 0,
130}
131
132impl EntryLevel {
133 pub fn mapping_size(self) -> u64 {
138 match self {
139 Self::Pml4 => X64_1GB_PAGE_SIZE * 512,
140 Self::Pdpt => X64_1GB_PAGE_SIZE,
141 Self::Pd => X64_LARGE_PAGE_SIZE,
142 Self::Pt => X64_PAGE_SIZE,
143 }
144 }
145
146 pub fn leaf(self, va: u64) -> PageTableEntryType {
148 match self {
149 Self::Pml4 => panic!("cannot insert a leaf entry into a PML4 table"),
150 Self::Pdpt => PageTableEntryType::Leaf1GbPage(va),
151 Self::Pd => PageTableEntryType::Leaf2MbPage(va),
152 Self::Pt => PageTableEntryType::Leaf4kPage(va),
153 }
154 }
155
156 fn pa_mask(self) -> u64 {
157 match self {
158 Self::Pml4 => 0x000f_ffff_c000_0000,
159 Self::Pdpt => 0x000f_ffff_ffe0_0000,
160 Self::Pd => 0x000f_ffff_ffff_f000,
161 Self::Pt => 0x000f_ffff_ffff_f000,
162 }
163 }
164
165 pub fn directory_pa(self, va: u64) -> u64 {
168 va & self.pa_mask()
169 }
170}
171
172impl PageTableEntry {
173 const VALID_BITS: u64 = 0x000f_ffff_ffff_f000;
174
175 pub fn set_entry(&mut self, entry_type: PageTableEntryType) {
178 self.entry = X64_PTE_PRESENT | X64_PTE_ACCESSED | X64_PTE_READ_WRITE;
179
180 match entry_type {
181 PageTableEntryType::Leaf1GbPage(address) => {
182 assert!(address % X64_1GB_PAGE_SIZE == 0);
184 self.entry |= address;
185 self.entry |= X64_PTE_LARGE_PAGE | X64_PTE_DIRTY;
186 }
187 PageTableEntryType::Leaf2MbPage(address) => {
188 assert!(address % X64_LARGE_PAGE_SIZE == 0);
190 self.entry |= address;
191 self.entry |= X64_PTE_LARGE_PAGE | X64_PTE_DIRTY;
192 }
193 PageTableEntryType::Leaf4kPage(address) => {
194 assert!(address % X64_PAGE_SIZE == 0);
196 self.entry |= address;
197 self.entry |= X64_PTE_DIRTY;
198 }
199 PageTableEntryType::Pde(address) => {
200 assert!(address % X64_PAGE_SIZE == 0);
202 self.entry |= address;
203 }
204 }
205 }
206
207 pub fn is_present(&self) -> bool {
209 self.entry & X64_PTE_PRESENT == X64_PTE_PRESENT
210 }
211
212 pub fn gpa(&self) -> Option<u64> {
214 if self.is_present() {
215 Some(self.entry & Self::VALID_BITS)
217 } else {
218 None
219 }
220 }
221
222 pub fn set_addr(&mut self, addr: u64) {
224 assert!(addr & !Self::VALID_BITS == 0);
225
226 self.entry &= !Self::VALID_BITS;
228 self.entry |= addr;
229 }
230
231 pub fn get_addr(&self) -> u64 {
233 self.entry & Self::VALID_BITS
234 }
235
236 pub fn clear(&mut self) {
238 self.entry = 0;
239 }
240}
241
242#[repr(C)]
243#[derive(Clone, PartialEq, Eq, IntoBytes, Immutable, KnownLayout, FromBytes)]
244pub struct PageTable {
246 entries: [PageTableEntry; PAGE_TABLE_ENTRY_COUNT],
247}
248
249impl PageTable {
250 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut PageTableEntry> {
252 self.entries.iter_mut()
253 }
254
255 pub fn entry(&mut self, gva: u64, level: u8) -> &mut PageTableEntry {
257 let index = get_amd64_pte_index(gva, level as u64) as usize;
258 &mut self.entries[index]
259 }
260}
261
262impl core::ops::Index<usize> for PageTable {
263 type Output = PageTableEntry;
264
265 fn index(&self, index: usize) -> &Self::Output {
266 &self.entries[index]
267 }
268}
269
270impl core::ops::IndexMut<usize> for PageTable {
271 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
272 &mut self.entries[index]
273 }
274}
275
276pub fn get_amd64_pte_index(gva: u64, page_map_level: u64) -> u64 {
278 let index = gva >> (X64_PAGE_SHIFT + page_map_level * X64_PTE_BITS);
279 index & ((1 << X64_PTE_BITS) - 1)
280}
281
282pub fn calculate_pde_table_count(start_gpa: u64, size: u64) -> u64 {
284 let mut count = 0;
285
286 let start_aligned_up = align_up_to_1_gb_page_size(start_gpa);
288 let end_gpa = start_gpa + size;
289 let end_aligned_down = (end_gpa / X64_1GB_PAGE_SIZE) * X64_1GB_PAGE_SIZE;
290
291 if size < X64_1GB_PAGE_SIZE {
293 if end_gpa > end_aligned_down && start_gpa < end_aligned_down {
295 count = 2;
296 } else {
297 count = 1;
298 }
299 } else {
300 if start_gpa != start_aligned_up {
302 count += 1;
303 }
304
305 if end_aligned_down > start_aligned_up {
307 count += (end_aligned_down - start_aligned_up) / X64_1GB_PAGE_SIZE;
308 }
309
310 if end_gpa > end_aligned_down {
312 count += 1;
313 }
314 }
315
316 count
317}
318
319#[derive(Debug, Clone)]
320struct PageTableBuilderInner {
321 page_table_gpa: u64,
322 confidential_bit: Option<u32>,
323}
324
325pub struct PageTableBuilder<'a> {
327 inner: PageTableBuilderInner,
329 page_table: &'a mut [PageTable],
331 flattened_page_table: &'a mut [u8],
333 ranges: &'a [MappedRange],
335}
336
337impl PageTableBuilderInner {
338 fn get_addr_mask(&self) -> u64 {
339 X64_PTE_ADDRESS_MASK & !self.get_confidential_mask()
340 }
341
342 fn get_confidential_mask(&self) -> u64 {
343 if let Some(confidential_bit) = self.confidential_bit {
344 1u64 << confidential_bit
345 } else {
346 0
347 }
348 }
349
350 fn build_pte(&self, entry_type: PageTableEntryType, permissions: u64) -> PageTableEntry {
351 let mut entry: u64 = permissions;
352
353 match entry_type {
354 PageTableEntryType::Leaf1GbPage(address) => {
355 assert_eq!(address % X64_1GB_PAGE_SIZE, 0);
357 entry |= address;
358 entry |= X64_PTE_LARGE_PAGE | X64_PTE_DIRTY;
359 }
360 PageTableEntryType::Leaf2MbPage(address) => {
361 assert_eq!(address % X64_LARGE_PAGE_SIZE, 0);
363 entry |= address;
364 entry |= X64_PTE_LARGE_PAGE | X64_PTE_DIRTY;
365 }
366 PageTableEntryType::Leaf4kPage(address) => {
367 assert_eq!(address % X64_PAGE_SIZE, 0);
369 entry |= address;
370 entry |= X64_PTE_DIRTY;
371 }
372 PageTableEntryType::Pde(address) => {
373 assert_eq!(address % X64_PAGE_SIZE, 0);
375 entry |= address;
376 }
377 }
378
379 let mask = self.get_confidential_mask();
380 if self.confidential_bit.is_some() {
381 entry |= mask;
382 } else {
383 entry &= !mask;
384 }
385
386 PageTableEntry { entry }
387 }
388
389 fn get_addr_from_pte(&self, pte: &PageTableEntry) -> u64 {
390 pte.entry & self.get_addr_mask()
391 }
392}
393
394impl<'a> PageTableBuilder<'a> {
395 pub fn new(
403 page_table_gpa: u64,
404 page_table: &'a mut [PageTable],
405 flattened_page_table: &'a mut [u8],
406 ranges: &'a [MappedRange],
407 ) -> Result<Self, Error> {
408 if flattened_page_table.len() != (page_table.len() * PAGE_TABLE_SIZE) {
411 Err(Error::BadBufferSize {
412 bytes_buf: flattened_page_table.len(),
413 struct_buf: page_table.len() * PAGE_TABLE_SIZE,
414 })
415 } else {
416 for range in ranges.iter() {
417 if range.start() > range.end() {
418 return Err(Error::InvalidRange);
419 }
420 }
421
422 for window in ranges.windows(2) {
423 let (l, r) = (&window[0], &window[1]);
424
425 if r.start() < l.start() {
426 return Err(Error::UnsortedMappings);
427 }
428
429 if l.end() > r.start() {
430 return Err(Error::OverlappingMappings);
431 }
432 }
433 Ok(PageTableBuilder {
434 inner: PageTableBuilderInner {
435 page_table_gpa,
436 confidential_bit: None,
437 },
438 page_table,
439 flattened_page_table,
440 ranges,
441 })
442 }
443 }
444
445 pub fn with_confidential_bit(mut self, bit_position: u32) -> Self {
447 self.inner.confidential_bit = Some(bit_position);
448 self
449 }
450
451 pub fn build(self) -> Result<&'a [u8], Error> {
455 let PageTableBuilder {
456 page_table,
457 flattened_page_table,
458 ranges,
459 inner,
460 } = self;
461
462 let (mut page_table_index, pml4_table_index) = (0, 0);
464
465 let mut link_tables = |start_va: u64, end_va: u64, permissions: u64| -> Result<(), Error> {
467 let mut current_va = start_va;
468 let mut get_or_insert_entry = |table_index: usize,
469 entry_level: EntryLevel,
470 current_va: &mut u64|
471 -> Result<Option<usize>, Error> {
472 if (*current_va).is_multiple_of(entry_level.mapping_size())
474 && (*current_va + entry_level.mapping_size() <= end_va)
475 {
476 let entry = page_table[table_index].entry(*current_va, entry_level as u8);
477 if entry.is_present() {
478 return Err(Error::AttemptedEntryOverwrite);
480 }
481
482 #[cfg(feature = "tracing")]
483 tracing::trace!(
484 "inserting entry for va: {:#X} at level {:?}",
485 current_va,
486 entry_level
487 );
488
489 let new_entry = inner.build_pte(entry_level.leaf(*current_va), permissions);
490 *entry = new_entry;
491 *current_va += entry_level.mapping_size();
492
493 Ok(None)
494 }
495 else {
500 let directory_pa = entry_level.directory_pa(*current_va);
501 let len = page_table.len();
502 let entry = page_table[table_index].entry(directory_pa, entry_level as u8);
503
504 if !entry.is_present() {
505 page_table_index += 1;
506
507 if page_table_index >= len {
508 return Err(Error::NotEnoughMemory);
509 }
510 let output_address =
512 inner.page_table_gpa + page_table_index as u64 * X64_PAGE_SIZE;
513
514 let new_entry = inner.build_pte(
519 PageTableEntryType::Pde(output_address),
520 X64_PTE_PRESENT | X64_PTE_ACCESSED | X64_PTE_READ_WRITE,
521 );
522
523 #[cfg(feature = "tracing")]
524 tracing::trace!(
525 "creating directory for va: {:#X} at level {:?}",
526 directory_pa,
527 entry_level
528 );
529 *entry = new_entry;
530
531 Ok(Some(page_table_index))
532 } else {
533 Ok(Some(
534 ((inner.get_addr_from_pte(entry) - inner.page_table_gpa)
535 / X64_PAGE_SIZE)
536 .try_into()
537 .expect("Valid page table index"),
538 ))
539 }
540 }
541 };
542
543 while current_va < end_va {
544 #[cfg(feature = "tracing")]
545 tracing::trace!("creating entry for va: {:#X}", current_va);
546 let pdpt_table_index =
549 get_or_insert_entry(pml4_table_index, EntryLevel::Pml4, &mut current_va)?;
550 if let Some(pdpt_table_index) = pdpt_table_index {
551 let pd_table_index =
552 get_or_insert_entry(pdpt_table_index, EntryLevel::Pdpt, &mut current_va)?;
553 if let Some(pd_table_index) = pd_table_index {
554 let pt_table_index =
555 get_or_insert_entry(pd_table_index, EntryLevel::Pd, &mut current_va)?;
556 if let Some(pt_table_index) = pt_table_index {
557 get_or_insert_entry(pt_table_index, EntryLevel::Pt, &mut current_va)?;
558 }
559 }
560 }
561 }
562
563 Ok(())
564 };
565
566 for range in ranges {
567 link_tables(range.start, range.end, range.permissions)?;
568 }
569
570 Ok(flatten_page_table(
572 page_table,
573 flattened_page_table,
574 page_table_index + 1,
575 ))
576 }
577}
578
579#[derive(Debug, Clone)]
580struct IdentityMapBuilderParams {
581 page_table_gpa: u64,
582 identity_map_size: IdentityMapSize,
583 address_bias: u64,
584 pml4e_link: Option<(u64, u64)>,
585 confidential_bit: Option<u32>,
586}
587
588pub struct IdentityMapBuilder<'a> {
591 params: IdentityMapBuilderParams,
592 page_table: &'a mut [PageTable],
594 flattened_page_table: &'a mut [u8],
596}
597
598impl<'a> IdentityMapBuilder<'a> {
599 pub fn new(
607 page_table_gpa: u64,
608 identity_map_size: IdentityMapSize,
609 page_table: &'a mut [PageTable],
610 flattened_page_table: &'a mut [u8],
611 ) -> Result<Self, Error> {
612 if flattened_page_table.len() != (page_table.len() * PAGE_TABLE_SIZE) {
613 Err(Error::BadBufferSize {
614 bytes_buf: flattened_page_table.len(),
615 struct_buf: page_table.len() * PAGE_TABLE_SIZE,
616 })
617 } else {
618 Ok(IdentityMapBuilder {
619 params: IdentityMapBuilderParams {
620 page_table_gpa,
621 identity_map_size,
622 address_bias: 0,
623 pml4e_link: None,
624 confidential_bit: None,
625 },
626 page_table,
627 flattened_page_table,
628 })
629 }
630 }
631
632 pub fn with_address_bias(mut self, address_bias: u64) -> Self {
635 self.params.address_bias = address_bias;
636 self
637 }
638
639 pub fn with_confidential_bit(mut self, bit_position: u32) -> Self {
641 self.params.confidential_bit = Some(bit_position);
642 self
643 }
644
645 pub fn with_pml4e_link(mut self, pml4e_link: (u64, u64)) -> Self {
649 self.params.pml4e_link = Some(pml4e_link);
650 self
651 }
652
653 pub fn build(self) -> &'a [u8] {
656 let IdentityMapBuilder {
657 page_table,
658 flattened_page_table,
659 params,
660 } = self;
661 let set_entry = |entry: &mut PageTableEntry, entry_type| {
662 entry.set_entry(entry_type);
663 if let Some(bit_position) = params.confidential_bit {
664 entry.entry |= 1u64 << bit_position;
665 }
666 };
667
668 let leaf_page_table_count = match params.identity_map_size {
674 IdentityMapSize::Size4Gb => 4,
675 IdentityMapSize::Size8Gb => 8,
676 };
677 let page_table_count = leaf_page_table_count + if params.address_bias == 0 { 2 } else { 1 };
678 let mut page_table_allocator = page_table.iter_mut().enumerate();
679
680 let pdpte_table = if params.address_bias == 0 {
682 let (_, pml4e_table) = page_table_allocator
684 .next()
685 .expect("pagetable should always be available, code bug if not");
686
687 let (pdpte_table_index, pdpte_table) = page_table_allocator
689 .next()
690 .expect("pagetable should always be available, code bug if not");
691
692 let output_address = params.page_table_gpa + pdpte_table_index as u64 * X64_PAGE_SIZE;
694 set_entry(
695 &mut pml4e_table.entries[0],
696 PageTableEntryType::Pde(output_address),
697 );
698
699 if let Some((link_target_gpa, linkage_gpa)) = params.pml4e_link {
701 assert!((linkage_gpa & 0x7FFFFFFFFF) == 0);
702 set_entry(
703 &mut pml4e_table.entries[linkage_gpa as usize >> 39],
704 PageTableEntryType::Pde(link_target_gpa),
705 );
706 }
707
708 pdpte_table
709 } else {
710 page_table_allocator
712 .next()
713 .expect("pagetable should always be available, code bug if not")
714 .1
715 };
716
717 let top_address = params.identity_map_size.address_space_size();
719 let mut current_va = 0;
720
721 while current_va < top_address {
722 let (pde_table_index, pde_table) = page_table_allocator
724 .next()
725 .expect("pagetable should always be available, code bug if not");
726
727 let pdpte_index = get_amd64_pte_index(current_va, 2);
729 let output_address = params.page_table_gpa + pde_table_index as u64 * X64_PAGE_SIZE;
730 let pdpte_entry = &mut pdpte_table.entries[pdpte_index as usize];
731 assert!(!pdpte_entry.is_present());
732 set_entry(pdpte_entry, PageTableEntryType::Pde(output_address));
733
734 for entry in pde_table.iter_mut() {
736 set_entry(
737 entry,
738 PageTableEntryType::Leaf2MbPage(current_va + params.address_bias),
739 );
740 current_va += X64_LARGE_PAGE_SIZE;
741 }
742 }
743
744 flatten_page_table(page_table, flattened_page_table, page_table_count)
746 }
747}
748
749pub fn align_up_to_page_size(address: u64) -> u64 {
751 (address + X64_PAGE_SIZE - 1) & !(X64_PAGE_SIZE - 1)
752}
753
754pub fn align_up_to_large_page_size(address: u64) -> u64 {
756 (address + X64_LARGE_PAGE_SIZE - 1) & !(X64_LARGE_PAGE_SIZE - 1)
757}
758
759pub fn align_up_to_1_gb_page_size(address: u64) -> u64 {
761 (address + X64_1GB_PAGE_SIZE - 1) & !(X64_1GB_PAGE_SIZE - 1)
762}
763
764fn flatten_page_table<'a>(
765 page_table: &mut [PageTable],
766 flattened_page_table: &'a mut [u8],
767 page_table_count: usize,
768) -> &'a [u8] {
769 for (page_table, dst) in page_table
770 .iter()
771 .take(page_table_count)
772 .zip(flattened_page_table.chunks_mut(PAGE_TABLE_SIZE))
773 {
774 let src = page_table.as_bytes();
775 dst.copy_from_slice(src);
776 }
777
778 &flattened_page_table[0..PAGE_TABLE_SIZE * page_table_count]
779}
780
781#[cfg(test)]
782mod tests {
783 use std;
784 use std::vec;
785
786 use super::Error;
787 use super::IdentityMapBuilder;
788 use super::IdentityMapSize;
789 use super::MappedRange;
790 use super::PAGE_TABLE_MAX_BYTES;
791 use super::PAGE_TABLE_MAX_COUNT;
792 use super::PageTable;
793 use super::PageTableBuilder;
794 use super::X64_1GB_PAGE_SIZE;
795 use super::X64_PTE_PRESENT;
796 use super::align_up_to_large_page_size;
797 use super::align_up_to_page_size;
798 use super::calculate_pde_table_count;
799 use zerocopy::FromZeros;
800
801 #[test]
802 fn test_align_up() {
803 assert_eq!(align_up_to_page_size(4096), 4096);
804 assert_eq!(align_up_to_page_size(4095), 4096);
805 assert_eq!(align_up_to_page_size(4097), 8192);
806 }
807
808 #[test]
809 fn test_large_align_up() {
810 assert_eq!(align_up_to_large_page_size(0), 0);
811 assert_eq!(align_up_to_large_page_size(4096), 0x200000);
812 assert_eq!(align_up_to_large_page_size(0x200000), 0x200000);
813 assert_eq!(align_up_to_large_page_size(0x200001), 0x400000);
814 }
815
816 #[test]
817 fn test_pde_size_calc() {
818 assert_eq!(calculate_pde_table_count(0, 512), 1);
819 assert_eq!(calculate_pde_table_count(0, 1024 * 1024), 1);
820 assert_eq!(calculate_pde_table_count(512, 1024 * 1024), 1);
821 assert_eq!(calculate_pde_table_count(X64_1GB_PAGE_SIZE - 512, 1024), 2);
822 assert_eq!(calculate_pde_table_count(X64_1GB_PAGE_SIZE - 512, 512), 1);
823 assert_eq!(calculate_pde_table_count(0, X64_1GB_PAGE_SIZE), 1);
824 assert_eq!(calculate_pde_table_count(0, X64_1GB_PAGE_SIZE + 1), 2);
825 assert_eq!(calculate_pde_table_count(1, X64_1GB_PAGE_SIZE + 1), 2);
826 assert_eq!(calculate_pde_table_count(512, X64_1GB_PAGE_SIZE * 2), 3);
827
828 assert_eq!(calculate_pde_table_count(0, X64_1GB_PAGE_SIZE * 3), 3);
829 assert_eq!(
830 calculate_pde_table_count(X64_1GB_PAGE_SIZE, X64_1GB_PAGE_SIZE * 3),
831 3
832 );
833 }
834
835 fn check_page_table_count(ranges: &[MappedRange], count: usize) {
836 let mut page_table_work_buffer: Vec<PageTable> =
837 vec![PageTable::new_zeroed(); PAGE_TABLE_MAX_COUNT];
838 let mut page_table: Vec<u8> = vec![0; PAGE_TABLE_MAX_BYTES];
839
840 let page_table_builder = PageTableBuilder::new(
841 0,
842 page_table_work_buffer.as_mut_slice(),
843 page_table.as_mut_slice(),
844 ranges,
845 )
846 .expect("page table builder initialization should succeed");
847
848 let page_table = page_table_builder.build().expect("building should succeed");
849 assert_eq!(page_table.len(), count);
850 }
851
852 fn page_table_builder_error(ranges: &[MappedRange]) -> Option<Error> {
853 let mut page_table_work_buffer: Vec<PageTable> =
854 vec![PageTable::new_zeroed(); PAGE_TABLE_MAX_COUNT];
855 let mut page_table: Vec<u8> = vec![0; PAGE_TABLE_MAX_BYTES];
856
857 PageTableBuilder::new(
858 0,
859 page_table_work_buffer.as_mut_slice(),
860 page_table.as_mut_slice(),
861 ranges,
862 )
863 .err()
864 }
865
866 #[test]
867 fn test_page_table_entry_sizing() {
868 const ONE_GIG: u64 = 1024 * 1024 * 1024;
869 const TWO_MB: u64 = 1024 * 1024 * 2;
870 const FOUR_KB: u64 = 4096;
871
872 check_page_table_count(&[MappedRange::new(0, ONE_GIG)], 4096 * 2);
873 check_page_table_count(&[MappedRange::new(0, TWO_MB)], 4096 * 3);
874 check_page_table_count(&[MappedRange::new(0, FOUR_KB)], 4096 * 4);
875 check_page_table_count(&[MappedRange::new(FOUR_KB, ONE_GIG)], 4096 * 4);
876 check_page_table_count(&[MappedRange::new(TWO_MB, ONE_GIG)], 4096 * 3);
877 check_page_table_count(&[MappedRange::new(TWO_MB, ONE_GIG + FOUR_KB)], 4096 * 5);
878 check_page_table_count(&[MappedRange::new(TWO_MB, ONE_GIG + TWO_MB)], 4096 * 4);
879 }
880
881 #[test]
882 fn test_page_table_builder_overlapping_range() {
883 const ONE_GIG: u64 = 1024 * 1024 * 1024;
884 const TWO_MB: u64 = 1024 * 1024 * 2;
885 const FOUR_KB: u64 = 4096;
886
887 let err = page_table_builder_error(&[
888 MappedRange::new(FOUR_KB, ONE_GIG),
889 MappedRange::new(TWO_MB, ONE_GIG),
890 ])
891 .expect("must fail");
892 assert!(matches!(err, Error::OverlappingMappings));
893 }
894
895 #[test]
896 fn test_page_table_builder_invalid_range() {
897 const ONE_GIG: u64 = 1024 * 1024 * 1024;
898 const FOUR_KB: u64 = 4096;
899
900 let err =
901 page_table_builder_error(&[MappedRange::new(ONE_GIG, FOUR_KB)]).expect("must fail");
902 assert!(matches!(err, Error::InvalidRange));
903 }
904
905 #[test]
906 fn test_page_table_builder_oom() {
907 const ONE_GIG: u64 = 1024 * 1024 * 1024;
908
909 let mut page_table_work_buffer: Vec<PageTable> = vec![PageTable::new_zeroed(); 1];
910 let mut page_table: Vec<u8> = vec![0; 4096];
911
912 let err = PageTableBuilder::new(
913 0,
914 page_table_work_buffer.as_mut_slice(),
915 page_table.as_mut_slice(),
916 &[MappedRange::new(0, ONE_GIG)],
917 )
918 .expect("page table builder initialization should succeed")
919 .build()
920 .expect_err("building page tables should fail");
921
922 assert!(matches!(err, Error::NotEnoughMemory));
923 }
924
925 #[test]
926 fn test_page_table_builder_mismatched_buffers() {
927 const ONE_GIG: u64 = 1024 * 1024 * 1024;
928
929 let mut page_table_work_buffer: Vec<PageTable> = vec![PageTable::new_zeroed(); 4];
930 let mut page_table: Vec<u8> = vec![0; 4096 * 5];
931
932 let err = PageTableBuilder::new(
933 0,
934 page_table_work_buffer.as_mut_slice(),
935 page_table.as_mut_slice(),
936 &[MappedRange::new(0, ONE_GIG)],
937 )
938 .err()
939 .expect("building page tables should fail");
940
941 assert!(matches!(
942 err,
943 Error::BadBufferSize {
944 bytes_buf: _,
945 struct_buf: _
946 }
947 ));
948 }
949
950 #[test]
951 fn identity_map_builder_sets_confidential_bit() {
952 const C_BIT: u32 = 51;
953 let mut page_table_work_buffer: Vec<PageTable> =
954 vec![PageTable::new_zeroed(); PAGE_TABLE_MAX_COUNT];
955 let mut page_table = vec![0; PAGE_TABLE_MAX_BYTES];
956
957 let page_table = IdentityMapBuilder::new(
958 0x4000,
959 IdentityMapSize::Size4Gb,
960 &mut page_table_work_buffer,
961 &mut page_table,
962 )
963 .unwrap()
964 .with_confidential_bit(C_BIT)
965 .build();
966
967 for entry in page_table.chunks_exact(8).map(|entry| {
968 u64::from_ne_bytes(entry.try_into().expect("page table entry is eight bytes"))
969 }) {
970 if entry & X64_PTE_PRESENT != 0 {
971 assert_ne!(entry & (1 << C_BIT), 0);
972 } else {
973 assert_eq!(entry, 0);
974 }
975 }
976 }
977}