Skip to content

[DO NOT REVIEW] cpu_profile: pin amd.genoa.v2 for Genoa hosts without the TSA immunities - #4769

Merged
Pedro Henrique Penna (ppenna) merged 2 commits into
feature/microvm/devfrom
copilot/cpu-profile-genoa-v2
Oct 27, 2026
Merged

Pedro Henrique Penna (ppenna) merged 2 commits into
feature/microvm/devfrom
copilot/cpu-profile-genoa-v2

Conversation

@ppenna

@ppenna Pedro Henrique Penna (ppenna) commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Pins amd.genoa.v2, a second revision of the Genoa CPU profile, for the Genoa hosts whose hypervisor presents nothing in 0x80000021 and no basic leaf above 0xd: the nested-MSHV host of microsoft/nvx#409 and the nested-WHP host of microsoft/nvx#928, both Azure VMs with an AMD EPYC 9V74. On both, every cold boot with --cpu-profile auto fails before the guest runs:

[E_PROFILE_UNSUPPORTED] the hypervisor's processor features cannot present CPU profile amd.genoa.v1: they lack tsa_l1_no_support, tsa_sq_no_support

Based on feature/microvm/dev. This branch is two commits directly on 57d0a18b3, the head of feature/microvm/dev since #4767 landed, and the revision that NVX's dev pins. It was rebased there from e68871cc6 after #4767 landed: git range-diff shows identical patches, and only the first commit's port note changed, to name the new base.

Cause

amd.genoa.v1 pins what one GitHub-hosted runner's KVM offers on an Azure VM with an EPYC 9V74: the maximum basic leaf 0x1c, LFENCE serialization and NoSmmCtlMsr in 0x80000021 EAX (0x204), and the TSA immunities, TSA_SQ_NO and TSA_L1_NO, in 0x80000021 ECX (0x6). The Microsoft hypervisor presents none of them to the partitions of these hosts, under nested MSHV (Microsoft Hv 10.0.26100.9444, Standard_D64als_v6) and nested WHP (10.0.26100.32522, Standard_D8ads_v5) alike, so both hosts' fingerprints fail amd.genoa.v1 on all three, and their processor features lack both TSA immunities.

amd.genoa.v2 (689577604)

Enrique Saurez's commit d946e7a8 from nanvix/openvmm, cherry-picked with its authorship; its message describes it in full. amd.genoa.v2 (sha256:8ee00e9ee8fd5defe3eebdc343733195d477f5f3449defa403f616d530a84839) derives from the runner's KVM fingerprint and the MSHV host's under the unchanged policy, openvmm-cpu-profile-policy/v1, and is amd.genoa.v1 but for two differences:

  • 0x80000021 reads zero, as under amd.milan.v1. Linux guests still serialize LFENCE through DE_CFG, whose LFENCE bit reads set under every AMD profile, and report TSA as vulnerable.
  • The maximum basic leaf is 0xd. The leaves 0xe to 0x1c, which amd.genoa.v1 lists as zero, are out of range.

Deriving it needed one fix to the registers' classes: 0x80000008 EDX, AMD's INVLPGB page count and RDPRU's largest register index, is now a limit, whose derivation takes the minimum, instead of a descriptive register, on which the two hosts' single votes tied. Every pinned profile clears that register, so every pinned profile rederives identically.

auto selects a generation's latest revision, so it selects amd.genoa.v2 on every Genoa host, GitHub's runners included. amd.genoa.v1 stays pinned: --cpu-profile amd.genoa.v1 still selects it, and its snapshots still restore where it is supported.

The cherry-pick conflicted only in the snapshot guide, whose paragraph on the pinned profiles this branch's base has reworded since. The resolution keeps the commit's sentences on Genoa's revisions and the base's on auto's fallback to a host profile. pinned_data.rs regenerates byte-identically with generate_pinned.

The WHP host (d2338ea22)

