Skip to content

K3 → StarryOS 一键自动启动闭环 ​

这一阶段主要解决:

现在 K3 启动 StarryOS 的步骤太多,能不能把人工流程自动化。

前一阶段已经确认,K3 不需要先启动 Bianbu Linux,也不需要 warm reboot,严格冷启动就可以直接走 Debug Golden Flow 进入 StarryOS。

也就是说,底层启动链本身已经跑通,接下来要解决的就是:

怎么把这条已经验证过的人工流程,变成真正的一键自动启动流程。

1. 原来的人工启动到底麻烦在哪 ​

之前虽然已经有 boot_golden_flow.sh,但它更像是一个“命令封装脚本”。

很多关键步骤仍然需要人工完成,比如:

text
观察 BootROM 是否进入 Recovery
↓
手动确认 version-brom
↓
上传 FSBL
↓
看串口确认 SPL 是否启动
↓
等待 fastboot 重新枚举
↓
上传 OpenSBI + U-Boot FIT
↓
看 Full U-Boot 是否启动
↓
人工输入 scsi scan
↓
确认 KINGSTON
↓
人工进入 kernel fastboot
↓
上传 StarryOS kernel
↓
人工进入 DTB fastboot
↓
上传 DTB
↓
人工输入 booti
↓
看 Welcome to Starry OS!

也就是说,虽然 Host 侧的一部分命令已经写进脚本,但真正决定“下一步能不能走”的判断,还是人在做。

这类脚本最大的问题是:

它自动执行了命令,但没有真正理解板子当前处于什么状态。

所以这一阶段的核心,不是单纯少输几条命令,而是把整个流程改造成一个真正的状态机。

2. 什么叫状态驱动启动 ​

这次自动化最重要的思想是:

只有看到板子真的进入下一个状态,脚本才执行下一步。

比如不能写成:

text
上传 FSBL
sleep 5 秒
继续下一步

因为不同启动阶段耗时可能变化,USB 重新枚举速度也不固定。

更可靠的方式是:

text
上传 FSBL
↓
等待 UART 出现:
U-Boot SPL 2022.10-dirty
Debug JTAG enabled on MMC1 pins
↓
等待 SPL fastboot gadget 真正重新枚举
↓
执行 getvar version
↓
确认 version: 0.4
↓
才进入下一阶段

所以现在整个自动化流程被拆成了:

text
BOOTROM_READY
↓
SPL_READY
↓
FULL_UBOOT_READY
↓
UFS_READY
↓
KERNEL_LOADED
↓
DTB_LOADED
↓
STARRYOS_READY

这样每一步都有明确的“成功判据”。

3. 第一次真板测试:BootROM serial 出问题 ​

自动化脚本第一次真板运行时,在最开始就停住了。

脚本一直显示:

text
< waiting for ???????????? >

手工检查:

text
fastboot devices

结果是:

text
????????????    DFU download

但是:

text
fastboot getvar version-brom

又可以正常返回:

text
version-brom: 1.0

这说明 K3 的 BootROM Recovery 阶段没有一个正常可用的 fastboot serial。

脚本第一版错误地认为:

text
只要找到 fastboot device
→ 就一定有可靠 serial
→ fastboot -s SERIAL ...

于是实际变成了:

text
fastboot -s ???????????? ...

当然就找不到设备。

后来把逻辑改成:

text
如果只有一个 fastboot device:

正常 serial
→ 使用 -s SERIAL

serial 是 ????????????
→ 不使用 -s
→ 直接 fastboot getvar / stage / continue

而且 BootROM 是否 ready,不再依赖 serial,而是依赖真正的:

text
version-brom: 1.0

第二次真板测试以后:

text
[STATE] BOOTROM_READY

成功出现。

说明第一个真实环境问题解决了。

4. 第二次真板测试:Full U-Boot 中间还藏了一层 fastboot ​

继续往后跑以后:

text
BOOTROM_READY
✅

SPL_READY
✅

combined FIT 也正常上传:

text
OpenSBI + Full U-Boot

然后串口已经明确出现:

text
U-Boot 2022.10-dirty
CPU: rv64imafdcvh
Model: spacemit k3 com260 ifx board

