@@ -25,7 +25,9 @@ limitations under the License.
2525//! The code uses an iterator-based approach to walk the page table hierarchy,
2626//! allocating intermediate tables as needed and setting appropriate flags on leaf PTEs
2727
28- use crate :: vmem:: { BasicMapping , Mapping , MappingKind , TableOps } ;
28+ use crate :: vmem:: {
29+ BasicMapping , Mapping , MappingKind , TableMovabilityBase , TableOps , TableReadOps , Void ,
30+ } ;
2931
3032// Paging Flags
3133//
@@ -78,7 +80,7 @@ const fn page_nx_flag(executable: bool) -> u64 {
7880/// # Safety
7981/// The caller must ensure that `entry_ptr` points to a valid page table entry.
8082#[ inline( always) ]
81- unsafe fn read_pte_if_present < Op : TableOps > ( op : & Op , entry_ptr : Op :: TableAddr ) -> Option < u64 > {
83+ unsafe fn read_pte_if_present < Op : TableReadOps > ( op : & Op , entry_ptr : Op :: TableAddr ) -> Option < u64 > {
8284 let pte = unsafe { op. read_entry ( entry_ptr) } ;
8385 if ( pte & PAGE_PRESENT ) != 0 {
8486 Some ( pte)
@@ -107,9 +109,37 @@ fn pte_for_table<Op: TableOps>(table_addr: Op::TableAddr) -> u64 {
107109 PAGE_PRESENT // P - this entry is present
108110}
109111
112+ /// This trait is used to select appropriate implementations of
113+ /// [`UpdateParent`] to be used, depending on whether a particular
114+ /// implementation needs the ability to move tables.
115+ pub trait TableMovability < Op : TableReadOps + ?Sized , TableMoveInfo > {
116+ type RootUpdateParent : UpdateParent < Op , TableMoveInfo = TableMoveInfo > ;
117+ fn root_update_parent ( ) -> Self :: RootUpdateParent ;
118+ }
119+ impl < Op : TableOps < TableMovability = crate :: vmem:: MayMoveTable > > TableMovability < Op , Op :: TableAddr >
120+ for crate :: vmem:: MayMoveTable
121+ {
122+ type RootUpdateParent = UpdateParentRoot ;
123+ fn root_update_parent ( ) -> Self :: RootUpdateParent {
124+ UpdateParentRoot { }
125+ }
126+ }
127+ impl < Op : TableReadOps > TableMovability < Op , Void > for crate :: vmem:: MayNotMoveTable {
128+ type RootUpdateParent = UpdateParentNone ;
129+ fn root_update_parent ( ) -> Self :: RootUpdateParent {
130+ UpdateParentNone { }
131+ }
132+ }
133+
110134/// Helper function to write a page table entry, updating the whole
111135/// chain of tables back to the root if necessary
112- unsafe fn write_entry_updating < Op : TableOps , P : UpdateParent < Op > > (
136+ unsafe fn write_entry_updating <
137+ Op : TableOps ,
138+ P : UpdateParent <
139+ Op ,
140+ TableMoveInfo = <Op :: TableMovability as TableMovabilityBase < Op > >:: TableMoveInfo ,
141+ > ,
142+ > (
113143 op : & Op ,
114144 parent : P ,
115145 addr : Op :: TableAddr ,
@@ -128,14 +158,26 @@ unsafe fn write_entry_updating<Op: TableOps, P: UpdateParent<Op>>(
128158/// This is done via a trait so that the selected impl knows the exact
129159/// nesting depth of tables, in order to assist
130160/// inlining/specialisation in generating efficient code.
131- trait UpdateParent < Op : TableOps > : Copy {
132- fn update_parent ( self , op : & Op , new_ptr : Op :: TableAddr ) ;
161+ ///
162+ /// The trait definition only bounds its parameter by
163+ /// [`TableReadOps`], since [`UpdateParentNone`] does not need to be
164+ /// able to actually write to the tables.
165+ pub trait UpdateParent < Op : TableReadOps + ?Sized > : Copy {
166+ /// The type of the information about a moved table which is
167+ /// needed in order to update its parent.
168+ type TableMoveInfo ;
169+ /// The [`UpdateParent`] type that should be used when going down
170+ /// another level in the table, in order to add the current level
171+ /// to the chain of ancestors to be updated.
172+ type ChildType : UpdateParent < Op , TableMoveInfo = Self :: TableMoveInfo > ;
173+ fn update_parent ( self , op : & Op , new_ptr : Self :: TableMoveInfo ) ;
174+ fn for_child_at_entry ( self , entry_ptr : Op :: TableAddr ) -> Self :: ChildType ;
133175}
134176
135177/// A struct implementing [`UpdateParent`] that keeps track of the
136178/// fact that the parent table is itself another table, whose own
137179/// ancestors may need to be recursively updated
138- struct UpdateParentTable < Op : TableOps , P : UpdateParent < Op > > {
180+ pub struct UpdateParentTable < Op : TableOps , P : UpdateParent < Op > > {
139181 parent : P ,
140182 entry_ptr : Op :: TableAddr ,
141183}
@@ -150,45 +192,64 @@ impl<Op: TableOps, P: UpdateParent<Op>> UpdateParentTable<Op, P> {
150192 UpdateParentTable { parent, entry_ptr }
151193 }
152194}
153- impl < Op : TableOps , P : UpdateParent < Op > > UpdateParent < Op > for UpdateParentTable < Op , P > {
195+ impl <
196+ Op : TableOps < TableMovability = crate :: vmem:: MayMoveTable > ,
197+ P : UpdateParent < Op , TableMoveInfo = Op :: TableAddr > ,
198+ > UpdateParent < Op > for UpdateParentTable < Op , P >
199+ {
200+ type TableMoveInfo = Op :: TableAddr ;
201+ type ChildType = UpdateParentTable < Op , Self > ;
154202 fn update_parent ( self , op : & Op , new_ptr : Op :: TableAddr ) {
155203 let pte = pte_for_table :: < Op > ( new_ptr) ;
156204 unsafe {
157205 write_entry_updating ( op, self . parent , self . entry_ptr , pte) ;
158206 }
159207 }
208+ fn for_child_at_entry ( self , entry_ptr : Op :: TableAddr ) -> Self :: ChildType {
209+ Self :: ChildType :: new ( self , entry_ptr)
210+ }
160211}
161212
162213/// A struct implementing [`UpdateParent`] that keeps track of the
163- /// fact that the parent "table" is actually the CR3 system register.
214+ /// fact that the parent "table" is actually the root (e.g. the value
215+ /// of CR3 in the guest)
164216#[ derive( Copy , Clone ) ]
165- struct UpdateParentCR3 { }
166- impl < Op : TableOps > UpdateParent < Op > for UpdateParentCR3 {
167- fn update_parent ( self , _op : & Op , new_ptr : Op :: TableAddr ) {
217+ pub struct UpdateParentRoot { }
218+ impl < Op : TableOps < TableMovability = crate :: vmem:: MayMoveTable > > UpdateParent < Op >
219+ for UpdateParentRoot
220+ {
221+ type TableMoveInfo = Op :: TableAddr ;
222+ type ChildType = UpdateParentTable < Op , Self > ;
223+ fn update_parent ( self , op : & Op , new_ptr : Op :: TableAddr ) {
168224 unsafe {
169- core :: arch :: asm! ( "mov cr3, {}" , in ( reg ) Op :: to_phys ( new_ptr) ) ;
225+ op . update_root ( new_ptr) ;
170226 }
171227 }
228+ fn for_child_at_entry ( self , entry_ptr : Op :: TableAddr ) -> Self :: ChildType {
229+ Self :: ChildType :: new ( self , entry_ptr)
230+ }
172231}
173232
174- /// A struct implementing [`UpdateParent`] that panics when used, for
175- /// the occasional situations in which we should never do an update
233+ /// A struct implementing [`UpdateParent`] that is impossible to use
234+ /// (since its [`update_parent`] method takes `Void`), used when it is
235+ /// statically known that a table operation cannot result in a need to
236+ /// update ancestors.
176237#[ derive( Copy , Clone ) ]
177- struct UpdateParentNone { }
178- impl < Op : TableOps > UpdateParent < Op > for UpdateParentNone {
179- // This is only used in contexts which should absolutely never try
180- // to update a page table, and so in no case should possibly ever
181- // call an update_parent. Therefore, this is impossible unless an
182- // extremely significant invariant violation has occurred.
183- # [ allow ( clippy :: panic ) ]
184- fn update_parent ( self , _op : & Op , _new_ptr : Op :: TableAddr ) {
185- panic ! ( "UpdateParentNone: tried to update parent" ) ;
238+ pub struct UpdateParentNone { }
239+ impl < Op : TableReadOps > UpdateParent < Op > for UpdateParentNone {
240+ type TableMoveInfo = Void ;
241+ type ChildType = Self ;
242+ fn update_parent ( self , _op : & Op , impossible : Void ) {
243+ match impossible { }
244+ }
245+ fn for_child_at_entry ( self , _entry_ptr : Op :: TableAddr ) -> Self {
246+ self
186247 }
187248}
188249
189250/// A helper structure indicating a mapping operation that needs to be
190251/// performed
191- struct MapRequest < Op : TableOps , P : UpdateParent < Op > > {
252+ struct MapRequest < Op : TableReadOps , P : UpdateParent < Op > > {
192253 table_base : Op :: TableAddr ,
193254 vmin : VirtAddr ,
194255 len : u64 ,
@@ -197,7 +258,7 @@ struct MapRequest<Op: TableOps, P: UpdateParent<Op>> {
197258
198259/// A helper structure indicating that a particular PTE needs to be
199260/// modified
200- struct MapResponse < Op : TableOps , P : UpdateParent < Op > > {
261+ struct MapResponse < Op : TableReadOps , P : UpdateParent < Op > > {
201262 entry_ptr : Op :: TableAddr ,
202263 vmin : VirtAddr ,
203264 len : u64 ,
@@ -216,11 +277,16 @@ struct MapResponse<Op: TableOps, P: UpdateParent<Op>> {
216277/// - PDPT: HIGH_BIT=38, LOW_BIT=30 (9 bits = 512 entries, each covering 1GB)
217278/// - PD: HIGH_BIT=29, LOW_BIT=21 (9 bits = 512 entries, each covering 2MB)
218279/// - PT: HIGH_BIT=20, LOW_BIT=12 (9 bits = 512 entries, each covering 4KB)
219- struct ModifyPteIterator < const HIGH_BIT : u8 , const LOW_BIT : u8 , Op : TableOps , P : UpdateParent < Op > > {
280+ struct ModifyPteIterator <
281+ const HIGH_BIT : u8 ,
282+ const LOW_BIT : u8 ,
283+ Op : TableReadOps ,
284+ P : UpdateParent < Op > ,
285+ > {
220286 request : MapRequest < Op , P > ,
221287 n : u64 ,
222288}
223- impl < const HIGH_BIT : u8 , const LOW_BIT : u8 , Op : TableOps , P : UpdateParent < Op > > Iterator
289+ impl < const HIGH_BIT : u8 , const LOW_BIT : u8 , Op : TableReadOps , P : UpdateParent < Op > > Iterator
224290 for ModifyPteIterator < HIGH_BIT , LOW_BIT , Op , P >
225291{
226292 type Item = MapResponse < Op , P > ;
@@ -286,7 +352,7 @@ impl<const HIGH_BIT: u8, const LOW_BIT: u8, Op: TableOps, P: UpdateParent<Op>> I
286352 } )
287353 }
288354}
289- fn modify_ptes < const HIGH_BIT : u8 , const LOW_BIT : u8 , Op : TableOps , P : UpdateParent < Op > > (
355+ fn modify_ptes < const HIGH_BIT : u8 , const LOW_BIT : u8 , Op : TableReadOps , P : UpdateParent < Op > > (
290356 r : MapRequest < Op , P > ,
291357) -> ModifyPteIterator < HIGH_BIT , LOW_BIT , Op , P > {
292358 ModifyPteIterator { request : r, n : 0 }
@@ -296,11 +362,20 @@ fn modify_ptes<const HIGH_BIT: u8, const LOW_BIT: u8, Op: TableOps, P: UpdatePar
296362/// # Safety
297363/// This function modifies page table data structures, and should not be called concurrently
298364/// with any other operations that modify the page tables.
299- unsafe fn alloc_pte_if_needed < Op : TableOps , P : UpdateParent < Op > > (
365+ unsafe fn alloc_pte_if_needed <
366+ Op : TableOps ,
367+ P : UpdateParent <
368+ Op ,
369+ TableMoveInfo = <Op :: TableMovability as TableMovabilityBase < Op > >:: TableMoveInfo ,
370+ > ,
371+ > (
300372 op : & Op ,
301373 x : MapResponse < Op , P > ,
302- ) -> MapRequest < Op , UpdateParentTable < Op , P > > {
303- let new_update_parent = UpdateParentTable :: new ( x. update_parent , x. entry_ptr ) ;
374+ ) -> MapRequest < Op , P :: ChildType >
375+ where
376+ P :: ChildType : UpdateParent < Op > ,
377+ {
378+ let new_update_parent = x. update_parent . for_child_at_entry ( x. entry_ptr ) ;
304379 if let Some ( pte) = unsafe { read_pte_if_present ( op, x. entry_ptr ) } {
305380 return MapRequest {
306381 table_base : Op :: from_phys ( pte & PTE_ADDR_MASK ) ,
@@ -330,7 +405,13 @@ unsafe fn alloc_pte_if_needed<Op: TableOps, P: UpdateParent<Op>>(
330405/// with any other operations that modify the page tables.
331406#[ allow( clippy:: identity_op) ]
332407#[ allow( clippy:: precedence) ]
333- unsafe fn map_page < Op : TableOps , P : UpdateParent < Op > > (
408+ unsafe fn map_page <
409+ Op : TableOps ,
410+ P : UpdateParent <
411+ Op ,
412+ TableMoveInfo = <Op :: TableMovability as TableMovabilityBase < Op > >:: TableMoveInfo ,
413+ > ,
414+ > (
334415 op : & Op ,
335416 mapping : & Mapping ,
336417 r : MapResponse < Op , P > ,
@@ -383,7 +464,7 @@ pub unsafe fn map<Op: TableOps>(op: &Op, mapping: Mapping) {
383464 table_base : op. root_table ( ) ,
384465 vmin : mapping. virt_base ,
385466 len : mapping. len ,
386- update_parent : UpdateParentCR3 { } ,
467+ update_parent : Op :: TableMovability :: root_update_parent ( ) ,
387468 } )
388469 . map ( |r| unsafe { alloc_pte_if_needed ( op, r) } )
389470 . flat_map ( modify_ptes :: < 38 , 30 , Op , _ > )
@@ -399,15 +480,18 @@ pub unsafe fn map<Op: TableOps>(op: &Op, mapping: Mapping) {
399480/// This function traverses page table data structures, and should not
400481/// be called concurrently with any other operations that modify the
401482/// page table.
402- unsafe fn require_pte_exist < Op : TableOps , P : UpdateParent < Op > > (
483+ unsafe fn require_pte_exist < Op : TableReadOps , P : UpdateParent < Op > > (
403484 op : & Op ,
404485 x : MapResponse < Op , P > ,
405- ) -> Option < MapRequest < Op , UpdateParentTable < Op , P > > > {
486+ ) -> Option < MapRequest < Op , P :: ChildType > >
487+ where
488+ P :: ChildType : UpdateParent < Op > ,
489+ {
406490 unsafe { read_pte_if_present ( op, x. entry_ptr ) } . map ( |pte| MapRequest {
407491 table_base : Op :: from_phys ( pte & PTE_ADDR_MASK ) ,
408492 vmin : x. vmin ,
409493 len : x. len ,
410- update_parent : UpdateParentTable :: new ( x. update_parent , x. entry_ptr ) ,
494+ update_parent : x. update_parent . for_child_at_entry ( x. entry_ptr ) ,
411495 } )
412496}
413497
@@ -429,7 +513,7 @@ unsafe fn require_pte_exist<Op: TableOps, P: UpdateParent<Op>>(
429513/// operations structure owns a large buffer, a reference can instead
430514/// be passed.
431515#[ allow( clippy:: missing_safety_doc) ]
432- pub unsafe fn virt_to_phys < ' a , Op : TableOps + ' a > (
516+ pub unsafe fn virt_to_phys < ' a , Op : TableReadOps + ' a > (
433517 op : impl core:: convert:: AsRef < Op > + Copy + ' a ,
434518 address : u64 ,
435519 len : u64 ,
@@ -480,7 +564,10 @@ mod tests {
480564 use core:: cell:: RefCell ;
481565
482566 use super :: * ;
483- use crate :: vmem:: { BasicMapping , Mapping , MappingKind , PAGE_TABLE_ENTRIES_PER_TABLE , TableOps } ;
567+ use crate :: vmem:: {
568+ BasicMapping , Mapping , MappingKind , MayNotMoveTable , PAGE_TABLE_ENTRIES_PER_TABLE ,
569+ TableOps , TableReadOps , Void ,
570+ } ;
484571
485572 /// A mock TableOps implementation for testing that stores page tables in memory
486573 /// needed because the `GuestPageTableBuffer` is in hyperlight_host which would cause a circular dependency
@@ -512,16 +599,9 @@ mod tests {
512599 }
513600 }
514601
515- impl TableOps for MockTableOps {
602+ impl TableReadOps for MockTableOps {
516603 type TableAddr = ( usize , usize ) ; // (table_index, entry_index)
517604
518- unsafe fn alloc_table ( & self ) -> Self :: TableAddr {
519- let mut tables = self . tables . borrow_mut ( ) ;
520- let idx = tables. len ( ) ;
521- tables. push ( [ 0u64 ; PAGE_TABLE_ENTRIES_PER_TABLE ] ) ;
522- ( idx, 0 )
523- }
524-
525605 fn entry_addr ( addr : Self :: TableAddr , entry_offset : u64 ) -> Self :: TableAddr {
526606 // Convert to physical address, add offset, convert back
527607 let phys = Self :: to_phys ( addr) + entry_offset;
@@ -532,11 +612,6 @@ mod tests {
532612 self . tables . borrow ( ) [ addr. 0 ] [ addr. 1 ]
533613 }
534614
535- unsafe fn write_entry ( & self , addr : Self :: TableAddr , entry : u64 ) -> Option < Self :: TableAddr > {
536- self . tables . borrow_mut ( ) [ addr. 0 ] [ addr. 1 ] = entry;
537- None
538- }
539-
540615 fn to_phys ( addr : Self :: TableAddr ) -> PhysAddr {
541616 // Each table is 4KB, entries are 8 bytes
542617 ( addr. 0 as u64 * PAGE_TABLE_SIZE as u64 ) + ( addr. 1 as u64 * 8 )
@@ -553,6 +628,26 @@ mod tests {
553628 }
554629 }
555630
631+ impl TableOps for MockTableOps {
632+ type TableMovability = MayNotMoveTable ;
633+
634+ unsafe fn alloc_table ( & self ) -> Self :: TableAddr {
635+ let mut tables = self . tables . borrow_mut ( ) ;
636+ let idx = tables. len ( ) ;
637+ tables. push ( [ 0u64 ; PAGE_TABLE_ENTRIES_PER_TABLE ] ) ;
638+ ( idx, 0 )
639+ }
640+
641+ unsafe fn write_entry ( & self , addr : Self :: TableAddr , entry : u64 ) -> Option < Void > {
642+ self . tables . borrow_mut ( ) [ addr. 0 ] [ addr. 1 ] = entry;
643+ None
644+ }
645+
646+ unsafe fn update_root ( & self , impossible : Void ) {
647+ match impossible { }
648+ }
649+ }
650+
556651 // ==================== bits() function tests ====================
557652
558653 #[ test]
0 commit comments