Skip to content

fix(httpboot): validate ELF and loader architectures before handoff - #217

Merged
ZR233 merged 1 commit into
mainfrom
fix/riscv-efi-boot
Nov 5, 2026
Merged

ZR233 merged 1 commit into
mainfrom
fix/riscv-efi-boot

Conversation

@YanLien

@YanLien YanLien commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

当前问题

HTTP Boot 原先依赖板卡配置中的 boot_arch 决定上传声明和 EFI 入口,缺省为 x86_64,未核对内核 ELF。配置、内核与设备架构不一致时,可能静默等待,或在串口交接后才失败。

OTA 的服务端和前端也存在 x86_64 限制,重复了 axloader 自身的镜像校验职责,阻止其他架构使用相同升级协议。此外,HTTP 响应读取、启动取消和错误传递存在缺口,影响故障定位;串口身份别名及历史配置隔离提示也需要修正。

修改方案

  • 内核架构来自 ELF,设备架构来自 axloader。CLI 复用已有 ELF 检测结果生成上传声明;服务端在上传和串口交接前核对文件长度、摘要、ELF 架构及设备实际架构。不支持或不匹配时明确失败并释放会话,具体原因传递到 CLI。
  • 移除持久化 boot_arch 和前端手动架构设置。在线只读展示设备架构,离线显示未知,允许先准备会话与内核。旧字段兼容读取后丢弃,合法旧 TOML 原子迁移;迁移失败保留源文件,其他未知字段仍拒绝。CLI 与服务端需配套升级。
  • 保留启动协议的 arch 字段。x86_64 使用 __x86_64_efi_pe_entry,RISC-V64、LoongArch64 使用 efi_pe_entry。完整读取设备 HTTP 响应体后再推进事务,保留命令行、initramfs 和过期任务替换行为。
  • OTA 镜像对服务端是不透明字节对象,格式、Machine 和平台兼容性由 axloader 校验。移除服务端和 UI 的架构限制,保留大小、摘要、MAC、注册代次、任务和租约检查;旧 poll/download 路径同样要求实际 OTA 能力。
  • 按设备 HTTP 响应处理 OTA:400/413/422 一次失败并保留原始响应;409 刷新状态后延迟重试;503 等待能力、状态或启动代次变化;网络错误及其他 5xx 有限退避,重传前先核对设备是否已经接受镜像。202 仅表示暂存,实际运行新槽并确认后才登记成功。缺少 OTA 状态时保持可见等待,普通 HTTP Boot 仍可继续;旧报告不能覆盖终态或首条失败原因。
  • 串行处理内核写入、命令发布和交接,并响应电源换代及会话停止。串口编辑器保留已有身份选择器,识别序列号和路径别名,明确提示共享序列号歧义。保持 usb_id、usb_path、serial_number 等既有配置兼容。
  • 已成功加载的板卡不再重复显示历史配置隔离错误。此前被旧版本隔离的配置不会自动恢复,仍需确认内容有效后放回配置目录。同步中英文 README、API、管理 UI 和网络启动文档。

测试验证

验证源码与提交 52a17ca 一致。

  • 确定性回归先在错误实现上失败,再验证修复通过,覆盖架构预检、配置迁移、启动取消、错误传递、OTA 响应分类、暂存恢复及旧注册保护。
  • cargo test -p ostool-server:198 项通过;默认忽略的 1 项真实 QEMU 测试已单独执行通过。
  • pnpm --dir ostool-server/webui test:40 项通过;生产构建通过。
  • 完整浏览器 E2E:2 项通过。
  • cargo fmt --all -- --check、git diff --check、服务端构建通过;Clippy 完成,仅有未修改代码中的既有警告。
  • 7 份现有板卡配置正常加载,包括此前报告 unknown variant usb_id 的配置,无需改写有效的串口身份类型。
  • 真实 x86_64 QEMU 完成 HTTP Boot 和 OTA 验证:错误 Machine 镜像由设备返回旧版 422 {},服务端保留错误且活动槽不变;正确镜像完成暂存、重启试运行和确认。axloader 宿主测试 35 项通过,包含 LoongArch PE 校验。
  • BananaPi(SpacemiT K3、RISC-V64)完成 UEFI → 实体 FAT32 U 盘 axloader → 网络下载内核及 initramfs → ArceOS,CLI 退出码为 0,BOOT_RESOURCES_PASSED、HOST_CMDLINE、HOST_INITRAMFS_PASSED 和 Hello, world! 均出现。错误架构内核在交接前被拒绝,CLI 返回具体原因。
  • 新版已正式安装到实体机上,安装后再次完成 BananaPi 正向启动;会话释放后租约恢复 idle,板卡配置未改动。

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

