Skip to content
Open
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
5 changes: 5 additions & 0 deletions .github/workflows/dep_build_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ jobs:
# with only one driver enabled (kvm/mshv3 features are unix-only, no-op on Windows)
just test ${{ inputs.config }} ${{ inputs.hypervisor == 'mshv3' && 'mshv3' || 'kvm' }}

- name: Run Rust tests with hw-interrupts
run: |
# with hw-interrupts feature enabled (+ explicit driver on Linux)
just test ${{ inputs.config }} ${{ runner.os == 'Linux' && (inputs.hypervisor == 'mshv3' && 'mshv3,hw-interrupts' || 'kvm,hw-interrupts') || 'hw-interrupts' }}

- name: Run Rust Gdb tests
env:
RUST_LOG: debug
Expand Down
7 changes: 7 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ test-like-ci config=default-target hypervisor="kvm":
@# with only one driver enabled + build-metadata + init-paging
just test {{config}} build-metadata,init-paging,{{ if hypervisor == "mshv3" {"mshv3"} else {"kvm"} }}

@# with hw-interrupts enabled (+ explicit driver on Linux)
{{ if os() == "linux" { if hypervisor == "mshv3" { "just test " + config + " mshv3,hw-interrupts" } else { "just test " + config + " kvm,hw-interrupts" } } else { "just test " + config + " hw-interrupts" } }}

@# make sure certain cargo features compile
just check

Expand Down Expand Up @@ -151,6 +154,9 @@ build-test-like-ci config=default-target hypervisor="kvm":
@# Run Rust tests with single driver
{{ if os() == "linux" { "just test " + config+ " " + if hypervisor == "mshv3" { "mshv3" } else { "kvm" } } else { "" } }}

@# Run Rust tests with hw-interrupts
{{ if os() == "linux" { if hypervisor == "mshv3" { "just test " + config + " mshv3,hw-interrupts" } else { "just test " + config + " kvm,hw-interrupts" } } else { "just test " + config + " hw-interrupts" } }}

@# Run Rust Gdb tests
just test-rust-gdb-debugging {{config}}

Expand Down Expand Up @@ -283,6 +289,7 @@ 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 hw-interrupts {{ target-triple-flag }}

fmt-check:
rustup +nightly component list | grep -q "rustfmt.*installed" || rustup component add rustfmt --toolchain nightly
Expand Down
11 changes: 11 additions & 0 deletions src/hyperlight_common/src/outb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ pub enum OutBAction {
TraceMemoryAlloc = 105,
#[cfg(feature = "mem_profile")]
TraceMemoryFree = 106,
/// IO port for PV timer configuration. The guest writes a 32-bit
/// LE value representing the desired timer period in microseconds.
/// A value of 0 disables the timer.
PvTimerConfig = 107,
/// IO port the guest writes to signal "I'm done" to the host.
/// This replaces the `hlt` instruction for halt signaling so that
/// KVM's in-kernel LAPIC (which absorbs HLT exits) does not interfere
/// with hyperlight's halt-based guest-host protocol.
Halt = 108,
}

impl TryFrom<u16> for OutBAction {
Expand All @@ -120,6 +129,8 @@ impl TryFrom<u16> for OutBAction {
105 => Ok(OutBAction::TraceMemoryAlloc),
#[cfg(feature = "mem_profile")]
106 => Ok(OutBAction::TraceMemoryFree),
107 => Ok(OutBAction::PvTimerConfig),
108 => Ok(OutBAction::Halt),
_ => Err(anyhow::anyhow!("Invalid OutBAction value: {}", val)),
}
}
Expand Down
28 changes: 28 additions & 0 deletions src/hyperlight_guest/src/exit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,34 @@ use core::arch::asm;
use core::ffi::{CStr, c_char};

use hyperlight_common::outb::OutBAction;
use tracing::instrument;

/// Signal successful completion of the guest's work and return control
/// to the host. This replaces the previous `hlt`-based exit: under the
/// `hw-interrupts` feature, `hlt` becomes a wait-for-interrupt (the
/// in-kernel IRQ chip wakes the vCPU), so we use an explicit IO-port
/// write (port 108) to trigger a VM exit that the host treats as a
/// clean shutdown.
///
/// This function never returns — the host does not re-enter the guest
/// after seeing the Halt port.
#[inline(never)]
#[instrument(skip_all, level = "Trace")]
pub fn halt() {
#[cfg(all(feature = "trace_guest", target_arch = "x86_64"))]
{
// Send data before halting
// If there is no data, this doesn't do anything
// The reason we do this here instead of in `hlt` asm function
// is to avoid allocating before halting, which leaks memory
// because the guest is not expected to resume execution after halting.
hyperlight_guest_tracing::flush();
}

unsafe {
out32(OutBAction::Halt as u16, 0);
}
}

