3. Layerscape启动流程与BSP目录结构
Layerscape系列处理器的启动过程是一条严格分阶段的信任链:从上电那一刻起,芯片内部固化的代码依次把控制权交给下一级软件,每一级都负责把下一级从存储介质搬到内存并校验,最终把Linux内核交给CPU执行。理解这条链路,是后续修改U-Boot环境、替换ATF固件、调整设备树和排查"卡在启动某一行"问题的前提。同时,NXP官方BSP把这条链路上每一级的源码、二进制、配置和产物按目录组织起来,只有把目录职责与启动阶段对应上,才能在正确的位置改正确的东西。
一、五级启动链路的分工
Layerscape的启动链可以概括为PBL → SPL → ATF → U-Boot → Linux五个阶段,但不同芯片(如LS1046A、LS1043A、LS2088A、LX2160A)在细节上略有差异,下面以常见的LS1046A为例说明。
PBL(Pre-Boot Loader)是芯片ROM中固化的不可修改代码。上电复位后,CPU从BootROM开始执行,PBL根据RCW(Reset Configuration Word)中配置的启动介质(QSPI、SD/eMMC、NAND、NOR等)去读取一个固定格式的镜像头,通常称为Boot Header或PBI/PSA结构。PBL会解析这个头部,把其中的PBI命令(如写寄存器、初始化DDR控制器)执行掉,然后把SPL镜像搬运到片上SRAM(OCRAM)或已初始化的DDR中,跳转执行。PBL阶段的关键输入是RCW和Boot Header,输出是"SPL已经在内存中并开始运行"。
SPL(Secondary Program Loader)是U-Boot体系中的第一阶段引导程序,在Layerscape上通常由U-Boot源码编译生成u-boot-spl.bin。SPL体积很小,运行在OCRAM中,主要职责是完成DDR初始化(如果PBI没有完全做完)、时钟和引脚复用配置,然后从启动介质中把完整的ATF+U-Boot镜像加载到DDR,最后跳转到ATF入口。SPL是"从SRAM到DDR"的桥梁。
ATF(ARM Trusted Firmware)是ARMv8架构下的安全固件,在Layerscape上以BL2/BL31的形式存在。ATF负责建立异常等级(EL3)环境、配置安全相关寄存器、初始化PSCI服务,然后以BL33的身份跳转到U-Boot。ATF是"安全世界与普通世界的分界点",也是后续Linux使用CPU热插拔、电源管理的基础。
U-Boot是大家最熟悉的引导程序,运行在EL2或EL1。它负责加载设备树(DTB)、加载内核镜像(Image或fitImage)、准备启动参数(bootargs),最后通过booti或bootm命令跳转到内核。U-Boot还承担环境变量管理、网络启动(TFTP/NFS)、固件更新等工程任务。
Linux内核接管后完成驱动初始化、挂载rootfs、启动init进程,系统进入用户态。
二、BSP目录的职责划分
NXP官方BSP(如LSDK或flexbuild体系)通常把源码和产物按功能分目录管理,常见结构如下:
- bootloader/:存放U-Boot源码、ATF源码、RCW配置以及编译脚本。U-Boot源码中
arch/arm/cpu/armv8/fsl-layerscape/是Layerscape平台的核心目录,SPL和U-Boot共用同一套源码,通过CONFIG_SPL_BUILD区分编译阶段。ATF源码通常放在atf/子目录,编译生成bl2.bin和bl31.bin,最终与U-Boot打包成u-boot-with-spl.bin或fip.bin。 - kernel/:Linux内核源码、板级defconfig(如
ls1046a_defconfig)、NXP补丁集。编译产物是arch/arm64/boot/Image或打包后的fitImage。内核目录还包含DTS文件,但通常与dtb目录有交叉引用。 - dtb/:设备树源文件(
.dts/.dtsi)和编译后的.dtb。Layerscape的DTS通常按"芯片级dtsi + 板级dts"组织,例如fsl-ls1046a.dtsi描述SoC内部外设,fsl-ls1046a-rdb.dts描述具体开发板。设备树是U-Boot和内核共享的硬件描述,U-Boot阶段用u-boot.dtb,内核阶段用kernel.dtb。 - rootfs/:根文件系统镜像,可能是Yocto构建的
.ext4、.tar.gz,或BusyBox手工制作的最小系统。rootfs的格式必须与U-Boot的bootargs中root=参数一致,例如root=/dev/mmcblk0p2 rootwait rw。 - firmware/:存放ATF二进制、管理复杂(MC)固件、网络PHY固件、DPAA2的DPL/DPC配置文件等。这些文件通常由U-Boot或内核在运行时加载到指定内存地址,例如DPAA2的MC固件通过
fsl_mc驱动加载。
三、启动链与目录的对应关系
把启动阶段和目录对应起来,排查问题时会非常高效。PBL阶段依赖的是RCW和Boot Header,它们通常由bootloader/rcw/下的.rcw文件编译生成,烧录到QSPI的0x0偏移或SD卡的特定扇区。SPL来自bootloader/u-boot/编译产物。ATF来自firmware/或bootloader/atf/。U-Boot主体、DTB、Image、rootfs则分别来自bootloader/、dtb/、kernel/、rootfs/。
一个典型的工程判断流程是:如果串口完全无输出,问题在PBL或RCW,检查启动介质选择和Boot Header是否正确;如果SPL打印了"U-Boot SPL"后卡住,问题多在DDR初始化或ATF加载;如果U-Boot命令行出现但booti后无输出,检查DTB地址、Image加载地址和bootargs;如果内核打印到"VFS: Cannot open root device"则说明rootfs参数或存储驱动有问题。
sf probe和sf read可以在U-Boot中验证各分区内容,避免"改了源码但烧录位置不对"的常见错误。
四、实操示例:从源码到可启动镜像
以LS1046A RDB为例,假设BSP根目录为~/lsdk,一次完整的构建流程大致如下:
# 1. 编译U-Boot(含SPL)
cd ~/lsdk/bootloader/u-boot
make distclean
make ls1046ardb_qspi_defconfig
make -j8
# 2. 编译ATF
cd ~/lsdk/bootloader/atf
make PLAT=ls1046a bl2 bl31 -j8
# 3. 打包FIP(ATF + U-Boot)
cd ~/lsdk/bootloader
./build_fip.sh
# 4. 编译内核与DTB
cd ~/lsdk/kernel
make ARCH=arm64 ls1046a_defconfig
make ARCH=arm64 Image dtbs -j8
# 5. 生成最终烧录镜像
cd ~/lsdk
./build_image.sh -b qspi -r rootfs.ext4
构建完成后,bootloader/下会生成u-boot-with-spl.bin和fip.bin,kernel/arch/arm64/boot/下生成Image,dtb/下生成fsl-ls1046a-rdb.dtb。把这些文件按QSPI分区表烧录后,上电应能看到类似下面的启动日志片段:
U-Boot SPL 2022.04 (Jan 10 2025 - 10:23:45)
DDR init done
NOTICE: BL2: v2.6(release)
NOTICE: BL31: v2.6(release)
U-Boot 2022.04
Hit any key to stop autoboot: 0
Loading Image from QSPI...
Loading fsl-ls1046a-rdb.dtb...
Booting Linux...
[ 0.000000] Linux version 5.15.0
看到"Linux version"这一行,说明五级链路全部打通。如果卡在"BL2"之前,重点查ATF编译参数和FIP打包;如果卡在"Loading Image"之后,重点查内核加载地址与DTB是否匹配。
五、常见误区与检查要点
- 混淆SPL与ATF的顺序:在Layerscape上,SPL先运行,然后跳转到ATF的BL2,再由BL31跳转到U-Boot。不要以为ATF在SPL之前。
- DTB地址与内核地址冲突:U-Boot的
bootargs和fdt_addr_r、kernel_addr_r必须落在DDR有效范围内且不重叠,否则会出现"内核启动后找不到设备树"。 - rootfs格式与bootargs不匹配:ext4镜像却写
root=/dev/ram0,或ubifs却写root=/dev/mmcblk0p2,都会导致VFS挂载失败。 - firmware未随镜像烧录:DPAA2的MC固件、PHY固件如果缺失,网络接口可能无法up,但内核仍能启动,这类问题容易被误判为驱动bug。
把启动链和BSP目录的对应关系记牢,后续修改U-Boot环境变量、替换ATF、调整设备树或更换rootfs时,就能快速定位到正确的源码目录和烧录位置,而不是在几十个文件夹里盲目搜索。