
做 ZYNQ MPSOC 的 SD 卡启动盘很多兄弟一上来就卡在分区和 BOOT.BIN 这两个地方。明明工程编译没问题镜像也导出来了结果板子上电串口一点反应都没有最后发现要么是引导分区没放对位置要么是 FAT32 格式压根没格对。这篇文章我直接把从空白 SD 卡到跑起 Linux 的完整流程拆开讲包括启动链路、分区格式化、文件拷贝、U-Boot 调试和常见坑照着操作基本能一遍过。1. 先吃透 MPSOC 的 SD 启动链路1.1 BootROM、FSBL、U-Boot 各自干什么ZYNQ MPSOC 说白了就是一颗把 ARM 多核处理器和 FPGA 可编程逻辑放进同一封装里的异构 SoC。它的启动方式和单片机那种上电从固定地址取指完全不一样是一级接一级的接力链。芯片内部有一段固化在硅片里的代码叫 BootROM这段代码不可修改。它上电后第一件事就是根据 MODE 引脚的组合去判断用什么介质启动。如果把启动方式拨到 SD 卡BootROM 就会去初始化 SD 控制器然后到 SD 卡的第一个 FAT32 分区里找一个固定文件名的 BOOT.BIN。对固定文件名、固定位置BootROM 没有界面也没有菜单找不到这个文件就直接不往下跑。BOOT.BIN 是这一级的核心产物它里面通常打包了四个组件各有分工FSBL第一阶段引导程序负责 DDR 初始化、时钟配置以及把后续镜像从 SD 卡搬运到内存。PMU Firmware电源管理单元固件管理上电时序、电源域和功耗控制。ATF也就是 ARM Trusted Firmware提供安全监控层负责异常等级切换。U-Boot第二级引导程序最后接管硬件负责加载内核和设备树。这一串东西被打包进一个 BOOT.BIN是因为 BootROM 只认这一个约定好的入口。理解这条链之后排查启动问题就变成按图索骥了。看到串口卡在 DDR 初始化你会先去怀疑 FSBL 的 DDR 配置而不是反过来去翻内核驱动。1.2 BOOT.BIN 是一个容器不是现成 ROM很多人第一次接触时会以为 BOOT.BIN 是从某个链接直接下载的一个文件其实它是一个容器类似把一个 zip 压缩包改了扩展名。里面可以封装多个 ELF 格式的二进制然后通过工具按顺序拼接生成。在 PetaLinux 里打包命令大概是下面这样petalinux-package --boot --format BIN \ --fsbl ./image/linux/zynqmp_fsbl.elf \ --pmufw ./image/linux/pmufw.elf \ --atf ./image/linux/bl31.elf \ --u-boot ./image/linux/u-boot.elf \ --output ./BOOT.BIN如果不用 PetaLinux用开发环境手动建 Boot Image 也可以但顺序要一致FSBL、PMU、ATF、U-Boot。任何一组缺失或者顺序颠倒启动链就会在对应环节断掉。还有一个非常容易踩的误区直接把编译出来的 u-boot.elf 改名成 BOOT.BIN这是不行的。BootROM 对 BOOT.BIN 的镜像头有严格校验裸 ELF 文件根本进不了第二阶段。安全性是另一个层面但结构和格式校验是必须先过的门槛。1.3 分区和文件布局为什么这么定既然启动链路清楚了SD 卡为什么需要分区也就不难理解了。至少两个分区分区1FAT32放 BOOT.BIN、内核镜像、设备树、boot.scr。分区2EXT4放完整的根文件系统 rootfs。“分区1必须是物理上的第一个分区”这句话我要重点标出来。BootROM 解析分区表时找的是索引为 0 的那个条目。你要是把 rootfs 放在第一分区、引导文件放第二分区BootROM 读第一个分区发现文件系统不认直接罢工。论坛上太多“板子完全不启动、串口零输出”的求助帖最后查来查去就是分区顺序颠倒。另外 FAT32 分区大小不用太大512MB 到 1GB 完全够用里面无非几十 MB 的引导文件。rootfs 分区则用剩余全部空间。如果是用厂商提供的整卡镜像那就更简单直接用 dd 整盘写入就行这个我放到后面详细说。2. 制作前的准备工具、镜像和启动模式确认2.1 Linux 主机侧的工具清单做启动盘最好在 Linux 环境里操作工具链非常顺。我每次必用的工具如下sudo apt update sudo apt install dosfstools e2fsprogs util-linux这些包分别提供 mkfs.vfat、mkfs.ext4、fdisk、partprobe、mount 这些命令。如果你还需要解包 rootfs可能再装一个 tar 和 xz-utils不过 Ubuntu 默认基本都带。Windows 能不能做能做但我不推荐。Windows 自带的磁盘管理虽然能建分区但文件系统类型可选范围很窄经常只有 FAT32 和 NTFS要建 EXT4 还得依赖第三方驱动。来回切换工具很容易把分区表搞乱得不偿失。2.2 引导文件从哪来BOOT.BIN、内核、dtb这一步是把 SD 卡启动盘和整个工程构建串联起来的关键。通常引导文件在 PetaLinux 编译完成后的 image/linux 目录下BOOT.BIN第二阶段启动镜像。Image 或者 image.ub内核镜像。system.dtb 或者类似命名的设备树文件。boot.scrU-Boot 的启动脚本可选但建议带上。不同版本的嵌入式 Linux 工具链内核镜像格式会有差异。老一些的版本是 Image 单独 dtb新版本倾向把 kernel、dtb、rootfs 封装成一个 image.ub 单一文件。这两种方案在 U-Boot 环境变量里写法不一样后面调试章节我会给具体命令。如果你只拿到厂商给的 BSP没有自己构建整套镜像那一般 BSP 包里也会带 BOOT.BIN 和 Image可以先拿来做一个最小可启动盘验证硬件没问题再迭代自己的工程。2.3 rootfs 的两种来源和选择思路rootfs 的来源基本分两条路用 PetaLinux 构建出来的 rootfs.ext4 或者 tar 包。调试阶段这个最简单里面自带网络工具、常用命令还缺什么组件可以后续用软件包管理去补。自己下载一个通用 Linux 发行版根文件系统然后用 chroot 或者交叉编译方式裁剪。这种方式更适合产品化但入手门槛高第一次做启动盘不建议这么搞。我自己调试的时候优先用 tar 包方式解到第二分区而不是直接把 rootfs.ext4 这个整盘镜像 dd 过去。原因很简单整盘镜像自带文件系统和分区元数据直接覆盖到一个已有 EXT4 分区上容易出现大小不匹配、卷标不对之类的问题。tar 解包则把文件系统目录直接铺开干净、可验证。2.4 板卡启动拨码和串口连接确认这个环节看着不起眼丢分却最多。拿到板卡先查硬件手册确定 SD 启动对应的 MODE 引脚组合再去底板找启动拨码开关。不同板卡拨码定义不一样有的标 MODE[3:0]有的标 SW1有的标 Boot Mode。核心原则是插上 SD 卡后把拨码拨到 SD/MMC 对应档位再上电。如果拨码停在 QSPI 或者 JTAG 档SD 卡做得再完美也白搭。串口连接方面MPSOC 调试串口一般是 115200 波特率8N1。用 USB 转串口线连接调试串口后在 Linux 主机上用 minicom 或者 screen 打开dmesg | grep ttyUSB screen /dev/ttyUSB0 115200如果屏幕能输出说明串口链路没问题。如果完全没输出先别急着怀疑 SD 卡把 TX/RX 两根线对调一下再试。3. SD 卡分区与格式化实操3.1 分区方案与参数推荐手工制作启动盘我推荐下面这个方案分区文件系统大小用途分区1FAT321GB引导文件分区2EXT4剩余rootfs1GB 是折中值既能放下多份内核和设备树备份又不会因为在 FAT32 下创建过大分区而触发兼容性问题。如果你只有 4GB 的卡引导分区可以缩减到 512MB剩下给 rootfs。分区表类型建议用 DOS/MBR不要用 GPT。BootROM 在设计时主要按 MBR 分区表找第一个分区虽然有些新版本固件也能支持 GPT但没必要给自己找不确定性风险。3.2 用 fdisk 重建分区表先插上 SD 卡用lsblk确认设备节点。这一步我强烈建议先把其他 USB 存储设备拔掉只留目标 SD 卡。lsblk sudo fdisk /dev/sdX进入 fdisk 交互界面后按以下顺序操作输入d删除已有分区如果还有多个分区就继续d输入o创建新的 DOS 分区表输入n创建第一个分区起始扇区用默认值大小指定1G输入t把分区类型改成c也就是 W95 FAT32输入a给第一分区打上 boot 标记输入n创建第二个分区起始扇区和大小都用默认值输入p检查分区表确认没有异常输入w写入退出。写入后系统会自动重新识别分区一般会出现 /dev/sdX1 和 /dev/sdX2。如果没有用 partprobe 刷新一下sudo partprobe /dev/sdX sudo lsblk这里有个细节值得留意fdisk 里设置的分区类型是给后面识别分区用的元数据真正格式化的时候还要用 mkfs 命令。两者一定要一致别出现标成 FAT32 实际格式化成 ext4 的情况不然后面 U-Boot 里 fatls 什么都看不到。3.3 格式化并复制引导文件分区建好后就轮到格式化命令很简单sudo mkfs.vfat -F 32 -n BOOT /dev/sdX1 sudo mkfs.ext4 -L rootfs /dev/sdX2-F 32指定创建 FAT32-n BOOT给文件系统打个卷标。调试时在别的机器上一扫就知道哪个分区是什么省去用 fdisk 再去猜的麻烦。格式化完成后强烈建议顺手 fsck 一次sudo fsck.vfat /dev/sdX1 sudo fsck.ext4 /dev/sdX2很多劣质读卡器和低速 SD 卡在 mkfs 阶段就能暴露问题与其等上板之后才发现启动失败不如在这个环节就把坏卡淘汰掉。接下来挂载复制文件mkdir -p /mnt/boot /mnt/rootfs sudo mount /dev/sdX1 /mnt/boot sudo mount /dev/sdX2 /mnt/rootfs把引导文件复制到第一分区sudo cp BOOT.BIN boot.scr Image system.dtb /mnt/boot/设备树文件名按你自己工程实际输出为准有的叫 system-top.dtb有的叫 zynqmp-zcu102-rev1.0.dtb反正复制时和你在 U-Boot 里 load 的名字保持一致。3.4 解包 rootfs 到第二分区rootfs 用 tar 包方式解压示例命令如下cd /mnt/rootfs sudo tar -xJf /path/to/rootfs.tar.xz解包完成后务必检查第二分区根目录下是否有 bin、etc、lib、usr、dev 这些目录。如果里面只有一堆散文件那肯定哪里没弄对。文件系统里还得有 init 程序或者 systemd否则内核启动后挂载 rootfs 成功也会因为找不到 init 而 panic。检查完成后卸载并同步sudo umount /mnt/boot /mnt/rootfs syncsync 不能省。有些读卡器有写缓存看着文件已经复制完实际数据还在缓存里没落盘。不 sync 直接拔卡很可能拿到一块“空卡”上机启动失败白白浪费一个下午。4. 上板启动验证与 U-Boot 调试4.1 上电前检查清单插卡上电前我习惯快速过三遍启动拨码是否在 SD 模式SD 卡是否完全插到位串口 TX、RX 是否接对。确认无误后在主机上打开串口终端再给板子上电。如果串口终点有日志滚动说明起码 BootROM 已经把 BOOT.BIN 找到了启动链第一环是通的。4.2 串口日志的分段解读正常启动时串口日志会经历很明显的几个阶段CPU 型号、PSCI、DDR 初始化信息这一阶段由 FSBL 和 ATF 输出U-Boot 的 banner包括版本号、编译时间、板卡名称内核启动日志能看到 mmc 识别、分区枚举、文件系统挂载登录提示符或业务程序输出。如果输出停在 DDR 初始化早期通常是 BOOT.BIN 里的 FSBL 和板卡 DDR 配置不匹配或者镜像打包顺序有问题。如果 U-Boot banner 已经出来但一直停在Hit any key to stop autoboot那是 U-Boot 没找到下一步要加载的内核或设备树。我曾经遇到过一次很典型的卡死串口输出了几行 U-Boot 信息然后就没动静了。查了很久发现是 boot.scr 文件没放进 FAT 分区U-Boot 找不到启动脚本默认 bootcmd 又不对就一直空转。4.3 U-Boot 命令行里怎么排查在 U-Boot 提示符出现时按任意键进入命令行先看分区和文件mmc list mmc part fatls mmc 0:1fatls mmc 0:1是列第一个 SD 控制器第一个分区里的文件。如果这里什么都看不到要么是分区号不对要么是文件系统不是 FAT32要么文件名大小写和预期不一致。再看环境变量printenv重点看 bootcmd 和 bootargs。老式做法的 bootcmd 大概是load mmc 0:1 0x20000000 Image load mmc 0:1 0x40000000 system.dtb booti 0x20000000 - 0x40000000新式做法的 bootcmd 则可能是load mmc 0:1 0x20000000 image.ub bootm 0x20000000无论哪种保证文件名和实际上传的文件完全一致。如果发现有偏差直接在 U-Boot 里手动加载一次能跑起来再改环境变量保存。4.4 根文件系统挂载失败的处理内核启动到最后如果打印类似VFS: Unable to mount root fs基本就是 bootargs 里的 root 参数指向不对。常见 root 参数写法root/dev/mmcblk0p2 rw rootfstypeext4mmcblk0p2 表示第一个 SD 控制器识别出的整卡设备的第二个分区。如果板子上有两个 SD 控制器卡被识别成 mmcblk1就要改成 mmcblk1p2。在 U-Boot 命令行里直接修改并保存setenv bootargs consolettyPS0,115200 root/dev/mmcblk0p2 rw rootfstypeext4 saveenv boot如果这样能起来说明启动链已经全通。剩下就是业务软件的定制问题。5. 踩坑记录与固定操作清单5.1 常见问题速查表我把自己在调试中遇到过的典型问题整理成一张速查表你可以直接对照排查现象可能原因排查 / 解决上电后串口完全无输出启动模式不对 / BOOT.BIN 缺失 / 串口线接反检查拨码、查看分区1内容、交换 TX/RX卡在 DDR 初始化FSBL 和 DDR 配置不匹配 / BOOT.BIN 打包顺序错误重新生成 FSBL核对 DDR 参数U-Boot banner 出现但卡住boot.scr 缺失 / bootcmd 环境变量错误进命令行手动 load 内核内核起来但挂载 rootfs 失败bootargs 的 root 参数指向错误 / 分区文件系统损坏fsck 检查修正 root/dev/mmcblk0p2内核报 mmc 错误并频繁重启卡接触不良 / 电平匹配问题 / 转接板故障换卡、换读卡器、检查接线FAT 分区有文件但 U-Boot 读不到分区不在首位 / 文件系统非 FAT32 / 文件名大小写不符重做分区表核对文件列表大容量卡不稳定部分旧固件对 SDXC 支持不完整调试期优先用 8~32GB 的 SDHC 卡5.2 我的固定制作启动盘流程踩的坑多了之后现在做启动盘已经形成一套肌肉记忆分享出来供你参考lsblk 确认设备节点拔掉其他 USB 存储fdisk 全删分区新建 MBR 分区表创建 FAT32 EXT4 两个分区mkfs.vfat 和 mkfs.ext4 格式化顺手打卷标挂载分区复制 BOOT.BIN、内核、dtb、boot.scr 到第一分区解包 rootfs 到第二分区检查目录结构umount 后 sync。上电前核对启动拨码接好串口再上电。按这个清单走基本每批卡都是一遍过。还有一个小技巧如果你有一张已经能启动的“黄金卡”每次改完工程后不要急着做新卡先拿这张卡验证镜像本身没问题再做新卡。这样可以把变量分离开不会同时怀疑镜像和卡排查速度快很多。5.3 不想手工分区可以直接写整卡镜像如果 PetaLinux 或者 BSP 给了 .img 整卡镜像也就是把分区表和文件系统全打包好的文件那就不用手工分区一条命令解决sudo dd ifpetalinux-image.img of/dev/sdX bs4M statusprogress convfsync这条命令会按物理扇区把整张卡覆盖原来的分区表自然不复存在。写入完成后用 fdisk -l 看一下分区结构确认第一分区是 FAT32第二分区是 EXT4。dd 写盘的缺点是镜像里的 rootfs 分区大小一般比你实际 SD 卡容量小剩余空间是未分配的。想用满空间就再用 parted 或 fdisk 把第二分区扩大然后执行 resize2fs。这个扩展操作听起来简单实际做的时候要先卸载挂载点再修改分区大小最后再重新挂载顺序错了容易导致文件系统损坏。整卡镜像的方案适合量产和快速复现手工分区则适合理解原理、灵活调整。两种方法我都经常用核心判断标准是我需要在这张卡上改多少文件。如果是固定版本直接 dd 整卡镜像如果后续要反复换内核、换设备树还是手工分区更灵活。最后说一个我一直保留的习惯新板子到手的第一次启动我不会急着放自己的工程镜像而是先拿一套确认可用的基础镜像在空白卡上验证硬件链路是否正常。硬件没确认之前所有软件问题都可能被误判成硬件问题。SD 启动盘这件事本身不复杂难的是把每一步都做扎实让板子一次跑起来。