vlambda博客
学习文章列表

开源固件Linuxboot编译与验证

1. 概述

1.1 概念与原理

LinuxBoot是下一代开源的固件,主要目标是用 Linux 内核UEFI BIOS固件的特定功能,如 UEFI 的 DXE、BDS、TSL 和 RT 阶段,其中 UEFI 和 LinuxBoot 的引导过程如1.1和1.2所示。

图1.1 UEFI启动过程(来源官网)

开源固件Linuxboot编译与验证

图1.2 LinuxBoot启动过程(来源官网)

在 LinuxBoot 的官方提供的 LinuxBoot Book 文档中,可以找到其工作原理,以及各个功能组件的编译,如:UEFI PEI、coreboot、LinuxBoot、HEADS、u-root 等组件的编译构建。LinuxBoot 支持 UEFI、coreboot、U-boot等多种引导方式,其中 UEFI 为推荐使用的闭源方式( 依赖主板供应商提供的UEFI),而 coreboot 为全开源方式。

开源固件Linuxboot编译与验证

图1.3 LinuxBoot引导方式(来源官网)

1.2 参考资料

LinuxBoot官网:https://www.linuxboot.org/

LinuxBoot github: https://github.com/linuxboot/linuxboot

LinuxBoot Book github: https://github.com/linuxboot/book

Ubuntu 16.04.7 64-bit: https://releases.ubuntu.com/16.04.7/

2. 组成与特点

2.1 模块组成

开源固件Linuxboot编译与验证

LinuxBoot 由以BIOS、Linux内核 和 u-root -> initramfs 三部分组成:
  • BIOS:不是特定的BIOS,可以是 UEFI、coreboot、U-Boot等;

  • Linux内核:用于替换 UEFI 功能的Linux内核,而非生产环境中用户使用的操作系统内核;

  • 文件系统 (initramfs):嵌入在固件映像中的根文件系统,内涵 kexec 软件,与 Linux 内核一起放置在闪存设备中,作为 LinuxBoot 固件映像的一部分;

服务器开机后,首先加载固件(如 coreboot 或 UEFI),然后将 bzImage 和 initramfs 加载到内存中并启动内核,内核检查 initramfs 的存在,找到后解压,并挂载为“/”、运行/init,启动后可以向Linux一样使用文件系统中的任何命令和程序,比如利用 kexec 加载生产操作系统的内核等。

2.2 优势与特点

  • 充分利用Linux的优势,提升系统的安全性和可靠性;

  • 删除非必要的代码,大大缩短了服务器的启动时间;

  • 利用了Linux的灵活性,可以根据需求扩展个性化需求,比如和云管平台结合等。

3. 构建说明

3.1 前提条件

  1. 主板供应商(需CPU上支持)提供的 UEFI 固件;

  2. 编译时启用CONFIG_EFI_BDS选项的 Linux 内核;

  3. 具有kexec包的文件系统initrd.cpio。

对于UEFI固件不建议使用coreboot,建议使用供应商提供的 ROM 文件,将其复制到boards/$(BOARD)/$(BOARD).rom;对于 qemu 中用的 ROM 文件 ,可以使用 LinuxBoot 中利用 EDK2 自己构建的 ROM 文件。

3.2 构建方法

cp path/to/s2600wf.rom boards/s2600wf/ # 复制供应商提供的 UEFImake \ BOARD=s2600wf \ KERNEL=../path/to/bzImage \ INITRD=../path/to/initrd.cpio.xz \ configmake
以上代码的左右是写.config文件,并且编译 LinuxBoot,编译过程中会下载 EDK2 代码,编译生成 ROM 文件,位置为build/$(BOARD)/linuxboot.rom,中间可能会出现无法下载源码包的情况,可以手动下载后上传。

3.3 用QEMU模拟测试

可以通过如下命令,在 qemu 下运行 LinuxBoot,并查看运行效果:
make run

3.4 添加新主板支持

如果想增加新的主板支持,可以从其他主板中复制 Makefile.board 文件,并根据新主板的规格进行修改,其中比较关键的变量说明如下:
  • FVS:IFD(Intel firmware descriptor)列表,固件卷和其值;

  • linu-size: ROM 映像的最终大小(以字节为单位);

4. 构建步骤

4.1 准备环境

下载并安装ubuntu-16.04.7-desktop-amd64.iso,可以采用虚拟机也可以使用物理机,但是无论虚拟机还是物理机要求磁盘大于100GB。
  1. 更新系统并安装git和qemu

sudo apt updatesudo apt upgradesudo apt install git qemu    # 安装git和qemu

2. 载代码并准备环境

cd ~mkdir lbworks # 在用户目录下创建一个工作目录用于编译环境cd lbworksgit clone https://github.com/linuxboot/linuxbootcd linuxboot

4.2 基于Heads构建bzImage和initrd

Linuxboot推荐使用Heads firmware或者u-root来构建bzImage和initrd,这里咱们根据 Heads 来制作 bzImage 和 initrd,其中构建过程可以参考Heads的官网: http://osresearch.net/Building

1. 安装所需的工具包

sudo apt install build-essential zlib1g-dev uuid-dev libdigest-sha-perl libelf-dev \bc bzip2 bison flex git gnupg iasl m4 nasm patch texinfo \python wget gnat cpio ccache pkg-config cmake libusb-1.0-0-dev

2. 下载Heads代码

cd ~/lbworksgit clone https://github.com/osresearch/heads.git

3. 编译构建

cd headsmake BOARD=qemu-linuxboot # 加上BOARD=qemu-linuxboot,否则编译出来的是coreboot版本的image,编译时间会比较长cd build/qemu-linuxboot # ls -lR| grep "^-" | wc -l,查看文件数量ll # 查看目录下的内容
在编译过程中,可以进入 build 的 log 目录下查看编译过程中生成的日志文件。编译完成后,在qemu-linuxboot目录下应该可以找到bzImage、initrd.cpio.xz、linuxboot.rom三个文件,否则即为编译失败。

注意:编译工程中出现的错误处理

1. 编译过程中出现zlib-1.2.11.tar.gz无法下载导致编译失败,主要原因是因为当前版本为1.2.12,而要下载1.2.11版本需要修改一下下载路径,打开 ./modules/zlib文件,将“zlib_url := https://www.zlib.net/$(zlib_tar)” 改为 “zlib_url := https://www.zlib.net/fossils/$(zlib_tar)”;

2. 编译过程中出现tpmtotp-18b860fdcf5a55537c8395b891f2b2a5c24fc00a.tar.gz无法下载,可以手动在github上下载,然后上传上去;

3. 编译过程中出现libusb-1.0.21.tar.bz2下载失败,可以采用wget https://github.com/libusb/libusb/releases/download/v1.0.21/libusb-1.0.21.tar.bz2手动下载;

4. 使用QEMU运行并验证

cd ~/lbworks/headscp ./build/qemu-linuxboot/linuxboot-qemu-linuxboot-v0.2.0-1168-g182bd6b-dirty.rom ./build/qemu-linuxboot/linuxboot.rom make BOARD=qemu-linuxboot run
运行QEMU之后,就会发现可以成功进入linux kernel,运行效果如下下图:

4.3 编译构建LinuxBoot

1. 复制到LinuxBoot加载目录

接着将bzImage,initrd.cpio.xz,linuxboot.rom拷贝到linuxboot的对应的目录里,我们最终的目的是通过linuxboot来启动linux kernel。
cd ~/lbworkscp ./heads/build/qemu-linuxboot/bzImage ./lbbincp ./heads/build/qemu-linuxboot/initrd.cpio.xz ./lbbincp ./heads/build/qemu-linuxboot/linuxboot.rom ./linuxboot/boards/qemu/qemu.rom

2. 进入linuxboot目录并编译

cd ~/lbworks/linuxboot
make \ BOARD=qemu \ KERNEL=../lbbin/bzImage \ INITRD=../lbbin/initrd.cpio.xz \ configmake
LinuxBoot在编译过程会自动下载edk2的分支代码(UDK2018),如果出现无法下载代码的话,可以手动下载后上传上去,当出现如下图提示是,说明编译成功。

4.4 基于QEMU测试验证

进入linuxboot目录,使用qemu启动linux kernel,如果你看到Heads中的启动效果,说明编程成功。
cd ~/lbworks/linuxbootmake run