目录
(2)U-Boot:嵌入式领域的BIOS+BootLoader结合体
(3)编译U-Boot,使之符合vexpress-a9开发板环境
(4)一次失败的启动:vexpress-a9与新版U-Boot的兼容性问题
在学习Linux驱动开发的时候,我发现理解设备树、总线架构、子系统等等本身并不是难点,但是自己在编写代码的时候却经常出错,而且大多数时候是因为运行环境的问题,比如这个子系统没裁剪进来、那个依赖的子系统没有开启等等。为了解决这些问题,我决定系统的学习一遍Linux裁剪、编译的原理。
一、Linux启动流程
(1)BIOS与BootLoader的理解
在嵌入式开发中,上电启动流程是所有开发的起点,也是很多开发者最容易混淆的核心概念。我最开始会把 BIOS、Bootloader、startup 文件、main 函数混为一谈,甚至搞不清 STM32 裸机开发和带 Linux 系统的嵌入式开发在启动逻辑上的本质区别。

BIOS就类比STM32的startup.s启动文件,他只做最基本的初始化,比如初始化时钟、堆栈指针等等,然后在最后一行跳转到main函数去执行裸机代码。
startup.s 的作用:完全对应 PC 的 BIOS,只做三件事:
- 初始化堆栈指针(SP)
- 初始化中断向量表
- 跳转到
main函数全程不初始化任何外设(LED、串口、SPI、SD 卡一概不管),只做最基础的硬件环境搭建。
在STM32中裸机开发中,我们是直接在这个main函数中对各种外设初始化并运行的,比如我们要使用SD卡,于是我们初始化SPI功能。由于STM32是哈弗架构的,所以main函数是直接从Flash读取并运行的。
而现代操作系统(如 Linux)的代码量、动态内存需求极大,而 Flash 的随机访问速度、带宽远低于内存(DDR内存)。如果继续沿用裸机的「指令直接从 Flash 取指」的哈佛架构模式,会严重拖慢系统运行效率。因此,带操作系统的嵌入式系统(多为 ARM Cortex-A 系列,冯诺依曼架构),会将内核镜像、根文件系统从 Flash/SD 卡等存储设备,完整搬运到高速 DDR 内存中执行,从根本上解决性能瓶颈。这个过程我们称为BootLoader。
BootLoader相当于在STM32的裸机开发最后加入一行指令,跳转到内存中的操作系统。即BootLoader一方面在裸机环境中做了基本外设的初始化,其中最为关键的是:把Flash中的操作系统代码拷贝到内核中,最后将控制权交给操作系统内核。
Bootloader(U-Boot):核心的「搬运 + 跳转」环节,干三件事:
- 初始化必要外设:DDR 内存、SD 卡 / EMMC、Flash 等存储设备,为系统运行搭建硬件环境
- 搬运系统镜像:把 SD 卡 / EMMC 中的 Linux 内核镜像、设备树文件,从 Flash 搬运到 DDR 内存中
- 跳转执行:跳转到内存中的 Linux 内核入口地址,把控制权完全交给操作系统
(2)U-Boot:嵌入式领域的BIOS+BootLoader结合体
在嵌入式 Linux 世界里,U-Boot = 最主流的 BootLoader 实现。它不是简单的引导程序,而是集 BIOS + BootLoader 于一身的超级启动固件。

所以我们想要进行嵌入式Linux驱动开发,首先就得下载U-Boot。不过通常U-Boot官网上的引导启动程序不是很完善,只是一个通用模板,而具体的开发板厂商会针对自己的板子进行定制化修改。比如你想要使用i.MX6ULL开发板,你就去NXP官网下载它对应的U-Boot即可。
二、如何在QEMU上模拟启动流程?
(1)官网下载U-Boot
由于我们使用的是QEMU模拟的vexpress-a9开发板,U-Boot是完美支持的,所以可以直接在官网下载。(如果你使用的是各种厂家真实开发板,则需要下载厂家定制的U-Boot)
Index of /pub/u-boot/在这个链接下可以找到所有版本的U-Boot。将其放到与Linux内核同层级处:

解压缩并进入U-Boot的目录中:
cpp
# 解压.bz2格式的源码包
tar -jxvf u-boot-2026.04.tar.bz2

(2)检测工具链完整性
首先检测一下你的工具链、QEMU是否正常安装好了:
cpp
echo "===== 开始检测嵌入式开发环境 ====="
# 1. 检测 ARM 交叉编译工具链
echo -e "\n[1/3] 检测 arm-linux-gnueabihf-gcc 工具链..."
if command -v arm-linux-gnueabihf-gcc &> /dev/null; then
echo "✅ 工具链已安装:"
arm-linux-gnueabihf-gcc --version | head -n1
else
echo "❌ 工具链未安装 或 未加入环境变量"
echo "安装命令:sudo apt install gcc-arm-linux-gnueabihf"
fi
# 2. 检测 QEMU ARM 模拟器
echo -e "\n[2/3] 检测 qemu-system-arm..."
if command -v qemu-system-arm &> /dev/null; then
echo "✅ QEMU 已安装:"
qemu-system-arm --version | head -n1
else
echo "❌ QEMU 未安装"
echo "安装命令:sudo apt install qemu-system-arm"
fi
# 3. 检测 make / git 等编译依赖
echo -e "\n[3/3] 检测 make、git、gcc 等依赖..."
if command -v make &> /dev/null && command -v git &> /dev/null && command -v gcc &> /dev/null; then
echo "✅ 所有基础编译工具都正常"
else
echo "❌ 部分依赖缺失"
echo "安装命令:sudo apt install git make gcc libncurses-dev bzip2"
fi
echo -e "\n===== 检测完成 ====="
只要输出结果是三个绿色√就是正常的,接着往下走。
(3)编译U-Boot,使之符合vexpress-a9开发板环境
由于U-Boot是全世界所有开发板平台通用的,他里面有一堆我们当前开发板不需要的文件,我们使用make编译选项,只编译出需要的uboot即可。

即对这个Makefile进行编译选项的配置、裁剪。
cpp
#清理,防止以前有旧文件影响
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
#加载 vexpress-a9 开发板默认配置(QEMU 专用)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- vexpress_ca9x4_defconfig
#编译 U-Boot(多核加速,自动用满 CPU 核心)
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc)

可以看到已经编译成功,输出了.config文件。关于这个警告只是说:你把设备树(这里是U-Boot自己初始化硬件时候用的设备树,不是Linux内核中的设备树)放到u-boot里面去了,这个在现代我们不推荐,已经过时了。不过我们对于初学者而言,我们不用理会。
(4)一次失败的启动:vexpress-a9与新版U-Boot的兼容性问题
在命令行中输入以下命令,启动QEMU。
cpp
qemu-system-arm \
-M virt \
-m 512M \
-nographic \
-bios u-boot.bin
但是随即发现并无任何输出,于是上网查阅资料后得知:新版QEMU的vexpress-a9与新版U-Boot的兼容性很差,有各种各样乱七八糟的问题,网上推荐我使用virt虚拟开发板,来平替之前的vexpress-a9。
输入以下命令,重新xuanzeU-Boot对应开发板,并且编译:
cpp
ls configs/qemu_arm_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- qemu_arm_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc)

重新用以下新版命令,启动QEMU的U-Boot:
cpp
qemu-system-arm \
-M virt,secure=off \
-m 512M \
-serial stdio \
-bios u-boot.bin \
-net none

(5)现在的QEMU启动命令与之前有什么差异?
比如我们之前有遇到过GPIO子系统无法正常开启,极有可能就是这个启动命令太过简略,跳过了U-Boot的一系列初始化工作导致的,而且我们编译了多次内核都无法解决,可见没有U-Boot时有多难排查问题。
前期的 QEMU 启动方式,是「为了跑通系统而跳过核心流程的捷径」;而基于 U-Boot 的启动方式,是「符合行业标准、可排查、可维护、可量产的正规开发流程」。学习嵌入式 Linux,必须彻底抛弃捷径,从 U-Boot 的完整流程入手,这是从「会用」到「懂原理、能开发」的核心转折点。
而现在使用U-Boot是工作中标准的开发流程,之前那种启动QEMU的方式以后坚决不可取!
