整理 第二阶段引导 部分.

Signed-off-by: lion.chan <cy187lion@sina.com>
This commit is contained in:
lion.chan 2022-05-06 22:41:20 +08:00
parent cf9116d342
commit 39fb85fe7a
1 changed files with 39 additions and 45 deletions

View File

@ -8,7 +8,7 @@
通常硬件系统具有一组上电自动复位电路这将引起整个硬件系统的复位包括将某些寄存器赋予初始值以及将程序指针IP/PC指针指向复位位置。如果该位置具有有效指令则程序将继续执行从而进入到引导阶段。
随着系统复杂度的增加以及接口的持续标准化,一些硬件厂商倾向于在系统 ROM 中嵌入一段引导代码,用于完成一部分初始化工作,这段代码运行在其他软件引导程序之前,可认为是硬件引导程序。
随着系统复杂度的增加以及接口的持续标准化,一些硬件厂商倾向于在系统 ROM 中嵌入一段引导代码,用于完成一部分初始化工作,这段代码运行在其他软件引导程序之前,可认为是硬件引导程序,比如说 BIOS 系统
以 ARM 平台为例,硬件引导阶段通常不具备外部 DRR 的驱动能力,只使用内部的 s-RAM 进行工作。硬件引导在完成一系列准备工作后,将加载第一阶段软件引导程序到 s-RAM 中的指定位置,并跳转到该程序位置继续执行,此时软件引导代码接管系统权限。
@ -20,19 +20,52 @@
## 软件引导阶段
如前文所述,软件引导可以分为多个阶段。对于基于 x86/64 的 Linux 系统,通常由 BIOS/UEFI 引导,然后交由 GRUB 进行后续引导,最后将控制权交给内核。
如前文所述,软件引导可以分为多个阶段。对于基于 x86/64 的 Linux 系统,通常由 BIOS 完成硬件引导,然后交由 GRUB 进行后续引导,最后将控制权交给内核。
而对于基于 ARM 的 Linux 系统,通常先由硬件厂商定制的引导程序引导,然后交由 UBoot 等进行第二阶段引导。
而对于基于 ARM 的 Linux 系统,通常先由硬件厂商定制的引导程序引导,然后交由 UBoot 等进行第二阶段引导,装载内核并启动系统
### 第一阶段引导
对于一些 ARM 系统,其引导程序以二进制形式直接烧录于外部的 SPI Flash。加载这种引导程序不需要复杂的外存控制器驱动也不需要文件系统来支撑对于资源有限的硬件引导程序来说这种设计比较实际。
基于 ARM 的 SOC 通常可选多种启动和引导方式,比如 Flash 启动或 SD 卡启动等,因此其第一阶段软件引导程序的形式也比较多样。有的第一阶段引导程序只支持从谋一种外存中加载程序,因此需要注意烧录的引导程序镜像与当前所选择的启动方式是否一致
第一阶段引导程序的后续主要工作就是装载第二阶段引导系统,第二阶段引导系统可能位于外部 Flash、eMMC 甚至是 SD 卡中,为此,一阶段引导程序就必须具备相应设备的驱动能力,有的第一阶段引导程序只支持从其中一种存储器中装载程序,此时需要注意烧录镜像是否正确,或者在支持多存储器的情况下,注意拨码开关的设置
如果第一阶段引导程序需要从 Flash、EMMC 或 SD 卡中加载后续程序,那么第一阶段引导程序就必须具备相应设备的驱动能力。一旦第一阶段引导程序能够从外存中读取后续引导程序,并将其装载到 TODO:
一旦能成功访问到第二阶段引导程序,其将被装载到一个特定的内存地址中,第一阶段引导程序完成全部工作后,就跳转到这个地址,第二阶段引导程序开始执行。
### UBoot
值得注意的是,第一阶段引导程序也有可能划分为多个子阶段或子程序,但从功能上来看,它们都在从事第一阶段的引导工作。
### 第二阶段引导和 UBoot
第二阶段引导程序的主要工作是准备内核运行环境包括设置启动引导参数等决定使用的设备树文件内核或驱动的参数等例如Linux 内核文档中对于 ARM 平台引导阶段所需要做的事情给出了以下清单:
1. 设置并初始化 RAM
2. 探知硬件平台类型
3. 设置设备树
4. 装载 initramfs
5. 调用系统内核
最为常见的二级引导程序有GRUB、UBoot 和 Systemd-Boot 等,其中 UBoot 是在嵌入式系统中使用最广泛的二级引导程序,其特点是功能强大,移植容易。它能够引导 zImage、uImage 等类型的内核镜像、装载 ramfs还支持一种 FIT 引导方式。
zImage 是 Linux 定义的内核镜像格式,其特点是头部第 37~40 字节处为由 LINUX_ZIMAGE_MAGIC 宏定义的魔数0x016F2818。zImage 的实质是从 vmlinuz/vmlinuxelf 格式) 通过 objcopy 得来的二进制程序文件,之后该程序文件又经过了压缩和增加头部信息等处理,进一步的减少了文件体积。而为了能正确解压该镜像文件,其中又嵌入了一段解压代码。
UBoot 等二级引导程序将 zImage 加载到系统的内存中,确切的说是[内核内存管理](./8.11_内核内存管理.md)中提到的直接映射区。之后引导程序跳转到内核首地址去执行,这个地址被称为内核的入口点。此后系统权限让度给 Linux 系统内核,解压算法首先被执行,将真正的内核镜像释放到正确的内存位置上。
为了加载 zImage就需要提供一组参数如内核加载地址内核入口地址等。为了方便 UBoot 加载内核镜像UBoot 增加了一种新的内核镜像格式——uImage。uImage 在 zImage 基础上增加了一个 64 字节的头,打包了魔数、镜像长度、内核加载地址、内核入口地址等加载内核时所需的参数。
UBoot 提供了 mkimage 工具用于制作 uImage 文件,该命令基本格式如下:
```bash
# -A ==> set architecture to 'arch'(“alpha”,”arm”,”x86″,”ia64″,”m6k8″,”microblaze”,”mips”,”mips64″,”nios”,”nios2″,”ppc”,”s390″,”sh”,”sparc”,”sparc64″,“blackfin”,”avr32″)
# -O ==> set operating system to 'os'(“4_4bsd”,”artos”,”esix”,”freebsd”,”irix”,”linux”,”lynxos”,”ncr”,”netbsd”,”openbsd”,”psos”,”qnx”,”rtems”,”sco”,”sloaris”,“u-boot”,vxworks”)
# -T ==> set image type to 'type'(“filesystem”,”firmware”,”firmware”,”kernel”,”multi”,”ramdisk”,”script”,”standalone”,”flat_dt”)
# -C ==> set compression type 'comp'(“none”,”bzip2″,”gzip”)
# -a ==> set load address to 'addr' (hex)
# -e ==> set entry point to 'ep' (hex)(一般是-a参数指定的值加上0x40。因为前面有个mkimage添加的0x40个字节的头)
# -n ==> set image name to 'name'
# -d ==> use image data from 'datafile'
# -x ==> set XIP (execute in place)
mkimage -n 'linux-2.6.32' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -d zImage uImage
```
## 内核阶段
@ -50,47 +83,8 @@ Rootfs device is searched and mounted and then the init process is searched with
All the daemons are started and system level services are started either executing the init services present in /etc/ or if the system is systemctl based system then all the services are started as per the guidelines mentioned for systemctl system. After all the services are started then shell program is invoked which creates a login session prompt for the user.
## 内核镜像类型
vmlinuzLinux 内核经过编译后也会生成一个 elf 格式的可执行程序,叫 vmlinux 或 vmlinuz这个就是原始的未经任何处理加工的原版内核 elf 文件。嵌入式系统部署时烧录的一般不是这个 vmlinuz/vmlinux而是要用 objcopy 工具去制作成烧录镜像格式(就是 xxx.bin 这种,但是内核没有 .bin 后缀),经过制作加工成烧录镜像的文件就叫做 Image。
原则上 Image 就可以直接烧录到 Flash 上进行启动执行,但是实际上并不是这么简单,实际上 Linux 的作者觉得 Image 还是太大了,所以对 Image 进行了压缩,并且在 Image 压缩后的文件的前端附加了一部分解压缩代码,构成了一个压缩格式的镜像就叫 zImage。
uboot 为了启动 Linux 内核,还发明了一种内核格式叫做 uImage。uImage 是由 zImage 加工得到的uboot 中有一个工具,可以将 zImage 加工生成 uImage。uboot 中的 mkimage 工具将 zImage 加工生成 uImage这个加工过程其实就是在 zImage 前面加上 64 字节的 uImage 的头信息(包括魔数、镜像长度、内核加载地址、内核入口地址)。
```bash
# -A ==> set architecture to 'arch'(“alpha”,”arm”,”x86″,”ia64″,”m6k8″,”microblaze”,”mips”,”mips64″,”nios”,”nios2″,”ppc”,”s390″,”sh”,”sparc”,”sparc64″,“blackfin”,”avr32″)
# -O ==> set operating system to 'os'(“4_4bsd”,”artos”,”esix”,”freebsd”,”irix”,”linux”,”lynxos”,”ncr”,”netbsd”,”openbsd”,”psos”,”qnx”,”rtems”,”sco”,”sloaris”,“u-boot”,vxworks”)
# -T ==> set image type to 'type'(“filesystem”,”firmware”,”firmware”,”kernel”,”multi”,”ramdisk”,”script”,”standalone”,”flat_dt”)
# -C ==> set compression type 'comp'(“none”,”bzip2″,”gzip”)
# -a ==> set load address to 'addr' (hex)
# -e ==> set entry point to 'ep' (hex)(一般是-a参数指定的值加上0x40。因为前面有个mkimage添加的0x40个字节的头)
# -n ==> set image name to 'name'
# -d ==> use image data from 'datafile'
# -x ==> set XIP (execute in place)
mkimage -n 'linux-2.6.32' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -d zImage uImage
```
## 引导阶段
1. 硬件引导层
2. 第一阶段引导
3. 第二极端引导等
通常硬件里集成了一组启动程序,硬件启动程序执行完后,将系统控制权交给引导程序。
引导程序可能有一级,也可能有多级。一般在 x86/64 平台上为 GRUB 等,而 ARM 平台上为 UBoot 等。在 ARM 平台上,如果厂商预置了其他引导程序,则通常在 UBoot 之前运行。
GRUB 或 UBoot 最主要的工作是准备内核运行环境,包括设置启动引导参数等。如设置串口输出的波特率,使用的设备树文件,内核或驱动的参数等。
\<Linux Kernel Source>/Documentation/arm/Booting
1. Setup and initialise RAM
2. Detect the machine type
3. Setup the device tree
4. Load initramfs
5. Calling the kernel image
引导系统将内核装载到指定位置并跳转到入口点去执行。在[内核内存管理](./8.11_内核内存管理.md)提到,内核对低端内存进行了直接映射,而引导系统加载内核的地址范围,也正是在此段区域。
引导系统将 zImage 加载到对应地址上后,就会执行 zImage 中的自解压程序,将内核释放。