本 PR 修复 axloader v5 的多架构 EFI 入口选择与 HTTP 响应体读取,并让发现页创建板卡时携带设备架构、编辑器识别既有串口身份别名。改动主要影响 HTTP Boot 交接和 board editor;没有改变 REST/API 数据模型,PXE、U-Boot 及既有串口身份配置契约看起来保持不变。历史 PR 搜索找到 #194、#198、#206,分别是相关启动载荷、v5 和自动串口绑定的前序工作,没有发现重复的同类待审改动。

验证:固定 head 47a103d 的较早 CI run 41818170621 全部通过,包括 fmt、clippy、build、Rust 单测、Web UI 单测/build、e2e 和 publish dry-run。较新的 run 41830890755 失败时 annotation 明确为 GitHub runner 写入自身日志时 No space left on device,发生在 Unit test 步骤运行期间;这是 runner 环境故障而非 PR 代码失败。git diff --check 和 reviewer clone/head 校验通过;本地 cargo fmt --check 与 pnpm --dir ostool-server/webui test 因当前环境没有 cargo/pnpm 而无法运行。当前 head 没有既有 review、inline review comments 或 PR issue comments,因此没有历史审查意见需要追踪。

仍有一个阻塞问题,见下方 inline comment:不支持架构的拒绝发生在串口交接之后,可能使活动会话停留在没有 boot status 的半完成状态。

Powered by gpt-6-luna

Comment thread ostool-server/src/device.rs Outdated
let epoch = observed.boot_epoch.as_str();
let observed_boot_id = observed.boot.as_ref().map(|boot| boot.boot_id.as_str());
let manifest = v5_boot_manifest(command);
let manifest = v5_boot_manifest(command)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

对 AArch64/Other 的 bail! 目前是在 push_boot() 中才触发,但 reconcile() 已先完成 /serial/continue 和 serial_runtime.confirm()。随后错误只被记录为 handoff warning,未将 runtime/session 标记失败;SerialGrant 仅异步撤销设备 binding,因此不支持架构会先进入物理串口交接,再被本地拒绝并可能留下半完成的活动会话。请把 manifest 架构校验移到这次串口交接之前(或在发布 boot command 时拒绝),并覆盖该失败路径。

@YanLien
YanLien force-pushed the fix/riscv-efi-boot branch from 47a103d to ea44de5 Compare November 4, 2026 13:13

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

本 PR 修复了 axloader v5 的 HTTP Boot ELF 入口选择、完整读取设备响应体后再推进启动事务,并在串口交接前拒绝不支持的架构;Web UI 现在传递发现设备架构,并能按 USB ID/by-path/序列号别名显示与保留既有串口选择。影响范围主要是 ostool-server 的 HTTP Boot 交接与 board editor;没有改变 REST 模型或已保存的配置字段,U-Boot/PXE 等流程看起来未受影响。

我检查了先前针对 device.rs 的 review comment:它指出不支持架构是在 /serial/continue 后才拒绝,这个问题合理;当前 head 已在串口绑定和交接前构造并校验 BootRequest,失败时标记运行时失败,且新增测试覆盖 AArch64/Other 的无交接路径。PR issue comments 为空。相似的已合并 PR #215(稳定串口身份)和 #206(axloader v6 自动串口绑定)属于相关前序工作,没有发现重复的待审改动。

验证:git diff --check origin/main...HEAD 通过;本地无法运行 cargo fmt --check、pnpm UI tests/build,因为环境没有安装 cargo 和 pnpm。针对精确 head ea44de5 的 GitHub check 有一项已通过,另一项仍在运行,未见失败的 check。未发现未解决的代码阻塞问题;LoongArch 等其他架构的实体启动验证范围仍有限,且第二项 CI 尚未完成。

Powered by gpt-6-luna

@YanLien
YanLien requested a review from ZR233 November 4, 2026 13:35
@YanLien
YanLien force-pushed the fix/riscv-efi-boot branch from ea44de5 to 7497c50 Compare November 4, 2026 13:50

@ZR233 ZR233 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Request changes:HTTP Boot 架构信息应以 ELF 与 axloader 实际状态为准

PR #217 修复了多架构 EFI 入口、串口交接前拒绝不支持架构、HTTP 响应体读取以及串口身份展示等问题,这些改动方向正确。但目前“发现设备后将 arch 写入板卡 boot_arch,再检查两者一致”的设计仍然维持了一份不必要的持久化架构副本,建议在本 PR 中一并调整,避免多架构功能建立在容易失配的配置上。

当前问题(已核对代码)

  1. axloader 已上报架构,不需要用户再配置一次。 httpboot-protocol::LoaderAnnouncement.arch 和 LoaderDeviceStatus.arch 已存在,ostool-server/src/loader.rs 已在发现/状态核对后保存架构;x86_64 和 LoongArch 的 axloader 实现也已填入架构。PR 目前只在 Boards.tsx、BoardEditor.tsx 将这个值复制到持久化 UefiHttpProfile.boot_arch,属于重复状态。
  2. CLI 仍信任板卡配置,而不是内核文件。 ostool/src/run/httpboot_board.rs 使用 profile.boot_arch.unwrap_or(X86_64) 生成 X-HttpBoot-Arch;但 ostool/src/artifact/runtime.rs 已使用 object::File 检测 ELF 架构并保存到运行时状态。配置缺省为 x86_64 并不意味着设备/内核就是 x86_64。
  3. 架构不匹配时静默跳过启动。 ostool-server/src/device.rs 的 if command.arch != observed.arch { return Ok(()); } 会让会话无明确失败原因地不再推进。应在串口绑定/continue 前明确报错并走现有会话失败、释放流程。
  4. OTA 也依赖同一个配置。 ostool-server/src/api/ota.rs::queue_job 以 boot_arch == x86_64 || None 作为升级许可条件;离线或配置过期时会误判。应区分镜像的真实目标架构与设备实际上报架构。

建议修改(明确数据所有权,不再叠兜底)

  1. 移除 HTTP Boot 的手工 boot_arch 配置。 UefiHttpProfile 不再持久化架构,Board Editor 删除架构下拉框、URL 传参/同步/一致性校验;发现页或详情页继续只读展示当前 axloader 上报的 arch。设备未上线时显示“未知”,不要默认为 x86_64。旧板卡 TOML 中已有 boot_arch 需有明确的一次性兼容读取/迁移方案(当前 deny_unknown_fields,不可直接删字段使已有配置加载失败),最终不保留长期双来源。
  2. 内核架构由 ELF 确定。 ostool CLI 利用已有 ELF 解析结果生成上传的 X-HttpBoot-Arch,不再通过 boot-profile 获取板卡架构;可以暂时保留现有 HTTP header / SessionBootCommand.arch / DeviceBootJob.arch 的协议形状,减少无关改动。上传端/服务端在合理边界校验声明与 ELF e_machine 一致(无法识别或不支持则明确失败)。
  3. 设备架构由 axloader 确定。 server 在发现/状态核对后使用本次有效 observed.arch 与内核架构匹配;不匹配则返回明确错误、标记本次启动失败并终止会话,不允许进入串口绑定/continue 或下发内核。EFI 入口选择继续按实际目标架构的既有契约(x86_64: __x86_64_efi_pe_entry;LoongArch64/RISC-V64: efi_pe_entry),不使用持久化配置决定入口。设备离线时允许先准备会话和内核,但不能凭默认 x86_64 认定校验通过;设备上线后再校验。
  4. 同步修正 OTA 判断。 在上传/排队或实际下发的适当阶段识别并校验 EFI PE Machine;当前若仅支持 x86_64 OTA,就明确限制为 x86_64 镜像,并在设备实际在线时核对 observed.arch,不再以 boot_arch == None 等同 x86_64。离线任务可保留排队,但必须在下发前完成设备匹配校验;不一致时明确报告/失败,不发送错误镜像。
  5. 测试与文档:覆盖 x86_64、RISC-V64、LoongArch64 的 ELF→上传→清单架构链路;配置缺失/旧字段、设备离线后上线、内核与设备不一致的显式失败且无串口交接、OTA 架构不符的拒绝;更新 docs/axloader-network-control.md、docs/api.md、docs/admin-ui.md 和相关 CLI/UI 测试。

需要保留的区别:DeviceBootJob.arch 是启动协议的必要字段,应保留;删除的是板卡配置里人工维护的 boot_arch。如果未来 CI 在设备离线时必须按架构分配板卡,应使用现有 board_type/tags 或单独的 调度期望属性,不能拿它覆盖 axloader 实际架构。

这样形成明确的单一来源:内核架构来自 ELF,设备架构来自 axloader,server 只负责校验和下发。请按这一方向调整后再请求复审。

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

本 PR 修复了 axloader v5 的多架构 EFI 入口选择和 HTTP 响应体读取,并让发现页将设备架构带入板卡编辑器、补充既有串口身份别名识别。主要影响 HTTP Boot 交接与 board editor;没有改变 REST/API 数据模型或已保存字段,PXE/U-Boot 和其它串口流程看起来未受影响。

验证与 CI: 已通过 helper 的 7 项测试,git diff --check origin/main...HEAD、精确 head/clone 校验通过。本地无法运行 cargo fmt --check、clippy、Rust tests 或 pnpm UI tests/build,因为环境缺少 cargo 和 pnpm。同一 head 的 GitHub Actions 有两次结果:run 41841840119 的完整流程(fmt、clippy、build、Rust tests、UI tests/build、e2e、publish dry-run)全部通过;run 41841847816 在 Unit test 步骤以 exit 101 失败,annotation 未给出测试级原因。通过的结果降低了该失败是稳定回归的可能性,但仍无法据现有日志判定失败原因,且本地无法复现。

