This document consolidates the validation results for Deploy-Drivers-For-WindowsServer. Because this repository ships experimental scripts that target AMD's consumer-class Ryzen chipset / Radeon iGPU / Ryzen AI NPU, all meaningful validation depends on access to physical AMD consumer hardware. Testing on non-AMD-consumer hardware (server-class EPYC, ARM, Intel, virtual machines without the target devices, etc.) cannot exercise the device-bind, driver-upgrade, or post-install verification paths that this pipeline exists to validate. This document therefore covers only physical-hardware validation:
- Validation Result 1: ThinkCentre M75q Tiny Gen 2 (Windows Server 2025 physical / Cezanne Zen 3 — chipset & graphics validated)
- Validation Result 2: ThinkPad X13 Gen 1 AMD (2020) (Windows 11 Enterprise LTSC 2024 / Renoir Zen 2 — chipset & graphics validated)
- Validation Result 3 (NPU script) — 🆘 NOT YET VALIDATED on physical NPU hardware. See §3 for the current limited validation status.
- Validation Result 4 (BthPan script) — ⏳ PLANNED. ThinkPad + Intel AX210 + Windows Server 2025 build 26100.32860 is the first physical-validation target. See §4 for the planned test sequence.
Documentation language policy: This document is maintained in English only. See
README.mdandREADME.ja.mdfor the bilingual entry-point documentation; for the repository-wide language policy seeSPEC.md§A.12.
Read this before sections 1-3. The four scripts have very different validation maturity levels.
| Script | Physical-hardware validation | Real driver install on target HW | Recommended use |
|---|---|---|---|
| Chipset | ✓ M75q Tiny Gen 2, X13 Gen 1 AMD (see CHANGELOG for per-revision validation history) | ✓ install completed successfully on M75q (WS2025) | Lab + cautious production |
| Graphics | ✓ M75q Tiny Gen 2, X13 Gen 1 AMD (see CHANGELOG for per-revision validation history) | ✓ install completed successfully on M75q (WS2025) | Lab + cautious production |
| NPU | ❌ none (no physical NPU machine in maintainer's lab) | ❌ never executed | Experimental / research-grade only. Do not deploy in production. |
| BthPan | ⏳ planned — ThinkPad + Intel AX210 + Windows Server 2025 build 26100.32860 is the first target (see §4 below) | ❌ not yet executed | New script; physical validation pending. Logic shares the proven Phase / Secure Boot / WDAC framework from the Chipset script (Edit-InfForServer, Get-OsContext, Resolve-PhaseSelection, etc. are verbatim-inherited). |
Note on the category-priority override (see SPEC §D.15): The category-priority override changes the install-decision semantics in a breaking way for chipset and graphics: self-signed
[C]drivers now always supersede Microsoft generic[A]and vendor[B]drivers regardless of version. Earlier physical-hardware validation results below remain structurally valid (extraction, patching, signing, WDAC deployment all behave the same), but the V05 / V06 / I03 driver-install decisions will differ — devices that earlier revisions classified asSKIP-newerare now classified asINSTALL_UPGRADE. Re-validation on the M75q Tiny Gen 2 and X13 Gen 1 AMD fixtures is recommended after upgrading.Note on the CiTool / UTF-8 / pnputil operational fixes (see SPEC §D.5 / §D.16 / §D.17, and the regression scenarios in §9 below). Three operational issues were identified on a clean WS2025 install and fixed:
- CiTool.exe was invoked without
--jsonand blocked at I02 on "Press Enter to Exit" stdin prompt (SPEC §D.16);- Console encoding was never set to UTF-8 so CiTool's ja-JP stdout displayed as mojibake (SPEC §D.5 / §D.16);
- pnputil exit=259 (
ERROR_NO_MORE_ITEMS) was misclassified as failure in the I03 summary, diverging from I04's correct REBOOT_NEEDED / no-op recognition (SPEC §D.17).These fixes do NOT alter the structural pipeline behaviour validated on the M75q / X13 Gen 1 AMD fixtures (extraction, patching, signing, WDAC deployment all behave the same). The user-visible improvements are: I02 no longer hangs ~60-75 s waiting for ENTER; the CiTool log line reads
処理が成功しましたinstead of mojibake; the I03 summary reportsno-op (already present)instead of mis-counted failures. See CHANGELOG.md for the release in which each fix landed.
The NPU script's verification is currently limited to:
- Static analysis with
psa.py(latest mainline) — full rule set including thePSA1xxx..PSA9xxxgeneric families and thePSAP0xxxproject-convention family (this repository opts in toPSAP0001..PSAP0005, withPSAP0005in strict mode), 0 errors / 0 warnings / 0 info with the repository-shipped.psa.config.json— seeSPEC.md§A.11.5. The exact rule count is not reproduced here to avoid mechanical drift on upstream additions; the canonical source is the runtimeRULESregistry (visible viapsa.py --list-rules).psa.pyis maintained as a canonical artifact in the ai-generated-artifacts repository; obtain it perSPEC.md§A.11 before running, and follow the "Version policy" subsection there (validate against the latest mainline, no fixed-version pinning). Before the analysis pass, two cheap pre-flight self-quality gates SHOULD be run when applicable:psa.py --config-check .psa.config.jsonwhenever.psa.config.jsonhas been edited, andpsa.py --self-checkwhenever a freshly-fetchedpsa.pyis being introduced — seeSPEC.md§A.11.6 for the activation matrix. - Code review of the AMD-published
quicktest.pyNPU detection logic translated to PowerShell. - No
-Action Installexecution has been performed by the maintainers anywhere. - No end-to-end run on physical NPU hardware has been performed by the maintainers.
If you have a Ryzen AI 300 / Ryzen AI Max 300 / Ryzen 7040 / 8040 series machine and successfully run any phase of the NPU script, please report results via GitHub Issues so the validation gap can be closed.
| Item | Value |
|---|---|
| Model | Lenovo ThinkCentre M75q Tiny Gen 2 |
| CPU | AMD Ryzen 7 PRO 5750GE (Cezanne, Zen 3, 8 core / 16 thread, 35 W TDP) |
| iGPU | AMD Radeon Graphics (Vega 8, integrated in Cezanne) |
| NPU | none (Cezanne predates AMD's NPU; XDNA NPU first appears in Phoenix / 7040 series) |
| Memory | DDR4 SO-DIMM 16–32 GB |
| Storage | M.2 NVMe SSD |
| BIOS | UEFI, Secure Boot configurable |
| TPM | fTPM (via AMD PSP) |
| Item | Value |
|---|---|
| OS | Windows Server 2025 Standard / Datacenter |
| Build | 26100 |
| ProductType | 3 (Server) |
| Secure Boot | ON |
| HVCI | OS default (varies by environment) |
| BitLocker | Optional (when enabled, secure the recovery key in advance) |
# Elevated PowerShell
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force
# Stage 1: PrepareVerify, V06 review (system unchanged)
# Recommended: use -LogFile to keep console colors while capturing the run.
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
.\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action PrepareVerify -CleanWorkRoot `
-LogFile "C:\Temp\m75q-amd-chipset_PrepareVerify_$ts.log"
.\Deploy-AMDGraphicsDriverOnWindowsServer.ps1 -Action PrepareVerify -CleanWorkRoot `
-LogFile "C:\Temp\m75q-amd-graphics_PrepareVerify_$ts.log"
# Legacy fallback (Write-Host coloring is stripped from the captured file):
# .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action PrepareVerify -CleanWorkRoot *>&1 |
# Tee-Object "C:\Temp\m75q-amd-chipset_PrepareVerify_$ts.log"
# Stage 2: Once V06 risk is acceptable, run Install
# IMPORTANT: secure the BitLocker recovery key beforehand
.\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action Install
.\Deploy-AMDGraphicsDriverOnWindowsServer.ps1 -Action Install
# NPU script: NOT APPLICABLE on Cezanne hardware (no NPU device present)
# M75q has no NPU device, so the NPU script cannot be meaningfully exercised here.- P03 detection:
Cezanne / Zen 3 / Desktop APU, AM4 - P03 download:
amd_chipset_software_8.02.18.557.exe(~75 MB) - P05 inventory: 67 INFs detected; 32 W11x64 variant INFs selected
- P06 patching: 1 INF patched (
AmdMicroPEP.inf); 31 INFs already Server-compatible and copied through - V06 main upgrade candidates (varies with the actual OEM driver baseline):
- AMD GPIO Controller:
oem17.inf v2.2.0.130→amdgpio2.inf v2.2.0.136 - AMD PSP 10.0 Device:
oem26.inf v5.22.0.0→amdpsp.inf v5.43.0.0(HIGH risk — BitLocker caution) - AMD SMBus:
oem12.inf v5.12.0.38→SMBUSamd.inf v5.12.0.44
- AMD GPIO Controller:
- P03 detection:
Cezanne APU, Vega-Polaris Legacy branch - P03 download:
whql-amd-software-adrenalin-edition-XX.X.X-win11-XXX-vega-polaris.exe(~600 MB) - P05 inventory: 19 INFs detected;
WT64A(audio) +WT6A_INF(display) variants selected - P06 patching: 1 INF patched (
u0197843.inf); 18 INFs already Server-compatible and copied through - V06 main upgrade candidates:
- AMD Audio CoProcessor:
oem70.inf v6.0.0.79→amdacpbus.inf v6.0.1.83(MEDIUM risk) - AMD Radeon Graphics: newer version in the AMD package → display upgrade (MEDIUM risk)
- AMD HD Audio Device:
oem58.inf v10.0.1.30→AtihdWT6.inf v10.0.1.30(date-newer, MEDIUM risk)
- AMD Audio CoProcessor:
- Not applicable on this host (Cezanne has no NPU). The NPU script cannot be meaningfully exercised on hardware that lacks an XDNA NPU device.
- All 21 phases completed successfully (chipset + graphics)
- Self-signed certificate (RSA 4096 / SHA-384, 5-year validity) generated successfully
- 32 catalogs (chipset) + 19 catalogs (graphics) generated by
inf2cat /os:Server2025_X64 - All catalogs successfully timestamp-signed by
signtool - After I03 (Install), Device Manager shows 3 chipset + 3 graphics devices bound to
[C] Self-signed
- On hosts with BitLocker enabled, a PSP driver upgrade can trigger a recovery prompt at the next boot. Always have the recovery key available (Control Panel BitLocker UI, or via Microsoft Account backup).
- Some
ROOT\AMD*software-only entities (AMDLOG / AMDXE etc.) are added by I03 but never appear inWin32_PnPSignedDriverenumeration; V06 Section 1 reports them as "software-only" for information only. - Successful install is confirmed by the
[B] Vendor→[C] Self-signedtransition observed in I04.
| Item | Value |
|---|---|
| Model | Lenovo ThinkPad X13 Gen 1 (AMD, 2020) |
| CPU | AMD Ryzen 5 PRO 4650U (Renoir, Zen 2, 6 core / 12 thread, 15 W TDP) |
| iGPU | AMD Radeon Graphics (Vega 6, integrated in Renoir) |
| NPU | none (Renoir predates AMD's NPU) |
| Memory | DDR4 16 GB on-board |
| Storage | M.2 NVMe SSD |
| BIOS | UEFI, Secure Boot toggleable |
| TPM | dTPM (Discrete TPM, e.g. Infineon SLB9670) |
| Item | Value |
|---|---|
| OS | Microsoft Windows 11 Enterprise LTSC 2024 |
| Build | 26100 (24H2 LTSC) |
| ProductType | 1 (Workstation) — runs in WS2025 PREVIEW MODE in this script |
| Secure Boot | OFF (toggled off for testing) |
| HVCI | ON |
| BitLocker | OFF (lab use) |
Windows 11 Enterprise LTSC 2024 shares NT kernel build 26100 with Windows Server 2025, so the script runs in WS2025 PRE-MIGRATION PREVIEW MODE (P00 banner declares it explicitly).
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force
# Install phases auto-block on Workstation OS — PrepareVerify only
# Recommended: use -LogFile to keep console colors while capturing the run.
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
.\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action PrepareVerify -CleanWorkRoot `
-LogFile "C:\Temp\x13gen1-amd-chipset_PrepareVerify_Win11-preview_$ts.log"
.\Deploy-AMDGraphicsDriverOnWindowsServer.ps1 -Action PrepareVerify -CleanWorkRoot `
-LogFile "C:\Temp\x13gen1-amd-graphics_PrepareVerify_Win11-preview_$ts.log"
# Legacy fallback (Write-Host coloring is stripped from the captured file):
# .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action PrepareVerify -CleanWorkRoot *>&1 |
# Tee-Object "C:\Temp\x13gen1-amd-chipset_PrepareVerify_Win11-preview_$ts.log"
# NPU script: NOT APPLICABLE (no NPU on Renoir)[+] OS detected: Microsoft Windows 11 Enterprise LTSC (build 26100)
Profile applied : WS2025 (Windows Server 2025)
ProductType : 1 (1=Workstation, 3=Server)
+-----------------------------------------------------------------+
| WS2025 PRE-MIGRATION PREVIEW MODE |
| (Windows 11 24H2 and Windows Server 2025 share NT build 26100) |
+-----------------------------------------------------------------+
Install phases auto-block (override with -AllowWorkstationInstall, but discouraged).
- P03 detection:
Renoir / Zen 2 / Mobile - P03 download:
amd_chipset_software_8.02.18.557.exe(same as M75q) - P05 inventory: 67 INFs detected; 32 W11x64 variant INFs selected
- P06 patching: 1 INF patched (
AmdMicroPEP.inf) - V06 main upgrade candidates (compared against Win11 OEM drivers):
- AMD PSP 10.0 Device:
oem144.inf v5.42.0.0→amdpsp.inf v5.43.0.0(HIGH risk) - GPIO / I2C / SMBus / MicroPEP — same version (KEEP)
- AMD PSP 10.0 Device:
- P03 detection:
Renoir / Vega-Polaris Legacy - P03 download:
whql-amd-software-adrenalin-edition-26.1.1-win11-jan-vega-polaris.exe(~624 MB) - P05 inventory: 19 INFs detected;
WT64A+WT6A_INFvariants selected - P06 patching: 1 INF patched (
u0197843.inf), mirroring 6 decorations - V06 upgrade candidates:
- AMD Audio CoProcessor:
v6.0.0.79 → v6.0.1.83(real version upgrade) - AMD Radeon Graphics:
v31.0.21923.11000 → v31.0.21924.61(real version upgrade) - AMD HD Audio Device:
v10.0.1.30 → v10.0.1.30(date-only newer; the graphics script explicitly displays "same version, but newer date")
- AMD Audio CoProcessor:
- All 21 phases completed (Install phases auto-blocked because the host is Workstation OS)
- All 19 INFs flow through the pipeline
- 19 catalogs + 19 signtool signatures all succeed
- AMD HW detected: AMD Audio CoProcessor, AMD Radeon Graphics, AMD HD Audio Device, AMD GPIO Controller, AMD I2C Controller, AMD Micro PEP, AMD SMBus, AMD PSP 10.0 Device, etc.
Comparing Validation Result 1 (M75q + WS2025) and Validation Result 2 (X13 Gen 1 + Win11 24H2): the script's decision logic is identical between the two OSes because they share kernel build 26100, but V06 upgrade candidate counts differ because the existing OEM driver baseline differs:
| V06 section | M75q (WS2025) | X13 Gen 1 (Win11) |
|---|---|---|
| Detected AMD HW | identical detection logic (HW topology differs by machine) | identical |
| MS-GENERIC count | high (clean WS2025 has bare Server in-box drivers) | lower (Win11 has OEM drivers pre-installed) |
| WILL be replaced count | more (MS generic → AMD vendor swaps are frequent) | fewer (only swap when AMD package is newer than the OEM driver) |
| KEEP (same/newer) count | fewer | more |
| Recommended Install execution | YES (target host) | NO (Workstation OS, auto-blocked) |
In other words, PrepareVerify on Win11 24H2 functions as pre-migration verification for WS2025: the patched-INF signatures and catalog structures generated remain valid on WS2025 (same kernel build). The actual install decisions (which devices fall into WILL be replaced) should be re-confirmed on WS2025 after migration.
🆘 THIS SECTION DOCUMENTS WHAT HAS NOT BEEN VERIFIED. Do not interpret it as evidence of working behaviour.
| Verification activity | Status | Evidence |
|---|---|---|
Static analysis with psa.py (latest mainline) with the repository-shipped .psa.config.json (see SPEC.md §A.11) |
✅ done | 0 errors / 0 warnings / 0 info — see CHANGELOG.md for the verified baseline (see §A.11.5) |
Pre-flight .psa.config.json schema validation via psa.py --config-check (see SPEC.md §A.11.6) |
✅ done | Config reports issues : 0 against psa.py latest mainline |
| Code review of NPU detection logic | ✅ done | Get-AmdNpuPlatform is a direct PowerShell port of AMD-published quicktest.py |
| Detection on physical NPU machine | ❌ NOT DONE | No physical NPU hardware in maintainer's lab as of this writing |
| INF parsing of real NPU driver ZIP | ❌ NOT DONE | NPU driver ZIPs (NPU_RAI*_WHQL.zip) are EULA-gated; maintainer does not have a verified copy of every RAI version's INF structure |
-Action Install on physical NPU machine |
❌ NOT DONE | Same as above |
Post-install bind to [C] Self-signed |
❌ NOT DONE | Same as above |
| AMD account auto-download (Tier 2) | Implemented from public form structure observation; AMD form changes can break without notice | |
| Ryzen AI Software user-mode stack on Server 2025 | ❌ explicitly unsupported by AMD | AMD documentation states Win11 24H2 (build >= 22621.3527) only |
Note on validation scope: The validation of this NPU script is fundamentally bottlenecked by access to physical Ryzen AI hardware. Because the script is an experimental tool targeting AMD's consumer-class NPU silicon, no meaningful end-to-end validation can be performed on hardware that lacks the target NPU device. Static analysis and code review are the only verification activities completed; everything that depends on actual device-bind behaviour, INF parsing of real driver ZIPs, or post-install verification remains pending until a physical NPU machine becomes available.
-
Acquire a Ryzen AI hardware test fixture. Candidates:
- ThinkPad T14s Gen 6 AMD (Ryzen AI 7 PRO 360 / Strix Point) — accessible via Lenovo retail.
- ASUS ProArt P16 (Ryzen AI 9 HX 370) — Strix Point with NPU enabled.
- HP OmniBook Ultra Flip 14 (Ryzen AI 9 HX 375) — Strix Point.
- Mini-PC builds with Ryzen AI Max 300 — limited availability as of 2026.
-
Run
-Action PrepareVerifyon the fixture with each of the 4 download tiers:- Tier 1: pre-captured
entitlenow.comURL. - Tier 2:
-AmdAccountUser/-AmdAccountPasswordwith a real AMD account. Confirm or adjust form-parsing regex. - Tier 3: probe AMD EULA URL (expected to fall through; document if AMD ever simplifies this).
- Tier 4:
-OfflineZipwith manually-downloaded ZIPs for RAI 1.5 / 1.6.1 / 1.7 / 1.7.1.
- Tier 1: pre-captured
-
Run
-Action Installon the fixture with the recommended workflow:- Capture
Get-CimInstance Win32_PnPSignedDriverbefore / after. - Confirm
[B] Vendor→[C] Self-signedtransition for the NPU device. - Run
Task Manager → Performance → NPU0and confirm the device appears. - Try
pnputil /enum-driversand confirm the patched INF appears under our self-signed cert.
- Capture
-
Document the failure modes:
- Does Server 2025 ever load the NPU kernel driver successfully? (Per AMD docs, the user-mode stack does not work, but the kernel driver itself is the focus of this script.)
- Does Cleanup actually remove the driver from the driver store, or does manual
pnputil /delete-driver oemNN.inf /forceremain necessary? - What event log entries appear in
CodeIntegrity / Operationalif WDAC blocks anything unexpected?
The 4-tier URL resolution in Resolve-AmdNpuDriverUrl (script line 772) controls how P03 obtains the NPU driver ZIP. The behaviour is not symmetric across all parameter combinations, so the table below documents the actual outcome of each invocation pattern. Use this when planning runs.
| # | Invocation | Outcome | Path through 4-tier resolver |
|---|---|---|---|
| 1 | -Action PrepareVerify -CleanWorkRoot -OfflineZip <path> |
✅ Recommended for first dry run. | T4 priority block (line 824) → ZIP copied to workspace → P03 succeeds |
| 2 | -Action PrepareVerify -CleanWorkRoot -OfflineZip <path> -AssumeIfMissing |
Same as #1 plus default Strix Point profile when no NPU detected | |
| 3 | -Action PrepareVerify -CleanWorkRoot (no -OfflineZip) |
T1 skip → T4 priority skip → T2 skip → T3 falls through (HTML form) → T4 auto-scan (script dir, ./cache, workspace, ~/Downloads) → if nothing found, throws | |
| 4 | -Action Install -OfflineZip <path> |
✅ Recommended for real-NPU install. | T4 priority block → I00 prompts for "I AGREE" → I01-I04 |
| 5 | -Action Install -AmdAccountUser ... -AmdAccountPassword ... |
T1 skip → T4 priority skip → T2 attempts authenticated download → falls back to T3/T4 on failure | |
| 6 | -Action Install -InstallerUrl <captured-url> |
✅ Works if the URL is fresh (entitlenow.com URLs expire). | T1 direct download → P03 succeeds |
| 7 | -Action Install -NpuOverride STX -NpuDriverPackage NPU_RAI1.6.1_314 (no source) |
❌ Misleading; do not use. | T1/T2/T3 skip → T4 auto-scan picks up whatever NPU_RAI*_WHQL.zip is in ~/Downloads (may not match the override) |
Why pattern #1 (PrepareVerify + OfflineZip) is the strongest recommendation:
- Deterministic: the Tier 4 priority block at line 824 short-circuits the resolver immediately. No network calls to AMD, no form-parsing fragility, no race against EULA URL expiry.
- System-untouched:
PrepareVerifyruns P00–P09 + V01–V06 only. No certs imported, no WDAC policy deployed, no drivers installed. - Reproducible across hosts: copy the same ZIP to a new machine, get the same P05/P06/V05/V06 output. Critical for CI regression testing.
- Gives you V05/V06 output: dry-run install plan and hardware impact analysis are produced even when the host has no NPU device (in which case
-AssumeIfMissingis needed to bypass detection failure).
Common pitfall — pattern #7: switches like -NpuOverride, -NpuDriverPackage, and -RyzenAiSoftwareVersion modify resolver behaviour but do not provide a download source. If you specify them without -OfflineZip / -InstallerUrl / -AmdAccountUser, the resolver falls through to Tier 4 auto-scan. Auto-scan picks up whichever NPU_RAI*_WHQL.zip it finds first — and that ZIP may not match the codename or version you tried to override. The version check happens inside the ZIP's INFs (P05), not against the filename. Always pin the source explicitly.
Even before any of the above gaps are closed, follow this checklist before running the NPU script on any host:
- You have read § Risk classification of the README.
- You have a Ryzen AI 300 / Ryzen AI Max 300 / Ryzen 7040 / 8040 series CPU (or you accept that detection will fall through to
-AssumeIfMissingand the run is a pipeline-soundness check only). - You have downloaded the appropriate
NPU_RAI*_WHQL.zipfrom https://ryzenai.docs.amd.com/en/latest/inst.html#install-npu-drivers and placed it next to the script (Tier 4 — recommended). - You have read AMD's Ryzen AI EULA at https://account.amd.com/en/forms/downloads/ryzenai-eula-public-xef.html and accepted it.
- You understand that Ryzen AI Software user-mode stack is officially Windows-11-only and will not give you AI inference on Server 2025.
- If running
-Action Install: you can roll back via-Action Cleanup(and you accept that driver-store removal may need manual intervention). - If running on a host with BitLocker: you have your recovery key recorded.
- You will report results to GitHub Issues regardless of success or failure (especially failure — the maintainers need this data to close the validation gap).
These are the outputs you should see when the script runs successfully. Deviation indicates a problem.
-------------------------------------------------------------------------
Ryzen AI Software OS support note
-------------------------------------------------------------------------
[!] AMD officially supports Ryzen AI Software ONLY on Windows 11 (build >= 22621.3527).
[!] Windows Server 2025 is NOT in AMD's supported OS matrix.
[!] This script patches the kernel-mode NPU driver to install on Server, but the
[!] user-mode Ryzen AI Software stack (conda env, OGA, Vitis AI EP) will likely
[!] not function on Server 2025 without unofficial workarounds.
[>] Enumerating PCI devices via pnputil /enum-devices /bus PCI /deviceids
[+] CPU : AMD Ryzen AI 9 HX 370 w/ Radeon 890M
[+] NPU codename : Strix Point / Strix Halo
[+] NPU short name : STX
[+] Hardware ID : PCI\VEN_1022&DEV_17F0&REV_00
[+] Detection source : pnputil
[+] Detected on host : True
[+] Preferred RAI ver: 1.7.1
[+] Recommended drv : 32.0.203.380
[>] Enumerating PCI devices via pnputil /enum-devices /bus PCI /deviceids
[!] No AMD NPU detected via pnputil. Using default profile (Strix Point + RAI 1.7.1).
[+] CPU : (host CPU - no NPU)
[+] NPU codename : Strix Point (default - no NPU detected)
[+] NPU short name : STX
[+] Detection source : default-strix-rai1.7.1
[+] Detected on host : False
followed by:
------------------------------------------------------------------
[!] NPU was NOT detected on the host (proceeding with default profile).
[!] Driver Install (I03) will likely produce 0 device bindings here.
[!] This run is useful for pipeline regression testing only.
------------------------------------------------------------------
+----------------------------------------------------------------+
| AMD RYZEN AI EULA ACCEPTANCE REQUIRED BEFORE INSTALL |
+----------------------------------------------------------------+
| By proceeding, you confirm: |
| 1. You have accepted the Ryzen AI EULA at: |
| https://account.amd.com/en/forms/downloads/ |
| ryzenai-eula-public-xef.html |
| 2. You acknowledge Windows Server 2025 is NOT officially |
| supported by AMD for Ryzen AI Software (Windows 11 only). |
| 3. You understand the kernel-mode driver alone does not |
| enable AI inference; Ryzen AI SW must be installed manually.|
| 4. You have BitLocker recovery keys recorded if applicable. |
+----------------------------------------------------------------+
Type "I AGREE" exactly to proceed with install (anything else aborts):
+================================================================+
| RYZEN AI SOFTWARE (USER-MODE STACK) - INSTALL THIS SEPARATELY |
+================================================================+
This script installed the kernel-mode NPU driver only.
To actually use the NPU for AI inference, install Ryzen AI Software:
Detected NPU codename : STX
Recommended RAI ver : 1.7.1
PREREQUISITES (per AMD documentation):
1. Windows 11 build >= 22621.3527 (NOT supported on Server 2025!)
2. Visual Studio 2022 (with Desktop Development with C++)
3. cmake >= 3.26
4. Miniforge (Python distribution); add condabin/Scripts to PATH
INSTALLATION STEPS:
1. Download Ryzen AI installer:
https://account.amd.com/en/forms/downloads/xef.html
Filename: ryzen-ai-lt-1.7.1.exe
2. Launch the EXE installer (run as Administrator)...
3. Verify the install (Miniforge Prompt):
conda activate ryzen-ai-1.7.1
cd %RYZEN_AI_INSTALLATION_PATH%\quicktest
python quicktest.py
The Invoke-AmdAccountAuthentication function in Deploy-AMDNpuDriverOnWindowsServer.ps1 was reviewed against the actual AMD account portal on 2026-05-10 to determine whether the implemented HTTP form POST flow can succeed against the current account.amd.com back-end. The verification used only public sources (no real AMD account credentials were used).
| Step | What was checked | How |
|---|---|---|
| 1 | account.amd.com rendering model |
Web fetch of related AMD portals (docs.amd.com/auth/login, pensandosupport.amd.com, fsdz.amd.com) |
| 2 | EULA URL pattern in current AMD docs | GitHub amd/ryzen-ai-documentation/blob/main/docs/inst.rst (latest commit) |
| 3 | Driver-version naming convention | Cross-check between RAI 1.5 / 1.6.1 / 1.7 / 1.7.1 documentation pages on ryzenai.docs.amd.com |
| 4 | End-user behavior of the EULA flow | GitHub amd/RyzenAI-SW#249, #328, and cnx-software.com end-user blog post (Feb 2024) |
| 5 | Existence of public PowerShell/Python automation | Web search for account.amd.com automation, AMD account download scripting |
| # | Finding | Severity | Evidence |
|---|---|---|---|
| F1 | account.amd.com is a JavaScript-driven SPA. Related AMD portals return "JavaScript is required" or "Loading application" HTML stubs on direct fetch. |
High | Direct probe of docs.amd.com/auth/login and fsdz.amd.com/adfs/ls/... |
| F2 | Login forms are not present in the initial HTML payload. CSRF tokens, form actions, and fields are likely injected by JavaScript at runtime. | High | F1 implies the login form is rendered client-side |
| F3 | EULA acceptance is interactive. End users report that they "could not avoid signing the Beta Software EULA" — implying a JS-driven multi-step modal, not a single hidden form POST. | Medium | cnx-software.com testimonial (2024); GitHub #249 (2025) |
| F4 | Two distinct EULA URL patterns exist in AMD's documentation. Original code assumed only one. | Medium | ryzenai-eula-public-xef.html for NPU drivers vs xef.html for RAI Software EXE / NuGet |
| F5 | The default driver/RAI mapping 1.7.1 → 32.0.203.380 was not real. AMD's RAI 1.7.1 documentation reuses the 1.6.1 driver (32.0.203.314) and there is no NPU_RAI1.7.1_380_WHQL.zip publicly listed. The script's own comment admitted this was a "placeholder build until AMD publishes". |
Medium | Cross-check of ryzenai.docs.amd.com/en/latest/inst.html and github.com/amd/ryzen-ai-documentation/blob/main/docs/inst.rst |
| F6 | No public automation script for AMD account login was found. Web search returned zero PowerShell/Python implementations that successfully drive the form. | Low | Negative search result; informational |
The Invoke-AmdAccountAuthentication function as implemented (HTTP form POST against https://account.amd.com/en/forms/auth/login.html) is highly unlikely to succeed against the current AMD portal. The portal architecture does not match the assumptions encoded in the function (server-rendered HTML form with hidden CSRF token, simple POST credentials → redirect to authenticated EULA → simple POST EULA accept → redirect to entitlenow.com).
This conclusion was reached without making authenticated requests against AMD's servers — it follows from publicly visible architectural evidence (F1–F3), driver-version inconsistency (F5), and absence of any working public implementation (F6).
| Change | Description | Location |
|---|---|---|
| C1 | Tier 2 disabled by default. The function now returns $null immediately unless -ForceAmdAccountAuth is passed. |
Invoke-AmdAccountAuthentication (~line 1170) |
| C2 | VERIFIED 2026-05-10 banner added with explicit "highly unlikely to succeed" warning. |
Invoke-AmdAccountAuthentication head |
| C3 | -ForceAmdAccountAuth switch added to param() block. Operators who believe AMD has changed their portal can opt in to test. |
Top-level param() |
| C4 | Versioning fully separated. Parameter -PreferredRyzenAiVersion (mixed driver + software in one knob) was replaced by two independent parameters: -NpuDriverPackage (default latest = NPU_RAI1.6.1_314) and -RyzenAiSoftwareVersion (default latest = 1.7.1). Filename generation now produces NPU_RAI1.6.1_314_WHQL.zip matching what AMD actually publishes. Compatibility between A and B is evaluated as a separate axis. |
[string]$NpuDriverPackage = 'latest'; [string]$RyzenAiSoftwareVersion = 'latest'; new functions Get-NpuDriverPackageInfo, Get-LatestRyzenAiSoftwareInfo, Test-NpuDriverRaiCompatibility |
| C5 | Get-RecommendedNpuDriverBuild mapping corrected. RAI 1.7 / 1.7.1 entries now both return 32.0.203.314 (the real published driver) instead of fictional 329 / 380 builds. Cross-references to AMD docs are added in the function header. |
Get-RecommendedNpuDriverBuild |
| C6 | All header .EXAMPLE filenames updated from NPU_RAI1.7.1_380_WHQL.zip (fictional) to NPU_RAI1.6.1_314_WHQL.zip (verified). |
Script header lines ~93, 99, 110, 124, 132 |
| C7 | Default-Strix profile label changed from default-strix-rai1.7.1 to default-strix-rai1.6.1. P03 banner reflects the verified driver build. |
Get-AmdNpuPlatform $AssumeIfMissing branch |
When set, the existing form-based POST sequence is attempted unchanged:
.\Deploy-AMDNpuDriverOnWindowsServer.ps1 `
-Action Install `
-ForceAmdAccountAuth `
-AmdAccountUser 'you@example.com' `
-AmdAccountPassword (Read-Host 'AMD password' -AsSecureString)Expected result on the current AMD portal: failure at one of the following points (most likely Step 2 or Step 3):
- Step 1 GET EULA page → fetch likely succeeds but no CSRF token in HTML
- Step 2 POST credentials → likely fails (no form actually exists at the documented URL)
- Step 3 GET authenticated EULA → likely succeeds but no acceptance form action found
- Step 4 POST EULA acceptance → likely fails (no form actually exists)
If by some chance AMD has reverted to a server-rendered form, the existing fallback code path handles success; no further changes needed in that case.
Re-run this verification when:
- AMD announces a new Ryzen AI release (≥ 1.7.2 or 1.8) — driver mapping table may need updates
- A user reports that
-ForceAmdAccountAuthnow succeeds — Tier 2 can be re-enabled by default - A new EULA URL pattern appears in AMD documentation (a third path beyond the two known)
The verification re-run procedure is the same as in 4.6.1: fetch public AMD pages, cross-check EULA URL patterns in amd/ryzen-ai-documentation GitHub repository, and check for end-user reports of successful automation.
The NPU script's version-handling logic was redesigned on 2026-05-10 to fully separate the NPU kernel-mode driver versioning system from the Ryzen AI Software (user-mode stack) versioning system, per AMD's authoritative documentation at https://ryzenai.docs.amd.com/en/latest/inst.html (Last updated 2026-04-19).
AMD's installation guide treats NPU drivers and Ryzen AI Software as fully decoupled artefacts:
| Aspect | NPU kernel-mode driver (axis A) | Ryzen AI Software (axis B) |
|---|---|---|
| What it is | Windows kernel-mode driver bundled in npu_sw_installer.exe, providing PCI device binding and firmware loading |
User-mode runtime: Python conda environment, ONNX Runtime VitisAI EP, OnnxRuntime GenAI (OGA), AMD Quark quantizer, xrt-smi tool |
| Distribution | EULA-gated ZIP at account.amd.com/en/forms/downloads/ryzenai-eula-public-xef.html?filename=NPU_RAI*_WHQL.zip |
EULA-gated EXE at account.amd.com/en/forms/downloads/xef.html?filename=ryzen-ai-lt-*.exe (note the different EULA URL pattern) |
| Currently published versions (per AMD docs 2026-04-19) | NPU_RAI1.5_280_WHQL.zip (driver 32.0.203.280) and NPU_RAI1.6.1_314_WHQL.zip (driver 32.0.203.314) |
1.7.1 (latest), with installer ryzen-ai-lt-1.7.1.exe and NuGet 1.7.1_nuget_signed.zip |
| Update cadence | Slow — only when a new firmware/driver pair is released. Backward-compatible with prior RAI Software versions in the supported range. | Frequent — ships new model support, performance improvements, and bug fixes. AMD recommends always using the latest for end-user workloads. |
| Operator default in this script | latest → NPU_RAI1.6.1_314 (the newer of the two documented packages) |
latest → 1.7.1 (auto-resolves to whatever this script currently knows as the latest) |
| Naming inside ZIP filenames | The RAI1.5 / RAI1.6.1 token in NPU_RAI*_WHQL.zip is a historical naming artefact — both ZIPs work with current Ryzen AI Software 1.7.1 |
Versioning is its own scheme: 1.5 → 1.6.1 → 1.7 → 1.7.1 |
The crucial point: the 1.6.1 in NPU_RAI1.6.1_314_WHQL.zip is NOT the Ryzen AI Software version. It is a release-channel label inherited from the original RAI 1.6.1 release window. The same driver ZIP is the recommended driver for RAI Software 1.7.1.
AMD documents driver-software compatibility in the Ryzen AI Software installation guide. As of RAI 1.7.1 (the current latest):
"Download and Install the NPU driver version: 32.0.203.280 or newer using the following links" — both
NPU_RAI1.5_280andNPU_RAI1.6.1_314are listed as valid options.
This produces the following compatibility matrix (axis C — derived from axes A + B):
| RAI 1.5 | RAI 1.6.1 | RAI 1.7 | RAI 1.7.1 | |
|---|---|---|---|---|
Driver 32.0.203.280 (NPU_RAI1.5_280) |
✅ | ✅ | ✅ | ✅ |
Driver 32.0.203.314 (NPU_RAI1.6.1_314) |
✅ | ✅ | ✅ | ✅ |
The minimum driver requirement (32.0.203.280) is consistent across all supported RAI Software versions per AMD's documentation. The script's Test-NpuDriverRaiCompatibility function encodes this matrix and emits OK or MISMATCH at P03.
| Layer | Before | After |
|---|---|---|
| Operator parameters | Single -PreferredRyzenAiVersion <ver> (mixed driver + software in one knob) |
Two independent parameters: -NpuDriverPackage <NPU_RAI1.5_280 | NPU_RAI1.6.1_314 | latest> and -RyzenAiSoftwareVersion <1.5 | 1.6.1 | 1.7 | 1.7.1 | latest>. Both default to latest. |
| Catalog functions | Get-RecommendedNpuDriverBuild $RaiVersion → $build (incorrect coupling) and Get-NpuZipFilename $RaiVersion $build → $filename (string concatenation that produced fictional filenames) |
Three independent functions: Get-NpuDriverPackageInfo (axis A: returns full package metadata for the documented ZIPs), Get-LatestRyzenAiSoftwareInfo (axis B: returns RAI Software metadata with IsLatest flag), Test-NpuDriverRaiCompatibility (axis C: evaluates the matrix above with [version] comparison) |
| Detected-platform fields | RecommendedRaiVer, RecommendedDriver (2 fields, ambiguously coupled) |
NpuDriverPackage, NpuDriverBuild, NpuDriverZipName (axis A), RyzenAiSoftwareVersion, RyzenAiSoftwareInstaller (axis B), DriverSoftwareCompatible, DriverSoftwareCompatNote (axis C) — 7 fields with explicit axis attribution |
| P03 banner output | Single block listing "Preferred RAI ver" and "Recommended drv" | Three labelled blocks: "NPU kernel-mode driver (independent versioning axis)", "Ryzen AI Software (independent versioning axis - always latest unless pinned)", "Driver <-> RAI Software compatibility (separate evaluation axis)" with OK/MISMATCH status |
| Post-install guidance (I04) | Hardcoded fallback to 1.7.1 if RAI version was missing |
Reads RyzenAiSoftwareInstaller field directly; falls back to ryzen-ai-lt-1.7.1.exe only if the field is empty. Explicitly states "NPU driver and Ryzen AI Software are versioned INDEPENDENTLY. Always use the LATEST Ryzen AI Software for end-user workloads." |
When AMD publishes a new Ryzen AI release, update the script in two places:
- If a new NPU driver ZIP is published (e.g.
NPU_RAI1.8_400_WHQL.zip): add an entry to theGet-NpuDriverPackageInfocatalog and the-NpuDriverPackageValidateSet. If the new driver introduces a different minimum-required driver build for current RAI Software, updateTest-NpuDriverRaiCompatibility. - If a new Ryzen AI Software version is released (e.g.
1.8.0): add an entry to theGet-LatestRyzenAiSoftwareInfocatalog, update$latestVersionto the new version, and add the new value to the-RyzenAiSoftwareVersionValidateSet. Cross-check the AMD release notes for any new minimum driver requirement and update$minimumPerRaiinTest-NpuDriverRaiCompatibilityaccordingly.
The two updates are independent — adding driver support does not require touching software metadata, and vice versa. This is the central design property the redesign achieves.
The BthPan script is brand-new; physical validation has not yet been performed. This section documents the planned first physical-validation run.
| Item | Value |
|---|---|
| Model | Lenovo ThinkPad (specific SKU TBD; any model with bound Intel AX210) |
| Bluetooth host controller | Intel AX210 (USB\VID_8087&PID_0032, also seen as USB\VID_8087&PID_0033) |
| Host controller driver source | Intel published Bluetooth_22.x.x.x_64UWD-RetailWHCK.zip (vendor-signed; loads on Server with no patching) |
| OS | Windows Server 2025 (build 26100.32860 — the first WS2025 GA build) |
| ProductType | 3 (Server) |
| Disk | NVMe (free space >5 GB for workspace; BthPan workspace is small ~10 MB) |
After installing the Intel AX210 host controller driver via its vendor installer, BTH\MS_BTHPAN should appear in Device Manager. The expected starting state is one of:
- Unknown Device (code 28):
BTH\MS_BTHPANenumerated but no driver bound. This is the cleanest case for I04 to verify true resolution against. - Phantom OK:
BTH\MS_BTHPANshowing Status=OK, withDriverInfPath=bth.inf,Class=Bluetooth,Service=(empty). This is the trickier case the script is specifically designed to detect.
V06 will diagnose and print the actual starting classification.
# Stage 0: confirm host controller is bound
Get-PnpDevice -Class Bluetooth | Select-Object FriendlyName, Status, InstanceId
# Stage 1: diagnosis only (no system change)
.\Deploy-MSBthPanInboxOnWindowsServer.ps1 -Action PrepareVerify -CleanWorkRoot
# Read V05 + V06 output carefully. Confirm:
# - V05 reports the device count and classification
# - V06 risk class is LOW (BthPan default; only MEDIUM if Phantom OK detected)
# - Patched bthpan.inf is at C:\Temp\Workspace_Microsoft-BthPan\patched\bthpan\bthpan.inf
# - inf2cat catalog targets Server2025_X64 + ServerFE_X64 + ServerRS5_X64 + Server2016_X64
# Stage 2: full install
.\Deploy-MSBthPanInboxOnWindowsServer.ps1 -Action Install
# Expected I03 output:
# pnputil /add-driver bthpan.inf /install -> exit=0 (or 3010 if reboot needed)
# pnputil /scan-devices -> exit=0
# Expected I04 output:
# [OK] TRUE resolution: oem*.inf bound, Class=Net, Service=BthPan
# *** TRUE RESOLUTION ACHIEVED ***
# If I04 reports `*** TRUE RESOLUTION NOT YET ACHIEVED ***`:
# Reboot, then re-run the same command. The script's resume-after-reboot
# logic should detect the now-correct binding and confirm true resolution.# Runtime artifacts
Test-Path C:\Windows\System32\drivers\bthpan.sys # expected: True
Get-Service BthPan # expected: present, Status=Running or Stopped
(Get-Service BthPan).StartType # expected: Manual (default)
# Device-level binding
$dev = Get-PnpDevice -InstanceId 'BTH\MS_BTHPAN*'
$dev | Get-PnpDeviceProperty -KeyName DEVPKEY_Device_DriverInfPath, DEVPKEY_Device_Class, DEVPKEY_Device_Service
# Expected:
# DriverInfPath = oem<N>.inf (e.g. oem17.inf)
# Class = Net
# Service = BthPan
# NetAdapter visibility
Get-NetAdapter | Where-Object InterfaceDescription -Match 'Bluetooth.*Personal Area Network'
# Expected: one NetAdapter present, MediaType=Bluetooth
# Self-signed catalog still trusted
signtool verify /pa /v C:\Temp\Workspace_Microsoft-BthPan\patched\bthpan\bthpan.cat
# Expected: "Successfully verified"
# WDAC supplemental policy active
CiTool --list-policies --json | ConvertFrom-Json |
Select-Object -ExpandProperty Policies |
Where-Object PolicyID -eq '{A6E72D4F-3B98-4C5A-9E1D-7F8B2A4C6E5D}'
# Expected: one Policy returned, IsActive=TrueThe validation run is considered PASS only if all of the following hold:
- P03 locates the DriverStore source without errors (
bthpan.inf_amd64_*directory exists) - P06 generates a patched bthpan.inf with at least one server decoration (
ServerDecCount >= 1) - P08 generates a signed catalog targeting all four Server SKUs
- I01 imports the cert into LocalMachine\Root + LocalMachine\TrustedPublisher without error
- I02 deploys the WDAC supplemental policy with the BthPan-specific GUID
A6E72D4F-… - I03 returns exit 0 (or 3010 with subsequent reboot)
- I04 reports
*** TRUE RESOLUTION ACHIEVED *** - Post-install verification commands in §4.4 all return their expected values
Once §4.5 PASS is achieved with the default Strategy A, the planned regression test sequence is:
- Strategy B run —
-DecorationStrategy B -CleanWorkRoot. Confirm that the patched INF gains four additionalNTamd64.10.0...XXXXXentries in[Manufacturer]and four corresponding mirrored InstallSection blocks. Confirm the same*** TRUE RESOLUTION ACHIEVED ***outcome. - Cleanup test —
-Action Cleanup. Confirm the workspace is removed, WDAC supplemental policy is uninstalled, and re-running V06 reports the system has returned to its pre-install state (Phantom OK or Unknown Device). - Resume-after-reboot test — simulate the I03 reboot scenario by running
-Action Installon a Phantom OK host where PnP does not immediately rebind. After reboot, re-run-Action Installand confirm the resume-after-reboot logic correctly detects the now-true-resolution state and reports cached/skip for I01/I02/I03 + still runs I04 for the verdict.
- How reliably does
pnputil /scan-devicescause an immediate rebind frombth.inf(Phantom proxy) to the patchedoem*.inf, vs requiring a reboot? - Are there any DEVPKEY values that differ between Strategy A and Strategy B-installed devices? (Expected: no — both should produce identical Class/Service/DriverInfPath, only the PnP ranking score differs.)
- Does Strategy B's per-build decoration actually improve PnP ranking over Strategy A, or is it functionally indistinguishable?
| Item | M75q Tiny Gen 2 | X13 Gen 1 AMD | Real NPU machine |
|---|---|---|---|
| Instance / model | ThinkCentre physical | ThinkPad physical | TBD |
| OS | WS2025 | Win11 LTSC 2024 | TBD |
| ProductType | 3 | 1 (PREVIEW MODE) | TBD |
| CPU | Ryzen 7 PRO 5750GE (Cezanne) | Ryzen 5 PRO 4650U (Renoir) | Ryzen AI 300 / 7040 / 8040 |
| Has NPU | no | no | yes |
| Chipset INFs processed | 32/32 + 3 V06 upgrades | 32/32 + 1 V06 upgrade | n/a (out of scope for NPU script) |
| Graphics INFs processed | 19/19 + 3 V06 upgrades | 19/19 + 3 V06 upgrades | n/a (out of scope for NPU script) |
| NPU script PrepareVerify | n/a (no NPU device) | n/a (no NPU device) | PENDING |
| NPU script Install | n/a | n/a (auto-block) | PENDING |
| Validation purpose | Pre-production rehearsal (chipset+graphics) | WS2025 pre-migration check | NPU end-to-end validation |
| Scenario | Recommended environment |
|---|---|
| "Real driver install validation" (chipset/graphics) | M75q Gen 2 physical (production target) |
| "Win11 → WS2025 pre-migration evaluation" (chipset/graphics) | X13 Gen 1 physical |
| "NPU end-to-end validation" | Ryzen AI 300 / 7040 / 8040 series host (NOT YET IN MAINTAINER'S LAB — PRs welcome) |
Why no non-AMD-consumer-hardware testing is documented: This pipeline is an experimental tool for AMD's consumer Ryzen / Radeon / NPU silicon. Validation outcomes are by definition dependent on physical access to those devices. Running the pipeline on server-class EPYC, ARM, Intel, or virtual hosts cannot exercise the device-bind logic (V06), the actual driver upgrade decisions, or the post-install verification path (I04). The maintainers have concluded that "pipeline-soundness only" testing on non-target hardware adds little value relative to the cost of maintaining such infrastructure, and have therefore restricted validation to physical AMD consumer hardware.
The complete per-bug discovery-and-fix history is consolidated in
CHANGELOG.md, under "Discovered bugs and fix history
(validation-discovered)". That table maps each validation-discovered bug
to the script revision where it was found and the revision where it was
fixed, with cross-references to the relevant SPEC.md Part D section for
root-cause analysis.
For full validation logs and the corresponding fix commits, see https://github.com/usui-tk/Deploy-Drivers-For-WindowsServer/commits/main.
This is the per-script validation checklist for the cross-script UEFI Secure Boot baseline feature. All three sister scripts share the same six core functions, so the expected output is uniform across them. Validate on at least one Windows Server 2025 host with KB5089549-equivalent updates installed.
| Phase | Expected | Actual on test host |
|---|---|---|
| P00 | One-line compact: Secure Boot baseline: enabled=true UEFI-CA-2023=NotStarted health=Warning [MS-sample=ok] (values vary by host state) |
✅ |
| P05 | New file <WorkRoot>\inf_inventory_report.txt exists and ends with a "UEFI Secure Boot Baseline" appendix block (chipset / graphics: as section after the INF inventory; NPU: at end after the inline inventory) |
✅ |
| V05 | New section: [Dry-Run UEFI Baseline] heading followed by one-line compact readout. If Health is Warning or Critical, a yellow advisory line follows |
✅ |
| V06 | New numbered section: "4. UEFI Secure Boot Baseline" (chipset / graphics) or "Section 5: UEFI Secure Boot Baseline" (NPU). Multi-line breakdown showing embedded inventory + MS sample script results (BucketId / Confidence / EventNNNN counts) | ✅ |
| I02 | New pre-check block: --- UEFI Secure Boot baseline pre-check --- followed by compact readout and advisory. Never blocks. |
(Install phase — run separately) |
| Artefact | Expected location | Purpose |
|---|---|---|
| Raw stdout dump | <WorkRoot>\secureboot_ms_sample\detect_stdout.log |
Forensics when MS sample script behaves unexpectedly |
| Extracted JSON | <WorkRoot>\secureboot_ms_sample\detect_stdout_extracted.json |
Parsed Hostname, UEFICA2023Status, BucketId, Confidence, Event1801..1803 |
| Inventory report appendix | <WorkRoot>\inf_inventory_report.txt |
Persisted snapshot for change-management documentation |
Notes:
- The MS sample script is delivered by KB5089549 (Win 11), KB5087544 / KB5088863 (Win 10), or the WS2025 equivalent (starting 2026-05-12). On unpatched hosts,
[MS-sample=absent]is expected instead of[MS-sample=ok]. - The diagnostic files survive across runs unless
-CleanWorkRootis passed.
| Host state | Expected health= value |
|---|---|
Secure Boot ON, UEFICA2023Status = Updated (KB rollout complete) |
Healthy |
Secure Boot ON, UEFICA2023Status = NotStarted / Started / Pending |
Warning |
| Secure Boot OFF | Critical |
UEFICA2023Error non-zero |
Critical |
| Secure Boot status unreadable (some firmware quirks) | Unknown |
Run all four scripts in PrepareVerify mode on the same host with -CleanWorkRoot. The captured BucketId, Confidence, and event counts in V06 should be identical across all four scripts (the MS sample script returns deterministic results for the same host state).
This section documents the expected diagnostic output and the validation procedure for AMD's two-layer Chipset Software 8.x (8.02.18.557 and later) extraction path. The extraction strategy and its historical evolution are described in SPEC §D.12; the revision in which this strategy was introduced is logged in CHANGELOG.md.
AMD Chipset Software 8.x ships as a two-layer wrapper:
- Outer layer: NSIS self-extracting EXE (7-Zip can extract this).
- Inner layer: InstallShield SFX in
ISSetupStreamformat (7-Zip CANNOT extract; only InstallShield's own/aadmin install can).
Earlier revisions detected the 7-Zip failure on the inner layer and fell back to launching the installer and harvesting from C:\AMD\, which is fragile because AMD aggressively cleans up that directory. The current pipeline inserts a dedicated InstallShield-aware strategy between the old 7-Zip strategy and the launch-watch fallback.
See SPEC.md §B.1 "AMD 8.x installer architecture" for the full architecture.
When the installer is AMD 8.x, P04 console output should look approximately like the following (truncated for readability):
[*] Phase 04 : P04 ExtractInstaller (Build group)
[*] Extracting installer (multiple strategies will be attempted)
Strategy 1/3: 7-Zip auto-detect
[!] 7-Zip auto-detect produced no usable payload (exit 0) - trying next strategy
Strategy 2/3: InstallShield /a admin install (AMD 8.x+ chain)
Step 1/3: 7-Zip outer NSIS shell...
Inner SFX : C:\Temp\Workspace_AMD-Chipset\is-stage-nsis\AMD_Chipset_Drivers.exe (75.3 MB)
Step 2/3: InstallShield /a admin install...
Unpacked : 36 MSI files (InstallShield exit 0)
Step 3/3: msiexec /a on 36 sub-MSI(s)...
msiexec /a : 35 succeeded, 1 failed
INF total : 96
[PREFERRED] W11x64 : 32 INF(s)
[ skip ] WTx64 : 32 INF(s)
[ skip ] WTx86 : 32 INF(s)
[+] Extracted via InstallShield admin install chain
[+] Extracted to: C:\Temp\Workspace_AMD-Chipset\extract
When the new path runs successfully, all of these should hold:
| Check | Expected value | How to verify |
|---|---|---|
| InstallShield exit code | 0 (best) or 1 (acceptable if MSI count is correct) |
Console line Unpacked : NN MSI files (InstallShield exit X) |
| MSI count | >= 36 (1 parent + 35 sub-MSIs for 8.02.18.557; future versions may differ) |
Same console line |
| msiexec /a success rate | >= 30 of 36 |
Console line msiexec /a : NN succeeded, M failed |
| INF total | >= 80 (varies with version; usually 96 in 8.02.18.557) |
Console line INF total : NN |
| PREFERRED variant has non-zero INFs | [PREFERRED] <variant> : >= 25 INF(s) |
Console line; this is the critical signal |
| PREFERRED variant matches host OS | W11x64 on WS2022/WS2025; WTx64 on WS2016/WS2019 |
Cross-check $Ctx.Os from console banner |
If the PREFERRED variant shows 0 INF(s) despite the extraction succeeding, the most likely causes are:
-
InstallShield /a failed silently: Check
C:\Temp\Workspace_AMD-Chipset\installshield-admin.logfor MSI errors during the admin install. Look forAction ended ...lines with non-zero return values. -
msiexec /a failed for the OS-variant sub-MSIs: Check
C:\Temp\Workspace_AMD-Chipset\msiexec-admin-*.logfor the specific failing sub-MSIs. Each sub-MSI has its own log named after the MSI filename. -
AMD changed the directory layout in a future version: If you are running against a Chipset Software version newer than 8.02.18.557 and the
Binaries\<DriverName>\<OS>\structure changed, theGet-AmdSourceVariantclassifier (script line ~5003) may need updating. File a GitHub issue with the directory tree underC:\Temp\Workspace_AMD-Chipset\extract\.
If Strategy 2 fails for any reason (caught by the try { ... } catch block in Expand-AmdInstaller), the script falls through to Strategy 3/3 (launch + watch), preserving the legacy behaviour from earlier revisions. The console output in that case will be:
[!] InstallShield /a strategy failed: <error message>
Strategy 3/3: launch installer and harvest from C:\AMD\
This is the legacy fallback path used by earlier revisions and should be considered a regression fallback only.
These regression scenarios validate the three operational fixes for the CiTool interactive-prompt, ja-JP UTF-8 console encoding, and the pnputil exit=259 reclassification (root causes in SPEC §D.5 / §D.16 / §D.17; release information in CHANGELOG.md). All three can be exercised on the same WS2025 install used for §1 (M75q Tiny Gen 2) without re-imaging.
Pre-fix symptom: I02 stalls ~60-75 s between the two log lines below; pressing ENTER in the active console resumes the script:
[*] Converting XML to .cip binary and deploying to active CI policies...
[+] Deployed: ...
Regression test: After running -Action Install -OnlyPhases I02 with the new revision:
| Observation | Pre-fix | Post-fix |
|---|---|---|
| Wall-clock elapsed for I02 | 60-75 s with ENTER input ~mid-phase | < 10 s end-to-end, no input required |
| Stdin requirement | Operator must press ENTER once per CiTool invocation (I02 + Cleanup) | No stdin interaction |
| CiTool stdout in log | 処理は成功しました\n続行するには、Enter キーを押してください (literal) OR mojibake under cp932 |
Clean JSON envelope, no "Press Enter" line |
Pass criterion: I02 completes without any stdin interaction; the operator can walk away from the console.
Verification commands (operator can run in any elevated PS console):
# This is a SAFE no-input test: CiTool --list-policies --json prints JSON and exits
# WITHOUT the "Press Enter to Exit" prompt. Without --json, it prints the prompt
# and blocks on stdin.
& CiTool.exe --list-policies --json | ConvertFrom-Json | Select-Object -First 3If this returns control to the prompt immediately, the --json mechanism is functioning on this host.
Pre-fix symptom: I02 log line reads:
CiTool: 蜃ヲ逅・・謌仙粥縺励∪縺励◆
(The UTF-8 byte sequence of 処理が成功しました decoded as cp932.)
Regression test: With the fixed revision, the same line reads:
CiTool: 処理は成功しました
OR (when the CiTool --json parse extracts the canonical OperationResult):
CiTool: Success
Pass criterion: No CJK mojibake in any CiTool, signtool, or pnputil stdout captured in the run log.
Verification commands:
# (a) Confirm the three encodings are UTF-8 after P00 has run.
# Run AFTER any phase of the script has executed.
[Console]::OutputEncoding.WebName # expected: utf-8
[Console]::InputEncoding.WebName # expected: utf-8
$OutputEncoding.WebName # expected: utf-8
# (b) Confirm CiTool's ja-JP stdout decodes correctly.
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$stdout = & CiTool.exe --list-policies --json 2>&1 | Out-String
$stdout | Select-String '"OperationResult"' -CaseSensitive
# Expected: a line like "OperationResult": "Success"
# NOT mojibake.Pre-fix symptom (chipset): On a clean WS2025 install, I03 final summary reports:
Driver install: 52 ok (2 need reboot) / 3 failed / 0 skipped (current newer)
but I04 PostInstallVerification immediately reports FAILED: 0. The three "failed" cases were duplicate-source INFs (SMBUSamd.inf, AMDInterface.inf, AmdMicroPEP.inf) where the second invocation returned exit=259 because the driver package was already in the store.
Regression test: With the fixed revision, the same I03 run reports:
Driver install: 52 ok (2 need reboot, 3 no-op) / 0 failed / 0 skipped (current newer)
And the I03 per-INF lines previously rendered as [!] exit=259 (see ...) now render as [~] no-op (driver store already up-to-date).
Pass criterion:
- I03 failure count is 0 on a clean install (modulo any genuine pnputil errors).
- I04
FAILEDcount matches I03failedcount (both should be 0 or both should be the same non-zero number). - Devices that earlier showed under both "I03: 3 failed" AND "I04: REBOOT_NEEDED" now show only under "I04: REBOOT_NEEDED" with the corresponding I03 entries marked
no-op.
Verification command (post-install state inspection):
# Compare I03 install result count vs I04 device classification
# Read the persisted I03 results
$ws = 'C:\Temp\Workspace_AMD-Chipset'
# I03 writes to install_results.csv if Export-Csv is wired in (otherwise check console log)
# Easier: re-run the script and compare summary line vs Section 1 of I04.
.\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action Install -OnlyPhases I04
# Expected: "FAILED : 0 device(s)" and no devices in the [FAILED] sub-list.When validating these fixes on the M75q Tiny Gen 2 or X13 Gen 1 AMD fixtures:
| # | Check | Pass criterion |
|---|---|---|
| 1 | Banner shows the script version string (e.g., chipset-YYYY.MM.DD-rNN) at script startup |
✓ correct version string |
| 2 | P00 log emits [~] Console encoding set to UTF-8 (NPU only) or simply does not display mojibake later |
✓ no cp932 indicator in CiTool output |
| 3 | I02 completes in < 10 s WITHOUT operator stdin input | ✓ no hang at "Converting XML to .cip binary..." |
| 4 | I02 final line includes Activation method: CiTool (immediate, no reboot) rendered via Write-Detail (4-space indent, Gray) |
✓ visually subordinate to the preceding [+] Deployed: marker line |
| 5 | I03 final summary line includes a , N no-op segment for chipset / graphics |
✓ matches the new 5-tuple format |
| 6 | I04 FAILED count = I03 failed count |
✓ both 0 on the clean-install scenario |
| 7 | All ja-JP strings in the log are readable (no 蜃ヲ, 謌仙 etc.) |
✓ no mojibake |
These regression scenarios validate the nine enhancements bundled in the
detection-accuracy-multi-os release (root causes in SPEC §D.18 / §D.18b
/ §D.18c / §D.18d / §D.19 / §D.20 / §D.21 / §D.22 / §D.22b; release information
in CHANGELOG.md). The scenarios are organized by feature;
each can be exercised independently on the same WS2025 install used for §1
(M75q Tiny Gen 2) without re-imaging.
Pre-fix symptom: On the first full Install pass on a clean WS2025 host, I00's TO-BE display incorrectly labels the script's own self-signed catalogs as category=Vendor, causing the priority override in SPEC D.15 to pick the wrong INF for binding.
Regression test: After running -Action Install -OnlyPhases I00,I02,I03 with the new revision on a host with a fresh certificate / WDAC policy:
| Observation | Pre-fix | Post-fix |
|---|---|---|
| TO-BE category for AMD INFs after I02 activates the supplemental policy | [B] Vendor |
[C] Self-Signed (catalog thumbprint match) |
| Source of classification | Step 1 string-match (failed → fell through to Step 2 Provider match) | Step 0 catalog thumbprint match |
| Decision matrix outcome | Wrong INF chosen for some devices | Correct INF chosen |
Pass criterion: I00 reports category=[C] Self-Signed (this script, catalog thumbprint match) (note the explicit "catalog thumbprint match" suffix that distinguishes Step 0 from the legacy Step 1 path) for every INF that was signed by $Ctx.CertThumbprint in I02. No INF that was signed by the script is misclassified as [B] Vendor on second-pass run.
Verification commands:
# Inspect the I00 detail log for the classification label.
# After running -Action Install at least once:
Select-String -Path "$env:ProgramData\Deploy-Drivers-For-WindowsServer\logs\*.log" `
-Pattern 'catalog thumbprint match|Self-Signed \(this script' |
Select-Object -Last 20The log should show every AMD INF (Chipset and Graphics) classified with the "catalog thumbprint match" label suffix once I02 has populated the policy. The Step-0 label is Self-Signed (this script, catalog thumbprint match); the legacy Step-1 label is Self-Signed (this script) (no suffix). Either is a valid [C] classification; Step-0 is preferred because it is independent of the WMI Signer field (which may be empty for self-signed catalogs).
Pre-fix symptom on Japanese WS2025: I04 OverallResult = PartialOrPhantom, script requests reboot, but PAN connectivity is already functional and Bluetooth デバイス (パーソナル エリア ネットワーク) appears in ncpa.cpl.
Regression test: After running -Action Install -OnlyPhases I00,I01,I02,I03,I04 with the new BthPan revision on a Japanese WS2025 host:
| Observation | Pre-fix | Post-fix |
|---|---|---|
$Ctx.I04OverallResult on Japanese WS2025 |
PartialOrPhantom |
TrueResolution |
| Reboot request | Yes (spurious) | No |
Test-BthPanRuntimeArtifacts.HasNetAdapter |
$false (regex failed) |
$true (language-independent match) |
Get-BthPanNetChildBinding invoked |
(helper does not exist) | Yes; returns Net-class child with IsSignedByUs=$true |
Invoke-InstPhase04 Section 1 display |
parent BTH\MS_BTHPAN\* only |
parent + Net-child binding sub-block |
Pass criterion: I04 OverallResult = TrueResolution on every Japanese-locale WS2025 / WS2022 host where bthpan.sys is loaded and the catalog signature matches $Ctx.CertThumbprint.
Verification commands (must run on a Japanese WS2025 host with the script's WDAC policy active):
# (a) Confirm the language-independent Net-adapter detection works.
Get-NetAdapter | Where-Object {
$_.DriverFileName -ieq 'bthpan.sys' -or
$_.ComponentID -ieq 'ms_bthpan' -or
$_.PnPDeviceID -match '^BTH\\MS_BTHPAN(?:XFER)?\\'
} | Format-List Name, InterfaceDescription, DriverFileName, ComponentID, PnPDeviceID
# (b) On a Japanese SKU, InterfaceDescription will contain hiragana/katakana,
# but the three property fields above will still be in English. THIS IS THE POINT.If (a) returns at least one adapter and the three matched fields are visibly English while InterfaceDescription contains Japanese characters, the language-independence design is functioning as specified.
Pre-fix symptom: I00 prints ~1000 visually-identical TO-BE rows per Graphics device, and Risk Summary reports [MEDIUM] 1069 item(s) for a single AMD u0197843.inf match.
Regression test: On the M75q Tiny Gen 2 host (or any Phoenix-class device matched by u0197843.inf):
| Observation | Pre-fix | Post-fix |
|---|---|---|
| TO-BE rows per Graphics device | ~5046 | 1 (with [+5046 HWID variants] suffix) |
Risk Summary [MEDIUM] count |
1069 items | ~5 items |
| Visual scan time to review I00 output | minutes | seconds |
Pass criterion: TO-BE display shows one row per unique (InfName, SrcSubDir) pair. Risk Summary [MEDIUM] count reflects the number of actual replacement decisions, not HWID-variant impressions.
Verification commands:
# Inspect the I00 output count for a Graphics-only run.
$logFile = Get-ChildItem "$env:ProgramData\Deploy-Drivers-For-WindowsServer\logs\graphics-*.log" |
Sort-Object LastWriteTime -Descending | Select-Object -First 1
# Expect one TO-BE row per (InfName, SrcSubDir) with [+N HWID variants] suffix when N>1
Select-String -Path $logFile.FullName -Pattern 'TO-BE:.*\[\+\d+ HWID variants\]' |
Measure-Object | Select-Object -ExpandProperty CountPre-fix symptom: Sub-MSI failures in the P04 Nested loop are silently recovered (correct behaviour), but no breadcrumb is left if the parent EXE ultimately reports a payload-missing condition after Nested recovery succeeds.
Regression test: This is a diagnostics-only feature; normal runs produce no observable change. Forced regression test:
- Run
-Action Install -OnlyPhases P04on a clean WS2025 host with the AMD Chipset 8.x payload. - Mid-run (after the first MSI extraction), rename one of the
.cabfiles in%TEMP%\AMD\*\to provoke MSI error 1335 ("corrupt cabinet"). - The Nested loop retries and succeeds (because the original cab is reconstructed by AMD's installer on retry).
| Observation | Pre-fix | Post-fix |
|---|---|---|
$logRoot\submsi-failures-diag.txt exists |
No (file not created) | Yes (≥ 1 sub-MSI failure was captured) |
| Pattern classification in the diag file | (file absent) | 1335 corrupt cabinet at least once |
| TARGETDIR snapshot at failure time | (file absent) | Exists=True, InfCount=N, FileCount=M, LastWriteHint=... |
| User-visible P04 outcome | success (parent EXE recovered) |
success (unchanged) |
Pass criterion: submsi-failures-diag.txt is created and contains the pattern classification when sub-MSI failures occurred. The file is NOT created on clean runs with no sub-MSI failures (zero-noise default).
E-1 — I05 ForceRebind regression test (BthPan-only):
This phase activates ONLY when $Ctx.I04OverallResult -eq 'PartialOrPhantom'. Force-induced regression:
- On a known-good WS2025 host with bthpan working, run
pnputil /delete-driver oem<N>.inf /uninstall /forceto manually break the binding (where<N>is the OEM number of the patched bthpan.inf). - Run
-Action Install -OnlyPhases I04,I05.
| Observation | Without I05 | With I05 |
|---|---|---|
| I04 verdict | PartialOrPhantom |
PartialOrPhantom (initially) |
| I05 invoked | (phase does not exist) | Yes |
| I05 cascade attempts | (n/a) | Attempt 1 (Restart-PnpDevice) succeeds on WS2025 |
| I04 verdict after I05 promotion | PartialOrPhantom |
TrueResolution (promoted by I05) |
| Reboot required | Yes | No |
$Ctx.I05OverallResult |
(field does not exist) | Recovered |
Pass criterion: After I05, $Ctx.I04OverallResult is TrueResolution and no reboot is requested. The cascade attempt that succeeded is logged in $Ctx.I05PerDeviceResults.
I05 no-op test (on a clean working WS2025 host without breakage):
| Observation | Expected |
|---|---|
| I05 phase header printed | Yes |
| Cascade attempts run | 0 (short-circuited by I04OverallResult -eq 'TrueResolution') |
$Ctx.I05OverallResult |
$null (no-op) |
| Run-time impact | < 1 s |
E-2 — WS2019 CIM bridge regression test (all four scripts):
This regression requires a WS2019 host (the CIM bridge is only activated when CiTool.exe is absent, i.e., on WS2019 and WS2016).
| OS | CiTool.exe |
PS_UpdateAndCompareCIPolicy |
Expected ActivationMethod |
|---|---|---|---|
| WS2025 (build 26100) | present | (skipped — CiTool already succeeded) | CiTool (immediate, no reboot) |
| WS2022 (build 20348) | present | (skipped) | CiTool (immediate, no reboot) |
| WS2019 (build 17763) | absent | present | CIM bridge (PS_UpdateAndCompareCIPolicy, no reboot) |
| WS2016 (build 14393) | absent | absent (class missing) | reboot (existing behaviour) |
Pass criterion (WS2019 host):
Install-AmdWdacPolicy/Install-MsBthPanWdacPolicy/Install-WdacPolicyreturnsRebootRequired=$falseANDActivationMethod='CIM bridge (PS_UpdateAndCompareCIPolicy, no reboot)'.- Subsequent I03 verifies the supplemental policy is active (queryable via
Get-WmiObject -Namespace 'root\Microsoft\Windows\CI' -Class PS_QueryDeviceGuardStatus).
Pass criterion (WS2016 host):
- The CIM bridge attempt fails silently (
$cimBridgeErroris populated with "class not found" or equivalent). ActivationMethod='reboot'is selected.-UseTestSigningswitch is the supported activation path on WS2016 and produces an explicit reboot request.
Pre-fix symptom: I05 raises ParameterArgumentValidationError on the two early-return paths (I04OverallResult is TrueResolution / NoDevice, or Get-MsBthPanDevice returns empty) because Write-PhaseFooter 'I05' 'no-op' is rejected — 'no-op' is not in the [ValidateSet('done','cached','skipped','failed')] allowed values.
Regression test: Run on a Japanese WS2022 / WS2025 host where bthpan is in a clean state (no phantom Net adapter to rebind):
.\Deploy-MSBthPanInboxOnWindowsServer.ps1 -Action Install -OnlyPhases I04,I05| Observation | Pre-fix | Post-fix |
|---|---|---|
I05 ends with footer Write-PhaseFooter 'I05' 'skipped' |
✗ throws ParameterArgumentValidationError on 'no-op' |
✓ accepts 'skipped' |
| Pipeline exit code from I05 phase | non-zero (PowerShell error) | 0 (clean exit) |
| Debug-trace JSONL record | missing status field on I05 record |
`{"phase":"I05","status":"skipped","reason":"TrueResolution |
User-visible Write-Skip line |
"no-op" wording preserved | "no-op" wording preserved |
Pass criterion:
- The three early-return paths all emit
Write-PhaseFooter 'I05' 'skipped':I04OverallResultis null (existing — unchanged)I04OverallResultisTrueResolutionorNoDevice(fixed)Get-MsBthPanDevicereturns empty (fixed)
- The successful-rebind path still emits
Write-PhaseFooter 'I05' 'done'(unchanged). - No ParameterArgumentValidationError appears in the console log.
Verification commands:
# Pattern-match every Write-PhaseFooter 'I05' callsite to confirm valid Status tokens.
Select-String -Path Deploy-MSBthPanInboxOnWindowsServer.ps1 `
-Pattern "Write-PhaseFooter 'I05'" |
ForEach-Object {
if ($_.Line -match "Write-PhaseFooter 'I05' '(done|cached|skipped|failed)'") {
"L$($_.LineNumber): OK ($($matches[1]))"
} else {
"L$($_.LineNumber): FAIL ($($_.Line.Trim()))"
}
}
# Expected: 3 'skipped' + 1 'done' = 4 OK lines, 0 FAIL lines.10.5c Chipset / Graphics I04 classification + disposition robustness (SPEC §D.18b / §D.18c / §D.18d)
Pre-fix symptoms (operator log, Japanese WS2022 Datacenter, build 20348):
[LOADED]row showsAFTER: [B](Vendor) for a device that was just bound to our self-signed driver (e.g.,AMD Radeon(TM) Graphics).[REBOOT_NEEDED]count exceeds I03's actual "reboot required" count (e.g., I03 =1 reboot requiredbut I04 =5 REBOOT_NEEDED).[REBOOT_NEEDED]rows render uninformative lines likeStill on v, new INF queued: (none)for devices whose previous binding had an empty version field.
Regression test (chipset; analogous on graphics): Run -Action Install -OnlyPhases I00,I01,I02,I03,I04 on a Japanese WS2022 / WS2025 host. Then inspect the I04 output:
| Observation | Pre-fix | Post-fix |
|---|---|---|
I04 builds $ourInfSet via Get-OurSignedOemInfSet -ExpectedThumbprint $Ctx.CertThumbprint |
(not built) | Known signed-by-us INF/CAT name(s): <N> (N ≥ 1 after I03 installs) |
Get-DriverSourceCategory called with -KnownOurInfSet $ourInfSet for both AS-IS and AFTER classification |
(not passed) | Both calls receive the parameter |
[LOADED] AFTER category for self-signed-by-us drivers |
sometimes [B] Vendor |
always [C] Self-Signed |
| Disposition decision when OS reports our InfName + same DriverVersion | conservative fallback → REBOOT_NEEDED |
new branch → LOADED |
[REBOOT_NEEDED] device count vs I03's reboot-required count |
I04 > I03 (over-counting) | I04 = I03 (matching) |
[REBOOT_NEEDED] display: empty Before.DriverVersion |
renders Still on v, (no value) |
renders Still on v(unknown), |
[REBOOT_NEEDED] display: null Candidate |
renders new INF queued: (none) |
renders new INF queued: (OS-bound: oemNN.inf) |
Pass criteria:
- All AFTER-categories for self-signed-by-us drivers report
[C]in the I04[LOADED]block. - I04
REBOOT_NEEDEDdevice count equals I03's reboot-required INF count (within ±1 for race conditions in pnputil's status reporting). - No
[REBOOT_NEEDED]row containsStill on v,(empty version field afterv) or(none)when the OS knows the bound INF.
PSA8001 invariant check (must pass on both Chipset and Graphics):
# Get-OurSignedOemInfSet must be byte-identical across Chipset + Graphics.
diff <(sed -n '/^function Get-OurSignedOemInfSet/,/^}$/p' Deploy-AMDChipsetDriverOnWindowsServer.ps1) \
<(sed -n '/^function Get-OurSignedOemInfSet/,/^}$/p' Deploy-AMDGraphicsDriverOnWindowsServer.ps1)
# Expected: no output (zero diff).
# Get-DriverSourceCategory must remain byte-identical after the Step 0b extension.
diff <(sed -n '/^function Get-DriverSourceCategory/,/^}$/p' Deploy-AMDChipsetDriverOnWindowsServer.ps1) \
<(sed -n '/^function Get-DriverSourceCategory/,/^}$/p' Deploy-AMDGraphicsDriverOnWindowsServer.ps1)
# Expected: no output (zero diff).Pre-fix symptom (r64): P08 reports Catalog generation: 59 ok / 1 failed (using /os:ServerRS5_X64) on AMD Chipset Software 8.05.04.516 against Renoir + WS2019. The single failure is Chipset_Software_CIR_Driver_WTx64 with inf2cat error 22.9.1: amdcir.sys ... is missing or cannot be decompressed. P04's submsi-failures-diag.txt classifies all 12 sub-MSI failures as unknown.
Regression test (r65) — natural reproduction on the same environment:
- WS2019 host (build 17763), AMD Ryzen 5 PRO 4650U (Renoir) or any AMD platform that does not include a Consumer Infrared device.
- Run
.\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action PrepareVerify -CleanWorkRoot.
| Observation | Pre-fix (r64) | Post-fix (r65) |
|---|---|---|
P04 submsi-failures-diag.txt Failure pattern frequency |
12 x unknown |
12 x 1603: SECREPAIR missing source files (AMD MSI packaging defect; ...) |
| P05 console output for the CIR Driver | (not surfaced) | [!] INFs ineligible for catalog generation (phantom file references): 1 block listing AMDCIR.inf with missing: AMDCIR.sys |
P05 inf_inventory.csv new columns |
absent | ReferencedFilesCount, MissingReferencedFiles, EligibleForCatalog |
| P05 phase marker metadata | {Total, Selected, CsvPath, ReportPath, Variants} |
adds Ineligible |
| P06 console output | (not surfaced) | Note: 1 INF(s) will be copied for traceability but skipped at P08 ... listing AMDCIR.inf |
| P08 console output | Generating catalogs for 60 INF folder(s) then inf2cat: [WTx64] Chipset_Software_CIR_Driver_WTx64 [!] FAILED (exit=-2) |
[~] Skipping 1 INF folder(s) due to phantom file references (SPEC D.24): block listing the CIR Driver, then Generating catalogs for 59 INF folder(s) |
| P08 summary line | Catalog generation: 59 ok / 1 failed (using /os:ServerRS5_X64) |
Catalog generation: 59 ok / 0 failed / 1 skipped (using /os:ServerRS5_X64) |
| P08 phase marker metadata | {Ok, Failed, OsArg} |
adds Skipped |
| P09 signing count | 59 ok / 0 failed |
unchanged (59 ok / 0 failed) — P09 enumerates .cat files; the CIR Driver folder has no .cat so P09 has nothing to act on for it |
| V03 console output | (no notice) | [~] Not verifying 1 INF folder(s) - no .cat exists (skipped at P08; phantom file references, SPEC D.24) block listing Chipset_Software\CIR Driver\WTx64\AMDCIR.inf. The 59-catalog Verifying ... loop is unchanged. |
| V04 summary line | INF verification: 60 ok / 0 missing decoration |
INF verification: 59 ok / 0 missing decoration / 1 skipped plus a [~] block listing AMDCIR.inf |
| V05 I03 dry-run output | 60 INF(s) would be processed by 'pnputil /add-driver /install' (with AMDCIR.inf appearing in Group B "no matching device") |
[~] Excluding 1 INF(s) from dry-run plan ... block listing Chipset_Software\CIR Driver\WTx64\AMDCIR.inf, then 59 INF(s) would be processed by 'pnputil /add-driver /install' |
| V06 output | AMDCIR.inf is listed under section 2 "Devices with NO matching patched INF" (as a fallback bucket entry) |
[~] Excluding 1 ineligible INF(s) from TO-BE candidates (phantom file references, SPEC D.24): block at the top of V06 listing AMDCIR.inf. Section 2's enumeration is unchanged for all other INFs. |
I03 console output (when -Action Install is run) |
pnputil would attempt to install AMDCIR.inf; without a .cat it fails with 0x80004005 "the third-party INF does not contain digital signature information" |
[~] Excluding 1 ineligible INF(s) from install ... block listing AMDCIR.inf (with explanation "no .cat exists; would have failed pnputil signature check"). I03 then iterates only 59 INFs. |
Pass criterion:
- P05's
inf_inventory.csvrow forAMDCIR.infhasEligibleForCatalog=FalseandMissingReferencedFiles=AMDCIR.sys. - P08's tri-state summary line ends with
... / 1 skipped (using /os:ServerRS5_X64). - V04's tri-state summary line ends with
... / 1 skipped. - V05's dry-run install plan reports 59 INFs (not 60).
- V06's section 2 ("Devices with NO matching patched INF") no longer lists
AMDCIR.infas part of any device's TO-BE candidates. - I03's pnputil loop iterates 59 INFs and successfully completes without the
0x80004005signature failure on the CIR Driver. - Zero pipeline failures end-to-end (the original P08
1 failedis eliminated). patched\Chipset_Software\CIR Driver\WTx64\still containsAMDCIR.infandAMDCIR64.sys(copied by P06 for traceability) but no newly-generatedamdcir.cat(AMD's original 2015amdcir.catfrom the extracted tree is also not present inpatched/because P06 only copies the source tree, and the script idempotently cleans existing.catfrom each catalog target directory before inf2cat would have run; for the skipped directory, the cleanup step is itself skipped).
Verification commands:
# Verify the CSV column addition and ineligibility flagging.
$csv = Import-Csv 'C:\Temp\Workspace_AMD-Chipset\inf_inventory.csv'
$csv | Where-Object Inf -eq 'AMDCIR.inf' |
Select-Object Inf, SourceVariant, EligibleForCatalog, MissingReferencedFiles, ReferencedFilesCount
# Expected (Renoir + WS2019 + Chipset 8.05.04.516):
# Inf : AMDCIR.inf
# SourceVariant: WTx64
# EligibleForCatalog : False
# MissingReferencedFiles: AMDCIR.sys
# ReferencedFilesCount : 2
# Verify the sub-MSI pattern classifier picks up the SECREPAIR pattern.
$diag = Get-Content 'C:\Temp\Workspace_AMD-Chipset\logs\submsi-failures-diag.txt'
$diag | Select-String 'Failure pattern frequency' -Context 0,4
# Expected (post-fix):
# Failure pattern frequency:
# 12 x 1603: SECREPAIR missing source files (AMD MSI packaging defect; ...)No-op test on a platform without the defect (e.g. WS2025 + Phoenix Point with a newer Chipset Software version that doesn't include the dual-arch CIR Driver):
| Observation | Expected |
|---|---|
P05 [!] INFs ineligible ... block |
absent (no INFs flagged) |
| P05 inventory CSV new columns | present, all rows have EligibleForCatalog=True and empty MissingReferencedFiles |
| P06 phantom file notification | absent |
| P08 skip block | absent |
| P08 orphan-cleanup line (r66) | absent (no skip block to clean from) |
| P08 summary line | reverts to legacy two-state form Catalog generation: N ok / 0 failed (using /os:...) |
| P08 phase marker | includes Skipped=0 |
| P09 orphan-filter block (r66) | absent (no ineligible dirs to filter) |
| P09 summary line | reverts to legacy two-state form Signing: N ok / 0 failed |
| P09 phase marker (r66) | includes Skipped=0 |
| V01 catalog count | matches P08/P09 N (no orphan delta) |
| V03 skip notice | absent |
| V04 summary line | reverts to legacy two-state form INF verification: N ok / 0 missing decoration |
| V05 dry-run skip block | absent |
| V06 ineligible notice | absent |
| I03 ineligible-INF skip block | absent |
Pass criterion (no-op test): pipeline behavior is identical to r64 on this platform; no spurious skip messages or count changes. All r65/r66 code paths are guarded by Lookup.Count -gt 0 (V03/V04/V05/V06/I03) / $copyOnlyIneligible.Count -gt 0 (P06) / $ineligibleDirs.Count -gt 0 (P08) / $ineligibleDirSet.Count -gt 0 (P09), so on a clean package the modifications are byte-identical-to-r64 silent.
The r65 real-machine verification (2026-05-22, WS2019 + Renoir + Chipset 8.05.04.516) confirmed that P05/P06/P08/V03/V04/V05/V06/I03 all correctly skip ineligible INFs, but also surfaced a residual issue: P09 was enumerating Get-ChildItem -Recurse -Filter *.cat under patched/ and picking up 5 original AMD-shipped .cat files that P06 had transitively copied alongside the ineligible INFs. P09 re-signed them with the self-signed cert, so V01 reported Catalog files: 60 instead of 55, V03 verified 60 catalogs (5 of them orphans), and patched/ ended up with 5 unused but signed .cat artifacts.
r66 closes this gap with two cooperating defense layers (case alpha B+C). Test against the same 2026-05-22 reproducer workspace (or a fresh -CleanWorkRoot run):
| Observation | r65 actual (defect) | r66 expected (fixed) |
|---|---|---|
| P05 ineligible block | 5 INFs flagged | 5 INFs flagged (unchanged) |
| P06 copy-only notification | 5 INFs listed | 5 INFs listed (unchanged) |
| P08 skip block | 5 directories listed | 5 directories listed (unchanged) |
| P08 orphan-cleanup line | absent | Cleaned 5 orphan .cat file(s) from skipped directories (would otherwise be picked up by P09). |
| P08 summary | 55 ok / 0 failed / 5 skipped |
55 ok / 0 failed / 5 skipped (unchanged) |
| P09 enumeration count | 60 .cat enumerated | 55 .cat enumerated (orphans deleted at P08) |
| P09 filter block | absent | absent (Layer B left nothing for Layer C to filter) |
| P09 summary | Signing: 60 ok / 0 failed |
Signing: 55 ok / 0 failed |
| P09 phase marker | Ok=60, Failed=0 |
Ok=55, Failed=0, Skipped=0 |
| V01 catalog count | Catalog files: 60 |
Catalog files: 55 |
| V03 verifying count | 60 catalogs | 55 catalogs |
| V03 notice text | "no .cat exists" (inaccurate) | "no .cat exists" (now accurate) |
Pass criterion (r66 fix):
- P09 enumeration finds exactly
(eligible variant-selected INFs) - (decoration patches that consolidate identical files).catfiles; matches P08'sN okcount. - V01
Catalog files: Nequals P08'sN ok. - After re-running on the workspace, no orphan
.catremains in any directory listed in the P08 skip block. Verify with:
# Verify no orphan .cat survived in skipped directories.
$csv = Import-Csv 'C:\Temp\Workspace_AMD-Chipset\inf_inventory.csv'
$ineligibleDirs = $csv | Where-Object {
$_.EligibleForCatalog -eq 'False' -and $_.VariantSelected -eq 'True'
} | Select-Object -ExpandProperty RelativeDir
foreach ($d in $ineligibleDirs) {
$full = Join-Path 'C:\Temp\Workspace_AMD-Chipset\patched' $d
$orphans = @(Get-ChildItem -LiteralPath $full -Filter *.cat -File -ErrorAction SilentlyContinue)
Write-Host ('{0,-5} {1}' -f $orphans.Count, $d)
}
# Expected: all rows show 0 orphan .cat files.Standalone P09 test (Layer C exercise):
To confirm Layer C alone is sufficient when P08's cleanup is bypassed:
- Run a fresh
-Action PrepareVerify -CleanWorkRootto populatepatched/with the r66 expected state (55 catalogs). - Manually copy any 5 stray
.catfiles into the 5 ineligible directories (simulating an r65 workspace). - Run
-Action Prepare -OnlyPhases P09 -Force. - Expected: P09 prints
[~] Excluding 5 orphan .cat file(s) from signing ...block, signs 55, reportsSigning: 55 ok / 0 failed / 5 skipped. The orphans remain on disk (Layer C does not delete, only filters) but are never re-signed.
Cross-script Multi-OS capability matrix to validate when expanding from the current WS2025-only validation to WS2022 / WS2019 / WS2016:
| Capability | WS2025 (26100) | WS2022 (20348) | WS2019 (17763) | WS2016 (14393) |
|---|---|---|---|---|
CiTool.exe --json --update-policy |
✓ | ✓ | absent | absent |
PS_UpdateAndCompareCIPolicy CIM |
✓ (skipped) | ✓ (skipped) | ✓ | absent |
Restart-PnpDevice |
✓ | ✓ | ✓ | absent |
Disable-PnpDevice / Enable-PnpDevice |
✓ | ✓ | ✓ | absent |
pnputil /add-driver /install |
✓ | ✓ | ✓ | ✓ |
pnputil /remove-device /scan-devices |
✓ | ✓ | ✓ | ✓ |
Stop-Service / Start-Service BthPan |
✓ | ✓ | ✓ | ✓ |
BCDEdit testsigning + reboot (-UseTestSigning) |
✓ | ✓ | ✓ | ✓ |
inf2cat /os Server2025_X64 |
✓ | (fallback: ServerFE_X64) | (fallback: ServerRS5_X64) | (fallback: Server2016_X64) |
Current validation status:
- WS2025: validated on M75q Tiny Gen 2 + ThinkPad X13 Gen 1 AMD (proxy via Win11 LTSC).
- WS2022 / WS2019 / WS2016: capability matrix is derived from Microsoft documentation. Field validation is pending on real hardware.
For all four scripts, no production code path should match against InterfaceDescription, FriendlyName, Description, Name, or Caption for classification purposes. Manual audit command:
# Grep for the forbidden localized-string matches across all four scripts.
$forbidden = @(
'InterfaceDescription\s+-\s*(?:i?match|-i?eq|-i?like)',
'FriendlyName\s+-\s*(?:i?match|-i?eq|-i?like)',
'Description\s+-\s*(?:i?match|-i?like)' # 'Description -eq' is acceptable in some unit-test contexts
)
foreach ($f in @(
'Deploy-AMDChipsetDriverOnWindowsServer.ps1',
'Deploy-AMDGraphicsDriverOnWindowsServer.ps1',
'Deploy-AMDNpuDriverOnWindowsServer.ps1',
'Deploy-MSBthPanInboxOnWindowsServer.ps1'
)) {
foreach ($pat in $forbidden) {
$hits = Select-String -Path $f -Pattern $pat -CaseSensitive
if ($hits) {
Write-Warning "Potential localization-dependent match in ${f}:"
$hits | ForEach-Object { Write-Host (' L{0}: {1}' -f $_.LineNumber, $_.Line.Trim()) }
}
}
}Pass criterion: Zero hits. Any hit must be auditable (e.g., explicit comment noting that the matched string is hard-coded in English and not subject to localization on this code path, such as the inbox Microsoft provider strings used in V01 Secure Boot baseline classification).
This is a post-mortem case study, not a reproducible test scenario. The bench that surfaced this incident was retired to OS reinstall. The lessons documented here drive the bug fixes and design changes in Chipset r68 / Graphics r34 / BthPan r16 and the planned improvements tracked under SPEC §D.26.3 for r69/r35/r17.
| Attribute | Value |
|---|---|
| OS | Windows Server 2019 Datacenter, build 17763 |
| CPU | AMD Ryzen 5 PRO 4650U (Renoir) |
| Firmware | UEFI, GPT system disk |
| Secure Boot | ON (Healthy baseline; UEFI CA 2023 N/A; no MS sample script) |
| BitLocker | OFF |
| HVCI / VBS | OFF |
| Pre-existing drivers | Inbox display (display.inf), inbox AMD chipset stubs, inbox bthpan.inf (Phantom OK), no AMD Adrenalin, no AMD chipset software |
| Pipeline release | Chipset r67 / Graphics r33 / NPU r16 (not run) / BthPan r15 / WDAC SPF orchestrator r04 |
1. Chipset -Action Install -> reports success
2. Graphics -Action Install -> reports success (with internal inconsistencies, see below)
3. MSBthPan -Action Install -> reports I04 FAIL + I05 attempts all fail
4. Restart-Computer
5. Host fails to boot (normal mode, all Safe Mode variants, WinRE attempts)
Step 4 was the first reboot of the entire sequence. Steps 1–3 were run back-to-back with no reboot between scripts.
I02 deployed SPF policy successfully (State : None -> Ours-Healthy, WMI bridge activation, 3.57 s). I03 installed 55 INFs (1 reboot-required for AMD PSP, 2 no-op, 0 failed). I04 enumerated 42 AMD devices: 0 LOADED / 5 REBOOT_NEEDED / 0 KEPT_CURRENT / 37 UNCHANGED / 0 FAILED.
I02 reported [+] Legacy WDAC SPF policy is active. No reboot required (per WMI CIM bridge) — yet the I04 boot-signing table on the same run still printed:
Boot Signing : Firmware=UEFI ... SecureBoot=ON TestSigning=off HVCI=off WDAC-AMD=off
Self-signed driver : BLOCKED
[!] Self-signed driver loading is currently BLOCKED. ...
I04 Section 1 classified two devices as LOADED:
[LOADED] - new driver is active right now:
- AMD Audio CoProcessor
AS-IS: [?] v AFTER: [B] v6.0.1.85 INF: amdacpbus.inf
- AMD High Definition Audio Controller
AS-IS: [A] v10.0.17763.1 AFTER: [B] v10.0.0.35 INF:
I04 Section 2 (functional probe) then immediately reported the same two devices as [FAIL]:
[FAIL] AMD Audio CoProcessor
[x] PnP status OK : Error
[x] ConfigCode = 0 : CM_PROB_DRIVER_FAILED_LOAD
[x] Service running : amdacpbus -> Stopped
[FAIL] AMD High Definition Audio Controller
[x] PnP status OK : Error
[x] ConfigCode = 0 : CM_PROB_NEED_RESTART
[x] Service running : AMDHDAudBusService -> Stopped
The script then placed Microsoft 基本ディスプレイ アダプター (the Microsoft Basic Display Adapter) in REBOOT_NEEDED, queued for replacement by u0201039.inf (the AMD Adrenalin display driver with 1066+ HWID variants), and exited successfully.
I02 again reported SPF active. I03 installed bthpan.inf. I04 found BTH\MS_BTHPAN\7&1F82E917&0&2 in Unknown state (PnP Status: Error, Class: blank, Service: blank, DriverInfPath: blank — driver bind had not occurred). I05 cascaded through Attempts 1–4 and failed all of them, including the Attempt 3 Start-Process validator failure documented in SPEC §D.26.1.C.
The host did not present any visible boot progress on the next start, and did not respond to F8 / Shift+F8 / repeated power cycles intended to trigger automatic WinRE. Boot from installation media presented WinRE but dism /image:C:\ /cleanup-image /revertpendingactions did not restore boot. The host was added to the reinstall queue.
The most plausible chain of causation, listed in order from most likely to least:
- Display driver replacement on a single-display-path host with Secure Boot enforcement. The
display.inf -> u0201039.infswap installed a brand-new self-signed display driver whose catalog had to pass kernel CI at the boot loader's evaluation point. If the boot loader did not accept the SPF policy as authorizing that specific catalog (for any reason — Option 6 / Option 10 not actually set in the deployed policy, signature timestamp issue, etc.), the kernel falls back to Basic Display only IF that driver itself is still loadable; on a system where the Basic Display Adapter has already been visibly replaced in PnP, the fallback may not happen, leaving the host with no display path. - AMD PSP driver replacement on a host that may have firmware-level expectations on PSP behaviour. r67 already warns about this in the BitLocker context; BitLocker was off on this bench, but a PSP rejection at boot can still freeze the system before display init.
- Cumulative kernel-mode driver surface from three concurrent Installs. Even individually-safe driver replacements can interact at boot — three new self-signed catalogs simultaneously evaluated against a SPF policy that has authorized three different certs is not a code path the orchestrator's pilot validation exercised in isolation.
- WDAC SPF policy regressions across re-deploys. Each driver script's I02 invokes the orchestrator's
AddCertaction, which rewrites the SPF policy with the accumulated cert list. If any of the three I02 invocations produced a policy missing Option 10 (Boot Audit on Failure), the host has no audit fallback at boot.
None of these is individually confirmable without forensic offline access to the bricked system, which was reinstalled before forensic capture was attempted.
| Defect | Test that would have caught it |
|---|---|
| §D.26.1.A SPF-aware boot-signing table | Run Chipset Install on a WS2019 bench; observe that I04 prints BLOCKED on a SPF-active host. There was no such test in §11 — a "self-consistency probe between I02 reported state and I04 reported state" check belongs in §11 or §12. |
| §D.26.1.B LOADED disposition vs functional probe | The graphics log itself contains the disagreement, side-by-side. A self-consistency cross-check between Section 1 (LOADED) and Section 2 (PASS) of I04 belongs in the harness. |
| §D.26.1.C BthPan I05 Attempt 3 redirect bug | Force I04 to fail (e.g. by running on a host where PnP rebind cannot complete in time), trigger I05, observe Attempt 3 fail with the validator error. Unit-test-shaped: Invoke-BthPanPnputilRebind can be exercised in isolation against a synthetic $InstanceId. |
| §D.26.1.D BthPan I05 Attempt 4 error visibility | Same as above; trigger Attempt 4 and inspect the captured Write-Detail output for the InnerException / NativeErrorCode lines. |
| §D.26.2.* (design defects) | No fast unit test catches these. They require the "back-to-back Install on a production-shaped host" scenario the README now explicitly disqualifies from the supported deployment model. |
The orchestrator currently activates the SPF policy via the WMI PS_UpdateAndCompareCIPolicy CIM bridge and treats a successful return as "policy is live". The planned Test-WdacPolicyBootLoadable extension will additionally:
- Re-read
C:\Windows\System32\CodeIntegrity\SiPolicy.p7bfrom disk. - Verify with
signtool verify /pa(the policy file is self-contained; this catches corrupt deployments). - Parse the policy header and assert Option 6 (Enabled:Unsigned System Integrity Policy) and Option 10 (Enabled:Boot Audit on Failure) are both set.
- Block I03 if any of (1)–(3) fail.
This does NOT guarantee boot-time acceptance (the boot loader has its own enforcement decisions that runtime tools cannot fully predict), but it eliminates the failure modes where the deployed policy is structurally invalid.
The October 2026 narrative above assumed the brick mechanism required a 3-script cumulative install (Chipset → Graphics → MSBthPan) on the same host without reboots. A second WS2019 + Renoir bench run on 2026-05-23 disproved that assumption: a fresh-install WS2019 host running only Chipset r69 -Action Install (Path C, Secure Boot ON) was left unable to complete the next boot, including Safe Mode. Graphics and MSBthPan were never run on this bench.
Recovery required booting WinRE from USB and del C:\Windows\System32\CodeIntegrity\SiPolicy.p7b. After the deletion, the host booted normally — and notably, the WHQL co-signed AMD drivers (AmdMicroPEP.sys, amdgpio2.sys, amdpsp10.sys) that had already been installed in I03 loaded with Status=OK without any WDAC policy in place. Conversely, the non-WHQL drivers in the same package (amdi2c.sys, amdsfhkmdf.sys) remained Status=Error / ProblemCode=39 (CM_PROB_DRIVER_FAILED_LOAD) after WDAC removal, demonstrating that the SPF policy was never the reason WHQL drivers loaded and never sufficient to make non-WHQL drivers load.
Conclusion (drives r70): the brick is caused by the WDAC SPF policy itself, not by any cumulative-stacking dynamic. A single Install execution can produce it. The two bench observations (2025 cumulative, 2026-05-23 single-script) are both expressions of the same underlying defect: the boot loader's re-evaluation of SiPolicy.p7b against the newly-installed boot-critical driver set is an enforcement layer that the WMI CIM bridge Update() success signal cannot speak to. That same boot loader layer also rejects non-WHQL drivers regardless of the policy's contents (see SPEC §D.30 F6, F7).
The full investigation summary, including Microsoft Learn cross-references on bcdedit /set TESTSIGNING ON behaviour under Secure Boot (the rejection happens at command execution, not silently in the boot loader) and on the silently-ignored NOINTEGRITYCHECKS / DISABLE_INTEGRITY_CHECKS flags on WS2008+ x64, is documented in SPEC §D.30. The decision to delete Path C entirely in r70 — rather than continue trying to harden it — flows directly from F1–F12 in that section.
What r70 retires: the Path C orchestrator, all driver-script delegation helpers (SECTION 1g, SECTION QI-10, Invoke-LegacyWdacAuthorization, the I02 Path C branch, the post-I02 BootLoadableCheck dispatcher hook, the manifest.json-based C3 CRITICAL check), and the -ForceOverrideForeign / -AuditMode / -StrictBootValidation switches. The "Boot-time policy validation (planned for r69/r35/r17)" paragraph above is preserved as historical context; its actual landing in r69 / r35 / r17 (the BootLoadableCheck action and its driver-side helper) is also removed in r70 because the policy it was meant to validate is itself removed.
What r70 does NOT retire: the lesson that "kernel-mode signing-state changes on a fresh-install Server SKU are an inherently high-risk operation that has no fast rollback on a physical machine." The README's BRICK-LEVEL RISK disclaimer is rewritten to integrate the 2026-05-23 single-script observation alongside the original 3-script-cumulative observation. The Path B (testsigning + Secure Boot Disabled in firmware) path remains supported but requires an explicit -UseTestSigning invocation; r71 is planned to surface this as an early-abort prerequisite check.
Code-review validated only. The only WS2019 + Renoir bench is queued for OS reinstall as of release time; physical replay is not possible. Test cases below describe what should be observed when a bench becomes available and the r69/r35/r17/r17 release is replayed against it. (Note: the QI-10 / r05 test cases previously documented in this section were removed in r70 along with the Path C orchestrator and the Invoke-BootLoadableCheck helper; see SPEC §D.30.)
This section covers the three post-r04 improvements that remain in scope after the r70 Path C deprecation:
- QI-6: CRITICAL severity acknowledgement checklist in I00 (Chipset/Graphics/BthPan). See SPEC §D.28.
- QI-9: System Restore status warning in P01 (Chipset/Graphics/BthPan). See SPEC §D.26.2.D (now retained as historical context within the §D.30 deprecation narrative).
- Q-X1: NPU refuses Install / All on legacy Windows Server. See SPEC §D.27.
(QI-10 — BootLoadableCheck post-I02 — is intentionally absent from this list: the orchestrator and its BootLoadableCheck action were removed in r70, and the driver-side Invoke-BootLoadableCheck helper and post-I02 dispatcher hook were removed with them. See SPEC §D.30 for the rationale.)
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | WS2019 (build 17763) host. Place NPU_RAI1.6.1_314_WHQL.zip next to the script. |
— |
| 2 | Run .\Deploy-AMDNpuDriverOnWindowsServer.ps1 -Action Install -OfflineZip ... |
P00 fires Show-OperatingSystemDetail, then immediately throws NPU -Action Install refused on legacy Windows Server. See message above. |
| 3 | Workspace is not modified; no certs are created; no WDAC policy is touched. | Test-Path C:\Temp\Workspace_AMD-NPU returns the previous state. |
Same as TC13.1 but on WS2016 (build 14393) with -Action All. Expected: the throw fires for both Install and All.
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | WS2019 host. | — |
| 2 | Run .\Deploy-AMDNpuDriverOnWindowsServer.ps1 -Action PrepareVerify -OfflineZip ... |
P00 completes without throw; P01–P09 run; V01–V05 run; no I-phases run. |
WS2025 host. The refuse check does not fire (Test-IsLegacyWindowsServerOs returns false). Install proceeds as before r17.
TC13.5 — QI-9: P01 prints System Restore status with SiPolicy.p7b exclusion caveat (SR disabled, default case)
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | Fresh WS2019 host (System Restore is OFF by default). | — |
| 2 | Run any of Chipset / Graphics / BthPan -Action PrepareVerify. |
P01 finishes workspace creation, then prints: |
--- System Restore status (snapshot recommendation) --- |
||
System Restore is DISABLED on the system drive |
||
[!] You have NO automatic rollback path for driver-store regressions. |
||
[IMPORTANT] System Restore does NOT capture WDAC boot policy. |
||
C:\Windows\System32\CodeIntegrity\SiPolicy.p7b is excluded from System Restore by design. |
||
| 3 | P01 completes normally; subsequent phases run. | The SR warning is informational only — it does NOT abort. |
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | Workstation host (Win11) with SR enabled and a recent restore point. | — |
| 2 | Run Chipset -Action PrepareVerify -AllowWorkstationInstall. |
P01 prints: |
System Restore is ENABLED on the system drive |
||
Recent restore points (last 30 days): N (where N > 0) |
||
Still prints the [IMPORTANT] SiPolicy.p7b is excluded caveat. |
Per Q9-A=b, the script should not create restore points automatically. Verify: after P01 completes, the count of restore points on the system drive is unchanged. (No regression of the deprecated Checkpoint-Computer behaviour that was withdrawn in §D.26.2.D.)
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | Graphics script on a single-display host (laptop with only built-in panel). | — |
| 2 | Run .\Deploy-AMDGraphicsDriverOnWindowsServer.ps1 -Action Install. |
I00 builds $matched[], then Get-CriticalRiskItem finds C1 matches (display.inf candidate + single display). |
| 3 | I00 prints [CRITICAL][C1] Display driver replacement on single-display host with the C1 detail block. |
The y/N prompt is presented: I understand display loss is possible and have an alternative display path or remote access ready (y/N): |
| 4 | Operator types N and presses Enter. |
I00 throws CRITICAL risk item(s) not acknowledged. Aborting before I01. |
| 5 | Re-run with -ForceUnsafe. |
I00 prints the CRITICAL block but bypasses the prompt; Set-DebugStep records CRITICAL bypass via -ForceUnsafe: items=C1. |
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | WS2022 host with BitLocker enabled on C:\. Chipset install plan includes a PSP-family INF. |
— |
| 2 | Run .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action Install. |
I00 emits [CRITICAL][C2] BitLocker ON + AMD PSP driver replacement with the KeyProtector enumeration in the ack prompt. |
| 3 | Operator answers y. |
I00 records the acknowledgement and proceeds to next item (or to I01 if C2 was the only item). |
Verify: on a host that has not been rebooted in 25+ hours, (Get-Date - LastBootUpTime).TotalHours -gt 24 is true and C5 is added to the items list with the uptime hours displayed.
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | Any host (WS2022+ or legacy). | — |
| 2 | Run .\Deploy-MSBthPanInboxOnWindowsServer.ps1 -Action Install. |
I00 passes @() to Get-CriticalRiskItem. C1 and C2 yield no items (empty $Matched). C5 may or may not fire depending on uptime. |
| 3 | The CRITICAL block contains C5 only (when applicable), no C1/C2. | — |
(TC13.10 — the original "QI-6 C3: same-session WDAC SPF cert stacking" test — was removed in r70. C3 inspected the orchestrator's manifest.json for cross-script cert deployment evidence, but the orchestrator and its manifest are gone after r70. See SPEC §D.28.1 for the historical rationale and SPEC §D.30 for the deprecation context. The r71 planned C6 condition — "WHQL co-sign shortfall on a Secure-Boot-ON host" — will be tested in TESTING.md §14 once r71 ships.)
(TC13.13 – TC13.16 — the four "QI-10: BootLoadableCheck" tests — were also removed in r70. The driver-side Invoke-BootLoadableCheck helper, the post-I02 dispatcher hook, and the orchestrator's BootLoadableCheck action that those tests exercised were all deleted along with Path C. See SPEC §D.30.)
(Negative test — "orchestrator hash mismatch on disk" — was removed in r70. The driver scripts no longer reference an orchestrator, so the canonical-hash verification logic and the test that exercised it are both retired.)
14. Validation Scenario 14: r71 WHQL co-sign pre-detection + Path B prerequisite check + C6 + -SkipNonCosignedDrivers + r72 I02 short-circuit
Code-review validated only. The WS2019 + Renoir bench is queued for OS reinstall as of release time; physical replay is not possible. Test cases below describe what should be observed when the bench becomes available and r72 (Chipset r72 / Graphics r38 / BthPan r20 / NPU r18) is replayed against it.
This section covers the four r71 mechanisms documented in SPEC §D.31 and the one r72 follow-on documented in §D.31.11:
- WHQL co-sign analysis in P05 (
Test-WhqlCoSignature,New-WhqlCoSignAnalysis,Show-WhqlCoSignAnalysisReport). See §D.31.2. - Path B prerequisite check in I02 (
Invoke-PathBPrerequisiteCheck,Test-SecureBootEnabledFromFirmware). See §D.31.3. - C6 CRITICAL acknowledgement in I00 (
Get-CriticalRiskItemextension). See §D.31.4. -SkipNonCosignedDriversswitch (Get-EligibleInfRecordList, P06 entry trim). See §D.31.5.- r72 I02 short-circuit for all-WHQL trimmed plans. See §D.31.11. Covered by TC14.3, TC14.9, TC14.10, TC14.11.
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | WS2019 (build 17763) host with WDK installed (signtool available). Place the AMD Chipset Driver installer zip next to the script. | — |
| 2 | Run .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action PrepareVerify |
P05 completes the INF inventory, then prints --- WHQL co-signature analysis --- with three counts (Fully co-signed / Mixed / No WHQL co-signature). |
| 3 | Inspect $Ctx.WhqlCoSignAnalysis via the workspace-stored phase marker. |
Array of pscustomobject; each entry has InfName, DriverFiles, CoSignedFiles, NonCoSignedFiles, IsFullyCoSigned, HasMixedSigning. |
| 4 | No I-phases run because PrepareVerify excludes them. C6 does not fire, Path B prerequisite check does not run, -SkipNonCosignedDrivers has no effect. |
The run completes with the same V01–V06 output as before r71, plus the new WHQL summary block. |
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | WS2019 host with UEFI Secure Boot ENABLED in firmware. | — |
| 2 | Run .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action Install -UseTestSigning |
I00 PreInstallReview completes (assume no C1/C2/C5/C6 fire for this scenario, or all are acknowledged). I01 succeeds. I02 enters the Path B branch. |
| 3 | I02 calls Invoke-PathBPrerequisiteCheck. Confirm-SecureBootUEFI returns $true. |
The helper returns Result=abort, Reason=secure-boot-on. I02 prints the multi-line guidance block in red and throws I02: Path B prerequisite not met (reason=secure-boot-on). Aborting before bcdedit is invoked. |
| 4 | The host is unmodified: no bcdedit /set TESTSIGNING ON was attempted, no driver-store changes, no cert work. Re-running with -Force would bypass the check (intentionally less prominent in the message). |
bcdedit /enum {current} shows testsigning unchanged. The patched-INF workspace exists (P-phases ran) but the host's boot policy is untouched. |
TC14.3 — -SkipNonCosignedDrivers trims at P06 entry, C6 does not fire, r72 short-circuit fires at I02
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | WS2019 host with Secure Boot ON. AMD Chipset install set contains a mix of WHQL co-signed (e.g. AmdMicroPEP.sys, amdgpio2.sys) and non-WHQL (e.g. amdi2c.sys, amdsfhkmdf.sys) drivers. | — |
| 2 | Run .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action Install -SkipNonCosignedDrivers |
P05 completes the WHQL analysis. P06 entry prints --- r71: -SkipNonCosignedDrivers filter applied --- with the before/after INF counts. After the trim, $Ctx.WhqlCoSignAnalysis retains only WHQL-co-signed INFs. |
| 3 | I00 PreInstallReview runs. Get-CriticalRiskItem evaluates C6. |
C6 does NOT fire because $Script:SkipNonCosignedDrivers is $true (one of the four required AND conditions fails). C1/C2/C5 evaluate independently. |
| 4 | I02 entry: Test-InstallPhaseAlreadyDone returns $false (host has neither WDAC policy nor testsigning ON). The r72 short-circuit predicate evaluates: -not $Ctx.UseTestSigning (true) AND $Script:SkipNonCosignedDrivers (true) AND $Ctx.WhqlCoSignAnalysis populated (true) AND $nonCoSignedAfterTrim.Count -eq 0 (true). |
I02 prints --- I02 short-circuit (r72): install plan is fully WHQL co-signed --- and the rationale block. It writes the I02 phase marker with Metadata=@{ ShortCircuit=$true; Reason='all-whql-skip'; AnalysedInfCount=<N> } and emits Write-PhaseFooter 'I02' 'short-circuit'. The Path B prerequisite check is NOT invoked; no firmware ABORT occurs. |
| 5 | I03 runs normally. pnputil accepts the script-re-signed catalogs because the script's self-signing cert is in Trusted Publisher (from I01). | The WHQL-co-signed subset loads on the host with Secure Boot ON via the drivers' embedded Microsoft signatures. The non-WHQL subset was never patched (P06 trim removed it before P07/P08/P09). No WDAC supplemental policy file exists on disk; bcdedit /enum {current} shows testsigning unchanged. |
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | WS2022 (build 20348) host with UEFI Secure Boot ON. AMD Chipset install set contains both WHQL and non-WHQL drivers. | — |
| 2 | Run .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action Install (no -SkipNonCosignedDrivers, no -UseTestSigning) |
P05 WHQL analysis populates $Ctx.WhqlCoSignAnalysis. I00 evaluates C6. |
| 3 | C6 fires with [CRITICAL][C6] WHQL co-sign shortfall on Secure-Boot-ON host (N non-co-signed INF(s)) listing up to 5 sample INFs. |
The acknowledgement prompt asks: I understand non-WHQL drivers will be kernel-CI-rejected at boot and accept this outcome (y/N): |
| 4 | Operator answers N. |
I00 throws CRITICAL risk item(s) not acknowledged. Aborting before I01. No driver-store changes were made. |
| 5 | Re-run with -SkipNonCosignedDrivers OR -UseTestSigning (after disabling Secure Boot in firmware first per the C6 guidance text) OR -ForceUnsafe (audit-logged bypass). |
The chosen escape route proceeds without C6 firing. |
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | Constrained VM or legacy BIOS host where Confirm-SecureBootUEFI throws. |
— |
| 2 | Run .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action Install -UseTestSigning |
I02 calls Invoke-PathBPrerequisiteCheck. The helper catches the exception, returns Result=ok, Reason=secure-boot-unknown. |
| 3 | I02 prints the warning block as Write-Caution messages and continues to the existing legacy Secure Boot guard (which uses the OS-layer $bootEnvBefore.SecureBootEnabled view). |
If both views agree on "off", Path B proceeds normally. If they diverge, the legacy guard fires. |
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | WS2025 (build 26100) host with a Chipset install set whose drivers are all WHQL co-signed (e.g. a release where AMD's INFs use only Microsoft-co-signed catalogs). | — |
| 2 | Run .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action Install |
P05 prints Fully WHQL co-signed INFs: N / Mixed: 0 / No WHQL: 0. I00 evaluates C6; the predicate is $nonCoSignedInfs.Count -gt 0 which is false, so C6 does not fire. |
| 3 | I02 runs the WDAC MPF Path A normally on WS2025. All drivers load. | Standard pre-r71 behaviour with one additional console block (the WHQL summary) and zero behavioural change. |
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | Any supported host (WS2022 / WS2025 since BthPan does not refuse on legacy Server as NPU does). | — |
| 2 | Run .\Deploy-MSBthPanInboxOnWindowsServer.ps1 -Action PrepareVerify |
P05 builds a single-record WHQL analysis for bthpan.inf. The Microsoft inbox driver is WHQL co-signed; the report shows Fully WHQL co-signed INFs: 1. |
| 3 | Pass -SkipNonCosignedDrivers. |
BthPan P06 entry prints r71: -SkipNonCosignedDrivers set; bthpan.inf is WHQL co-signed by Microsoft. No trim needed. and the run continues unchanged. |
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | WS2019 host without the WDK installed; Find-KitTool 'signtool.exe' returns $null. Install set has a non-WHQL primary signer (typical for AMD's own publisher cert on non-co-signed drivers). |
— |
| 2 | Run .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action PrepareVerify |
Test-WhqlCoSignature falls back to the primary-signer-only check. Non-Microsoft primary signers are classified Reason=self-only, IsCoSigned=$false. |
| 3 | Inspect P05 output. | Conservative classification: the WHQL summary may over-report No WHQL co-signature on actually-co-signed drivers because nested signers are not visible. C6 may fire on Secure-Boot-ON hosts where it would not fire with signtool present. Operators with no WDK can either install signtool or accept the conservative outcome. |
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | Any host where C6 would fire (mixed install plan + Secure Boot ON + no Skip / TestSigning). | — |
| 2 | Run with -ForceUnsafe added. |
C6 (and any other CRITICAL items) appear in the console summary but the acknowledgement prompt is skipped. Set-DebugStep records the bypass with the comma-separated item ID list. |
| 3 | Inspect the debug trace JSONL stream. | A line containing CRITICAL bypass via -ForceUnsafe: items=C6 (or with other IDs interleaved) is present. The audit anchor is preserved. |
Historical note (pre-r72): When -SkipNonCosignedDrivers was set on WS2019, the WDAC MPF Path A path could not run (legacy Server does not have CiTool.exe) and after the deprecation of Path C in r70, I02 fell through to Path B. Even though the install plan was fully WHQL co-signed (because Skip trimmed it), Path B's prerequisite check ABORTed on Secure Boot ON because the firmware state had not changed. SPEC §D.31.9 recorded this as a deferred follow-on refinement. The r72 release closes the gap with the I02 short-circuit documented in §D.31.11 and validated by TC14.3, TC14.9, TC14.10, and TC14.11 below.
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | WS2019 host with Secure Boot ON in firmware. AMD Chipset install set has at least one WHQL co-signed INF and at least one non-WHQL INF. | — |
| 2 | Run .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action Install -SkipNonCosignedDrivers |
P05 emits the WHQL analysis. P06 entry trims $Ctx.InfInventory to the WHQL subset. I00 evaluates C6 — does not fire. I01 imports the script's self-signing cert into LocalMachine\Root + LocalMachine\TrustedPublisher. |
| 3 | I02 enters. Test-InstallPhaseAlreadyDone -PhaseId 'I02' returns false. Set-DebugStep 'r72 short-circuit evaluation' is recorded. The four-clause predicate evaluates as: -not $Ctx.UseTestSigning=true AND $Script:SkipNonCosignedDrivers=true AND $Ctx.WhqlCoSignAnalysis.Count > 0 AND $nonCoSignedAfterTrim.Count==0. |
The short-circuit fires. Console shows --- I02 short-circuit (r72): install plan is fully WHQL co-signed --- in green, then the rationale block. Set-PhaseMarker -PhaseId 'I02' -Metadata @{ ShortCircuit=$true; Reason='all-whql-skip'; AnalysedInfCount=<N> } is invoked. Write-PhaseFooter 'I02' 'short-circuit' closes the phase. |
| 4 | I03 runs unchanged. pnputil validates the script-re-signed catalogs against the cert chain (cert is in Trusted Publisher from I01). | All WHQL-co-signed drivers are installed and load via their embedded Microsoft signatures. No WDAC supplemental policy is written to %SystemRoot%\System32\CodeIntegrity\CiPolicies\Active. bcdedit /enum {current} shows testsigning unchanged. |
| 5 | After install completes, run .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action Verify |
V-phases confirm drivers are installed and in Started state. Device Manager shows the WHQL-co-signed devices as Status=OK. |
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | WS2022 (build 20348) or WS2025 (build 26100) host with Secure Boot ON. AMD Graphics install set contains a mix of WHQL and non-WHQL INFs. | — |
| 2 | Run .\Deploy-AMDGraphicsDriverOnWindowsServer.ps1 -Action Install -SkipNonCosignedDrivers |
P05 / P06 / I00 / I01 behave as on WS2019 (P06 trim, no C6, cert import). |
| 3 | I02 entry. On WS2022+ the host has CiTool available, so a non-short-circuit run would have taken Path A and deployed a WDAC supplemental policy. With the r72 short-circuit, the four-clause predicate still holds and the short-circuit fires. | The console output is identical to TC14.9 step 3. No WDAC supplemental policy file is created on disk — even though the WS2022+ Path A would otherwise have created one. This is the intentional OS-version-uniform behaviour documented in SPEC §D.31.11.6. |
| 4 | Inspect Get-CIPolicy -Online or %SystemRoot%\System32\CodeIntegrity\CiPolicies\Active. |
No script-deployed .cip file appears. (Existing OS-default policies are untouched; the short-circuit does not remove anything.) Drivers load via WHQL embedded signatures. |
TC14.11 — Resume-after-reboot: short-circuit marker does NOT trap subsequent runs that drop -SkipNonCosignedDrivers
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | Any supported host. Workspace already contains a successful run from TC14.9 or TC14.10 (I02 phase marker has Metadata.ShortCircuit=$true). Driver state on host: WHQL drivers installed; no WDAC supplemental policy; no testsigning. |
— |
| 2 | Re-run the script WITHOUT -SkipNonCosignedDrivers: .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action Install |
P05 re-runs the WHQL analysis on the (now broader) install plan. P06 does NOT trim (Skip flag absent). I00 evaluates C6 with the un-trimmed analysis. |
| 3 | I02 entry. Test-InstallPhaseAlreadyDone -PhaseId 'I02' inspects HOST STATE (Test-AmdWdacPolicyDeployed and the BCD testsigning value), not the phase marker. Neither host-state predicate holds, so it returns $false. The phase enters its main body. |
The r72 short-circuit predicate's clause 2 ($Script:SkipNonCosignedDrivers) is now $false, so the short-circuit does NOT fire. I02 proceeds with the standard Path A / Path B evaluation. The fact that a prior run wrote a short-circuit marker does not trap this new run. |
| 4 | On WS2022+, Path A deploys the WDAC supplemental policy normally. On WS2019, Path B prerequisite check runs (and ABORTs if Secure Boot ON, or proceeds to set testsigning if OFF). | The re-run is exactly equivalent to a first-time run on a host that happens to already have I01 trust-store import done — no surprise trapping behaviour. |
| 5 | Inspect the workspace's phase-markers.json (or equivalent). |
The new I02 marker (Path A or Path B success) replaces the prior ShortCircuit=$true marker. The diagnostic history is not lost — operators inspecting the previous run's transcript still see the short-circuit invocation; only the current workspace state reflects the most recent outcome. |
Added with the Chipset r73 / Graphics r39 / BthPan r21 release as the static-analysis gate that prevents recurrence of the Chipset r72 P05 hard-failure defect. This test does NOT require a Windows host — it runs purely on the developer / CI machine via Python 3 and the canonical psa.py 3.8.0 (or newer) artifact.
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | Working tree at the current mainline of Deploy-Drivers-For-WindowsServer. Python 3.8+ installed. Fetch the canonical analyzer: curl -sSL https://raw.githubusercontent.com/usui-tk/ai-generated-artifacts/main/scripts/python/powershell-static-analyzer/psa.py -o /tmp/psa.py (and the sibling VERSION file). Confirm python3 /tmp/psa.py --version reports psa.py 3.8.0 or later. |
— |
| 2 | Run python3 /tmp/psa.py --include PSA2009 --no-color Deploy-AMDChipsetDriverOnWindowsServer.ps1. |
Issues : 0 errors, 0 warnings, 0 info followed by (no issues found). The exit code is 0. |
| 3 | Run python3 /tmp/psa.py --include PSA2009 --no-color Deploy-AMDGraphicsDriverOnWindowsServer.ps1. |
Same as step 2. |
| 4 | Run python3 /tmp/psa.py --include PSA2009 --no-color Deploy-AMDNpuDriverOnWindowsServer.ps1. |
Same as step 2. (NPU does not use [pscustomobject]@{...} for its $Ctx and is exempt from the WHQL producer-consumer contract; the rule still scans the file and finds zero violations.) |
| 5 | Run python3 /tmp/psa.py --include PSA2009 --no-color Deploy-MSBthPanInboxOnWindowsServer.ps1. |
Same as step 2. |
| 6 | (Regression replay only — do not run on the current mainline). Check out the r72 / r38 / r18 / r20 baseline (i.e., the immediate predecessor of this release) and re-run steps 2–5. | Step 2 (Chipset) reports Issues : 0 errors, 2 warnings, 0 info with both warnings pointing at the P05 happy-path assignment line and the catch-block fallback line for $Ctx.WhqlCoSignAnalysis. Step 5 (BthPan) reports the same. Steps 3 and 4 (Graphics, NPU) report 0 warnings. This replay confirms that PSA2009 would have caught the historical defect at static-analysis time, and that the current mainline closes the regression. |
CI integration: this test case is the recommended gate for any pre-commit hook or CI pipeline that wants to prevent recurrence of the [pscustomobject] sealed-object defect class. Adding --include PSA2009 to the existing full-rule invocation is redundant but harmless (PSA2009 is on by default at warning severity in psa.py 3.8.0+).
TC14.13 — Graphics P05 emits the WHQL co-signature analysis summary banner (r39 producer-site smoke test)
Added with the Graphics r39 release to verify that the historical producer-site gap (r37 / r38 shipped consumers but no producer) is closed. The test requires a Windows host with a real Adrenalin INF set extracted into the workspace — the Adrenalin 26.5.2 Vega-Polaris Legacy run already validated under TC10.x is the canonical reference.
| Step | Setup | Expected outcome |
|---|---|---|
| 1 | Windows Server 2019 / 2022 / 2025 host with Adrenalin 26.5.2 Vega-Polaris Legacy (or a comparable Adrenalin package) cached. Run .\Deploy-AMDGraphicsDriverOnWindowsServer.ps1 -Action PrepareVerify -CleanWorkRoot. |
P05 runs normally; the inventory CSV and TXT are written to the workspace. |
| 2 | Inspect the P05 phase transcript section. Look for the new three-line WHQL summary banner: --- WHQL co-signature analysis --- followed by Fully WHQL co-signed INFs : <N>, Mixed-signing INFs (partial): <M>, No WHQL co-signature : <P>. |
The banner is present immediately before the PHASE P05 -> DONE footer. Prior to r39 this banner was missing entirely on the Graphics script. |
| 3 | Inspect $Ctx.WhqlCoSignAnalysis via the workspace-stored phase marker (%WorkRoot%\markers\P05-*.json or equivalent). |
The marker's Metadata section now includes the WHQL analysis result count. Prior to r39 the field was absent (because the producer never ran). |
| 4 | Re-run with -SkipNonCosignedDrivers: .\Deploy-AMDGraphicsDriverOnWindowsServer.ps1 -Action PrepareVerify -SkipNonCosignedDrivers -CleanWorkRoot. |
P06 entry now emits the r71: -SkipNonCosignedDrivers filter applied banner with a concrete trim count (or the "already fully WHQL co-signed" message if Adrenalin happens to be fully co-signed). Prior to r39 the filter was a silent no-op on Graphics because $Ctx.WhqlCoSignAnalysis was never populated. |
| 5 | Re-run with -Action All on a Secure-Boot-ON host with mixed-signing Adrenalin: .\Deploy-AMDGraphicsDriverOnWindowsServer.ps1 -Action All. |
I00 §C6 ("WHQL co-sign shortfall on Secure-Boot-ON host") now fires (or correctly does not fire if Adrenalin is fully WHQL co-signed). Prior to r39, C6 was unreachable on Graphics because its $hasAnalysis precondition was always false. |
This test case is a runtime acceptance test (not a static-analysis test); it complements TC14.12's static gate by verifying that the wiring actually works end-to-end on real Adrenalin packaging.
15. Validation Scenario 15: Chipset r73 / Graphics r39 / BthPan r21 — $Ctx.WhqlCoSignAnalysis pre-declaration fix + Graphics WHQL producer port
This scenario records the field-reported defect that triggered the Chipset r73 / Graphics r39 / BthPan r21 release on 2026-05-23, together with the static-analysis hardening that closes the defect class going forward.
Reporter: end-user.
Environment: clean-installed Windows Server 2019 Datacenter (build 17763), ja-JP locale, shift_jis (cp932) console encoding, PowerShell 5.1.17763.8755 Desktop, ConsoleHost. AMD Ryzen 5 PRO 4650U with Radeon Graphics, mobile FP6-series BGA (Zen 2 / Renoir). UEFI firmware in GPT mode, Secure Boot OFF (legacy posture). System Restore disabled (default on Server SKUs). No prior workspace.
Command: .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -Action PrepareVerify -CleanWorkRoot at script version chipset-2026.05.23-r72 (script tag legacy-ws2019-wdac-spf-integration, SHA256 first-12 a580af9da833).
Outcome: PHASE P05 transitioned to FAILED at +6.42s into the phase with the localised exception:
[X] P05 [AnalyzeInfs] failed: "WhqlCoSignAnalysis" の設定中に例外が発生しました:
"このオブジェクトにプロパティ 'WhqlCoSignAnalysis' が見つかりません。
プロパティが存在し、設定可能であることを確認してください。"
The stack trace pointed at line 8470 column 9 of the r72 source, which is the catch-block fallback assignment $Ctx.WhqlCoSignAnalysis = @(). The two warning lines emitted immediately before the failure (r71: WHQL co-sign analysis failed: 指定された名前のパラメーターを使用してパラメーター セットを解決できません。 and r71: I00 C6 condition and -SkipNonCosignedDrivers will operate on an empty analysis.) were the script's own diagnostic narration from inside the same catch block — the initial exception inside the try block was a different defect (a param-binding failure in a downstream signtool helper) that the catch block correctly intercepted; the failure that aborted P05 was the catch-block fallback itself attempting to assign to a non-existent property.
The P04 extraction had already completed successfully (downloaded amd_chipset_software_8.05.04.516.exe, 76.5 MB; extracted via InstallShield admin-install chain; 117 INF files harvested; preferred variant WTx64 selected; 60 INFs eligible for patching) and P05 had completed its inventory-CSV and inventory-TXT writes. The failure point was purely the WHQL-analysis production block at the end of P05, immediately before Set-PhaseMarker.
Three nested defects:
- Inner defect (the trigger) — The signtool helper
Test-WhqlCoSignature(or one of its downstream helpers) raised a localised指定された名前のパラメーターを使用してパラメーター セットを解決できません。(English: "Cannot resolve parameter set with the specified named parameters") at parameter-binding time on this host. The exact site is not material to the P05 failure because thetryblock was specifically designed to catch this class of helper-side failure. - Middle defect (the actual failure) — The
catchblock was designed to write$Ctx.WhqlCoSignAnalysis = @()as a graceful-degradation sentinel. Because the[pscustomobject]@{...}$Ctxinitialiser at the top of the r71 / r72 script does NOT includeWhqlCoSignAnalysis = $null, thecatchblock's own assignment raises a SECOND terminating exception (the localised property-not-found message). This second exception is NOT caught by the sametry/catch(thecatchblock is the one raising it) and propagates to the phase runner, which records P05 as FAILED. - Outer defect (the silent regression) — The same defective initialiser is shared between Chipset r72 and BthPan r20. Graphics r38 has a different but equally severe defect: the entire P05 WHQL-analysis production block is missing, so
$Ctx.WhqlCoSignAnalysisremains at its (implicit, undeclared)$nullvalue, every consumer site silently degrades to its fallback path, and the user never sees a runtime error — they just don't get the WHQL pre-detection benefit at all.
The psa.py v3.7.0 rule catalog (36 rules) did not include a check for "PSCustomObject property assigned without prior declaration". PSScriptAnalyzer's equivalent (Invoke-ScriptAnalyzer) also does not include such a rule. The defect is therefore detectable only by:
- Runtime execution on a host that traverses the affected phase (which is exactly how the user discovered it — they hit it on their first
-Action PrepareVerifyrun). - A new static-analysis rule that models the PSv5 sealed-object semantic specifically. This is what
psa.pyv3.8.0's new PSA2009 rule does. The rule was developed as part of the r73 / r39 / r21 fix work; see SPEC §A.11.5c for the rule documentation and §D.31.16 for the broader checklist applied to future$Ctx.<NewField>integrations.
The defect went undetected for two prior revisions (r71 introduced it on 2026-05-23, r72 hardened the I02 short-circuit on the same day but did not touch the initialiser) because:
- The project's CI matrix did not include a
PrepareVerifyrun on WS2019 with the cleanest-possible workspace (no cached tools, fresh download, fresh extraction). The defect needs thetry-block's inner failure to fire in order to reach thecatch-block's outer failure; on hosts with cached signtool the inner failure does not fire reliably. - The project's static-analysis baseline (
psa.py--config .psa.config.json) reported 0 errors / 0 warnings / 0 info on r72, which appeared to confirm clean-baseline status. The defect was below the rule catalog's detection floor.
The r73 / r39 / r21 release applies the following changes:
| Script | Change | Verification |
|---|---|---|
| Chipset r73 | Add WhqlCoSignAnalysis = $null to the $Ctx initialiser with a multi-line explanatory comment cross-referencing SPEC §D.31 and PSA2009. |
psa.py --include PSA2009 reports 0 findings (was 2). Re-running TC10.x WS2019 PrepareVerify scenario completes P05 with the WHQL summary banner present. |
| Graphics r39 | Add WhqlCoSignAnalysis = $null to $Ctx (same shape as Chipset). Port the P05 WHQL-analysis production block from Chipset r71 (~17 lines, byte-identical except for revision-tag comments rephrased to r39). |
psa.py --include PSA2009 reports 0 findings (was 0; Graphics had the producer-gap defect, not the initialiser defect). New TC14.13 verifies the producer-site banner appears at runtime. |
| BthPan r21 | Add WhqlCoSignAnalysis = $null to $Ctx (same shape as Chipset). |
psa.py --include PSA2009 reports 0 findings (was 2). Re-running the bthpan flow completes P05 with the synthetic-record WHQL analysis. |
| NPU r18 | No change. NPU's $Ctx does not exercise WhqlCoSignAnalysis. |
psa.py --include PSA2009 reports 0 findings (was 0). Per SPEC §A.7 ("no empty revisions"), NPU is NOT bumped. |
psa.py v3.7.0 → v3.8.0 |
New rule PSA2009. | All four scripts report 0 findings at the new baseline. The r72 / r38 / r18 / r20 regression-replay (TC14.12 step 6) reproduces 2 + 0 + 0 + 2 findings, confirming the rule would have caught the defect at static-analysis time. |
- Initial diagnosis must start with the script version recorded in the operator's transcript, not with the current mainline. The r72 transcript line
chipset-2026.05.23-r72/a580af9da833was the entry point; the fix branch is bumped to r73 to make the relationship explicit. - A failed
catchblock is harder to debug than a failedtryblock because the operator's transcript shows thecatchblock's own narration before the actual failure surfaces. Always read the FINAL exception in the transcript, then walk backward. - PowerShell 5.1 sealed-object semantics are a recurring footgun. The
[pscustomobject]@{...}accelerator is the strictest form available in PSv5 and the project uses it intentionally to surface integration defects loudly — but the strictness backfires when the surfaced error is itself inside acatchblock. SPEC §D.31.16 codifies the checklist that prevents this from recurring; PSA2009 is the static-analysis gate that mechanically enforces it. - A clean static-analysis baseline is necessary but not sufficient. The r72 baseline was clean under
psa.pyv3.7.0 and still shipped the defect. Adding new rules (when a defect class is identified) is the correct response — see SPEC §A.11 ("Static Analysis with psa.py") for the canonical artifact-versioning workflow that this release exercised.
This section records the test scenarios that close the four r74 defects documented in SPEC §D.32. The bench host is the same one used for §15 (clean-installed Windows Server 2019 Datacenter, build 17763, ja-JP, PowerShell 5.1.17763.8755 Desktop, ConsoleHost, AMD Ryzen 5 PRO 4650U / Renoir, UEFI / GPT, Secure Boot OFF, no prior workspace, no BitLocker).
Reporter: end-user.
Environment: identical to §15.1 except the host had a prior r73 install run completed and rebooted before the diagnostic snapshot was taken; testsigning ON, 5 AMD chipset devices on script-installed drivers.
Command: .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -OnlyPhases V06 at script version chipset-2026.05.23-r73.
Outcome: V06 reported [A]=36 [B]=5 [C]=0 [?]=1 and "Match summary: 2 device(s) WILL be replaced" — both incorrect on a freshly-installed-and-rebooted host. The diagnostic pre-reboot snapshot also surfaced that Test-WhqlCoSignature had been returning conservative self-only for every .sys file across the entire r71–r73 lifetime (the silent degradation documented in §D.32.2).
The user's report contained four artefacts that were critical to triage:
CONSOLIDATED_REPORT.txt(statement of work + raw signtool output for each staged.sys).12_pre-reboot-amd-driver-bindings.csv(showedIsSigned=Falsefor all script-installed drivers, expected for self-signed kernel drivers).13_pre-reboot-bcd.txt(testsigning Yesplus the surprisingdisplaymessageoverride Recoveryvalue — later determined to be the WS2019 default).- The full
-Action Installtranscript that revealed I02 → I03 ran in the same execution despite the "reboot then re-run" message (Defect 4).
| Step | Action | Expected |
|---|---|---|
| 1 | Cherry-pick a Windows-inbox .sys known to be WHQL co-signed (e.g. C:\Windows\System32\drivers\bthpan.sys) into a temp directory. Install WDK 10 so signtool.exe is on PATH. |
— |
| 2 | Dot-source the patched Test-WhqlCoSignature body or run the script's P05 against an INF that references this file. |
Test-WhqlCoSignature returns IsCoSigned=$true, Reason='cosigned', WhqlMarker non-empty. |
| 3 | Re-run with Find-KitTool 'signtool.exe' returning $null (no WDK). |
Returns IsCoSigned=$false, Reason='self-only'. This is the conservative fallback documented in §D.32.2. |
TC16.2 — Test-WhqlCoSignature returns self-only for AmdMicroPEP.sys from chipset 8.05.04.516 (negative)
| Step | Action | Expected |
|---|---|---|
| 1 | After a successful r74 Install on the bench host, locate C:\Windows\System32\DriverStore\FileRepository\amdmicropep.inf_amd64_*\AmdMicroPEP.sys. |
— |
| 2 | Dot-source and call Test-WhqlCoSignature -Path <path>. |
Returns IsCoSigned=$false, Reason='self-only'. Confirms §D.32.3 finding that chipset 8.05.04.516 dropped the WHQL co-signature. |
| Step | Action | Expected |
|---|---|---|
| 1 | On the bench host, after a successful r74 Install + reboot, run .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -OnlyPhases V06. |
— |
| 2 | Inspect the Driver-source distribution among AMD HARDWARE: line. |
[C]>0 for the 5 devices the script installed (chipset). On Graphics, the count is package-specific; on BthPan, V06 does not exercise Get-DriverSourceCategory and this TC is N/A. |
| 3 | Inspect Section 2 Match summary: line. |
0 device(s) WILL be replaced (idempotent) on a clean install + reboot. If >0, V06 still flagged a legitimate replacement target — verify against the install plan to confirm. |
| Step | Action | Expected |
|---|---|---|
| 1 | After TC16.3, run -Action Install a second time (no flags). |
I00 detects all phases in target state, I02 is cached, I03 hits "all patched INFs already in driver store" cached path, I04 reports the same disposition as TC16.3 step 2. |
| 2 | No driver is replaced. No REBOOT_REQUIRED lines appear. |
— |
| Step | Action | Expected |
|---|---|---|
| 1 | Boot a clean-installed WS2019 host (testsigning OFF in BCD). Secure Boot OFF in firmware so Path B is available. | — |
| 2 | Run -Action PrepareVerify -CleanWorkRoot then -Action Install. |
P00–P09, V01–V06 complete normally. I00 reviews. I01 imports cert. I02 sets BCD testsigning ON. |
| 3 | I02 footer | PHASE I02 -> DONE. The next message line is the *** A REBOOT IS REQUIRED *** block (unchanged from r73). |
| 4 | I03 entry | Prints I03: halting because I02 just enabled testsigning in this run. followed by the 3-step operator workflow. Footer is PHASE I03 -> halted-pending-reboot. |
| 5 | I04 entry | Same halt body. Footer is PHASE I04 -> halted-pending-reboot. |
| 6 | Workspace markers | I02 marker is written; I03 / I04 markers are NOT written. PendingRebootMarker is written. |
| 7 | RUN SUMMARY | Phases run shows P00 -> P01 -> I00 -> I01 -> I02 -> I03 -> I04. Phase timings table shows I03 / I04 with halted-pending-reboot status. |
| Step | Action | Expected |
|---|---|---|
| 1 | After TC16.5, reboot the host. Test Mode watermark appears. | — |
| 2 | Re-run -Action Install (same command). |
$Ctx.RebootRequiredBeforeI03 starts as $false (per-process, NOT persisted). |
| 3 | I02 entry | Hits the cached "already on" branch. Footer is cached. |
| 4 | I03 entry | Does NOT halt. Stages drivers normally. |
| 5 | I04 entry | Does NOT halt. Runs the post-install verification normally. |
| 6 | I04 functional-health probe | Reports actual driver-load state. PendingRebootMarker is cleared by I04. |
The r74 release adds no new psa.py rule. The four r74 defects (per SPEC §D.32) are all integration-level defects that local-form analysis cannot detect. A planned psa.py v3.9.0 rule PSA2010 — invocation of undefined function would catch Defect 1 (Find-Signtool) at static-analysis time, but the rule needs the full function-definition table across all four scripts simultaneously, which is a structural change to the analyzer. PSA2010 is tracked as future work.
| Risk | Mitigation |
|---|---|
signtool verify /all produces unexpectedly verbose output that breaks the Issued to: regex. |
/all adds nested-signature blocks but the per-signer Issued to: line format is unchanged across signtool 6.0–10.0.x. Verified against signtool 10.0.26100.0 (the version this repository's Find-KitTool resolves to). |
$ourInfSet build at V06 entry is slow on hosts with many oem*.cat files. |
The same build runs in I04 already and has been stable since r60. V06's invocation reuses the helper unchanged. |
$Ctx.RebootRequiredBeforeI03 is added but not removed on -CleanWorkRoot. |
The flag is per-process. -CleanWorkRoot rebuilds $Ctx from scratch, so the flag is implicitly absent in the next run. The flag is also explicitly NOT persisted to disk (per §D.32.5 design rationale). |
Operator runs -OnlyPhases I03,I04 skipping I02. |
I03 / I04 do not check RebootRequiredBeforeI03 against $Ctx.UseTestSigning — they trust the flag. If I02 was not run in this session, the flag is $null/$false and I03 / I04 proceed as before. This is the intended behavior. |
- Helper functions referenced by name but never defined are not caught by
psa.py. This is a structural blind spot the project lived with from r71 to r73. PSA2010 (planned, v3.9.0) is the static-analysis answer. Until then, a manualgrep -E '\bFind-[A-Z][a-zA-Z]+' *.ps1cross-check againstgrep -E '^function Find-[A-Z]'is the recommended pre-commit gate. - External-tool flag changes (e.g., signtool's
/all) are easy to miss in code review because the call site looks unchanged. The countermeasure is to inline the rationale for every flag (the r74 helper comment explicitly enumerates/all,/pa,/vand what each does) so future readers do not silently re-remove a flag they think is unused. - V06 / I04 share the
Get-DriverSourceCategoryconsumer surface but have asymmetricGet-OurSignedOemInfSetproducer sites. Any future helper that depends on a one-time-per-phase build SHOULD be invoked at the same site in BOTH V06 and I04 unless there is a documented reason not to. The r74 V06 fix codifies this pattern.
This section is the test-scenario counterpart of SPEC.md §D.33. The r75 release fixes three defects revealed by a clean-bench cycle on 2026-05-24/25 against a Windows Server 2019 Datacenter ja-JP host (build 17763.8755, PowerShell 5.1.17763.8755) with AMD Ryzen 5 PRO 4650U "Renoir" silicon. The diagnostic evidence captured during that cycle lives in two operator-side logs that this section references throughout:
diag-r40-followup-v2-20260524-111804.log(347 lines) — the v2 diagnostic with Step 1.7 (Split-Path direct probe) and Steps 2.8a/b/c (CatRoot enumeration).pre-reinstall-snapshot-20260524-113102.log— the final state snapshot taken after the MSBthPan installation but before the next bench-cycle clean-install.
Both logs are kept in the bench's operator archive (not committed to this repository); the relevant excerpts are reproduced inline in each TC below.
Purpose: Confirm that the operator-visible warning 指定された名前のパラメーターを使用してパラメーター セットを解決できません。 from r71–r74 P05 was the Split-Path AmbiguousParameterSet bug, independent of the Find-Signtool typo that §D.32.2 misdiagnosed as the cause.
Setup: A clean-installed Windows Server 2019 Datacenter ja-JP host, build 17763.8755, with PowerShell 5.1.17763.8755 (the in-box powershell.exe, not pwsh 7.x). Any directory path is fine for the probe; C:\Windows\System32\notepad.exe is recommended for being a known-good file with a known parent.
Procedure:
$p = 'C:\Windows\System32\notepad.exe'
# Form 1 — the r74 form (expected to fail on PS 5.1 ja-JP)
try {
$r1 = Split-Path -LiteralPath $p -Parent
Write-Host "[OK] Form 1: $r1"
} catch {
Write-Host "[FAIL] Form 1: $($_.FullyQualifiedErrorId)"
}
# Form 2 — the r75 alternative (Split-Path -Path positional)
$r2 = Split-Path -Path $p -Parent
Write-Host "[OK] Form 2: $r2"
# Form 3 — the r75 chosen fix (.NET method, no PS binder)
$r3 = [System.IO.Path]::GetDirectoryName($p)
Write-Host "[OK] Form 3: $r3"Expected output (on a failing PS 5.1 ja-JP host):
[FAIL] Form 1: AmbiguousParameterSet,Microsoft.PowerShell.Commands.SplitPathCommand
[OK] Form 2: C:\Windows\System32
[OK] Form 3: C:\Windows\System32
Reference: diag-r40-followup-v2-20260524-111804.log Step 1.7 captured this exact pattern; Form 1's FullyQualifiedErrorId field reads AmbiguousParameterSet, Microsoft.PowerShell.Commands.SplitPathCommand (cmdlet identified directly by the binder).
Pass criteria: Form 1 fails with AmbiguousParameterSet. Forms 2 and 3 succeed and return the same string. On PowerShell 7.x or PS 5.1 en-US, Form 1 may succeed — this is expected (the defect is locale-and-build-specific) and does NOT contradict the r75 fix being necessary for ja-JP.
Purpose: Confirm that Get-InfDriverFileList — the consumer of the line fixed in TC17.1's Form 3 — returns the expected .sys file list at the r75 baseline.
Setup: After installing Chipset r75 (or Graphics r41) with -Action PrepareVerify, the patched INF and .sys files are present in <workspace>\patched\<InfBase>\. Pick any one INF that has been patched.
Procedure:
# Load r75's Chipset script (without running its main entry point)
. .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -SkipMain
$patchedInf = "<workspace>\patched\<InfBase>\AMD_Chipset_Drivers.inf" # adjust path
$result = Get-InfDriverFileList -InfPath $patchedInf
Write-Host "Returned $($result.Count) file(s):"
$result | ForEach-Object { Write-Host " $_" }Pass criteria: At least one .sys file is returned. At the r74 baseline (without the Defect A fix), this same procedure returns an empty array on PS 5.1 ja-JP because Get-InfDriverFileList's outer try/catch swallows the AmbiguousParameterSet exception silently. At the r75 baseline, the array contains the actual driver binaries.
Purpose: Confirm that Get-OurSignedOemInfSet Pass 1a successfully enumerates oem*.cat files signed with the script's self-signed cert from C:\Windows\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}\.
Setup: A clean-installed WS2019 host with Chipset r75 or Graphics r41 freshly installed (the script's cert thumbprint is recorded in the workspace .psd1 and the catalogs are physically present on disk).
Procedure:
# Pre-check: how many oem*.cat files exist at the new CatRoot location?
$catRoot = 'C:\Windows\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}'
Write-Host "CatRoot oem*.cat count: $(@(Get-ChildItem -LiteralPath $catRoot -Filter 'oem*.cat').Count)"
# Pre-check: how many oem*.cat files exist at the r74 (wrong) location?
$infDir = "$env:windir\INF"
Write-Host "C:\Windows\INF oem*.cat count: $(@(Get-ChildItem -LiteralPath $infDir -Filter 'oem*.cat' -ErrorAction SilentlyContinue).Count)"
# Run the r75 helper directly
. .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -SkipMain
$ctx = Get-WorkspaceContext # or however the script exposes it
$set = Get-OurSignedOemInfSet -ExpectedThumbprint $ctx.CertThumbprint
Write-Host "Helper returned $($set.Count) entries:"
$set.Keys | Sort-Object | ForEach-Object { Write-Host " $_" }Pass criteria: On a host that has Graphics r41 installed with the v2-diagnostic cert thumbprint 9FEB313999B8314D5B38744255A20C0A15648E2E, the CatRoot pre-check reports 18 of 18 expected Graphics catalogs, the C:\Windows\INF\ pre-check reports 0, and the helper's return-set count is non-zero (the exact size depends on how many INFs and OEM-aliases the cert covers). On a host that has additionally been through MSBthPan r23 (cert thumbprint A0B563EAB490458B9CD4A920974C5EF27915E103), the BthPan-cert call returns at least 1 entry.
Reference: diag-r40-followup-v2-20260524-111804.log Steps 2.8a/b/c performed exactly this enumeration on the bench host and recorded the 0 / 18 / 18 split (C:\Windows\INF\ empty, CatRoot full, DriverStore\FileRepository full).
Purpose: Confirm that when Pass 1a finds 0 matches (CatRoot unreadable or empty), the new Pass 1b — pnputil /enum-drivers Signer Name lookup — populates the same set via the cert's Subject CN.
Setup: A clean-installed WS2019 host with Graphics r41 installed. To simulate "CatRoot unreachable", temporarily rename the CatRoot subfolder:
# WARNING: This is invasive — only run on a disposable bench VM, then revert.
$catRoot = 'C:\Windows\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}'
$tempBak = $catRoot + '.tc17-4-bak'
Rename-Item -LiteralPath $catRoot -NewName ($tempBak | Split-Path -Leaf)
# ... run the test below ...
# Then restore:
Rename-Item -LiteralPath $tempBak -NewName ($catRoot | Split-Path -Leaf)Procedure:
. .\Deploy-AMDChipsetDriverOnWindowsServer.ps1 -SkipMain
$ctx = Get-WorkspaceContext
$set = Get-OurSignedOemInfSet -ExpectedThumbprint $ctx.CertThumbprint
Write-Host "Pass 1b returned $($set.Count) entries:"Pass criteria: Even though Pass 1a found 0 (CatRoot renamed), the helper's return-set count is non-zero. The pnputil-side lookup of the cert's Subject CN against the Signer Name field in pnputil /enum-drivers output populates the set.
Important: Restore the CatRoot folder immediately after this test — running other scripts with the folder renamed will cause silent driver verification failures elsewhere in Windows.
Purpose: Confirm that the combined r74 Defect 3 fix + r75 Defect B fix delivers the originally-promised V06 idempotency: after a successful install and reboot, running -OnlyPhases V06 reports 0 device(s) WILL be replaced for all script-installed drivers.
Setup: A clean-installed WS2019 host. Run the full r75 install cycle: -Action PrepareVerify -CleanWorkRoot, then -Action Install, then reboot the host, then -Action Install -OnlyPhases V06.
Expected V06 output excerpt (Graphics):
--- AMD HARDWARE that this script can affect ---
Driver-source: [A]Microsoft [B]Vendor [C]Self-signed [?]Unknown
...
Match summary:
0 device(s) WILL be replaced
9 device(s) keep current driver
Pass criteria: The V06 "Match summary" line reports 0 device(s) WILL be replaced and a non-zero count of device(s) keep current driver matching the number of AMD devices that the script targets. At the r74 baseline, this same procedure reported N>0 WILL be replaced on a freshly-installed-and-rebooted host (the original Defect 3 symptom that r74's V06 fix could not fully close because Get-OurSignedOemInfSet returned an empty set). At the r75 baseline, the count is 0.
Reference: pre-reinstall-snapshot-20260524-113102.log documents the pre-r75 state where V06 reported N>0 despite a clean install + reboot.
Purpose: Confirm that the r75 release passes the 0 errors gate under psa.py 3.9.0 with the project's standard .psa.config.json opt-ins enabled (PSAP0001..PSAP0004 on, severity floor at error).
Procedure:
# From the repository root:
curl -sSL https://raw.githubusercontent.com/usui-tk/ai-generated-artifacts/main/scripts/python/powershell-static-analyzer/psa.py -o /tmp/psa-3.9.0.py
python3 /tmp/psa-3.9.0.py \
--config .psa.config.json \
--severity error \
Deploy-AMDChipsetDriverOnWindowsServer.ps1 \
Deploy-AMDGraphicsDriverOnWindowsServer.ps1 \
Deploy-AMDNpuDriverOnWindowsServer.ps1 \
Deploy-MSBthPanInboxOnWindowsServer.ps1
echo "Exit code: $?"Expected output: Each file reports Issues : 0 errors, 0 warnings, 0 info and the overall exit code is 0.
Pass criteria: Exit code is 0 AND every file reports 0 errors. Specifically, no PSA2001 firing on $ourInfSet inside Invoke-InstPhase00_PreInstallReview (this was the latent defect that the r75 Defect C fix closed).
Negative-baseline reference: Running the same command against the r74 sources reports 1 PSA2001 error each on Chipset and Graphics (undefined variable $ourinfset in function Invoke-InstPhase00_PreInstallReview), demonstrating that r75's Defect C fix is observable at static-analysis time as well as runtime.
Purpose: Confirm that PSA2010 (the rule that, had it existed, would have caught the §D.32.2 Find-Signtool typo before r71 shipped) actually fires on its target pattern and does not fire on the correct call form.
Procedure:
# Create a synthetic test file
$ScriptContent = @'
function Find-KitTool {
param([string]$Name)
# returns path to the Windows Kit tool
}
function Test-Foo {
# Correct call — should NOT fire PSA2010
$kit = Find-KitTool 'signtool.exe'
# Typo — SHOULD fire PSA2010
$sig = Find-Signtool
}
'@
$ScriptContent | Set-Content -Encoding utf8 -Path .\test-psa2010.ps1python3 /tmp/psa-3.9.0.py --include PSA2010 --severity error ./test-psa2010.ps1
# Expected output: 1 error at the Find-Signtool linePass criteria: Exactly 1 PSA2010 error is reported, on the Find-Signtool call. The Find-KitTool call is not flagged because the function is defined in the same file. If --include PSA2010 does not fire at all, the rule is broken — escalate before proceeding with releases.
Purpose: Confirm that PSA2011 fires on Split-Path -LiteralPath ... -Parent and does not fire on the two recommended fix forms.
Procedure:
$ScriptContent = @'
$p = "C:\Windows\System32\notepad.exe"
# Should fire PSA2011
$a = Split-Path -LiteralPath $p -Parent
# Should NOT fire (different switch order is still positive — also should fire)
$b = Split-Path -Parent -LiteralPath $p
# Should NOT fire — recommended fix 1
$c = [System.IO.Path]::GetDirectoryName($p)
# Should NOT fire — recommended fix 2
$d = Split-Path -Path $p -Parent
'@
$ScriptContent | Set-Content -Encoding utf8 -Path .\test-psa2011.ps1python3 /tmp/psa-3.9.0.py --include PSA2011 --severity error ./test-psa2011.ps1
# Expected: 2 errors (both -LiteralPath + -Parent forms), 0 errors for fixesPass criteria: Exactly 2 PSA2011 errors are reported, one for each -LiteralPath + -Parent form. The [System.IO.Path]::GetDirectoryName call and the Split-Path -Path call are not flagged.
Additional verification — historical reproduction: Run PSA2011 against the r74 sources to confirm it would have caught Defect A in r71:
git checkout <commit-at-r74>
python3 /tmp/psa-3.9.0.py --include PSA2011 --severity error \
Deploy-AMDChipsetDriverOnWindowsServer.ps1 \
Deploy-AMDGraphicsDriverOnWindowsServer.ps1 \
Deploy-MSBthPanInboxOnWindowsServer.ps1 \
Deploy-AMDNpuDriverOnWindowsServer.ps1
# Expected: 3 errors (Chipset, Graphics, BthPan all have the same line in Get-InfDriverFileList)
# Expected: NPU reports 0 errors (no Get-InfDriverFileList helper)This reproduction is the gold-standard verification that PSA2011 catches the form of the defect that surfaced in the 2026-05-25 bench cycle.
Purpose: Confirm that NPU r19 differs from NPU r18 only in $Script:ScriptVersion and $Script:ScriptTag, validating the §D.33.10 documented exception to SPEC §A.7 ("no empty revisions").
Procedure:
# Extract just the source-meaningful bytes (excluding ScriptVersion / ScriptTag lines)
git show <r18-commit>:Deploy-AMDNpuDriverOnWindowsServer.ps1 \
| grep -v '\$Script:ScriptVersion\s*=\|\$Script:ScriptTag\s*=' \
| sha256sum > /tmp/npu-r18.sha256
git show HEAD:Deploy-AMDNpuDriverOnWindowsServer.ps1 \
| grep -v '\$Script:ScriptVersion\s*=\|\$Script:ScriptTag\s*=' \
| sha256sum > /tmp/npu-r19.sha256
diff /tmp/npu-r18.sha256 /tmp/npu-r19.sha256Pass criteria: The two sha256sum outputs are identical (zero diff). Any non-empty diff fails this TC and means the NPU bump was not actually a clean ScriptTag-alignment; investigate the source-code change that snuck in.
Pass criteria (positive verification): git diff <r18-commit> HEAD -- Deploy-AMDNpuDriverOnWindowsServer.ps1 shows exactly two changed lines (the $Script:ScriptVersion assignment and the $Script:ScriptTag assignment).
This section documents the verification procedures for the r76 / r42 / r24 / r20 release. r76 has no runtime behaviour changes (see §17 for the most recent functional regression suite); the verification is entirely about confirming the static-analysis posture.
Purpose: Confirm that psa.py 4.0.0 with the canonical .psa.config.json produces 0 errors / 0 warnings / 0 info on all four scripts EXCEPT for PSAP0005 warnings (which are documented separately in TC18.3 as the migration baseline).
Procedure:
# From the repository root with psa.py 4.0.0 on PATH or relative
python3 path/to/psa.py --config .psa.config.json \
Deploy-AMDChipsetDriverOnWindowsServer.ps1 \
Deploy-AMDGraphicsDriverOnWindowsServer.ps1 \
Deploy-AMDNpuDriverOnWindowsServer.ps1 \
Deploy-MSBthPanInboxOnWindowsServer.ps1Pass criteria: All four scripts report 0 errors. Warnings are PSAP0005 only (no PSAP0003 / no PSA2001 / no PSA8001 / etc.).
Purpose: Confirm that the nine # NOTE (r74): / # r74: inline revision-tag comments introduced by r74 have been cleaned up and do not regress.
Procedure:
python3 path/to/psa.py --include PSAP0003 \
Deploy-AMDChipsetDriverOnWindowsServer.ps1 \
Deploy-AMDGraphicsDriverOnWindowsServer.ps1 \
Deploy-AMDNpuDriverOnWindowsServer.ps1 \
Deploy-MSBthPanInboxOnWindowsServer.ps1Pass criteria: All four scripts report 0 PSAP0003 findings. The r75 baseline reproduced 9 (7 Chipset, 1 Graphics, 1 BthPan); r76 reports 0.
Regression replay (optional, run only on the r75 source if revisiting historic state):
git checkout <r75-commit> -- Deploy-AMD*.ps1 Deploy-MSBthPan*.ps1
python3 path/to/psa.py --include PSAP0003 Deploy-*.ps1
# Expected: 9 findings (7 Chipset / 1 Graphics / 1 BthPan)
git checkout HEAD -- Deploy-AMD*.ps1 Deploy-MSBthPan*.ps1Purpose: Confirm that the per-script PSAP0005 counts under psap0005_relaxed_mode: true match the documented migration baseline in SPEC §A.11.5.
Procedure:
python3 path/to/psa.py --config .psa.config.json --include PSAP0005 \
Deploy-AMDChipsetDriverOnWindowsServer.ps1 \
Deploy-AMDGraphicsDriverOnWindowsServer.ps1 \
Deploy-AMDNpuDriverOnWindowsServer.ps1 \
Deploy-MSBthPanInboxOnWindowsServer.ps1Pass criteria (initial migration baseline values; these will decrease across subsequent releases as cleanup phases complete per SPEC §A.13 Migration roadmap):
| Script | PSAP0005 (relaxed) |
|---|---|
| Chipset | 22 |
| Graphics | 24 |
| NPU | 2 |
| BthPan | 16 |
A deviation in either direction requires investigation: a higher count means a new revision-anchored reference has been added (regression); a lower count means a cleanup happened without an accompanying SPEC §A.11.5 baseline update.
Purpose: Confirm that the four relaxed-mode exemption patterns (SECTION header, SPEC cross-reference, Added-in-release phrasing, Earlier-revisions prose) are correctly applied and that disallowed forms ("As of rNN, ...") still fire even under relaxed mode.
Procedure: Run the upstream psa.py test suite, which includes Section 2c (15 relaxed-mode test cases) and Section 2d (PSAP0003 + PSAP0005 dedupe).
cd path/to/ai-generated-artifacts/scripts/python/powershell-static-analyzer
python3 test_psa_rules.pyPass criteria: Result: 213 passed, 0 failed. The relevant cases are tagged PSAP0005 relaxed: ... in the output.
Purpose: Confirm that r76 / r42 / r24 / r20 differs from r75 / r41 / r23 / r19 only in (a) inline-comment hygiene rewrites and (b) $Script:ScriptVersion / $Script:ScriptTag updates, with no runtime behaviour change.
Procedure:
# Functional diff scope: ignore identity strings and pure-comment lines
git diff <r75-commit> HEAD -- Deploy-AMD*.ps1 Deploy-MSBthPan*.ps1 \
| grep -E '^[+-]' \
| grep -vE '^[+-]\s*#' \
| grep -vE '\$Script:Script(Version|Tag)\s*='Pass criteria: Empty output (zero non-comment, non-identity diff lines). The only differences are inline-comment text and the two $Script:Script* assignments.
The 2026-05-24 r80 / r46 / r24 / r28 (psa-py-v4-llm-governance-strict)
release completes the migration started at r76. This section
documents the verification steps for the strict-mode flip.
The acceptance criterion of r80 is that all four sister scripts
report 0 errors, 0 warnings, 0 info under the default psa.py
4.0.2 configuration (PSAP0001..PSAP0005 enabled, PSAP0005 in strict
mode because psap0005_relaxed_mode is omitted from
.psa.config.json).
# From the repository root with psa.py 4.0.2 on PATH or relative
python3 /path/to/psa.py --config .psa.config.json \
Deploy-AMDChipsetDriverOnWindowsServer.ps1 \
Deploy-AMDGraphicsDriverOnWindowsServer.ps1 \
Deploy-AMDNpuDriverOnWindowsServer.ps1 \
Deploy-MSBthPanInboxOnWindowsServer.ps1Pass criteria (verified at the r80 release):
File : Deploy-AMDChipsetDriverOnWindowsServer.ps1
Issues : 0 errors, 0 warnings, 0 info
File : Deploy-AMDGraphicsDriverOnWindowsServer.ps1
Issues : 0 errors, 0 warnings, 0 info
File : Deploy-AMDNpuDriverOnWindowsServer.ps1
Issues : 0 errors, 0 warnings, 0 info
File : Deploy-MSBthPanInboxOnWindowsServer.ps1
Issues : 0 errors, 0 warnings, 0 info
A targeted PSAP0005-only run confirms the strict-mode rewrite is complete:
python3 /path/to/psa.py --config .psa.config.json --include PSAP0005 \
Deploy-AMDChipsetDriverOnWindowsServer.ps1 \
Deploy-AMDGraphicsDriverOnWindowsServer.ps1 \
Deploy-AMDNpuDriverOnWindowsServer.ps1 \
Deploy-MSBthPanInboxOnWindowsServer.ps1Pass criteria: No [PSAP0005] line in the output (the analyzer
reports (no issues found) for each script).
The flip is irreversible — removing the relaxed_mode key ensures any future regression fires immediately:
grep -E '"psap0005_relaxed_mode"' .psa.config.jsonPass criteria: No matching line (the key has been removed).
grep -E '^\$Script:ScriptTag' Deploy-AMD*.ps1 Deploy-MSBthPan*.ps1Pass criteria: All four matches show 'psa-py-v4-llm-governance-strict'.
The bulk rewrite must not corrupt UTF-8 BOM or CRLF line endings (PSA7001 / PSA7002 invariants).
# UTF-8 BOM check (first 3 bytes must be 0xef 0xbb 0xbf)
for f in Deploy-AMD*.ps1 Deploy-MSBthPan*.ps1; do
head -c 3 "$f" | od -An -tx1 -N3
done
# CRLF line-ending check (CR count == LF count)
for f in Deploy-AMD*.ps1 Deploy-MSBthPan*.ps1; do
cr=$(tr -cd '\r' < "$f" | wc -c)
lf=$(tr -cd '\n' < "$f" | wc -c)
echo "$f: CR=$cr LF=$lf"
donePass criteria:
- All four BOM checks show
ef bb bf. - All four scripts have equal CR and LF counts (CRLF intact).
The bulk rewrite must not introduce drift on cross-script-shared helpers. PSA8001 enforces this automatically; a clean PSA8001 run under TC19.1 is sufficient evidence. To verify a specific helper manually (illustrative example):
# Compare the byte-identical New-WhqlCoSignAnalysis declaration
# block across Chipset / Graphics / BthPan
for f in Deploy-AMDChipsetDriverOnWindowsServer.ps1 \
Deploy-AMDGraphicsDriverOnWindowsServer.ps1 \
Deploy-MSBthPanInboxOnWindowsServer.ps1; do
awk '/# WHQL co-signature analysis \(see SPEC §D.31\)/,/^ }/' "$f" \
| sha256sum | awk -v f="$f" '{print $1, f}'
donePass criteria: All three SHA-256 hashes are identical (the helper body is byte-for-byte the same across the three scripts).
Five # psa-disable-line PSAP0005 -- AMD ... identifier suppression
directives exist in Graphics for hardware identifiers that
grammatically match rNN (R9700, R1*, V1*). These must remain
in place after the r80 rewrite.
grep -cE 'psa-disable-line PSAP0005 -- AMD' Deploy-AMDGraphicsDriverOnWindowsServer.ps1Pass criteria: Returns 5 (the count is unchanged from r42).
The bulk-rewrite Python script (one-shot, all four files) is recorded
in SPEC.md §D.34 ("D.34.3 Rewrite patterns by category" table) and
in the CHANGELOG.md r80 entry. The script preserves UTF-8 BOM,
CRLF line endings, and applies the same set of regex / literal
replacements to all four .ps1 files in one pass.