SPL 的由来,一切为了自举
ARM 的启动流程
bootRom –> SPL –> uboot –> kernel –> file system –> init
为什么要有 SPL
ARM 的启动流程如上,其中有一阶段是 SPL,今天我们就来探究下,为什么要有 SPL ?
CPU、内存、磁盘 形成僵局(死锁)
我们都知道,CPU 是执行指令的主体,而这些指令是保存在磁盘里的,所以 CPU 就必须想办法拿到磁盘中的指令。但是 CPU 有个坏毛病,这个人比较高冷,觉得磁盘读写速度慢,就不乐意直接搭理人家;另一个原因是,CPU 读取指令需要按字节寻址,而(大多数)磁盘没这个能力。这就导致 CPU 不能直接从磁盘中读取指令。
但是 CPU 愿意从内存中读取指令,因为内存的读写速度很快,另一方面,内存可以按字节寻址。但是内存也有个毛病:使用之前必须先要进行初始化。这也是所有外设的特(通)性(病)了。而初始化内存的指令又存在磁盘中。。
无解了:CPU 不能直接从磁盘中读取指令,需要先将指令放入内存中,然后从内存中读取;但是 CPU 想使用内存,需要先从磁盘中拿到初始化内存的指令,将内存初始化。这就形成了死锁。。。
SRAM
怎么化解死锁呢?还得老大 CPU 来想办法。在僵持不下的时候,CPU 突然想到自己还有一个小弟:Cache(高速缓存 ),一般是 CPU 内部集成的 SRAM。由于是 CPU 内部器件,所以无需初始化就可直接使用(PS:初始化的作用一般是为了适配,内部器件用法固定,无需适配。个人猜想。。)。
“有了 SRAM,我该怎么用呢?“,CPU 心想。很简单,CPU 不是不愿意直接从磁盘中读取指令嘛,那就先把磁盘中的指令读到 SRAM 中,这样 CPU 就可以从 SRAM 中取指令运行了。想法是对的,但是要注意两点,第一个问题:读多少?通常来讲 SRAM 都很小,32K、64K 量级,所以显然不能将整个镜像(uboot + kernel + rootfs)读到 SRAM 中去运行。那只把 uboot 加载到 SRAM 中呢?答案也是不行的,因为 uboot 小则几百K,大则几兆几十兆,SRAM 仍然存放不下,那怎么办呢?
SPL
于是,SPL 就出来了。uboot 整个放进 SRAM 是放不进去的,那就截取 uboot 的一部分放进 SRAM 中,一般是截取 uboot 的开头 4K 内容,这部分就是 SPL。
第二个问题:谁来读?答案是 bootROM。bootROM 读取启动选择引脚,决定从何种磁盘介质(NAND、SD卡、SPI Flash 等)上读取 SPL,并完成对磁盘介质的初始化,然后读取 SPL 到 SRAM,接下来 bootROM 便将控制权交给了 SPL。
SPL 最需要做的一件事情就是初始化内存,它需要赶紧做这个事情,因为接下来的 uboot 第二阶段的代码需要加载到内存中运行,所以初始化内存是 SPL 的重要使命。
初始化内存后,SPL 将 uboot 的第二段代码加载到内存。uboot 第二阶段将初始化更多外设,最后将内核加载到内存,并将控制权交给内核。这样,整个 Linux 系统就跑起来(自举)了。
有一个疑问:bootROM 为什么不直接初始化内存呢?这样就能直接将整个镜像拷贝到内存中运行了,简单直接。我猜测原因可能是:1.磁盘介质的初始化代码固定,而内存的初始化代码不固定(需要根据内存型号进行适配,还需要调参)。2.内存的初始化代码较大,bootROM 存不下。
可以看出,SPL 的作用,就是为了让整个系统能够自举。
参考
Zynq的启动与配置过程详解 bootROM