Skip to content

Report the Hyper-V generation from win-detect-nvme-readiness - #155

Open
Edwin Bernal Microsoft (EdwinBernal1) wants to merge 1 commit into
Azure:mainfrom
EdwinBernal1:nvme-detect-generation
Open

Edwin Bernal Microsoft (EdwinBernal1) wants to merge 1 commit into
Azure:mainfrom
EdwinBernal1:nvme-detect-generation

Conversation

@EdwinBernal1

Copy link
Copy Markdown
Member

Summary

win-detect-nvme-readiness now reports the Hyper-V generation of the attached Windows installation, and returns a stable GEN1_TO_GEN2_CONVERSION_REQUIRED signature when it finds a Generation 1 guest.

Independent of #154; no dependency in either direction.

The problem

NVMe-capable VM sizes are Generation 2 only. A Generation 1 guest boots BIOS/MBR, so no stornvme registry value can make it boot on an NVMe controller — it needs a BIOS/MBR → UEFI/GPT conversion first.

The detector had no generation awareness whatsoever. Its evidence record was osDrive, systemHiveFound, stornvmeDriverPresent, controlSet, the stornvme* values, storahciStart, cddbNvmeEntries, bootReadyForNvme, problems — nothing about firmware or partition layout. Two consequences:

  1. The documented flow could reach a repair decision without ever establishing that the guest boots BIOS, and win-enable-nvme-boot-driver would then write a correct-looking stornvme configuration to a disk that still cannot boot.
  2. Worse: a Generation 1 disk that happens to have stornvme Start=0 reported bootReadyForNvme = true. A caller routing on that field concludes the VM is fine and goes looking for a different fault entirely.

Changes

src/windows/win-detect-nvme-readiness.ps1 — v1.0.0 → v1.1.0.

New per-finding fields:

Field Source
partitionStyle Get-Disk on the disk hosting the drive letter — MBR / GPT / Unknown
efiSystemPartitionPresent A partition whose GptType is the UEFI-specification ESP GUID {c12a7328-f81f-11d2-ba4b-00a0c93ec93b}
firmwareType BIOS / UEFI / Unknown, derived from the two above
hyperVGeneration V1 / V2 / Unknown
conversionRequired true when hyperVGeneration is V1
mbr2gptAvailable Whether MBR2GPT.exe exists in the attached image — the in-box tool any future conversion would use

New top-level signature field on the evidence record: NVME_REPAIR_APPLICABLE, GEN1_TO_GEN2_CONVERSION_REQUIRED, STORNVME_DRIVER_MISSING, or ALREADY_NVME_READY. Free text gets reworded and truncated; a caller should route on this.

bootReadyForNvme now also requires -not conversionRequired, closing the misroute described above.

Schema version 1.01.1.

Two decisions worth reviewing

Generation is derived from the disk layout, not from Trusted Launch. The ARM security profile is not visible from inside a repair VM, and Trusted Launch would be the wrong signal even if it were: it implies Generation 2, but the converse is false — a plain Generation 2 VM does not use it, so treating its absence as Generation 1 would reject valid repair targets. The disk in front of the script is the only honest source.

GPT without an ESP reports Unknown, not a guess. Claiming V2 there would invite a repair that cannot work; claiming V1 would invite an unnecessary and destructive conversion. Same for an unreadable disk or partition — Unknown, and the scan continues so the rest of the stornvme evidence is still collected rather than lost to an exception.

Testing

tests/test-win-detect-nvme-readiness-generation.ps1 — new. The detector had no test file at all; this one extracts Get-OfflineWindowsGeneration and drives it against a fixture of the disk cmdlets, so it runs anywhere without an attached disk.

pwsh -NoProfile -File ./tests/test-win-detect-nvme-readiness-generation.ps1
  PASS: win-detect-nvme-readiness generation detection — Gen 2, Gen 1, ambiguous GPT,
        unreadable disk/partition, and the signature/schema contract.