先前审查意见: 先前针对 device.rs 的意见指出不支持架构在 /serial/continue 后才被拒绝,该问题合理;当前 BootRequest::new() 已在绑定/continue 前校验支持的架构,失败时标记 runtime,新增测试覆盖 AArch64/Other,因此该意见已解决。PR issue comments 为空。另一份同 head 审查提出架构来源与实际内核可能不一致;我确认 CLI 将持久化 boot_arch 用作 X-HttpBoot-Arch,上传端未校验 ELF e_machine,详见下方阻塞 inline comment。相似的既有工作包括 #198、#206;搜索未发现重复的待审 PR。

仍有一个阻塞问题:入口预检只验证声明架构,不验证内核 ELF 架构。声明与 ELF 不一致时仍会先串口 continue,后续设备事务才会发现不匹配,而当前错误路径只记录 warning,没有将 session/runtime 标为失败。请按 inline comment 在串口绑定前校验 ELF、声明与设备架构一致,并显式失败。

另有一项非阻塞文档遗漏:docs/api.md:694,1072、README.md:336 和 README.en.md:360 仍说入口固定为 __x86_64_efi_pe_entry,与本 PR 对 LoongArch64/RISC-V64 使用 efi_pe_entry 的实现不符;请同步更新。

Powered by gpt-6-luna


