计算机启动过程

以嵌入式芯片(MCU)为例,芯片的指令芯片厂商已经通过电路硬件实现在了芯片内部,自己实现的软件,要通过芯片对应的编译器(汇编、C语言)编译成该芯片可以识别的指令,最后写到芯片的Rom里。芯片上电后,CPU会自动去Rom里取指令加载到Ram然后不停的执行。

嵌入式芯片在一个芯片里把我们日常用的PC机的组件都做进去了,CPU,内存(Ram),硬盘(Rom),并且在CPU内固化好逻辑告诉CPU上电后到指定位置取用户存放的代码,放到内存指定开始位置,然后开始执行。

我们日常用的PC机,CPU、内存、硬盘都是分开的,硬盘系统不仅可以重复安装还可以格式化为不同文件系统装不同操作系统,哪里实现的加载逻辑呢? PC上电后CPU从哪里开始执行程序?

PC机的CPU也有这个固化逻辑从哪里取代码、放到内存哪个位置开始执行,只是第一个加载的程序不是用户硬盘中放的程序,而是主板的ROM中存放的系统程序(BIOS程序)。

CPU先取到BIOS的代码执行,BIOS程序完成系统检测等工作,然后按照用户的设置引导CPU接下来上那块硬盘,那个区,那个文件取代码,放到那个内存地址,最后从那个内存地址开始执行。

CPU执行BIOS程序沿着BIOS的引导在硬盘上找到程序放入0x7c00处开始的内存位置,然后跳转到0x7c00开始执行。注意这个程序通常还不是操作系统程序,而是我们常说的“引导”程序,比如GRUB。

CPU继续执行引导程序,沿着引导程序的引导加载操作系统代码到内存,并从某时刻跳转到操作系统main函数,至此BIOS和引导程序的使命完成,计算机内的天下全部变成操作系统的了。

相比之下:

MCU:Rom中的程序 = PC机:BIOS程序+引导程序+操作系统程序。

为什么PC机的启动这么复杂,其实还是计算机解耦分层的思想。PC上BIOS程序最简单但也比MCU的程序复杂多,要实现指引CPU上那块硬盘,硬盘分区是MBR还GPT的哪里找接下来的程序,让CPU能读硬盘BIOS还要实现支持硬盘分区的文件系统等等功能。接下来引导程序,作为中间层可以解耦BIOS和操作系统的绑定关系,相信大家学习Linux时候应该装过Windows和Linux双系统吧,开机时候是不是要在GRUB界面选择要启动的操作系统。

树莓派启动过程

了解了PC的启动过程,参考树莓派的文档,便很容易理解树莓派的启动过程。有意思的是,树莓派的启动过程不是CPU完成的,而是用的GPU,当然树莓派的CPU和GPU是集成在一起的。

首先,系统芯片加载固化程序(Boot Rom), 这个程序在支持的启动设备(SD卡、USB等)中寻找启动程序文件bootcode.bin,把bootcode.bin加载到Cache中并运行它,

然后,bootcode.bin程序会检索程序start.elf,然后运行它。

最后,start.elf程序将kernel.img加载到内存中,内核开始运行,系统启动完成。

其中需要注意,存放bootcode.bin、start.elf的分区需要是FAT32(所以明白了平时做的树莓派系统卡一个是FAT32分区,一个是Ext4),树莓派4因为内置了EEPROM芯片存放bootcode.bin程序,所以树莓派4会忽略bootcode.bin,直接加载EEPROM芯片中的代码。

另外start.elf程序支持用户配置config.txt,实现树莓派系统配置,同时支持配置加载kernel.img时的参数cmdline.txt。

当系统完全启动后,启动相关的文件和程序,可以在/boot目录下找到,用户可以修改config.txt和cmdline.txt然后重启树莓派配置一些初始化参数。

树莓派更新固件和内核

经过一番研究,顺便还发现,树莓派启动相关的代码,是不开源的,系统定制的kernel代码是开源的,bootcode.bin,start.elf官方放到Github上的都是二进制!要重新认识树莓派了,不完全称得上是开源硬件。

网上讨论,树莓派的芯片是博通专供且这部分固件代码也不开源,树莓派的自由与方便差不多是Kernel之上的自由和方便。基金会和博通这么干,也是市面上没有树莓派仿品板子的原因。

既然代码不开源,官方提供了更新这部分软件的通道,使用rpi-update命令可以更新官方编译好的kernel和/boot目录下的相关固件文件,树莓派4更新EEPROM可以使用rpi-eeprom-update命令。