Covers: GPT+ESP → V2/UEFI · MBR → V1/BIOS · GPT without ESP → Unknown · missing partition → Unknown, no throw · missing disk → Unknown, no throw. Plus contract assertions that the signature exists, the schema version was bumped, conversionRequired is derived from the detected generation, bootReadyForNvme excludes Gen 1, and the ESP is matched by its specification GUID rather than a label or size heuristic.

Whole repository, after the change:

test-get-disk-partitions-v3.ps1                  exit=0
test-map-catalog.ps1                             exit=0
test-win-detect-nvme-readiness-generation.ps1    exit=0
test-win-enable-nvme-boot-driver.ps1             exit=0

Invoke-ScriptAnalyzer -Path ./src/windows/win-detect-nvme-readiness.ps1   (clean)

No live Azure validation was run for this change. The generation derivation is exercised against fixtures only — a live confirmation on a real Generation 1 VM has not been done, and is the obvious thing to ask for before anyone relies on the signature to route a customer.

Behaviour change

Additive for consumers reading individual fields. One semantic change to flag: bootReadyForNvme now returns false for a Generation 1 guest that previously returned true. That previous true was wrong — the VM could not boot on NVMe — but a consumer that treats bootReadyForNvme = false as "run the repair" should route on signature instead.

The script remains read-only. It mounts the SYSTEM hive, reads, and unmounts; the new fields come from Get-Disk / Get-Partition and a Test-Path, none of which write.

Related

  • The conversion capability itself (win-convert-gen1-to-gen2) is not in this PR. It is designed but blocked on an open question: whether MBR2GPT.exe, which is built to convert its own Windows installation, can be driven against a disk attached to a different repair VM. Until that is answered the detector correctly stops at the prerequisite.
  • Register the win-enable-nvme-boot-driver run id (SCR-3) #154 registers the win-enable-nvme-boot-driver run id.

NVMe-capable VM sizes are Generation 2 only, so a Generation 1 guest cannot boot on an NVMe controller whatever the stornvme values say - it needs a BIOS/MBR to UEFI/GPT conversion first. The detector had no generation awareness at all, so the flow could reach a repair decision without ever knowing the guest boots BIOS, and a Gen 1 disk with stornvme Start=0 reported bootReadyForNvme true. A caller routing on that field would conclude the VM was fine and go looking for a different fault.

Adds partitionStyle, efiSystemPartitionPresent, firmwareType, hyperVGeneration, conversionRequired and mbr2gptAvailable to each finding, plus a top-level signature field. Generation is derived from the disk layout - GPT with an EFI system partition matched by its UEFI-specification GUID, or MBR - because the ARM security profile is not visible from a repair VM, and Trusted Launch would be the wrong signal regardless: it implies Generation 2, but a plain Generation 2 VM does not use it.

GPT without an ESP reports Unknown rather than guessing. Claiming V2 would invite a repair that cannot work; claiming V1 would invite an unnecessary conversion. Unreadable disks and partitions report Unknown instead of throwing, so the rest of the stornvme evidence is still collected. Schema version bumped to 1.1.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Reviewed the current head for generation classification and routing safety. The implementation correlates the Windows volume to its owning disk, requires GPT plus an ESP before reporting V2, keeps incomplete layouts as Unknown, and prevents an MBR/V1 installation from reporting bootReadyForNvme=true. The top-level signature precedence also routes conversion before driver repair. I found no blocking correctness or safety issue.

Residual validation gap: GitHub currently reports only the passing CLA check; the added fixture test is not shown as an executed check here. This review is static and did not rerun the script against attached Gen1/Gen2 disks.

@glimoli

Copy link
Copy Markdown
Contributor

Note

⚠️ APPROVE WITH NOTES — Overall Score 92/100 (Grade A)

The Gen1 bootReadyForNvme regression is fixed and proven on real Gen1 and Gen2 OS disks; static analysis is clean and the signature/schema contract holds across all four signatures. One non-blocking edge remains (false-ready on GPT-without-ESP) — recommended as a one-line fast follow, not a merge blocker.

VMRepair Script Test Report: win-detect-nvme-readiness.ps1

