ZYNQMPSoc系列FSBL启动uboot流程
1 FSBL介绍
1.1 背景
最近由于在工作中需要做一些底层备份的工作,用的芯片属于ZYNQ UltraScale+ MPSoC系列的,大致看了网上的一些资料,基本都是讲ZYNQ 7000系列,涉及ZYNQMP系列比较少,所以自己也是去了解了一下ZYNQMP系列的FSBL流程。与ZYNQ7000系列FSBL流程基本差别不大,涉及的一些流程可以借鉴。这里主要看的是米联客介绍的FSBL启动流程。
1.2 ZYNQMP启动流程
- ZYNQMP的启动步骤,可以大致分为BootRom、FSBL、Uboot、Linux系统几个环节,这四个环节流程图如下:
1.首先, 电源管理单元(PMU)执行 PMU ROM 固化程序来初始化系统,重置和唤醒相关的进程。
2.接着,根据拨码开关从相应内存位置将引导加载程序(PS 的 FSBL 代码)加载到芯片 RAM (OCM)中并执行。
3.然后,运行 ATF 之后启动 uboot 第一阶段引导程序初始化内存等基础硬件,将 uboot 第二阶段和kernel 加载到 DDR 中。
4.最后,从内存 DDR 中加载运行 uboot 第二阶段初始化相关设备,配置 kernel 启动的环境并引导kernel 从而启动 rootfs。 - BootRom是固化在芯片内部的一段代码,我们不可以进行修改,而通过上图我们知道BootRom主要是用来加载FSBL到OCM中运行,并且将执行权限交给FSBL,关于BootRom怎么找到FSBL,主要是我们利用petalinux-package打包BOON.BIN时,存在一个BOOT.BIN的头部,里面包括了FSBL的大小,偏移地址等等,通过BOOT.BIN头找到FSBL,将执行权限交给FSBL,有兴趣的可以去Xilinx官网下载相关的文档查看,里面对Boot Header,image header table,partition header等介绍的很清楚。
- 我们主要来看看FSBL启动流程
2 FSBL启动流程
2.1 工程
因为我这边用到的工程主要是在Linux系统下利用Petalinux搭建的,具体xilinx的Vavido或者vitis我不会用,用的是FPGA那边给的配置好的xsa或者hdf文件,在ubuntu环境下搭建的工程,工程搭建完毕后直接利用petalinux-config -c u-boot/kernel/fsbl,就可以在工程中生成FSBL,U-BOOT和kernel的源码,这里我们以工程中生成的FSBL源码进行大致的分析。
2.2 流程
-
FSBL 的全名为 First Stage Boot Loader,即第一阶段引导加载程序。 FSBL 的任务比较重要,它需要完成以下的工作:
a. 初始化PS端
b. 使用 bit 文件配置 PL 端。
c. 加载 Uboot 到内存中。
d. 移交执行权限给 Uboot。 -
以下为 fsbl 的启动流程:
这个阶段需要完成一系列的初始化操作,首先会初始化 PS 端的内存与 MIO 接口等,然后读取 bit文件配置好 PL 端, 值得注意的是 PL 端需要在 FSBL 的阶段配置好,否则是无法直接配置的。接着加载 Uboot 到内存中,最后 ARM 会跳转到内存中执行 Uboot 程序。
2.3 代码分析
- 因为 FSBL 的作用是引导 SSBL,所以引导裸机和引导 Uboot 的 FSBL 没有什么区别,因此我们直接从petalinux创建的工程 查看 FSBL 的相关源码。
- 首先我们的平台是ZYNQMP,所以我们需要到***fsbl\lib\sw_apps\zynqmp_fsbl\src***下,里面存在链接脚本文件lscript_a53.ld和lscript.ld,打开如下:
可以看到ENTRY函数,进入初始化向量表_vector_table,这里进行一些初始化操作。 - 而*_vector_table定义在源码*fsbl\lib\bsp\standalone\src\arm\ARMv8\64bit\gcc\asm_vectors.S里面。
在向量表中可以看到,启动程序会跳转到_boot, 为了查看其源码,我们需要找到_boot 的实现, _boot的实现在 boot.S 文件中, 打开同目录下的 boot.S 文件: 找到_boot,可以看到其首先在初始化处理器的各种模式,初始化 C 语言的执行环境:
这里会使能 I cache、 SP 堆栈、 caches、 MMU,然后跳转至_startup 函数启动
同样的方法,我们需要找到_startup 的实现,这个函数在同目录下的 xil-crt0.S 中,打开 xil-crt0.S文件并找到_startup:
在_startup 函数中,对 bss 进行了一系列的操作,然后清 bss,构造全局结构体,重启定时器之后跳转到 main 函数开始执行。 - 这里进入main函数,main函数路径:fsbl\lib\sw_apps\zynqmp_fsbl\src\xfsbl_main.c
main 函数会经历以下四个主要阶段:
a.各种初始化。
b.引导设备安装和 image 验证。
c. 加载并验证分区
d. 执行完FSBL,转到uboot,即第二阶段。
各种初始化:
这个函数包括了诸多软硬件的初始化操作,非常清楚地看到在 switch 中属于 XFSBL_STAGE1 也就是 第 一 阶 段 , 在 其 运 行 成 功 后 , 才 会 跳 转 到 第 二 阶 段 继 续 执 行 , 其 位 置 为fsbl\lib\sw_apps\zynqmp_fsbl\xfsbl_initialization.c:
这个函数的第一步便是获取复位的原因。
紧接着是系统初始化:
在这个函数的注释中,调用了 psu_init()这个函数, 该函数根据 PS 的类型进行 MIO,PLL,CLOCK,DDR一系列参数的设定。
处理器初始化:
DDR 初始化:
板卡初始化:
重置验证:
- 如果这些初始化操作均成功了,到这里整个初始化工作便可以告一段落了,接下来要运行第二阶段,也就是引导设备安装和 image 验证。
- 引导设备安装和 image 验证
这一段代码比较有趣,逻辑也比较清晰: 首先运行初始化函数,然后根据返回参数来判断启动方式,分为错误、 JTAG 和普通启动模式。其中如果引导发现是 JTAG 模式就会直接跳转到第四步,这是因为第三步的任务是加载分区,而 JTAG 模式是不需要的。了解的初始化函数的返回值,我们再去查看其实现,同样还是在 xfsbl_initialization.c 文件中:
第一步为获取模式开关的参数,查看 XFsbl_PrimaryBootDeviceInit 函数:
其中一段就展示了获取模式开关的代码,注意此处不是读取模式开关,因为读取是由 BootROM完成的, FSBL 只是 获取了 BootROM 的参数。 这里以 NAND 启动方式为例, 在读取到为XFSBL_NAND_BOOT_MODE 这个参数后,会进行如下的操作:
先是初始化设备(这里是 NAND),再从设备中读取 BOOT.bin 文件,最后关闭文件。
上一步完成后,紧接着就是读取验证头:
最后是设置第二启动设备,如果有第二启动设备,还要对其进行初始化操作: - 加载并验证分区
部分启动方式可以跳过此部分,比如 JTAG 模式。
第三部分也是如出一辙,先加载分区,分为成功与失败两个情况,成功的情况下会继续验证分区。我们先来看 XFsbl_PartitionLoad 这个函数,它的定义在同目录 xfsbl_partition_load.c 这个文件里:
以上为 XFsbl_PartitionLoad 的核心代码,分为三步,先验证分区头,查验是不是对应的型号,如果是对应的型号则复制分区内容,最后是分区的验证。
回到 xfsbl_main.c 文件中,第三步还会对所有分区进行一次验证,保证所有分区均已加载完成:
这一步完成后,便会跳转至下个阶段。 - 转移执行权限
在第四阶段中, 同样也是先运行移交权限的 XFsbl_Handoff 函数,然后处理各种返回的情况:分为三种情况,为:分区未加载完全,会跳转到第三步继续执行。继续移交权限,主要出现在一个应用执行完,需要运行下一个应用时。失败的情况,移交权限失败。还有一种情况就如同注释写的那样,应该永远也不会被执行。
看完整体的构架,接下来要看看 XFsbl_Handoff 的定义,了解一下移交执行权限究竟经过了哪些步骤,这个函数的定义位于 xfsbl_handoff.c 文件中:
恢复 SD 卡检测信号:
解除PS-PL 隔离以允许 u-boot 和 linux 访问 PL:
刷新 L1 和 L2 数据缓存,禁用数据缓存:
PM 初始化:
保护配置:
切换到 CPU 运行: - 到这里, FSBL 阶段的工作全部完成,接下来就由 Uboot 来继续操作。
3 总结
- 相信大家看了上面的分析以及源代码后,大致可以了解FSBL的启动流程,对于FSBL,汇编阶段我们可以不必过于关注,如果我们需要修改FSBL,也只是修改FSBL的C语言阶段。
- 当然,如果真的分析了源码,我们可以发现在主函数中的starge3中,会依次加载BOOT.BIN中的几个分区,有几个分区就分别加载几个,但是,在***XFsbl_PartitionLoad***函数中,调用了三个函数,我仔细看了这三个函数,只是在验证每个分区的头部,对于uboot实体,并没有进行验证,如果我们在nand启动时由于bit位翻转,那系统将无法启动,所以后面我增加了对整个uboot实体的crc校验以及增加了备份分区,主分区加载失败,FSBL启动备份分区uboot镜像。
- 将在下一篇博客分析,后面也还会在uboot中增加kernel备份镜像功能。