说明 Full U-Boot 已经启动了。

但是脚本一直等:

text
=>

等不到。

后来重新对照人工流程才发现,中间实际上还漏了一层:

text
SPL fastboot
↓
上传 combined FIT
↓
OpenSBI
↓
Full U-Boot 启动
↓
Full U-Boot 自己再次进入 fastboot
↓
Host 需要再执行一次 fastboot continue
↓
才会真正进入 U-Boot shell

第一版状态机误写成:

text
Full U-Boot banner
↓
直接等待 =>

所以第二次真板测试实际帮我们发现了一个以前人工操作时没有特别意识到的隐藏启动阶段。

后来改成:

text
SPL_READY
↓
stage combined FIT
↓
SPL fastboot continue
↓
等待 Full U-Boot banner
↓
等待 Full U-Boot fastboot gadget
↓
实际 getvar 探测 ready
↓
Full U-Boot fastboot continue
↓
等待新的 =>
↓
FULL_UBOOT_READY

而且两个 continue 都只允许执行一次,不能为了重试而盲目重复。

5. 又发现一个很有意思的人工隐含动作:5 秒 autoboot ​

第三次运行时又发现了一个以前人工操作时很容易忽略的细节。

Full U-Boot 启动以后,会出现类似:

text
Hit any key to stop autoboot: 5

如果这时候什么都不做,U-Boot 就会继续执行默认启动路径,很可能又走到官方系统。

人工调试的时候,我们一般会随手按一个键,所以以前并没有特别意识到:

“按键打断 autoboot”本身其实也是 Golden Flow 的一部分。

自动化以后,这个动作必须明确写出来。

所以脚本现在会在检测到 autoboot 倒计时以后,自动发送一个字符:

text
s

这里的 s 没有特殊命令含义,只是相当于:

text
按下任意键

作用是:

text
打断默认 autoboot
↓
进入 U-Boot shell
↓
出现 =>

这也是这次自动化过程中很有代表性的一个发现:

人工流程里很多“下意识完成”的动作,到了自动化阶段都必须显式建模。

6. UFS 也改成自动判断 ​

进入 U-Boot shell 以后,脚本会自动发送:

text
scsi scan

然后不是等几秒就继续,而是必须看到:

text
KINGSTON

才能认为:

text
[STATE] UFS_READY

这一步的作用就是确认:

text
UFS 控制器初始化成功
↓
真实存储设备已经枚举出来
↓
后面的 StarryOS rootfs 路径才有意义

所以现在不是“命令执行过了就算成功”,而是:

必须看到真实设备结果才进入下一状态。

7. kernel 和 DTB 也全部自动加载 ​

UFS ready 之后,脚本会自动进入 kernel fastboot:

text
fastboot -l 0x140000000 -s 0x02000000 usb 0

然后 Host 自动上传:

text
starryos_host_dwarf_release.bin

对应地址:

text
0x140000000

上传完成以后:

text
[KERNEL_LOADED]

然后继续进入 DTB fastboot:

text
fastboot -l 0x138000000 -s 0x00800000 usb 0

自动上传:

text
spacemit-k3-com260-ifx.dtb

对应地址:

text
0x138000000

完成后:

text
[STATE] DTB_LOADED

整个过程已经不需要人工切换终端、复制路径或者手动执行 fastboot stage。

8. 最终自动执行 booti ​

kernel 和 DTB 都加载完成以后,脚本自动发送:

text
booti 0x140000000 - 0x138000000

然后等待:

text
Welcome to Starry OS!

最终经过多轮真板验证,StarryOS 可以稳定启动,并由状态机正确识别 Welcome to Starry OS!。

也就是说,现在实际流程已经变成:

text
人工:
完全断电
↓
FORCE_RECOVERY
↓
上电
↓
接 USB / UART

然后只执行:

./boot_golden_flow.sh

脚本自动完成:

BootROM Recovery
↓
Debug FSBL
↓
SPL
↓
OpenSBI
↓
Full U-Boot
↓
打断 autoboot
↓
U-Boot shell
↓
scsi scan
↓
KINGSTON
↓
加载 StarryOS kernel
↓
加载 DTB
↓
booti
↓
Welcome to Starry OS!
↓
STARRYOS_READY
↓
释放 Python 对 /dev/ttyUSB0 的占用
↓
sudo minicom -o -D /dev/ttyUSB0 -b 115200
↓
minicom 接管长期串口观察

