Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ test-rust-tracing target=default-target features="":
check-i686 target=default-target:
cargo check -p hyperlight-common --target i686-unknown-linux-gnu --profile={{ if target == "debug" { "dev" } else { target } }}
cargo check -p hyperlight-guest --target i686-unknown-linux-gnu --profile={{ if target == "debug" { "dev" } else { target } }}
cargo check -p hyperlight-common --target i686-unknown-linux-gnu --features nanvix-unstable --profile={{ if target == "debug" { "dev" } else { target } }}
cargo check -p hyperlight-common --target i686-unknown-linux-gnu --features i686-guest --profile={{ if target == "debug" { "dev" } else { target } }}
# Verify that trace_guest correctly fails on i686 (compile_error should trigger)
! cargo check -p hyperlight-guest --target i686-unknown-linux-gnu --features trace_guest --profile={{ if target == "debug" { "dev" } else { target } }} 2>/dev/null

Expand All @@ -291,8 +291,8 @@ check:
{{ cargo-cmd }} check -p hyperlight-host --features print_debug {{ target-triple-flag }}
{{ cargo-cmd }} check -p hyperlight-host --features gdb {{ target-triple-flag }}
{{ cargo-cmd }} check -p hyperlight-host --features trace_guest,mem_profile {{ target-triple-flag }}
{{ cargo-cmd }} check -p hyperlight-host --features nanvix-unstable {{ target-triple-flag }}
{{ cargo-cmd }} check -p hyperlight-host --features nanvix-unstable,executable_heap {{ target-triple-flag }}
{{ cargo-cmd }} check -p hyperlight-host --features i686-guest {{ target-triple-flag }}
{{ cargo-cmd }} check -p hyperlight-host --features i686-guest,executable_heap {{ target-triple-flag }}
{{ cargo-cmd }} check -p hyperlight-host --features hw-interrupts {{ target-triple-flag }}

fmt-check: (ensure-nightly-fmt)
Expand Down
3 changes: 2 additions & 1 deletion src/hyperlight_common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ fuzzing = ["dep:arbitrary"]
trace_guest = []
mem_profile = []
std = ["thiserror/std", "log/std", "tracing/std"]
nanvix-unstable = []
i686-guest = []
guest-counter = []

[lib]
bench = false # see https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options
Expand Down
66 changes: 66 additions & 0 deletions src/hyperlight_common/src/arch/amd64/vmem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
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;
Expand Down
11 changes: 7 additions & 4 deletions src/hyperlight_common/src/arch/i686/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

// This file is just dummy definitions at the moment, in order to
// allow compiling the guest for real mode boot scenarios.
// i686 layout constants for 32-bit protected mode with paging.

pub const MAX_GVA: usize = 0xffff_ffff;
pub const MAX_GPA: usize = 0xffff_ffff;

