XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFESystem

Deploy Console and Desktop as separate single-system images

a62698b
XFE工作室室长 <mail@xfegzs.com>
提交于

代码差异

13 个文件 +1010 -82
Modified AGENTS.md +12 -0
@@ -1,5 +1,17 @@
1 1 # XFESystem 用户约定
2 2
3 2026-10-16 用户明确纠正:**两个版本必须分别作为单系统交付,每次写入只能是 Console 或 Desktop 之一,不能同时放入两个系统让用户开机选择。** 本轮用户已选择 **Desktop 图形系统**。此新要求覆盖下文所有历史“双启动镜像、两菜单、10 秒选择、IndependentDualCSharp256”交付约定;历史记录和产物保留,但不得继续按旧双启动计划写盘。后续镜像每张只含一个对应内核、一个静默启动入口、零选择等待,保留既有 Logo / XFESystem / 真实就绪加载动画。Console 和 Desktop 各自构建、验收、打包,写入使用对应单系统镜像与 `IndependentCSharp128` 新计划/token 流程;切换版本需重写另一张单系统镜像。本轮 U 盘曾暂未检测到,随后恢复正常身份和容量;已按下述新单系统计划完成 Desktop 写入与完整校验。
4
5 用户本轮确认关注普通 .NET 控制台应用。已有 `run X:\Programs\ManagedHello\ManagedHello.dll` 运行真实标准 .NET PE 的受限 ConsoleIL,不能描述为“没有运行 C# 控制台程序的能力”;也不能描述为支持任意 .NET 应用。当前 `StorageEnabled=false`、`MemoryWorkspace` 不读取物理盘,`X:` 是 RAM;其他盘文件导入与完整 CLR/API 兼容均需单独实现和验收。内核使用 Microsoft NativeAOT 不等于支持 Assembly.LoadFile 或通用 DLL 动态加载。
6
7 2026-10-16 已完成 **0.3.0-dev.4-single.1 / Console、Desktop 分别交付**。镜像生成器删除组合两个内核的入口,每张 128 MiB 镜像只有所选版本的 `System/Kernel.elf`、一个静默启动入口和零菜单等待。只读解析器遍历实际 FAT 文件目录并核验启动配置;来宾、启动器和打包器均校验单系统证据。写盘器在任何目标磁盘操作前拒绝旧 `IndependentDualCSharp256` 配置;历史双镜像仍可只读审计。补齐 `IndependentCSharp128` 的 Console 专用验收,不再强制其满足 Desktop 的 Bing / Monaco 条件。
8
9 16 组、1451 项宿主检查、9 项单系统策略检查、28 项写盘证据保护检查及 13 项历史双镜像只读保护检查通过。两个新独立镜像通过 QEMU 8 虚拟 CPU(实际 BSP 1)的动画及相应完整应用验收。使用 `0.3.0-dev.4-gpu-usb.2` 的完全相同 Console / Desktop 内核字节(未重编译内核),EFI 按主目录实际源码重新构建;新镜像、回执与源码重新绑定,不沿用旧镜像验收。首轮过期 EFI 回执被拒绝及写盘测试 fixture 漏复制 GPU 诊断 JSON 的失败保留;更新回执、修复测试复制清单后分别通过。860 个宿主/来宾合并输入保持构建字节一致,859 个与暂存字节相同;唯一例外是原有 `EfiEntry.cs` CRLF 差异,规范化文本与 HEAD 一致、继续未暂存。宿主 `build/CSharp/HostTests-1792088104518/Result.json`;最终 Console / Desktop `build/IndependentKernel/Proof-1792088141356190500/Result.json` / `Proof-1792088141570011300/Result.json`。
10
11 **本轮专用 U 盘已更新为 Desktop 单系统。** USB 序列号 `230222336207`、UniqueId `5000000000002AEF`、实际容量 `256060514304` 字节、扇区 512,使用新 `IndependentCSharp128` 计划/token。源镜像 SHA256 `7e18578812e96f5a13b165506c876029d255a5940c502058af3628aee94628eb`;完整 `134217728` 字节容量调整后的预期/实际 SHA256 均为 `854a7e287656bc438ff876d10a6801c2a16198d3e17d222f6eedba5257ebdd2a`,真实盘尾备份 GPT `ActualLastLba=500118191` 通过。句柄关闭,辅助进程 PID `57524` 退出码 0 且已不存在,fresh 身份相符、非系统/启动盘、保持离线。记录 `build/usb-media/XFEstudio-Desktop-Single-0.3.0-dev.4-1792088411452/Completion.json` / `Postflight.json`,完成记录 SHA256 `6aa471aa350a367c9bed918dce4c5835a9bd65ec1c41053658f02f73cc5d8f27`。
12
13 两个独立 ZIP 完整回读、解压哈希和重定位后的 Windows PowerShell 5.1 启动计划通过:`dist/XFESystem-Console-Independent-RAM-0.3.0-dev.4-single.1-1792088311403-4c2f330b.zip`(SHA256 `e658a7e08294bad4cdb7dc98f19c17aba8e8494385d78f9f345fcbc38b7a1694`)和 `dist/XFESystem-Desktop-Independent-RAM-0.3.0-dev.4-single.1-1792088352538-330ae19f.zip`(SHA256 `c6070ffe9868ed2657201e4f9505c801debf0b75afafe30bc0eea1ce2f0c6efe`)。详情 `docs/validation-results/SingleEditionDeployment20261016.json`。`DeliveryComplete=true` 仅表示本轮 Desktop 单系统实验交付完成;实体启动/输入、物理盘读取、完整 .NET 动态应用、GPU/CUDA、SMP 与长期稳定性均未由本轮取得验收,继续 RAM-only、`FullReleaseQualified=false`。
14
3 15 2026-10-15 **0.3.0-dev.4-gpu-usb.2** 已将 RTX 5090 只读资源/FSP 状态诊断合入最新双启动版本,原启动动画、Monaco 延迟分析优化和网页功能保留。最终镜像进一步修复 xHCI 1.x 集线器 Configure Endpoint、单 TRB Data Stage 的 CH=0,以及先读 8 字节再协商 EP0 最大包长。QEMU 不模型化集线器 Hub/TT 回读,仅为精确 `1b36:000d` 虚拟设备保留明确例外;实体控制器必须回读匹配。原严格回读失败留存,不能宣称 QEMU 验证了实体 TT 硬件。当前独立内核新增自研 C# xHCI 原生工作线程,完成固件接管、端口供电、集线器枚举和 HID 键鼠;只处理集线器/HID,不读写 USB 存储。16 组、1451 项宿主检查通过,含 19 项 HID 检查;873 个宿主/来宾/GPU 绑定输入与暂存字节完全一致;两个独立版本和同一 256 MiB 双启动镜像两个实际菜单入口通过 QEMU 8 虚拟 CPU(实际 BSP 1)动画/应用验收。两版本均在 **i8042 关闭、只有 xHCI USB 键鼠、经过集线器** 的配置实际编辑、执行命令并拔插键盘重新输入通过。无串口图形启动、GPU 源码保护检查、118 项设备映射断言和 157 项固件资源断言通过。首轮宿主 HTTP 测试的测试服务器关闭竞态已修复,原失败记录保留。实体键鼠、RGB 灯效、CUDA/GPU 渲染仍未验收;系统内 CUDA 与 GPU 渲染未实现,`FullReleaseQualified=false`,继续 RAM-only。
4 16
5 17 新镜像 SHA256 `531bcf6c033548bf7960372dd0361fefcdcc85e6eb9a35e6b213f692fef859b9`,两个 ZIP 全回读、解压哈希、重定位与 PowerShell 5.1 启动计划通过,已复制到主目录 `dist`。专用 USB 又报告 262144 字节 / UniqueId `5001FF20564C4900`,精确存储父节点恢复返回 3010 后未恢复。用户明确回复“暂不方便,先完成镜像”,因此本轮 **未开始原始写盘、未改写 U 盘镜像**;记录 `build/usb-media/XFEstudio-Dual-GpuUsb2-0.3.0-dev.4-1792075963972/NotWritten.json`。恢复后再按同一身份和 `IndependentDualCSharp256` 新计划流程写入。构建工作树 `C:\Users\XFEstudio\.codex\worktrees\bing-native-https\XFESystem`;主目录原有 `EfiEntry.cs` 换行差异继续保留。详情 `docs/DualGpuUsb20261015.md` 和 `docs/validation-results/DualGpuUsb20261015.json`。`ImageDeliveryComplete=true`,`DeliveryComplete=false`、`UsbWritten=false`。
Modified README.md +8 -4
@@ -15,7 +15,7 @@ XFESystem 默认使用 **C# 自研裸机内核**:页分配与映射、中断
15 15 | Console | 参考旧 Rust 版的深色全屏控制台、品牌页眉、状态栏和文本光标;RAM 文件命令、历史、光标编辑与 ConsoleIL | `Source/XFESystem.NativeRuntime/ConsoleApplication/ConsoleApplication.csproj` |
16 16 | Desktop | 原有图形桌面、窗口、任务栏、文件、浏览器、文档与终端 | `Source/XFESystem.NativeRuntime/Application/NativeApplication.csproj` |
17 17
18 Console 入口不初始化桌面、窗口管理器、浏览器、Monaco、文档或网络,不链接这些应用依赖;直接使用 framebuffer 输出文字和内核 PS/2 键盘输入。外部字体和运行时许可仍适用。两版都是 RAM-only 实验系统,关机不保存。专用 U 盘的双系统镜像提供 `XFESystem Desktop` 和 `XFESystem Console` 菜单,默认 10 秒后进入 Desktop;上下键选择,Enter 启动。
18 Console 入口不初始化桌面、窗口管理器、浏览器、Monaco、文档或网络,不链接这些应用依赖;直接使用 framebuffer 输出文字,使用现有 PS/2 或 xHCI HID 输入。外部字体和运行时许可仍适用。两版都是 RAM-only 实验系统,关机不保存。**2026-10-16 用户纠正交付方式:一张镜像、一次 U 盘写入只包含其中一个系统,启动后直接进入所选版本,不提供 Console / Desktop 选择菜单。本轮选择 Desktop。** 两个版本继续分别提供独立镜像和启动包;切换版本需要重新写入另一张单系统镜像。
19 19
20 20 ```powershell
21 21 # 分别构建;省略 Edition 时仍默认 Desktop
@@ -27,9 +27,13 @@ python .\Tools\IndependentKernel\TestBoot.py <Console的Kernel.elf> --console --
27 27 python .\Tools\IndependentKernel\TestBoot.py <Desktop的Kernel.elf> --desktop --browser --documents --bing --virtual-cpus 8 --timeout 360
28 28 ```
29 29
30 分别向 `Tools/OpenCSharp.ps1` 或 `Tools/PackageCSharp.ps1` 传入 `-Edition Console` / `-Edition Desktop` 及对应版本的镜像和验收回执。控制台验收不替代图形版的 Bing、Monaco、Markdown 和终端验收。版本拆分与实际交付记录见 [双系统说明](docs/ConsoleAndDesktopEditions20261015.md)。
30 分别向 `Tools/OpenCSharp.ps1` 或 `Tools/PackageCSharp.ps1` 传入 `-Edition Console` / `-Edition Desktop` 及对应版本的镜像和验收回执。控制台验收不替代图形版的 Bing、Monaco、Markdown 和终端验收。版本拆分与单系统写入方式见 [独立版本说明](docs/ConsoleAndDesktopEditions20261015.md)。镜像生成器已移除组合两个内核的入口,写盘器拒绝旧双启动配置;历史双启动回执只用于只读审计。
31 31
32 0.3.0-dev.4 的 16 组、1332 项宿主检查通过;两版独立镜像及同一 256 MiB 双启动镜像的两个实际菜单入口均通过 QEMU 8 虚拟 CPU(实际一个 BSP)验收。两个独立 ZIP 已完整回读、解压核验,并通过重定位后的 Windows PowerShell 5.1 启动计划。专用 U 盘已更新为双启动镜像,完整回读与真实盘尾 GPT 校验通过,保持离线。实际哈希、源码绑定、保留的失败和复测记录见 [本轮验证摘要](docs/validation-results/ConsoleDesktopEditions20261015.json)。实体启动与输入仍待验收,当前不是完整发行版。
32 2026-10-16 的 **0.3.0-dev.4-single.1** 已分别生成 Console / Desktop 单系统镜像和 ZIP。16 组、1451 项宿主检查、单系统策略及写盘保护检查通过;两版独立 QEMU 动画和应用验收、ZIP 全回读、解压哈希和 Windows PowerShell 5.1 启动计划通过。专用 U 盘已按用户选择写入 **Desktop 单系统**,完整 128 MiB 与实际盘尾备份 GPT 校验通过,保持离线。源与回读哈希、源码绑定、历史失败见 [单系统交付摘要](docs/validation-results/SingleEditionDeployment20261016.json)。
33
34 现有 `run X:\Programs\ManagedHello\ManagedHello.dll` 能运行符合 ConsoleIL 指令/API 范围的标准 .NET C# 控制台程序集;这是真实 PE 解释执行,不是预设输出,也不是任意 .NET 应用兼容。`X:` 仍是内存工作区,当前没有物理磁盘数据挂载或外部文件导入能力。程序运行兼容性与其他盘文件读取是两个独立功能;内核采用 NativeAOT 不代表已经具备通用 DLL 动态加载。
35
36 以下 2026-10-15 的双启动交付属于历史记录;后续按上述单系统政策交付。0.3.0-dev.4 当时的 16 组、1332 项宿主检查通过;两版独立镜像及同一 256 MiB 双启动镜像的两个实际菜单入口均通过 QEMU 8 虚拟 CPU(实际一个 BSP)验收。两个独立 ZIP 已完整回读、解压核验,并通过重定位后的 Windows PowerShell 5.1 启动计划。专用 U 盘已更新为双启动镜像,完整回读与真实盘尾 GPT 校验通过,保持离线。实际哈希、源码绑定、保留的失败和复测记录见 [本轮验证摘要](docs/validation-results/ConsoleDesktopEditions20261015.json)。实体启动与输入仍待验收,当前不是完整发行版。
33 37
34 38 2026-10-15 的 `0.3.0-dev.4-loadspeed.1` 修复了 CSS 解析的二次方扫描,按选择器右端筛选候选规则,复用 DOM 引导语法及同源 HTTP/TLS 连接,并移除文档加载任务之间的 50 ms 轮询空档。同一固定网页在原生 QEMU 中的三次加载中位数由 921 ms 降至 375 ms,网页区域逐像素一致;九个实际资源请求由九条 TCP 连接降为一条。合并后的 Markdown 验收样例首帧为 0.44–0.90 秒,完整 Monaco 初始化尚未稳定提速。16 组、1377 项宿主检查、独立图形镜像与新双启动镜像的两个入口均通过验收;新图形 ZIP 和专用双启动 U 盘已完整校验。原始计时、Chromium 源码对照、复测命令和能力边界见 [加载优化验证记录](docs/validation-results/BrowserLoadSpeed20261015.json)。
35 39
@@ -49,7 +53,7 @@ python .\Tools\IndependentKernel\TestBoot.py <Desktop的Kernel.elf> --desktop --
49 53
50 54 此前 0.3.0-dev.3 使用同一最终镜像通过 QEMU 8 虚拟 CPU(实际一个 BSP)的原生 HTTPS Bing、JavaScript DOM 结果 12、Monaco 实际字形/颜色和第四行键盘编辑、Markdown 八项流程及经典终端九次命令验收。1314 项宿主检查通过;同一内核的单 CPU 随机源缺失测试正确拒绝 HTTPS 并保持桌面响应。地址栏后台刷新覆盖输入、裸机默认 LF 导致提示符缩进的问题均有实际复现与修复。该版 ZIP 完整回读、重定位验证和 PowerShell 5.1 启动计划通过。历史范围和交付状态见 [0.3.0-dev.3 验证摘要](docs/validation-results/IndependentNativeBing20261015.json);此前 0.3.0-dev.2 的记录保留于 [合并摘要](docs/validation-results/IndependentKernelMerge20261015.json)。
51 55
52 专用 U 盘此前的 0.3.0-dev.3 更新、首次管理员提权取消及后续成功重试均保留历史记录,详见该版摘要。最新双版本交付以 [品牌启动动画摘要](docs/validation-results/BootAnimation20261015.json) 为准。`DeliveryComplete=true` 表示实验镜像交付完成,实体启动与输入仍需实测,不代表完整发行版验收。
56 专用 U 盘此前的 0.3.0-dev.3 更新、首次管理员提权取消及后续成功重试均保留历史记录,详见该版摘要。最新交付以 [单系统交付摘要](docs/validation-results/SingleEditionDeployment20261016.json) 为准。`DeliveryComplete=true` 表示实验镜像交付完成,实体启动与输入仍需实测,不代表完整发行版验收。
53 57
54 58 RTX 5090 的只读硬件与 CUDA 前提诊断已合入双启动版本;Windows 宿主 CUDA 参考结果不代表 XFESystem 内 CUDA 已实现。详见 [原生 CUDA 状态](docs/NativeCudaPort.md)。
55 59
Modified Tools/CSharpBoot/Program.cs +9 -50
@@ -3,11 +3,9 @@ using System.Security.Cryptography;
3 3 using System.Text;
4 4 using System.Text.Json;
5 5
6 if (args.Length is < 2 or > 6 || (args.Length >= 4 && args[3] != "--independent-kernel") ||
7 args.Length == 5 || (args.Length == 6 && args[4] != "--console-kernel"))
8 throw new ArgumentException("Usage: PackImage <Kernel.elf> <new System.img> [widthxheight] [--independent-kernel [--console-kernel <Console Kernel.elf>]]");
6 if (args.Length is < 2 or > 4 || (args.Length == 4 && args[3] != "--independent-kernel"))
7 throw new ArgumentException("Usage: PackImage <Console or Desktop Kernel.elf> <new System.img> [widthxheight] [--independent-kernel]. Each image contains one system; dual-system images are no longer generated.");
9 8 var independentKernel = args.Length >= 4;
10 var dualEdition = args.Length == 6;
11 9 var resolution = args.Length >= 3 ? args[2] : "0x0";
12 10 if (resolution.Split('x') is not [var resolutionWidth, var resolutionHeight] ||
13 11 !int.TryParse(resolutionWidth, out var width) || !int.TryParse(resolutionHeight, out var height) ||
@@ -27,9 +25,6 @@ if (kernel.Length < 64 || !kernel.AsSpan(0, 4).SequenceEqual("\u007fELF"u8) || k
27 25 BinaryPrimitives.ReadUInt16LittleEndian(kernel.AsSpan(18)) != 62)
28 26 throw new InvalidDataException("Expected an ELF64 x86_64 kernel.");
29 27 JsonElement? independentBuild = null;
30 JsonElement? independentConsoleBuild = null;
31 byte[]? consoleKernel = null;
32 string? consoleKernelPath = null;
33 28 if (independentKernel)
34 29 {
35 30 using var buildDocument = JsonDocument.Parse(File.ReadAllText(kernelPath + ".json"));
@@ -48,35 +43,6 @@ if (independentKernel)
48 43 }
49 44 independentBuild = build.Clone();
50 45 }
51 if (dualEdition)
52 {
53 if (independentBuild!.Value.GetProperty("Edition").GetString() != "Desktop" ||
54 independentBuild.Value.GetProperty("BootstrapOnly").GetBoolean() ||
55 !independentBuild.Value.GetProperty("ManagedRuntimeLinked").GetBoolean())
56 throw new InvalidDataException("The first dual-system kernel must be the Desktop edition.");
57 consoleKernelPath = Path.GetFullPath(args[5]);
58 consoleKernel = File.ReadAllBytes(consoleKernelPath);
59 if (consoleKernel.Length < 64 || !consoleKernel.AsSpan(0, 4).SequenceEqual("\u007fELF"u8) ||
60 consoleKernel[4] != 2 || BinaryPrimitives.ReadUInt16LittleEndian(consoleKernel.AsSpan(18)) != 62)
61 throw new InvalidDataException("Expected an ELF64 x86_64 console kernel.");
62 using var consoleDocument = JsonDocument.Parse(File.ReadAllText(consoleKernelPath + ".json"));
63 var consoleBuild = consoleDocument.RootElement;
64 if (consoleBuild.GetProperty("Kind").GetString() != "XfeIndependentKernelBuild" ||
65 !consoleBuild.GetProperty("Passed").GetBoolean() || consoleBuild.GetProperty("ContainsCosmos").GetBoolean() ||
66 consoleBuild.GetProperty("BootstrapOnly").GetBoolean() || consoleBuild.GetProperty("Edition").GetString() != "Console" ||
67 !consoleBuild.GetProperty("ManagedRuntimeLinked").GetBoolean() ||
68 consoleBuild.GetProperty("Target").GetString() != "x86_64-none" ||
69 consoleBuild.GetProperty("KernelSha256").GetString() != Convert.ToHexStringLower(SHA256.HashData(consoleKernel)))
70 throw new InvalidDataException("The second dual-system kernel needs its exact, complete Console build receipt.");
71 foreach (var source in consoleBuild.GetProperty("SourceSha256").EnumerateObject())
72 {
73 var sourcePath = Path.GetFullPath(Path.Combine(rootPath, source.Name));
74 if (!sourcePath.StartsWith(expectedPrefix, StringComparison.OrdinalIgnoreCase) ||
75 Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(sourcePath))) != source.Value.GetString())
76 throw new InvalidDataException("Console kernel source changed: " + source.Name);
77 }
78 independentConsoleBuild = consoleBuild.Clone();
79 }
80 46 var loaderPath = Path.Combine(rootPath, ".tools/limine/BOOTX64.EFI");
81 47 var loader = File.ReadAllBytes(loaderPath);
82 48 var shimReceiptPath = Path.Combine(rootPath, "build/CSharpBoot/BootShimBuild/BootShimReceipt.json");
@@ -115,7 +81,7 @@ var shimMetadata = JsonSerializer.SerializeToUtf8Bytes(new
115 81 RefreshRateMeaning = "Monitor/EDID target; no GPU VSync guarantee"
116 82 }, new JsonSerializerOptions { WriteIndented = true });
117 83 const int sectorSize = 512;
118 int diskBytes = (dualEdition ? 256 : 128) * 1024 * 1024;
84 const int diskBytes = 128 * 1024 * 1024;
119 85 const int first = 2048;
120 86 int last = diskBytes / sectorSize - 1;
121 87 int partitionLast = last - 33;
@@ -164,12 +130,10 @@ var nextCluster = 2;
164 130 var rootCluster = Allocate(1); var efiCluster = Allocate(1);
165 131 var efiBootCluster = Allocate(1); var efiXfeCluster = Allocate(1); var systemCluster = Allocate(1);
166 132 var shimCluster = Store(shim); var loaderCluster = Store(loader); var kernelCluster = Store(kernel);
167 var consoleKernelCluster = consoleKernel is null ? 0 : Store(consoleKernel);
168 133 var shimMetadataCluster = Store(shimMetadata);
169 var edition = dualEdition ? "Dual" : independentBuild is { } appBuild && appBuild.TryGetProperty("Edition", out var builtEdition) ? builtEdition.GetString() : "Desktop";
170 var configText = $"timeout: {(dualEdition ? 10 : 0)}\nserial: yes\nverbose: no\nterse: yes\nquiet: {(dualEdition ? "no" : "yes")}\nfirmware_logo: no\n\n/XFESystem {(independentKernel ? edition == "Console" ? "Console" : "Desktop" : "CSharp")}\n protocol: limine\n resolution: {resolution}x32\n path: boot():/System/Kernel.elf\n";
171 if (dualEdition)
172 configText += $"\n/XFESystem Console\n protocol: limine\n resolution: {resolution}x32\n path: boot():/System/Console.elf\n";
134 var edition = independentBuild is { } appBuild && appBuild.TryGetProperty("Edition", out var builtEdition) ? builtEdition.GetString() : "Desktop";
135 if (edition is not ("Console" or "Desktop")) throw new InvalidDataException("An image must contain exactly one Console or Desktop system.");
136 var configText = $"timeout: 0\nserial: yes\nverbose: no\nterse: yes\nquiet: yes\nfirmware_logo: no\n\n/XFESystem {(independentKernel ? edition : "CSharp")}\n protocol: limine\n resolution: {resolution}x32\n path: boot():/System/Kernel.elf\n";
173 137 var config = Encoding.UTF8.GetBytes(configText);
174 138 var configCluster = Store(config);
175 139 Directory(rootCluster,
@@ -184,8 +148,6 @@ Directory(efiXfeCluster, Dot(efiXfeCluster), DotDot(efiCluster),
184 148 var systemEntries = new List<byte[]> { Dot(systemCluster), DotDot(0),
185 149 Entry("Kernel.elf", "KERNEL ELF", false, kernelCluster, kernel.Length),
186 150 Entry("BootShim.json", "BOOTSHIMJSN", false, shimMetadataCluster, shimMetadata.Length) };
187 if (consoleKernel is not null)
188 systemEntries.Add(Entry("Console.elf", "CONSOLE ELF", false, consoleKernelCluster, consoleKernel.Length));
189 151 Directory(systemCluster, systemEntries.ToArray());
190 152 fat.CopyTo(image, partition + reserved * sectorSize);
191 153 fat.CopyTo(image, partition + (reserved + fatSectors) * sectorSize);
@@ -213,12 +175,9 @@ var receipt = new
213 175 UnrestrictedManagedDllLoading = false,
214 176 KernelImplementation = independentKernel ? "XFESystem independent kernel" : "Cosmos 3.0.94 integration",
215 177 IndependentKernelBuild = independentBuild,
216 ConsoleKernelPath = consoleKernelPath,
217 ConsoleKernelSha256 = consoleKernel is null ? null : Convert.ToHexStringLower(SHA256.HashData(consoleKernel)),
218 IndependentConsoleKernelBuild = independentConsoleBuild,
219 BootEntries = dualEdition ? new[] { "Desktop", "Console" } : new[] { edition },
220 DefaultBootEntry = dualEdition ? "Desktop" : edition, BootMenuTimeoutSeconds = dualEdition ? 10 : 0
221 , BootVisualPolicy = independentBuild is { } visualBuild && visualBuild.TryGetProperty("BootVisualPolicy", out var visualPolicy) ? visualPolicy.GetString() : "Legacy"
178 DeploymentPolicy = "SingleEditionOnly", BootEntries = new[] { edition },
179 DefaultBootEntry = edition, BootMenuTimeoutSeconds = 0,
180 BootVisualPolicy = independentBuild is { } visualBuild && visualBuild.TryGetProperty("BootVisualPolicy", out var visualPolicy) ? visualPolicy.GetString() : "Legacy"
222 181 };
223 182 File.WriteAllText(outputPath + ".json", JsonSerializer.Serialize(receipt, new JsonSerializerOptions { WriteIndented = true }));
224 183 Console.WriteLine(JsonSerializer.Serialize(receipt));
Modified Tools/CSharpBoot/ReadImage.py +32 -0
@@ -113,6 +113,17 @@ def inspect_image(path):
113 113 assert size <= len(value)
114 114 return value[:size]
115 115
116 file_paths, directories = [], set()
117 def walk(cluster, prefix='', depth=0):
118 assert depth < 8 and cluster not in directories, 'Cyclic or excessively nested FAT directory'
119 directories.add(cluster)
120 for name, attributes, child, _ in entries(cluster):
121 if name in ('.', '..'): continue
122 path = prefix + name
123 if attributes & 16: walk(child, path + '/', depth + 1)
124 else: file_paths.append(path)
125 assert len(file_paths) <= 1024, 'Unexpected FAT file count'
126 walk(root)
116 127 files = {name: read_file(name) for name in ['EFI/BOOT/BOOTX64.EFI', 'EFI/XFE/Limine.efi', 'System/Kernel.elf', 'System/BootShim.json', 'Limine.conf']}
117 128 if 'boot():/System/Console.elf' in files['Limine.conf'].decode('utf-8'):
118 129 files['System/Console.elf'] = read_file('System/Console.elf')
@@ -127,4 +138,25 @@ def inspect_image(path):
127 138 'GptPrimaryAndBackupVerified': True, 'FatMirrorsVerified': True,
128 139 'FatBootAndFsInfoBackupsVerified': True,
129 140 'DisplayInitializerMetadataVerified': True, 'BootShimSourceSha256': metadata['SourceSha256'],
141 'FilePaths': sorted(file_paths),
130 142 'Files': {name: hashlib.sha256(content).hexdigest() for name, content in files.items()}}, files
143
144
145 def verify_single_edition(inspection, files, manifest):
146 """Check the actual FAT inventory and boot configuration, not just a filename or label."""
147 edition = manifest.get('Edition')
148 assert edition in ('Console', 'Desktop'), 'Select one Console or Desktop system'
149 assert manifest.get('DeploymentPolicy') == 'SingleEditionOnly', 'Single-system deployment policy is required'
150 assert manifest.get('ImageBytes') == 128 * 1024 * 1024, 'A single-system image is 128 MiB'
151 assert manifest.get('BootEntries') == [edition] and manifest.get('DefaultBootEntry') == edition, 'Exactly one boot entry is required'
152 assert manifest.get('BootMenuTimeoutSeconds') == 0, 'A single-system image must enter without a selection menu'
153 assert not any(manifest.get(name) for name in ('ConsoleKernelPath', 'ConsoleKernelSha256', 'IndependentConsoleKernelBuild')), 'A second kernel is not permitted'
154 expected_paths = sorted(['EFI/BOOT/BOOTX64.EFI', 'EFI/XFE/Limine.efi', 'System/Kernel.elf', 'System/BootShim.json', 'Limine.conf'])
155 assert inspection.get('FilePaths') == expected_paths, 'Unexpected files or additional system kernels in the actual FAT image'
156 expected_config = ('timeout: 0\nserial: yes\nverbose: no\nterse: yes\nquiet: yes\nfirmware_logo: no\n\n'
157 f'/XFESystem {edition}\n protocol: limine\n resolution: {manifest["Resolution"]}x32\n path: boot():/System/Kernel.elf\n')
158 assert files['Limine.conf'].decode('utf-8') == expected_config, 'The actual boot configuration must silently start only the selected system'
159 assert manifest.get('IndependentKernelBuild', {}).get('Edition') == edition, 'The selected kernel edition differs'
160 return {'Kind': 'XfeSingleEditionImageProof', 'Passed': True, 'Edition': edition,
161 'KernelCount': 1, 'BootEntryCount': 1, 'SystemSelectionMenu': False, 'BootMenuTimeoutSeconds': 0,
162 'FilePaths': expected_paths, 'ConfigurationSha256': hashlib.sha256(files['Limine.conf']).hexdigest()}
Added Tools/CSharpBoot/TestSingleEditionImage.py +94 -0
@@ -0,0 +1,94 @@
1 """Offline image-policy negative controls; actual boot acceptance is recorded separately."""
2 import copy
3 import json
4 from pathlib import Path
5 import subprocess
6 import tempfile
7 import unittest
8
9 from ReadImage import verify_single_edition
10
11 ROOT = Path(__file__).resolve().parents[2]
12
13
14 class SingleEditionImageTests(unittest.TestCase):
15 def fixture(self, edition='Desktop'):
16 paths = sorted(['EFI/BOOT/BOOTX64.EFI', 'EFI/XFE/Limine.efi', 'System/Kernel.elf', 'System/BootShim.json', 'Limine.conf'])
17 manifest = {'Edition': edition, 'DeploymentPolicy': 'SingleEditionOnly', 'ImageBytes': 134217728,
18 'BootEntries': [edition], 'DefaultBootEntry': edition, 'BootMenuTimeoutSeconds': 0,
19 'Resolution': '1280x800', 'IndependentKernelBuild': {'Edition': edition}}
20 config = ('timeout: 0\nserial: yes\nverbose: no\nterse: yes\nquiet: yes\nfirmware_logo: no\n\n'
21 f'/XFESystem {edition}\n protocol: limine\n resolution: 1280x800x32\n path: boot():/System/Kernel.elf\n')
22 return {'FilePaths': paths}, {'Limine.conf': config.encode()}, manifest
23
24 def test_each_edition_has_one_kernel_and_no_selection_menu(self):
25 for edition in ('Console', 'Desktop'):
26 with self.subTest(edition=edition):
27 result = verify_single_edition(*self.fixture(edition))
28 self.assertEqual(edition, result['Edition'])
29 self.assertFalse(result['SystemSelectionMenu'])
30
31 def test_dual_or_mislabeled_kernel_is_rejected(self):
32 for edition in ('Dual', 'Unknown'):
33 inspection, files, manifest = self.fixture(); manifest['Edition'] = edition
34 with self.subTest(edition=edition), self.assertRaises(AssertionError):
35 verify_single_edition(inspection, files, manifest)
36 inspection, files, manifest = self.fixture(); manifest['IndependentKernelBuild']['Edition'] = 'Console'
37 with self.assertRaises(AssertionError): verify_single_edition(inspection, files, manifest)
38
39 def test_multiple_boot_entries_are_rejected(self):
40 inspection, files, manifest = self.fixture(); manifest['BootEntries'].append('Console')
41 with self.assertRaises(AssertionError): verify_single_edition(inspection, files, manifest)
42
43 def test_second_kernel_in_manifest_is_rejected(self):
44 for key in ('ConsoleKernelPath', 'ConsoleKernelSha256', 'IndependentConsoleKernelBuild'):
45 inspection, files, manifest = self.fixture(); manifest[key] = 'second kernel'
46 with self.subTest(key=key), self.assertRaises(AssertionError):
47 verify_single_edition(inspection, files, manifest)
48
49 def test_hidden_second_kernel_in_actual_fat_inventory_is_rejected(self):
50 inspection, files, manifest = self.fixture(); inspection['FilePaths'].append('System/Console.elf')
51 with self.assertRaises(AssertionError): verify_single_edition(inspection, files, manifest)
52
53 def test_menu_and_kernel_path_changes_are_rejected(self):
54 _, baseline, _ = self.fixture()
55 for config in (baseline['Limine.conf'] + b'\n/XFESystem Console\n',
56 baseline['Limine.conf'].replace(b'quiet: yes', b'quiet: no'),
57 baseline['Limine.conf'].replace(b'System/Kernel.elf', b'System/Console.elf')):
58 inspection, files, manifest = self.fixture(); files['Limine.conf'] = config
59 with self.subTest(config=config), self.assertRaises(AssertionError):
60 verify_single_edition(inspection, files, manifest)
61
62 def test_timeout_and_old_dual_size_are_rejected(self):
63 for changes in ({'BootMenuTimeoutSeconds': 10}, {'ImageBytes': 268435456}, {'DeploymentPolicy': 'Dual'}):
64 inspection, files, manifest = self.fixture(); manifest.update(changes)
65 with self.subTest(changes=changes), self.assertRaises(AssertionError):
66 verify_single_edition(inspection, files, manifest)
67
68 def test_old_combined_image_cli_cannot_write_output(self):
69 with tempfile.TemporaryDirectory() as folder:
70 output = Path(folder)/'System.img'
71 result = subprocess.run(['dotnet', str(ROOT/'Tools/CSharpBoot/bin/Release/net10.0/PackImage.dll'),
72 'Desktop.elf', str(output), '1280x800', '--independent-kernel', '--console-kernel', 'Console.elf'],
73 capture_output=True, text=True)
74 self.assertNotEqual(0, result.returncode)
75 self.assertIn('dual-system images are no longer generated', result.stderr)
76 self.assertFalse(output.exists())
77
78 def test_old_dual_media_profile_is_rejected_before_disk_access(self):
79 for write in (False, True):
80 with self.subTest(write=write), tempfile.TemporaryDirectory() as folder:
81 log = Path(folder)/'Rejected.json'
82 result = subprocess.run(['powershell.exe', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass',
83 '-File', str(ROOT/'scripts/physical-media.ps1'), '-ImageProfile', 'IndependentDualCSharp256',
84 '-ResultPath', str(log), *(['-Write'] if write else [])], capture_output=True)
85 self.assertNotEqual(0, result.returncode)
86 receipt = json.loads(log.read_text(encoding='utf-8-sig'))
87 self.assertFalse(receipt['PhysicalDiskOpened'])
88 self.assertTrue(receipt['HandlesClosed'])
89 self.assertEqual('failed', receipt['Status'])
90 self.assertIn('Dual-system deployment was retired', receipt['Error'])
91
92
93 if __name__ == '__main__':
94 unittest.main()
Modified Tools/IndependentKernel/Open.ps1 +17 -1
@@ -187,6 +187,22 @@ if(!$network -or $network.Kind -cne 'XfeIndependentNativeNetworkProfile' -or $ne
187 187 Assert-IndependentHash $sourceImage $boot.ImageSha256 'verified image'
188 188 $imageManifestPath=$sourceImage+'.json'
189 189 $imageManifest=Read-IndependentJson $imageManifestPath
190 if($imageManifest.DeploymentPolicy -ceq 'SingleEditionOnly') {
191 $single=$boot.SingleEditionImageProof
192 $expectedFiles=@('EFI/BOOT/BOOTX64.EFI','EFI/XFE/Limine.efi','Limine.conf','System/BootShim.json','System/Kernel.elf')
193 if($imageManifest.Edition -cne $Edition -or $imageManifest.ImageBytes -ne 134217728 -or
194 @($imageManifest.BootEntries).Count -ne 1 -or $imageManifest.BootEntries[0] -cne $Edition -or
195 $imageManifest.DefaultBootEntry -cne $Edition -or $imageManifest.BootMenuTimeoutSeconds -ne 0 -or
196 !$single -or $single.Kind -cne 'XfeSingleEditionImageProof' -or $single.Passed -ne $true -or
197 $single.Edition -cne $Edition -or $single.KernelCount -ne 1 -or $single.BootEntryCount -ne 1 -or
198 $single.SystemSelectionMenu -ne $false -or $single.BootMenuTimeoutSeconds -ne 0 -or $null -ne $boot.BootEntry -or
199 ($single.FilePaths -join '|') -cne ($expectedFiles -join '|') -or
200 ($boot.ImageVerification.FilePaths -join '|') -cne ($expectedFiles -join '|') -or
201 $single.ConfigurationSha256 -cne $boot.ImageVerification.Files.'Limine.conf' -or
202 $imageManifest.ConsoleKernelPath -or $imageManifest.ConsoleKernelSha256 -or $imageManifest.IndependentConsoleKernelBuild) {
203 throw 'Single-system delivery requires one verified kernel, one silent boot entry and no system selection menu.'
204 }
205 }
190 206 $kernelImagePath=if($boot.KernelImagePath){[string]$boot.KernelImagePath}else{'System/Kernel.elf'}
191 207 if($kernelImagePath -cnotin @('System/Kernel.elf','System/Console.elf') -or
192 208 ($kernelImagePath -ceq 'System/Console.elf' -and ($Edition -cne 'Console' -or $imageManifest.Edition -cne 'Dual'))) {
@@ -423,7 +439,7 @@ $verified=[pscustomobject]@{Edition=$Edition;KernelImagePath=$kernelImagePath;Im
423 439 BingEvidencePath=$bingEvidencePath;BingEvidenceSha256=$bingBinding.Sha256;BingEvidence=$bingEvidence}
424 440 if($ValidateOnly){return $verified}
425 441 if($imageManifest.Edition -ceq 'Dual') {
426 throw 'Interactive launch and launch plans require a single-edition image. Use the Console or Desktop package Start.ps1; the shared USB image provides its own Desktop/Console boot menu. Dual images support -ValidateOnly here.'
442 throw 'Choose the independent Console or Desktop image. Historical dual images support read-only -ValidateOnly auditing here and must not be deployed.'
427 443 }
428 444 if(!$QemuPath){
429 445 $localQemu=Join-Path $ToolsRoot 'qemu/qemu-system-x86_64.exe'
Modified Tools/IndependentKernel/Package.ps1 +3 -0
@@ -16,6 +16,9 @@ if($Version -cnotmatch '^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$'){throw 'Version must
16 16 $verified=. (Join-Path $PSScriptRoot 'Open.ps1') -Edition $Edition -ImagePath $ImagePath -VerifiedBootResult $VerifiedBootResult -ToolsRoot $ToolsRoot -ValidateOnly
17 17 if($verified.Packaged){throw 'Package a verified repository build, rather than repackaging an existing distribution.'}
18 18 if($verified.ImageManifest.Edition -ceq 'Dual'){throw 'Edition packages require their separately verified single-edition images.'}
19 if($verified.ImageManifest.DeploymentPolicy -cne 'SingleEditionOnly' -or $verified.Boot.SingleEditionImageProof.Passed -ne $true) {
20 throw 'New packages require actual single-system image inspection and a boot proof with no edition selection menu.'
21 }
19 22 # Validation-only dot-sourcing shares validators without opening a guest.
20 23 $hostPath=(Resolve-Path -LiteralPath $VerifiedHostResult).Path
21 24 $hostProof=Read-IndependentJson $hostPath
Modified Tools/IndependentKernel/TestBoot.py +9 -5
@@ -15,7 +15,7 @@ from BootAnimationEvidence import BootAnimationCapture
15 15 ROOT = Path(__file__).resolve().parents[2]
16 16 sys.path.insert(0, str(ROOT / 'Tools' / 'CSharpBoot'))
17 17 sys.path.insert(0, str(ROOT / 'Tools'))
18 from ReadImage import inspect_image
18 from ReadImage import inspect_image, verify_single_edition
19 19 from TestBrowserPipeline import _monaco_frame_stats, _monaco_fourth_line_delta
20 20 from IndependentDocumentEvidence import PRESENTED, run_documents, seed_documents, window_geometry
21 21 from IndependentBingEvidence import json_blocks, native_network_profile, network_arguments, run_bing, validate_network_arguments
@@ -377,7 +377,7 @@ def verify_boot(kernel_path, cpus, serial_enabled, runtime_required, timeout, de
377 377 assert edition == 'Desktop', 'Desktop acceptance requires the Desktop edition'
378 378 assert boot_entry is None or existing_image is not None, 'Explicit menu selection requires an existing dual image'
379 379 assert kernel_image_path is None or existing_image is not None, 'An image kernel path applies only to an existing image'
380 assert existing_image is None or serial_enabled, 'Dual menu acceptance requires serial evidence of the actual entry'
380 assert existing_image is None or serial_enabled, 'Existing image acceptance requires serial evidence of the actual system'
381 381 selected_kernel = kernel_image_path or 'System/Kernel.elf'
382 382 for relative, expected in build['SourceSha256'].items():
383 383 path = (ROOT / relative).resolve()
@@ -417,12 +417,16 @@ def verify_boot(kernel_path, cpus, serial_enabled, runtime_required, timeout, de
417 417 assert files[selected_kernel] == kernel_path.read_bytes(), 'Selected image entry differs from the exact kernel under test'
418 418 manifest = read_json(str(image) + '.json')
419 419 assert manifest['KernelImplementation'] == 'XFESystem independent kernel'
420 if existing_image is not None:
421 assert manifest.get('Edition') == 'Dual' and boot_entry in (0, 1), 'Existing image acceptance requires an explicit dual menu selection'
420 if manifest.get('Edition') == 'Dual':
421 assert existing_image is not None and boot_entry in (0, 1), 'Historical dual image acceptance requires an explicit menu selection'
422 422 expected_entry = 1 if console_required else 0
423 423 assert boot_entry == expected_entry and selected_kernel == ('System/Console.elf' if console_required else 'System/Kernel.elf'), 'Selected edition, menu index and kernel disagree'
424 424 expected_build = manifest.get('IndependentConsoleKernelBuild' if console_required else 'IndependentKernelBuild')
425 425 assert expected_build == build, 'The image selected build receipt differs from the exact compiled kernel'
426 else:
427 assert boot_entry is None and selected_kernel == 'System/Kernel.elf', 'A single-system image has no boot menu selection'
428 assert manifest['Edition'] == edition and manifest['IndependentKernelBuild'] == build, 'The single-system image must contain the selected compiled edition'
429 result['SingleEditionImageProof'] = verify_single_edition(inspected, files, manifest)
426 430 result.update(ImagePath=str(image), ImageSha256=digest(image), ImageVerification=inspected,
427 431 ImageManifestSha256=digest(str(image) + '.json'), KernelImagePath=selected_kernel,
428 432 BootEntry=boot_entry, BootShimSha256=inspected['Files']['EFI/BOOT/BOOTX64.EFI'])
@@ -627,7 +631,7 @@ if __name__ == '__main__':
627 631 parser.add_argument('--documents', action='store_true')
628 632 parser.add_argument('--console', action='store_true', help='Require actual full-screen Console edition RAM shell, history, caret editing and embedded managed sample')
629 633 parser.add_argument('--boot-visual-wait', action='store_true', help='Native-only diagnostic: show actual loading pixels and never release an unready application')
630 parser.add_argument('--image', type=Path, help='Verify an existing dual image without repacking it')
634 parser.add_argument('--image', type=Path, help='Verify the exact existing image without repacking it')
631 635 parser.add_argument('--boot-entry', type=int, choices=(0, 1), help='Actual Limine menu index selected through QMP PS/2 input')
632 636 parser.add_argument('--kernel-image-path', choices=('System/Kernel.elf', 'System/Console.elf'))
633 637 parser.add_argument('--bing', action='store_true', help='Require real guest e1000/NAT HTTPS Bing, immutable snapshot presentation and actual framebuffer evidence')
Modified docs/ConsoleAndDesktopEditions20261015.md +17 -13
@@ -1,34 +1,38 @@
1 1 # Console 与 Desktop 双版本
2 2
3 2026-10-16 用户纠正了交付方式:两个版本分别交付,每张镜像、每次 U 盘写入只有一个系统,不提供开机版本选择。本轮已选择 Desktop。以下当前用法覆盖 2026-10-15 的双启动方案;旧镜像、ZIP、失败和成功记录均保留为历史。
4
3 5 两个版本共享 C# 自研内核、Microsoft .NET 10 NativeAOT 和 RAM 工作区协议,分别链接应用,不靠桌面里的终端窗口充当控制台系统。
4 6
5 Console 的独立项目为 `Source/XFESystem.NativeRuntime/ConsoleApplication/ConsoleApplication.csproj`。参考历史提交 `1139821` 的 `kernel/src/drivers/framebuffer.rs` 与 `user/init/src/shell.rs`,使用深色背景、品牌页眉、固定宽度字形、正文区域及底部状态栏。复用现有 TerminalShell、ANSI 文本缓冲区和受限 ConsoleIL。它没有浏览器、文档或网络依赖。当前仍用内核 PS/2 键盘,不表示实体 USB HID 已适配。
7 Console 的独立项目为 `Source/XFESystem.NativeRuntime/ConsoleApplication/ConsoleApplication.csproj`。参考历史提交 `1139821` 的 `kernel/src/drivers/framebuffer.rs` 与 `user/init/src/shell.rs`,使用深色背景、品牌页眉、固定宽度字形、正文区域及底部状态栏。复用现有 TerminalShell、ANSI 文本缓冲区和受限 ConsoleIL。它没有浏览器、文档或网络依赖。支持现有 PS/2 和自研 xHCI HID;QEMU USB 集线器键鼠已有验收,实体兼容仍未验收。
6 8
7 9 Desktop 的原应用项目保持不变,保留窗口、任务栏、浏览器、编辑器、Markdown 与终端。`BuildSystem.ps1`、`Build.ps1`、`Open.ps1`、`Package.ps1` 以及兼容包装入口接受 `-Edition Desktop|Console`。默认值为 Desktop,构建目录、包名、回执都标明版本。启动器拒绝版本不符的回执,打包器只接受对应的独立单版本镜像。
8 10
9 双系统 U 盘采用同一 ESP 放置两个独立 ELF:
11 每个版本单独生成 128 MiB 镜像:
10 12
11 | 菜单 | 镜像路径 | 默认行为 |
13 | 所选系统 | 镜像内核 | 启动行为 |
12 14 | --- | --- | --- |
13 | XFESystem Desktop | `System/Kernel.elf` | 10 秒后自动启动 |
14 | XFESystem Console | `System/Console.elf` | 上下键选择后 Enter |
15 | Desktop | 仅该版 `System/Kernel.elf` | 黑底品牌动画后进入图形桌面 |
16 | Console | 仅该版 `System/Kernel.elf` | 黑底品牌动画后进入全屏控制台 |
15 17
16 生成菜单镜像:
18 分别构建和验收,不能把两版内核拼成同一张镜像:
17 19
18 20 ```powershell
19 dotnet run --project Tools/CSharpBoot/PackImage.csproj -c Release --no-build -- <Desktop Kernel.elf> <新建Dual.img> 1280x800 --independent-kernel --console-kernel <Console Kernel.elf>
20 python Tools/IndependentKernel/TestBoot.py <Desktop Kernel.elf> --image <Dual.img> --boot-entry 0 --kernel-image-path System/Kernel.elf --desktop --browser --documents --bing --virtual-cpus 8 --timeout 360
21 python Tools/IndependentKernel/TestBoot.py <Console Kernel.elf> --image <Dual.img> --boot-entry 1 --kernel-image-path System/Console.elf --console --virtual-cpus 8 --timeout 180
22 python Tools/IndependentKernel/VerifyDualDelivery.py <Dual.img> --desktop-proof <Desktop Result.json> --console-proof <Console Result.json> --output <新的DualResult.json>
21 powershell -NoProfile -ExecutionPolicy Bypass -File Tools/IndependentKernel/BuildSystem.ps1 -Edition Desktop
22 powershell -NoProfile -ExecutionPolicy Bypass -File Tools/IndependentKernel/BuildSystem.ps1 -Edition Console
23 python Tools/IndependentKernel/TestBoot.py <Desktop Kernel.elf> --desktop --browser --documents --bing --virtual-cpus 8 --timeout 360
24 python Tools/IndependentKernel/TestBoot.py <Console Kernel.elf> --console --virtual-cpus 8 --timeout 180
23 25 ```
24 26
25 两次来宾验收必须使用完全相同的双版本镜像,通过真实 QMP PS/2 按键选择不同菜单。控制台检查文件写入读取、历史草稿恢复、Delete/Insert 光标编辑、ConsoleIL 程序输出和实际画面变化,并排除桌面/浏览器初始化。图形版仍要求原有完整验收。源文件、两个 ELF、完整镜像、回执及截图分别绑定 SHA256;双版本回执只在严格启动器分别接受两份证据后生成。
27 `TestBoot.py` 生成并检查所选版本的独立镜像,回执绑定完整镜像和实际运行结果。控制台检查 RAM 文件写入读取、历史草稿恢复、Delete/Insert 光标编辑、ConsoleIL 程序输出和实际画面变化,并排除桌面/浏览器初始化。图形版仍要求原有完整验收。两版保留相同品牌启动动画和真实首帧就绪交接。
28
29 镜像生成器拒绝原 `--console-kernel` 组合入口。`ReadImage.py` 遍历实际 FAT 文件目录并检查精确启动配置;验收要求只有一个内核、一个对应版本入口、`quiet: yes` 和 `timeout: 0`。启动器与打包器核验该证据;物理写盘器在读取目标磁盘前拒绝旧 `IndependentDualCSharp256` 配置。旧双镜像只读验证工具保留作历史审计。
26 30
27 两个独立镜像各为 128 MiB,双系统镜像为 256 MiB。物理写入使用新增 `IndependentDualCSharp256` 配置,沿用 `scripts/physical-media.ps1` 的同一专用 USB 身份、分区指纹、计划 token、源与证据锁、完整回读及真实盘尾备份 GPT 校验。不能拿两个独立镜像各自通过的结果替代双系统镜像的实际菜单启动验收。
31 物理写入使用 `IndependentCSharp128`,仍须核对专用 USB 身份、分区指纹、计划 token、源与证据锁,完整回读容量调整后的镜像并验证真实盘尾 GPT。只写入用户选定的独立镜像;切换 Console / Desktop 需要重新写盘。没有检测到目标设备或容量异常时,保留未写入记录,不能标为交付完成。
28 32
29 33 RAM-only、单 BSP、实体启动与输入、持久存储、GPU/CUDA、SMP 和长期稳定性边界保持原有实验范围。实际构建、测试和写盘结果另存本轮验证摘要;未通过的项目不得标记成功。
30 34
31 ## 本次实际交付
35 ## 2026-10-15 的历史交付
32 36
33 37 16 组、1332 项宿主检查全部通过。两份独立镜像分别通过来宾验收;同一 256 MiB 双启动镜像通过实际键盘选择 Desktop 和 Console 的两次完整验收,均为 QEMU 8 虚拟 CPU、实际一个 BSP。控制台十命令检查包括 RAM 文件读写、历史草稿恢复、光标 Delete/Insert 编辑和真实 ConsoleIL;图形版继续通过 Bing、Monaco 实际编辑、Markdown 八项及经典终端九命令检查。两个最终独立 ZIP 完整回读、解压后逐文件哈希与 PowerShell 5.1 启动计划通过。
34 38
Modified scripts/physical-media-csharp-tests.py +1 -1
@@ -48,7 +48,7 @@ class ExperimentalMediaTests(unittest.TestCase):
48 48 # artifacts beside this copied receipt. Copy their unchanged bytes
49 49 # once; no physical disk or original proof is modified by tests.
50 50 for source in cls.original_boot.parent.iterdir():
51 if source.is_file() and (source.suffix.lower() in (".png", ".log", ".txt") or source.name == "BingEvidence.json"):
51 if source.is_file() and (source.suffix.lower() in (".png", ".log", ".txt") or source.name in ("BingEvidence.json", "GpuDiscoveryEvidence.json")):
52 52 shutil.copyfile(source, folder / source.name)
53 53 cls.bing_path = folder / "BingEvidence.json"
54 54 cls.bing_original = cls.bing_path.read_bytes()
Modified scripts/physical-media-verify.py +22 -8
@@ -13,7 +13,7 @@ import xml.etree.ElementTree as ET
13 13
14 14 ROOT = Path(__file__).resolve().parents[1]
15 15 sys.path.insert(0, str(ROOT / "Tools" / "CSharpBoot"))
16 from ReadImage import inspect_image
16 from ReadImage import inspect_image, verify_single_edition
17 17
18 18
19 19 def digest(path):
@@ -81,12 +81,19 @@ def verify_independent_ram_profile(snapshot, manifest, boot):
81 81 require(build.get("Kind") == "XfeIndependentKernelBuild" and build.get("Passed") is True and
82 82 build.get("ContainsCosmos") is False and build.get("BootstrapOnly") is False,
83 83 "A complete independent application kernel build is required")
84 require(boot.get("DesktopAcceptancePassed") is True and boot.get("ManagedRuntimeAcceptancePassed") is True and
85 boot.get("ContainsCosmos") is False, "Actual independent managed desktop acceptance is required")
86 input_proof = boot.get("DesktopInputProof", {})
87 require(input_proof.get("NoRenderFailures") is True and input_proof.get("MonacoRasterPaintCompleted") is True and
88 input_proof.get("MonacoKeyboardEditedModel") is True,
89 "Actual independent visible Monaco rendering and PS/2 editing acceptance is required")
84 edition = 'Console' if manifest.get('Edition') == 'Console' else 'Desktop'
85 require(boot.get("Edition", 'Desktop') == edition and boot.get("ManagedRuntimeAcceptancePassed") is True and
86 boot.get("ContainsCosmos") is False, "Actual independent managed system acceptance is required")
87 if edition == 'Console':
88 require(boot.get('ConsoleAcceptanceRequired') is True and boot.get('ConsoleAcceptancePassed') is True and
89 boot.get('ConsoleInputProof', {}).get('Passed') is True and boot.get('NoDesktopOrBrowserInitialization') is True,
90 'Actual independent console workflow and desktop isolation acceptance is required')
91 else:
92 require(boot.get("DesktopAcceptancePassed") is True, 'Actual independent managed desktop acceptance is required')
93 input_proof = boot.get("DesktopInputProof", {})
94 require(input_proof.get("NoRenderFailures") is True and input_proof.get("MonacoRasterPaintCompleted") is True and
95 input_proof.get("MonacoKeyboardEditedModel") is True,
96 "Actual independent visible Monaco rendering and PS/2 editing acceptance is required")
90 97 require(build.get("Configuration") == profile, "The compiled configuration differs from its source")
91 98 for name, expected in snapshot.items():
92 99 path = (ROOT / name).resolve()
@@ -167,6 +174,11 @@ def verify(image_path, boot_path, profile="CSharpExperimental128", _dual_image=F
167 174 require(not _dual_image or independent and manifest.get('Edition') == 'Dual', 'Expected the independent dual image')
168 175 require(image_path.stat().st_size == (256 if _dual_image else 128) * 1024 * 1024, "Unexpected C# image size for the selected profile")
169 176 inspected, files = inspect_image(image_path)
177 single_proof = None
178 if independent and not _dual_image:
179 single_proof = verify_single_edition(inspected, files, manifest)
180 require(boot.get('SingleEditionImageProof') == single_proof and boot.get('BootEntry') is None,
181 'Boot acceptance must verify this exact single-system image with no menu selection')
170 182 verify_elf(files["System/Kernel.elf"])
171 183 verify_efi(files["EFI/BOOT/BOOTX64.EFI"], "EFI/BOOT/BOOTX64.EFI")
172 184 verify_efi(files["EFI/XFE/Limine.efi"], "EFI/XFE/Limine.efi")
@@ -212,7 +224,8 @@ def verify(image_path, boot_path, profile="CSharpExperimental128", _dual_image=F
212 224 for name, expected in inspected["BootShimSourceSha256"].items():
213 225 require(snapshot.get(name) == expected, "EFI source differs from the compiled source snapshot: " + name)
214 226 ram_only = verify_independent_ram_profile(snapshot, manifest, boot) if independent else verify_ram_only_project(snapshot)
215 delivery = verify_independent_delivery(image_path, boot_path, snapshot, image_hash, boot_hash) if independent else None
227 edition = 'Console' if manifest.get('Edition') == 'Console' else 'Desktop'
228 delivery = verify_independent_delivery(image_path, boot_path, snapshot, image_hash, boot_hash, edition) if independent else None
216 229 return {"Kind": "XfeCSharpExperimentalPhysicalMediaEvidence", "Profile": profile,
217 230 "Architecture": "x86_64", "ImagePath": str(image_path), "ImageBytes": len(raw), "ImageSha256": image_hash,
218 231 "KernelSha256": kernel_hash, "BootResultPath": str(boot_path), "BootResultSha256": boot_hash,
@@ -220,6 +233,7 @@ def verify(image_path, boot_path, profile="CSharpExperimental128", _dual_image=F
220 233 "SourceSnapshotPath": str(snapshot_path), "SourceSnapshotSha256": snapshot_hash,
221 234 **ram_only,
222 235 **({"IndependentFinalDeliveryGate": delivery} if independent else {}),
236 **({'SingleEditionImageProof': single_proof, 'Edition': edition} if single_proof else {}),
223 237 "IndependentImageInspection": inspected, "FullReleaseQualified": False, "StabilityPassed": False,
224 238 "StabilityStatus": "Strict stability acceptance has not passed; investigation remains open",
225 239 "CudaExecutionVerified": False, "GpuRasterizationVerified": False,
Modified scripts/physical-media.ps1 +3 -0
@@ -235,6 +235,9 @@ if (-not $ResultPath) {
235 235 }
236 236 $logFile = [IO.File]::Open([IO.Path]::GetFullPath($ResultPath),[IO.FileMode]::CreateNew,[IO.FileAccess]::Write,[IO.FileShare]::Read)
237 237 try {
238 if ($ImageProfile -ceq 'IndependentDualCSharp256') {
239 throw 'Dual-system deployment was retired at the user request. Select one Console or Desktop 128 MiB image and IndependentCSharp128; no disk was opened.'
240 }
238 241 if (-not $Image -or $DiskNumber -lt 0) { throw 'Specify -Image FILE.img and -DiskNumber N. Without -Write this only produces a review plan.' }
239 242 if ($env:OS -ne 'Windows_NT') { throw 'This script requires Windows Storage cmdlets.' }
240 243 $imagePath = (Resolve-Path -LiteralPath $Image).ProviderPath