44//! Methods to construct page tables on x64.
55
66use crate :: IdentityMapSize ;
7- use crate :: PageTableBuffer ;
87use zerocopy:: FromBytes ;
9- use zerocopy:: FromZeros ;
108use zerocopy:: Immutable ;
119use zerocopy:: IntoBytes ;
1210use zerocopy:: KnownLayout ;
@@ -18,6 +16,7 @@ const X64_PTE_DIRTY: u64 = 1 << 6;
1816const X64_PTE_LARGE_PAGE : u64 = 1 << 7 ;
1917
2018const PAGE_TABLE_ENTRY_COUNT : usize = 512 ;
19+ const PAGE_TABLE_ENTRY_SIZE : usize = 8 ;
2120
2221const X64_PAGE_SHIFT : u64 = 12 ;
2322const X64_PTE_BITS : u64 = 9 ;
@@ -31,6 +30,14 @@ pub const X64_LARGE_PAGE_SIZE: u64 = 0x200000;
3130/// Number of bytes in a 1GB page for X64.
3231pub const X64_1GB_PAGE_SIZE : u64 = 0x40000000 ;
3332
33+ /// Maximum number of page tables created for an x64 identity map
34+ pub const PAGE_TABLE_MAX_COUNT : usize = 13 ;
35+
36+ const PAGE_TABLE_SIZE : usize = PAGE_TABLE_ENTRY_COUNT * PAGE_TABLE_ENTRY_SIZE ;
37+
38+ /// Maximum number of bytes needed to store an x64 identity map
39+ pub const PAGE_TABLE_MAX_BYTES : usize = PAGE_TABLE_MAX_COUNT * X64_PAGE_SIZE as usize ;
40+
3441#[ derive( Copy , Clone , PartialEq , Eq , IntoBytes , Immutable , KnownLayout , FromBytes ) ]
3542#[ repr( transparent) ]
3643pub struct PageTableEntry {
@@ -341,11 +348,11 @@ impl PageTableBuilder {
341348 /// Build a set of X64 page tables identity mapping the given regions. `size` must be less than 512GB.
342349 /// This creates up to 3+N page tables: 1 PML4E and up to 2 PDPTE tables, and N page tables counted at 1 per GB of size,
343350 /// for 2MB mappings.
344- pub fn build < P , F > ( self , page_table : & mut P , flattened_page_table : & mut F )
345- where
346- P : PageTableBuffer < Element = PageTable > ,
347- F : PageTableBuffer < Element = u8 > ,
348- {
351+ pub fn build < ' a > (
352+ self ,
353+ page_table : & mut [ PageTable ] ,
354+ flattened_page_table : & ' a mut [ u8 ] ,
355+ ) -> & ' a [ u8 ] {
349356 const SIZE_512_GB : u64 = 0x8000000000 ;
350357
351358 if self . size == 0 {
@@ -389,25 +396,24 @@ impl PageTableBuilder {
389396 }
390397
391398 // Allocate single PML4E page table.
392- page_table. push ( PageTable :: new_zeroed ( ) ) ;
393- let pml4_table_index = 0 ;
399+ let ( mut page_table_index, pml4_table_index) = ( 0 , 0 ) ;
394400 let confidential = self . confidential_bit . is_some ( ) ;
395401
396402 let mut link_tables = |start_va : u64 , end_va : u64 , use_large_pages : bool | {
397403 let mut current_va = start_va;
398404 while current_va < end_va {
399405 let pdpte_table_index = {
400- let next_index = page_table. len ( ) ;
401406 let pml4_entry = page_table[ pml4_table_index] . entry ( current_va, 3 ) ;
402407 if !pml4_entry. is_present ( ) {
408+ page_table_index += 1 ;
403409 // Allocate and link PDPTE table.
404- let output_address = page_table_gpa + next_index as u64 * X64_PAGE_SIZE ;
410+ let output_address =
411+ page_table_gpa + page_table_index as u64 * X64_PAGE_SIZE ;
405412 let mut new_entry =
406413 Self :: build_pte ( PageTableEntryType :: Pde ( output_address) ) ;
407414 self . set_pte_confidentiality ( & mut new_entry, confidential) ;
408415 * pml4_entry = new_entry;
409- page_table. push ( PageTable :: new_zeroed ( ) ) ;
410- next_index
416+ page_table_index
411417 } else {
412418 ( ( self . get_addr_from_pte ( pml4_entry) - page_table_gpa) / X64_PAGE_SIZE )
413419 . try_into ( )
@@ -416,26 +422,25 @@ impl PageTableBuilder {
416422 } ;
417423
418424 let pde_table_index = {
419- let next_index = page_table. len ( ) ;
420425 let pdpte_entry = page_table[ pdpte_table_index] . entry ( current_va, 2 ) ;
421426 if !pdpte_entry. is_present ( ) {
427+ page_table_index += 1 ;
422428 // Allocate and link PDE table.
423- let output_address = page_table_gpa + next_index as u64 * X64_PAGE_SIZE ;
429+ let output_address =
430+ page_table_gpa + page_table_index as u64 * X64_PAGE_SIZE ;
424431 let mut new_entry =
425432 Self :: build_pte ( PageTableEntryType :: Pde ( output_address) ) ;
426433 self . set_pte_confidentiality ( & mut new_entry, confidential) ;
427434 * pdpte_entry = new_entry;
428- page_table. push ( PageTable :: new_zeroed ( ) ) ;
429435
430- next_index
436+ page_table_index
431437 } else {
432438 ( ( self . get_addr_from_pte ( pdpte_entry) - page_table_gpa) / X64_PAGE_SIZE )
433439 . try_into ( )
434440 . expect ( "Valid page table index" )
435441 }
436442 } ;
437443
438- let next_index = page_table. len ( ) ;
439444 let pde_entry = page_table[ pde_table_index] . entry ( current_va, 1 ) ;
440445 assert ! ( !pde_entry. is_present( ) ) ;
441446
@@ -449,15 +454,16 @@ impl PageTableBuilder {
449454 current_va += X64_LARGE_PAGE_SIZE ;
450455 } else {
451456 let pt_table_index = if !pde_entry. is_present ( ) {
457+ page_table_index += 1 ;
452458 // Allocate and link page table.
453- let output_address = page_table_gpa + next_index as u64 * X64_PAGE_SIZE ;
459+ let output_address =
460+ page_table_gpa + page_table_index as u64 * X64_PAGE_SIZE ;
454461 let mut new_entry =
455462 Self :: build_pte ( PageTableEntryType :: Pde ( output_address) ) ;
456463 self . set_pte_confidentiality ( & mut new_entry, confidential) ;
457464 * pde_entry = new_entry;
458- page_table. push ( PageTable :: new_zeroed ( ) ) ;
459465
460- next_index
466+ page_table_index
461467 } else {
462468 ( ( self . get_addr_from_pte ( pde_entry) - page_table_gpa) / X64_PAGE_SIZE )
463469 . try_into ( )
@@ -487,7 +493,7 @@ impl PageTableBuilder {
487493 }
488494
489495 // Flatten page table vec into u8 vec
490- flatten_page_table ( page_table, flattened_page_table) ;
496+ flatten_page_table ( page_table, flattened_page_table, page_table_index + 1 )
491497 }
492498}
493499
@@ -497,18 +503,15 @@ impl PageTableBuilder {
497503/// An optional PML4E entry may be linked, with arguments being (link_target_gpa, linkage_gpa).
498504/// link_target_gpa represents the GPA of the PML4E to link into the built page table.
499505/// linkage_gpa represents the GPA at which the linked PML4E should be linked.
500- pub fn build_page_tables_64 < P , F > (
506+ pub fn build_page_tables_64 < ' a > (
501507 page_table_gpa : u64 ,
502508 address_bias : u64 ,
503509 identity_map_size : IdentityMapSize ,
504510 pml4e_link : Option < ( u64 , u64 ) > ,
505511 read_only : bool ,
506- page_table : & mut P ,
507- flattened_page_table : & mut F ,
508- ) where
509- P : PageTableBuffer < Element = PageTable > ,
510- F : PageTableBuffer < Element = u8 > ,
511- {
512+ page_table : & mut [ PageTable ] ,
513+ flattened_page_table : & ' a mut [ u8 ] ,
514+ ) -> & ' a [ u8 ] {
512515 // Allocate page tables. There are up to 6 total page tables:
513516 // 1 PML4E (Level 4) (omitted if the address bias is non-zero)
514517 // 1 PDPTE (Level 3)
@@ -519,9 +522,6 @@ pub fn build_page_tables_64<P, F>(
519522 IdentityMapSize :: Size8Gb => 8 ,
520523 } ;
521524 let page_table_count = leaf_page_table_count + if address_bias == 0 { 2 } else { 1 } ;
522- for _ in 0 ..page_table_count {
523- page_table. push ( PageTable :: new_zeroed ( ) ) ;
524- }
525525 let mut page_table_allocator = page_table. iter_mut ( ) . enumerate ( ) ;
526526
527527 // Allocate single PDPTE table.
@@ -586,11 +586,8 @@ pub fn build_page_tables_64<P, F>(
586586 }
587587 }
588588
589- // All pagetables should be used, code bug if not.
590- assert ! ( page_table_allocator. next( ) . is_none( ) ) ;
591-
592589 // Flatten page table vec into u8 vec
593- flatten_page_table ( page_table, flattened_page_table)
590+ flatten_page_table ( page_table, flattened_page_table, page_table_count )
594591}
595592
596593/// Align an address up to the start of the next page.
@@ -607,14 +604,22 @@ pub fn align_up_to_large_page_size(address: u64) -> u64 {
607604pub fn align_up_to_1_gb_page_size ( address : u64 ) -> u64 {
608605 ( address + X64_1GB_PAGE_SIZE - 1 ) & !( X64_1GB_PAGE_SIZE - 1 )
609606}
610- fn flatten_page_table < P , F > ( page_table : & mut P , flattened_page_table : & mut F )
611- where
612- P : PageTableBuffer < Element = PageTable > ,
613- F : PageTableBuffer < Element = u8 > ,
614- {
615- for table_index in 0 ..page_table. len ( ) {
616- flattened_page_table. extend ( page_table[ table_index] . as_bytes ( ) )
607+
608+ fn flatten_page_table < ' a > (
609+ page_table : & mut [ PageTable ] ,
610+ flattened_page_table : & ' a mut [ u8 ] ,
611+ page_table_count : usize ,
612+ ) -> & ' a [ u8 ] {
613+ for ( page_table, dst) in page_table
614+ . iter ( )
615+ . take ( page_table_count)
616+ . zip ( flattened_page_table. chunks_mut ( PAGE_TABLE_SIZE ) )
617+ {
618+ let src = page_table. as_bytes ( ) ;
619+ dst. copy_from_slice ( src) ;
617620 }
621+
622+ & flattened_page_table[ 0 ..PAGE_TABLE_SIZE * page_table_count]
618623}
619624
620625#[ cfg( test) ]
0 commit comments