一般为一套镜像,由厂商提供,刷机时会将各个镜像刷入到各自的分区中。
- Boot Loader:启动时由CPU(或芯片中的应用处理器)执行的代码。这些代码一般用来寻找和加载boot镜像;或者固件升级;或者让系统启动到recovery模式下。多数Boot Loader还会实现简单的usb栈,用来提供给用户控制启动以及升级过程(fastboot)。通常被刷入
/aboot
分区。 - boot镜像:由内核和ramdisk组成,用来加载系统。ramdisk用作root文件系统,里面包含系统内目录的基本框架,并在
/init.rc
和相关文件中规定了其它目录的加载方法。通常被刷入/boot
分区。 - recovery镜像:由内核和ramdisk(另一个)组成,用来在启动失败或OTA升级时把系统加载到recovery模式下。通常被刷入
/recovery
分区。 /system
镜像:存放的完整的android系统,包含谷歌提供的可执行文件和框架,还有厂商提供的类似定制化的东西。/data
镜像:存放的“默认出厂设置”的数据,它是/system
分区中程序正常运行所必需的文件。恢复出厂设置,只需要将这个分区的镜像刷写回去就可以了。
Boot Loader
可以自定义,但是大多数厂商还是选用了LK(Little Kernel)启动加载器。LK并不是android源码树的一部分。LK并不是内核,它只实现了启动功能中最基本的部分(源码 git)。如下:
- 基本硬件的支持:在LK的
dev/
(屏幕帧缓存,按键和被用作usb设备)、platform/
(soc驱动)、target/
(设备中特定硬件的驱动)等源码树种支持。 - 找到并启动内核:找到bootimage,并对它进行解析(内核镜像、ramdisk和设备树),随后运行指定的命令行,把系统的控制权交个内核。这部分代码在
app/aboot
中。 - 基本的UI:在用户中断(
adb reboot boot loader
)命令时或者在一开始启动时通过特定按键终止启动(这里小米应该是重启时按住音量下键不放)时,aboot能够提供一个简单的界面,用户可以通过物理按键来进行操作。 - 支持console:一般在调试母版中才有此功能,在
lib/console
(被app/shell
调用)中提供一个单独的解释器(单独的线程),并且支持用户往里面添加新命令。在lib/gfxconsole
中还提供了字体之类的基本图形功能。 - 被用作usb目标设备:是Boot Loader可以通过名为fastboot的协议与电脑进行通信。其实现在
app/aboot/fastboot.c
中,可以按照需求扩展。 - 支持闪存分区:支持基本的文件系统,在系统升级或者recovery过程中,能够擦除或者重写一些分区。这部分源码在
lib/fs
中。 - 支持数字签名:能够加载用SSL证书做过数字签名的镜像。在
lib/openssl
源码中集成了OpenSSL项目的中部分代码。
bootloader 镜像
BootLoader能够像其他的系统镜像一样能够更新或者重刷。android源码树中提供了releasetools.py脚本中给出了bootloader镜像文件头的格式。根据文件头格式,用imgtool就可以解析出bootloader中所包含的镜像(Nexus 5的BootLoader镜像):
img | usage | |
---|---|---|
sbl1 | Secondary Boot Loader,stage 1 | 次级Boot Loader |
tz | TrustZone image | ARM TrustZone 镜像 |
rpm | Resource Power Mgmt | 资源电源管理引导模块 |
aboot | Application Boot Loader | 应用处理器的BootLoader |
sdi | ||
imgdata | RLE565 graphics used by boot loader |
Nexus 5使用的骁龙的处理器,它的aboot镜像包含40字节头:
偏移量 | 字段 | 作用 |
---|---|---|
0x00 | 文件签名 | 0x00000005(常量) |
0x04 | 版本 | 版本号 |
0x08 | ? | 0填充 |
0x0c | 镜像加载地址 | 把镜像的除头部以外的部分加载到这个字段指定的内存地址上 |
0x10 | 镜像大小 | aboot的镜像大小 |
0x14 | 代码大小 | aboot中的代码大小 |
0x18 | 最后一条指定的地址 | 镜像加载基地址+代码大小 |
0x1c | 数字签名大小 | 数字签名的大小(一般为0x100字节) |
0x20 | 被加载到内存中的最后一个字节的地址 | 最后一条指令的地址+数字签名的大小 |
0x24 | 证书链 | 证书链的大小(可选) |
紧挨着这个头部的后面就是arm的可启动镜像,这个镜像会被加载到0x0c指定的内存地址中去,镜像的头部存放的是一些arm异常处理向量,也就是一些跳转指令,当发生某些异常(中断、异常或停机)时,处理器就会根据异常处理向量中的指令跳转到指定的代码中去。根据LK中的定义,这个向量中的第一条时reset处理函数。
下方列出了去掉文件头后提取数字签名的命令:
1 | od -A d -t x4 aboot | head -5 #od命令解析aboot镜像 |
Boot镜像
android的Boot镜像包含内核和RAM disk。它由android源码树中的mkbootimg创建。Boot镜像有一个很小的头部、内核命令行、一个hash以及一个可选的二级启动加载器。所有这组件都是闪存页边界(通常为2KB)对齐的。可以用下面的命令去掉Boot镜像的头部(HTC定义的头部)。
1 | dd if=boot of=boot.sans.header bs=0x100 skip=1 #去掉头部 |
Boot镜像的格式在bootimg.h的源码中定义:
1 | /* |
内核
linux的内核大多是经过压缩的。内核镜像文件格式(一般为zImage)要求镜像中需要有用来把内核中压缩过的部分解压到内存中的自解压代码。镜像压缩的算法有多种,可以在内核build过程中通过make config
决定:
压缩算法的签名 | 对应的压缩算法 | 说明 |
---|---|---|
\x1f\x8b\x08\x00\x00\x00\x00\x00 | GZip | 最常见的压缩算法 |
\89LZOx00\x0d\x0a\x1a\x0a | LZO | 比GZip快,但是压缩率低10%左右。三星用的这种算法 |
内核总是最先运行自解压代码,通过搜索压缩算法的签名来确认如何解压。用imgtool
可以解压GZip和LZO算法压缩的镜像。
内核是android系统中与体系结构中最为紧密相关(主板类型、芯片组类型,移动设备中为SoC类型),内核为芯片提供专用的驱动,这些驱动是android源码树的一部分,不同的芯片组对应不同的内核设备树:
项目名称 | 芯片组厂商 | 设备 |
---|---|---|
goldfish | N/A | android emulator |
msm | Qualcomm MSM | Nexus One, Nexus 4, Nexus 5 |
cmap | TI OMAP | Galaxy Nexus, Glass |
samsung | samsung | Nexus S |
tegra | NVIDIA | moto Xoom,Nexus 7&9, Shield |
exynos | Samsung Exynos | Nexus 10 |
arm中的设备树
大多数的arm内核需要依赖设备树(device tree)文件来向内核提供硬件设备定义的相关信息。这个文件提供了设备之间的相互关系,内核依据此关系来启动相应的设备。设备树一般加载到内核镜像的尾部,有时也会单独分配一个分区。
设备树是一个二进制blob文件,通过0xd00dfeed签名来进行识别。具体细节可以参考ePAPR specification和Thomas Petazzoni的关于blob的pdf。
RAM disk
Boot或者recovery镜像的另一个组件就是initial RAM disk(initrd),在操作系统启动时,它被用作根目录文件系统,它被Boot Loader预加载到RAM中去,不许要其它的驱动。
initramfs使用来提供内核操作时所需的相关设备的驱动的,由于系统在启动时没有包含任何操作系统,所以initramfs本身是一个文件,而不是一个文件系统镜像。一些重要的驱动文件会包含在这个文件中,内核在启动时就能够在RAM中加载到它们。initramfs中还包含了启动程序/init
,内核会把它作为系统中的第一个程序启动起来(pid=1)。
/system与/data镜像
这个两个分区的镜像的存储完全由厂商自己说了算。由于大多数厂商使用的都是fastboot,所以大多数该类镜像都是使用的simg(sparse image)格式。处理这个文件个的工具在asop的/system/core/libsparse
的目录下。
simg文件的文件头为28个字节,格式如下:
偏移量 | 长度 | 字段含义 |
---|---|---|
0 | 4 | 文件签名 |
4 | 4 | 版本号(major_version+minor_version) |
8 | 2 | 头部的大小,总是28 |
10 | 2 | 数据块的大小 |
12 | 4 | 簇的大小,对于ext4来说,这个值一般为0x1000,4KB |
16 | 4 | 文件系统中簇的个数 |
20 | 4 | 镜像文件中数据块的个数 |
24 | 4 | 校验和(非必须,一般为全0) |