331 Android Bootloader:从芯片上电到内核加载

系列导航330-启动总览 | 332-init进程 | 333-Zygote

上篇我们梳理了 Android 启动的完整链路。本篇聚焦第一个软件阶段——Bootloader

Bootloader 是硬件上电后第一个运行的软件代码(固化在 ROM/Flash 中),它负责初始化硬件、加载内核、验证签名。在 Android 设备上,Bootloader 往往是定制最深的组件,也是各手机厂商的”护城河”——锁 bootloader 是保证系统完整性的第一道防线。


Bootloader 的位置

在嵌入式系统(手机、平板、IoT 设备)中,Bootloader 和 PC 的 BIOS/UEFI 扮演类似角色,但有几大区别:

维度 PC BIOS/UEFI 手机 Bootloader
存储位置 主板 SPI Flash SoC 内部 ROM + eMMC/UFS
是否可升级 通常可升级 BL1 不可改,BL2 可升级
职责 枚举硬件、加载 GRUB 初始化 DRAM、加载内核、验证签名
安全 UEFI Secure Boot(可选) 强制签名验证、DM-Verity

手机上电后的内存布局:

Bash
0x00000000 ─┬─ ROM(Boot ROM,芯片内固化,不可修改)
            │   └─ BL1:CPU 芯片出厂时烧录,CPU 复位后首先执行这段代码
            │
0x00000000 ─┴─ SRAM(芯片内部高速内存,容量小,用于临时数据)
            │
0x10000000 ─┬─ DRAM(外部内存,需 Bootloader 初始化后才能使用)
            │   └─ 加载 Kernel + ramdisk(initramfs)
            │
0x00000000 ─┬─ eMMC/UFS(外部存储)
                └─ BL2:可升级的引导程序(LK/ABOOT/U-Boot)

Bootloader 的两阶段结构

Android 设备的 Bootloader 通常采用两阶段(甚至三阶段)设计:

第一阶段:BL1(Boot ROM)

CPU 复位后执行的第一条指令。固化在芯片内部。

Bash
CPU Reset → Boot ROM(BL1)
    → 初始化最少硬件(MMU/cache/Stack)
    → 从存储介质加载 BL2 到 SRAM
    → 跳转到 BL2

BL1 的职责极度精简——它只需要能把 BL2 从持久存储加载到内存。因为此时 DRAM 可能还没初始化,很多芯片只能用 SRAM。

第二阶段:BL2(Little Kernel / ABOOT / U-Boot)

BL2 是真正功能完整的引导程序,可以定制升级。

高通设备常用 LK(Little Kernel)

Bash
LK(BL2)
    → 初始化 DRAM
    → 解析 boot 分区(kernel + ramdisk)
    → 签名验证
    → 加载 kernel 到 DRAM
    → 跳转执行 kernel

展讯/联发科设备常用 U-Boot

Bash
U-Boot(BL2)
    → 初始化 DRAM / NAND Flash
    → 设置启动参数(ATAG/DTB)
    → 加载 kernel
    → bootz 或 bootm

Bootloader 详细工作流程

Bash
┌─────────────────────────────────────────────────────────────┐
│                    芯片上电 / 复位                          │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  Boot ROM(BL1,芯片内固化)                                 │
│    ・关闭中断、设置栈                                         │
│    ・初始化 MMU、指令/数据缓存                                │
│    ・从存储介质(eMMC/UFS)特定地址加载 BL2 到 SRAM           │
│    ・验证 BL2 签名(可选,如果开启了 secure boot)            │
│    ・跳转到 BL2 入口                                          │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  Bootloader Stage 2(BL2:LK / ABOOT / U-Boot)             │
│    ・初始化 DRAM 控制器                                       │
│    ・配置时钟树(PLL)、电源管理                               │
│    ・初始化显示(early console,可打印 logo)                   │
│    ・解析 boot 分区(kernel + ramdisk)                       │
│    ・验证 boot.img 签名(Image signatures)                   │
│    ・验证 dtb(Device Tree Blob)                            │
│    ・加载 kernel 到 DRAM(0x00080000 或 0x40080000)          │
│    ・加载 ramdisk 到 DRAM                                     │
│    ・配置 ATAG 或 DTB 传递给内核的参数                         │
│    ・跳转执行 kernel                                          │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  Linux Kernel                                              │
│    ・解压内核(如果内核是压缩格式)                            │
│    ・初始化内核子系统                                         │
│    ・执行 /sbin/init(PID 1)                               │
└─────────────────────────────────────────────────────────────┘

Boot.img 的结构

Android 的 kernel 和 ramdisk 不是独立文件,而是打包进一个 boot.img 镜像:

Bash
boot.img 结构:
┌────────────────┬────────────────┬────────────────┬─────────────┐
│   kernel       │   ramdisk      │   second stage │   dtb       │
│   (压缩内核)   │   (cpio归档)   │   (可选)       │ (设备树)    │
└────────────────┴────────────────┴────────────────┴─────────────┘
         ↑                ↑                  ↑              ↑
      内核镜像          根文件系统         备用内核        硬件描述

boot.img 的头部(boot header)包含元数据:

Bash
struct boot_img_hdr {
    uint8_t  magic[8];           // "ANDROID!"
    uint32_t kernel_size;        // kernel 大小
    uint32_t kernel_addr;        // 内核加载地址
    uint32_t ramdisk_size;       // ramdisk 大小
    uint32_t ramdisk_addr;       // ramdisk 加载地址
    uint32_t second_size;        // second stage 大小
    uint32_t second_addr;        // second stage 加载地址
    uint32_t tags_addr;          // ATAG/DTB 地址
    uint32_t page_size;          // Flash 页大小(通常 20484096)
    uint32_t flags;               // 标志位(签名验证等)
    uint32_t cmdline[10];        // 内核命令行
    uint32_t id[8];              // SHA1 镜像 ID
};

kernel_addr 和 ramdisk_addr 是关键——这两个地址是 Bootloader 加载内核和 ramdisk 的物理地址。如果地址写错,内核会跑飞。


签名验证原理

Bootloader 是 Android 安全的核心环节。一个锁了 bootloader 的设备,意味着:

  1. BL1 不可修改(固化在芯片 ROM)
  2. BL2 必须是厂商签名(无法自行编译 U-Boot/LK 刷入)
  3. boot.img 必须是官方签名(无法用自编译内核替代)

签名验证流程

Bash
BL2 检查 boot.img 头部的 signature
        ↓
用公钥验证 kernel + ramdisk 的 HMAC/SHA256
        ↓
验证通过 → 加载 kernel + ramdisk
验证失败 → 进入 fastboot 模式或拒绝启动

AVB(Android Verified Boot)2.0

Android 8.0+ 引入 AVB2.0 作为统一的验证启动框架:

Bash
AVB 2.0 验证链:
┌───────────────────────────────────────────────────────────┐
│  Bootloader(AVB 验证启动)                                 │
│    → 读取 AVB footer(分区末尾的元数据)                   │
│    → 读取 vbmeta 分区(包含哈希树/公钥)                   │
│    → 逐级验证各分区:system/vendor/product                 │
│    → 验证失败 → 强制进入 recovery 或停机                   │
└───────────────────────────────────────────────────────────┘

AVB2.0 相比旧的 DM-Verity 的进步:支持 rollback protection(防止降级攻击)和 unlocked boot loader 检测。


fastboot 协议

当 Bootloader 检测到特殊按键组合(音量下 + 电源键),会进入 fastboot 模式。这是 Bootloader 提供的 USB 刷机协议:

Bash
# 进入 fastboot 模式后,PC 端可以用 fastboot 工具
fastboot devices
# 列出 fastboot 设备

fastboot flash boot boot.img
# 刷写 boot 分区

fastboot flash system system.img
# 刷写 system 分区

fastboot oem unlock
# 解锁 bootloader(会清除数据)

fastboot flashing unlock_critical
# 解锁关键分区(刷机用)

fastboot erase userdata
# 擦除用户数据

fastboot 的本质:Bootloader 提供一个简单的 USB bulk 传输协议,接收 PC 发来的命令和数据。设备处于 fastboot 模式时,BL2 在跑,但内核还没加载。

一个重要区别:edify(recovery 的脚本引擎)可以解析 update.zip 里的签名,但 fastboot 直接烧写裸数据,没有脚本层。这影响了线刷包的格式。


高通 Bootloader:EL3 / EL2 / EL1

高通的 Bootloader 还有一个特殊之处——它运行在 ARM Exception Level 的不同层级:

Bash
EL3(Secure Monitor)→ BL1(boot ROM)的一部分,运行在安全世界
EL2(Hypervisor)     → 可选,SMMU 配置
EL1(Kernel)         → 正常的内核态

高通的安全设计:

Bash
EL3: QSEE(Qualcomm Secure Environment)
    → 处理 DRM、密钥存储、指纹/人脸生物数据

EL2: hypervisor 分区隔离

EL1: 非安全世界(Normal World)
    → Linux kernel + Android

这意味着即使 Linux 内核被攻破,EL3 的安全世界仍然是隔离的。这和苹果的 SEP(Secure Enclave Processor)是类似思路。


U-Boot 命令实践

如果你在嵌入式开发板(而非手机)上工作,很可能遇到 U-Boot。以下是常用命令:

Bash
# 查看环境变量
printenv

# 设置启动参数
setenv bootcmd 'mmc dev 1; ext4load mmc 1:2 0x40080000 /boot/zImage; bootz 0x40080000'
setenv bootargs 'console=tty0 console=ttyS0,115200 root=/dev/mmcblk0p2'

# 保存环境变量
saveenv

# 从网络加载内核(开发阶段有用)
tftp 0x40080000 zImage

# 打印启动信息
bdinfo
printenv bootargs

和 Windows Boot 对比

阶段 Android Bootloader Windows Boot
固件 Boot ROM(芯片内固化) BIOS/UEFI(主板 SPI)
引导程序 LK/ABOOT/U-Boot winload.exe/bootmgfw.efi
签名验证 AVB2.0 + 厂商密钥 UEFI Secure Boot(PK/KEK/DB)
启动参数 ATAG/DTB → kernel cmdline BCD(Boot Configuration Data)
刷机协议 fastboot / edify (recovery) DISM / WIM / recovery

Windows 的 Secure Boot 原理和 Android AVB 类似,都是用公钥验证下一阶段代码。但 Windows 的 UEFI 证书存储在 NVRAM,而 Android 的 AVB 公钥存在 vbmeta 分区。


Wandos 对比

Wandos 实现了简化的 PE loader 来模拟 Windows 启动过程:

Bash
BIOS → MBR → wandos bootloader (PE loader)
                          ↓
                    自研 kernel
                          ↓
                    自研 init

Wandos 的 Bootloader 比 Android 的简单得多:

  • 不需要签名验证(教学目的)
  • 不需要多阶段设计
  • 不需要处理 DRM/密钥

这让我们可以把注意力集中在”加载内核并跳转执行”这个核心逻辑上,而不是安全基础设施。


动手环节

1. 进入 Bootloader 模式

不同设备的进入方式不同:

Bash
# 高通设备:关机后按 音量下 + 电源键
# 华为设备:音量下 + 电源键(部分型号是音量上)
# 通用方法:adb reboot bootloader
adb reboot bootloader

2. 查看 fastboot 设备

Bash
fastboot devices
# 列出 fastboot 模式下的设备

fastboot oem device-info
# 查看设备解锁状态(高通设备)

3. 解锁 Bootloader(会清除数据!)

Bash
# 高通设备
fastboot oem unlock

# 华为设备(需要申请解锁码)
fastboot oem unlock <unlock_code>

4. 提取 boot.img 并查看内容

Bash
# 从设备提取 boot 分区
adb shell dd if=/dev/block/bootdevice/by-name/boot of=/sdcard/boot.img
adb pull /sdcard/boot.img

# 用 binwalk 分析 boot.img 结构
binwalk boot.img
# 显示各段(kernel/ramdisk/dtb)的偏移量

# 提取 ramdisk
mkdir boot_parts
cd boot_parts
dd if=../boot.img bs=1 skip=$((0x800)) count=$((0x100000)) of=ramdisk.cpio.gz

系列导航


附录:常见 Bootloader 错误及排查

症状 原因 解决
Bootloader 卡住不动 BL2 损坏或签名验证失败 进入 fastboot 重新刷写 boot
fastboot 无法识别设备 USB 驱动未安装 安装高通/MTK USB 驱动
FAILED (remote: invalid magic) boot.img 损坏 重新提取或从 ROM 包重新打包
FAILED (remote: signature verify fail) 签名验证失败(BL 已锁) 解锁 bootloader 或使用官方 boot.img
循环重启进入 fastboot 内核 panic 或 bootreason 错误 查看 adb shell dmesgadb logcat

作者:公众号「解码日记」,专注于操作系统、虚拟机、内核级技术深度解析。每周三更新。

最后修改: 2024年5月23日

作者

评论

发表评论

您的邮箱地址不会被公开。