From b3c370e77147f5cf7574970474931a37cfc43ae1 Mon Sep 17 00:00:00 2001 From: MarsDoge Date: Fri, 22 May 2026 14:35:21 +0800 Subject: [PATCH 1/3] phase30(docs): add XArch productization validation matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 Phase30 XArch/productization validation matrix,覆盖 X64、AARCH64、LOONGARCH64、RISCV64 的产品化验证状态与 App/provider 边界。 同步 README、Docs、Tests 文档入口,并扩展 smoke gate 检查 XArch 不替代 edk2 ARCH、native HII/FormBrowser/ConfigAccess ownership、Hardware Health demo-only/read-only、ModernUiPreferencesLib app-owned preferences,以及 PCIe native policy 边界。 Validation: - python3 Tests/Smoke/smoke_validate.py - Scripts/xarch-validate.sh --all --mode dry-run --format markdown - Scripts/xarch-validate.sh --all --mode dry-run --format json - git diff --check --- CHANGELOG.md | 7 + Docs/ProductizationFeatureMatrix.md | 3 + Docs/ProductizationFeatureMatrix.zh-CN.md | 2 + Docs/ProductizationValidationMatrix.md | 84 +++++++ Docs/ProductizationValidationMatrix.zh-CN.md | 84 +++++++ Docs/README.md | 1 + Docs/README.zh-CN.md | 1 + Docs/XArch.md | 3 + Docs/XArch.zh-CN.md | 2 + README.md | 2 + README.zh-CN.md | 1 + Tests/README.md | 2 + Tests/Smoke/README.md | 4 + Tests/Smoke/smoke_validate.py | 250 +++++++++++++++++++ 14 files changed, 446 insertions(+) create mode 100644 Docs/ProductizationValidationMatrix.md create mode 100644 Docs/ProductizationValidationMatrix.zh-CN.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a11974..c15aec3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,13 @@ this file as both a release log and a lightweight development progress record. ### Added +- Phase30 XArch/productization validation docs and smoke gate coverage through + `Docs/ProductizationValidationMatrix.md`, its zh-CN counterpart, doc-index + links, and host-side checks for evidence wording, concrete ARCH values, + ModernSetupApp/native HII boundaries, Hardware Health demo-only/read-only + status, app-owned preferences, PCIe policy ownership, and xarch dry-run target + metadata. This is documentation/test coverage only; runtime behavior is + unchanged. - Phase 29 Dashboard density layout: the app-owned `DashboardDensity` preference now feeds the Dashboard grid helper, so `Compact` reduces the top summary area and quick-card spacing while navigation uses the same density-aware diff --git a/Docs/ProductizationFeatureMatrix.md b/Docs/ProductizationFeatureMatrix.md index 7887116..1c46297 100644 --- a/Docs/ProductizationFeatureMatrix.md +++ b/Docs/ProductizationFeatureMatrix.md @@ -9,6 +9,9 @@ SPDX-License-Identifier: BSD-2-Clause-Patent Language: English | [简体中文](ProductizationFeatureMatrix.zh-CN.md) +See also: [ProductizationValidationMatrix.md](ProductizationValidationMatrix.md) for +the Phase30 evidence-backed validation matrix and smoke-gate contract. + ModernSetup has two XArch productization layers: ```text diff --git a/Docs/ProductizationFeatureMatrix.zh-CN.md b/Docs/ProductizationFeatureMatrix.zh-CN.md index 9e41612..e3f701a 100644 --- a/Docs/ProductizationFeatureMatrix.zh-CN.md +++ b/Docs/ProductizationFeatureMatrix.zh-CN.md @@ -9,6 +9,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent 语言:[English](ProductizationFeatureMatrix.md) | 简体中文 +另请参阅:[ProductizationValidationMatrix.zh-CN.md](ProductizationValidationMatrix.zh-CN.md),其中记录 Phase30 基于证据的验证矩阵和 smoke gate 契约。 + ModernSetup 有两层 XArch 产品化边界: ```text diff --git a/Docs/ProductizationValidationMatrix.md b/Docs/ProductizationValidationMatrix.md new file mode 100644 index 0000000..13a77eb --- /dev/null +++ b/Docs/ProductizationValidationMatrix.md @@ -0,0 +1,84 @@ + + +# Productization Validation Matrix + +Language: English | [简体中文](ProductizationValidationMatrix.zh-CN.md) + +This matrix is the Phase30 evidence checklist for XArch productization. It records what can be validated today from repository docs, smoke tests, scripts, and existing provider boundaries. It is not a feature promise and it is not an edk2 build-architecture abstraction. + +XArch is cross-architecture validation/productization vocabulary for ModernSetupPkg. XArch does not replace edk2 ARCH values. Product integrations, DSC/FDF overlays, build scripts, and toolchains still use concrete edk2 ARCH values: `ARCH=X64`, `ARCH=AARCH64`, `ARCH=LOONGARCH64`, and `ARCH=RISCV64`. There is no supported `ARCH=XArch` or `TARGET=XArch` build implication. + +Related source-of-truth docs: + +- [XArch.md](XArch.md) for target mapping and validation language. +- [ProductizationFeatureMatrix.md](ProductizationFeatureMatrix.md) for feature ownership and App/provider scope. +- [CompatibilityMatrix.md](CompatibilityMatrix.md) for DisplayEngine/FormBrowser evidence. +- [Tests/Smoke/README.md](../Tests/Smoke/README.md) for the host-side smoke gate. + +## Evidence Language + +The validation terms below describe current evidence only: + +- `Smoke`: host-side `Tests/Smoke/smoke_validate.py` static and overlay dry-run checks. +- `Script`: a repository script exists and is checked for syntax/metadata. +- `Manual`: local maintainer validation path is documented, but not CI-gated. +- `Captured`: screenshot/screendump evidence exists for a path. +- `Build/script validation`: the script/overlay path is validated without claiming graphical runtime evidence. +- `Planned`: documented target or behavior only; do not describe it as validated. + +## XArch Target Validation Matrix + +| XArch target | Concrete edk2 ARCH | Platform path | Primary scripts | Current maturity evidence | Productization validation notes | +| --- | --- | --- | --- | --- | --- | +| X64 / OVMF X64 | `X64` | `OvmfPkg/OvmfPkgX64` | `Scripts/build-ovmf-x64.sh`, `Scripts/run-ovmf-x64.sh`, `Scripts/capture-ovmf-x64.sh` | Manual OVMF build/run/capture path; smoke overlay generation; local/manual App validation. | Evidence supports target metadata, native/modern DisplayEngine overlay separation, and local screenshot capture path. | +| AARCH64 / ArmVirtQemu | `AARCH64` | `ArmVirtPkg/ArmVirtQemu` | `Scripts/build-armvirt.sh`, `Scripts/run-armvirt.sh`, `Scripts/capture-armvirt.sh`, `Scripts/build-modern-app.sh` | Captured ArmVirt before/after evidence; active build/run path; smoke overlay generation. | Primary compatibility capture path for native UiApp/FormBrowser plus ModernDisplayEngine. | +| LOONGARCH64 / LoongArchVirtQemu | `LOONGARCH64` | `OvmfPkg/LoongArchVirt/LoongArchVirtQemu` | `Scripts/build-loongarchvirt.sh`, `Scripts/run-loongarchvirt.sh` | Active build/run script path; smoke overlay generation. | Evidence covers generated overlays and documented manual run path; external cross toolchain remains product-team responsibility. | +| RISCV64 / RiscVVirtQemu | `RISCV64` | `OvmfPkg/RiscVVirt/RiscVVirtQemu` | `Scripts/build-riscvvirt.sh` | Build/script validation; smoke overlay generation. | RISCV64 remains Build/script validation in Phase30; graphical QEMU helper and captured UI evidence are not claimed. | + +`Scripts/xarch-validate.sh --all --mode dry-run --format json` is the fast target metadata smoke companion. In Phase30 the smoke gate asserts four target `PASS` results and preserves the RISCV64 `Build/script validation` maturity wording. + +## Product Class Validation Matrix + +| Product class | Evidence-backed App role | Native owner / boundary | Current validation evidence | +| --- | --- | --- | --- | +| Desktop / workstation | Dashboard, Boot, Devices/HII, Security, Firmware, Diagnostics, Power/Thermal, Performance, PCIe capability summary, Preferences, Exit. | Boot order editing, Secure Boot key management, CPU/memory tuning, fan policy, PCIe policy, and chipset controls remain native HII/FormBrowser. | OVMF X64 and ArmVirt paths document desktop/workstation-style checks; smoke validates boundary tokens. | +| Server | Dashboard provider health, Management, Diagnostics, Performance, Hardware Health demo visualization, PCIe inventory/policy-entry hints, Exit/native entries. | BMC/IPMI/Redfish configuration, RAS/NUMA policy, PCIe resource policy, ACS/ARI/IOMMU policy, SEL/log clearing remain native or service-app owned. | Provider snapshot, server inventory, management, performance, PCIe, and diagnostics boundaries are smoke-checked. | +| Embedded / industrial | Device/HII entry list, boot/recovery posture, firmware update status, diagnostics evidence, security posture. | GPIO, serial, watchdog, provisioning, boot-pin behavior, board muxes, and power-restore policy remain platform HII/native. | ArmVirt, LoongArchVirt, and RiscVVirt script paths provide cross-arch metadata evidence; product specifics require platform validation. | +| Tablet / appliance | Minimal dashboard, Boot/recovery entry, Security, Firmware, Power/Thermal, Preferences, Exit. | Battery/adapter policy, display panel/backlight, thermal trip points, recovery writes, and appliance provisioning remain native/platform owned. | App/provider docs define read-only status and entry behavior; no product-specific runtime evidence is claimed. | + +## App / Provider Validation Matrix + +| Area | Evidence-backed App/provider behavior | Boundary tokens that must remain true | Phase30 validation evidence | +| --- | --- | --- | --- | +| Dashboard | Shows provider-backed platform, boot, device, firmware, power/thermal, performance, and provider-health summaries. | Dashboard consumes `ModernSetupAppProvider.c` snapshots; it does not call provider LibraryClasses directly. | Smoke checks Dashboard draw ownership, quick-card count, provider snapshot use, and density layout. | +| Boot | Lists boot inventory and launches selected boot options when available. | Boot policy editing and Boot Maintenance remain native; no App-owned platform policy writes. | Smoke covers App source boundaries; feature matrix documents display/entry behavior. | +| Devices / HII | Lists HII/device entries and opens real setup pages through `EFI_FORM_BROWSER2_PROTOCOL.SendForm()`. | Native `FormBrowser2`, native HII, VFR/IFR parsing, `ConfigAccess`, callbacks, validation, defaults, and varstore writes own semantics. ModernSetupApp must not parse IFR, implement ConfigAccess, write HII varstores, or write platform policy. | Smoke checks HII bridge read-only preview boundaries and prohibited App source tokens. | +| Security | Shows read-only Secure Boot, Setup Mode, key presence, and TPM/TCG/TCM posture when discoverable. | Key enrollment, password, physical-presence, measured-boot policy, and chassis security policy remain native. | Smoke validates provider snapshot boundaries and App mutation-token exclusion. | +| Firmware | Shows capsule/update/recovery availability and native entry hints. | Capsule construction, flash programming, rollback policy, and recovery writes remain native/platform utility owned. | Feature and validation docs use evidence language only. | +| Diagnostics | Shows table/log/provider-health summaries and service/debug evidence. | POST log management, error clearing, vendor diagnostics, and repair flows remain native or service-app owned. | Smoke checks provider health derivation and diagnostics inclusion. | +| Management | Shows BMC/IPMI/Redfish presence and management host hints. | BMC networking, users, KVM/media, SEL policy, and remote update configuration remain BMC/native owned. | Smoke checks provider snapshot boundary and server inventory summary. | +| Power / Thermal | Shows ACPI/chassis/power-supply status and can display demo Hardware Health curves. | Fan curves, pump headers, thermal trip points, acoustic profiles, battery, and adapter policy remain native HII/FormBrowser-owned. | Smoke checks Power provider wiring through App boundaries. | +| Hardware Health | Demo-only/read-only provider for deterministic temperature trend UX. | The Hardware Health demo provider does not claim real sensors and does not program SMBus, I2C, IPMI, SuperIO, MMIO, PCI, fan, or trip-point policy. | Smoke checks demo provider files, demo text, read-only docs, and prohibited hardware/mutation tokens. | +| Performance | Shows CPU/memory inventory and tuning/RAS entry availability. | CPU frequency/voltage, memory timing/profile, NUMA/RAS, and workload profile policy remain native. | Smoke checks provider snapshot use and server inventory summary. | +| PCIe | Shows PCIe inventory and read-only capability/native policy-entry hints for ReBAR, Above 4G, SR-IOV, ASPM, bifurcation, hot-plug, ACS, ARI, and IOMMU. | ReBAR, Above 4G, SR-IOV, ASPM, bifurcation, hot-plug, ACS, ARI, IOMMU, BAR/resource allocation, and fabric policy changes stay native HII/FormBrowser-owned. | Smoke checks PCIe provider wiring, mutation-token exclusion, and docs language. | +| Preferences | App-owned UX preferences use `ModernUiPreferencesLib`. | App-owned preferences are not platform policy; platform variables such as BootOrder, SecureBoot, CPU, fan, chipset, and PCIe policy stay out of the preferences library. | Smoke checks `ModernUiPreferencesLib`, app usage, schema/version fields, and no runtime variable access. | +| Exit | Provides session actions, app/version info, language/theme preference access, native UiApp/native setup entries where available. | Save/discard/defaults workflows for real setup variables remain native FormBrowser/platform HII. | Smoke checks App source boundaries and preference routing. | + +## Phase30 Smoke Gate Requirements + +The smoke gate must remain fast and deterministic. For Productization Validation it checks: + +- English and zh-CN validation matrix files exist and link each other. +- X64, AARCH64, LOONGARCH64, and RISCV64 appear with platform paths and scripts. +- XArch does not replace edk2 ARCH values, and no `ARCH=XArch` or `TARGET=XArch` build implication appears. +- ModernSetupApp boundaries: no IFR parsing, no ConfigAccess implementation, no HII varstore writes, no platform policy writes. +- `EFI_FORM_BROWSER2_PROTOCOL.SendForm()`, FormBrowser2/native HII/ConfigAccess ownership, and native semantics are documented. +- Hardware Health remains demo-only/read-only. +- App-owned preferences route through `ModernUiPreferencesLib`. +- PCIe policy tokens remain native-owned: ReBAR, Above 4G, SR-IOV, ASPM, bifurcation, hot-plug, ACS, ARI, and IOMMU. +- `Scripts/xarch-validate.sh --all --mode dry-run --format json` reports four target `PASS` results and preserves RISCV64 as `Build/script validation`. diff --git a/Docs/ProductizationValidationMatrix.zh-CN.md b/Docs/ProductizationValidationMatrix.zh-CN.md new file mode 100644 index 0000000..a95e651 --- /dev/null +++ b/Docs/ProductizationValidationMatrix.zh-CN.md @@ -0,0 +1,84 @@ + + +# 产品化验证矩阵 + +语言:[English](ProductizationValidationMatrix.md) | 简体中文 + +本矩阵是 Phase30 的 XArch 产品化证据清单。它记录当前可以通过仓库文档、smoke 测试、脚本和既有 provider 边界验证的内容。它不是功能承诺,也不是 edk2 构建架构抽象。 + +XArch 是 ModernSetupPkg 的跨架构验证/产品化术语。XArch 不会替代 edk2 ARCH 值。产品集成、DSC/FDF overlay、构建脚本和工具链仍然使用具体 edk2 ARCH 值:`ARCH=X64`、`ARCH=AARCH64`、`ARCH=LOONGARCH64` 和 `ARCH=RISCV64`。当前不存在受支持的 `ARCH=XArch` 或 `TARGET=XArch` 构建含义。 + +相关事实来源文档: + +- [XArch.zh-CN.md](XArch.zh-CN.md):目标映射和验证语言。 +- [ProductizationFeatureMatrix.zh-CN.md](ProductizationFeatureMatrix.zh-CN.md):功能归属与 App/provider 范围。 +- [CompatibilityMatrix.md](CompatibilityMatrix.md):DisplayEngine/FormBrowser 证据。 +- [Tests/Smoke/README.md](../Tests/Smoke/README.md):主机端 smoke gate。 + +## 证据语言 + +以下验证术语只描述当前证据: + +- `Smoke`:主机端 `Tests/Smoke/smoke_validate.py` 静态检查和 overlay dry-run 检查。 +- `Script`:仓库脚本存在,并被语法/元数据检查覆盖。 +- `Manual`:本地维护者验证路径已有文档,但不是 CI gate。 +- `Captured`:相关路径有截图或 screendump 证据。 +- `Build/script validation`:脚本或 overlay 路径已验证,但不声明图形运行证据。 +- `Planned`:仅为规划或已记录目标,不应描述成已验证。 + +## XArch 目标验证矩阵 + +| XArch 目标 | 具体 edk2 ARCH | 平台路径 | 主要脚本 | 当前成熟度证据 | 产品化验证说明 | +| --- | --- | --- | --- | --- | --- | +| X64 / OVMF X64 | `X64` | `OvmfPkg/OvmfPkgX64` | `Scripts/build-ovmf-x64.sh`, `Scripts/run-ovmf-x64.sh`, `Scripts/capture-ovmf-x64.sh` | Manual OVMF 构建/运行/捕获路径;smoke overlay generation;本地/手动 App 验证。 | 证据覆盖目标元数据、native/modern DisplayEngine overlay 分离和本地截图捕获路径。 | +| AARCH64 / ArmVirtQemu | `AARCH64` | `ArmVirtPkg/ArmVirtQemu` | `Scripts/build-armvirt.sh`, `Scripts/run-armvirt.sh`, `Scripts/capture-armvirt.sh`, `Scripts/build-modern-app.sh` | Captured ArmVirt before/after 证据;active 构建/运行路径;smoke overlay generation。 | native UiApp/FormBrowser 加 ModernDisplayEngine 的主要兼容性捕获路径。 | +| LOONGARCH64 / LoongArchVirtQemu | `LOONGARCH64` | `OvmfPkg/LoongArchVirt/LoongArchVirtQemu` | `Scripts/build-loongarchvirt.sh`, `Scripts/run-loongarchvirt.sh` | Active 构建/运行脚本路径;smoke overlay generation。 | 证据覆盖生成 overlay 和已记录的手动运行路径;外部交叉工具链仍由产品团队负责。 | +| RISCV64 / RiscVVirtQemu | `RISCV64` | `OvmfPkg/RiscVVirt/RiscVVirtQemu` | `Scripts/build-riscvvirt.sh` | Build/script validation;smoke overlay generation。 | Phase30 中 RISCV64 仍保持 Build/script validation;不声明图形 QEMU helper 或捕获 UI 证据。 | + +`Scripts/xarch-validate.sh --all --mode dry-run --format json` 是快速目标元数据 smoke 辅助检查。Phase30 smoke gate 会断言四个目标均为 `PASS`,并保持 RISCV64 的 `Build/script validation` 成熟度用语。 + +## 产品类别验证矩阵 + +| 产品类别 | 有证据支持的 App 角色 | 原生 owner / 边界 | 当前验证证据 | +| --- | --- | --- | --- | +| Desktop / workstation | Dashboard、Boot、Devices/HII、Security、Firmware、Diagnostics、Power/Thermal、Performance、PCIe capability summary、Preferences、Exit。 | Boot order 编辑、Secure Boot 密钥管理、CPU/内存调优、风扇策略、PCIe 策略和芯片组控制保持 native HII/FormBrowser 所有。 | OVMF X64 和 ArmVirt 路径记录桌面/工作站风格检查;smoke 验证边界 token。 | +| Server | Dashboard provider health、Management、Diagnostics、Performance、Hardware Health demo 可视化、PCIe inventory/policy-entry hints、Exit/native entries。 | BMC/IPMI/Redfish 配置、RAS/NUMA 策略、PCIe resource policy、ACS/ARI/IOMMU 策略、SEL/log clearing 保持 native 或 service app 所有。 | Provider snapshot、server inventory、management、performance、PCIe 和 diagnostics 边界由 smoke 检查。 | +| Embedded / industrial | Device/HII 入口列表、boot/recovery 姿态、firmware update 状态、diagnostics 证据、安全姿态。 | GPIO、serial、watchdog、provisioning、boot-pin behavior、board muxes 和 power-restore policy 保持平台 HII/native。 | ArmVirt、LoongArchVirt 和 RiscVVirt 脚本路径提供跨架构元数据证据;产品细节需要平台验证。 | +| Tablet / appliance | 最小 Dashboard、Boot/recovery 入口、Security、Firmware、Power/Thermal、Preferences、Exit。 | Battery/adapter policy、display panel/backlight、thermal trip points、recovery writes 和 appliance provisioning 保持 native/platform 所有。 | App/provider 文档定义只读状态和入口行为;不声明产品特定运行证据。 | + +## App / Provider 验证矩阵 + +| 区域 | 有证据支持的 App/provider 行为 | 必须保持的边界 token | Phase30 验证证据 | +| --- | --- | --- | --- | +| Dashboard | 显示 provider-backed platform、boot、device、firmware、power/thermal、performance 和 provider-health summary。 | Dashboard 消费 `ModernSetupAppProvider.c` snapshot;不直接从展示模块调用 provider LibraryClasses。 | Smoke 检查 Dashboard 绘制归属、quick-card count、provider snapshot 使用和 density layout。 | +| Boot | 列出 boot inventory,并在可用时启动选中的 boot option。 | Boot policy editing 和 Boot Maintenance 保持 native;无 App-owned platform policy writes。 | Smoke 覆盖 App source boundary;feature matrix 记录 display/entry 行为。 | +| Devices / HII | 列出 HII/device entries,并通过 `EFI_FORM_BROWSER2_PROTOCOL.SendForm()` 打开真实 setup 页面。 | Native `FormBrowser2`、native HII、VFR/IFR 解析、`ConfigAccess`、callbacks、validation、defaults 和 varstore writes 拥有语义。ModernSetupApp 不得解析 IFR、不得实现 ConfigAccess、不得写 HII varstores、不得写 platform policy。 | Smoke 检查 HII bridge read-only preview 边界和 App source 禁止 token。 | +| Security | 显示只读 Secure Boot、Setup Mode、key presence 和可发现的 TPM/TCG/TCM 姿态。 | Key enrollment、password、physical-presence、measured-boot policy 和 chassis security policy 保持 native。 | Smoke 验证 provider snapshot boundary 和 App mutation-token 排除。 | +| Firmware | 显示 capsule/update/recovery availability 和 native entry hints。 | Capsule construction、flash programming、rollback policy 和 recovery writes 保持 native/platform utility 所有。 | Feature 和 validation 文档仅使用证据语言。 | +| Diagnostics | 显示 table/log/provider-health summaries 和 service/debug evidence。 | POST log management、error clearing、vendor diagnostics 和 repair flows 保持 native 或 service app 所有。 | Smoke 检查 provider health derivation 和 diagnostics inclusion。 | +| Management | 显示 BMC/IPMI/Redfish presence 和 management host hints。 | BMC networking、users、KVM/media、SEL policy 和 remote update configuration 保持 BMC/native 所有。 | Smoke 检查 provider snapshot boundary 和 server inventory summary。 | +| Power / Thermal | 显示 ACPI/chassis/power-supply status,并可展示 demo Hardware Health curves。 | Fan curves、pump headers、thermal trip points、acoustic profiles、battery 和 adapter policy 保持 native HII/FormBrowser-owned。 | Smoke 检查 Power provider 经 App boundary 的 wiring。 | +| Hardware Health | demo-only/read-only provider,用于确定性的 temperature trend UX。 | Hardware Health demo provider 不声明真实传感器,不编程 SMBus、I2C、IPMI、SuperIO、MMIO、PCI、fan 或 trip-point policy。 | Smoke 检查 demo provider 文件、demo 文案、只读文档和禁止硬件/mutation token。 | +| Performance | 显示 CPU/memory inventory 和 tuning/RAS entry availability。 | CPU frequency/voltage、memory timing/profile、NUMA/RAS 和 workload profile policy 保持 native。 | Smoke 检查 provider snapshot 使用和 server inventory summary。 | +| PCIe | 显示 PCIe inventory,以及 ReBAR、Above 4G、SR-IOV、ASPM、bifurcation、hot-plug、ACS、ARI、IOMMU 的只读 capability/native policy-entry hints。 | ReBAR、Above 4G、SR-IOV、ASPM、bifurcation、hot-plug、ACS、ARI、IOMMU、BAR/resource allocation 和 fabric policy changes 保持 native HII/FormBrowser-owned。 | Smoke 检查 PCIe provider wiring、mutation-token 排除和文档语言。 | +| Preferences | App-owned UX preferences 通过 `ModernUiPreferencesLib`。 | App-owned preferences 不是 platform policy;BootOrder、SecureBoot、CPU、fan、chipset 和 PCIe policy 等平台变量不得进入 preferences library。 | Smoke 检查 `ModernUiPreferencesLib`、App 使用、schema/version fields 和无 runtime variable access。 | +| Exit | 提供 session actions、app/version info、language/theme preference access,以及可用时的 native UiApp/native setup entries。 | 真实 setup variables 的 save/discard/defaults 工作流保持 native FormBrowser/platform HII。 | Smoke 检查 App source boundary 和 preference routing。 | + +## Phase30 Smoke Gate 要求 + +Smoke gate 必须保持快速且确定性。Productization Validation 会检查: + +- 英文和 zh-CN 验证矩阵文件存在且互相链接。 +- X64、AARCH64、LOONGARCH64 和 RISCV64 均带有平台路径和脚本。 +- XArch 不会替代 edk2 ARCH 值,并且不存在 `ARCH=XArch` 或 `TARGET=XArch` 构建含义。 +- ModernSetupApp 边界:不解析 IFR、不实现 ConfigAccess、不写 HII varstore、不写 platform policy。 +- 已记录 `EFI_FORM_BROWSER2_PROTOCOL.SendForm()`、FormBrowser2/native HII/ConfigAccess ownership 和 native semantics。 +- Hardware Health 保持 demo-only/read-only。 +- App-owned preferences 通过 `ModernUiPreferencesLib`。 +- PCIe policy tokens 保持 native-owned:ReBAR、Above 4G、SR-IOV、ASPM、bifurcation、hot-plug、ACS、ARI 和 IOMMU。 +- `Scripts/xarch-validate.sh --all --mode dry-run --format json` 报告四个 target `PASS`,并保持 RISCV64 为 `Build/script validation`。 diff --git a/Docs/README.md b/Docs/README.md index fa069ad..ac7e89c 100644 --- a/Docs/README.md +++ b/Docs/README.md @@ -18,6 +18,7 @@ Simplified Chinese counterparts for the core project docs. | --- | --- | --- | | XArch architecture model | [XArch.md](XArch.md) | [XArch.zh-CN.md](XArch.zh-CN.md) | | Productization feature matrix | [ProductizationFeatureMatrix.md](ProductizationFeatureMatrix.md) | [ProductizationFeatureMatrix.zh-CN.md](ProductizationFeatureMatrix.zh-CN.md) | +| Productization validation matrix | [ProductizationValidationMatrix.md](ProductizationValidationMatrix.md) | [ProductizationValidationMatrix.zh-CN.md](ProductizationValidationMatrix.zh-CN.md) | | Module boundaries | [MODULE_BOUNDARIES.md](MODULE_BOUNDARIES.md) | [MODULE_BOUNDARIES.zh-CN.md](MODULE_BOUNDARIES.zh-CN.md) | | Development guide | [DEVELOPMENT.md](DEVELOPMENT.md) | [DEVELOPMENT.zh-CN.md](DEVELOPMENT.zh-CN.md) | | IBV and platform setup survey | [IbvAndPlatformSetupSurvey.md](IbvAndPlatformSetupSurvey.md) | [IbvAndPlatformSetupSurvey.zh-CN.md](IbvAndPlatformSetupSurvey.zh-CN.md) | diff --git a/Docs/README.zh-CN.md b/Docs/README.zh-CN.md index ee40850..8c70c84 100644 --- a/Docs/README.zh-CN.md +++ b/Docs/README.zh-CN.md @@ -17,6 +17,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent | --- | --- | --- | | XArch 架构模型 | [XArch.zh-CN.md](XArch.zh-CN.md) | [XArch.md](XArch.md) | | 产品化功能矩阵 | [ProductizationFeatureMatrix.zh-CN.md](ProductizationFeatureMatrix.zh-CN.md) | [ProductizationFeatureMatrix.md](ProductizationFeatureMatrix.md) | +| 产品化验证矩阵 | [ProductizationValidationMatrix.zh-CN.md](ProductizationValidationMatrix.zh-CN.md) | [ProductizationValidationMatrix.md](ProductizationValidationMatrix.md) | | 模块边界 | [MODULE_BOUNDARIES.zh-CN.md](MODULE_BOUNDARIES.zh-CN.md) | [MODULE_BOUNDARIES.md](MODULE_BOUNDARIES.md) | | 开发指南 | [DEVELOPMENT.zh-CN.md](DEVELOPMENT.zh-CN.md) | [DEVELOPMENT.md](DEVELOPMENT.md) | | IBV 与平台 Setup 调研 | [IbvAndPlatformSetupSurvey.zh-CN.md](IbvAndPlatformSetupSurvey.zh-CN.md) | [IbvAndPlatformSetupSurvey.md](IbvAndPlatformSetupSurvey.md) | diff --git a/Docs/XArch.md b/Docs/XArch.md index 84f49d2..def122c 100644 --- a/Docs/XArch.md +++ b/Docs/XArch.md @@ -9,6 +9,9 @@ SPDX-License-Identifier: BSD-2-Clause-Patent Language: English | [简体中文](XArch.zh-CN.md) +See also: [ProductizationValidationMatrix.md](ProductizationValidationMatrix.md) for +the Phase30 XArch target/product validation evidence matrix. + XArch is ModernSetupPkg's cross-architecture architecture model for keeping one Setup UX, one HII/FormBrowser ownership boundary, and one validation vocabulary across X64, AARCH64, LOONGARCH64, and RISCV64 targets. diff --git a/Docs/XArch.zh-CN.md b/Docs/XArch.zh-CN.md index f95a827..fc5d25d 100644 --- a/Docs/XArch.zh-CN.md +++ b/Docs/XArch.zh-CN.md @@ -9,6 +9,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent 语言:[English](XArch.md) | 简体中文 +另请参阅:[ProductizationValidationMatrix.zh-CN.md](ProductizationValidationMatrix.zh-CN.md),其中记录 Phase30 XArch 目标/产品化验证证据矩阵。 + XArch 是 ModernSetupPkg 的跨架构项目模型:在 X64、AARCH64、LOONGARCH64 和 RISCV64 目标之间保持同一套 Setup UX、同一条 HII/FormBrowser 所有权边界,以及同一套验证词汇。 XArch 是项目、产品和验证词汇,不替代 edk2 的 `ARCH` 值。构建脚本、DSC/FDF overlay、工具链选择和 edk2 构建变量仍使用具体值:`ARCH=X64`、`ARCH=AARCH64`、`ARCH=LOONGARCH64`、`ARCH=RISCV64`。 diff --git a/README.md b/README.md index 4244908..ea9a7aa 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,8 @@ demos. `Docs/XArch.md` defines the target model and validation vocabulary, and `Docs/ProductizationFeatureMatrix.md` records the common desktop, workstation, server, embedded, and appliance front-page capabilities that should be filled in through provider libraries while keeping real setup pages on native FormBrowser. +`Docs/ProductizationValidationMatrix.md` records the Phase30 evidence matrix and +smoke-gate checks for the same XArch/productization boundaries. For a lightweight target metadata check, run: ```sh diff --git a/README.zh-CN.md b/README.zh-CN.md index 055c8e2..25c79c3 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -80,6 +80,7 @@ ModernSetupApp - [`Docs/DEVELOPMENT.zh-CN.md`](Docs/DEVELOPMENT.zh-CN.md) / [`Docs/DEVELOPMENT.md`](Docs/DEVELOPMENT.md):编码规则、函数注释、架构边界和扩展点。 - [`Docs/XArch.zh-CN.md`](Docs/XArch.zh-CN.md) / [`Docs/XArch.md`](Docs/XArch.md):XArch 模型、具体目标映射、验证词汇、成熟度和所有权边界。 - [`Docs/ProductizationFeatureMatrix.zh-CN.md`](Docs/ProductizationFeatureMatrix.zh-CN.md) / [`Docs/ProductizationFeatureMatrix.md`](Docs/ProductizationFeatureMatrix.md):XArch App 功能路线图和 provider 边界。 +- [`Docs/ProductizationValidationMatrix.zh-CN.md`](Docs/ProductizationValidationMatrix.zh-CN.md) / [`Docs/ProductizationValidationMatrix.md`](Docs/ProductizationValidationMatrix.md):Phase30 XArch 产品化证据矩阵和 smoke gate 检查。 - [`Docs/MODULE_BOUNDARIES.zh-CN.md`](Docs/MODULE_BOUNDARIES.zh-CN.md) / [`Docs/MODULE_BOUNDARIES.md`](Docs/MODULE_BOUNDARIES.md):稳定契约和层级规则。 - [`Docs/IbvAndPlatformSetupSurvey.zh-CN.md`](Docs/IbvAndPlatformSetupSurvey.zh-CN.md) / [`Docs/IbvAndPlatformSetupSurvey.md`](Docs/IbvAndPlatformSetupSurvey.md):IBV/OEM/platform form setup surface 调研。 - `CHANGELOG.md`:开发进度、用户可见变化和计划版本工作。 diff --git a/Tests/README.md b/Tests/README.md index 74729ce..76c68cd 100644 --- a/Tests/README.md +++ b/Tests/README.md @@ -89,6 +89,8 @@ Tests pages. - `Docs/ProductizationFeatureMatrix.md` tracks standard App feature coverage across x86, ARM, RISC-V, and LoongArch product classes. +- `Docs/ProductizationValidationMatrix.md` tracks the Phase30 XArch target, + product-class, and App/provider validation matrix enforced by smoke checks. - `Docs/IbvAndPlatformSetupSurvey.md` records the public IBV/OEM/form-factor survey used to decide common App surfaces. diff --git a/Tests/Smoke/README.md b/Tests/Smoke/README.md index ab01643..8e7e27b 100644 --- a/Tests/Smoke/README.md +++ b/Tests/Smoke/README.md @@ -54,6 +54,10 @@ The smoke harness currently checks: - PCIe docs must describe read-only capability summaries and native HII entry hints; real ReBAR, Above 4G, SR-IOV, ASPM, and bifurcation policy changes remain platform HII/FormBrowser-owned. +- `Docs/ProductizationValidationMatrix.md` and its zh-CN counterpart exist, + keep XArch separate from concrete edk2 ARCH values, preserve + ModernSetupApp/HII/native ownership boundaries, and match the dry-run XArch + target metadata report. Use this as the first validation for docs, ownership, script, and static overlay changes. It complements, but does not replace, manual QEMU checks for firmware UI diff --git a/Tests/Smoke/smoke_validate.py b/Tests/Smoke/smoke_validate.py index df75668..8a1823f 100755 --- a/Tests/Smoke/smoke_validate.py +++ b/Tests/Smoke/smoke_validate.py @@ -277,6 +277,7 @@ (Path("README.md"), Path("README.zh-CN.md")), (Path("Docs") / "XArch.md", Path("Docs") / "XArch.zh-CN.md"), (Path("Docs") / "ProductizationFeatureMatrix.md", Path("Docs") / "ProductizationFeatureMatrix.zh-CN.md"), + (Path("Docs") / "ProductizationValidationMatrix.md", Path("Docs") / "ProductizationValidationMatrix.zh-CN.md"), (Path("Docs") / "MODULE_BOUNDARIES.md", Path("Docs") / "MODULE_BOUNDARIES.zh-CN.md"), (Path("Docs") / "DEVELOPMENT.md", Path("Docs") / "DEVELOPMENT.zh-CN.md"), (Path("Docs") / "IbvAndPlatformSetupSurvey.md", Path("Docs") / "IbvAndPlatformSetupSurvey.zh-CN.md"), @@ -292,6 +293,147 @@ "RAS/NUMA/PCIe policy", "ARCH=LOONGARCH64", ) +PRODUCTIZATION_VALIDATION_DOCS = ( + Path("Docs") / "ProductizationValidationMatrix.md", + Path("Docs") / "ProductizationValidationMatrix.zh-CN.md", +) +PRODUCTIZATION_VALIDATION_LINK_SOURCES = ( + Path("README.md"), + Path("README.zh-CN.md"), + Path("Docs") / "README.md", + Path("Docs") / "README.zh-CN.md", + Path("Docs") / "XArch.md", + Path("Docs") / "XArch.zh-CN.md", + Path("Docs") / "ProductizationFeatureMatrix.md", + Path("Docs") / "ProductizationFeatureMatrix.zh-CN.md", + Path("Tests") / "README.md", + Path("Tests") / "Smoke" / "README.md", + Path("CHANGELOG.md"), +) +PRODUCTIZATION_VALIDATION_TARGET_TOKENS = ( + "X64", + "AARCH64", + "LOONGARCH64", + "RISCV64", + "OvmfPkg/OvmfPkgX64", + "ArmVirtPkg/ArmVirtQemu", + "OvmfPkg/LoongArchVirt/LoongArchVirtQemu", + "OvmfPkg/RiscVVirt/RiscVVirtQemu", + "Scripts/build-ovmf-x64.sh", + "Scripts/build-armvirt.sh", + "Scripts/build-loongarchvirt.sh", + "Scripts/build-riscvvirt.sh", +) +PRODUCTIZATION_VALIDATION_DOC_CONTRACTS = ( + ( + Path("Docs") / "ProductizationValidationMatrix.md", + { + "XArch-not-ARCH boundary": ( + "XArch", + "edk2 ARCH", + "ARCH=X64", + "ARCH=AARCH64", + "ARCH=LOONGARCH64", + "ARCH=RISCV64", + ), + "native HII/SendForm ownership": ( + "EFI_FORM_BROWSER2_PROTOCOL.SendForm()", + "FormBrowser2", + "native HII", + "ConfigAccess", + "IFR", + "HII varstores", + "platform policy", + ), + "Hardware Health demo-only/read-only boundary": ( + "Hardware Health", + "demo-only/read-only", + "does not claim real sensors", + "does not program", + ), + "ModernUiPreferencesLib ownership": ( + "Preferences", + "ModernUiPreferencesLib", + "not platform policy", + ), + "PCIe native policy ownership": ( + "PCIe", + "ReBAR", + "Above 4G", + "SR-IOV", + "ASPM", + "bifurcation", + "hot-plug", + "ACS", + "ARI", + "IOMMU", + "native HII", + ), + }, + ), + ( + Path("Docs") / "ProductizationValidationMatrix.zh-CN.md", + { + "XArch-not-ARCH boundary": ( + "XArch", + "不会替代", + "edk2 ARCH", + "ARCH=X64", + "ARCH=AARCH64", + "ARCH=LOONGARCH64", + "ARCH=RISCV64", + ), + "native HII/SendForm ownership": ( + "EFI_FORM_BROWSER2_PROTOCOL.SendForm()", + "FormBrowser2", + "native HII", + "ConfigAccess", + "不得解析 IFR", + "不得写 HII varstores", + "不得写 platform policy", + ), + "Hardware Health demo-only/read-only boundary": ( + "Hardware Health", + "demo-only/read-only", + "不声明真实传感器", + "不编程", + "只读", + ), + "ModernUiPreferencesLib ownership": ( + "Preferences", + "ModernUiPreferencesLib", + "不是 platform policy", + ), + "PCIe native policy ownership": ( + "PCIe", + "ReBAR", + "Above 4G", + "SR-IOV", + "ASPM", + "bifurcation", + "hot-plug", + "ACS", + "ARI", + "IOMMU", + "native HII", + ), + }, + ), +) +PRODUCTIZATION_XARCH_NEGATION_CUES = ( + "no supported", + "not supported", + "does not", + "do not", + "not an edk2 build-architecture abstraction", + "no `ARCH=XArch`", + "no `TARGET=XArch`", + "不存在", + "不会", + "不是 edk2 构建架构抽象", + "不支持", + "不应", +) class SmokeFailure(Exception): @@ -1479,6 +1621,113 @@ def check_bilingual_documentation_contract(root: Path) -> list[str]: return [f"PASS bilingual documentation pairs and IBV taxonomy split: {len(pairs)} pairs"] +def check_xarch_build_tokens_are_negated(relative: Path, text: str) -> None: + for line_number, line in enumerate(text.splitlines(), start=1): + for token in ("ARCH=XArch", "TARGET=XArch"): + if token not in line: + continue + lowered = line.lower() + if not any(cue.lower() in lowered for cue in PRODUCTIZATION_XARCH_NEGATION_CUES): + raise SmokeFailure( + f"{relative}:{line_number} mentions {token} without an explicit same-line rejection" + ) + + +def check_phase30_productization_validation_matrix(root: Path) -> list[str]: + english = root / PRODUCTIZATION_VALIDATION_DOCS[0] + chinese = root / PRODUCTIZATION_VALIDATION_DOCS[1] + for relative in PRODUCTIZATION_VALIDATION_DOCS: + if not (root / relative).exists(): + raise SmokeFailure(f"missing Phase30 productization validation doc: {relative}") + + english_text = english.read_text(encoding="utf-8") + chinese_text = chinese.read_text(encoding="utf-8") + combined = english_text + "\n" + chinese_text + + if PRODUCTIZATION_VALIDATION_DOCS[1].name not in english_text: + raise SmokeFailure("ProductizationValidationMatrix.md missing zh-CN cross-link") + if PRODUCTIZATION_VALIDATION_DOCS[0].name not in chinese_text: + raise SmokeFailure("ProductizationValidationMatrix.zh-CN.md missing English cross-link") + + for relative in PRODUCTIZATION_VALIDATION_LINK_SOURCES: + if "ProductizationValidationMatrix" not in (root / relative).read_text(encoding="utf-8"): + raise SmokeFailure(f"{relative} missing Phase30 validation matrix link/reference") + + for token in PRODUCTIZATION_VALIDATION_TARGET_TOKENS: + if token not in combined: + raise SmokeFailure(f"Phase30 validation matrix missing target/platform/script token: {token}") + + for relative, contracts in PRODUCTIZATION_VALIDATION_DOC_CONTRACTS: + text = (root / relative).read_text(encoding="utf-8") + for area, tokens in contracts.items(): + for token in tokens: + if token not in text: + raise SmokeFailure(f"{relative} missing Phase30 {area} token: {token}") + check_xarch_build_tokens_are_negated(relative, text) + + for required_heading in ( + "XArch Target Validation Matrix", + "Product Class Validation Matrix", + "App / Provider Validation Matrix", + "XArch 目标验证矩阵", + "产品类别验证矩阵", + "App / Provider 验证矩阵", + ): + if required_heading not in combined: + raise SmokeFailure(f"Phase30 validation matrix missing section: {required_heading}") + + for area in ( + "Dashboard", + "Boot", + "Devices / HII", + "Security", + "Firmware", + "Diagnostics", + "Management", + "Power / Thermal", + "Hardware Health", + "Performance", + "PCIe", + "Preferences", + "Exit", + ): + if area not in combined: + raise SmokeFailure(f"Phase30 validation matrix missing App/provider area: {area}") + + bash = shutil.which("bash") + if bash is None: + return ["PASS Phase30 productization validation matrix docs; SKIP xarch JSON smoke: bash not found"] + + with tempfile.TemporaryDirectory(prefix="phase30-xarch-smoke-") as tmp: + json_path = Path(tmp) / "xarch-validation.json" + run( + [ + bash, + str(root / XARCH_RUNNER), + "--all", + "--mode", + "dry-run", + "--format", + "json", + "--output", + str(json_path), + ], + cwd=root, + ) + payload = json.loads(json_path.read_text(encoding="utf-8")) + targets = payload.get("targets") + if not isinstance(targets, list) or len(targets) != 4: + raise SmokeFailure("Phase30 xarch JSON smoke expected four targets") + for target in targets: + if target.get("result") != "PASS": + raise SmokeFailure(f"Phase30 xarch JSON target did not pass: {target}") + riscv = [target for target in targets if target.get("edk2_arch") == "RISCV64"] + if len(riscv) != 1 or riscv[0].get("validation_level") != "Build/script validation": + raise SmokeFailure("Phase30 xarch JSON smoke must keep RISCV64 at Build/script validation") + + return ["PASS Phase30 XArch/productization validation matrix docs and xarch JSON smoke"] + + def check_hii_bridge_view_model_boundary(root: Path) -> list[str]: header = root / "Include" / "ModernUi" / "ModernUiHiiBridge.h" source = root / "Library" / "ModernUiHiiBridgeLib" / "ModernUiHiiBridgeLib.c" @@ -1741,6 +1990,7 @@ def main() -> int: messages.extend(check_hardware_health_demo_provider(root)) messages.extend(check_pcie_docs_language(root)) messages.extend(check_bilingual_documentation_contract(root)) + messages.extend(check_phase30_productization_validation_matrix(root)) messages.extend(check_hii_bridge_view_model_boundary(root)) messages.extend(check_modern_ui_builtin_glyph_subset(root)) messages.extend(check_ip_hygiene_notices(root)) From f9e835cfe7e5ca50dd9050cc30a71bcb925a4124 Mon Sep 17 00:00:00 2001 From: MarsDoge Date: Fri, 22 May 2026 19:20:52 +0800 Subject: [PATCH 2/3] ModernSetupApp: add UiApp replacement mode Add a LoongArchVirt opt-in path that provides the UiApp/Firmware Setup FFS GUID with ModernSetupApp while keeping native BootManagerMenuApp responsible for Boot#### launch.\n\nThe replace-UiApp overlay keeps PcdBootManagerMenuFile on the native UiApp GUID, includes BootManagerMenuApp in the FV, and lets ModernSetupApp hand off boot launch to the native manager. Smoke validation now checks the generated overlay contract.\n\nValidation:\n- python3 Tests/Smoke/smoke_validate.py\n- git diff --check\n- MODERN_SETUP_REPLACE_UIAPP=1 MODERN_SETUP_DISPLAY_ENGINE=modern Scripts/build-loongarchvirt.sh --- Application/ModernSetupApp/ModernSetupApp.c | 2 +- .../ModernSetupApp/ModernSetupAppActions.c | 53 ++++--- Library/ModernUiStringLib/ModernUiStringLib.c | 4 +- Scripts/build-loongarchvirt.sh | 131 +++++++++++++++++- Tests/Manual/LoongArchVirtQemu.md | 8 ++ Tests/Smoke/smoke_validate.py | 38 +++++ 6 files changed, 199 insertions(+), 37 deletions(-) diff --git a/Application/ModernSetupApp/ModernSetupApp.c b/Application/ModernSetupApp/ModernSetupApp.c index 73ff186..29c8307 100644 --- a/Application/ModernSetupApp/ModernSetupApp.c +++ b/Application/ModernSetupApp/ModernSetupApp.c @@ -236,7 +236,7 @@ UefiMain ( Redraw = TRUE; } else if (Page == PageBoot) { Status = ModernSetupLaunchSelectedBootOption (BootSelection); - UnicodeSPrint (StatusMessage, sizeof (StatusMessage), ModernUiGetString (ModernUiStringBootReturnedFormat), Status); + UnicodeSPrint (StatusMessage, sizeof (StatusMessage), ModernUiGetString (ModernUiStringClassicReturnedFormat), Status); Redraw = TRUE; } else if (Page == PageDevices) { Status = ModernSetupOpenSelectedDeviceEntry (DeviceSelection); diff --git a/Application/ModernSetupApp/ModernSetupAppActions.c b/Application/ModernSetupApp/ModernSetupAppActions.c index 2d188b7..8a1a84c 100644 --- a/Application/ModernSetupApp/ModernSetupAppActions.c +++ b/Application/ModernSetupApp/ModernSetupAppActions.c @@ -10,7 +10,11 @@ #include "ModernSetupAppInternal.h" -STATIC CONST EFI_GUID mUiAppGuid = { 0x462CAA21, 0x7614, 0x4503, { 0x83, 0x6E, 0x8A, 0xB6, 0xF4, 0x66, 0x23, 0x31 } }; +#if defined (MODERN_SETUP_NATIVE_FALLBACK_BOOT_MANAGER_MENU) && (MODERN_SETUP_NATIVE_FALLBACK_BOOT_MANAGER_MENU != 0) +STATIC CONST EFI_GUID mNativeFallbackAppGuid = { 0xEEC25BDC, 0x67F2, 0x4D95, { 0xB1, 0xD5, 0xF8, 0x1B, 0x20, 0x39, 0xD1, 0x1D } }; +#else +STATIC CONST EFI_GUID mNativeFallbackAppGuid = { 0x462CAA21, 0x7614, 0x4503, { 0x83, 0x6E, 0x8A, 0xB6, 0xF4, 0x66, 0x23, 0x31 } }; +#endif STATIC CONST MODERN_SETUP_DASHBOARD_ROUTE mDashboardCategoryRoutes[DASHBOARD_QUICK_CARD_COUNT] = { { PageBoot, SetupFocusContent }, @@ -372,39 +376,26 @@ ModernSetupGetPageSelectableCount ( } /** - Boot one visible Boot page row through UefiBootManagerLib. + Open the native Boot Manager for a visible Boot page row. - @param[in] Selection Zero-based visible Boot page row index. + ModernSetupApp is the setup/front-page UI. It lists Boot#### summaries, but + hands Boot#### launch responsibility to edk2's native BootManagerMenuApp (or + UiApp on platforms where the native setup app remains the fallback). Selection + is accepted for the UI contract and is not dereferenced here. - @retval EFI_SUCCESS Boot option was launched and returned. - @retval EFI_NOT_FOUND The selected Boot#### option could not be found. - @retval others Status from boot option decoding or launch. + @param[in] Selection Zero-based visible Boot page row index. Ignored. + + @retval EFI_SUCCESS Native boot manager returned successfully. + @retval EFI_NOT_FOUND Native fallback app could not be resolved. + @retval others Status returned by the native fallback handoff. **/ EFI_STATUS ModernSetupLaunchSelectedBootOption ( IN UINTN Selection ) { - EFI_STATUS Status; - MODERN_UI_BOOT_OPTION *Options; - UINTN OptionCount; - UINT16 OptionNumber; - - Options = NULL; - Status = ModernUiBootDataGetOptions (mModernSetupImageHandle, &Options, &OptionCount); - if (EFI_ERROR (Status)) { - return Status; - } - - if ((Options == NULL) || (Selection >= OptionCount)) { - ModernUiBootDataFreeOptions (Options, OptionCount); - return EFI_NOT_FOUND; - } - - OptionNumber = Options[Selection].OptionNumber; - ModernUiBootDataFreeOptions (Options, OptionCount); - Status = ModernUiBootDataBootOption (OptionNumber); - return Status; + (VOID)Selection; + return ModernSetupLaunchUiAppFallback (mModernSetupImageHandle); } /** @@ -881,11 +872,15 @@ ModernSetupHandlePreferencesEnter ( } /** - Load and start the classic edk2 UiApp from the same firmware volume. + Load and start the native edk2 fallback app from the same firmware volume. + + By default this targets native UiApp. Test builds that replace the UiApp FFS + file with ModernSetupApp define MODERN_SETUP_NATIVE_FALLBACK_BOOT_MANAGER_MENU + so this action targets BootManagerMenuApp instead and avoids self-recursion. @param[in] ImageHandle Current image handle. Must not be NULL. - @retval EFI_SUCCESS UiApp returned successfully. + @retval EFI_SUCCESS Native fallback app returned successfully. @retval EFI_NOT_FOUND Current image device path could not be resolved. @retval EFI_OUT_OF_RESOURCES Device path allocation failed. @retval others Status returned by HandleProtocol(), LoadImage(), @@ -917,7 +912,7 @@ ModernSetupLaunchUiAppFallback ( return EFI_NOT_FOUND; } - EfiInitializeFwVolDevicepathNode (&FileNode, &mUiAppGuid); + EfiInitializeFwVolDevicepathNode (&FileNode, &mNativeFallbackAppGuid); AppPath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&FileNode); if (AppPath == NULL) { return EFI_OUT_OF_RESOURCES; diff --git a/Library/ModernUiStringLib/ModernUiStringLib.c b/Library/ModernUiStringLib/ModernUiStringLib.c index 54acda9..0c23874 100644 --- a/Library/ModernUiStringLib/ModernUiStringLib.c +++ b/Library/ModernUiStringLib/ModernUiStringLib.c @@ -109,7 +109,7 @@ STATIC CONST CHAR16 *mEnglishStrings[ModernUiStringMax] = { L"Key management is intentionally read-only in v1.", L"Use Up/Down to select an action, Enter to run it.", L"Continue boot", - L"Launch classic UiApp fallback", + L"Open Native Boot Manager", L"Reset system", L"Language: %s", L"Language", @@ -125,7 +125,7 @@ STATIC CONST CHAR16 *mEnglishStrings[ModernUiStringMax] = { L"Enter opens the selected form or advances a supported value.", L"RouteConfig returned: %r", L"Boot option returned: %r", - L"Classic UiApp returned: %r", + L"Native Boot Manager returned: %r", L"ModernSetupApp: graphics initialization failed: %r\n", L"Boot & Devices", L"Platform Health", diff --git a/Scripts/build-loongarchvirt.sh b/Scripts/build-loongarchvirt.sh index 4de0c88..e319c1c 100755 --- a/Scripts/build-loongarchvirt.sh +++ b/Scripts/build-loongarchvirt.sh @@ -16,10 +16,22 @@ JOBS="${JOBS:-$(sysctl -n hw.ncpu 2>/dev/null || echo 4)}" MODERN_SETUP_DEMO_DRIVER_SAMPLE="${MODERN_SETUP_DEMO_DRIVER_SAMPLE:-1}" MODERN_SETUP_THEME="${MODERN_SETUP_THEME:-orange}" MODERN_SETUP_DISPLAY_ENGINE="${MODERN_SETUP_DISPLAY_ENGINE:-modern}" -GCC_LOONGARCH64_PREFIX="${GCC_LOONGARCH64_PREFIX:-loongarch64-unknown-linux-gnu-}" +MODERN_SETUP_INCLUDE_APP="${MODERN_SETUP_INCLUDE_APP:-0}" +MODERN_SETUP_REPLACE_UIAPP="${MODERN_SETUP_REPLACE_UIAPP:-0}" +GCC_LOONGARCH64_PREFIX="${GCC_LOONGARCH64_PREFIX:-}" GENERATE_ONLY="${GENERATE_ONLY:-0}" OVERLAY_DIR="${WORKSPACE}/Build/ModernSetupPkgOverlay" +if [[ -z "${GCC_LOONGARCH64_PREFIX}" ]]; then + if command -v loongarch64-unknown-linux-gnu-gcc >/dev/null 2>&1; then + GCC_LOONGARCH64_PREFIX="loongarch64-unknown-linux-gnu-" + elif command -v loongarch64-linux-gnu-gcc >/dev/null 2>&1; then + GCC_LOONGARCH64_PREFIX="loongarch64-linux-gnu-" + else + GCC_LOONGARCH64_PREFIX="loongarch64-unknown-linux-gnu-" + fi +fi + export PATH="/opt/homebrew/bin:${PATH}" export WORKSPACE export GCC_LOONGARCH64_PREFIX @@ -32,8 +44,9 @@ fi mkdir -p "${OVERLAY_DIR}" -python3 - <<'PY' "${WORKSPACE}" "${OVERLAY_DIR}" "${MODERN_SETUP_DEMO_DRIVER_SAMPLE}" "${MODERN_SETUP_THEME}" "${MODERN_SETUP_DISPLAY_ENGINE}" +python3 - <<'PY' "${WORKSPACE}" "${OVERLAY_DIR}" "${MODERN_SETUP_DEMO_DRIVER_SAMPLE}" "${MODERN_SETUP_THEME}" "${MODERN_SETUP_DISPLAY_ENGINE}" "${MODERN_SETUP_INCLUDE_APP}" "${MODERN_SETUP_REPLACE_UIAPP}" from pathlib import Path +import re import sys workspace = Path(sys.argv[1]) @@ -41,6 +54,8 @@ overlay = Path(sys.argv[2]) enable_driver_sample = sys.argv[3] != "0" theme_name = sys.argv[4].strip().lower() display_engine = sys.argv[5].strip().lower() +include_app_flag = sys.argv[6].strip().lower() +replace_uiapp_flag = sys.argv[7].strip().lower() theme_pcd = { "orange": "0x00", "amber": "0x00", @@ -58,15 +73,50 @@ if display_engine not in {"modern", "native"}: raise SystemExit( f"Unsupported MODERN_SETUP_DISPLAY_ENGINE={display_engine!r}; use modern or native" ) +if include_app_flag not in {"0", "1", "false", "true", "no", "yes"}: + raise SystemExit( + f"Unsupported MODERN_SETUP_INCLUDE_APP={include_app_flag!r}; use 0 or 1" + ) +if replace_uiapp_flag not in {"0", "1", "false", "true", "no", "yes"}: + raise SystemExit( + f"Unsupported MODERN_SETUP_REPLACE_UIAPP={replace_uiapp_flag!r}; use 0 or 1" + ) +replace_uiapp = replace_uiapp_flag in {"1", "true", "yes"} +include_modern_setup_app = (include_app_flag in {"1", "true", "yes"}) or replace_uiapp +modern_setup_app_component = " ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf" +modern_setup_app_component_boot_manager_fallback = """ ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf { + + GCC:*_*_*_CC_FLAGS = -DMODERN_SETUP_NATIVE_FALLBACK_BOOT_MANAGER_MENU=1 + }""" +modern_setup_app_fdf_inf = "INF ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf" +modern_setup_app_uiapp_fdf_inf = "INF RuleOverride = MODERN_SETUP_UIAPP ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf" modern_display_component = " ModernSetupPkg/Universal/ModernDisplayEngineDxe/ModernDisplayEngineDxe.inf" modern_display_fdf_inf = "INF ModernSetupPkg/Universal/ModernDisplayEngineDxe/ModernDisplayEngineDxe.inf" +boot_manager_menu_component = " MdeModulePkg/Application/BootManagerMenuApp/BootManagerMenuApp.inf" +boot_manager_menu_fdf_inf = "INF MdeModulePkg/Application/BootManagerMenuApp/BootManagerMenuApp.inf" driver_sample_component = " MdeModulePkg/Universal/DriverSampleDxe/DriverSampleDxe.inf" driver_sample_fdf_inf = "INF MdeModulePkg/Universal/DriverSampleDxe/DriverSampleDxe.inf" library_block = """ ModernUiEngineLib|ModernSetupPkg/Library/ModernUiEngineLib/ModernUiEngineLib.inf ModernUiRendererLib|ModernSetupPkg/Library/ModernUiRendererLib/ModernUiRendererLib.inf ModernUiThemeLib|ModernSetupPkg/Library/ModernUiThemeLib/ModernUiThemeLib.inf """ +app_library_block = """ ModernUiPlatformDataLib|ModernSetupPkg/Library/ModernUiPlatformDataLib/ModernUiPlatformDataLib.inf + ModernUiBootDataLib|ModernSetupPkg/Library/ModernUiBootDataLib/ModernUiBootDataLib.inf + ModernUiDeviceDataLib|ModernSetupPkg/Library/ModernUiDeviceDataLib/ModernUiDeviceDataLib.inf + ModernUiSecurityDataLib|ModernSetupPkg/Library/ModernUiSecurityDataLib/ModernUiSecurityDataLib.inf + ModernUiFirmwareDataLib|ModernSetupPkg/Library/ModernUiFirmwareDataLib/ModernUiFirmwareDataLib.inf + ModernUiHardwareHealthDataLib|ModernSetupPkg/Library/ModernUiHardwareHealthDataLib/ModernUiHardwareHealthDataLib.inf + ModernUiHiiBridgeLib|ModernSetupPkg/Library/ModernUiHiiBridgeLib/ModernUiHiiBridgeLib.inf + ModernUiDiagnosticsDataLib|ModernSetupPkg/Library/ModernUiDiagnosticsDataLib/ModernUiDiagnosticsDataLib.inf + ModernUiManagementDataLib|ModernSetupPkg/Library/ModernUiManagementDataLib/ModernUiManagementDataLib.inf + ModernUiPowerDataLib|ModernSetupPkg/Library/ModernUiPowerDataLib/ModernUiPowerDataLib.inf + ModernUiPerformanceDataLib|ModernSetupPkg/Library/ModernUiPerformanceDataLib/ModernUiPerformanceDataLib.inf + ModernUiPcieDataLib|ModernSetupPkg/Library/ModernUiPcieDataLib/ModernUiPcieDataLib.inf + ModernUiInputLib|ModernSetupPkg/Library/ModernUiInputLib/ModernUiInputLib.inf + ModernUiPreferencesLib|ModernSetupPkg/Library/ModernUiPreferencesLib/ModernUiPreferencesLib.inf + ModernUiStringLib|ModernSetupPkg/Library/ModernUiStringLib/ModernUiStringLib.inf +""" dsc_path = workspace / "OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc" dsc = dsc_path.read_text() @@ -81,8 +131,39 @@ dsc = dsc.replace( 1, ) if "ModernUiEngineLib|ModernSetupPkg" not in dsc: - if display_engine == "modern": + if display_engine == "modern" or include_modern_setup_app: dsc = dsc.replace("[LibraryClasses.common]\n", "[LibraryClasses.common]\n" + library_block, 1) +if include_modern_setup_app and "ModernUiPlatformDataLib|ModernSetupPkg" not in dsc: + dsc = dsc.replace("[LibraryClasses.common]\n", "[LibraryClasses.common]\n" + app_library_block, 1) +if include_modern_setup_app and modern_setup_app_component not in dsc: + dsc = dsc.replace( + " MdeModulePkg/Application/UiApp/UiApp.inf {", + (modern_setup_app_component_boot_manager_fallback if replace_uiapp else modern_setup_app_component) + "\n MdeModulePkg/Application/UiApp/UiApp.inf {", + 1, + ) +if replace_uiapp and boot_manager_menu_component not in dsc: + dsc = dsc.replace( + " MdeModulePkg/Application/UiApp/UiApp.inf {", + boot_manager_menu_component + "\n MdeModulePkg/Application/UiApp/UiApp.inf {", + 1, + ) +if replace_uiapp: + dsc, uiapp_component_count = re.subn( + r"(?m)^ MdeModulePkg/Application/UiApp/UiApp\.inf \{\r?\n" + r"(?: [^\r\n]*\r?\n)*?" + r" \}\r?\n", + "", + dsc, + count=1, + ) + if uiapp_component_count != 1: + raise SystemExit("MODERN_SETUP_REPLACE_UIAPP could not remove native UiApp DSC component") + if enable_driver_sample and driver_sample_component not in dsc: + dsc = dsc.replace( + " OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf {", + driver_sample_component + "\n OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf {", + 1, + ) if display_engine == "modern": dsc = dsc.replace( " CustomizedDisplayLib | MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf", @@ -125,18 +206,58 @@ if display_engine == "modern": modern_display_fdf_inf, 1, ) -if enable_driver_sample and driver_sample_fdf_inf not in fdf: +if replace_uiapp: + if modern_setup_app_uiapp_fdf_inf not in fdf: + fdf = fdf.replace( + "INF MdeModulePkg/Application/UiApp/UiApp.inf", + modern_setup_app_uiapp_fdf_inf, + 1, + ) + if boot_manager_menu_fdf_inf not in fdf: + fdf = fdf.replace( + modern_setup_app_uiapp_fdf_inf, + boot_manager_menu_fdf_inf + "\n" + modern_setup_app_uiapp_fdf_inf, + 1, + ) +elif include_modern_setup_app and modern_setup_app_fdf_inf not in fdf: fdf = fdf.replace( "INF MdeModulePkg/Application/UiApp/UiApp.inf", - driver_sample_fdf_inf + "\nINF MdeModulePkg/Application/UiApp/UiApp.inf", + modern_setup_app_fdf_inf + "\nINF MdeModulePkg/Application/UiApp/UiApp.inf", 1, ) +if enable_driver_sample and driver_sample_fdf_inf not in fdf: + if replace_uiapp: + fdf = fdf.replace( + modern_setup_app_uiapp_fdf_inf, + driver_sample_fdf_inf + "\n" + modern_setup_app_uiapp_fdf_inf, + 1, + ) + else: + fdf = fdf.replace( + "INF MdeModulePkg/Application/UiApp/UiApp.inf", + driver_sample_fdf_inf + "\nINF MdeModulePkg/Application/UiApp/UiApp.inf", + 1, + ) +if replace_uiapp and "[Rule.Common.UEFI_APPLICATION.MODERN_SETUP_UIAPP]" not in fdf: + fdf += ( + "\n[Rule.Common.UEFI_APPLICATION.MODERN_SETUP_UIAPP]\n" + " FILE APPLICATION = 462CAA21-7614-4503-836E-8AB6F4662331 {\n" + " PE32 PE32 $(INF_OUTPUT)/$(MODULE_NAME).efi\n" + " UI STRING=\"ModernSetupApp\" Optional\n" + " }\n" + ) (overlay / "LoongArchVirtQemuModernSetup.fdf").write_text(fdf) PY echo "Generated: ${OVERLAY_DIR}/LoongArchVirtQemuModernSetup.dsc" echo "Generated: ${OVERLAY_DIR}/LoongArchVirtQemuModernSetup.fdf" echo "DisplayEngine: ${MODERN_SETUP_DISPLAY_ENGINE}" +if [[ "${MODERN_SETUP_REPLACE_UIAPP}" =~ ^(1|true|yes)$ ]]; then + echo "ModernSetupApp in FV: 1 (via MODERN_SETUP_REPLACE_UIAPP)" +else + echo "ModernSetupApp in FV: ${MODERN_SETUP_INCLUDE_APP}" +fi +echo "Replace UiApp with ModernSetupApp: ${MODERN_SETUP_REPLACE_UIAPP}" if [[ "${GENERATE_ONLY}" == "1" ]]; then exit 0 diff --git a/Tests/Manual/LoongArchVirtQemu.md b/Tests/Manual/LoongArchVirtQemu.md index ea3d5d6..0f464f0 100644 --- a/Tests/Manual/LoongArchVirtQemu.md +++ b/Tests/Manual/LoongArchVirtQemu.md @@ -36,6 +36,11 @@ Expected result: - With `MODERN_SETUP_DISPLAY_ENGINE=native`, the overlay keeps upstream `DisplayEngineDxe` for before/after comparison. - The overlay keeps native `UiApp` as the setup entry. +- For the opt-in architecture-validation image, + `MODERN_SETUP_REPLACE_UIAPP=1` keeps LoongArchVirt's original + `PcdBootManagerMenuFile` pointing at the UiApp file GUID, replaces that FV + file with `ModernSetupApp`, and adds native `BootManagerMenuApp` to the FV so + ModernSetupApp's native fallback does not recurse into itself. - `DriverSampleDxe` is included by default unless `MODERN_SETUP_DEMO_DRIVER_SAMPLE=0` is set. - `Build/LoongArchVirtQemu/DEBUG_GCC/FV/QEMU_EFI.fd` exists. @@ -69,6 +74,9 @@ Expected result: - QEMU opens a LoongArch64 graphical window. - Pressing `Esc` or `F2` during BDS enters native `UiApp`. - UiApp rendering is handled by `ModernDisplayEngineDxe`. +- With `MODERN_SETUP_REPLACE_UIAPP=1`, the same firmware setup entry enters + `ModernSetupApp`; use the app's native fallback action to launch + `BootManagerMenuApp` for the native Boot#### launch path. - Device Manager can enumerate platform HII formsets. - DriverSample appears automatically when the demo driver is enabled. - No ASSERT, exception, or GOP initialization failure is printed to serial. diff --git a/Tests/Smoke/smoke_validate.py b/Tests/Smoke/smoke_validate.py index 8a1823f..0e638b2 100755 --- a/Tests/Smoke/smoke_validate.py +++ b/Tests/Smoke/smoke_validate.py @@ -502,6 +502,13 @@ def check_static_overlay_script_contracts(root: Path) -> list[str]: for token in PROHIBITED_DEFAULT_OVERLAY_TOKENS: if token in text: + is_loongarch_replace_uiapp_opt_in = ( + name == "build-loongarchvirt.sh" + and "MODERN_SETUP_REPLACE_UIAPP" in text + and token in {"ModernSetupApp", "ModernUiHiiBridgeLib"} + ) + if is_loongarch_replace_uiapp_opt_in: + continue raise SmokeFailure(f"{path} default overlay generator references prohibited token: {token}") lowered = text.lower() @@ -561,6 +568,7 @@ def loongarch_fixture(workspace: Path) -> None: workspace / "OvmfPkg" / "LoongArchVirt" / "LoongArchVirtQemu.dsc", """[Defines] FLASH_DEFINITION = OvmfPkg/LoongArchVirt/LoongArchVirtQemu.fdf + gEfiMdeModulePkgTokenSpaceGuid.PcdBootManagerMenuFile | { 0x21, 0xaa, 0x2c, 0x46, 0x14, 0x76, 0x03, 0x45, 0x83, 0x6e, 0x8a, 0xb6, 0xf4, 0x66, 0x23, 0x31 } !include LoongArchVirt.fdf.inc [LibraryClasses.common] @@ -1966,6 +1974,36 @@ def check_overlay_generation(root: Path) -> list[str]: messages.append(f"PASS {platform} {engine} overlay generation dry run") + overlay_dir = workspace / "Build" / "ModernSetupPkgOverlay" + if overlay_dir.exists(): + shutil.rmtree(overlay_dir) + + env = os.environ.copy() + env.update( + { + "WORKSPACE": str(workspace), + "GENERATE_ONLY": "1", + "MODERN_SETUP_DISPLAY_ENGINE": "modern", + "MODERN_SETUP_DEMO_DRIVER_SAMPLE": "0", + "MODERN_SETUP_REPLACE_UIAPP": "1", + } + ) + script = workspace / "ModernSetupPkg" / "Scripts" / "build-loongarchvirt.sh" + run([bash, str(script)], cwd=workspace / "ModernSetupPkg", env=env) + + dsc = workspace / "Build" / "ModernSetupPkgOverlay" / "LoongArchVirtQemuModernSetup.dsc" + fdf = workspace / "Build" / "ModernSetupPkgOverlay" / "LoongArchVirtQemuModernSetup.fdf" + assert_contains(dsc, "ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf") + assert_contains(dsc, "MdeModulePkg/Application/BootManagerMenuApp/BootManagerMenuApp.inf") + assert_contains( + dsc, + "gEfiMdeModulePkgTokenSpaceGuid.PcdBootManagerMenuFile | { 0x21, 0xaa, 0x2c, 0x46, 0x14, 0x76, 0x03, 0x45, 0x83, 0x6e, 0x8a, 0xb6, 0xf4, 0x66, 0x23, 0x31 }", + ) + assert_contains(fdf, "INF MdeModulePkg/Application/BootManagerMenuApp/BootManagerMenuApp.inf") + assert_contains(fdf, "INF RuleOverride = MODERN_SETUP_UIAPP ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf") + assert_contains(fdf, "FILE APPLICATION = 462CAA21-7614-4503-836E-8AB6F4662331") + messages.append("PASS loongarch replace-uiapp opt-in overlay generation dry run") + return messages From 51d3beca4faab536b4998f418b73b567c83f0ce2 Mon Sep 17 00:00:00 2001 From: MarsDoge Date: Fri, 22 May 2026 19:20:52 +0800 Subject: [PATCH 3/3] README: show Graphite Gold dashboard capture Replace the old OVMF dashboard display with a Graphite Gold themed ModernSetupApp homepage capture and update the English and Chinese README wording to match.\n\nValidation:\n- ARCH=X64 Scripts/build-modern-app.sh\n- Scripts/build-ovmf-x64.sh\n- QEMU screendump capture via Scripts/capture-ovmf-x64.sh\n- git diff --check --- ...odern-ovmf-x64-dashboard-graphite-gold.png | Bin 0 -> 57816 bytes README.md | 22 ++++++++++-------- README.zh-CN.md | 4 ++-- 3 files changed, 14 insertions(+), 12 deletions(-) create mode 100644 Assets/Screenshots/modern-ovmf-x64-dashboard-graphite-gold.png diff --git a/Assets/Screenshots/modern-ovmf-x64-dashboard-graphite-gold.png b/Assets/Screenshots/modern-ovmf-x64-dashboard-graphite-gold.png new file mode 100644 index 0000000000000000000000000000000000000000..7cde1507c807521e56700f60d3bf92933d104190 GIT binary patch literal 57816 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