Repository navigation
feat(ostool-server): add automatic axloader serial binding - #206
Conversation
There was a problem hiding this comment.
结论:REQUEST_CHANGES(1 个阻塞项 + 1 个建议项)
变更内容
把 loader 协议升级到 v6(httpboot-protocol 0.5.0),新增 ostool-serial crate(单例 SerialManager actor:枚举并独占打开候选串口、读取 AXLOADER-SERIAL/1 <serial_id> 身份帧、把不可克隆租约整段移交给会话),会话侧新增 SessionSerialRuntime(绑定/确认/撤销/运行态),server 增加 serial_manager SSE 主题、串口运行态 API、继电器排除与保留、device::reconcile() 的串口门禁,CLI 新增 ostool axloader run|continue,Web UI 在 axloader 模式隐藏手动串口并展示实际参数/运行态/管理器计数;OVMF+QEMU/PTY 集成测试与文档同步更新。
影响面
- 公共契约:
DEVICE_PROTOCOL_VERSION5→6,新增PREVIOUS_DEVICE_PROTOCOL_VERSION;LoaderAnnouncement/LoaderDeviceStatus的新增字段都带serde(default),旧设备 JSON 仍可反序列化。但POST /api/v1/loaders/poll现在对 v2/v3/v4 poll 装载器一律返回serial_protocol_upgrade_required(LoaderPollResponse::Boot已无任何调用点),旧装载器不再能启动。这是有意且有文档的破坏性变更(docs/api.md、docs/axloader-network-control.md§4.1),但必须与 TGOSKits 固件同批落地,否则 httpboot 板卡无法启动。 - axloader 会话现在必须先建立串口 WebSocket 才会推送启动(
device.rs的is_serial_connected()门禁);CLI(ostool/src/run/httpboot_board.rs会连接 WS)与 WebUI 已适配,serial: null的板卡在create_session/get_serial_status仍返回可用串口。 - U-Boot 手动串口流程保留,但现在先经
serial_manager.reserve()再打开,并在会话结束前用wait_owner_released()等待真实归还;继电器端口在发现阶段通过排除集合保护。 - 变更不是完全隔离的:
state.rs/session.rs/ws.rs/transport.rs/virtual_qemu.rs的串口与释放路径都被改写,SerialLease::Drop通过spawn_blocking关闭 fd 并归还 token 的语义需要保持。
验证
- 本审查环境缺少
cargo/rustc(PATH 与~/.cargo均无工具链),因此未能本地复现cargo fmt --all -- --check、cargo clippy --target x86_64-unknown-linux-gnu --all-features、cargo build、cargo test及 pnpm 单测/构建/E2E。这是环境限制,不作为缺陷记录。 - CI(head
4a3a66bf):check (stable, x86_64-unknown-linux-gnu)两个 run(113174481120、113174757739)均completed/success,mergeable_state: clean;未发现由本 PR 引起的 CI 失败。 - 已用
git diff --unified=0 origin/main...HEAD核对行号,两条 inline 评论都落在新增(RIGHT)行上。
历史评论
/pulls/206/reviews、/pulls/206/comments、/issues/206/comments 均为空,没有需要处理或延续的历史意见。
待解决问题
- 阻塞
ostool-server/src/device.rs:247:operation的所有错误(包括可重试的设备侧推送失败)都被交给serial_runtime.fail(),会终止串口运行时并request_stop(SerialClosed),会话被释放、已上传的启动文件被清理;这与本 PR 文档“第二次仍冲突则停止本次推送,等待后续设备广播”以及改动前只记log::warn!后重试的行为不一致。 - 建议
ostool-server/src/serial/runtime.rs:279:用?/ensure!处理单次绑定请求的设备侧条件(ready == false等),reply 被丢弃、调用方只看到 “serial binding cancelled”,且会话被结束;CLI 对同样条件只是清晰报错。 - 残留风险(未阻塞):
/api/v1/loaders/poll无手动/旧协议回退路径;实体板卡、真实 UDP 广播路由与正式 registry 发布的验证只在 PR 文档 §4.3 中声明,本环境未复核。
复核过的正面点
身份帧解码有界(64 字节行 + 溢出丢弃)、SerialLease 不可克隆且 drop 后再归还 token、wait_owner_released 等待真实归还而非 WS 布尔值、SerialHub::attach 在生产者锁内订阅+快照以消除历史/实时边界丢字节、loader.rs 发现循环按 MAC+epoch 记录并 abort 旧 reconcile,这些设计与文档一致。
Powered by deepseek-flash
|
已按 review 修复并推送
本地验证:
|
There was a problem hiding this comment.
结论:COMMENT(0 个阻塞项 + 1 个建议项)
变更内容
把 loader 协议升级到 v6(httpboot-protocol 0.5.0,DEVICE_PROTOCOL_VERSION = 6,并新增 PREVIOUS_DEVICE_PROTOCOL_VERSION),新增 ostool-serial crate(单例 SerialManager actor:枚举并独占打开候选串口、按 AXLOADER-SERIAL/1 <serial_id> 身份帧匹配、把不可克隆的 SerialLease 整段移交会话),server 侧新增 SessionSerialRuntime、serial_manager SSE 主题、SerialRuntimeStatus API、继电器排除与保留、device::reconcile() 的串口门禁,CLI 新增 ostool axloader run|continue,Web UI 在 axloader 模式隐藏手动串口并展示实际参数/运行态/管理器计数;OVMF+QEMU/PTY 集成测试与三份文档同步更新。
影响面与契约
- 破坏性协议变更:
POST /api/v1/loaders/poll对 v2/v3/v4 一律返回serial_protocol_upgrade_required(LoaderPollResponse::Boot已无调用点),v5 设备只保留识别与 OTA。这是有意且有文档的(docs/api.md、docs/axloader-network-control.md§4.1),但必须与 TGOSKits 的 v6 固件、以及httpboot-protocol 0.5.0/ostool-serial 0.1.0的发布同批落地,否则现有 httpboot 板卡无法启动。这是合并顺序要求,不是代码缺陷。 - 新增字段(
LoaderAnnouncement.serial_id/serial_ready、LoaderDeviceStatus.serial、Session.serial_runtime)都带serde(default),旧 JSON 仍可反序列化。 - axloader(UefiHttp)会话现在必须先建立串口 WebSocket 才会推送启动(
device.rs的is_serial_connected()门禁,serial_ws在升级前就try_set_serial_connected());CLI 与 Web UI 已适配,serial: null仍返回available: true与ws_url。 - U-Boot 手动串口流程保留(经
serial_manager.reserve()后打开,并在释放时wait_owner_released()等真实归还);继电器端口在发现阶段被排除集合保护,QEMU 串口 provider 改为从电源配置派生(BoardConfig::serial_available()、去掉 QEMU 串口校验)。 - 变更不是完全隔离的:
state.rs/session.rs/ws.rs/transport.rs/virtual_qemu.rs的串口与释放路径均被改写;SerialHub::attach在生产者锁内订阅+快照、transport.rs把 power-on 门禁从 WS 读取移到串口写入,这两处抵消了历史/实时边界丢字节与等待期无法处理 Ping/Close 的问题。
验证
- 本地环境没有
cargo/rustc/pnpm(仅 node),因此未能复现cargo fmt --all -- --check、cargo clippy/build/test与 pnpm 单测/构建/E2E;已用git diff --unified=0 origin/main...HEAD核对行号,新文件runtime.rs的 hunk 为@@ -0,0 +1,514 @@,inline 行落在新增(RIGHT)侧。这是环境限制,不作为缺陷记录。 - CI(head
f70b5a1):check run113195629281completed/success,113205866973仍在in_progress;未发现由本 PR 引起的 CI 失败。
历史评论
存在 1 条 CHANGES_REQUESTED(review 5452466994 @ 4a3a66b,Job 9c469b3e):阻塞项 device.rs:247(把可重试的设备侧推送失败交给 serial_runtime.fail(),导致会话释放、启动文件被清理)和建议项 serial/runtime.rs:279(Command::Bind 用 ? 丢弃 pending reply 并把可上报失败升级为致命错误)。两项技术判断合理,且已在 head f70b5a1 修复,实测证据如下:
device.rs:operation的错误改为log::warn!(..."waiting for the next device broadcast")并返回Err给 UDP 发现循环;ostool-server/tests/axloader_serial.rs新增用例断言设备409后serial_runtime仍可用于下一 epoch 的重试。serial/runtime.rs:新增reject_bind(),四个拒绝分支向 pendingreply发送真实错误、把 phase 置为Recovering、记录error后continue,ensure!已移除。
剩余问题
- 建议
ostool-server/src/serial/runtime.rs:285:被拒绝的绑定不会推进等待期限,60 秒后 actor 仍会bail!并终止整个会话(细节见 inline 评论)。这是上一轮建议项未闭环的另一半,不阻塞合并,但会让「设备上报ready = false」这一可恢复状态升级为会话丢失。
残余风险(未阻塞)
- 期限到期终止会话(60 秒等待、已绑定后的 5 秒恢复)本身是
docs/axloader-serial-ownership.md§2.1/§3.1 记录的行为,本环境无法验证真实板卡上的实际体验。 - 设备侧
DELETE /api/v1/serial/bindings/{id}在本仓库只按200校验(device.rs的撤销/清理路径),该状态码契约属于 TGOSKits 固件,未在本 PR 内核对。 - 未逐行审阅的面:新增集成测试(
ostool-server/tests/axloader_serial.rs、qemu_serial.rs、tests/common/serial.rs、ostool-serial/tests/manager.rs)、scripts/test-axloader-local.py重写与 web UI 测试改动。 - 已复核并排除的怀疑点:
ostool-serial/src/native.rs用O_NONBLOCK打开 tty 后并不会让物理读线程忙等——upstream serialport 的 posixTTYPort::read先poll::wait_read_fd(fd, timeout)再读,20 ms 超时成为轮询周期;SerialLease/候选/定位缓存与租约归还的生命周期各阶段也按文档实现了「取消/超时先关闭 IO 再解除保留」。
同类 PR
repo:drivercraft/ostool is:pr axloader serial 仅命中本 PR 与已合并的历史工作 #121(feat: Add UEFI HTTP boot support),没有重复或竞争的未合并 PR。
Powered by deepseek-flash
|
补充修复已推送 验证结果:
|
There was a problem hiding this comment.
结论:COMMENT(0 个阻塞项 + 2 个建议项)
变更内容
把 loader 设备协议升级到 v6(DEVICE_PROTOCOL_VERSION = 6、新增 PREVIOUS_DEVICE_PROTOCOL_VERSION = 5、httpboot-protocol 0.5.0),axloader 每次上电生成 32 位 serial_id 并上报实际 UART 参数与 boot_epoch;新增 ostool-serial crate(单例 SerialManager actor:枚举并独占打开候选串口、按 AXLOADER-SERIAL/1 <serial_id> 身份帧匹配、把不可克隆的 SerialLease 整段移交会话);server 侧新增 SessionSerialRuntime(绑定/确认/撤销/运行态)、serial_manager SSE 主题、SerialRuntimeStatus API、继电器排除与保留、device::reconcile() 的串口门禁,并把 serial: null 的 axloader 板卡从「无串口」改为「自动绑定」;CLI 新增 ostool axloader run|continue;Web UI 在 axloader 模式隐藏手动串口字段并展示实际参数/运行态/管理器计数。
影响面与契约
- 破坏性协议变更:
POST /api/v1/loaders/poll对 v2/v3/v4 一律返回serial_protocol_upgrade_required(LoaderPollResponse::Boot在 server 中已无任何构造点,v2 的boot_payload_unsupported分支被删除),v5 设备只保留识别与 OTA。这是有意且有文档的(docs/api.md、docs/axloader-network-control.md§4.1/§4.2),但必须与 TGOSKits 的 v6 固件、以及httpboot-protocol 0.5.0/ostool-serial 0.1.0的发布同批落地,否则现有 httpboot 板卡无法启动。这是合并顺序要求,不作为代码缺陷记录。 - 新增字段(
LoaderAnnouncement.serial_id/serial_ready、LoaderDeviceStatus.serial、Session.serial_runtime、SerialStatusResponse.runtime/manager)都带serde(default),旧 JSON 仍可反序列化;ostool board run的 httpboot 路径会先建立串口 WebSocket,与新的is_serial_connected()门禁相容。 - 变更不是完全隔离的:
state.rs/session.rs/serial/ws.rs/serial/transport.rs/virtual_qemu.rs的串口与释放路径都被改写(wait_for_session_tasks_to_stop改为等待管理器确认租约真实归还、SerialHub::attach在生产锁内订阅+快照、power-on 门禁从 WS 读取移到串口写入)。已逐条核对这些路径没有引入新的死锁或生命周期漏洞。
验证
- 本审查环境没有 Rust 工具链与 pnpm(
command -v cargo/pnpm均不存在,~/.cargo不存在),因此未能本地复现 helper 建议的cargo fmt/clippy/test与 pnpm 单测/构建/E2E;这是环境限制,不作为缺陷记录。 - CI(head
50b51bfe,check run 的head_sha与本 Job 固定版本完全一致):113212940947、113212957988两次check (stable, x86_64-unknown-linux-gnu)均completed/success(run37747672404、37747677703),覆盖cargo fmt --all -- --check、clippy、build、单测、webui 单测/构建、Playwright E2E 与cargo publish --workspace --dry-run --locked。未发现由本 PR 引起的 CI 失败;mergeable_state: blocked来自既有 review 状态而非 CI。 - 已用
git diff --unified=0 origin/main...HEAD核对行号:ostool-server/src/serial/runtime.rs为新增文件(唯一 hunk@@ -0,0 +1,514 @@),inline 评论落在新增(RIGHT)侧。
历史评论
- review 5452466994(CHANGES_REQUESTED @
4a3a66b)阻塞项device.rs:把可重试的设备侧推送失败交给serial_runtime.fail()。技术判断成立,已在f70b5a1修复:错误改为log::warn!(... "waiting for the next device broadcast")并返回给 UDP 循环,ostool-server/tests/axloader_serial.rs新增「设备 409 后下一次广播重试」用例断言starts == 3。 - 同轮建议项
serial/runtime.rs(Command::Bind用?丢弃 pending reply):已修复,四个拒绝分支改为reject_bind(),向 pendingreply返回真实错误、置Recovering并继续运行。 - review 5453479862(COMMENTED @
f70b5a1)剩余建议项(被拒绝的绑定不推进 60 秒等待期限):在 head 上仍然成立,见 F-002。 - 作者在 issue comment 6054699269 / 6055786253 中的说明与代码一致;
50b51bf把两处Atomic::fetch_update改为try_update,经核对语义等价(network_test.rs只做map(|_| ())/map_err(|_| ...),state.rs测试只做is_err(),都不读取 Err 载荷),不影响行为,且 CI 在 stable 上通过。
待解决问题
- 建议(body-only,README 在本次改动范围外,无可用 RIGHT 行)
README.md:18/336、README.en.md:18/360未随本 PR 同步:仍写「v2/v3/v4 旧协议兼容及 v5 设备 HTTP 接口」「v2/v3/v4 poll 仍收到原httpboot_entry」「服务端仍允许 v2 loader 启动两个新字段均未配置的旧会话,遇到任一新字段则在 poll 阶段明确拒绝」。但 head 上poll_loader已删除该分支,任何旧 poll 都返回serial_protocol_upgrade_required,且grep -rn "LoaderPollResponse::Boot" --include=*.rs .只剩httpboot-protocol的一个单测。按/project/repo/AGENTS.md「用户可见的服务器 API/工作流变化需同步 README,且中英文根 README 保持一致」,建议在同一变更内更新这两份 README,明确「旧 poll 仅保留识别与升级、普通启动要求 v6」。 - 建议(inline)
ostool-server/src/serial/runtime.rs:448-455:reject_bind不推进 60 秒deadline,设备持续上报serial.ready == false会在 60 秒后把可恢复状态升级为「会话丢失 + 已上传文件被清理」。详细说明见 inline 评论。
残余风险(未阻塞,仅记录)
- v5 设备(或 v2/v3/v4 poll)在存在活跃会话时会触发
serial_runtime.fail()(device.rs、api/router.rs)并终止会话,SessionStopReason呈现为SerialClosed,与「必须升级装载器」的原因不完全对应;运行态error里有正确原因,属有意且有文档的升级策略,但界面措辞可读性一般。 - 实体板卡、生产 UDP/netns 路由、非 Linux 宿主、以及正式 registry 发布后的构建不在本 PR 内且本环境无法复核(PR 文档 §4.3 也声明为本地验收,非远程结论)。
- 未逐行审阅的面:新增集成测试(
ostool-server/tests/axloader_serial.rs、qemu_serial.rs、tests/common/serial.rs、ostool-serial/tests/manager.rs)、scripts/test-axloader-local.py重写与 webui 测试/e2e 增补;已抽查断言有效(真实 PTY 打开/关闭计数、租约移交、别名排除、定位复用、两次上电参数恢复)。
复核过的正面点
身份帧解码有界(64 字节行 + 溢出丢弃)、SerialLease 不可克隆且 drop 后由 wait_owner_released 等真实归还、Command::Confirm/Release 带 token 防止旧租约影响新主人、SerialHub::attach 在生产者锁内订阅+快照消除历史/实时边界丢字节、loader.rs 发现循环按 MAC+epoch 去重并 abort 旧 reconcile、discovery.rs 的同型号 USB 匹配改为要求唯一命中——这些设计与 docs/axloader-serial-ownership.md 一致,未发现新的正确性、并发或资源泄漏问题。
同类 PR
repo:drivercraft/ostool is:pr axloader serial 只命中本 PR 与已合并的 #121(UEFI HTTP Boot 支持),没有重复或竞争的未合并 PR。
Powered by deepseek-flash
|
补充实机验证(不含敏感连接信息):
|
|
更正实机诊断表述: |
There was a problem hiding this comment.
结论:COMMENT(0 个阻塞项 + 3 个建议项)
本 PR 在 head 84e7848 上仍以单例 ostool-serial 管理器 + v6 设备协议实现 axloader 串口自动绑定;本轮新增的最后一个提交 84e7848 feat(ostool-server): persist axloader serial overrides 把宿主串口参数做成可选持久化覆盖并加入 115200/8N1 回退,其余内容与上一轮一致。未发现阻塞问题。
本轮新增内容(相对 50b51bf)
UefiHttpProfile.serial_parameters: Option<AxloaderSerialParameters>(config.rs:721-747)持久化宿主侧覆盖;BoardConfig::validate与normalize_boot_config复用validate();Web UI 在 axloader 区新增「指定串口参数」及波特率/数据位/校验/停止位/流控,未启用时提交serial_parameters: null。- 会话侧
effective_parameters()给出优先级「持久化覆盖 → 本次设备上报 → 115200/8N1」,SerialRuntimeStatus新增warning(取自设备serial.error),docs/api.md、docs/admin-ui.md、docs/axloader-serial-ownership.md同步更新;ostool-server/tests/axloader_serial.rs把覆盖设为 38400 并断言宿主实际按 38400 配置(覆盖端到端路径),config.rs、runtime.rs、board-form.test.ts、BoardEditor.test.tsx也有对应单测。
影响面与契约
- 破坏性协议变更不变:
POST /api/v1/loaders/poll对任何带 boot command 的 poll 返回serial_protocol_upgrade_required(api/router.rs:813),v2/v3/v4 已不能启动,必须与 TGOSKits v6 固件、httpboot-protocol 0.5.0/ostool-serial 0.1.0同批落地;新增字段都带serde(default),旧 JSON 仍可反序列化。 - 新配置只影响 httpboot 板卡的串口发现参数,不改动 U-Boot/PXE 串口、TFTP、OTA 与会话释放路径(
serial对 httpboot 仍强制为 null),改动可视为对该子系统的局部扩展。
验证
- CI(head
84e7848,check run 的head_sha与本 Job 固定版本完全一致):check (stable, x86_64-unknown-linux-gnu)两个 run(113265664971、113265688580)均completed/success;未发现由本 PR 引起的 CI 失败。mergeable_state: blocked来自本 Job 家族此前的CHANGES_REQUESTEDreview,不是 CI。 - 本审查环境没有
cargo/rustc/pnpm(command -v均不存在),因此未能本地复现cargo fmt/clippy/build/test与 pnpm 单测/构建/E2E,这是环境限制,不作为缺陷记录。 - 行号核对:
git diff --unified=0 origin/main...HEAD显示ostool-server/src/serial/runtime.rs为新增文件(唯一 hunk@@ -0,0 +1,591 @@),ostool-server/src/config.rs新增 hunk 为@@ -719,0 +721,100 @@,两条 inline 评论都落在新增(RIGHT)行上。
历史评论
- review 5452466994(CHANGES_REQUESTED @
4a3a66b):阻塞项device.rs把可重试的设备侧推送失败交给serial_runtime.fail(),已在f70b5a1修复,本轮复核有效(改为log::warn!后返回,等待下一次广播,且新增「设备 409 后下一 epoch 重试」用例)。 - review 5453479862(@
f70b5a1)与 5453945232(@50b51bf)的建议项在 head 上仍然成立:reject_bind不推进 60 秒期限(F-002)、README 未同步(F-001,且本轮新增的serial_parameters也未写入 README)。 - 作者在 issue comments 6054699269 / 6055786253 / 6056454453 / 6056509601 中的说明与代码一致;其实机记录的
ready=false场景正好落在 F-002 上。
待解决问题(全部非阻塞)
- F-001 建议(body-only)
README.md:18/336、README.en.md:18/360仍写「v2/v3/v4 旧协议兼容」「v2/v3/v4 poll 仍收到原httpboot_entry」「服务端仍允许 v2 loader 启动两个新字段均未配置的旧会话」,但 head 上任何带 boot command 的 poll 都只返回serial_protocol_upgrade_required,且本 PR 新增并持久化了boot.serial_parameters却未写进两份根 README。/project/repo/AGENTS.md要求用户可见的服务器 API/配置变化同步 README,并保持中英文一致。README 不在本 PR 改动文件内,无法作为 inline 行评论,故记在此处。 - F-002 建议(inline,
ostool-server/src/serial/runtime.rs:456)reject_bind()走的可恢复路径不推进deadline,设备持续上报ready = false时 60 秒后会bail!并由attach()触发session.request_stop(SerialClosed),终止会话、清理已上传文件,并用 deadline 文案覆盖设备给出的真实原因。 - F-003 建议(inline,
ostool-server/src/config.rs:739) 持久化覆盖接受parity = mark/space与stop_bits = one_point_five,而非 QEMU 板卡的宿主后端固定拒绝这些组合,保存期无法发现,失败被推迟到运行期并与 F-002 叠加成会话终止;Web UI 下拉仍把这些组合列为可选项。
残余风险(未阻塞,仅记录)
- 实体板卡自动绑定尚未通过(作者 comment 6056454453 / 6056509601 记录
ready=false、UART selection failed: NOT_FOUND,且无法据此断言 BIOS 缺少 SerialIo),因此实体路径目前只有失败结论;生产 UDP/netns 路由、非 Linux 宿主、registry 正式发布后的构建不在本 PR 内,本环境也无法复核。 - 未逐行审阅的面:新增/改写的集成测试(
ostool-server/tests/axloader_serial.rs、qemu_serial.rs、tests/common/serial.rs、ostool-serial/tests/manager.rs)、scripts/test-axloader-local.py重写、webui 测试与 e2e 增补;已抽查关键断言(覆盖参数生效、租约移交、relay 别名排除、定位复用、双次上电参数恢复)有效。
同类 PR
repo:drivercraft/ostool is:pr axloader serial 只命中本 PR 与已合并的 #121(UEFI HTTP Boot 支持),没有重复或竞争的未合并 PR。
Powered by deepseek-flash
|
已处理当前 review 中记录的全部问题,并推送到 head
本地已通过格式化、配置回归测试、Axloader PTY 集成测试;新的 CI 正在运行。 |
There was a problem hiding this comment.
结论:COMMENT(0 个阻塞项 + 2 个建议项)
本轮审查 head 97af14f(相对上一轮 84e7848 新增 e1d6862、97af14f)。此前轮次的阻塞项与建议项均已修复并复核,未发现新的阻塞问题;剩余两条建议都集中在「等待窗口语义」这一处。
本轮新增内容(相对 84e7848)
e1d6862 fix(ostool-server): close axloader serial review gaps:非 QEMU 板卡在BoardConfig::validate中调用ostool_serial::validate_host_parameters,保存期即拒绝mark/space/one_point_five(含回归测试physical_httpboot_rejects_host_unsupported_serial_modes);Web UI 校验/停止位下拉移除这些组合;docs/api.md同步说明;两份根 README 改写为「v6 串口自动绑定、v5 识别与 OTA、v2/v3/v4 仅保留识别与升级、普通启动返回serial_protocol_upgrade_required」。97af14f fix(ostool-server): refresh serial retry deadline on rejection:把等待窗口刷新移入reject_bind()(新增deadline: &mut Instant参数与SERIAL_BIND_TIMEOUT常量),四个设备侧拒绝分支(缺serial、ready = false、参数非法、宿主不支持)都会顺延 60 秒窗口,不再因最初窗口到期而终止会话。
影响面与契约
- 破坏性协议变更不变:
DEVICE_PROTOCOL_VERSION = 6、PREVIOUS_DEVICE_PROTOCOL_VERSION = 5、httpboot-protocol 0.5.0;/api/v1/loaders/poll对任何带 boot command 的旧 poll 返回serial_protocol_upgrade_required,v5 只保留识别与 OTA。新增字段(LoaderAnnouncement.serial_id/serial_ready、LoaderDeviceStatus.serial、Session.serial_runtime、SerialStatusResponse.runtime/manager)都带serde(default),旧 JSON 仍可反序列化;ostool侧的SerialStatusResponse也带serde(default)。 - 合并顺序要求(非代码缺陷,但不满足会让现有 httpboot 板卡无法启动):需与 TGOSKits 的 v6 固件、
httpboot-protocol 0.5.0/ostool-serial 0.1.0的 registry 发布同批落地。 - 新配置只影响 httpboot 板卡的串口发现:
normalize_board_upsert_request对 httpboot 强制serial: null,serial_available()与 QEMU 串口 provider 从电源配置派生。U-Boot/PXE 手动串口、TFTP、OTA 与会话释放路径的改动集中在「经管理器 reserve / wait_owner_released」;state.rs/session.rs/ws.rs/transport.rs的行为改写已复核,未发现新的死锁、丢字节或租约泄漏(SerialHub::attach在生产者锁内订阅+快照、transport.rs把上电门禁从 WS 读取移到串口写入、SerialLease::drop经spawn_blocking关闭 IO 后再归还 token)。
验证
- CI(head
97af14f,check run 的head_sha与本 Job 固定版本完全一致):check (stable, x86_64-unknown-linux-gnu)两次均completed/success(run37869050284、37869057327,job113622600570、113622624408),覆盖cargo fmt --all -- --check、clippy、build、单测、webui 单测/构建、Playwright E2E 与cargo publish --workspace --dry-run --locked。未发现由本 PR 引起的 CI 失败;mergeable_state: blocked来自此前CHANGES_REQUESTEDreview,不是 CI。 - 本审查环境没有 Rust 工具链与 pnpm(
command -v cargo/rustc/pnpm均不存在,~/.cargo不存在),因此未能本地复现cargo fmt/clippy/build/test与 pnpm 单测/构建/E2E;这是环境限制,不作为缺陷记录。 - 行号核对:
git diff --unified=0 origin/main...HEAD显示ostool-server/src/serial/runtime.rs为新增文件(唯一 hunk@@ -0,0 +1,606 @@),inline 评论落在新增(RIGHT)侧的runtime.rs:387。
历史评论
- review 5452466994(CHANGES_REQUESTED @
4a3a66b)阻塞项device.rs:把可重试的设备侧推送失败交给serial_runtime.fail()。判断成立,已在f70b5a1修复;本轮复核 head 仍成立——operation的Err只记log::warn!(... waiting for the next device broadcast)并返回给 UDP 发现循环。 - 同轮建议项
serial/runtime.rs(Command::Bind用?丢弃 pending reply):已修复为reject_bind(),向 pendingreply发送真实错误并置Recovering。 - review 5453479862 / 5453945232 / 5455307370 的建议项「
reject_bind不推进 60 秒期限」:已在97af14f修复(reject_bind直接刷新*deadline,四个分支都传入&mut deadline),作者回复 4225772050 / 4225772248 与代码一致。 - review 5455307370 的 README 未同步项:已在
e1d6862修复(两份根 README 的中英文段落都改写为 v6/旧 poll 语义,保持一致)。 - review 5455307370 的「持久化覆盖接受宿主不支持组合」项:已在
e1d6862修复(保存期拒绝 + Web UI 移除选项 + 文档说明 + 回归测试)。 - 作者在 issue comments 中的说明与代码一致;本环境无法复核其实机记录。
待解决问题(全部非阻塞)
- 建议(inline,
ostool-server/src/serial/runtime.rs:387)RuntimeEvent::Opened仍用?把管理器错误升级为整个会话的终止,而管理器真正给身份帧的窗口是扫描期限max(5 秒, 组合数 × 2 秒)(单组合约 6 秒,ostool-serial/src/manager.rs的scan_deadline),不是 docs §2.1 暗示的 60 秒。设备ready = true但宿主在数秒内没看到身份帧时会销毁会话并清理已上传文件,与97af14f刚把reject_bind()改为可恢复的做法不一致,文档也未描述该窗口。详见 inline 评论。 - 建议(body-only) 服务端现在按每个有效设备报告重启 60 秒窗口,但
ostool/src/axloader.rs:109的 CLI 仍是一次性deadline: Instant::now() + 60s;docs/axloader-serial-ownership.md§2.1 的「设备等待上限 60 秒」也没有说明窗口刷新规则。同一块持续上报ready = false的板子会呈现「服务端一直等 / CLI 60 秒失败」两种行为。建议在 §2.1 写清服务端按报告刷新、管理器按请求扫描窗口max(5 秒, 组合数 × 2 秒),并让 CLI 在轮询到新状态时刷新期限(或在 CLI 帮助与docs/axloader-network-control.md中明确run的身份帧上限是 60 秒)。
残余风险(未阻塞,仅记录)
- v5 设备(或旧 poll)在存在活跃会话时仍会触发
serial_runtime.fail()并终止会话,SessionStopReason呈现为SerialClosed,与「必须升级装载器」的原因不完全对应;运行态error里有正确原因,属有意且有文档的升级策略。 - 实体板卡的自动绑定目前只有失败结论(作者 comment 记录
ready = false、UART selection failed: NOT_FOUND),生产 UDP/netns 路由、非 Linux 宿主、正式 registry 发布后的构建不在本 PR 内,本环境也无法复核。 - 未逐行审阅的面:新增/改写的集成测试(
ostool-server/tests/axloader_serial.rs、qemu_serial.rs、tests/common/serial.rs、ostool-serial/tests/manager.rs)、scripts/test-axloader-local.py重写、webui 测试与 e2e 增补;已抽查关键断言(覆盖参数 38400 实际生效、租约移交、relay 别名排除、定位复用、双次上电参数恢复、ready=false 后恢复)有效。
复核过的正面点
身份帧解码有界(64 字节行 + 溢出丢弃)、SerialLease 不可克隆且 drop 后再归还 token、Command::Confirm/Release 带 token 防止旧租约影响新主人、wait_owner_released 等真实归还而非 WS 布尔值、loader.rs 发现循环按 MAC+epoch 去重并 abort 旧 reconcile、discovery.rs 同型号 USB 匹配要求唯一命中、native.rs 的 unsafe 均有 SAFETY 说明且 RestoreSettings 在读取线程 join 后再恢复 termios——这些设计与 docs/axloader-serial-ownership.md 一致。
同类 PR
用 repo:drivercraft/ostool is:pr axloader serial 搜索只命中本 PR 与已合并的 #121(UEFI HTTP Boot 支持),没有重复或竞争的未合并 PR。
Powered by deepseek-flash
|
已处理新 review,并推送
本地 server、ostool、ostool-serial 测试和 Clippy 已通过,新的 CI 正在运行。 |
There was a problem hiding this comment.
结论:COMMENT(0 个阻塞项 + 2 个建议项)
本轮审查 head 532c030(相对上一轮 97af14f 只改 3 个文件:docs/axloader-serial-ownership.md、ostool-server/src/serial/runtime.rs、ostool/src/axloader.rs)。上一轮的两条建议项都已闭环,未发现新的阻塞问题;剩余两条建议都属于「运行态信息 / 测试覆盖」范畴。
本轮新增内容
RuntimeEvent::Opened不再用?把管理器错误升级为会话终止:Timeout/DiscoveryFailed/Busy/Io进入新的可恢复分支(current.take()、serial_manager.invalidate()、清空verifying_until、reject_bind(Some(reply))回复具体错误并刷新 60 秒窗口),InvalidRequest/Stopped仍终止会话。reject_bind()的 reply 改为Option(供Opened分支在 reply 已被消费时复用),并新增port/binding_id清空与warning(取自设备serial.error)。- CLI
ostool axloader run:改为先fetch_status再进循环;ready = false时每 250 ms 重读状态;manager.bind返回Timeout|DiscoveryFailed|Busy|Io时重读状态并重新申请绑定;单次请求期限min(剩余, 5 秒)、总窗口 60 秒(CLI_BIND_TIMEOUT)。 - 文档 §2.1/§3 补上「服务端按有效报告刷新窗口」「管理器单次扫描窗口
max(5 秒, 组合数 × 2 秒)」「CLI 60 秒总窗口与重试条件」。
影响面与契约
- 只涉及 axloader 自动绑定与 CLI 等待逻辑;U-Boot/PXE 手动串口、TFTP、OTA、会话释放路径未改动;协议版本与
serde(default)兼容结论与上一轮一致。 - 合并顺序要求不变(非代码缺陷):
DEVICE_PROTOCOL_VERSION = 6需与 TGOSKits v6 固件、httpboot-protocol 0.5.0/ostool-serial 0.1.0的 registry 发布同批落地。
验证
- CI(head
532c030,check run 的head_sha与本 Job 固定版本一致):两次check (stable, x86_64-unknown-linux-gnu)均completed/success(run37871714991、37871708641,job113631086723、113631067357),覆盖 fmt/clippy/build/单测、webui 单测与构建、Playwright E2E 与cargo publish --workspace --dry-run --locked;未发现由本 PR 引起的 CI 失败。mergeable_state: blocked来自此前CHANGES_REQUESTEDreview,不是 CI。 - 本审查环境没有 Rust 工具链与 pnpm(
command -v cargo/rustc/pnpm均缺失、~/.cargo不存在),因此未能本地复现cargo fmt/clippy/test与 pnpm 测试;这是环境限制,不作为缺陷记录。 - 行号核对:
git diff --unified=0 origin/main...HEAD显示ostool-server/src/serial/runtime.rs(唯一 hunk@@ -0,0 +1,648 @@)与ostool/src/axloader.rs(唯一 hunk@@ -0,0 +1,327 @@)在本 PR 内都是新增文件,两条 inline 评论都落在新增(RIGHT)行上。
历史评论
- review 5452466994 / 5453479862 / 5453945232 / 5455307370 / 5464758765 的全部条目已在前几轮或
97af14f闭环:device.rs的可重试设备侧推送错误不再fail()、reject_bind刷新 60 秒窗口、物理板保存期拒绝 mark/space 与 1.5 stop bits、两份根 README 同步。 - 上一轮 job 59e5fd25 的两条建议:①
Opened的?会把约 5~6 秒扫描超时升级为会话终止 → 本轮已修复(见 F-001 的核对,代码确为可恢复分支);② CLI 一次性 60 秒期限 → 已改为轮询+重试且文档写明。作者 comment 6072619291 的说明与代码一致。 - 作者 issue comments 6054699269 / 6055786253 / 6056454453 / 6056509601 / 6072281053 / 6072619291 的说明均与 head 代码一致;实机自动绑定仍未通过(
ready=false、UART selection failed: NOT_FOUND)属固件侧结论,不在本 PR 判定范围。
待解决问题(均为建议,非阻塞)
- 建议(inline,
ostool-server/src/serial/runtime.rs:395) 新增的可恢复分支没有回归测试:本轮提交没有任何测试变更,而该分支正是上一轮建议项与更早阻塞项的同一类语义(可恢复失败不得结束会话)。 - 建议(inline,
ostool-server/src/serial/runtime.rs:516)reject_bind()无条件清空port/binding_id,会把仍被会话持有并正在使用的实时租约信息一起抹掉,并让device.rs的令牌一致性比较失去区分能力。
残余风险(未阻塞,仅记录)
- 60 秒窗口现在只在「没有后续设备报告」时到期:设备持续广播但始终没有身份帧时,会话与板卡租约、候选串口扫描会一直重试(文档 §2.1 已写明刷新语义,停止条件只能由操作者取消会话)。
- CLI 与服务端对「
status.serial缺失」「ready = true但parameters缺失/宿主不支持」仍不对称:CLI 在第一条报告上即失败,服务端为可恢复;文档 §3 只列举了 CLI 重试的三类条件。 - 实体板卡自动绑定目前只有失败结论;生产 UDP/netns 路由、非 Linux 宿主、正式 registry 发布后的构建不在本 PR 内,本环境无法复核。
- 未逐行审阅的面:
ostool-server/tests/axloader_serial.rs、qemu_serial.rs、tests/common/serial.rs、ostool-serial/tests/manager.rs、scripts/test-axloader-local.py重写与 webui 测试/e2e 增补(已抽查关键断言:覆盖参数 38400 生效、租约移交、relay 别名排除、定位复用、双次上电参数恢复、ready=false后恢复)。
同类 PR
repo:drivercraft/ostool is:pr axloader serial 只命中本 PR 与已合并的 #121(UEFI HTTP Boot 支持),没有重复或竞争的未合并 PR。
Powered by deepseek-flash
There was a problem hiding this comment.
结论:COMMENT(0 个阻塞项 + 1 个建议项)
本轮审查 head 77061dd(相对上一轮 532c030 新增 654026b、700ab63、a854be4、77061dd)。上一轮的两条建议项都已在 head 上闭环,未发现新的阻塞问题;剩余一条建议属于「设备侧报告的校验归类」范畴。
本轮新增内容(相对 532c030)
700ab63 fix(ostool-server): keep active binding state by epoch:reject_bind()新增active: ActiveBindingStatus参数,引入active_binding_status(lease, current, boot_epoch),在仍持有实时租约时继续发布port与同一 boot epoch 的binding_id,只有没有实时租约时才清空;docs/api.md、docs/axloader-serial-ownership.md§3 同步说明Recovering可能保留待重新验证的租约。654026b/a854be4:ostool-server/tests/axloader_serial.rs增加「身份帧扫描超时后不终止会话(Recovering+ 仍可重试同一 epoch)」与「设备上报ready=false时保留实时租约端口/令牌、且已上传kernel.elf仍在」的断言,正好覆盖上一轮的回归测试缺口。
影响面与契约
- 破坏性协议变更不变:
DEVICE_PROTOCOL_VERSION = 6、PREVIOUS_DEVICE_PROTOCOL_VERSION = 5、httpboot-protocol 0.5.0;/api/v1/loaders/poll对任何带 boot command 的旧 poll 返回serial_protocol_upgrade_required,v5 只保留识别与 OTA。新增字段(LoaderAnnouncement.serial_id/serial_ready、LoaderDeviceStatus.serial、Session.serial_runtime、SerialStatusResponse.runtime/manager)都带serde(default),旧 JSON 仍可反序列化。 - 本 PR 不是完全隔离的改动:
state.rs/session.rs/serial/ws.rs/serial/transport.rs/virtual_qemu.rs的串口与释放路径都被改写(wait_for_session_tasks_to_stop改为等管理器确认租约真实归还、SerialHub::attach在生产锁内订阅+快照、power-on 门禁从 WS 读取移到串口写入、PhysicalSerial迁入ostool-serial);U-Boot/PXE 手动串口、TFTP、OTA 的语义保持,仅改为经serial_manager.reserve()/wait_owner_released()协调。 - 合并顺序要求(非代码缺陷):需与 TGOSKits 的 v6 固件、
httpboot-protocol 0.5.0/ostool-serial 0.1.0的 registry 发布同批落地,否则现有 httpboot 板卡无法启动。
验证
- 本审查环境没有 Rust 工具链与 pnpm(
command -v cargo/rustc/pnpm均不存在,target/仅有占位目录),因此未能本地复现 helper 建议的cargo fmt --check、cargo clippy、cargo test与 pnpm 单测/构建/E2E;这是环境限制,不作为缺陷记录。 - CI(head
77061dd,check run 的head_sha与本 Job 固定版本完全一致):两次check (stable, x86_64-unknown-linux-gnu)均completed/success(job113648864770/run37877348607、job113648852836/run37877344990),覆盖 fmt、clippy、build、cargo test(含本 PR 新增的ostool-server/tests/axloader_serial.rs、被#[ignore]的qemu_serial.rs除外)、webui 单测/构建、Playwright E2E 与cargo publish --workspace --dry-run --locked。未发现由本 PR 引起的 CI 失败;mergeable_state: blocked来自此前CHANGES_REQUESTEDreview,不是 CI。 - 行号核对:
git diff --unified=0 origin/main...HEAD显示ostool-server/src/serial/runtime.rs为新增文件(@@ -0,0 +1,702 @@),inline 评论落在新增(RIGHT)侧的 374 行。
历史评论
- review 5452466994(CHANGES_REQUESTED @
4a3a66b)阻塞项device.rs:可重试的设备侧推送失败被交给serial_runtime.fail()终止会话 → 已在f70b5a1修复,head 上operation的Err只log::warn!(... waiting for the next device broadcast)并返回给 UDP 发现循环,本轮复核成立。 - 同轮建议项(
Command::Bind用?丢弃 pending reply)→ 已由reject_bind()修复;review 5453479862 / 5453945232 / 5455307370 的「reject_bind不推进 60 秒期限」→ 已在97af14f修复(*deadline = Instant::now() + SERIAL_BIND_TIMEOUT);README 未同步与「持久化覆盖接受 mark/space/1.5 stop bits」→ 已在e1d6862修复;review 5464758765 的「Opened的?把扫描超时升级为会话终止」「CLI 一次性期限」→ 已在532c030修复。上述各项均已用 head 代码复核。 - review 5464944711(job 5b09d667 @
532c030)的两条建议:①可恢复Opened分支缺少回归测试 → 本轮测试补齐;②reject_bind()无条件清空port/binding_id会抹掉仍在使用的实时租约信息 → 本轮active_binding_status()按 epoch 保留并配文档。作者 issue comments(6072281053 / 6072619291 等)与 head 代码一致;其实机ready=false、UART selection failed: NOT_FOUND的记录属固件侧诊断,不在本 PR 判定范围。
待解决问题(非阻塞)
- 建议(inline,
ostool-server/src/serial/runtime.rs:374) 设备上报的serial_id/boot_epoch没有在服务端校验就进入绑定,管理器返回的BindError::InvalidRequest被当作不可恢复错误,会让会话进入 releasing 并清理已上传文件;这与本 PR 对其它设备侧报告(ready=false、缺参数、非法参数、宿主不支持)一律走reject_bind()保留会话的做法不一致。详见 inline 评论。
残余风险(未阻塞,仅记录)
- v5 设备(或 v2/v3/v4 poll)在存在活跃会话时会经
serial_runtime.fail()(device.rs、api/router.rs)终止会话,SessionStopReason呈现为SerialClosed;运行态error里有正确原因,属有意且有文档的升级策略(docs/api.md、docs/axloader-network-control.md§4.1),但界面措辞与「必须升级装载器」的原因不完全对应。 - 60 秒等待窗口只在「没有后续有效设备报告」时到期:设备持续广播但始终没有身份帧时,会话、板卡租约与候选串口扫描会持续重试,停止条件只能由操作者取消会话(
docs/axloader-serial-ownership.md§2.1 已写明刷新语义)。服务端按报告刷新窗口,而 CLI 是固定 60 秒总窗口,两者对持续ready=false的设备会呈现不同行为。 - 实体板卡自动绑定目前只有失败结论;生产 UDP/netns 路由、非 Linux 宿主、正式 registry 发布后的构建不在本 PR 内,本环境无法复核。
- 未逐行审阅的面:
ostool-server/scripts/test-axloader-local.py重写、ostool-server/webui的 e2e/单测增补;已抽查关键断言(覆盖参数 38400 实际生效、租约移交、relay 别名排除、定位复用、双次上电参数恢复、ready=false后恢复并保留会话文件)有效。
复核过的正面点
身份帧解码有界(64 字节行 + 溢出丢弃)、SerialLease 不可克隆且 Drop 经 spawn_blocking 关闭 IO 后再归还 token、Command::Confirm/Release 带 token 防止旧租约影响新主人、wait_owner_released 等真实归还而非 WS 布尔值、loader.rs 发现循环按 MAC+epoch 去重并 abort 旧 reconcile、SerialHub::attach 在生产者锁内订阅+快照消除历史/实时边界丢字节、discovery.rs 同型号 USB 匹配要求唯一命中、ostool-serial/src/native.rs 的 unsafe 均有 SAFETY 说明且 RestoreSettings 在接收线程 join 后才恢复 termios——这些设计与 docs/axloader-serial-ownership.md 一致。
同类 PR
repo:drivercraft/ostool is:pr axloader serial 只命中本 PR 与已合并的 #121(UEFI HTTP Boot 支持),没有重复或竞争的未合并 PR。
Powered by deepseek-flash
There was a problem hiding this comment.
结论:COMMENT(0 个阻塞项 + 2 个建议项)
本轮审查 head c66f8a7,相对上一轮 77061dd 只新增一个提交 c66f8a7 fix(ostool-server): recover from invalid loader identity(ostool-server/src/serial/runtime.rs +32、ostool-server/tests/axloader_serial.rs +60)。上一轮的建议项已闭环,未发现阻塞问题;剩余两条建议属「运行态诊断」与「CLI/服务端行为一致性」范畴。
上一轮建议项复核(review 5465310063 @ 77061dd)
唯一建议项「设备上报的 serial_id/boot_epoch 未校验就进入绑定,管理器返回 BindError::InvalidRequest 被当作不可恢复错误并终止会话」已在 c66f8a7 修复且复核有效:
runtime.rs:280-295在power_wait = true之后、读取device.serial之前先拒绝空boot_epoch;runtime.rs:312-327拒绝非 32 位小写十六进制的serial_id,两者都复用reject_bind()(发布Recovering、回真实错误、刷新 60 秒窗口)。- 我逐条核对了
ostool-serial/src/lib.rs:110-118的InvalidRequest三个前置条件(空boot_epoch、!valid_serial_id、parameters.validate()失败):前两项现在被服务端提前覆盖,第三项由runtime.rs:349-354的parameters.validate()覆盖(BindRequest.parameters即同一组已校验参数)。因此RuntimeEvent::Opened的Err(error) => return Err(error.into())现在只剩管理器Stopped(以及理论上不可达的InvalidRequest)会终止会话,与 PR 的失败恢复策略一致。 httpboot_protocol::valid_serial_id与ostool-serial内部校验是同一实现(httpboot-protocol/src/serial.rs:118),无规则漂移。- 新增测试用真实 PTY/HTTP/WebSocket 断言两个畸形报告都返回具体错误、phase 为
Recovering、!session.is_stop_requested(),随后同一 epoch 仍能完成绑定;随后并入原有两次上电循环,未破坏既有断言,覆盖是有效的。
验证
- CI(head
c66f8a760458a9cee11ec7033449eaf065fb3e7c):check (stable, x86_64-unknown-linux-gnu)两次均completed/success(check run113656975486、113656962130),覆盖cargo fmt --all -- --check、clippy、build、单测(含ostool-server/tests/axloader_serial.rs;qemu_serial.rs为#[ignore])、webui 单测/构建、Playwright E2E 与cargo publish --workspace --dry-run --locked。未发现由本 PR 引起的 CI 失败;mergeable_state: blocked来自此前CHANGES_REQUESTEDreview,不是 CI。 - 本地未复现 Rust/前端检查:本环境没有
cargo/rustc(~/.cargo不存在),也没有pnpm(只有 node v24.14.1),因此 helper 建议的cargo fmt --check、cargo clippy --all-features -- -D warnings、cargo test(httpboot-protocol / ostool / ostool-serial / ostool-server)与 pnpm 单测/构建/E2E 均未能运行;按/project/repo/AGENTS.md的要求在此明确说明未运行的检查,这是环境限制,不作为缺陷记录。 - 行号核对:
git diff --unified=0 origin/main...HEAD显示ostool-server/src/serial/runtime.rs(@@ -0,0 +1,734 @@)与ostool/src/axloader.rs(@@ -0,0 +1,327 @@)在本 PR 内均为新增文件,两条 inline 评论都落在新增(RIGHT)侧的真实行上。
影响面与契约
- 破坏性协议变更不变:
DEVICE_PROTOCOL_VERSION = 6、PREVIOUS_DEVICE_PROTOCOL_VERSION = 5、httpboot-protocol 0.5.0;/api/v1/loaders/poll对任何带 boot command 的旧 poll 返回serial_protocol_upgrade_required,v5 只保留识别与 OTA。LoaderAnnouncement.serial_id/serial_ready、LoaderDeviceStatus.serial、Session.serial_runtime、SerialStatusResponse.runtime/manager都带serde(default),旧 JSON 仍可反序列化。 - 合并顺序要求(非代码缺陷):需与 TGOSKits 的 v6 固件、
httpboot-protocol 0.5.0/ostool-serial 0.1.0的 registry 发布同批落地,否则现有 httpboot 板卡无法启动。 - 本 PR 不是隔离改动:
state.rs/session.rs/serial/ws.rs/serial/transport.rs/virtual_qemu.rs的串口与释放路径都被改写。本轮复核未发现新的死锁、丢字节或租约泄漏;顺带确认transport.rs的「输出队列try_send满即结束会话」是 base 已有且有注释说明的既有语义(origin/main:ostool-server/src/serial/transport.rs:16),不是本 PR 引入的回归。
待解决问题(均为建议,非阻塞)
- F-001(inline,
ostool-server/src/serial/runtime.rs:280) 新增的空boot_epoch可恢复分支会把空串写进公开运行态并被restart()退休,导致后续同类报告被静默忽略、界面保留过期错误。 - F-002(inline,
ostool/src/axloader.rs:107) CLI 对「ready = true但parameters缺失」直接失败退出,而服务端按「覆盖值 > 上报值 > 115200 8N1」回退并继续绑定;CLI 既无覆盖入口也无默认回退,PR 文档的优先级链在 CLI 上未落地。
残余风险(未阻塞,仅记录)
- 实体板卡的自动绑定目前只有失败结论(作者 comment 记录
ready = false、UART selection failed: NOT_FOUND),属固件侧诊断,不在本 PR 判定范围。 - v5 设备或旧 poll 在存在活跃会话时会经
serial_runtime.fail()(device.rs、api/router.rs)终止会话,SessionStopReason呈现为SerialClosed,与「必须升级装载器」的原因不完全对应(运行态error有正确原因),属有意且有文档的升级策略。 - 60 秒窗口只在「没有后续有效设备报告」时到期;设备持续广播但始终没有身份帧时会持续重试,停止条件只能由操作者取消会话(
docs/axloader-serial-ownership.md§2.1 已写明刷新语义)。 - 未逐行审阅的面:
ostool-server/tests/common/serial.rs、ostool-serial/tests/manager.rs、ostool-server/tests/qemu_serial.rs(#[ignore],仅由重写后的scripts/test-axloader-local.py手动触发)与 webui e2e 增补;已抽查关键断言(覆盖参数 38400 实际生效、租约移交、relay 别名排除、定位复用、双次上电参数恢复、畸形身份后会话保留且文件仍在)有效。生产 UDP/netns 路由、非 Linux 宿主与正式 registry 发布后的构建不在本 PR 内,本环境无法复核。
同类 PR
用 repo:drivercraft/ostool is:pr axloader serial 搜索只命中本 PR 与已合并的 #121(UEFI HTTP Boot 支持),没有重复或竞争的未合并 PR。
Powered by deepseek-flash
There was a problem hiding this comment.
结论:APPROVE(0 个阻塞项)
变更内容
把 loader 设备协议升级到 v6(DEVICE_PROTOCOL_VERSION = 6、新增 PREVIOUS_DEVICE_PROTOCOL_VERSION = 5、httpboot-protocol 0.5.0),axloader 每次上电上报 serial_id/boot_epoch/就绪状态/实际 UART 参数;新增 ostool-serial crate(单例 SerialManager actor:枚举并独占打开候选串口、按 AXLOADER-SERIAL/1 <serial_id> 身份帧匹配、把不可克隆的 SerialLease 整段移交会话);server 侧新增 SessionSerialRuntime(绑定/确认/撤销/运行态)、serial_manager SSE 主题、SerialRuntimeStatus API、继电器排除与保留、device::reconcile() 的串口门禁;CLI 新增 ostool axloader run|continue;Web UI 在 axloader 模式隐藏手动串口并展示实际参数/运行态/管理器计数。
影响面与契约
- 破坏性协议变更:
/api/v1/loaders/poll对任何带 boot command 的旧 poll 返回serial_protocol_upgrade_required,v5 设备只保留识别与 OTA。已确认 v4 的 OTA 决策分支(api/router.rs的PROTOCOL_VERSION == 4分支)位于新 Reject 之前,因此文档 §4.2 的「旧设备仍可识别和升级」与代码一致。 - 合并顺序要求(非代码缺陷):需与 TGOSKits 的 v6 固件、
httpboot-protocol 0.5.0/ostool-serial 0.1.0的 registry 发布同批落地;否则现有 v5 板卡不能自动启动,且其活跃会话会经serial_runtime.fail()以SerialClosed结束(运行态error保留正确原因,属有文档的升级策略)。 - 新增字段(
LoaderAnnouncement.serial_id/serial_ready、LoaderDeviceStatus.serial、Session.serial_runtime、SerialStatusResponse.runtime/manager)都带serde(default),旧 JSON 仍可反序列化。U-Boot/PXE 手动串口保留,但改为先serial_manager.reserve()再打开,并在释放时用wait_owner_released()等待真实归还。 - 非隔离改动:
state.rs/session.rs/serial/ws.rs/serial/transport.rs/virtual_qemu.rs的串口与释放路径被改写。本轮复核未发现新的死锁、丢字节或租约泄漏:SerialHub::attach在生产锁内订阅+快照,SerialLease::drop经spawn_blocking关闭 IO 并收到 reader 退出后再归还 token,wait_for_session_tasks_to_stop在 WS 断开后等管理器确认真实归还且有 2 秒兜底与整体重试。
验证
- 本审查环境没有 Rust 工具链与 pnpm(
command -v cargo/rustc/pnpm均缺失、~/.cargo不存在),因此 helper 建议的cargo fmt --check、cargo clippy --all-features -- -D warnings、cargo test(httpboot-protocol / ostool / ostool-serial / ostool-server)与 pnpm 单测/构建/E2E 均未能在本地复现,按/project/repo/AGENTS.md的要求在此明确说明。 - CI(head
95af5ada,与固定版本一致):check (stable, x86_64-unknown-linux-gnu)两次均completed/success(job113666862726/ run37883052411、job113666851978/ run37883049048),覆盖 fmt、clippy、build、cargo test(含新增的ostool-server/tests/axloader_serial.rs)、webui 单测/构建、Playwright E2E 与cargo publish --workspace --dry-run --locked。未发现由本 PR 引起的 CI 失败;mergeable_state: blocked来自此前 review 状态,不是 CI。 - 行号核对:本 PR 内
ostool-server/src/serial/runtime.rs与ostool/src/axloader.rs均为新增文件(唯一 hunk@@ -0,0 +1,736 @@/@@ -0,0 +1,345 @@);本轮无 inline 评论。
历史评论
- review 5465500614(job 7ace838f @
c66f8a7)的两条建议项均已在 head95af5ada修复并复核有效:- 空
boot_epoch不再写入运行态有效代次、也不进入退休代次(runtime.rs:86-88只退休非空 epoch,583-585用!device.boot_epoch.is_empty()保护status.boot_epoch);tests/axloader_serial.rs新增「restart(true)后再次上报空 epoch 仍返回invalid device boot epoch且!session.is_stop_requested()」断言,旧实现下该断言会得到waiting for a new boot epoch,确实能拦住回归。 - CLI 改为
effective_parameters(reported) = reported.unwrap_or_default()(协议默认 115200 8N1、无流控),新增单测missing_firmware_parameters_use_protocol_defaults;docs/axloader-serial-ownership.md§3.2 同步说明「CLI 使用本次上报参数、缺失时用默认值,Web UI 覆盖只属于 server session」。
- 空
- 更早各轮条目(
device.rs可重试推送错误不再fail()、reject_bind刷新 60 秒窗口、Opened的Timeout/DiscoveryFailed/Busy/IO走可恢复路径、Recovering保留同 epoch 端口与令牌、保存期拒绝 mark/space/1.5 stop bits、两份根 README 同步、BindError::InvalidRequest可恢复)在 head 上逐条复核仍为已修复。
待解决问题
无阻塞项,也没有需要修改的建议项;本轮不产生 inline 评论。残余风险(不阻塞,仅记录):
- 实体板卡自动绑定目前只有失败结论(作者记录
ready=false、UART selection failed: NOT_FOUND,属固件侧 ConOut/SerialIo 选择诊断);生产 UDP/netns 路由、非 Linux 宿主与正式 registry 发布后的构建不在本 PR 内,本环境也无法复核(docs/axloader-serial-ownership.md§4.3 亦声明为本地验收)。 - 60 秒窗口只在「没有后续有效设备报告」时到期:设备持续广播但始终没有身份帧时会持续重试,停止条件只能由操作者取消会话(§2.1 已写明刷新语义)。
- CLI 与服务端对「宿主不支持或非法的上报参数」仍不对称(CLI 立即报错退出,服务端按可恢复处理);§3.2 只列举了 CLI 的三类重试条件,属可接受且有文档的语义差异。
- 未逐行审阅的面:
ostool-server/scripts/test-axloader-local.py重写、webui 单测/e2e 增补与部分新增集成测试(已抽查关键断言有效:覆盖参数 38400 实际生效、租约移交、relay 别名排除、定位复用、双次上电参数恢复、畸形身份/空 epoch 后会话保留且 TFTP 文件仍在)。
同类 PR
repo:drivercraft/ostool is:pr axloader 只命中本 PR 与历史已合并工作(#121 UEFI HTTP Boot、#179/#194/#198 axloader 协议演进),没有重复或竞争的未合并 PR。
Powered by deepseek-flash
问题
axloader 启动前需要确认本次上电对应的实际串口。服务端此前依赖手工串口配置,且固件上报信息不足、设备重复广播或一次扫描失败时,串口租约和会话恢复边界不够清晰。
实现
serial_id、boot_epoch、就绪状态和实际 UART 参数;服务端先确认串口身份,再通过网络 binding token 放行启动。ostool-serial管理器,负责候选串口发现、继电器串口排除、定位复用、不可克隆租约移交、读取线程回收和失败后的重新绑定。serial不再要求手工填写;U-Boot 手工串口流程保持不变。boot.serial_parameters覆盖,默认保存为null。有效优先级为:Web UI 覆盖值 > 本次 axloader 上报值 > 固件无法读取 UART 参数时的115200 8N1 无流控默认值。UI 覆盖必须与实际串口输出一致。ready=false、参数缺失、参数非法、宿主不支持,以及管理器Timeout/DiscoveryFailed/Busy/IO 错误均作为可恢复绑定失败:回复具体错误、发布Recovering、刷新 60 秒设备等待窗口,等待下一次广播;空boot_epoch不写入运行态有效代次,也不进入退休代次,重启后仍会重复返回明确的非法代次错误;只有管理器停止或非法请求等不可恢复错误终止 session。ostool axloader run在 60 秒总窗口内重新读取设备状态,按每次扫描窗口重新申请绑定,支持设备就绪、身份帧迟到和 boot epoch 变化;设备缺少 UART 参数时使用协议默认值115200 8N1、无流控;continue仍只执行 direct 放行。验证
cargo fmt --allcargo test -p ostool-server --lib --target x86_64-unknown-linux-gnu -- --nocapture:167 passedcargo test -p ostool-server --test axloader_serial --target x86_64-unknown-linux-gnu -- --nocapture:1 passed;覆盖身份帧扫描超时后的 session 恢复、同 epoch 重试、畸形 serial_id/boot_epoch 拒绝(含重启后空代次重复上报)、租约状态保留和 TFTP 文件保留cargo test -p ostool-serial --target x86_64-unknown-linux-gnu -- --nocapture:6 passedcargo test -p ostool --target x86_64-unknown-linux-gnu -- --nocapture:356 passedcargo clippy -p ostool-server -p ostool --target x86_64-unknown-linux-gnu --all-features:通过;本次改动未新增 warning,仅有既存async_trait展开的double_must_use警告pnpm test -- --run:8 files / 30 tests passedpnpm build:通过PR head
95af5ad的 GitHub Actions 已完成,两个 Quality Check 均通过: