计算机组成原理 BootLoader/BIOS/U-Boot概念理解
Posted 坐望云起
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算机组成原理 BootLoader/BIOS/U-Boot概念理解相关的知识,希望对你有一定的参考价值。
一、Bios概述
BIOS是 Basic Input/Output System 的首字母缩写词。也称为 System BIOS、ROM BIOS 或 PC BIOS。
计算机的BIOS(基本输入/输出)是其主板固件,该软件运行在比操作系统更低的级别,并告诉计算机从哪个驱动器启动、拥有多少RAM ,并控制其他重要细节,如CPU 频率。您可以进入 BIOS 菜单来更改启动顺序、超频 PC、禁用板载外围设备,甚至设置主密码。
在加载 Windows 或 Linux 之前,BIOS 初始化和测试系统硬件组件(开机自检),并从大容量存储设备加载引导加载程序,然后初始化内核。此过程中,BIOS 会找到 Master Boot Record (MBR) 以完成引导。
BIOS程序被固化在计算机主机板上的一块很小的ROM芯片里。通常不同的主机板所用的BIOS也有所不同。
尽管大多数人使用术语 BIOS 来指代任何主板固件,但这在技术上是不正确的。现代计算机和主板上的设置程序称为 UEFI(通用可扩展固件接口),它支持更大的驱动器并具有比老式 BIOS 更丰富的图形菜单,后者无法支持大于 2.2TB 的存储。但是,通常情况下,人们将 UEFI 称为 UEFI BIOS,将旧的固件称为 Legacy BIOS。现代所有PC都有UEFI BIOS。
二、BootLoader
Bootloader是嵌入式系统在加电后执行的第一段代码,通过这段小程序,进行硬件初始化,获取内存大小信息等,调整手机到适配状态。在它完成CPU和相关硬件的初始化之后,再将操作系统映像或固化的嵌入式应用程序装在到内存中然后跳转到操作系统所在的空间,启动操作系统运行。
uboot是BootLoader的一个具象化的表现,uboot是bootloader的一个子集,但BootLoader不仅仅指uboot,比如bios也是属于BootLoader。但在日常的嵌入式开发中,经常把BootLoader和uboot混在一起,很多时候BootLoader和uboot都指的是uboot,因为在嵌入式开发中,使用的BootLoader基本都是uboot。
三、U-Boot
引导加载程序是在任何系统上运行的关键软件。每当计算系统最初启动时,要加载和运行的第一段代码就是引导加载程序。它为用户提供了一个加载操作系统和应用程序的界面。uboot的核心作用就是启动操作系统内核,uboot的本质就是一段裸机程序。
开源生态中有很多引导加载程序,如 GRUB、UEFI、RedBoot、Yaboot 等。但是,U-Boot 或 Das U-Boot 是最常用的开源跨平台引导加载程序。它通常用于嵌入式系统,主要目标是配置系统和加载下一级操作系统。它支持多种架构,并受到硬件制造商的大量追随。
U-Boot 引导加载程序通常由系统的引导 ROM 从各种引导源加载,通常是非易失性存储器,如 NOR 闪存、SD 卡和 SPI 闪存,在通电期间控制硬件。一旦 U-Boot 开始执行,它会将硬件配置为从板载存储或网络加载下一级映像,然后开始加载下一级映像。加载下一级映像后,U-Boot 将执行控制权转移到下一级可执行映像。U-Boot 还为用户提供了一个类似“shell”的界面,以便用户可以在下一级映像接管之前使用硬件配置。
uboot(universal boot)是通用的启动代码,支持多种架构的CPU。uboot是高度定制的,大致分为Soc级资源管理和板级资源管理。不同的CPU或者同款CPU不同的开发板,uboot都是不同的,要根据硬件电路进行移植。
四、编译和刷入U-Boot
以下内容转载自U-Boot - SFPi 咸鱼派
1、从源代码编译 U-Boot
获取 U-Boot
在网上获取 U-Boot 的源代码。以 Github 为例,把 u-boot/u-boot 仓库克隆下来:
$ git clone git@github.com:u-boot/u-boot.git
如果想要一个稳定版本的 U-Boot ,你可以 checkout 一个 tag,以 v2018.11-rc3 为例:
$ git checkout v2018.11-rc3
或者直接下载指定版本的 U-Boot 源码,以 v2018.11-rc3 为例:
$ wget https://github.com/u-boot/u-boot/archive/v2018.11-rc3.tar.gz
$ tar xvf v2018.11-rc3.tar.gz
2、获取交叉编译工具链
您如果是 Archlinux 用户,可以直接安装 arm-none-eabi-gcc :
$ sudo pacman -Sy arm-none-eabi-gcc
您如果是 Debian 用户,可以安装 gcc-arm-linux-gnueabihf :
$ sudo apt install gcc-arm-linux-gnueabihf
您如果是 Ubuntu 用户,可以安装 gcc-arm-linux-gnueabihf
$ sudo apt-get install gcc-arm-linux-gnueabihf
您如果是 Fedora 用户,可以安装 arm-none-eabi-gcc-cs
$ sudo dnf install arm-none-eabi-gcc-cs
对于未列出的 Linux 发型版,您可以搜索一下它的源有没有交叉编译工具链,如果没有,也可以使用 Linaro GCC。
由于各发型版安装的交叉编译器前缀不同,如果安装的编译器是 arm-none-eabi-gcc ,那么我们之后用到的 CROSS_COMPILE 就是 arm-none-eabi- ,即去掉最后的 gcc 部分。其它常见的还有 arm-linux-gnueabihf- 和 arm-linux-gnueabi ,都可以。
3、配置 U-Boot
提供的已经测试过的 U-Boot 的 .config 文件已经存放在 sbc-fish/sfpi 的 u-boot 目录下。这里的 .config 都是可以直接复制到 U-Boot 根目录中使用的。
以 U-Boot v2018.11-rc3 为例,进入到 U-Boot 目录,下载 .config :
$ wget https://raw.githubusercontent.com/sbc-fish/sfpi/master/u-boot/v2018.11-rc3/.config
如果我们没有提供相应版本的 .config ,可以找一个版本较近的 .config 下载下来用,如果有部分配置需要更改,在后续编译的时候会有相应的提示,一般用默认参数就可以了。
如果想要自己调整配置:
$ make ARCH=arm CROSS_COMPILE=arm-none-eabi- menuconfig
其中 CROSS_COMPILE 是您所安装的交叉编译工具链的前缀。如果您使用我们提供的 .config ,应该不需要做更改。
4、编译 U-Boot
然后开始编译:
$ make ARCH=arm CROSS_COMPILE=arm-none-eabi- -j24
其中 -j24
根据您的机器的 CPU 进行调整。此时应该得到一个 u-boot-sunxi-with-spl.bin
的文件,在当前目录下。
5、刷入 U-Boot
分区
建议向 TF 卡写入 MBR 格式的分区表,并在第一个分区前预留一定的空间。如果我们在上一步编译的文件大小为几百 K ,一个可供参考的分区方案是:
Disk: /dev/disk4 geometry: 980/128/63 [7907328 sectors]
Offset: 0 Signature: 0xAA55
Starting Ending
#: id cyl hd sec - cyl hd sec [ start - size]
------------------------------------------------------------------------
1: 0B 1023 254 63 - 1023 254 63 [ 2048 - 20480] Win95 FAT-32
2: 83 1023 254 63 - 1023 254 63 [ 22528 - 7884800] Linux files*
3: 00 0 0 0 - 0 0 0 [ 0 - 0] unused
4: 00 0 0 0 - 0 0 0 [ 0 - 0] unused
第一个分区(FAT-32)从第 2048 个扇区开始,即在 1M (2048*512=1M) 的地方开始,这样给 U-Boot 预留出足够的空间。这个预留的空间大小,根据编译出来的 u-boot-sunxi-with-spl.bin
文件和 TF 卡容量自行调整。对于分区工具的使用,可以参考 Partitioning - Archlinux Wiki 、 fdisk - Archlinux Wiki 或 fdisk Manpages 。
使用 DD 刷入 U-Boot
把 u-boot-sunxi-with-spl.bin
写入到 TF 卡的 8192 偏移处即可,命令如下:
$ sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/disk4 bs=1024 seek=8
如果您是 macOS 用户,可以使用我们编写的 flash_uboot_macOS.sh 。
6、启动
连接串口
把 TF 卡插入到咸鱼派中,连接 microUSB 到电脑中,应该会看到一个 USB to Serial 设备。各平台的驱动可以在沁恒官网下载得到。在 Windows 下,可以采用串口助手等工具,在 Linux 和 macOS 下,可以用如下命令:
$ screen [tty] 115200
查看串口。对于 macOS ,此处的 tty 应为 /dev/tty.wchusbserial* 的格式。
按下板上的上电按钮,如果配置成功,应该可以成功看到 U-Boot 的启动如下:
U-Boot SPL 2018.11-rc3 (Nov 09 2018 - 11:55:32 +0800)
DRAM: 64 MiB
Trying to boot from MMC1
U-Boot 2018.11-rc3 (Nov 09 2018 - 11:55:32 +0800) Allwinner Technology
CPU: Allwinner V3s (SUN8I 1681)
Model: Lichee Pi Zero
DRAM: 64 MiB
MMC: SUNXI SD/MMC: 0
Loading Environment from FAT... OK
In: serial@01c28000
Out: serial@01c28000
Err: serial@01c28000
Net: No ethernet found.
starting USB...
No controllers found
Hit any key to stop autoboot: 0
=>
至此,我们就可以进行下一步的 Linux 内核编译过程了。
五、Linux 内核编译
1、下载 Linux 源码和应用 Patch
我们在 sbc-fish/sfpi:/linux 维护了对 Linux 主线内核的 Patch 。它提供了咸鱼派设备的 DTS、默认的 .config
和其它一些小的更改。目前已经支持的内核版本见以上链接,其它内核版本可根据最接近的内核版本的 Patch 进行更改。
下载 Linux 内核源码,以 4.19.1 为例:
$ wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.19.1.tar.xz
$ tar xvf linux-4.19.1.tar.xz
$ cd linux-4.19.1
应用我们提供的 Patch :
$ curl https://raw.githubusercontent.com/sbc-fish/sfpi/master/linux/4.19.1/sfpi-linux-4.19.1.patch | patch -p1
patching file arch/arm/boot/dts/Makefile
patching file arch/arm/boot/dts/sun8i-v3s-saltedfishpi.dts
patching file arch/arm/boot/dts/sun8i-v3s.dtsi
patching file arch/arm/configs/saltedfishpi_defconfig
patching file drivers/bluetooth/Kconfig
patching file drivers/bluetooth/hci_h5.c
2、获取交叉编译工具链
您如果已经阅读过 [编译和刷入 U-Boot](https://sbc-fish.github.io/sfpi/uboot/)中的相关部分,可以跳过这一小节。
您如果是 Archlinux 用户,可以直接安装 arm-none-eabi-gcc :
$ sudo pacman -Sy arm-none-eabi-gcc
您如果是 Debian 用户,可以安装 gcc-arm-linux-gnueabihf :
$ sudo apt install gcc-arm-linux-gnueabihf
您如果是 Ubuntu 用户,可以安装 gcc-arm-linux-gnueabihf
$ sudo apt-get install gcc-arm-linux-gnueabihf
您如果是 Fedora 用户,可以安装 arm-none-eabi-gcc-cs
$ sudo dnf install arm-none-eabi-gcc-cs
对于未列出的 Linux 发型版,您可以搜索一下它的源有没有交叉编译工具链,如果没有,也可以使用 Linaro GCC。
由于各发型版安装的交叉编译器前缀不同,如果安装的编译器是 arm-none-eabi-gcc ,那么我们之后用到的 CROSS_COMPILE 就是 arm-none-eabi- ,即去掉最后的 gcc 部分。其它常见的还有 arm-linux-gnueabihf- 和 arm-linux-gnueabi ,都可以。
3、生成并更改 Linux .config
采用我们提供的 .config
在刚才应用的 Patch 中,已经包含了一个已经测试的 .config ,输入如下命令:
$ make ARCH=arm CROSS_COMPILE=arm-none-eabi- saltedfishpi_defconfig
如果想要自己调整配置:
$ make ARCH=arm CROSS_COMPILE=arm-none-eabi- menuconfig
4、编译 Linux 内核
用如下命令编译 Linux 内核:
$ make ARCH=arm CROSS_COMPILE=arm-none-eabi- -j4
编译 Linux 的时长取决于您的机器的硬件设施,并根据 CPU 线程数调整 -j
参数。不妨在等待 Linux 编译的时候点杯咖啡。
编译好后,应该可以得到我们想要的 arch/arm/boot/zImage
和 arch/arm/boot/dts/sun8i-v3s-saltedfishpi.dtb
文件。
5、写入 TF 卡
将编译得到的 arch/arm/boot/zImage
和 arch/arm/boot/dts/sun8i-v3s-saltedfishpi.dtb
文件拷贝入 TF 卡的文件系统中。建议写入 FAT 或者 EXT 文件系统。
6、准备 rootfs
仅有内核并不够,我们还需要一个 rootfs 。常见的办法有 buildroot 和直接采用已有的发型版的镜像,这里以 ArchLinuxARM 为例,假设我们预期的 rootfs 分区已经挂载到了 mnt
处:
$ wget http://os.archlinuxarm.org/os/ArchLinuxARM-armv7-latest.tar.gz
$ bsdtar -xpf ArchLinuxARM-armv7-latest.tar.gz -C mnt
您如果采用的是 macOS 系统并且无法写入 EXT4 文件系统,可以先找到一台 Linux 机器,进行如下操作:
$ dd if=/dev/zero of=archlinuxarm.img bs=1M count=1024
$ mkfs.ext4 archlinuxarm.img
$ sudo mount -o loop archlinuxarm.img mnt
$ wget http://os.archlinuxarm.org/os/ArchLinuxARM-armv7-latest.tar.gz
$ bsdtar -xpf ArchLinuxARM-armv7-latest.tar.gz -C mnt
$ sudo umount mnt
将得到的 archlinuxarm.img
复制到 macOS 系统下,再写入:
$ sudo dd if=archlinuxarm.img of=/dev/[disk] bs=65535
此处的 [disk]
可以用 diskutil list
找到相应的分区。为了性能更好,可以在设备名称前添加一个 r
,如 /dev/rdisk4s2
。
如果您在 macOS 上安装了 e2fsprogs (brew install e2fsprogs
),可以把文件系统扩大成分区的完整大小:
$ sudo /usr/local/opt/e2fsprogs/sbin/resize2fs -p /dev/[disk]
这里的 [disk]
意义同上。
7、配置 U-Boot 启动选项
在上一节中,您应该已经可以进入到 U-Boot 的界面了。首先需要找到我们刚才构建得到的内核和 .dtb
文件。查看 TF 卡的分区表:
=> part list mmc 0
Partition Map for MMC device 0 -- Partition Type: DOS
Part Start Sector Num Sectors UUID Type
1 8192 85045 68db6199-01 0c
2 94208 30453760 68db6199-02 83
=>
此处可以看到 TF 卡上的分区情况。这里的例子是,一个 FAT 分区保存内核和 .dtb
文件,另一个 EXT4 分区存放着 rootfs 。输入以下命令可以确认一下内核存放的地方:
=> fatls mmc 0:1 # 浏览分区 1 (FAT 文件系统)上的文件
=> ext4ls mmc 0:2 # 浏览分区 2 (EXT4 文件系统)上的文件
在本教程中,所需的文件 zImage
和 sun8i-v3s-saltedfishpi.dtb
放在了第一个分区(FAT 文件系统)中。输入如下命令把内核和 .dtb
文件加载到内存中:
=> fatload mmc 0:1 0x41000000 zImage
=> fatload mmc 0:1 0x41800000 sun8i-v3s-saltedfishpi.dtb
如果是 EXT4 文件系统,可以采用 ext2load
或者 ext4load
代替上面的 fatload
命令。现在开始启动入内核:
=> setenv bootargs console=ttyS0,115200
=> bootz 0x41000000 - 0x41800000
此时应该可以看到 Linux 内核成功启动,但因为找不到 rootfs,所以未能完成启动。在上面的例子中,rootfs 位于 TF 卡的第二个分区,于是可以更改启动参数并进入内核:
=> fatload mmc 0:1 0x41000000 zImage
=> fatload mmc 0:1 0x41800000 sun8i-v3s-saltedfishpi.dtb
=> setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait
=> bootz 0x41000000 - 0x41800000
需要注意的是,这里的 rootwait
必须要添加,另外 /dev/mmcblk0p2
代表了 TF 卡上的第二个分区,在刚才没有指定 rootfs 的时候,内核会输出如下信息:
[ 2.252872] Please append a correct "root=" boot option; here are the available partitions:
[ 2.261315] b300 15273984 mmcblk0
[ 2.261320] driver: mmcblk
[ 2.268156] b301 42522 mmcblk0p1 68db6199-01
[ 2.268159]
[ 2.274971] b302 15226880 mmcblk0p2 68db6199-02
[ 2.274974]
您可以根据在这里显示出来的信息找到并把 mmcblk0p2
改成相应的 rootfs 。
如果想要保存启动命令,使得之后都可以自动启动,输入以下指令:
=> setenv bootcmd 'fatload mmc 0:1 0x41000000 zImage;fatload mmc 0:1 0x41800000 sun8i-v3s-saltedfishpi.dtb; setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait; bootz 0x41000000 - 0x41800000'
=> saveenv
此后每次上电之后 U-Boot 就会自动启动进入 Linux 了。也可以手动输入 boot
进入 Linux 。
以上是关于计算机组成原理 BootLoader/BIOS/U-Boot概念理解的主要内容,如果未能解决你的问题,请参考以下文章