news 2026/9/22 2:45:15

瑞芯微SDK镜像生成与烧录全流程解析:从U-Boot到update.img

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
瑞芯微SDK镜像生成与烧录全流程解析:从U-Boot到update.img

做嵌入式开发这些年,瑞芯微平台一直是我手里的主力,从RK3288、RK3399到现在的RK3568、RK3588,前前后后折腾过不少板子。很多人拿到瑞芯微官方SDK之后,第一步不是看代码,而是想搞清楚一件事:这一堆源码到底怎么变成能烧进板子里的镜像。这个问题听起来挺基础,但真做起来会发现,里面牵扯到U-Boot、Kernel、Rootfs三套完全不同的构建体系,还夹着Loader模式、parameter分区表、update.img整包制作这些概念。这篇文章就把瑞芯微SDK里镜像生成与烧录这条链路完整拆开,讲清楚每个镜像从哪里来、怎么编出来、又该怎么烧进去。

文章面向的是正在用或准备用瑞芯微平台做产品的开发者,不管你是做BSP、驱动还是整机集成,只要需要自己编译SDK并且把固件刷到板子上,这篇文章都能给你一条很明确的主线。我会尽量按实际操作顺序来讲,顺便把那些容易踩的坑标出来,省得大家再走一遍我走过的弯路。

1. 先搞清楚SDK目录与镜像产物的对应关系

1.1 SDK顶层结构与编译入口

瑞芯微官方发布的Linux SDK,拿到手解压之后第一眼会觉得很乱,目录很多,但你只要抓住几个关键路径,心里就有底了。顶层目录里最重要的几个分别是u-bootkernelbuildrootdebianappdevicedocs,另外还有几个脚本文件build.shmkimage.shenvsetup.sh

build.sh是整个SDK的编译入口,很多刚接触的人会直接执行./build.sh,结果发现在没有指定参数的情况下它会把U-Boot、Kernel、Rootfs从头到尾编一遍,耗时极长。实际上日常开发中更多用的是./build.sh uboot./build.sh kernel./build.sh rootfs这样带子命令的编译,分区编译完再统一打包。

device/rockchip目录下存放的是不同芯片平台和板型的配置文件,比如rk3568rk3588rv1126这些子目录,里面又有BoardConfig*.mkRockchip*.mk两种文件。前者定义了板级配置,比如RK_UBOOT_DEFCONFIGRK_KERNEL_DEFCONFIGRK_ROOTFS_TYPE等变量;后者更像是引用关系,告诉构建脚本在当前平台上该用哪份配置。

mkimage.sh是最后一环的打包脚本,它会把前面编译出来的U-Boot、Kernel、Rootfs各自处理成最终固件格式,然后存放到rockdev/目录。这个脚本就是“镜像生成”的核心,后面我会专门讲它的执行逻辑。

1.2 一次完整编译会生成哪些镜像

在瑞芯微平台上一套完整的固件通常不是单一文件,而是按分区拆开的多个镜像。编译完之后,rockdev/目录下你会看到这些文件:

镜像文件对应分区主要来源作用
uboot.imgubootU-Boot编译产物U-Boot主体
trust.imgtrust安全启动相关(ATF、OP-TEE)启动信任链与运行时可执行环境
boot.imgbootKernel、dtb、ramdiskLinux内核启动镜像
rootfs.imgrootfsBuildroot/Debian根文件系统
oem.imgoem板厂私有分区预装应用或无关紧要数据
userdata.imguserdata初始数据用户数据分区
parameter.txt分区表配置文件定义Flash分区布局
update.img整包上述所有镜像打包用于整包升级

这里最容易搞混的是uboot.imgtrust.img的区别。在瑞芯微新平台的启动流程中,芯片上电后先运行BootROM,BootROM加载的是U-Boot SPL或者DDR初始化代码,而trust.img装的是ARM可信固件ATF和OP-TEE,它负责在U-Boot之前或与U-Boot配合完成DDR初始化、切换到安全世界、拉起U-Boot主程序。所以单独烧uboot.img而不烧trust.img,板子大概率起不来,这两者必须配套。

parameter.txt也容易被忽略,它虽然不是可执行镜像,但烧录时必须先写进去,因为升级工具要靠它来识别每个分区在Flash中的偏移和大小。如果分区间错位了,轻则文件系统挂不上,重则直接变砖返厂。

2. 从U-Boot到Kernel:两个Boot阶段的分工与编译细节

2.1 U-Boot侧到底在干什么

U-Boot在瑞芯微平台上承担的任务比一般嵌入式Linux项目要重一些。它不只是引导内核,还要负责DDR初始化、时钟初始化、存储设备驱动加载、显示初始化、启动logo显示等。这也是为什么RK平台的U-Boot编译出来会有多个二进制文件,而且它们的组织方式跟主线U-Boot不太一样。

在SDK里编译U-Boot其实特别简单,执行./build.sh uboot,脚本会自动读取BoardConfig*.mk里指定的RK_UBOOT_DEFCONFIG,然后进入u-boot/目录用这个配置去做编译。你在命令行里看到它执行了make rk3568_defconfig之类的动作,实际上就是在用平台默认配置生成.config,接着就是常规的make -j16

编译结束之后,u-boot/目录里会得到很多文件,但最关键的是uboot.imgtrust.img。这两个镜像不是U-Boot直接编出来的,而是通过瑞芯微提供的打包工具,把U-Boot二进制、ATF、DDR初始化代码、Miniloader等部件组合到一起。所以你如果在u-boot/目录里找不到一个叫作uboot.img的文件,别着急,它生成在rockdev/目录下。

有一个实际开发中经常遇到的需求是给U-Boot阶段加上开机动画或者自定义logo。瑞芯微平台在这方面做得比较友好,它支持在U-Boot阶段就显示logo,然后无缝过渡到Kernel,减少切换闪烁。具体做法是在U-Boot里配置CONFIG_LOGO相关选项,然后把图片放到u-boot/logo/目录下,注意图片格式和分辨率要匹配,不然编译能过但显示不出来。

编译U-Boot时最典型的坑是交叉编译工具链问题。SDK通常会自带一套工具链,放在prebuilts/gcc/linux-x86/目录下,但如果你用了自己安装的交叉编译器,版本太新反而会报错。我实测过用GCC 9、GCC 8编译RK3568的U-Boot都能过,但用GCC 10以上就会出现某些结构体对齐相关的警告,个别的还会直接编译失败。所以能不动SDK自带的工具链就尽量别动。

2.2 Kernel侧的关键产物与dtb匹配

Kernel的编译用的是./build.sh kernel,这个命令做的事情就是读取RK_KERNEL_DEFCONFIG对应的配置,进入kernel/目录执行编译,然后把boot.img生成出来。如果你只是改了驱动源码而没动配置,也可以用./build.sh kernel直接把boot.img编出来,不需要重新配置内核。

这里有个关键点很多人没注意:boot.img里面包含的不只是ImagezImage,还有一个叫resource.img的概念。在比较旧的SDK版本里,resource.img包含了多个dtb文件和logo图片,而在新版本SDK里,dtb文件已经直接打到boot.img里面去了。所以你在检查Kernel编译产物时,不要光找kernel.img,要看最终生成的boot.img是否包含了正确的dtb。

dtb匹配问题是我见过最多的启动失败原因。Kernel编译默认会把该平台所有dts都编译成dtb,然后在U-Boot阶段通过读取分区或环境变量来选择加载哪一个。如果你板子的网口、屏幕或者SD卡驱动不对,先不要怀疑内核驱动代码,先去确认boot.img里的dtb是不是跟你的板子匹配。最简单的确认方法是解包boot.img,或者用mkimage工具查看dtb列表。

瑞芯微SDK里Kernel还有一个特殊目录叫kernel/arch/arm64/boot/dts/rockchip/,板型对应的dts文件都放在这里。如果你自己画了板子,最常规的做法是复制一份官方相近板型的dts,比如rk3568-evb.dts,改好之后在Makefile里添加编译目标。路径和依赖关系只要加对,./build.sh kernel就能自动把它编成dtb并打包到boot.img。

内核启动参数中console=ttyFIQ0是瑞芯微平台的默认配置,很多人想用ttyS0或者ttyAMA0,改了内核cmdline之后发现串口没输出。原因在于瑞芯微使用了FIQ debugger机制,串口输出经过FIQ处理,直接指定普通串口设备不一定能拿到打印信息。如果你只是想看完整启动日志,不要折腾参数,直接用SDK默认的console配置就行。

3. Rootfs的组装与update.img打包流程

3.1 Rootfs的两种主流方案

Rootfs在瑞芯微SDK中有两种主流选择:Buildroot和Debian。Buildroot适合做体积小、功能固定的量产镜像,SDK里通过buildroot/目录维护;Debian适合做功能丰富、方便调试的开发镜像,SDK里通过debian/目录维护。

BoardConfig*.mk里用RK_ROOTFS_TYPE来指定用哪一个。常见写法有RK_ROOTFS_TYPE=buildrootRK_ROOTFS_TYPE=debian。如果你改了这个变量,最好执行一下./build.sh cleanall再重新编译,因为两种Rootfs的构建系统差异太大,共存时的残留文件会导致一些奇怪的编译报错。

Buildroot的构建过程是先把工具链、busybox、第三方库全部编译一遍,然后打包成rootfs镜像。第一次执行./build.sh rootfs时会非常久,我试过在8核16线程的机器上编一个功能完整的Buildroot也要一个多小时。这里要注意磁盘空间,Buildroot构建过程会产生大量临时文件,buildroot/output/目录轻松吃掉几十GB。建议给SDK所在的磁盘预留至少100GB空闲空间,否则编到一半磁盘满了会出各种令人崩溃的诡异问题。

Debian的Rootfs思路不一样,它不是从源码编译整个系统,而是用debootstrap从一个基础软件包集合开始,安装一个最小的Debian根文件系统,然后根据需要装上各种应用。这种方式在开发调试阶段更省心,因为直接用apt就能装软件。但是要注意,SDK里带的Debian根文件系统是预置好的,你没法直接往里面加apt源之外的包,想改就得用chroot进rootfs手动操作。

我先说Buildroot,因为它更容易出现“表面成功编译但实际跑不起来”的情况。很多人改完Buildroot配置,重新编译rootfs后发现rootfs.img体积一点没变,烧进板子也看不到自己的改动,原因就很可能是没执行./build.sh cleanall而只执行了./build.sh rootfs。Buildroot有缓存机制,某些配置项改动后不会触发对应软件包的重编,所以需要先把旧的编译产物清掉再重新编。

3.2 mkimage.sh与update.img的生成逻辑

编译完各个分区的镜像之后,最后一步就是打包update.img。这个过程由mkimage.sh控制,它在SDK顶层目录下执行,把rockdev/目录里各个分区镜像以及parameter分区表都打包成一个统一固件文件。

mkimage.sh的核心动作其实就两件事:一是根据parameter.txt把各个镜像放到正确的偏移位置,二是生成一个带校验信息的升级头,让烧录工具能识别并校验。瑞芯微的升级工具在烧录update.img时会先解析升级头,然后按里面的分区信息逐个烧录。

很多人会有个疑问:既然有了单独的uboot.imgboot.imgrootfs.img,为什么还要打一个update.img?原因有两方面,一方面update.img里包含完整的分区表和校验信息,用RKDevTool的“升级固件”功能一键烧录,不用手动选择每个分区文件;另一方面update.img在生产烧录和售后升级时更方便,一个文件搞定所有分区,不容易漏掉或选错。

在实际量产时,我通常不会让产线用update.img整包升级,而是用一个叫upgrade_tool的命令行工具配合单独的镜像文件做分区烧录。这样可以省去整包校验和解析的时间,也更方便定制烧录流程。比如只烧uboot.imgtrust.img来更新Bootloader,或者只烧boot.img来更新内核,不用每次都带一个几百MB的rootfs。

这里要注意一个细节:update.img的打包参数RK_UPDATE_INIRockchip*.mk里配置,默认指向device/rockchip/rk3568/rockimg/rk3568.ini。如果你自己加了一个分区,就得同步修改这个ini文件,把分区镜像路径加进去,否则打包出来的update.img会缺少这个分区,烧录后系统依然能启动,但那个分区的数据是空的。

打包完成的update.imgrockdev/目录下,它的体积通常跟rootfs大小直接相关。Debug版本的Debian rootfs解压后可能超过2GB,整包固件也会跟着膨胀。如果发现update.img体积异常大,先检查rootfs是否包含了一些不该有的日志缓存和临时文件。

4. 烧录实操:从Loader模式到升级工具使用

4.1 进入Loader模式的几种方法

瑞芯微芯片内部有一个BootROM程序,它上电会检测USB或者SD卡设备,如果外部触发条件满足就进入下载模式。这个模式在RKDevTool里显示为“Loader模式”,是烧录的前提。

进入Loader模式最可靠的方法是按住板子上的RECOVERY键(有些板子叫UPDATE或MASKROM键)再上电。注意动作顺序很关键:先把RECOVERY键按住不松,然后再接USB线和电源。上电之后BootROM检测到RECOVERY引脚被拉低,就会停留在下载模式等待PC端工具连接。如果你的板子没有实体按键,也可以飞线短接对应的测试点,原理一样。

还有一种是软件层面的方式,就是在已经能启动的U-Boot或Linux里执行reboot loader命令。这条命令会让芯片直接进入Loader模式,适合远程维护场景。不过要注意,U-Boot环境里执行reboot loader和Linux系统里执行reboot loader,最终效果都是一样的,但前提是内核或U-Boot里对应驱动没有被动过。

我遇到过不少烧录失败的情况,最后排查出来都是USB连接问题。瑞芯微Loader模式依赖于USB枚举,PC端必须识别到一个Rockchip设备才算成功进入Loader模式。如果你插上USB线后设备管理器里没有任何变化,先确认RECOVERY引脚是否真的拉低了,再检查USB线是否支持数据通信,这个坑特别常见:很多USB线只能充电,插上去完全没反应。

4.2 RKDevTool与upgrade_tool两种烧录方式

PC端烧录工具常用的有两款:Windows下面用RKDevTool,Linux环境下用upgrade_tool。Windows版RKDevTool会把驱动和工具一起装好,图形界面操作直观,适合开发阶段单板调试。Linux下则更依赖命令行,适合产线和自动化集成。

RKDevTool的使用逻辑很简单。连接板子进入Loader模式后,工具界面上会显示一个“发现一个Loader设备”。此时有两个选择:如果你有update.img整包,就直接切到“升级固件”页,点击“升级固件”按钮选择文件,然后点“升级”;如果你只想单独刷某个分区,就切到“分区表”页,对照parameter的分区名,分别给每个分区指定镜像路径,再点“执行”。

upgrade_tool命令行工具在Linux下的用法更直接:

# 查看当前识别到的设备 sudo upgrade_tool ld # 烧录整包固件 sudo upgrade_tool uf update.img # 单独烧录分区镜像 sudo upgrade_tool di -b boot.img sudo upgrade_tool di -u uboot.img sudo upgrade_tool di -t trust.img # 烧录parameter分区表 sudo upgrade_tool di -p parameter.txt

这里di参数表示download image,后面跟着分区标识符,-b对应boot,-u对应uboot,-t对应trust,-p对应parameter。分区标识符跟parameter.txt里定义的名字必须一致,所以在执行之前先cat parameter.txt确认分区名称是个好习惯。

烧录顺序上,如果是从空板或异常状态救砖,我建议严格按照“parameter → loader/uboot → trust → boot → rootfs → 其他分区”的顺序来。先烧parameter是为了让工具正确识别分区的物理布局;然后再烧uboot和trust,这两个是启动关键,顺序对了之后板子就能正常进入U-Boot;之后再烧kernel和rootfs才有意义。如果你跳过parameter直接烧boot,工具不知道往Flash哪个地址写,结果就是烧完重启什么都没变。