Records the nested-WHP host of microsoft/nvx#928 as test_support::GENOA_WHP_CPUID and GENOA_WHP_HOST, from its --cpu-fingerprint (surface digest sha256:16be9bd6dcff0e889003df7c287ae9acf34d3a801edc2e4b31d6f8b2a3ebfd23). That WHP offers exactly the processor features of the Milan WHP host that amd.milan.v1 derives from, and its CPUID is that host's but for the CPU's identity and its cache and TLB descriptors.

  • hv_banks::tests::the_genoa_mshv_and_whp_hosts_present_the_second_genoa_profile, which extends the MSHV host's test: neither host's processor features can present amd.genoa.v1, both lacking tsa_l1_no_support and tsa_sq_no_support, and both present amd.genoa.v2.
  • check::tests::the_genoa_whp_host_supports_the_second_genoa_profile: the fingerprint fails amd.genoa.v1 with exactly the violations that the host reported, and passes amd.genoa.v2, which auto selects, once a partition configured from the profile reads zero at CET's XSAVE components, 0xd.11 and 0xd.12, as on the Milan WHP host.
  • derive::tests::the_whp_host_adds_nothing_to_the_second_genoa_profile: deriving revision 2 from the KVM, MSHV, and WHP hosts gives amd.genoa.v2's CPUID, XSAVE state, and MSRs, so the WHP host constrains nothing that the others do not, and amd.genoa.v2 keeps its digest and its provenance.

Validation

Part of microsoft/nvx#409 and microsoft/nvx#928.

@github-actions

Copy link
Copy Markdown

amd.genoa.v1 pins what Azure presents to the KVM of a GitHub-hosted
runner with an EPYC 9V74, CPUID 0x80000021 included: LFENCE
serialization and NoSmmCtlMsr in EAX (0x204), and the TSA immunities,
TSA_SQ_NO and TSA_L1_NO, in ECX (0x6). Not every Genoa host's hypervisor
presents them. On an Azure VM with the same CPU (family 25 model 17
stepping 1) whose root runs Linux with MSHV (Microsoft Hv
10.0.26100.9444), the host partition offers neither TSA immunity
(ProcessorFeatures1 0x50e00001ed), the root's 0x80000021 reads zero, and
its kernel reports TSA as "Vulnerable: Clear CPU buffers attempted, no
microcode". Every cold boot with --cpu-profile auto there fails before
the guest runs:

    [E_PROFILE_UNSUPPORTED] the hypervisor's processor features cannot
    present CPU profile amd.genoa.v1: they lack tsa_l1_no_support,
    tsa_sq_no_support

and --cpu-fingerprint also finds CPUID 0x0 EAX at 0x1c, above the
supported 0xd, and 0x80000021 EAX bits 2 and 9 unsupported. As the time
ABI design anticipates for such a host, a later revision intersects the
fingerprints.

amd.genoa.v2 derives from two fingerprints: the runner's KVM fingerprint
that amd.genoa.v1 derives from (surface digest
sha256:68c11e9ed42a2e64267876eca0d21ba1083d24f36d5b4f9d891a53523646c2aa),
and that MSHV host's (surface digest
sha256:0d4f63812ed2feae52196f88a16733f54b3822389ef062c8a0c31cdb31083585),
whose CPUID and processor features test_support::GENOA_MSHV_CPUID and
GENOA_MSHV_HOST record:

    cargo run -p cpu_profile --example derive_profile -- genoa 2 <kvm> <mshv>

That gives amd.genoa.v2, digest
sha256:8ee00e9ee8fd5defe3eebdc343733195d477f5f3449defa403f616d530a84839,
which is amd.genoa.v1 but for two differences:

- 0x80000021 reads zero: no LFENCE serialization, NoSmmCtlMsr, or TSA
  immunities, as under amd.milan.v1. Linux guests still serialize
  LFENCE through DE_CFG, whose LFENCE bit reads set under every AMD
  profile, and report TSA as vulnerable.