启动状态机只负责“把系统送到 STARRYOS_READY”,长期串口交互交给成熟的 minicom 工具,而不是在 Python 中重复实现一个串口终端。

这次已经真正实现:

K3 → StarryOS 软件启动流程一键闭环。

9. 最后一个自动化问题:终端看到 Welcome,状态机却识别不到 ​

真板运行时曾出现一个比较隐蔽的问题。

终端已经显示:

text
Welcome to Starry OS!

但是状态机仍然等待,最终 180 秒超时。

加入 raw UART 诊断以后发现,实际串口字节并不是简单的纯文本,而是包含 ANSI 颜色控制码:

text
Welcome to \x1b[96m\x1b[1mStarry OS\x1b[0m!

也就是说:

text
人眼看到的文本
≠
程序实际接收到的字节流

串口输出可能包含 ANSI 颜色控制码,肉眼显示正常,但直接字符串匹配会失败。最终将 UART 原始数据用于日志保存和终端显示,同时建立 ANSI 清洗后的分析流,所有状态判据统一基于清洗后的文本。

最终处理方式是:

text
原始 UART
→ 原样保存 raw log
→ 原样输出到终端

同时:

text
原始 UART
→ 去除 ANSI 控制序列
→ 状态机文本分析
→ Welcome / KINGSTON / U-Boot banner / => 等统一匹配

这样既保留了完整现场日志,又避免颜色控制字符干扰自动状态判断。

检测到 Welcome 后:

text
STARRYOS_READY
↓
释放 UART fd / flock / TIOCEXCL
↓
exec sudo minicom -o -D /dev/ttyUSB0 -b 115200

这里释放的是 Python 持有的串口文件描述符、文件锁和串口独占状态,让 minicom 能够接手同一个串口。后续长期串口交互由 minicom 接管。

10. 这次自动化真正解决了什么 ​

这一阶段不只是写了一个脚本。

真正解决的是以前人工流程里面几个容易出问题的地方:

text
BootROM fastboot serial 不可靠
↓
兼容无 serial 场景

USB gadget 会多次重新枚举
↓
不再依赖固定 sleep

Full U-Boot 中间还有一次 fastboot
↓
补齐真实 boot stage

U-Boot 有 5 秒 autoboot
↓
自动打断并接管 shell

旧 UART prompt 可能误判
↓
使用新的串口观察边界

continue 具有状态改变副作用
↓
不允许盲目 retry

UFS 是否成功
↓
必须看到 KINGSTON

StarryOS 是否启动
↓
在 ANSI 清洗后的文本中匹配 Welcome to Starry OS!

启动完成后的长期串口交互
↓
释放串口占用,自动交给 minicom

所以现在这套启动器已经不再只是:

text
自动执行一串命令

而是:

根据板子真实运行状态推进的启动状态机。

11. 当前阶段结论 ​

老师之前提出两个问题:

第一:

K3 能不能不经过 Bianbu,真正 cold boot 直接进入 StarryOS。

已经验证:

text
严格 cold
→ BootROM
→ Debug FSBL
→ OpenSBI
→ Full U-Boot
→ UFS
→ StarryOS

成功。

第二:

现在的人工启动流程能不能自动化。

目前也已经验证:

text
./boot_golden_flow.sh

可以自动完成整个软件启动链,正确识别 StarryOS 启动完成,并自动交接给 minicom:

text
./boot_golden_flow.sh
↓
StarryOS
↓
STARRYOS_READY
↓
释放 Python 串口占用
↓
minicom 接管长期串口交互

所以到目前为止:

text
K3 cold direct StarryOS
✅

K3 → StarryOS 一键自动启动
✅

ANSI 清洗后的启动状态识别
✅

启动完成后自动交接 minicom
✅

这一阶段最大的收获其实是:把原来人工调试过程中隐含的判断、等待、交互和异常条件,真正整理成了一套可以自动执行的状态机。