pub fn min_scratch_size(_input_data_size: usize, _output_data_size: usize) -> usize {
crate::vmem::PAGE_SIZE
/// Minimum scratch region size: IO buffers (page-aligned) plus 12 pages
/// for bookkeeping and the exception stack. Page table space is validated
/// separately by `set_pt_size()`.
pub fn min_scratch_size(input_data_size: usize, output_data_size: usize) -> usize {
(input_data_size + output_data_size).next_multiple_of(crate::vmem::PAGE_SIZE)
+ 12 * crate::vmem::PAGE_SIZE
}
20 changes: 16 additions & 4 deletions src/hyperlight_common/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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;
/// 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 {
Expand Down
7 changes: 0 additions & 7 deletions src/hyperlight_common/src/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,4 @@ pub struct HyperlightPEB {
pub output_stack: GuestMemoryRegion,
pub init_data: GuestMemoryRegion,
pub guest_heap: GuestMemoryRegion,
/// File mappings array descriptor.
/// **Note:** `size` holds the **entry count** (number of valid
/// [`FileMappingInfo`] entries), NOT a byte size. `ptr` holds the
/// guest address of the preallocated array (immediately after the
/// PEB struct).
#[cfg(feature = "nanvix-unstable")]
pub file_mappings: GuestMemoryRegion,
}
2 changes: 2 additions & 0 deletions src/hyperlight_common/src/vmem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ mod arch;
/// This is always the page size that the /guest/ is being compiled
/// for, which may or may not be the same as the host page size.
pub use arch::PAGE_SIZE;
#[cfg(all(feature = "i686-guest", target_arch = "x86_64"))]
pub use arch::i686_guest;
pub use arch::{PAGE_TABLE_SIZE, PageTableEntry, PhysAddr, VirtAddr};
Comment on lines +25 to 27
pub const PAGE_TABLE_ENTRIES_PER_TABLE: usize =
PAGE_TABLE_SIZE / core::mem::size_of::<PageTableEntry>();
Expand Down
3 changes: 2 additions & 1 deletion src/hyperlight_guest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ hyperlight-guest-tracing = { workspace = true, default-features = false, optiona
[features]
default = []
trace_guest = ["dep:hyperlight-guest-tracing", "hyperlight-guest-tracing?/trace"]
nanvix-unstable = ["hyperlight-common/nanvix-unstable"]
i686-guest = ["hyperlight-common/i686-guest"]
guest-counter = ["hyperlight-common/guest-counter"]
2 changes: 1 addition & 1 deletion src/hyperlight_guest/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub fn snapshot_pt_gpa_base_gva() -> *mut u64 {
pub use arch::{scratch_base_gpa, scratch_base_gva};

/// Returns a pointer to the guest counter u64 in scratch memory.
#[cfg(feature = "nanvix-unstable")]
#[cfg(feature = "guest-counter")]
pub fn guest_counter_gva() -> *const u64 {
use hyperlight_common::layout::{MAX_GVA, SCRATCH_TOP_GUEST_COUNTER_OFFSET};
(MAX_GVA as u64 - SCRATCH_TOP_GUEST_COUNTER_OFFSET + 1) as *const u64
Expand Down
3 changes: 2 additions & 1 deletion src/hyperlight_host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ hw-interrupts = []
gdb = ["dep:gdbstub", "dep:gdbstub_arch"]
fuzzing = ["hyperlight-common/fuzzing"]
build-metadata = ["dep:built"]
nanvix-unstable = ["hyperlight-common/nanvix-unstable"]
i686-guest = ["hyperlight-common/i686-guest"]
guest-counter = ["hyperlight-common/guest-counter"]

[[bench]]
name = "benchmarks"
Expand Down
6 changes: 2 additions & 4 deletions src/hyperlight_host/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,8 @@ fn main() -> Result<()> {
crashdump: { all(feature = "crashdump", target_arch = "x86_64") },
// print_debug feature is aliased with debug_assertions to make it only available in debug-builds.
print_debug: { all(feature = "print_debug", debug_assertions) },
// the nanvix-unstable and gdb features both (only
// temporarily!) need to use writable/un-shared snapshot
// memories, and so can't share
unshared_snapshot_mem: { any(feature = "nanvix-unstable", feature = "gdb") },
// gdb needs writable snapshot memory for debug access.
unshared_snapshot_mem: { feature = "gdb" },
}

#[cfg(feature = "build-metadata")]
Expand Down
53 changes: 17 additions & 36 deletions src/hyperlight_host/src/hypervisor/hyperlight_vm/x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,14 @@ impl HyperlightVm {
None => return Err(CreateHyperlightVmError::NoHypervisorFound),
};

#[cfg(not(feature = "nanvix-unstable"))]
#[cfg(not(feature = "i686-guest"))]
vm.set_sregs(&CommonSpecialRegisters::standard_64bit_defaults(_pml4_addr))
.map_err(VmError::Register)?;
#[cfg(feature = "nanvix-unstable")]
vm.set_sregs(&CommonSpecialRegisters::standard_real_mode_defaults())
.map_err(VmError::Register)?;
#[cfg(feature = "i686-guest")]
vm.set_sregs(&CommonSpecialRegisters::standard_32bit_paging_defaults(
_pml4_addr,
))
.map_err(VmError::Register)?;

#[cfg(any(kvm, mshv3))]
let interrupt_handle: Arc<dyn InterruptHandleImpl> = Arc::new(LinuxInterruptHandle {
Expand Down Expand Up @@ -248,21 +250,11 @@ impl HyperlightVm {
Ok(())
}

/// Get the current base page table physical address.
///
/// By default, reads CR3 from the vCPU special registers.
/// With `nanvix-unstable`, returns 0 (identity-mapped, no page tables).
/// Get the current base page table physical address from CR3.
pub(crate) fn get_root_pt(&self) -> Result<u64, AccessPageTableError> {
#[cfg(not(feature = "nanvix-unstable"))]
{
let sregs = self.vm.sregs()?;
// Mask off the flags bits
Ok(sregs.cr3 & !0xfff_u64)
}
#[cfg(feature = "nanvix-unstable")]
{
Ok(0)
}
let sregs = self.vm.sregs()?;
// Mask off the flags bits
Ok(sregs.cr3 & !0xfff_u64)
}

/// Get the special registers that need to be stored in a snapshot.
Expand Down Expand Up @@ -352,23 +344,12 @@ impl HyperlightVm {
self.vm.set_debug_regs(&CommonDebugRegs::default())?;
self.vm.reset_xsave()?;

#[cfg(not(feature = "nanvix-unstable"))]
{
// Restore the full special registers from snapshot, but update CR3
// to point to the new (relocated) page tables
let mut sregs = *sregs;
sregs.cr3 = cr3;
self.pending_tlb_flush = true;
self.vm.set_sregs(&sregs)?;
}
#[cfg(feature = "nanvix-unstable")]
{
let _ = (cr3, sregs); // suppress unused warnings
// TODO: This is probably not correct.
// Let's deal with it when we clean up the nanvix-unstable feature
self.vm
.set_sregs(&CommonSpecialRegisters::standard_real_mode_defaults())?;
}
// Restore the full special registers from snapshot, but update CR3
// to point to the new (relocated) page tables
let mut sregs = *sregs;
sregs.cr3 = cr3;
self.pending_tlb_flush = true;
self.vm.set_sregs(&sregs)?;

Ok(())
}
Expand Down Expand Up @@ -874,7 +855,7 @@ pub(super) mod debug {
}

#[cfg(test)]
#[cfg(not(feature = "nanvix-unstable"))]
#[cfg(not(feature = "i686-guest"))]
#[allow(clippy::needless_range_loop)]
mod tests {
use std::sync::{Arc, Mutex};
Expand Down
Loading
Loading