impl<'a> BootRequest<'a> {
fn new(command: &'a SessionBootCommand) -> anyhow::Result<Self> {
let entry_symbol = match command.arch {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

command.arch 在这里直接决定 manifest 入口,但当前它来自持久化 boot_arch / X-HttpBoot-Arch,而上传端没有核对 kernel ELF 的 e_machine。若配置与实际 ELF 架构不一致,BootRequest::new() 仍会通过,服务端随后先执行串口 continue 并上传镜像,直到后续设备事务校验才发现不匹配;该错误只被记录为 handoff warning,不会把 session/runtime 标记为失败。请在串口绑定前校验 ELF 架构与声明及设备架构一致,并将不匹配作为显式失败处理。

@YanLien
YanLien force-pushed the fix/riscv-efi-boot branch from 7497c50 to bfce43c Compare November 4, 2026 15:05
@YanLien YanLien changed the title fix(ostool-server): correct multi-arch EFI boot requests fix(httpboot): validate ELF and loader architectures before handoff Nov 4, 2026

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

本 PR 将 HTTP Boot 的内核架构改为从 ELF 推导,并在上传与串口交接前核验 ELF、声明和 axloader 实际架构;同时选择多架构 EFI 入口、限制 OTA 镜像为 x86_64 PE32+,并移除持久化 boot_arch(旧 TOML 兼容读取后迁移)。影响 HTTP Boot 启动、板卡 boot profile/API 和 OTA;U-Boot、PXE 与既有串口身份配置未见受影响。boot_arch 不再由 profile 返回,旧 CLI/服务端需配套升级。

验证:当前精确 head 的两个 GitHub Actions check 均已完成并通过;review helper 的 7 项测试和 git diff --check origin/main...HEAD 通过。本地因未安装 cargo 和 pnpm,无法运行 Rust fmt/clippy/tests 或 Web UI tests/build;未进行 RISC-V/LoongArch 实体板验证。未见当前 head 上由 PR 引起的失败 CI。

此前关于架构拒绝发生在 /serial/continue 之后的意见合理,现已通过在绑定/continue 前校验设备架构和 ELF 并使 session 失败来修复;关于 boot_arch 重复来源、ELF 校验及文档入口说明的意见也已落实。相关前序工作包括 #198 和 #206,未发现重复的待审改动。剩余建议见下方 inline comment:预检错误目前对 CLI 只呈现为通用的 WebSocket/session closed,而未传递具体架构不匹配原因。

Powered by gpt-6-luna

Comment thread ostool-server/src/device.rs Outdated
let request = match preflight {
Ok(request) => request,
Err(error) => {
session.serial_runtime.fail(format!("{error:#}"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

架构预检失败时这里仅调用 serial_runtime.fail(),但 CLI 的 monitor_loader_status() 只读取 /loader-status;这个分支发生在任何 update_loader_status() 之前,之后 WebSocket 关闭只会让 CLI 得到通用的 session/WebSocket closed 错误。请把具体的 ELF/device 架构不匹配发布到 loader status(或让 CLI 监控 serial runtime error),并测试客户端能显示实际失败原因。

@YanLien
YanLien force-pushed the fix/riscv-efi-boot branch from bfce43c to f4db43a Compare November 5, 2026 02:30

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

审查结论

本 PR 将 HTTP Boot 内核架构改为从 ELF 推导,并在上传和串口交接前核验 ELF、声明及 axloader 实际架构;按架构选择 EFI 入口;移除板卡 profile 中的持久化 boot_arch 并兼容迁移旧 TOML;将 OTA 限定为 x86_64 PE32+ EFI;同时修复设备 HTTP 响应体未读完即继续交接,以及串口身份别名展示问题。

影响集中在 HTTP Boot、OTA 及其板卡配置/API/CLI/UI 契约。旧 boot_arch 配置会迁移,profile 不再返回该字段;部署时 CLI 与 server 需配套升级。未发现对 PXE、U-Boot 启动或其它串口配置流程的回归。

验证: 精确 head f4db43a8 的两个 GitHub Actions check 均已完成并通过;git diff --check 和 review helper 的 7 项测试通过。本地无法运行 Rust fmt/clippy/tests 或 Web UI tests/build:环境未安装 cargo 和 pnpm。当前没有因本 PR 失败的 CI。没有进行 RISC-V64/LoongArch64 实体板验证。

既有意见: 先前关于不支持架构在串口交接后才拒绝、内核架构仅由 boot_arch 声明,以及 CLI 无法看到具体预检失败原因的意见均合理;当前版本已在串口绑定前核验 ELF/声明/设备架构,发布 boot_preflight_failed 和具体错误,并覆盖 CLI 通过 WebSocket/状态轮询接收原因的测试。移除重复架构配置及相关文档修订也已落实。相关已合并工作包括 #198(axloader HTTP 控制/OTA)和 #206(自动串口绑定);未找到其它开放的同类 PR。

未发现当前 head 仍成立的阻塞问题。剩余验证局限是本地 Rust/Web 工具不可用及未进行多架构实体板启动测试;CI 已通过。

Powered by gpt-6-luna

@ZR233 ZR233 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Request changes:OTA 的镜像兼容性应由 axloader 判定,server 不应硬编码 CPU 架构

基于当前 head f4db43a8 复核。此前关于 HTTP Boot 的 boot_arch 去重、ELF 架构来源及失败信息传递已经落实;这次要求修改的是 axloader 自身 OTA 的职责边界,不要撤销普通 HTTP Boot 的内核预检。

现在 OTA 的 server 代码重复了设备能力判断:

  • ostool-server/src/ota.rs::validate_efi_image() 将 EFI Machine 固定为 AMD64 0x8664,上传镜像时即拒绝其他架构;push_ota_image()、旧下载路径又重复校验。
  • ota.rs::reject_device_architecture()、OtaStore::decide(..., arch)、device.rs::reconcile() 会根据 observed.arch != X86_64 将已排队任务直接标为永久 Failed。
  • api/ota.rs::{queue_job,download_image} 及旧 api/router.rs poll 路径有相同白名单;BoardEditor.tsx 使用 arch === "x86_64" 决定是否允许 OTA。
  • 但 axloader 已有校验:x86_64 ota/disk.rs::verify_efi_image() 校验 EFI PE 并在 finish_update() 通过 UEFI LoadImage 验证;LoongArch64 axstd_loader/ota.rs::stage_image() 通过 LoadedPe::validate() 检查该架构 PE 镜像。设备端已可通过 HTTP 拒绝不兼容镜像。server 的固定 AMD64 规则反而阻断多架构 OTA,将来每新增一种 axloader 架构都要改 server。

目标与所有权(定为实施约束)

OTA 镜像视为对 server 不透明的字节对象:server 负责限额、保存、SHA-256、板卡绑定、任务排队、租约、传输、超时/重试、A/B 确认流程与错误展示;axloader 独占 PE/COFF 格式、Machine、EFI Subsystem、加载/内存平台限制、固件可兼容性及 A/B 试运行判断。server 可展示设备上报的 arch,但不得用它决定 OTA 镜像是否合法,不增加 server 的 PE 解析器/架构映射/白名单。

注意:普通内核 HTTP Boot 的 elf_arch、SessionBootCommand.arch、DeviceBootJob.arch、串口交接前内核与设备架构核对属于另一条启动链路,保持本 PR 的修复不变。

明确修改点(不再增设中间适配层)

  1. ostool-server/src/ota.rs:移除 validate_efi_image() 的固定 AMD64 格式/架构逻辑,并从 write_image() 删除对它的调用;去掉 reject_device_architecture()、reject_incompatible_device()、Decision::Reject 中仅为架构构造的路径,以及 decide(..., arch) 的额外参数。保留并复用现有镜像长度上限、SHA-256、磁盘读写/摘要校验、设备试运行与事务状态机。不要新增一个支持架构列表。
  2. ostool-server/src/api/ota.rs:upload_image 只验证请求合法性、大小和存储,不解析 PE Machine;queue_job 只验证 HTTP Boot 板卡、绑定 MAC、租约及 OTA 作业约束;download_image 保留板卡/MAC/在线身份与冲突检查、任务许可及文件完整性校验,但删除 x86_64 条件和 PE 架构校验。
  3. ostool-server/src/device.rs:reconcile 不因架构不同拒绝 OTA;push_ota_image 仅确认本地镜像大小/摘要后 PUT 到当前设备 /api/v1/ota/image,以设备响应判断是否被接受。保留 X-Boot-Epoch、X-Image-Sha256、X-Update-Id、X-Update-Source、Content-Length 和完整读取响应体等现有协议。
  4. 旧 poll OTA:api/router.rs 的 poll 指派及 api/ota.rs::download_image 删除同一 x86 白名单;保留注册代次/MAC/租约/任务绑定。镜像有效性由实际下载它的旧 axloader 验证并使用既有 ota-status 回报;如果某旧协议固件缺失设备端校验,应按可验证的协议能力决定是否提供 OTA,而不是根据 CPU 架构臆测。
  5. UI:BoardEditor.tsx 去掉 canUpdate={!boundLoader?.online || boundLoader.arch === "x86_64"};BoardOta 不用 CPU 架构控制按钮。在线时可根据既有 LoaderDeviceStatus.ota != None / 实际能力提示“OTA 不可用”,离线仍允许排队;显示真实设备失败原因。
  6. 文档与测试:同步更改中英文 README、docs/api.md、docs/admin-ui.md、docs/axloader-network-control.md,移除“OTA 只支持 x86_64、server 解析 EFI Machine”的陈旧契约。

固定 HTTP 错误契约(axloader → server)

复用现有 PUT /api/v1/ota/image,不新增专用架构检查端点,不要求服务器知道设备支持哪些 Machine。保持原有 header 与成功返回语义。

  • 202 Accepted:镜像摘要、格式/架构、设备可加载性及暂存已通过,进入现有 A/B 试运行流程;不代表升级最终成功,成功只能在新槽实际运行、确认且摘要匹配后登记。
  • 400 Bad Request:请求本身不合法(缺少/错误头、参数),server 将本次作业置为 Failed、原样记录设备错误。
  • 413 Payload Too Large:超出设备限额,本次作业 Failed,不再重复发送相同镜像。
  • 422 Unprocessable Content:摘要错误、PE 格式/架构/入口或平台兼容性验证失败,本次作业 Failed,不重试相同镜像。设备不得提交活动槽或重启到无效镜像。
  • 409 Conflict:OTA trial、设备忙、状态/epoch 冲突;先刷新实际设备状态/代次,视条件等待再次尝试,不把它当作镜像架构失败;避免紧密重试。
  • 503 Service Unavailable:设备当前没有 OTA 存储/能力;保持可见的等待/不可用原因,不应因架构或次数直接判定镜像格式错误,不持续重复推送。
  • 网络中断、超时、5xx:现有有限次数重试和失败记录;在结果不确定时先核对设备 OTA 状态/更新 ID,避免已暂存却重复下发。

建议统一设备错误体(兼容已有 {"error":"..."}):例如

{"error":"unsupported_image_arch","message":"EFI PE Machine is not supported by this loader"}

error 是稳定机器码,message 是可直接展示的简短文本;可使用 invalid_efi_image、image_digest_mismatch、ota_state_conflict、ota_storage_failed 等码。server 依据 HTTP 状态分类处理,不按 unsupported_image_arch 枚举架构,也不解析/复制设备的 PE 校验器。 兼容旧设备 422 { }:仍按 422 确定性拒绝,在 Job.error 至少保存 HTTP 422 和设备返回体;不将其误作网络失败重试三次。

server 状态机落地约束

  • 现在 push_ota_image() 的所有非 202 都走 record_delivery_failure(),该函数累计投递次数,最终三次 Failed。请拆分 设备已明确拒绝的确定性错误 与 传输不确定的可重试错误:可复用 OtaStore::report(..., Phase::Failed, Some(error), ...) 的既有合法状态转移(从 Downloading),或添加一个单一的 fail_rejected_delivery(...) 方法;不要让 422 继续累计三次重传。任务错误应包含 HTTP status + 设备 error/message,经现有 API/SSE 直达 UI。
  • 仍以现有 board_id + MAC + update_id + epoch 防止串板、旧结果污染新作业;缺失 ota 状态时禁止盲目下发,保持等待/不可用,而非认定为架构不兼容。重复/旧代次设备报告不能覆盖终态。
  • 旧 poll 下载/上报同理遵循设备端决定,不在服务器加入新的 CPU 架构分支。

设备仓库配合(rcore-os/tgoskits,保持协议一致)

设备端目前已经验证 PE Machine/格式:x86 UEFI ota/disk.rs + ota/runtime.rs,LoongArch axstd_loader/ota.rs + efi_image.rs。原则上复用这些校验,不重新造架构枚举或 server 端验证。只需补强对应 HTTP adapter 的错误回传:x86 当前 422 返回 {},LoongArch 当前把 digest 和 EFI 错误合成 image_digest_or_efi_validation_failed;建议细化成上述稳定码,失败时返回 422、保证未提交新 trial,server 保存其 error/message。这部分若需单独 TGOSKits PR,请在本 PR 描述中明确关联;server 必须能兼容旧响应体,不以设备端升级作为删除架构白名单的唯一前置条件。

必须补齐的回归测试

  1. 仓库/队列:任意架构 PE 镜像(含 LoongArch64 PE Machine 0x6264)可上传并排队;镜像内容缺陷由设备返回结果决定。x86_64 镜像也仍可正常升级。
  2. 发现与下发:非 x86_64 的真实 observed.arch 可以进入同一 OTA 下发流程,旧 poll/下载路径没有硬编码的 x86 限制;MAC 冲突、错误 update_id、epoch 和缺失 OTA 能力仍被拒绝/等待。
  3. 设备拒绝:fake axloader 返回 422 + 机器码 / 旧版 422 + {},作业一次变为 Failed,错误可通过 API/UI 读取,同一镜像不再重发;400/413 类似。
  4. 可恢复错误:409、503、超时/5xx 不被误作 PE 架构错误,不触发错误的永久失败或无节制重传;202 后继续现有试运行→确认→成功/回滚状态流程。
  5. 设备测试:x86 和 LoongArch 各自用正确/错误 Machine 验证设备接收/422 拒绝;设备拒绝时活动槽不变,OTA trial 不被错误开启。

验收标准:今后 axloader 扩展 RISC-V64/AArch64 或新的固件校验契约,只需要变更设备自身实现;只要保持同一 OTA HTTP/状态协议,ostool-server 和 Web UI 无须新增架构分支即可直接使用。 请按以上形状修改并补测试后重新请求复审。

@YanLien
YanLien force-pushed the fix/riscv-efi-boot branch from f4db43a to d2ced95 Compare November 5, 2026 04:36
@YanLien
YanLien marked this pull request as draft November 5, 2026 04:52

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

本 PR 将 HTTP Boot 内核架构改为从 ELF 推导,并在串口交接前校验 ELF、声明与 axloader 实际架构;按架构选择 EFI 入口,同时移除持久化 boot_arch、迁移旧配置,并修复串口身份别名及启动并发/错误传递。影响 HTTP Boot 的 CLI、板卡 profile/API、配置迁移、管理 UI 与 OTA 契约;U-Boot、PXE 和既有手动串口流程未见受到影响,部署端需要配套更新 CLI 与 server。

验证: 精确 head d2ced95677aa06953add94cd38e0fa66c254ff24 的两个 GitHub Actions check 均已完成并通过;git diff --check origin/main...HEAD 通过,review helper 的 7 项测试通过。本地未能执行 Rust fmt/clippy/tests 或 pnpm UI test/build:环境缺少 cargo 和 pnpm。没有发现当前 head 上由 PR 引起的失败 CI。

既有意见: 先前关于架构错误应在串口交接前拒绝、ELF 架构应为上传依据、旧 boot_arch 兼容迁移以及 CLI 应获得具体预检错误的意见,已在当前代码中落实并有相应测试。PR issue comments 为空。相关前序工作包括已合并的 #198(axloader HTTP/OTA)和 #206(自动串口绑定);搜索未发现重复的开放 PR。

仍有一项阻塞问题:OTA 仍由 server/UI 按 CPU 架构硬编码限制,而不是由设备自身决定镜像兼容性;具体证据和修复建议见下方 inline comment。

Powered by gpt-6-luna

Comment thread ostool-server/src/ota.rs Outdated
}

fn reject_device_architecture(job: &mut Job, arch: BootArch) -> Option<String> {
if arch == BootArch::X86_64 || job.phase.is_terminal() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

OTA 兼容性仍由 server 在这里硬编码:除 x86_64 外,排队任务会被永久标记为失败;api/ota.rs 和 BoardEditor.tsx 也有相应的 x86-only 限制。这样即使 axloader 自身支持并能验证该 EFI 镜像,RISC-V64/LoongArch64 设备也无法收到 OTA,新增测试还将这一拒绝固定为预期行为。请保留 server 侧的大小/摘要、MAC/epoch 和任务状态校验,让设备 OTA 接口决定镜像是否可加载。

内核架构来自 ELF,设备架构来自 axloader;上传和串口交接前核对文件、声明及实际设备架构,按目标选择 EFI 入口。移除持久化 boot_arch,兼容读取并原子迁移旧 TOML,前端只读展示在线设备架构,允许离线准备会话和内核。

串行处理内核写入、命令发布和交接,优先响应电源换代及会话停止;完整读取设备 HTTP 响应体,保留串口、loader status、WebSocket 和 CLI 中的具体失败原因。

OTA 镜像对服务端是不透明字节,格式及平台兼容性由 axloader 校验。移除服务端和 UI 的 CPU 架构限制,保留大小、摘要、MAC、任务、注册代次和租约检查。确定性 HTTP 拒绝一次失败,409 延迟重试,503 等待实际状态或代次变化;响应不确定时先核对设备暂存和试运行状态,避免重复下发或错误耗尽重试。缺少 OTA 能力时保持可见等待并允许普通 HTTP Boot;终态和首条失败原因不被旧报告覆盖。

保留串口身份别名、共享序列号歧义提示及 usb_id 等既有配置;已成功加载的板卡不再重复显示历史配置隔离错误。同步中英文 README、API、管理 UI 和网络启动文档。

验证:回归在错误实现上失败、修复后通过;服务端 198 项、前端 40 项、完整浏览器 E2E 2 项通过。格式、差异、构建和 Clippy 检查完成,Clippy 仅有未修改代码的既有警告;7 份现有板卡配置加载正常。

真实 x86 QEMU 验证普通 HTTP Boot 和 OTA:错误 Machine 返回旧版 422 {},服务端保留错误且活动槽不变;正确镜像完成暂存、重启试运行及确认。axloader 宿主 35 项通过,含 LoongArch PE 校验;LoongArch 实际 HTTP OTA 未验证。

BananaPi 通过实体 U 盘 axloader、网络内核及 initramfs、ArceOS 四项启动标记,以及错误架构在交接前拒绝的实板验证。板端 ota=null,任务正确等待且不阻断普通启动,未声称完成实体 OTA 刷写。负向首轮无固件 UART,明确冷复位后重试通过,物理原因未确认。正式安装后的新版服务再次完成正向启动,CLI 退出 0,会话释放、租约 idle、配置不变。
@YanLien
YanLien force-pushed the fix/riscv-efi-boot branch from d2ced95 to 52a17ca Compare November 5, 2026 07:23
@YanLien
YanLien marked this pull request as ready for review November 5, 2026 07:37

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

审查结论

本 PR 将 HTTP Boot 内核架构改为从 ELF 推导,上传及串口交接前核验 ELF、声明与 axloader 实际架构,并按架构选择 EFI 入口;同时移除持久化 boot_arch(旧 TOML 经验证后迁移),修复启动取消/错误传递和串口身份别名处理。OTA 镜像改为服务端不透明字节,由 axloader 验证格式与平台兼容性,服务端按 HTTP 状态调度重试与试运行确认。

影响范围: 主要涉及 HTTP Boot 的 CLI、服务器、板卡配置/API 和管理 UI,以及 OTA 状态机/UI;旧 CLI 与 server 需配套升级。未见 PXE、U-Boot 启动或既有串口身份配置契约受影响。配置迁移保留旧 boot_arch 兼容读取,并继续拒绝其他未知字段。

验证: 精确 head 52a17ca622b086c528329b872aa06995b07b4094 的两个 GitHub Actions check 均已完成并通过;review helper 的 7 项测试、clone/head 校验和 git diff --check origin/main...HEAD 通过。本地未能运行 Rust fmt/clippy/tests 或 Web UI tests/build:当前环境没有 cargo 和 pnpm。PR 描述记录了 QEMU 与 RISC-V 实机验证,本审查未独立复跑硬件测试。

先前审查意见: 不支持架构在串口交接后才拒绝、boot_arch/ELF 架构来源失配、CLI 无法得到预检失败原因,以及 OTA 由 server/UI 按 x86_64 限制等意见均有依据;当前版本分别通过交接前预检与明确错误状态、ELF 校验和架构配置迁移、WebSocket/loader 状态错误传递,以及移除 OTA 架构门控予以解决。相关前序 PR 包括 #194、#198、#206 和 #215;搜索未发现重复的开放改动。

未发现当前 head 仍成立的阻塞问题或需单独提出的建议。剩余验证限制是本审查环境无法本地运行 Cargo/pnpm 检查,且未独立进行实体板复测;CI 均通过。

Powered by gpt-6-luna

@ZR233
ZR233 merged commit aa87a89 into main Nov 5, 2026
2 checks passed
@ZR233
ZR233 deleted the fix/riscv-efi-boot branch November 5, 2026 08:07
@github-actions github-actions Bot mentioned this pull request Nov 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants