Android Boot Loader
简称ABL,是Android设备的第一阶段引导程序。它的主要作用是:
- 初始化硬件设备,设置内存等
- 加载并校验Android Boot Image(boot.img)
- 传递控制权给boot.img继续下一阶段的引导
ABL由设备厂商提供,需要适配具体的硬件平台。它使用专用的二进制格式,而不是标准的elf格式。
关于这几个概念,我们可以总结如下:
- elf格式:一种常见的二进制可执行文件格式,定义了文件内容和布局。gcc编译的程序默认使用此格式。
- Android Boot Loader(ABL):Android设备的第一阶段引导程序,由设备厂商提供,用于初始化硬件和加载boot.img。使用专有的二进制格式,而不是elf。
- boot.img:Android的第二阶段引导程序,包含Linux内核和ramdisk。ABL将其加载并执行,引导进入Linux系统。
- gcc:Linux下常用的编译器,生成的可执行程序默认为elf格式。
所以,elf格式定义了标准的二进制文件布局和执行信息;ABL作为 Android启动的第一步,使用专有格式执行硬件初始化和加载boot.img;boot.img然后引导Linux系统启动;gcc生成的程序为elf格式。
ABL代码示例(伪代码):
c
void abl_start(void)
{
/* Initialize hardware */
init_mem();
init_devices();
/* Load and verify boot.img */
bootimg = read_boot_partition();
if(!verify_bootimg(bootimg)) {
error();
}
/* Boot into boot.img */
jump_to_kernel(bootimg->kernel_addr, bootimg->ramdisk_addr,
bootimg->kernel_cmdline);
}
├── boot.img
│ ├── kernel (Linux内核)
│ ├── ramdisk (用于根文件系统)
│ ├── kernel_cmdline (内核启动参数)
│ ├── recovery_dtbo (Recovery设备树覆盖)
│ └── header
│ ├── kernel_size
│ ├── kernel_addr
│ ├── ramdisk_size
│ ├── ramdisk_addr
│ ├── second_size
│ ├── second_addr
│ ├── tags_addr
│ └── page_size
机器首先要启动,CPU 最先执行的一段程序就是 BootLoader,这和电脑上的BIOS是一个玩意儿。BootLoader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。在嵌入式系统中,通常并没有像BIOS那样的固件程序(注,有的嵌入式CPU也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由BootLoader来完成。比如在一个基于ARM7TDMI core的嵌入式系统中,系统在上电或复位时通常都从地址0x00000000处开始执行,而在这个地址处安排的通常就是系统的BootLoader程序。
BootLoader既然要做硬件初始化之类的,必然和硬件相关,所以它的代码并非通用的,不同的硬件需要不同的BootLoader代码,各大厂商可能都有自己的,并且加入开机画面之类的。最常听说的是uboot。我们常说的刷机,是不会动bootloader的,只会动这之后的系统部分。
BootLoader的主要功能是:
- 初始化硬件设备 - 初始化CPU、内存控制器、时钟等硬件设备。
- 加载操作系统内核 - 从存储设备(硬盘、NAND Flash等)读取操作系统内核并加载到内存。
- 转交控制权给内核 - 将控制权转交给操作系统内核,BootLoader的工作至此完成。
所以,简而言之,BootLoader是连接系统上电与操作系统内核启动之间的重要一环。它屏蔽了底层硬件差异,为操作系统提供统一的加载入口。
BootLoader通常存放在以下几种地方:
- ROM - 集成在主板上的ROM芯片中,内容无法修改。
- Flash - 独立的非易失性Flash芯片中,内容可以通过烧写程序更新。
- 硬盘分区 - 保存在硬盘的特定分区中,内容可以通过工具更新
fastboot:
fastboot,它是bootloader后期进入的一个特殊阶段。可以通过数据线与电脑连接,然后在电脑上执行一些命令,刷系统镜像到手机上。fastboot可以理解为实现了一个简单的通信协议,接收命令并更新镜像文件,其他什么的干不了。
须有一个PC机并且USB线要始终联着。所以这种方式称为线刷。
fastboot需要bootloader的支持,所以不是每家手机都会支持这种模式。
recovery:
如果没有进入fastboot,bootloader继续执行,如果又发现有特殊的按键组合,比如htc g2上是home键和开机键,则会进入recovery模式。分析recovery.img镜像文件就会发现,它里面包含了一个kernel以及一个可执行程序recovery,以及一些初始化文件。从某种意义来说,这就是一个小型操作系统,和正常启动进入的系统的kernel是一样的,只是init及之后干的事情不同。
这里的kernel和我们常说的linux内核还是有差异的,linux内核是包括kernel以及调度器内存管理等,除了显示界面外的完整系统。而kernel只是指内核init进程启动前的那一段逻辑。
在recovery模式下,会加载了部分文件系统,所以才可以读sdcard中的update.zip进行刷机,当然,也可以清除cache和用户数据。
Recovery恢复时,刷机包通常放在SD卡里,所以这里刷机一般称为卡刷。
安卓系统的镜像文件
- abl.elf:
Android Boot Loader,第一阶段的引导程序。用于初始化设备并加载Bootloader。是一个可执行文件,包含启动相关的代码,通常由芯片厂商提供,用于加载 Bootloader。 - boot.img:
Boot Image,第二阶段的引导程序。包含Kernel和Ramdisk,用于加载Linux内核并挂载系统分区。负责启动 Android 系统的核心部分. - dtbo.img:
Device Tree Blob Overlay Image。包含设备树覆盖数据,提供对原设备树的修补和定制。
设备树和其他硬件相关的二进制文件,通常与 Android 版本匹配; - metadata.img:
包含系统分区表和分区信息,用于引导过程中查找正确的分区。文件系统的元数据信息,用于指导 super.img 的创建和管理 - persist.img:
用于存储系统设置等持久化数据的分区镜像。存储设备的数据和状态,包括 WiFi、蓝牙、屏幕亮度等信息,通常不会随着系统重置而被删除 - super.img:
system_root分区镜像,包含操作系统根文件系统和内核。包含系统镜像(system)、供应商镜像(vendor)和产品镜像(product)等内容,以动态分区的形式组装成一个文件 - userdata.img:
用于存储用户数据和应用数据的分区镜像。用户数据分区,包含用户安装的应用程序、配置信息、照片、音乐等个人数据; - vbmeta.img:
Verified Boot Metadata Image。包含设备的Verified Boot信息,用于启动验证过程。针对 Android 9 及以上版本,包含公钥,用于验证系统分区的完整性; - vbmeta_system.img:
system分区对应的Verified Boot Metadata Image。 - vendor_boot.img:
厂商定制的引导镜像,包含硬件相关的内核和驱动。
包含 Android 系统所需的驱动程序和二进制文件,通常用于更新和修复系统。
所以,这些img文件主要用于Android系统的启动过程和分区组织。abl.elf、boot.img和vendor_boot.img参与系统引导;super.img和userdata.img分别提供操作系统和用户数据;persist.img存储设置;vbmeta.img和vbmeta_system.img用于启动验证。
理解这些img文件的作用有助于我们深入研究Android启动机制和文件系统布局。我们会继续深入分析这些组件及其在启动过程中的配合关系。
在AOSP系统中,/vendor分区用于存放设备厂商提供的二进制文件和资源。将可执行程序放入/vendor/bin目录需要遵循以下步骤:
- 添加程序到/vendor分区
先将编译好的程序二进制文件复制到/vendor分区下合适的位置,一般是/vendor/bin目录。 - 添加执行权限
程序文件需要有执行权限,我们可以通过以下命令为其添加:
bash
chmod 755 /path/to/vendor/bin/your_program - 添加到/vendor/etc/init/hw/init.rc 文件
如果程序是一个系统服务,需要在init.rc文件中添加其启动脚本以在系统启动时启动。
例如:
service your_program /vendor/bin/your_program
class main
user system
group system
- 在Android.mk添加LOCAL_MODULE定义
如果程序由设备厂商编译得到,那么必须在编译流程的Android.mk中添加LOCAL_MODULE定义,以指定程序名和最终安装位置。
例如:
makefile
LOCAL_MODULE := your_program
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/bin
- 添加权限(可选)
如果程序需要访问系统权限或硬件资源,需要在/vendor/etc/permissions/* 文件下添加相应权限声明。 - 添加与初始化 (可选)
如果程序是一个系统服务,可能还需要添加初始化脚本文件在/vendor/etc/init/hw/下和Android init配置项。 - 重新编译系统映像
添加完程序和资源后,需要重新编译生成vendor.img和其它系统映像。烧录到设备上后即可使用添加的程序。
所以,将可执行程序添加到AOSP系统的/vendor/bin主要涉及添加必要的文件与权限,定义编译流程,以及完成初始化配置。理解这个过程有助于我们熟练定制AOSP的/vendor映像,在系统中集成第三方二进制程序和驱动。
根文件系统镜像通常会包含大部分系统所需要的二进制程序和文件,但不一定会包含所有的bin程序。这主要有以下几个原因:
- 空间限制
根文件系统的空间是有限的,不可能包含所有的程序,特别是第三方的应用程序。一般只会包含基本的系统工具和服务。 - 权限限制
一些程序需要访问系统低级权限或硬件资源,不适合直接在用户空间运行。这类程序通常作为系统服务在更低级别运行。 - 定制需求
某些bin程序可能由设备厂商自行开发和提供,更适合放在/vendor分区而不是根文件系统。 - 功能划分
基于功能和安全考虑,一些程序被划分到不同的分区运行,如/system分区的程序只能访问Java API等。 - 模块设计
为了实现模块化设计和更好维护,某些功能会被划分到不同的软件包中,这些软件包的程序文件也不会直接在根文件系统。
所以,根文件系统中通常只包含基本的系统工具和服务,而更高级和定制的功能会被划分到不同的分区和软件包中实现。这有助于实现权限隔离、模块化设计以及更好的系统维护。
安卓系统的分区
分区主要包括以下三种
- 通用Android分区
- 高通或其他硬件专有分区
- 定制ROM系统增加的专有分区
通用的Android分区包括:/boot /system /recovery /data /cache /misc
-
boot - 包含Linux内核、ramdisk和设备树,用于初始化系统。Linux嵌入式系统内核,这个分区上有Android的引导启动程序,包括内核和内存操作程序。没有这个分区设备就不能被引导。恢复系统的时候会擦除这个分区,并且必须重新安装引导程序和ROM才能重启系统,由boot.img烧录。包括.bootloader、内核、ramdisk根文件系统。 负责挂载,LinuxKernel,负载system,cache,userdata等。
-
system - 包含系统应用和库,是Android系统核心部分。系统文件、应用,这个分区上是除了内核和内存操作之外的整个操作系统。里面包含了Android用户接口和预先安装的系统应用。擦除了这个分区就会删除掉Andorid系统,所以你需要进入recovery模式或者bootloader模式去安装一个新的ROM。
-
recovery - 包含恢复系统的镜像。系统故障时负责恢复,recovery分区被认为是另一个启动分区,你可以启动设备进入recovery控制台去执行高级的系统恢复和管理操作。
-
userdata - 存储用户数据,如应用数据、下载的文件等。用户使用APP产生的缓存数据,这个分区保存着用户数据。通讯录、短信、设置和你安装的apps都在这个分区上。擦除这个分区相当于恢复出厂设置,当你第一次启动设备的时候或者在安装了官方或者客户的ROM之后系统会自动重建这个分区。当你执行恢复出厂设置时,就是在擦除这个分区。
-
缓存/cache - 存储缓存数据,用于提高系统性能。系统运行时产生的缓存,这个分区是Android系统存储频繁访问的数据和app的地方。擦除这个分区不影响你的个人数据,当你继续使用设备时,被擦除的数据就会自动被创建,一般用来OTA升级进入recovery前,临时存放OTA包升级的存放文件。
-
persist - 持久数据分区,存储系统设置信息。
-
/misc:临时数据分区 ,这个分区包含各种复杂的类似于on/off的系统设置。这些设置可能是USB配置和某些硬件配置信息。这是一个重要的分区,如果该分区损坏或者丢失,设备的功能可能就工作不正常。
-
/sdcard:这个分区不是内部的存储区,而是SD card。这是你个人存储大文件的地方,存储多媒体文件、文档、ROM文件等等。如果你备份了这个分区上文件,那么擦除这个分区将会很安全。不过请注意一些用户安装的apps会存储数据和设置参数到SD card上,擦除这个分区会掉的这些数据。大小根据SD卡容量而定,可热插拔。。
-
/sd-ext:这个分区不是Android设备的标准分区,位于手机存储器上,但是在修改版的自定义ROM的情况下变得很流行。大小在刷机或首次启动时确定,不可热插拔。
而其他一些分区属于具体硬件专有分区,如:
- abl - Qualcomm芯片引导加载程序分区。
- hyp - Qualcomm hypervisorvisor分区。
- dtbo - 设备树 Blob分区,某些高通平台专有。
- vbmeta - 验证元数据分区,高通AVB实现专有。
- super - ext4分区,一些高通平台使用。
- vendor_boot - 第三方供应商boot镜像分区,高通AVB实现专有。
- metadata - 不常见,某些高通平台使用,作用未详。
定制ROM系统增加的专有分区
定制ROM会在Android系统中新增一些专有分区,主要用于存储定制的数据与设置。常见的有:
- META分区 - 用于存储ROM版本信息、ROM定制功能与设置等数据。该分区只能在该ROM下使用,升级其他ROM会格式化该分区。
- CSC分区 - 用于存储ROM国家与carrier相关的个性化定制包。OTA更新会保留该分区数据。
- USERDATA分区 - 用于存储用户数据与应用数据。OTA升级会保留该分区数据。
- CACHE分区 - 用于存储更新包、居 Evolution X 系统更新数据以实现无缝更新。OTA升级会格式化该分区。
- PERSIST分区 - 用于永久存储ROM与device相关的设置。OTA升级会保留该分区数据。
所以,这些专有分区主要用于:
- 存储ROM自身的版本、定制与个性化数据。
- 存储用户数据与设置,可保留避免数据丢失。
- 辅助ROM更新与功能实现。
- 提供ROM专有的存储空间。
定制ROM会根据自身的功能与需求,选择性地增加适当的分区。并非每款ROM都会增加所有的这些分区,部分定制ROM甚至不增加任何专有分区。
这需要基于该ROM的设计与用途进行判断。
在Android系统中,大多数重要分区都会设置主备分区,用于实现备份与恢复的功能。
Android通常设置主备分区的分区有:
- boot - 包含内核与ramdisk,设置为boot_a和boot_b。
- system - 包含系统应用与库,设置为system_a和system_b。
- userdata - 包含用户数据,设置为userdata_a和userdata_b。
- persist- 包含设置信息,设置为persist_a和persist_b。
- recovery - 包含恢复包,设置为recovery_a和recovery_b。
- abl - Qualcomm引导程序,设置为abl_a和abl_b。
- vbmeta - 验证元数据,设置为vbmeta_a和vbmeta_b等。
具体的主备分区数量和名称会根据不同设备和定制系统有所不同,但基本原理相同。
设置主备分区的好处是:
- 防止单点失败:如果主分区数据损坏,可以从备分区恢复,保证系统可用性。
- 方便OTA:可以烧写OTA包到主分区,如果出现问题可以从备分区恢复,然后重新推送OTA。
- A/B测试:可以在主分区和备分区运行不同版本的系统或配置进行测试,选择更稳定的版本使用。
fastboot刷机
fastboot与QFIL是两种对Android设备进行刷机的方法:
fastboot刷机:
- 设备进入fastboot模式,通常通过按住音量键+电源键。
- 使用fastboot工具,给设备下发flash/update命令,烧写对应分区的镜像文件。
- fastboot刷机操作简单,但仅能刷主要分区,且要求设备能正常进入fastboot模式。
fastboot命令烧写:
fastboot flash boot boot.img #这个是刷入boot的命令。
fastboot flash recovery recovery.img #刷入recovery 已有recovery的可以跳过。
fastboot erase boot #擦除boot分区
fastboot erase system -w #擦除system分区 擦除 userdata分区和cache分区
fastboot erase system #擦除system分区
fastboot erase cache #擦除cache分区
fastboot erase userdata #擦除userdata分区
fastboot update update.zip #将update.zip刷入
fastboot reboot #重启手机
path: img所在路径
fastboot flash abl_a path\abl.elf
fastboot flash abl_b path\abl.elf
fastboot flash boot_a path\boot.img
fastboot flash boot_b path\boot.img
fastboot flash dtbo_a path\dtbo.img
fastboot flash dtbo_b path\dtbo.img
fastboot flash metadata path\metadata.img
fastboot flash persist path\persist.img
fastboot flash super path\super.img
fastboot flash userdata path\userdata.img
fastboot flash vbmeta_a path\vbmeta.img
fastboot flash vbmeta_b path\vbmeta.img
fastboot flash vbmeta_system_a path\vbmeta_system.img
fastboot flash vbmeta_system_b path\vbmeta_system.img
fastboot flash vendor_boot_a path\vendor_boot.img
QFIL刷机
QFIL刷机:
- 设备进入EDL模式(紧急下载),通常通过短接EDL引脚或按键组合。
- 使用Qualcomm Flash Image Loader(QFIL)工具,加载令牌、分区表与镜像,进行烧写操作。
- QFIL可以完全重新编程设备,支持更底层分区,但操作较复杂,且要求设备能进入EDL模式。
两种方式对比:
优点:
- fastboot - 操作简单,不需要额外工具。
- QFIL - 支持更底层分区,可以使用还原镜像彻底修复设备。
缺点:
- fastboot - 仅支持fastboot可见的主要分区,无法修复bootloop设备。
- QFIL - 操作复杂,需要额外的QFIL工具与镜像文件。
所以,总结来说:
如果只是常规系统更新,fastboot方式简单高效。
如果系统损坏无法进入系统,需要彻底修复,QFIL方式更强大。