-
Notifications
You must be signed in to change notification settings - Fork 167
feat: i686 page tables, snapshot compaction, and CoW (standalone) #1385
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d29646f
30a5f41
7530746
16a47ac
bc9c0d6
8fcb7ea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -596,6 +596,72 @@ pub type PageTableEntry = u64; | |
| pub type VirtAddr = u64; | ||
| pub type PhysAddr = u64; | ||
|
|
||
| /// i686 guest page-table walker and PTE constants for the x86_64 host. | ||
| /// | ||
| /// When the host builds with `i686-guest`, it needs to walk 2-level i686 | ||
| /// page tables in guest memory. The `arch/i686/vmem.rs` module only compiles | ||
| /// for `target_arch = "x86"` (the guest side), so the host-side walker lives | ||
| /// here, gated behind the feature flag. | ||
| #[cfg(feature = "i686-guest")] | ||
| pub mod i686_guest { | ||
| use alloc::vec::Vec; | ||
|
|
||
| use crate::vmem::{BasicMapping, CowMapping, Mapping, MappingKind, TableReadOps}; | ||
|
|
||
| pub const PAGE_PRESENT: u64 = 1; | ||
| pub const PAGE_RW: u64 = 1 << 1; | ||
| pub const PAGE_USER: u64 = 1 << 2; | ||
| pub const PAGE_ACCESSED: u64 = 1 << 5; | ||
| pub const PAGE_AVL_COW: u64 = 1 << 9; | ||
| pub const PTE_ADDR_MASK: u64 = 0xFFFFF000; | ||
|
|
||
| /// Walk an i686 2-level page table and return all present mappings. | ||
| /// | ||
| /// # Safety | ||
| /// The caller must ensure that `op` provides valid page table memory. | ||
| pub unsafe fn virt_to_phys_all<Op: TableReadOps>(op: &Op) -> Vec<Mapping> { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason this doesn't match the API of the amd64 stuff so that it can be re-exported from the architecture-independent module, like we do on other platforms? |
||
| let root = op.root_table(); | ||
| let mut mappings = Vec::new(); | ||
| for pdi in 0..1024u64 { | ||
| let pde_ptr = Op::entry_addr(root, pdi * 4); | ||
| let pde: u64 = unsafe { op.read_entry(pde_ptr) }; | ||
| if (pde & PAGE_PRESENT) == 0 { | ||
| continue; | ||
| } | ||
| let pt_phys = pde & PTE_ADDR_MASK; | ||
| let pt_base = Op::from_phys(pt_phys as crate::vmem::PhysAddr); | ||
| for pti in 0..1024u64 { | ||
| let pte_ptr = Op::entry_addr(pt_base, pti * 4); | ||
| let pte: u64 = unsafe { op.read_entry(pte_ptr) }; | ||
| if (pte & PAGE_PRESENT) == 0 { | ||
| continue; | ||
| } | ||
| let phys_base = pte & PTE_ADDR_MASK; | ||
| let virt_base = (pdi << 22) | (pti << 12); | ||
| let kind = if (pte & PAGE_AVL_COW) != 0 { | ||
| MappingKind::Cow(CowMapping { | ||
| readable: true, | ||
| executable: true, | ||
| }) | ||
| } else { | ||
| MappingKind::Basic(BasicMapping { | ||
| readable: true, | ||
| writable: (pte & PAGE_RW) != 0, | ||
| executable: true, | ||
| }) | ||
| }; | ||
| mappings.push(Mapping { | ||
| phys_base, | ||
| virt_base, | ||
| len: super::PAGE_SIZE as u64, | ||
| kind, | ||
| }); | ||
| } | ||
| } | ||
| mappings | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use alloc::vec; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,19 +16,19 @@ limitations under the License. | |
|
|
||
| #[cfg_attr(target_arch = "x86", path = "arch/i686/layout.rs")] | ||
| #[cfg_attr( | ||
| all(target_arch = "x86_64", not(feature = "nanvix-unstable")), | ||
| all(target_arch = "x86_64", not(feature = "i686-guest")), | ||
| path = "arch/amd64/layout.rs" | ||
| )] | ||
| #[cfg_attr( | ||
| all(target_arch = "x86_64", feature = "nanvix-unstable"), | ||
| all(target_arch = "x86_64", feature = "i686-guest"), | ||
| path = "arch/i686/layout.rs" | ||
| )] | ||
| #[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/layout.rs")] | ||
| mod arch; | ||
|
|
||
| pub use arch::{MAX_GPA, MAX_GVA}; | ||
| #[cfg(any( | ||
| all(target_arch = "x86_64", not(feature = "nanvix-unstable")), | ||
| all(target_arch = "x86_64", not(feature = "i686-guest")), | ||
| target_arch = "aarch64" | ||
| ))] | ||
| pub use arch::{SNAPSHOT_PT_GVA_MAX, SNAPSHOT_PT_GVA_MIN}; | ||
|
|
@@ -39,13 +39,25 @@ pub const SCRATCH_TOP_ALLOCATOR_OFFSET: u64 = 0x10; | |
| pub const SCRATCH_TOP_SNAPSHOT_PT_GPA_BASE_OFFSET: u64 = 0x18; | ||
| pub const SCRATCH_TOP_EXN_STACK_OFFSET: u64 = 0x20; | ||
|
|
||
| /// Offset from the top of scratch for the number of active page directory roots. | ||
| /// The guest writes this before signaling boot-complete so the host can walk | ||
| /// all active PDs during snapshot creation (not just CR3). | ||
| #[cfg(feature = "i686-guest")] | ||
| pub const SCRATCH_TOP_PD_ROOTS_COUNT_OFFSET: u64 = 0x28; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Critical: PD roots bookkeeping may overlap exception stack (Flagged by: 2/3)
If any i686 guest ever uses exceptions that push state to this stack, the first push would land at offset Suggested mitigations:
|
||
| /// Offset from the top of scratch for the PD roots array (u32 GPAs on i686). | ||
| #[cfg(feature = "i686-guest")] | ||
| pub const SCRATCH_TOP_PD_ROOTS_ARRAY_OFFSET: u64 = 0x30; | ||
| /// Maximum number of PD roots the guest can expose to the host. | ||
| #[cfg(feature = "i686-guest")] | ||
| pub const MAX_PD_ROOTS: usize = 32; | ||
|
|
||
| /// Offset from the top of scratch memory for a shared host-guest u64 counter. | ||
| /// | ||
| /// This is placed at 0x1008 (rather than the next sequential 0x28) so that the | ||
| /// counter falls in scratch page 0xffffe000 instead of the very last page | ||
| /// 0xfffff000, which on i686 guests would require frame 0xfffff — exceeding the | ||
| /// maximum representable frame number. | ||
| #[cfg(feature = "nanvix-unstable")] | ||
| #[cfg(feature = "guest-counter")] | ||
| pub const SCRATCH_TOP_GUEST_COUNTER_OFFSET: u64 = 0x1008; | ||
|
|
||
| pub fn scratch_base_gpa(size: usize) -> u64 { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would make more sense to have the i686/vmem.rs module used when feature = i686-guest---we should understand that the arch in hyperlight_common is always about the guest arch.