还有一个需要提醒的点,就是Linux下用upgrade_tool需要设备权限。默认情况下普通用户访问USB设备会提示权限不足,所以要么用sudo执行所有命令,要么给设备创建一个udev规则。如果不想每次输密码,我在/etc/udev/rules.d/下放了一个规则,把VID为2207的设备权限改成0666,插上就能直接操作。

4.3 分区表parameter.txt的调整思路

parameter.txt是烧录时最容易拖后腿但也最关键的文件。它定义了Flash芯片上每个分区的名字、起始偏移和大小,格式类似下面这样:

FIRMWARE_VER: 1.0 MACHINE_MODEL: RK3568 MACHINE_ID: 007 MANUFACTURER: Rockchip CMDLINE: mtdparts=rnand0:0x00002000@0x00004000(uboot),0x00002000@0x00006000(trust),...

这里每个分区的偏移和大小都是用十六进制表示的,单位是扇区,一个扇区512字节。比如0x00002000就是8192个扇区,对应4MB空间。如果你要调整rootfs大小,就得数清楚后面的偏移地址,保证前一个分区结束的扇区号正好是后一个分区的起始扇区号,不能有重叠也不能有缝隙,否则后面分区数据会被错误覆盖。

修改parameter.txt之后一定要重新打包update.img,而不是只把parameter单独烧进去。原因很简单,update.img内部还会记录一份分区表信息,单独烧parameter只是改了Flash上的分区布局,但整包里的分区信息还是旧的,下次再用整包升级时又会把布局改回去。所以规范做法是:先改device/rockchip/rk3568/目录下的parameter文件,然后重新执行./build.sh updateimg,生成新的update.img

我吃过一次亏,当时为了给用户数据区腾空间,把userdata分区扩大了一倍,只改了parameter烧进去,没重新打包整包。后来产线用整包升级,分区布局瞬间又被重置了,用户数据全部丢失。那次之后我只要是动了分区表,一定会连同整包一起重新生成,并且做一次完整的升级验证。

5. 常见问题速查与避坑经验

5.1 编译阶段的高频报错

编译阶段最常见的报错主要有三类:磁盘空间不足、依赖库缺失、工具链不匹配。磁盘空间不足通常出现在编Buildroot和Debian rootfs时,报错信息五花八门,一会儿是No space left on device,一会儿又是cmake编译中段。建议在编译前先用df -h检查一下空间。

依赖库缺失主要出现在Ubuntu宿主机的编译环境里。SDK编译某些组件需要libssl-devlibncurses5-devpython2等,不同Ubuntu版本带的包名还不一样。我常备的一行安装命令是sudo apt-get install -y libssl-dev libncurses5-dev git gnupg flex bison gperf build-essential zip curl liblz4-tool zlib1g-dev libc6-dev-i386 x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig,基本覆盖大多数SDK的依赖需求。

工具链不匹配这个坑在新手手里出现频率极高,尤其是那些之前在别的项目里装过交叉编译器的开发者。SDK在build.sh里会主动使用prebuilts/gcc/linux-x86/下自带的工具链,但如果你在~/.bashrc里自定义了PATH,而且自定义的路径排在SDK工具链之前,就可能用错编译器,导致U-Boot编译报Error: unknown pseudo-op: .arch这样的汇编错误。解决办法是编译前检查which aarch64-linux-gnu-gcc,确认用的确实是SDK自带的那个。

5.2 烧录与启动阶段的排查实录

烧录阶段最典型的失败现象是RKDevTool一直提示“等待设备”或者“设备连接失败”。遇到这个现象,我通常按以下顺序排查:第一,确认板子是否真的成功进入Loader模式;第二,换一个USB端口和USB线;第三,重新安装驱动程序,Windows下尤其常见驱动被安全软件拦截的情况;第四,确认是否使用了USB Hub,我遇到过几次是USB Hub供电不稳导致设备枚举失败,直连主板USB口就正常了。

