Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0df68cc
feat: add narrow shared memory accessor to UninitializedSandbox
danbugs Mar 3, 2026
845885e
feat: replace shared_mem_mut() with bounds-checked guest_memory_ptr()
danbugs Mar 4, 2026
9546967
feat: use i686 layout for non-init-paging guests on x86_64
danbugs Mar 6, 2026
ba43551
feat: add PIC emulation, OutBAction variants, and guest halt mechanism
danbugs Mar 3, 2026
276793f
feat(kvm): add hardware interrupt support with in-kernel IRQ chip
danbugs Mar 3, 2026
6787b58
feat(mshv): add hardware interrupt support with SynIC timer
danbugs Mar 3, 2026
ff19a7a
feat(whp): add hardware interrupt support with software timer
danbugs Mar 3, 2026
aaae920
test: add hardware interrupt unit and integration tests
danbugs Mar 3, 2026
1de9625
ci: add hw-interrupts test step to CI and Justfile
danbugs Mar 3, 2026
78915b4
fix: add halt port IO write and restore hw_timer_interrupts test
danbugs Mar 4, 2026
d68e899
style: add trailing newline to integration_test.rs
danbugs Mar 4, 2026
c6d6077
experiment: replace in-kernel PIT with irqfd + host timer thread
danbugs Mar 5, 2026
0b0e36b
experiment: MSHV — replace SynIC timer with request_virtual_interrupt…
danbugs Mar 5, 2026
f928242
experiment: eliminate PIC state machine — hardcode vector 0x20, no-op…
danbugs Mar 5, 2026
708221d
fix: delete unused pic.rs file
danbugs Mar 6, 2026
d669e93
style: rustfmt fixes in kvm.rs
danbugs Mar 6, 2026
3f45b9f
refactor: address PR 1272 review feedback
danbugs Mar 6, 2026
ca21917
fix: reset timer_stop flag before spawning timer thread
danbugs Mar 7, 2026
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
8 changes: 3 additions & 5 deletions src/hyperlight_common/src/arch/i686/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@ 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.

pub const MAX_GVA: usize = 0xffff_efff;
pub const SNAPSHOT_PT_GVA_MIN: usize = 0xef00_0000;
pub const SNAPSHOT_PT_GVA_MAX: usize = 0xefff_efff;
pub const MAX_GVA: usize = 0xffff_ffff;
pub const MAX_GPA: usize = 0xffff_ffff;

pub fn min_scratch_size() -> usize {
1 * crate::vmem::PAGE_SIZE
pub fn min_scratch_size(_input_data_size: usize, _output_data_size: usize) -> usize {
crate::vmem::PAGE_SIZE
}
13 changes: 11 additions & 2 deletions src/hyperlight_common/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,20 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

#[cfg_attr(target_arch = "x86_64", path = "arch/amd64/layout.rs")]
#[cfg_attr(target_arch = "x86", path = "arch/i686/layout.rs")]
#[cfg_attr(
all(target_arch = "x86_64", feature = "init-paging"),
path = "arch/amd64/layout.rs"
)]
#[cfg_attr(
all(target_arch = "x86_64", not(feature = "init-paging")),
path = "arch/i686/layout.rs"
)]
mod arch;

pub use arch::{MAX_GPA, MAX_GVA, SNAPSHOT_PT_GVA_MAX, SNAPSHOT_PT_GVA_MIN};
pub use arch::{MAX_GPA, MAX_GVA};
#[cfg(feature = "init-paging")]
pub use arch::{SNAPSHOT_PT_GVA_MAX, SNAPSHOT_PT_GVA_MIN};

// offsets down from the top of scratch memory for various things
pub const SCRATCH_TOP_SIZE_OFFSET: u64 = 0x08;
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);
5 changes: 3 additions & 2 deletions src/hyperlight_host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ tracing = { version = "0.1.44", features = ["log"] }
tracing-log = "0.2.0"
tracing-core = "0.1.36"
tracing-opentelemetry = { version = "0.32.1", optional = true }
hyperlight-common = { workspace = true, default-features = true, features = [ "std", "init-paging" ] }
hyperlight-common = { workspace = true, default-features = true, features = [ "std" ] }
hyperlight-guest-tracing = { workspace = true, default-features = true, optional = true }
vmm-sys-util = "0.15.0"
crossbeam-channel = "0.5.15"
Expand Down Expand Up @@ -133,11 +133,12 @@ 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"]
build-metadata = ["dep:built"]
init-paging = []
init-paging = ["hyperlight-common/init-paging"]

[[bench]]
name = "benchmarks"
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