Summary

Overall Score: 92/100 (Grade: A) — APPROVE WITH NOTES

Category Score Notes
Functional Correctness 19/20 All 4 signatures validated on real attached OS disks; core Gen1 fix proven (F4). −1: adjacent GPT-without-ESP false-ready edge (out of PR scope, reproduced in F7b).
Code Quality 19/20 PSScriptAnalyzer 0 findings; STATUS_ constants, Log-*, try/catch/finally, one-signature if/elseif chain, schema bump. −1: minor dismount GC latency + $null tri-state doc.
Safety & Rollback 19/20 Provably read-only; robust Dismount-RegistryHive (GC + 5 retries, 3 defensive call sites, try/finally). −1: no explicit before/after partition-table byte-diff run.
Telemetry Coverage 17/20 Schema-versioned single-line [NVME-EVIDENCE-JSON] contract + .reg evidence bundle + Log-* tee. −3: no runtime event/correlation-id emission (acceptable for a read-only detector).
Test Coverage 18/20 7 real-disk fixtures across V1/V2 + edge layouts; PR unit test passes. −2: WS2016/WS2025 not exercised on real disks; unreadable-disk only mocked.

Fault Injection Results

Fault injection for a read-only classifier = seeding real offline-disk fixtures (SYSTEM-hive stornvme mutation + partition edits) on snapshot copies of real Gen1/Gen2 OS disks, attached to a repair VM.

Fixture Disk / Gen Mutation applied (fault) Observed signature bootReadyForNvme Status
F3 fixture-gen2 / V2 (GPT/UEFI) stornvme Start=0 (stock WS2022 image) ALREADY_NVME_READY true ✅ PASS
F2 fixture-gen2 / V2 (GPT/UEFI) stornvme Start=3 (disabled) NVME_REPAIR_APPLICABLE false ✅ PASS
F1 fixture-gen2 / V2 (GPT/UEFI) stornvme Start value deleted (absent) NVME_REPAIR_APPLICABLE false ✅ PASS
FDRV fixture-gen2 / V2 (GPT/UEFI) stornvme.sys driver file removed STORNVME_DRIVER_MISSING false ✅ PASS
F7 fixture-gen2 / Unknown (GPT no-ESP) ESP partition deleted STORNVME_DRIVER_MISSING (fw=Unknown,gen=Unknown) false ✅ PASS
F4 fixture-gen1 / V1 (MBR/BIOS) stornvme Start=0 (stock WS2019 default) — misroute case GEN1_TO_GEN2_CONVERSION_REQUIRED false ✅ PASS
F5 fixture-gen1 / V1 (MBR/BIOS) stornvme Start=3 GEN1_TO_GEN2_CONVERSION_REQUIRED false ✅ PASS
F7B fixture-gen2 / Unknown (GPT no-ESP) driver restored + stornvme Start=0 ALREADY_NVME_READY true ⚠️ OBSERVATION

Regression-guard verdict: CONFIRMED FIXED: real Gen1 disk (F4) with stock stornvme Start=0 yields bootReadyForNvme=false and GEN1_TO_GEN2_CONVERSION_REQUIRED. Old code would have reported bootReadyForNvme=true.

Testing Performed

Strategy: breaker-integrated (offline-disk fixture mutation — no static/dry-run)
Fault Injection: ✅ Real offline SYSTEM-hive mutation + partition edits on real snapshot disks (mutation engine: rvm_mutate_template.ps1)
Region: centralus (eastus2 rejected — Gen1-capable SKUs are NotAvailableForSubscription; all unrestricted eastus2 SKUs are Gen2-only)
Date: 2026-09-16 · Subscription: d4895902-…-f27ef254ebeb (InternalSub-glimoli)

Dimension Configuration Result
vm_generation V1 Real WS2019 Gen1 MBR/BIOS disk (F4, F5) ✅ GEN1_TO_GEN2_CONVERSION_REQUIRED, bootReady=false
vm_generation V2 Real WS2022 Gen2 GPT/UEFI disk (F1, F2, F3, FDRV) ✅ all signatures correct
partition_layout MBR / GPT+ESP / GPT-without-ESP ✅ V1 / V2 / Unknown
os_sku WS2019, WS2022 ✅ (WS2016/WS2025 not on real disks)
PR unit test tests/test-win-detect-nvme-readiness-generation.ps1 (pwsh 7) ✅ PASS
Quorum 7/7 contract fixtures (required 6) ✅ MET

Opportunities for Improvement

8 points recoverable (current 92 → potential 100)

  • [Functional / Safety, NON-BLOCKING] Gate bootReadyForNvme on the positive fact ($finding.hyperVGeneration -eq 'V2') instead of -not $finding.conversionRequired. Empirically reproduced in F7b: a GPT-without-ESP disk (generation Unknown) with stornvme Start=0 + driver present is currently reported bootReadyForNvme=true / ALREADY_NVME_READY, even though it is not a confirmed Gen2 UEFI disk. The proposed one-liner keeps the Gen1 fix intact and also refuses ready on ambiguous layouts.
  • [Code Quality, perf] Dismount-RegistryHive runs [gc]::Collect() + WaitForPendingFinalizers() + Start-Sleep 500ms before the first unload attempt (≈2.5 s avoidable latency ×3 call sites/drive). Try an immediate reg unload first, fall back to the GC path only if the key persists.
  • [Contract clarity] efiSystemPartitionPresent is $null on the unreadable-disk path but $false when evaluated-with-no-ESP. Document the tri-state (or normalize to $false) so strict JSON consumers don't conflate them.
  • [Test Coverage] Add WS2016 (mbr2gpt.exe absent → mbr2gptAvailable=false) and WS2025 real-disk fixtures; add a real unreadable-disk fixture.

Validation Evidence

Core fix — real WS2019 Gen1 disk, stock stornvme Start=0 (F4):

partitionStyle=MBR  firmwareType=BIOS  hyperVGeneration=V1
stornvmeStart=0  stornvmeDriverPresent=True  conversionRequired=True
bootReadyForNvme=False        <-- FIXED (old code would report True)
signature=GEN1_TO_GEN2_CONVERSION_REQUIRED   schemaVersion=1.1   exit=0

Reproduced edge — GPT-without-ESP, driver restored, stornvme Start=0 (F7b):

partitionStyle=GPT  efiSystemPartitionPresent=False  firmwareType=Unknown  hyperVGeneration=Unknown
stornvmeStart=0  stornvmeDriverPresent=True  conversionRequired=False
bootReadyForNvme=True         <-- false-ready on a NON-Gen2 disk (see Opportunity #1)
signature=ALREADY_NVME_READY

Review Checklist

  • Fault injection verified (seed fixture → run detector → assert signature/fields)
  • Code quality clean (PSScriptAnalyzer: 0 findings)
  • Telemetry coverage reviewed (structured JSON contract; 0 runtime error paths uninstrumented — read-only detector)
  • Safety features verified (provably read-only; robust hive unmount)
  • Multi-generation tested (Gen2 GPT/EFI + Gen1 MBR/BIOS, both on real disks)

Test Artifacts

  • Improvements: Output/TestReports/PR155-win-detect-nvme-readiness/2026-09-16/Improvements.md
  • HTML Report: Output/TestReports/PR155-win-detect-nvme-readiness/2026-09-16/TestReport.html
  • Execution Manifest: Output/TestReports/PR155-win-detect-nvme-readiness/2026-09-16/test_execution_manifest.json
  • Comprehensive ZIP: Output/TestReports/PR155-win-detect-nvme-readiness/2026-09-16/ComprehensiveTestResults_2026-09-16_1.zip

Note: No SME Tooling WI was resolved for this script during the run (selector tooling errored; ADO lookup deferred). Attach the artifacts above to the SME WI if one exists.


Generated by VMRepairMint Script Testing sub-agent | Test ID: win-detect-nvme-readiness-PR155-20260916 | Head SHA validated: 91e4e8a

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