Skip to content

nvx-rs, runtime-adapter: #845's VM scenarios (GPT clones, a used upper, generated machine IDs) - #961

Closed
Enrique Saurez (esaurez) wants to merge 3 commits into
esaurez/845-vm-tests-base-snapshot-20261028from
esaurez/845-vm-tests-20261025
Closed

Enrique Saurez (esaurez) wants to merge 3 commits into
esaurez/845-vm-tests-base-snapshot-20261028from
esaurez/845-vm-tests-20261025

Conversation

@esaurez

Copy link
Copy Markdown
Contributor

Part of #845: the VM scenarios of PR 1 (#957) and PR 2 (#959).

Review base: this PR temporarily targets esaurez/845-vm-tests-base-snapshot-20261028, a frozen copy of the work it builds on, so that its diff shows only its own commits while the pull requests below it are in review. The snapshot is e00f02c: #844's PR 5 (clones in VMs through the test adapter) merged with this issue's PRs 1 to 3 (#957, #959, #960).

This PR does not merge on its own. At the final restack, its commits fold into the PRs whose work they test: 7f4de94 and 844e960 into PR 1 (#957), and 62b88de into PR 2 (#959). This PR then closes.

What and why

nvx-rs: restore clones of GPT disks (7f4de94, for PR 1)

A clone's plan refused a GPT disk as its image before any VMM started, since no clone's guest rebuilt the partitions that its kernel created for the template's placeholder. With PR 1's reread, the plan takes a GPT disk as it takes an EROFS layer:

  • The record of a GPT clone names the disk: the identity of its binding, the GUID that the adapter's plan expects of it, and the bound of its first partition, which CloneRequest gains, as first_partition_bound, beside the journal policy. An EROFS layer has no partitions, so the record of a clone of one leaves the bound out.
  • A bound of 0 is a setting that no record holds, and the plan refuses it before it opens anything, as it refuses the request's other Activate settings, with the evidence InvalidActivate. InvalidFormat now names only an image that is not of the format and identity that its binding names.
  • The plan checks a GPT disk as it checks an EROFS layer, from its metadata alone: the check of its binding, and the geometry of the template's placeholder, whatever the placeholder's format. As at a cold launch, the guest reads and checks the partition table with nvx::gpt before it assembles the root, so the host reads none of the disk, and hashes nothing.

runtime-adapter: start GPT clones, and refuse a used upper, in VMs (844e960, for PR 1)

runtime-adapter-host start takes --first-partition-bound BYTES for a GPT disk, as launch does, and clone::Request takes the bound, 64 KiB unless given.

In vm_clone, one template, whose placeholder is a GPT disk of two layers, serves four clones at once: the EROFS image over a scratch image and in RAM, a GPT disk of one layer in RAM, and one of three layers over a scratch image, with a bound that its first partition meets exactly. The disks' layer partitions start where none of the placeholder's do, nor each other's, so a clone whose kernel kept the placeholder's partitions would fail its assembly. Each GPT clone's root stacks the layers of its own disk's table, read-only, and returns its own first partition. A disk whose first partition is one byte larger than the bound ends the clone before it is ready, as does a scratch image that an earlier clone used, whose upper is not fresh: status 8, with the record of a failure of the request, by the evidence of an unusable root, with the guest's status 54.

The GPT writer moves from vm_layered into tests/common/gpt.rs, with the small ext2 layers and the CRC, and writes disks of any layout and length. vm_layered writes the same disks as before through it.

runtime-adapter: start clones with generated machine IDs, in VMs (62b88de, for PR 2)

start takes --machine-id (image | generated), the image's own by default, and clone::Request passes the mode to the clone's activation record.

In vm_clone, one template serves three clones at once: over a scratch image and in RAM, each with a generated machine ID, and in RAM with the image's own. Two executions in each, as user ID 0, see the hostname, none of the markers that the other clones' executions wrote in their roots, and the clone's machine ID: a generated one, distinct between the clones, the same in both executions, held by one bind that is read-only, nosuid and nodev, which a write cannot change; or, with the image's own, none, since the image has none, and then the one that the first execution wrote. Each stop then tears its root down, binds included.

A clone of a GPT disk whose layer holds a regular file etc starts with the image's own machine ID, which needs no bind, and ends before it is ready with a generated one, which no bind can hold below a file: status 8, by the evidence of invalid settings, with the guest's status 55. The template of that scenario is of the EROFS image, so it also restores a GPT clone of an EROFS placeholder.

Validation

At this head, with OpenVMM at 79a501b6, the pin of its base:

  • On Windows and on Linux x86_64, every step of check-nvx-rs: fmt; Clippy with -D warnings with all features and on the library without default features; the tests with all features and with the default ones; the library without default features; the documentation with -D warnings; and the MSRV check, with the aarch64-apple-darwin check on Linux.
  • The test adapter's fmt, Clippy, build, tests and MSRV check, in its protocol-only and host configurations on both, and in its musl guest configuration on Linux.
  • On Linux, the guest binaries' check, build and tests, and the Markdown links.
  • On Linux, as root: the ignored tests of guest::clone::activation::apply and of the partition-table reread with a loop device, three times each, and the privileged tests of guest::entry and guest::mappings.
  • The test adapter's VM suites, on Windows (WHP) and on Linux (MSHV): vm_clone, 15 scenarios, with the four of this PR; vm_layered, 43; vm_restart, 3; vm_steps, 3; and vm_template, 9.

Enrique Saurez and others added 3 commits October 25, 2026 19:59
A clone's plan refused a GPT disk as its image before any VMM started:
the kernel of a clone's guest had created the block devices of the
partitions of the template's placeholder, and no clone rebuilt them. The
guest of a GPT clone now asks the kernel to reread the disk's partition
table before it assembles the root, so the plan takes a GPT disk as it
takes an EROFS layer.

The record of a GPT clone names the disk: the identity of its binding,
the GUID that the adapter's plan expects of it, and the bound of its
first partition, which CloneRequest gains, as first_partition_bound,
beside the journal policy. An EROFS layer has no partitions, so the
record of a clone of one leaves the bound out. A bound of 0 is a setting
that no record holds, and the plan refuses it before it opens anything,
as it refuses the request's other Activate settings, with the evidence
InvalidActivate. InvalidFormat now names only an image that is not of
the format and identity that its binding names.

The plan checks a GPT disk as it checks an EROFS layer, from its
metadata alone: the check of its binding, and the geometry of the
template's placeholder, whatever the placeholder's format. As at a cold
launch, the guest reads and checks the partition table with nvx::gpt
before it assembles the root, so the host reads none of the disk, and
hashes nothing.

The test adapter's clone requests bound a GPT disk's first partition by
64 KiB, the default of a launch's guest.

Part of #845

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80932e09-af82-4b79-b417-70a241303cda
A clone's guest rereads a GPT disk's partition table before it assembles
the root, and refuses an upper that is not fresh, and the host now plans
GPT clones. These VM tests start such clones through
runtime-adapter-host start, on the hypervisor.

start takes --first-partition-bound BYTES for a GPT disk, as launch
does, and clone::Request takes the bound, 64 KiB unless given.

In vm_clone, one template, whose placeholder is a GPT disk of two
layers, serves four clones at once: the EROFS image over a scratch image
and in RAM, a GPT disk of one layer in RAM, and one of three layers over
a scratch image, with a bound that its first partition meets exactly.
The disks' layer partitions start where none of the placeholder's do,
nor each other's, so a clone whose kernel kept the placeholder's
partitions would fail its assembly. Each GPT clone's root stacks the
layers of its own disk's table, read-only, and returns its own first
partition. A disk whose first partition is one byte larger than the
bound ends the clone with status 54, as does a scratch image that an
earlier clone used, whose upper is not fresh.

The GPT writer moves from vm_layered into tests/common/gpt.rs, with the
small ext2 layers and the CRC, and writes disks of any layout and
length. vm_layered writes the same disks as before through it.

Part of #845

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80932e09-af82-4b79-b417-70a241303cda
A clone's guest sets the hostname and the machine ID of the record's
workload view for every execution: the image's own machine ID, or one
that it generates once for the clone, which a read-only bind holds at
/etc/machine-id. These VM tests check that view in clones that run at
once.

start takes --machine-id image|generated, the image's own by default,
and clone::Request passes the mode to the clone's activation record.

In vm_clone, one template serves three clones at once: over a scratch
image and in RAM, each with a generated machine ID, and in RAM with the
image's own. Two executions in each, as user ID 0, see the hostname,
none of the markers that the other clones' executions wrote in their
roots, and the clone's machine ID: a generated one, distinct between
the clones, the same in both executions, held by one bind that is
read-only, nosuid and nodev, which a write cannot change; or, with the
image's own, none, since the image has none, and then the one that the
first execution wrote. Each stop then tears its root down, binds
included.

A clone of a GPT disk whose layer holds a regular file etc starts with
the image's own machine ID, which needs no bind, and ends with status
55 before it is ready with a generated one, which no bind can hold
below a file. The template of that scenario is of the EROFS image, so it
also restores a GPT clone of an EROFS placeholder.

The scenarios' GPT disks may hold regular files in their top layer.

Part of #845

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80932e09-af82-4b79-b417-70a241303cda

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues remain for the GPT clone support and runtime-adapter changes.

No open issues 🎉

What changed in this PR

This PR adds host-side GPT clone support and VM scenarios for #845’s root activation and workload-view changes. Its commits are intended to fold into #957 and #959 during the final restack rather than merge independently.

Changes:

  • Allow GPT clone plans and activation records to carry disk identity and a first-partition size bound, while retaining metadata-only host checks.
  • Expose first-partition bounds and machine-ID modes through the runtime adapter’s start command.
  • Add VM coverage for differing GPT layouts, used-upper rejection, and machine-ID isolation, with shared GPT test helpers.
File Description
tests/​runtime-adapter/​tests/​vm_layered.rs Reuses the shared GPT and ext2 fixture helpers.
tests/​runtime-adapter/​tests/​vm_clone.rs Adds GPT, used-upper, and machine-ID VM scenarios.
tests/​runtime-adapter/​tests/​common/​mod.rs Exposes the shared GPT helper module.
tests/​runtime-adapter/​tests/​common/​gpt.rs Provides configurable GPT disks and small ext2 layers.
tests/​runtime-adapter/​tests/​clone_start.rs Tests new option validation and request defaults.
tests/​runtime-adapter/​src/​clone.rs Passes partition bounds and machine-ID modes into clone requests.
tests/​runtime-adapter/​src/​bin/​runtime-adapter-host.rs Parses and documents the new start options.
tests/​runtime-adapter/​README.md Documents clone options and added VM coverage.
nvx-rs/​src/​host/​template/​clone/​tests.rs Tests GPT planning, activation records, and invalid bounds.
nvx-rs/​src/​host/​template/​clone/​record.rs Builds GPT activation records with disk GUIDs and bounds.
nvx-rs/​src/​host/​template/​clone/​failure.rs Updates InvalidFormat documentation for GPT support.
nvx-rs/​src/​host/​template/​clone.rs Removes GPT rejection and adds the request’s partition bound.
nvx-rs/​README.md Documents GPT support in the host template API.

Review effort: Balanced

@esaurez

Copy link
Copy Markdown
Contributor Author

Folded into #957 (7f4de94, 844e960) and #959 (62b88de) at the final restack: 7f4de94 → 78f0382 and 844e960 → 8d328fc in #957, 62b88de → d5c55de in #959.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants