Linux嵌入式x86/ARM中的Bootloader基本概念与启动流程解析
1 概述引导加载程序Bootloader 是嵌入式系统与计算机系统上电启动过程中最先运行的可定制代码段其核心职责包含多个固定执行环节首先完成处理器与核心外设的基础硬件初始化操作包括时钟树配置、供电模式设定、外部存储器接口初始化等系统运行必需的底层配置。随后从指定的启动源中加载核心的可执行二进制文件这类文件通常为操作系统内核镜像启动源包括flash、SD 卡、eMMC、以太网网络或 USB 存储设备等非易失性存储载体。对于经过压缩处理的内核镜像引导加载程序需要在加载完成后执行解压缩操作将镜像恢复为可执行状态。最终将系统执行权转交给解压完成的内核镜像完成系统启动的引导环节。在基础启动功能之外当前主流的引导加载程序均会提供交互式操作界面包括命令行 Shell 与图形化菜单两种形式。通过该界面可以手动选择待启动的操作系统镜像、调整启动参数、从存储或网络中读取与写入数据、检测物理内存状态、执行硬件外设诊断与寄存器读写操作。如有专业词汇翻译不当、内容理解有误的地方还望指正。2 x86 平台启动流程2.1 传统 BIOS 启动2005 至 2006 年之前设计生产的 x86 架构计算机平台均采用 BIOS 作为系统固件BIOS 全称 Basic Input Output System即基本输入输出系统。BIOS属于硬件平台的固有组成部分由主板厂商采用闭源方式开发用户无法进行源码级修改仅能通过有限的选项进行参数调整。BIOS 的核心功能是实现硬件上电启动流程同时提供一组运行时服务接口但这类接口在现代操作系统中几乎不会被调用。BIOS镜像常见于存储在主板独立的闪存芯片中与用户可访问的硬盘、U 盘等存储设备相互隔离不存在物理地址重叠。在 x86 传统启动规范中具备可引导属性的存储设备其物理地址的第一个扇区被定义为主引导记录即 MBRMaster Boot Record。该扇区内部包含三个关键部分分别是磁盘分区表、磁盘签名信息以及引导加载程序可执行代码。BIOS 完成硬件自检与内存初始化后会自动读取该扇区数据并将其中的引导代码加载至内存指定位置执行。受限于 MBR 中引导代码的字节长度限制无法存放完整功能的引导加载程序因此 x86 平台的引导程序必须采用两级加载结构第一阶段引导程序即 Stage1体积严格控制在限制字节以内仅实现存储介质识别与第二阶段引导程序加载功能。第二阶段引导程序即 Stage2由 Stage1 从存储设备的固定地址加载执行该程序体积不受严格限制具备完整的文件系统解析能力可以直接从文件系统中读取内核镜像与启动配置文件。2.1.1 启动顺序与存储布局传统 BIOS 启动遵循固定的硬件执行流程第一步由主板上的 BIOS 固件从内置闪存中运行执行硬件自检并完成物理内存的初始化配置确保内存可正常读写。第二步从可引导存储设备的 MBR 区域加载 Stage1 引导程序至内存处理器跳转执行该段代码。第三步由 Stage1 程序加载存储设备中预设地址的 Stage2 引导程序该程序运行后提供完整的交互与文件系统操作能力。第四步 Stage2 引导程序从文件系统中读取 Linux 内核镜像至内存完成启动参数传递后跳转执行内核代码。在存储设备物理布局上0 号扇区即 MBR 扇区存放分区表与 Stage1 引导代码该区域不归属任何文件系统管理。后续的连续物理扇区用于存放 Stage2 引导程序同样不纳入文件系统管理范围。磁盘分区完成后每个分区内部可存放 Linux 内核镜像、根文件系统与系统配置数据形成完整的可启动系统结构。2.2 UEFI 启动2005 至 2006 年之后x86 平台逐步采用 UEFI 替代传统 BIOSUEFI 全称 Unified Extensible Firmware Interface即统一可扩展固件接口。UEFI 重新定义了操作系统与系统固件之间的交互规范不仅负责硬件启动与初始化还为操作系统提供运行时服务、电源管理与硬件配置接口。UEFI 固件同样存储在主板独立闪存中与用户存储设备相互隔离具备模块化与可扩展的设计特性。UEFI 启动流程不再依赖 MBR 扇区而是采用专用的 EFI 系统分区该分区为独立的物理分区采用 FAT32 文件系统格式。在 MBR 分区表中该分区类型标识为 0xEF在 GPT 分区表中使用专属的全局唯一标识符进行区分。UEFI 固件会自动扫描磁盘中的 EFI 系统分区加载分区内预设路径的 EFI 格式可执行文件该文件可以是完整的引导加载程序也可以是内置 EFI 启动支持的 Linux 内核镜像。2.2.1 启动顺序与存储布局UEFI 启动的第一步为固件从主板闪存运行完成硬件初始化与内存配置同时枚举所有存储设备。第二步固件识别 EFI 系统分区解析 FAT32 文件系统并加载指定路径的 EFI 可执行文件该文件通常为 GRUB 等引导加载程序。第三步引导加载程序运行后从普通分区的文件系统中读取 Linux 内核镜像与初始内存文件系统完成参数配置后启动内核。在存储布局上EFI 系统分区为独立的物理分区仅存放引导程序与启动配置不存储系统与用户数据。其余的普通数据分区用于存放 Linux 内核、根文件系统、应用程序与用户文件各分区之间物理隔离文件系统格式可自由选择。2.3 ACPIACPI 全称 Advanced Configuration and Power Interface即高级配置与电源接口是一项跨平台的开放行业标准。该标准为操作系统提供统一的硬件管理接口支持硬件设备的自动发现、资源分配、运行状态监测、功耗控制与热插拔管理。ACPI 的核心实现方式为固件向操作系统提供一组固定格式的描述表这些表中记录了硬件资源布局、电源管理策略、设备中断映射等信息这类信息无法在运行时通过硬件枚举动态获取。在 x86 平台中无论是传统 BIOS 还是 UEFI 固件均会提供 ACPI 描述表Linux 内核启动时会自动解析 ACPI 表完成硬件资源管理与电源策略配置。在嵌入式 ARM 平台中ACPI 仅用于服务器级别的处理器嵌入式消费类设备极少采用该标准。2.4 ARM 平台的 UEFI 与 ACPIUEFI 与 ACPI 均起源于 x86 计算机体系ARM 联盟为了统一服务器与高端设备的启动规范将这两项技术纳入 ARM System Ready 认证体系。该认证仅面向服务器、工业控制主机与高端工作站级别的 ARM SoC要求固件提供完整的 UEFI 交互界面与 ACPI 硬件描述能力。在嵌入式 ARM Linux 系统中例如消费电子、工控设备、开发板等产品形态UEFI 与 ACPI 均不具备实用性行业普遍采用 U-Boot 与设备树的组合方案。RISC-V 架构的 UEFI 支持仍处于社区开发阶段尚未形成稳定的行业标准因此嵌入式 RISC-V 平台同样以 U-Boot 作为默认引导加载程序。对于采用 UEFI 的嵌入式平台其启动流程、分区规范与文件系统要求均与 x86 平台保持完全一致。3 嵌入式平台启动流程3.1 ROM 代码嵌入式处理器在芯片生产阶段会内置一段固化代码即 ROM code该代码是处理器上电后执行的第一段程序属于芯片硬件的固有组成部分用户无法进行任何修改、擦除或升级操作。ROM code 的执行逻辑与启动流程由芯片厂商在数据手册中明确规定启动源选择、加载地址与执行顺序均为固定配置。ROM code 的核心功能是自动枚举预设的启动设备列表依次检测是否存在有效的引导加载程序检测范围包括 NAND Flash、NOR Flash、SD 卡、eMMC、USB 设备与以太网等。由于处理器外部的 DDR 内存尚未完成初始化ROM code 只能将引导加载程序加载至芯片内置的 SRAM 中运行而内置 SRAM 的容量通常仅为几十 KB这一限制直接决定了第一阶段引导加载程序必须保持极小体积。基于上述硬件约束嵌入式平台的启动流程必须划分为两个阶段第一阶段引导程序用于初始化外部 DDR第二阶段引导程序运行于 DDR 中提供完整的启动功能。3.2 ROM 代码恢复机制绝大多数嵌入式处理器的 ROM code 均内置系统恢复功能该机制用于处理引导加载程序丢失、损坏或启动失败的场景。恢复模式通过特定的硬件引脚组合触发进入模式后ROM code 会开启 UART 或 USB 通信接口接收主机端发送的引导加载程序固件直接加载至内存并执行实现系统重新烧录。不同厂商的处理器有对应的专用烧录工具STM32MP1 系列使用 STM32 Cube ProgrammerNXP i.MX 系列使用 uuu 工具Microchip AT91/SAM 系列使用 SAM-BA全志系列使用 sunxi-fel 工具。这类工具部分为开源软件部分为闭源专有工具使用方式与通信协议互不兼容。3.3 两级启动流程嵌入式平台的两级启动为固定执行流程所有 ARM 与 RISC-V 嵌入式处理器均遵循该规范。第一步由 ROM code 从选定的启动设备中读取第一阶段引导加载程序加载至芯片内置 SRAM 并开始执行。第二步第一阶段引导程序运行后执行 DDR 控制器寄存器配置、时序调节与内存检测完成外部 DDR 内存的完整初始化使系统具备大容量内存运行能力。第一阶段引导程序从启动设备中读取体积更大的第二阶段引导加载程序加载至已初始化完成的 DDR 内存中随后跳转执行该程序由第二阶段引导程序完成后续的内核加载与系统启动。4 常见引导加载程序简介4.1 GRUBGRUB 全称 Grand Unified Bootloader是 GNU 项目开发的通用引导加载程序也是所有桌面与服务器版 Linux 发行版在 x86 平台上的默认引导方案。GRUB 同时兼容传统 BIOS 与 UEFI 两种固件环境支持 Ext4、XFS、Btrfs、FAT 等主流文件系统提供图形化启动菜单与命令行交互 Shell支持通过以太网使用 TFTP 协议远程加载内核镜像。GRUB 具备跨架构支持能力可用于 ARM、ARM64、RISC-V、PowerPC 等平台但在嵌入式领域其体积、启动速度与配置复杂度均不适合资源受限的设备因此嵌入式 Linux 系统几乎不会采用 GRUB而是使用更轻量的 U-Boot。4.2 SyslinuxSyslinux 是一款专注于可移动介质与简易启动场景的引导加载程序套件由多个独立组件构成分别对应不同的启动介质。syslinux 组件用于从 FAT 文件系统的 U 盘与 SD 卡启动pxelinux 组件用于网络 PXE 启动isolinux 组件用于光盘启动extlinux 组件支持从 Ext 系列文件系统启动。Syslinux 的配置文件格式简洁编译与部署流程简单适合用于制作启动盘与恢复系统。但该项目的维护活跃度较低硬件支持范围有限不具备嵌入式系统所需的驱动扩展能力因此仅用于辅助启动场景不作为产品级引导加载程序使用。4.3 systemd-bootsystemd-boot 是一款轻量级 UEFI 引导管理器属于 systemd 系统服务套件的独立组件与 systemd init 系统无运行时依赖。该引导程序仅支持 UEFI 环境设计目标为极简启动流程所有配置参数均存放于 EFI 系统分区的文本文件中无需额外的文件系统解析模块。systemd-boot 的体积小、启动速度快适合 UEFI 平台的极简启动需求但功能高度精简不支持内核参数动态编辑、网络启动与复杂存储介质识别仅作为 GRUB 的轻量化替代方案在嵌入式领域使用范围有限。4.4 shimshim 是专为安全启动 Secure Boot 设计的最小化 UEFI 引导加载程序其核心作用是通过微软官方签名认证使非 Windows 操作系统可在开启安全启动的 UEFI 平台上运行。shim 自身不提供内核加载功能仅用于验签并链式加载 GRUB 或 Linux 内核确保启动链中的每一个组件均具备合法签名。在嵌入式平台中安全启动功能通常由芯片厂商与 TF-A 可信固件实现shim 多用于 PC 与服务器环境嵌入式 Linux 产品极少使用该组件。4.5 U-BootU-Boot 全称 Universal Bootloader是 ARM、ARM64、RISC-V、PowerPC、MIPS 等全部嵌入式架构的事实标准引导加载程序同时支持搭载 UEFI 固件的 x86 平台。所有嵌入式 SoC 厂商、核心板模块厂商与开发板厂商均将 U-Boot 作为默认的引导解决方案是嵌入式 Linux 系统开发中必备的核心组件。U-Boot 采用 GPLv2 开源协议源码托管于 denx 社区的 Git 仓库社区通过公开邮件列表进行代码提交与技术讨论项目保持稳定的发布节奏每 2 至 3 个月发布一个正式版本版本号采用年份加月份的格式例如 2024.07。The U-Boot Archiveslists.denx.de/pipermail/u-boot/4.5.1 源码获取U-Boot 源码的获取优先级明确划分最优选择为官方上游主线版本即直接从 denx 社区仓库获取的代码该版本经过全球开发者的审核与测试硬件支持规范、驱动质量稳定、可长期同步更新。次优选择为芯片厂商或主板厂商提供的分叉版本这类版本通常基于旧版主线修改仅适配特定硬件不兼容主线更新代码质量与安全性无法保证。对于自主设计硬件产品的场景需要基于上游主线 U-Boot 进行移植适配仅当厂商硬件在主线暂无支持时才可临时使用厂商分叉版本作为过渡方案。4.5.2 配置U-Boot 采用与 Linux 内核完全一致的 kconfig 配置系统所有可配置项以菜单形式呈现。源码目录下的 configs 文件夹存放所有官方支持的开发板默认配置文件单个配置文件可适配同一系列、同一型号 SoC 的多款硬件产品。配置项包含处理器架构、外设驱动、命令集、网络功能、文件系统支持、启动策略等全部运行参数。U-Boot 的标准配置流程分为两步首先加载开发板对应的默认配置执行 make 命令后接开发板配置名称与 defconfig 后缀生成初始配置文件。随后执行 make menuconfig 命令打开字符配置界面根据硬件需求与产品功能对驱动、命令与功能模块进行自定义开启或关闭。4.5.3 编译U-Boot 必须采用交叉编译方式生成可执行镜像编译前需要将交叉编译器路径加入系统环境变量并通过 CROSS_COMPILE 变量指定交叉编译器前缀。完整编译命令为在加载配置后执行 make 命令并传入 CROSS_COMPILE 参数编译过程会自动完成链接、打包与镜像生成。编译输出的核心文件为 u-boot.bin这是 U-Boot 的原始二进制镜像。4.5.4 U-Boot SPLSPL 全称 Secondary Program Loader即第二阶段程序加载器是 U-Boot 的精简裁剪版本。SPL 的设计目标是满足嵌入式处理器的第一阶段引导程序体积限制代码仅保留 DDR 初始化与第二阶段镜像加载功能不包含任何交互命令、文件系统与网络模块所有执行逻辑通过 C 代码固化实现。部分复杂硬件平台需要三级启动流程即在 SPL 之前增加更小体积的 TPL即 Tertiary Program Loader用于初始化最基础的时钟与供电再加载 SPL 执行。三级启动多用于多核异构、安全启动的高端嵌入式平台。4.5.5 U-Boot 中的设备树设备树 Device Tree 是用于描述硬件拓扑结构的数据结构最初由 PowerPC 平台使用现已成为全部嵌入式架构的硬件描述标准。U-Boot 在绝大多数硬件平台上均依赖设备树完成硬件识别与驱动加载。自 2024.07 版本开始U-Boot 的设备树文件路径由 CONFIG_OF_UPSTREAM 配置项控制开启该选项时设备树存放于 dts/upstream/src 目录下对应架构与厂商的子目录未开启则存放于 arch/架构名/dts 目录。每一款硬件开发板均对应独立的设备树源文件 dts编译时可通过 DEVICE_TREE 变量指定使用的设备树覆盖默认配置。4.5.6 安装U-Boot 的安装部署方式由启动介质类型决定。对于 SD 卡、U 盘等外部可移动存储直接将编译生成的镜像写入存储设备的指定物理偏移地址即可。对于 eMMC、NAND Flash 等板载存储设备可使用烧录工具或厂商专有工具完成烧录。对于具备 JTAG 接口的硬件平台可通过 JTAG 调试器直接写入闪存但该方法流程复杂、操作成本高仅在无其他烧录方式时使用。4.5.7 Shell 与命令U-Boot 通过串口控制台提供独立的命令行交互 Shell该 Shell 的命令集与 Linux 系统完全无关所有命令均为 U-Boot 内部实现。直接输入 help 命令可列出当前编译配置下所有可用的命令列表输入 help 后接具体命令名称可查看该命令的详细用法、参数格式与使用示例。U-Boot 提供一组硬件信息查询命令用于查看系统状态version 命令显示当前 U-Boot 的版本号、编译时间与交叉编译器信息nand info 命令显示 NAND Flash 的容量、块大小、页大小等物理参数mmc info 命令显示 MMC/eMMC 设备的厂商、容量、总线宽度与工作模式bdinfo 命令显示内存起始地址、内存大小、波特率、重定位地址等核心硬件参数。此外还有很多相关命令比如操作SPI 、I2C、WDT控制器的命令不再一一列举。4.5.8 环境变量U-Boot 环境变量是运行时配置的核心载体以键值对形式存储部分变量用于控制命令执行逻辑与启动策略部分变量可由用户自定义用于存储启动脚本与参数。环境变量默认编译至 U-Boot 二进制文件中运行时加载至内存修改操作仅在内存生效断电后丢失需要执行保存命令写入非易失性存储后才可持久化。环境变量的持久化存储位置可通过编译配置指定支持存放在 NAND Flash 的固定偏移地址、MMC 设备的固定偏移地址、FAT 或 ext4 文件系统中的特定文件也可配置为不进行持久化存储仅使用编译内置的默认变量。环境变量的操作命令包含五类printenv 命令用于打印全部变量或指定变量的值setenv 命令用于设置或新建变量仅在内存生效editenv 命令用于交互式编辑变量内容saveenv 命令用于将内存中的环境变量写入持久化存储env 命令包含一组子命令支持恢复默认配置、查询存储状态、擦除环境变量等操作。4.5.9 内存与存储操作U-Boot 直接使用物理地址进行内存读写操作因为此时内核还未启动、MMU 未开启从而不存在虚拟地址映射。U-Boot 未提供内置的内存分配机制开发者需要通过 bdinfo 命令获取内存起始地址与总大小手动避开 U-Boot 自身代码与数据占用的内存尾部区域选择可用的物理地址段进行数据加载与存储。U-Boot 的内存操作命令包含三类md命令用于按字节、半字、字、双字格式显示内存区域内容mw命令用于向指定物理地址写入数据mm命令用于从指定地址开始交互式逐地址修改内存。4.5.10 网络功能U-Boot 的网络功能依赖三个核心环境变量ethaddr指定硬件网卡的 MAC 地址ipaddr设置开发板自身的静态 IP 地址serverip设置远程 TFTP 服务器的 IP 地址。U-Boot 提供基础的网络命令ping 命令用于测试与远程主机的网络连通性受限于单线程执行机制仅支持 U-Boot 主动 ping 外部主机外部主机无法 ping 通 U-Boot。dhcp 命令通过 DHCP 协议自动获取 IP 地址、子网掩码、网关与 DNS 地址。tftp 命令为核心网络加载命令通过 TFTP 协议从服务器下载文件至指定内存地址。4.5.11 脚本与内核启动U-Boot 环境变量支持存储可执行脚本多个命令之间使用分号分隔支持 if-then-else 条件判断结构可引用其他环境变量作为参数。脚本编写完成后使用 run 命令后接变量名即可直接执行常用于自动化启动流程、批量烧录镜像与硬件自动化测试。U-Boot 提供架构专属的内核启动命令ARM32 平台常常使用bootz命令启动压缩格式的 zImage 镜像ARM64 与 RISC-V 平台常常使用booti命令启动未压缩的 Image 镜像传统兼容模式使用bootm命令启动带 U-Boot 专用头的内核镜像x86 平台使用zboot命令启动压缩格式的 bzImage 镜像。启动命令的标准格式为内核启动命令后接内核镜像地址、initramfs 地址、设备树地址三个参数依次排列无 initramfs 时使用短横线占位。控制启动行为的两个核心环境变量为 bootcmd 与 bootargsbootcmd 存储自动启动执行的命令序列倒计时结束后自动运行bootargs 存储传递给 Linux 内核的命令行参数。4.6 BareboxBarebox 是一款面向嵌入式架构的开源引导加载程序设计初衷为解决 U-Boot 的历史技术债务与设计缺陷。Barebox 采用与 Linux 内核完全一致的 kconfig 配置系统内部实现规范化的设备模型命令行 Shell 与 Linux 系统保持高度相似代码结构简洁模块化社区维护活跃度较高。Barebox 支持 ARM/ARM64、MIPS、PowerPC、RISC-V、x86 等主流架构驱动框架与设备树集成方案优于早期版本的 U-Boot。但由于 U-Boot 已形成完整的行业生态芯片厂商与硬件厂商的默认支持均以 U-Boot 为主Barebox 的硬件支持广度、社区资料与工具链成熟度均低于 U-Boot因此仅在特定专用设备中使用。Bareboxwww.barebox.org/

相关新闻