/// Exits the VM with an Abort OUT action and code 0.
#[unsafe(no_mangle)]
Expand Down
3 changes: 2 additions & 1 deletion src/hyperlight_guest_bin/src/arch/amd64/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ core::arch::global_asm!("
mov cr4, rdi
flush_done:
call {internal_dispatch_function}\n
hlt\n
mov dx, 108\n
out dx, al\n
.cfi_endproc
", internal_dispatch_function = sym crate::guest_function::call::internal_dispatch_function);
3 changes: 2 additions & 1 deletion src/hyperlight_guest_bin/src/arch/amd64/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ core::arch::global_asm!("
mov rsp, r8\n
xor ebp, ebp\n
call {generic_init}\n
hlt\n
mov dx, 108\n
out dx, al\n
.cfi_endproc\n
", generic_init = sym crate::generic_init);
1 change: 1 addition & 0 deletions src/hyperlight_host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ trace_guest = ["dep:opentelemetry", "dep:tracing-opentelemetry", "dep:hyperlight
mem_profile = [ "trace_guest", "dep:framehop", "dep:fallible-iterator", "hyperlight-common/mem_profile" ]
kvm = ["dep:kvm-bindings", "dep:kvm-ioctls"]
mshv3 = ["dep:mshv-bindings", "dep:mshv-ioctls"]
hw-interrupts = []
# This enables easy debug in the guest
gdb = ["dep:gdbstub", "dep:gdbstub_arch"]
fuzzing = ["hyperlight-common/fuzzing"]
Expand Down
6 changes: 6 additions & 0 deletions src/hyperlight_host/src/hypervisor/hyperlight_vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2272,6 +2272,7 @@ mod tests {
// Tests
// ==========================================================================

#[cfg_attr(feature = "hw-interrupts", ignore)]
#[test]
fn reset_vcpu_simple() {
// push rax; hlt - aligns stack to 16 bytes
Expand Down Expand Up @@ -2417,6 +2418,7 @@ mod tests {

use super::*;

#[cfg_attr(feature = "hw-interrupts", ignore)]
#[test]
fn reset_vcpu_regs() {
let mut a = CodeAssembler::new(64).unwrap();
Expand Down Expand Up @@ -2476,6 +2478,7 @@ mod tests {
assert_regs_reset(hyperlight_vm.vm.as_ref());
}

#[cfg_attr(feature = "hw-interrupts", ignore)]
#[test]
fn reset_vcpu_fpu() {
#[cfg(kvm)]
Expand Down Expand Up @@ -2607,6 +2610,7 @@ mod tests {
}
}

#[cfg_attr(feature = "hw-interrupts", ignore)]
#[test]
fn reset_vcpu_debug_regs() {
let mut a = CodeAssembler::new(64).unwrap();
Expand Down Expand Up @@ -2649,6 +2653,7 @@ mod tests {
assert_debug_regs_reset(hyperlight_vm.vm.as_ref());
}

#[cfg_attr(feature = "hw-interrupts", ignore)]
#[test]
fn reset_vcpu_sregs() {
// Build code that modifies special registers and halts
Expand Down Expand Up @@ -2702,6 +2707,7 @@ mod tests {

/// Verifies guest-visible FPU state (via FXSAVE) is properly reset.
/// Unlike tests using hypervisor API, this runs actual guest code with FXSAVE.
#[cfg_attr(feature = "hw-interrupts", ignore)]
#[test]
fn reset_vcpu_fpu_guest_visible_state() {
let mut ctx = hyperlight_vm_with_mem_mgr_fxsave();
Expand Down
1 change: 1 addition & 0 deletions src/hyperlight_host/src/hypervisor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ pub(crate) mod tests {
use crate::sandbox::{SandboxConfiguration, UninitializedSandbox};
use crate::{Result, is_hypervisor_present, new_error};

#[cfg_attr(feature = "hw-interrupts", ignore)]
#[test]
fn test_initialise() -> Result<()> {
if !is_hypervisor_present() {
Expand Down
Loading