启动阶段的问题就更多了,而且需要结合串口日志来判断。串口输出里如果卡在DDR V1.09这样的信息上,说明U-Boot的DDR初始化没过,要么是DDR频率配置不对,要么是硬件上DDR颗粒型号和参数对不上。如果卡在Loading firmware...之后没有输出,多半是trust.imguboot.img版本不匹配,或者ATF找不到对应平台的BL31参数。

有一个经常被忽略的检查项是电源时序。RK3568、RK3588这些平台有多路电源轨,上电顺序有严格要求。如果你自研板卡烧录一切正常,但上电后就是起不来,先拿示波器抓一下各路电源的上电时序是否正确。我调试过一块板子,现象是偶尔能启动偶尔死机,折腾了好多天最后发现是某个DC-DC的使能引脚时序慢了十几毫秒,导致DDR初始化时供电还没稳定。

Kernel阶段起不来的话,优先看dtb匹配和console输出。我在RK平台遇到过最多次的现象是:内核打印到Starting kernel ...之后就没下文了。这种情况八成是dtb里的内存配置、串口配置跟实际硬件不符。你可以把boot.img里的dtb解包出来,用fdtdump对比一下chosenmemoryserial节点,基本能定位问题。

最后再分享一个实用习惯:每次编译新固件之前,先把rockdev/目录备份一下。瑞芯微SDK在编译过程中会覆盖rockdev/下的文件,如果你上一个版本能正常工作但新版本编译后想回退,没有备份就得重新编译旧代码,浪费时间不说,有时候旧分支可能已经被你改了。我现在都习惯在每次出固件之前执行一次tar czf rockdev_$(date +%Y%m%d).tar.gz rockdev/,一个命令,却能省掉很多不必要的麻烦。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/21 0:50:35

Java IO流原理与性能优化实战指南

1. 项目概述记得刚入行那会儿,第一次接触Java IO流时,被各种InputStream、OutputStream绕得头晕眼花。直到有次线上系统因为文件读取不当导致内存溢出,我才真正意识到掌握IO流原理的重要性。现在回头看,IO流就像城市的地下管网系统…

作者头像 李华
网站建设 2026/9/21 0:49:24

Netdiscover实战指南:ARP扫描在局域网资产发现中的应用

简介:Netdiscover 是一款面向网络管理员、安全审计人员和无线网络运维工程师的开源地址扫描工具,专注解决无 DHCP 无线网络中设备发现与信息收集难题,通过主动发送 ARP 请求快速定位在线设备,并输出 IP 地址、MAC 地址、网络掩码等…

作者头像 李华
网站建设 2026/9/21 0:49:19

电影字幕下载网站大全:类型、筛选标准与实战避坑指南

1. 从“找字幕”这件小事说起:为什么我们需要一份靠谱的字幕站点清单如果你平时有收藏高清电影、追冷门剧集或者看一些小众纪录片,大概率遇到过这样的场景:视频文件已经躺在硬盘里了,画质、音轨都满意,唯独缺一条匹配的…

作者头像 李华
网站建设 2026/9/21 0:47:12

Claude Code 桌面版接入 DeepSeek 与离线 Skills 安装全攻略

1. 为什么我要折腾这套组合:Claude Code 桌面版 DeepSeek 离线 Skills先说清楚这套东西到底是什么。Claude Code 是 Anthropic 推出的一个命令行 AI 编程助手,它跟普通聊天式 AI 最大的区别在于:它能直接读写你本地的项目文件、执行终端命令…

作者头像 李华
网站建设 2026/9/21 0:35:53

SAP资产历史数据迁移:用BAPI_FIXEDASSET_OVRTAKE_CREATE替代AS91/AB01L

1. 项目概述:为什么资产历史数据迁移必须告别AS91/AB01L?在SAP FICO模块的实际运维中,“AS91”和“AB01L”这两个事务码几乎就是资产历史数据迁移的代名词。我接触过的87%以上的企业在做系统升级、集团合并或S/4HANA迁移时,第一反…

作者头像 李华