Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
93c39d7
feat: add external byte codecs and C guest ByteChunks API
andreiltd Sep 2, 2026
7fb8217
fix: apply review suggestions
andreiltd Sep 4, 2026
0d85da0
fix: address code review feedback
andreiltd Sep 4, 2026
16901f5
feat(virtq): add stateful chain byte streams
andreiltd Jul 21, 2026
689ba19
refactor(virtq): remove reset api and harden allocation rollback
andreiltd Jul 22, 2026
e202ad9
feat(virtq): add tiered fixed slot allocation
andreiltd Jul 22, 2026
5a0d53e
feat(virtq): add canonical packed ring images
andreiltd Jul 23, 2026
e4c005e
refactor(layout): model scratch-top metadata
andreiltd Jul 24, 2026
ef34481
fix(guest): correct scratch allocator boundary
andreiltd Jul 24, 2026
eabe5c2
feat(virtq): define virtq transport metadata
andreiltd Jul 27, 2026
852e229
feat(virtq): implement host side memory access
andreiltd Jul 28, 2026
e23a41a
feat(virtq): configure transport geometry
andreiltd Jul 29, 2026
df7db8f
feat(virtq): initialize runtime transport
andreiltd Jul 30, 2026
f91bae8
feat(snapshot): preserve canonical virtq state
andreiltd Jul 31, 2026
d5a254c
feat(virtq): optimize inflight bookkeeping memory
andreiltd Aug 10, 2026
1211062
feat(virtq): use grouped allocation for virtq chains
andreiltd Aug 13, 2026
5f367a7
refactor(virtq) adjust virtio terminology
andreiltd Aug 11, 2026
1464929
fix: size foundation memory tests
andreiltd Sep 2, 2026
6a7345f
fix: add missing license headers
andreiltd Sep 5, 2026
ed4f2b1
feat(virtq): implement guest-to-host virtqueue transport
andreiltd Aug 3, 2026
d57a59b
feat(virtq): remove legacy guest-to-host stack transport
andreiltd Aug 3, 2026
8eec0b9
feat(virtq): use virtqueues for guest comms
andreiltd Aug 10, 2026
6c961a6
feat(virtq): purge input/output regions
andreiltd Aug 10, 2026
0fb1efd
feat(virtq): optimize inflight bookkeeping memory
andreiltd Aug 10, 2026
dfc813a
feat(virtq): checkpoint dirty queues for snapshots
andreiltd Aug 11, 2026
71ef3b5
feat(virtq): reject snapshots with retained buffers
andreiltd Aug 11, 2026
0b73c66
feat(virtq): add C guest `ByteChunks`
andreiltd Aug 11, 2026
4e84b23
fix(virtq): adjust scratch budget
andreiltd Aug 11, 2026
c0e8cf6
fix(virtq): address clippy warnings in release builds
andreiltd Aug 11, 2026
9525773
fix(virtq): use checked arithmetic in ExternalValueRefs
andreiltd Aug 11, 2026
b7edd6a
refactor: some stylish tweaks
andreiltd Aug 12, 2026
c04251b
fix: adjust test scratch size
andreiltd Aug 12, 2026
04139f9
fix(virtq): accept transport error in fuzzing
andreiltd Aug 12, 2026
81e4477
fix: increas fuzzing heap size
andreiltd Aug 13, 2026
f14e63c
fix: adjust test heap sizes
andreiltd Aug 13, 2026
0ce6404
fix: some docs improvements
andreiltd Aug 13, 2026
d19c6a8
fix: adjust snapshot restore test heap sizes
andreiltd Sep 2, 2026
3f28f23
feat(virtq): remove, now dead, hlbytechunks and hlsizeprefixedbytechunks
andreiltd Sep 2, 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
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Prerelease] - Unreleased

### Added
* Add per-direction virtqueue configuration and account its allocations in
scratch sizing.

### Changed
* `Snapshot::save` now writes the guest memory blob sparsely, skipping all-zero
Expand All @@ -14,10 +16,28 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
identical and its digest is unchanged, so this is transparent to readers and
to previously saved snapshots. Filesystems that do not support sparse files
store the blob as before.
* Expose C guest `ByteChunks` values as pointer and length arrays.
* Return typed `hl_ReturnValue` objects from C guest functions through
`hl_result_from_*` constructors.
* Place virtqueue rings and pools in host-owned scratch before page tables.
Snapshot ABI 3 rejects snapshots created with earlier layouts.
* Require guest logs and all host and guest function calls to use virtqueues.
* Keep registered Rust guest return values typed until transport encoding so
external byte results avoid intermediate FlatBuffer copies.
* Store canonical virtqueue rings in versioned OCI transport layers. Config v2
rejects snapshots without transport state.
* Running snapshots checkpoint dirty virtqueues before capture. Ordinary calls
keep their deferred result path.
* Reject snapshot capture while guest-owned transport buffers are retained.
* Use the reclaimed stack pages to raise the default G2H and H2G pools to 12
and 8 pages.

### Removed
* Remove legacy stack I/O, its `GuestHandle` methods, and its sandbox
configuration and builder options.

### Fixed
* Keep sandboxes usable after an H2G request exceeds available virtqueue capacity.

## [v0.17.0] - 2026-08-27

Expand Down
4 changes: 2 additions & 2 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ test-loom:
# runs tests that requires being run separately, for example due to global state
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 " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::uninitialized::tests::test_log_trace --exact --ignored
{{ 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 }} -p hyperlight-host --lib -- sandbox::outb::tests::test_log_outb_log --exact --ignored
{{ 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 }} -p hyperlight-host --lib -- sandbox::outb::tests::test_log_emit_guest_log --exact --ignored
{{ 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 }} -p hyperlight-host --test integration_test -- log_message --exact --ignored
@# CPU vendor check, gated to known CI runner hardware
{{ 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 }} -p hyperlight-host --lib -- sandbox::snapshot::file::config::tests::cpu_vendor_current_is_recognized --exact --ignored
Expand Down Expand Up @@ -524,7 +524,7 @@ coverage-run hypervisor="kvm": ensure-cargo-llvm-cov

# isolated tests (require running separately due to global state)
cargo +nightly test -p hyperlight-host --lib -- sandbox::uninitialized::tests::test_log_trace --exact --ignored
cargo +nightly test -p hyperlight-host --lib -- sandbox::outb::tests::test_log_outb_log --exact --ignored
cargo +nightly test -p hyperlight-host --lib -- sandbox::outb::tests::test_log_emit_guest_log --exact --ignored
cargo +nightly test -p hyperlight-host --test integration_test -- log_message --exact --ignored
cargo +nightly test -p hyperlight-host --no-default-features -F function_call_metrics,{{ if hypervisor == "mshv3" { "mshv3" } else { "kvm" } }} --lib -- metrics::tests::test_metrics_are_emitted --exact

Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ This project is composed internally of several components, depicted in the below

* [Security guidance for developers](./security-guidance-for-developers.md)
* [Paging Development Notes](./paging-development-notes.md)
* [Virtqueue host and guest communication](./virtio-host-guest-communication.md)
* [How to debug a Hyperlight guest](./how-to-debug-a-hyperlight-guest.md)
* [How to use Flatbuffers in Hyperlight](./how-to-use-flatbuffers.md)
* [How to make a Hyperlight release](./how-to-make-releases.md)
Expand Down
12 changes: 4 additions & 8 deletions docs/paging-development-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,10 @@ calls, i.e. there may be no calls in flight at the time of
snapshotting. This is not enforced, but odd things may happen if it is
violated.

Buffer management between the host and guest is needed to pass call
arguments and return values. Ideally, buffers would be dynamically
allocated from the scratch region as needed.

Currently, I/O buffers are statically allocated at the bottom of the
scratch region. This is a stopgap pending improved
physical allocation and buffer management.
Host and guest calls use two virtqueues in a fixed transport arena at
the bottom of scratch. The arena contains both rings and their
fixed-slot buffer pools. Copied page tables follow the arena. Dynamic
scratch allocations begin after the copied page tables.

The minimum scratch size is calculated by `min_scratch_size()` in the
architecture-specific layout modules under `hyperlight_common`; see
Expand Down Expand Up @@ -177,4 +174,3 @@ paging) and enables PAE. The guest is always entered in long mode.

Hyperlight unconditionally uses 48-bit virtual addresses. Hyperlight
presently only uses addresses in the lower (ttbr0) half of the address range.

27 changes: 17 additions & 10 deletions docs/snapshot-oci-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,28 @@ path/
<config-digest> Hyperlight config JSON
<snapshot-digest> raw memory bytes
(`memory_size` bytes)
<transport-digest> canonical virtqueue rings
```

Three blob kinds per tag:
Four blob kinds per tag:

* **manifest** (`application/vnd.oci.image.manifest.v1+json`). Tiny JSON
pointer record selected via `index.json`. References one config and
one layer by digest.
* **config** (`application/vnd.hyperlight.snapshot.config.v1+json`). The
two layers by digest.
* **config** (`application/vnd.hyperlight.snapshot.config.v2+json`). The
snapshot descriptor: arch, hypervisor, CPU vendor, ABI version,
resume address and captured registers, memory layout, registered
host functions, snapshot generation counter. Loaded eagerly and
fully parsed.
resume address and captured registers, memory and transport layout,
registered host functions, and snapshot generation counter. Loaded
eagerly and fully parsed.
* **layer / memory** (`application/vnd.hyperlight.snapshot.memory.v1`).
The raw guest memory image, exactly `memory_size` bytes. mmap'd on
restore.
* **layer / transport**
(`application/vnd.hyperlight.snapshot.transport.v1`). A bounded
binary image of the canonical G2H and H2G rings.

The runtime queue protocol and canonical checkpoint are described in
[Virtqueue host and guest communication](./virtio-host-guest-communication.md).

Blob filenames are the sha256 of the blob bytes, so identical blobs
across tags are stored once.
Expand All @@ -55,8 +62,8 @@ A single saved `Snapshot` consists of exactly:
config blob for tooling visibility,
* one **manifest** blob (referenced by that index entry),
* one **config** blob (referenced by the manifest's `config` field),
* one **layer** blob (the only entry in the manifest's `layers`
array, holding the raw memory image).
* one memory **layer** blob,
* one transport **layer** blob.

Saving two snapshots under different tags into the same `path`
produces two index entries and two manifests. Configs and layers are
Expand Down Expand Up @@ -98,12 +105,12 @@ podman), `go-containerregistry` (crane), and `regclient`.
## Read semantics

`Snapshot::load(path, reference)` reads a snapshot. It does not check
the manifest, config, or snapshot blobs against their sha256 digests.
the manifest, config, memory, or transport blobs against their sha256 digests.
`reference` is an
[`OciReference`], either a tag that matches the
`org.opencontainers.image.ref.name` annotation or the manifest
digest returned by `save`. `Snapshot::checked_load` adds the digest
check on those three blobs, catching accidental corruption on disk.
check on all four blobs, catching accidental corruption on disk.
Both run every other check (OCI structure, descriptor sizes, schema
versions, arch / hypervisor / CPU vendor / ABI tags, layout bounds,
entrypoint bounds). The caller is responsible for trusting the source.
Expand Down
23 changes: 13 additions & 10 deletions docs/snapshot-versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,34 @@ existing snapshots loadable, or while rejecting them with a clear error.

## What is versioned

A snapshot carries three independently evolvable version markers:
A snapshot carries four independently evolvable version markers:

* **Memory blob ABI**, `SNAPSHOT_ABI_VERSION` (a `u32` inside the
config blob, defined in
[src/hyperlight_host/src/sandbox/snapshot/file/media_types.rs](../src/hyperlight_host/src/sandbox/snapshot/file/media_types.rs)).
This is what the host reads back from a snapshot: the `OutBAction`
and `VmAction` port numbers, the input and output buffer stack
format, the offset and size of each memory region (including the
`HyperlightPEB` size), and the calling convention for guest function
entry. A change to any of these breaks older snapshots unless the
loader adds a compat path.
and `VmAction` port numbers, the virtqueue transport layout, the
offset and size of each memory region (including the `HyperlightPEB`
size), and the calling convention for guest function entry. A change
to any of these breaks older snapshots unless the loader adds a
compat path.
* **Snapshot blob encoding**, `MT_SNAPSHOT_V1`
(`application/vnd.hyperlight.snapshot.memory.v1`), aliased as
`MT_SNAPSHOT_CURRENT`. This is the on-wire format of the snapshot
blob: framing, section ordering, alignment, dirty/zero-page elision,
anything about how the bytes are packed inside the OCI layer.
* **Config schema**, `MT_CONFIG_V1`
(`application/vnd.hyperlight.snapshot.config.v1+json`), aliased as
* **Transport blob encoding**, `MT_TRANSPORT_V1`
(`application/vnd.hyperlight.snapshot.transport.v1`), aliased as
`MT_TRANSPORT_CURRENT`. This is the binary encoding of canonical
virtqueue state stored outside the memory layer.
* **Config schema**, `MT_CONFIG_V2`
(`application/vnd.hyperlight.snapshot.config.v2+json`), aliased as
`MT_CONFIG_CURRENT`. This is the JSON shape of the config blob:
field names, types, required vs optional, the descriptors the loader
needs in order to reconstruct the sandbox (memory sizes, buffer
sizes, `abi_version`, `hyperlight_version`, etc.). Renaming a field,
changing its type, or adding a required field is a schema change and
bumps this constant.
bumps this constant. Version 2 requires a transport layer.

The `OCI_LAYOUT_VERSION` constant is pinned by the OCI image-layout
spec at `1.0.0`.
Expand Down Expand Up @@ -367,4 +371,3 @@ major:
* The loader accepts the old `abi_version` (Option 2 step 4), so the old
golden loads.
* Register the host functions the old golden's checks call.

Loading
Loading