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
7 changes: 7 additions & 0 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[profile.arm64-mshv]
test-threads = 4

# Internally parallel tests run alone. VM replacement can double their partition count.
[[profile.arm64-mshv.overrides]]
filter = 'test(/thread|parallel|::create_[0-9]+_/) | (binary(integration_test) & test(/^interrupt_/))'
threads-required = "num-test-threads"
8 changes: 8 additions & 0 deletions .github/workflows/ValidatePullRequest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,14 @@ jobs:
cpu_vendor: arm
arch: arm64
config: release
- hypervisor: mshv3
cpu_vendor: arm
arch: arm64
config: debug
- hypervisor: mshv3
cpu_vendor: arm
arch: arm64
config: release
uses: ./.github/workflows/dep_build_test.yml
secrets: inherit
with:
Expand Down
47 changes: 30 additions & 17 deletions .github/workflows/dep_build_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,8 @@ on:
default: ""
full_aarch64:
description: >-
When "true", also run the single-driver tests on aarch64. PRs leave
this "false" so the aarch64 runner builds and runs the default Rust
tests; the daily schedule (DailyArm64.yml) sets it "true". This
input only affects aarch64: it does not change X64, where those tests
already run on Linux and are skipped on Windows regardless.
When "true", also run the single-driver tests on Linux aarch64.
ARM64 MSHV always runs its single-driver suite once.
required: false
type: string
default: "false"
Expand All @@ -62,7 +59,7 @@ defaults:
jobs:
build-and-test:
if: ${{ inputs.docs_only == 'false' }}
timeout-minutes: ${{ fromJSON(vars.BUILD_TEST_JOB_TIMEOUT || '45') }}
timeout-minutes: ${{ fromJSON(vars.BUILD_TEST_JOB_TIMEOUT || (inputs.arch == 'arm64' && inputs.hypervisor == 'mshv3' && '90' || '45')) }}
runs-on: ${{ fromJson(inputs.arch == 'X64'
&& format('["self-hosted", "{0}", "X64", "1ES.Pool=hld-{1}-{2}", "JobId=build-and-test-{3}-{4}-{5}-{6}"]',
inputs.hypervisor == 'hyperv-ws2025' && 'Windows' || 'Linux',
Expand All @@ -78,6 +75,12 @@ jobs:
github.run_id,
github.run_number,
github.run_attempt)
|| inputs.hypervisor == 'mshv3'
&& format('["self-hosted", "Linux", "ARM64", "1ES.Pool=hld-azlinux3-mshv-arm", "JobId=build-and-test-{0}-{1}-{2}-{3}"]',
inputs.config,
github.run_id,
github.run_number,
github.run_attempt)
|| format('["self-hosted", "{0}", "arm64", "{1}"]',
inputs.hypervisor == 'hvf' && 'macos' || 'Linux',
inputs.hypervisor)) }}
Expand Down Expand Up @@ -158,16 +161,25 @@ jobs:
- name: Build
run: just build ${{ inputs.config }}

- name: Run Rust tests
- name: Limit ARM64 MSHV test concurrency
if: inputs.arch == 'arm64' && inputs.hypervisor == 'mshv3'
run: |
# with default features
just test ${{ inputs.config }}
echo "RUST_TEST_THREADS=1" >> "$GITHUB_ENV"
echo "NEXTEST_PROFILE=arm64-mshv" >> "$GITHUB_ENV"

- name: Install ARM64 MSHV test runner
if: inputs.arch == 'arm64' && inputs.hypervisor == 'mshv3'
uses: taiki-e/install-action@682ce6c3c696a4cd5168a05f98e51aa02b877941 # v2.89.1
with:
tool: cargo-nextest@0.9.151

- name: Run Rust tests
# ARM64 MSHV selects its driver here to avoid a duplicate full run.
run: just test ${{ inputs.config }} ${{ inputs.arch == 'arm64' && inputs.hypervisor == 'mshv3' && 'mshv3 "nextest run"' || '' }}

- name: Run Rust tests with single driver
# Linux-only. Skipped on aarch64 PRs to conserve the limited runners;
# the daily schedule (full_aarch64 == 'true') restores it. On X64 Linux
# it always runs.
if: runner.os == 'Linux' && (inputs.arch == 'X64' || inputs.full_aarch64 == 'true')
# ARM64 MSHV runs this suite in the preceding step.
if: runner.os == 'Linux' && (inputs.arch == 'X64' || (inputs.full_aarch64 == 'true' && inputs.hypervisor != 'mshv3'))
run: |
# with only one driver enabled (kvm/mshv3 features are unix-only, no-op on Windows)
just test ${{ inputs.config }} ${{ inputs.hypervisor == 'mshv3' && 'mshv3' || 'kvm' }}
Expand Down Expand Up @@ -234,14 +246,15 @@ jobs:
if: inputs.arch == 'X64'
run: just test-csharp-bindings ${{ inputs.config }}

# Snapshot goldens are not published for ARM64 WHP or MSHV.
- name: Install oras
if: runner.os != 'Windows' || inputs.arch != 'arm64'
if: inputs.arch != 'arm64' || (runner.os != 'Windows' && inputs.hypervisor != 'mshv3')
uses: oras-project/setup-oras@5e2f2482b30b0cb3e14e67f89a80c88c659a7c63 # v2.0.3
with:
version: 1.3.1

- name: Log in to GHCR
if: runner.os != 'Windows' || inputs.arch != 'arm64'
if: inputs.arch != 'arm64' || (runner.os != 'Windows' && inputs.hypervisor != 'mshv3')
env:
GHCR_USER: ${{ github.actor }}
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -250,13 +263,13 @@ jobs:
echo "${GHCR_TOKEN}" | oras login ghcr.io -u "${GHCR_USER}" --password-stdin

- name: Snapshot goldens (pull and verify)
if: ${{ inputs.should_regen_goldens != 'true' && (runner.os != 'Windows' || inputs.arch != 'arm64') }}
if: ${{ inputs.should_regen_goldens != 'true' && (inputs.arch != 'arm64' || (runner.os != 'Windows' && inputs.hypervisor != 'mshv3')) }}
run: |
just snapshot-goldens-pull ${{ inputs.config }}
just snapshot-goldens-verify ${{ inputs.config }}

- name: Snapshot goldens (regenerate and verify)
if: ${{ inputs.should_regen_goldens == 'true' && (runner.os != 'Windows' || inputs.arch != 'arm64') }}
if: ${{ inputs.should_regen_goldens == 'true' && (inputs.arch != 'arm64' || (runner.os != 'Windows' && inputs.hypervisor != 'mshv3')) }}
run: |
# regenerate goldens from current branch
just snapshot-goldens-regenerate ${{ inputs.config }}
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

### Added
* Add `Sandbox::recreate()`, which rebuilds a sandbox's VM and memory from a snapshot. It recovers unrecoverable sandboxes and keeps configuration, host functions, and interrupt handles by @ludfjig in https://github.com/hyperlight-dev/hyperlight/pull/1751
* ARM64 Linux guests can run on MSHV.

### Fixed
* Fix restore on WHP not rewinding the guest TSC on hosts with `TSC_ADJUST` support.
Expand Down
10 changes: 5 additions & 5 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ like-ci config=default-target hypervisor="kvm":
just check-license-headers

# runs all tests
test target=default-target features="": (test-unit target features) (test-isolated target features) (test-integration target features) (test-doc target features) (test-tools target)
test target=default-target features="" runner="test": (test-unit target features runner) (test-isolated target features) (test-integration target features runner) (test-doc target features) (test-tools target)

# Cargo launches test binaries through the runner in this variable, for example
# CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER.
Expand All @@ -269,8 +269,8 @@ test-tools target=default-target:
{{ cargo-cmd }} test -p hyperlight-ci --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }}

# runs unit tests
test-unit target=default-target features="":
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --lib
test-unit target=default-target features="" runner="test":
{{ cargo-cmd }} {{ runner }} {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} {{ if runner == "test" { "--profile=" } else { "--cargo-profile=" } }}{{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --lib

# runs loom concurrency tests
test-loom:
Expand All @@ -296,7 +296,7 @@ test-isolated target=default-target features="" :
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F function_call_metrics," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- metrics::tests::test_metrics_are_emitted --exact

# runs integration tests
test-integration target=default-target features="":
test-integration target=default-target features="" runner="test":
@# run execute_on_heap test with feature "executable_heap" on (runs with off during normal tests)
{{ cargo-cmd }} test {{ if features =="" {"--features executable_heap"} else if features=="no-default-features" {"--no-default-features --features executable_heap"} else {"--no-default-features -F executable_heap," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --test integration_test execute_on_heap

Expand All @@ -308,7 +308,7 @@ test-integration target=default-target features="":
@# the glob but no-ops without a mode token, so it needs no golden
@# cache here. It verifies in its own step (see the snapshot-goldens
@# recipes).
{{ cargo-cmd }} test -p hyperlight-host {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --test '*'
{{ cargo-cmd }} {{ runner }} -p hyperlight-host {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} {{ if runner == "test" { "--profile=" } else { "--cargo-profile=" } }}{{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --test '*'

# tests compilation with no default features on different platforms
test-compilation-no-default-features target=default-target:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ Or skip setup entirely with a codespace:
| x86-64 | [KVM] | ✅ Yes | ✅ Yes | ✅ Yes |
| x86-64 | [MSHV] | ✅ Yes | ✅ Yes | ✅ Yes |
| AArch64 | [KVM] | ✅ Yes | ❌ No | ❌ No |
| AArch64 | [MSHV] | ❌ No | ❌ No | ❌ No |
| AArch64 | [MSHV] | ✅ Yes | ❌ No | ❌ No |

### Windows

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ impl HyperlightVm {
#[cfg(kvm)]
Some(HypervisorType::Kvm) => Box::new(KvmVm::new().map_err(VmError::CreateVm)?),
#[cfg(mshv3)]
Some(HypervisorType::Mshv) => return Err(CreateHyperlightVmError::NoHypervisorFound),
Some(HypervisorType::Mshv) => {
use crate::hypervisor::virtual_machine::mshv::MshvVm;
Box::new(MshvVm::new().map_err(VmError::CreateVm)?)
}
#[cfg(hvf)]
Some(HypervisorType::Hvf) => {
Box::new(HvfVm::new(interrupt_handle.clone()).map_err(VmError::CreateVm)?)
Expand Down
Loading
Loading