目标板:立创泰山派 目标:主线 U-Boot + rkbin 闭源二进制 + TEE 构建启动链,从零构建 Debian rootfs,调试 WiFi驱动.
一,启动链移植(主线 U-Boot + rkbin + OP-TEE)
1.1 组件来源(全部 GitHub 可查)
| 组件 | 来源 | 版本/内容 |
|---|---|---|
| U-Boot | github.com/u-boot/u-boot | tag v2026.07 + 板级 lckfb-tspi-rk3566_defconfig |
| DDR 初始化 (TPL) | github.com/rockchip-linux/rkbin | bin/rk35/rk3566_ddr_1056MHz_v1.25.bin(闭源) |
| BL31 (TF-A) | github.com/ARM-software/arm-trusted-firmware | 主线(master),PLAT=rk3568 SPD=opteed 自编 |
| OP-TEE (BL32) | github.com/rockchip-linux/rkbin → 转 ELF | rk3568_bl32_v2.16.bin(闭源)→ ELF 参与构建 |
OP-TEE 主线(optee_os)目前不支持 rk3568/rk3566,自编 TEE 不可用;ATF 主线已支持 rk3568,但必须用
SPD=opteed编译,否则不加载 OP-TEE.BL32 只能使用瑞芯微闭源固件,通过aarch64-linux-gnu-ld把 bin 转成 ELF 交给 binman 打包(方法参考 OrangePi-3B 移植 Linux —— 构建 U-Boot1).TZDRAM 固定0x08400000,与 rkbinRK3568TRUST.ini的 BL32 地址一致,保证与闭源信任链兼容.
1.2 编译命令(开源 ATF + 闭源 BL32 转 ELF)
| |
产物:idbloader.img(TPL+SPL),u-boot.itb(BL31+OP-TEE+U-Boot),u-boot-rockchip.bin(单镜像).
项目目录结构(Custom-uboot/):
Custom-uboot/
├── u-boot/ # 主线 U-Boot v2026.07(板级 defconfig + O=build 产物)
├── arm-trusted-firmware/ # ATF 主线(SPD=opteed 自编)
│ └── build/rk3568/release/bl31/bl31.elf
├── rkbin/ # 瑞芯微闭源固件
│ └── bin/rk35/
│ ├── rk3566_ddr_1056MHz_v1.25.bin # ROCKCHIP_TPL
│ └── rk3568_bl32_v2.16.elf # TEE(bin 转 ELF 后产物)
├── mainline-linux/ # 主线内核 7.2.0(Image + dtb + modules)
├── parameter.txt # 分区表(RKDevTool 烧录用)
├── boot.img # boot 分区镜像(ext4:extlinux.conf + Image + dtb)
├── rootfs.img # rootfs 分区镜像(ext4,rsync 打包产物)
└── build-rootfs.sh # Debian rootfs 构建脚本(增量 + 中科大源)相对路径的基准:在
u-boot/目录内执行 make,../指向与 u-boot 平级的arm-trusted-firmware/和rkbin/.参考:OrangePi-3B 移植 Linux —— 构建 U-Boot1.
1.3 为什么 BL32 要转 ELF:opteed 与 binman
主线 U-Boot 的 binman 靠 ELF 段信息生成 FIT 里 tee-1 的 load/entry(rockchip-u-boot.dtsi 的 @tee-SEQ 用 split-elf),直接塞 bin 无法确定加载地址.aarch64-linux-gnu-ld -b binary -Ttext=0x08400000 转出的 ELF:entry=0x8400000,u-boot.itb 中 tee-1 镜像 load=0x8400000 / entry=0x8400000,ATF opteed 把它加载到 TZDRAM(0x8400000 起,32MB),与内核 dts 的保留内存 optee@8400000(no-map)一一对应.
(历史:曾尝试自编 optee_os 的 PR #7887 rk3568 移植——64 位 core 默认同时编译 ta_arm32 库会报 -mthumb,需加 CFG_USER_TA_TARGETS=ta_arm64 绕过——但该移植最终不可用,rk3566 只能走"闭源 BL32 转 ELF"路线.)
二,U-Boot 板级问题
2.1 开机自动引导:GPT bootable 标记
bootflow scan 无条件设置 BOOTFLOWIF_ONLY_BOOTABLE(cmd/bootflow.c).GPT 中没有任何分区带 bootable 标记时,bootstd 只扫描分区 1,导致 boot 分区(分区 3)被跳过 → 无法自动引导.
解决:parameter.txt 中 boot 分区加 :bootable 标记:
CMDLINE: mtdparts=:0x00002000@0x00004000(uboot),0x00002000@0x00006000(misc),0x00020000@0x00008000(boot:bootable),…
2.2 NPU 电源域 panic
内核启动时 rockchip-pm-domain ... failed to get ack on domain 'npu' → panic_on_set_idle panic.
根因:Linux power-domain 驱动不知道 NPU 域的供电 regulator,而 U-Boot 未初始化 vdd_npu → NPU 上电失败.
解决(Armbian 官方已合并方案,armbian/build PR #7025,作者 Jonas Karlman):在板级 -u-boot.dtsi 给 vdd_npu 加:
| |
2.3 OP-TEE 与 kernel_comp_addr_r 内存冲突
OP-TEE 布局:TZDRAM 0x08400000(32MB)+ SHMEM 0x0a400000(4MB),安全区到 0x0a800000.
主线 U-Boot rk3568_common.h 默认 kernel_comp_addr_r=0x0a000000(128MB),与安全区重叠.且 PR #7887 用 DDR 防火墙保护 TZDRAM,普通世界写入会 SError.
解决(2026-08-26 更新:改用 U-Boot 后期添加的 OPTEE 配置,不再覆盖板级 env):
不再改 ENV_MEM_LAYOUT_SETTINGS 板级 env,改为在 U-Boot 里启用官方 OPTEE 支持配置,让 u-boot.itb 直接打包 OP-TEE(BL32)由 ATF opteed 加载,并把 optee 节点/保留内存同步给内核(git diff 可查实际改动):
defconfig 添加(
configs/lckfb-tspi-rk3566_defconfig):CONFIG_OPTEE_LIB=y CONFIG_OPTEE_IMAGE=y CONFIG_OPTEE_TZDRAM_SIZE=0x02000000构建时设
TEE=<bl32 转出的 ELF>环境变量(rkbin 闭源rk3568_bl32_v2.16.bin转 ELF,方法见 1.2),binman 自动把tee-1镜像打进 u-boot.itb:load=0x8400000 / entry=0x8400000,与 rkbinRK3568TRUST.ini的 BL32 地址一致;设备树添加 optee 节点与保留内存(
dts/upstream/src/arm64/rockchip/rk3566-lckfb-tspi.dts,U-Boot 与内核各一份):1 2 3 4 5 6 7 8 9 10 11 12 13firmware { optee { compatible = "linaro,optee-tz"; method = "smc"; }; }; reserved-memory { optee@8400000 { reg = <0x0 0x8400000 0x0 0x1000000>; no-map; }; };U-Boot 的
lib/optee/optee.c(optee_copy_fdt_nodes,CONFIG_OPTEE_LIB)启动时把/firmware/optee与 reserved-memory 拷入内核 DT,内核CONFIG_OPTEE=y驱动直接探测;内核侧无需额外启动参数(
optee.arm_smccc_version仅在 SMC 版本协商异常时兜底).
三,Debian rootfs 构建
本节内容与项目
build-rootfs.sh一致:基础版本 Debian 13 (trixie),镜像源 中科大 USTC,支持增量构建(已存在 rootfs/ 时不清空,不重新 debootstrap,直接原地 apt 更新 + 覆盖配置).
3.1 debootstrap(arm64 + qemu-user-static + 国内镜像)
| |
chroot 内继续 apt 安装其余包.脚本里 apt 源统一切为中科大:deb822 格式(debian.sources)改 URIs:,deb 格式(sources.list)改 URL,并保留 non-free-firmware 组件(WiFi 固件包在此组件下);切 https 源前先装 ca-certificates,否则证书校验失败.
3.2 brcm-firmware 配置(重点)
WiFi/BT 模组 AP6212A(BCM43430A1)需要三样东西:
WiFi 固件:
firmware-brcm80211包(位于non-free-firmware组件)提供brcmfmac43430-sdio.bin(软链接到../cypress/cyfmac43430-sdio.bin)和一堆板级 nvram,其中就包含 AP6212 专用的brcmfmac43430-sdio.AP6212.txt;WiFi nvram 软连接(最容易漏的一步):brcmfmac 驱动只请求固定文件名
brcmfmac43430-sdio.txt,而包内只有板级命名brcmfmac43430-sdio.AP6212.txt,必须建软连接,否则报Direct firmware load for brcm/brcmfmac43430-sdio.txt failed with error -2:1ln -s brcmfmac43430-sdio.AP6212.txt /lib/firmware/brcm/brcmfmac43430-sdio.txt为什么要用软连接而不是拷贝:Debian 固件包自身就是这么组织的(包内
brcmfmac43430-sdio.bin,各板级.txt之间大量用符号链接,例如brcmfmac43430-sdio.sinovoip,bpi-m2-plus.txt -> brcmfmac43430-sdio.AP6212.txt).软连接在固件包升级时自动指向新版本,不会残留旧文件,也不会出现两份 AP6212 nvram 内容不一致的问题.蓝牙固件:
bluez-firmware包的BCM43430A1.hcd(30KB)与 AP6212A1 芯片补丁不兼容,会报command 0xfc18 tx timeout,必须用瑞芯微 SDK 原配 48KB 版本(rkwifibt/AP6212A1/bt/BCM4343A1.hcd)覆盖到/lib/firmware/brcm/BCM43430A1.hcd.
3.3 打包 ext4 镜像
| |
/proc,/sys,/dev,/run,/tmp 是内核启动时才挂载的虚拟文件系统,不入镜像(保留空目录即可),否则体积虚增且拷入的是运行时垃圾. 坑:
sudo -S管道传密码时,若凭据已缓存,密码行会污染 stdin 写入文件内容,应改为"先写临时文件再sudo cp".
3.4 linaro 镜像 emergency mode 教训
linaro 预置镜像的 /etc/fstab 残留 PARTLABEL=oem,PARTLABEL=userdata 挂载项,删掉这两个分区后 systemd 挂载失败进 emergency mode.自建 rootfs 时 fstab 只保留 / 和 /tmp 即可.
四,WiFi 调试(bcmdhd / AP6212)
说明:本章为使用 rkSDK(bcmdhd 驱动)时的排障记录;当前主线方案改用
brcmfmac驱动(BCM43430A1 / AP6212A 模组),其中 4.3 的 MAC 地址问题只在 rkSDK 的 bcmdhd 下出现,brcmfmac 无"拒绝本地管理位 MAC"的行为.
4.1 驱动选型
rkSDK 的 bcmdhd 有 in-tree 与 external 两份源码,diff 0 行完全一致,仅编译宏不同:
| 版本 | 关键宏 | 固件加载 |
|---|---|---|
in-tree(CONFIG_AP6XXX=m) | GET_OTP_MAC_ENABLE,DHD_REQUEST_FW_PATH | request_firmware() → /lib/firmware/ |
external(SDK 00-wifibt.sh 独立编译) | 无上述宏 | filp_open 支持绝对路径 → /vendor/etc/firmware/ |
两者都有 SDIO modalias,modprobe 均可自动加载.
4.2 固件路径的坑
in-tree 版用 request_firmware(),内核 6.1 没有绝对路径特判(drivers/base/firmware_loader/main.c 无条件 snprintf("%s/%s", ...)):驱动请求 /fw_bcm43438a1.bin 会被拼成 /lib/firmware//fw_bcm43438a1.bin.所以固件必须放 /lib/firmware/,否则报错:
| |
4.3 MAC 地址问题(仅 rkSDK/bcmdhd)
主线 brcmfmac 无"拒绝本地管理位 MAC"的行为,此坑只属于 bcmdhd.
现象:固件/NVRAM 加载成功,但 preinit 设 MAC 失败,随后驱动反复重启:
| |
原因:bcmdhd 固件拒绝任何带"本地管理位"的 MAC(BCME_BADADDR=-21);udev 的 MACAddressPolicy=persistent 与 NetworkManager 默认的 scan-rand-mac-address=yes 都会给 wlan0 赋随机 MAC,驱动下次 dhd_open 硬 SET 被拒 → NM 反复重试 → 无限循环重启.
解决:让系统完全不碰 wlan0 的 MAC,preinit 直接用固件 OTP MAC:
/etc/systemd/network/99-wlan0-nomac.link(systemd.link(5)):1 2 3 4 5[Match] OriginalName=wlan0 [Link] MACAddressPolicy=none/etc/NetworkManager/conf.d/wifi-mac.conf(NetworkManager.conf(5)):1 2 3 4 5 6[connection] wifi.cloned-mac-address=preserve [device] match-device=interface-name:wlan0 wifi.scan-rand-mac-address=no
坑:
match-device=driver:bcmdhd匹配不到(SDIO 驱动名是bcmsdh_sdmmc而非bcmdhd),必须用match-device=interface-name:wlan0;wifi.scan-rand-mac-address是 device 段属性,放[connection]段会报unknown key.
4.4 加载方式与蓝牙(简述)
- 加载:SDK 官方用
wifibt-init.service早期 insmod(do_insmod幂等);标准方式为 modprobe 自动加载(SDIO modalias02d0:a9a6).教训:NetworkManager 若静态启用且无 MAC 保护,会反复重启驱动(buildroot 官方镜像里 NM 仅 D-Bus 激活). - 蓝牙:
brcm_patchram_plus1走 UART 加载/lib/firmware/BCM4343A1.hcd;缺该工具时 wifibt-init 的 BT guardian 会每 1.5s 反复 rfkill 复位.测试:装bluez后bluetoothctl(list看 hci0,scan on扫设备).
五,烧录(RKDevTool / rkdeveloptool)
分区布局以本项目 parameter.txt 为准(LBA 以 512B 扇区计),CMDLINE:
mtdparts=rk29xxnand;mtdparts=:0x00003fc0@0x00000040(idbloader),0x00002000@0x00004000(uboot),0x00020000@0x00008000(boot:bootable),0x00c00000@0x00028000(rootfs:grow)
注意:parameter.txt 中实际为单行,此处以普通文本展示,由浏览器自动折行;分区之间用逗号分隔(
(boot:bootable)后必须是逗号,不能用分号/无分隔).
| 分区 | 偏移 | 大小 | 镜像 |
|---|---|---|---|
| idbloader | 0x40 | 0x3FC0 (8160K) | idbloader.img |
| uboot | 0x4000 | 0x2000 (4M) | u-boot.itb |
| boot | 0x8000 | 0x20000 (64M) | boot.img(ext4,extlinux.conf + Image + dtb,bootable) |
| rootfs | 0x28000 | 0xC00000(grow) | rootfs.img |
注意:Rockchip 工具按 CMDLINE 生成 GPT,每个分区必须带
@偏移,缺偏移的分区会被直接丢弃(曾导致 rootfs 分区丢失,part list只剩 3 个分区).分区表用mtdparts=rk29xxnand;前缀声明 mtd-id.
- LOADER 模式:U-Boot 下
rockusb 0 mmc 0(需dr_mode=peripheral修复),或按住烧录键上电 - MaskROM 的 loader 文件:
rkbin/tools/boot_merger RKBOOT/RK3566MINIALL.ini合成
六,经验总结
- 闭源驱动(bcmdhd)与标准 Linux 工具链的兼容性差:固件路径(request_firmware 无绝对路径特判)和 MAC(拒绝本地管理位)两个坑都是 bcmdhd 特有问题,需要配置层绕过.
- NetworkManager 的 MAC 随机化策略是"看不见的坑":默认
scan-rand-mac-address=yes,配合 vendor 驱动极易触发反复重启. - in-tree 与 external 驱动源码同源:先确认编译宏差异,不要盲目换驱动.
- SDK 官方镜像是最可靠的参考:buildroot 官方用
00-wifibt.sh安装驱动/固件/服务,NM 仅 D-Bus 激活——复刻其服务布局是解决 WiFi 问题的关键线索. - RK3566 主线支持尚不完整:OP-TEE 无主线支持(用"闭源 BL32 转 ELF"方案绕过,参考 6xyun 文章1),U-Boot 缺 vdd_npu 初始化,rockusb otg 模式不可用,都需要板级补丁或配置绕过.
OrangePi-3B 移植 Linux —— 构建 U-Boot.该文章采用 BY-NC-ND 许可协议,本文仅引用其方法并注明出处. ↩︎ ↩︎ ↩︎