- The maximum basic leaf is 0xd, that MSHV's, instead of 0x1c. The
  leaves 0xe to 0x1c, which amd.genoa.v1 lists as zero, are out of
  range.

Everything else is identical: XCR0 0x7, the features, the cache and TLB
descriptors, the address widths, and no pinned MSR.

Deriving it needed one fix to the registers' classes. The two hosts
disagree on CPUID 0x80000008 EDX, KVM 0 and MSHV 0x10000, and the policy
took that register to be descriptive, so the backends' single votes tied
("no majority for CPUID 0x80000008 EDX among the backends"). It holds
AMD's INVLPGB page count (bits 15:0) and RDPRU's largest register index
(bits 25:16), Intel reserves it, and both are limits: the class is now
Limits, so derivation takes their minimum, and a support check requires a
profile's to be within the host's. Every profile clears the register, so
neither a derivation that succeeded before nor a support check changes:
every pinned profile rederives identically, and the policy stays
openvmm-cpu-profile-policy/v1.

auto selects a generation's latest revision, so it now selects
amd.genoa.v2 on every Genoa host, GitHub's runners included. amd.genoa.v1
stays pinned: --cpu-profile amd.genoa.v1 still selects it, and its
snapshots still restore where it is supported.

pinned_data.rs, regenerated with generate_pinned from the eight files in
catalog order, gains amd.genoa.v2's block after amd.genoa.v1's and changes
nothing else but the array's length.

Tests:

- derive: amd.genoa.v2 is the policy's derivation of the recorded KVM
  and MSHV hosts, both of which support it, and the MSHV host's surface
  fails amd.genoa.v1 with exactly the three violations above. Rederiving a
  pinned profile uses its own revision.
- hv_banks: the MSHV host's processor features cannot present
  amd.genoa.v1, lacking tsa_l1_no_support and tsa_sq_no_support, and
  present amd.genoa.v2.
- catalog: auto selects amd.genoa.v2 on Genoa hosts, and amd.genoa.v1
  stays selectable by its ID.
- virt_whp: the time ABI tests' sample of the pinned profiles gains
  amd.genoa.v2.

Validation:

- cargo test with Rust 1.95.0 on Windows: cpu_profile 115, virt 65,
  virt_whp 36, openvmm_core 98, and openvmm_helpers 99 passed. clippy for
  cpu_profile and virt_whp and rustdoc for cpu_profile are clean with
  warnings denied, and xtask fmt passes.
- On the MSHV host above, with a musl release build of this change on
  top of the streamed block snapshot stack (2b38c40), built through
  NVX's platform build:
  - --cpu-fingerprint passes on amd.genoa.v2, which auto selects
    (NVX-CPU-PROFILE: status=pass), checking the entries outside the
    profile on a partition configured from it.
  - The --x-time-abi-verify preflight passes on auto with 1 and 4 VPs, and
    still fails --cpu-profile amd.genoa.v1 on the TSA immunities.
  - A 2-VP microVM boots on auto: its time ABI boot checks pass, its clock
    synchronizes, and its guest reports TSA as "Vulnerable: No microcode".
  - 15 guest-triggered snapshot captures and restores through NVX's broker
    pass on auto, without a --cpu-profile argument: clone and continuation
    snapshots, local, portable, and Blob-storage transfers, and two runs
    that drain running commands first.
- Not run on GitHub-hosted runners: amd.genoa.v2 takes nothing from
  amd.genoa.v1 that their KVM lacks, and the recorded runner's surface
  supports it.

Part of microsoft/nvx#409.

Ported onto feature/microvm/dev (57d0a18) for microsoft/nvx#928: the
Guide conflict keeps this commit's sentences on Genoa's revisions and the
base's on auto's fallback to a host profile, and pinned_data.rs
regenerates identically.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit d946e7a)
An Azure Standard_D8ads_v5 VM with an AMD EPYC 9V74 (family 25 model 17
stepping 1), whose WHP is Microsoft Hv 10.0.26100.32522 on Windows
Server 2025, cannot cold-boot on amd.genoa.v1 (microsoft/nvx#928):

    [E_PROFILE_UNSUPPORTED] the hypervisor's processor features cannot
    present CPU profile amd.genoa.v1: they lack tsa_l1_no_support,
    tsa_sq_no_support

and its --cpu-fingerprint (surface digest
sha256:16be9bd6dcff0e889003df7c287ae9acf34d3a801edc2e4b31d6f8b2a3ebfd23)
fails amd.genoa.v1 on the maximum basic leaf and 0x80000021, as the MSHV
host that amd.genoa.v2 derives from does. test_support::GENOA_WHP_CPUID
and GENOA_WHP_HOST record its CPUID and processor features. They are the
Milan WHP host's but for the CPU's identity and its cache and TLB
descriptors.

amd.genoa.v2 serves that host as pinned:

- hv_banks: its processor features cannot present amd.genoa.v1, lacking
  both TSA immunities, and present amd.genoa.v2.
- check: its fingerprint fails amd.genoa.v1 with the violations that the
  host reported, and passes amd.genoa.v2, which auto selects, once a
  partition configured from the profile reads zero at CET's XSAVE
  components, as on the Milan WHP host.
- derive: deriving revision 2 from the KVM, MSHV, and WHP hosts gives
  amd.genoa.v2's CPUID, XSAVE state, and MSRs, so the WHP host
  constrains nothing that the others do not, and amd.genoa.v2 keeps its
  digest and its provenance.

Not run on that host: a cold boot, the time ABI preflight, and NVX's
doctor and test-microvm.

Part of microsoft/nvx#928 and microsoft/nvx#409.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit to microsoft/nvx that referenced this pull request Oct 27, 2026
Pin microsoft/openvmm#4769 at d2338ea22c36135b468e7f3ae168066b6c9efde9,
two commits on 57d0a18b3, the previous pin, which is the head of
feature/microvm/dev.

amd.genoa.v1 pins what the KVM of the GitHub-hosted runner that it
derives from presents on an EPYC 9V74: the basic leaves up to 0x1C, and
LFENCE serialization, NoSmmCtlMsr, and the TSA immunities in CPUID
0x80000021. The Microsoft hypervisor of the Azure Genoa VMs
fingerprinted so far presents none of them, under nested MSHV (#409)
and nested WHP (#928) alike, so every cold boot with --cpu-profile auto
there fails before the guest runs:

    [E_PROFILE_UNSUPPORTED] the hypervisor's processor features cannot
    present CPU profile amd.genoa.v1: they lack tsa_l1_no_support,
    tsa_sq_no_support

The first commit pins amd.genoa.v2, amd.genoa.v1 without them, which
auto now selects on every Genoa host; amd.genoa.v1 stays pinned, so
--cpu-profile amd.genoa.v1 still selects it and its snapshots still
restore. The second records the WHP host of #928 and tests that
amd.genoa.v2 serves it as pinned.

NVX's two copies of the catalog assumed one profile per generation.
nvx-rs/src/host/cli/cpu_generations.json, which copies the identity of
every pinned profile, gains amd.genoa.v2, and nvx-rs run's guidance
lists each generation that the built-in profiles cover once.
scripts/nvx_tools/time_abi.py's copy records the latest revision of
each generation's profile, which doctor reports without OpenVMM, and its
test compares every revision with the pinned profiles. doc/usage.md and
doc/ci.md name amd.genoa.v2 as Genoa's profile.

Fixes #928
Part of #409

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit to microsoft/nvx that referenced this pull request Oct 27, 2026
Record the two commits that the previous commit pins as positions 348
and 349 of the microVM chain, and move the NVX pin to d2338ea22.

Position 348 pins amd.genoa.v2, a second revision of the Genoa CPU
profile that position 282 pins, and makes CPUID 0x80000008 EDX a limit
of the derivation policy of positions 159-269. Position 349 records the
nested-WHP Genoa host that amd.genoa.v2 serves as a test fixture. They
add to the CPU profile work that no PR maps yet rather than fix mapped
work, so they join positions 158-333, 341-343, 346, and 347 as
unassigned. Add them to the chain and to the table of the commits after
the mapped tip, and extend the scope. The pin is now 192 commits past
the mapped tip.

Positions 346 and 347 are no longer only a proposal:
microsoft/openvmm#4767 merged them, so position 347, 57d0a18b3, is the
head of feature/microvm/dev. Say so in the scope, and name
microsoft/openvmm#4769 as the proposal of positions 348 and 349.

Part of #928

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ppenna
Pedro Henrique Penna (ppenna) merged commit d2338ea into feature/microvm/dev Oct 27, 2026
106 of 108 checks passed
@github-actions

Copy link
Copy Markdown

Pedro Henrique Penna (ppenna) added a commit to microsoft/nvx that referenced this pull request Oct 28, 2026
Record the three commits that the previous commit pins as positions
350-352 of the microVM chain, and move the NVX pin to b92daa350.

Positions 350-352 bound HttpBlob's requests in time, retry the ones
that fail transiently on a new connection, and show them in inspect.
They change only disk_blob, which upstream main and the fork base have
alike and no other commit of the chain changes, so they can go upstream
on their own; say so in the table's remarks. The pin is now 195 commits
past the mapped tip.

Positions 348 and 349 are no longer only a proposal:
microsoft/openvmm#4769 merged them, so position 349, d2338ea22, is the
head of feature/microvm/dev. Say so in the scope, and name
microsoft/openvmm#4773 as the proposal of positions 350-352.

Refs #170

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit to microsoft/nvx that referenced this pull request Oct 28, 2026
Record the two commits that the previous commit pins as positions 350
and 351 of the microVM chain, and move the NVX pin to d7892ffa8.

Position 350 tests that the virtio-fs device takes the state of a
dormant filesystem slot into the device of an aggregate. Position 351
lets a platform restore bind an aggregate to the slot that the
snapshot left dormant, with its children, policy paths, and owner,
validated as at a cold boot. They add behavior rather than fix a
mapped commit, and position 351 changes the dormant-slot restore of
PR18's position 113, which position 293 limited to one --mount when it
added aggregates, so they stay unassigned, like positions 346-349.
Note in the table's remarks that assigning them means revisiting that
work. The pin is now 194 commits past the mapped tip.

Positions 348 and 349 are no longer only a proposal:
microsoft/openvmm#4769 merged them, so position 349, d2338ea22, is the
head of feature/microvm/dev. Say so in the scope, and name
microsoft/openvmm#4775 as the proposal of positions 350 and 351.

Part of #540

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit to microsoft/nvx that referenced this pull request Oct 28, 2026
Record the two commits that the previous commit pins as positions 350
and 351 of the microVM chain, and move the NVX pin to d7892ffa8.

Position 350 tests that the virtio-fs device takes the state of a
dormant filesystem slot into the device of an aggregate. Position 351
lets a platform restore bind an aggregate to the slot that the
snapshot left dormant, with its children, policy paths, and owner,
validated as at a cold boot. They add behavior rather than fix a
mapped commit, and position 351 changes the dormant-slot restore of
PR18's position 113, which position 293 limited to one --mount when it
added aggregates, so they stay unassigned, like positions 346-349.
Note in the table's remarks that assigning them means revisiting that
work. The pin is now 194 commits past the mapped tip.

Positions 348 and 349 are no longer only a proposal:
microsoft/openvmm#4769 merged them, so position 349, d2338ea22, is the
head of feature/microvm/dev. Say so in the scope, and name
microsoft/openvmm#4775 as the proposal of positions 350 and 351.

Part of #540

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants