Android从7.0开始引入新的OTA升级方式,A/B System Updates,这里将其叫做A/B系统,涉及的内容较多,分多篇对A/B系统的各个方面进行分析。本文为第四篇,系统的启动和升级。
本文基于AOSP 7.1.1_r23 (NMF27D)代码进行分析。
1. 系统的启动
1.1 bootloader检查slot metadata
系统复位后,bootloader会去读取boot_control私有的存储数slot metadata并进行解析,以此确定从哪一个slot启动。
以下是Android官方的一个bootloader加载流程图:
1.2 linux系统的启动
上一步中,bootloader会根据slot metadata确定读取哪一个slot的boot分区进行启动。
每一个slot上有两个rootfs:
boot分区自带recovery mode的ramdisk;
system分区包含了Android系统的rootfs;
启动中,如何选择加载boot分区的ramdisk还是system分区的rootfs呢?
答案是由kernel的命令行参数skip_initramfs来决定。
下面来看skip_initramfs参数是如何起作用的。
系统同时包含init\noinitramfs.c和init\initramfs.c的代码,并在initramfs.c模块中定义并解析skip_initramfs参数:
# init\initramfs.c
static int __initdata do_skip_initramfs;
static int __init skip_initramfs_param(char *str)
{
if (*str)
return 0;
# 设置do_skip_initramfs标志
do_skip_initramfs = 1;
return 1;
}
# 用于解析命令行的`skip_initramfs`参数
__setup("skip_initramfs", skip_initramfs_param);
如果命令行设置了skip_initramfs,则do_skip_initramfs会被设置为1。
linux调用populate_rootfs默认会并加载boot分区自带的ramdisk,但如果do_skip_initramfs被
设置为1,则调用default_rootfs生成一个极小的rootfs:
# init\initramfs.c
static int __init populate_rootfs(void)
{
char *err;
# 如果do_skip_initramfs置1,则调用default_rootfs生成一个极小的rootfs
if (do_skip_initramfs)
return default_rootfs();
# 没有设置do_skip_initramfs的情况下,才会解析并加载`boot`分区所包含的`ramdisk`
err = unpack_to_rootfs(__initramfs_start, __initramfs_size);
if (err)
panic("%s", err); /* Failed to decompress INTERNAL initramfs */
...
return 0;
}
default_rootfs的内容很简单,用于在内存中生成一个极小的rootfs,仅包含/dev和root两个文件夹以及一个设备节点/dev/console:
# init\noinitramfs.c
/*
* Create a simple rootfs that is similar to the default initramfs
*/
static int __init default_rootfs(void)
{
int err;
# 创建/dev文件夹用于存放/dev/console设备节点
err = sys_mkdir((const char __user __force *) "/dev", 0755);
if (err < 0)
goto out;
# 创建/dev/console设备节点
err = sys_mknod((const char __user __force *) "/dev/console",
S_IFCHR | S_IRUSR | S_IWUSR,
new_encode_dev(MKDEV(5, 1)));
if (err < 0)
goto out;
# 创建/root目录,作为根用户root的home
err = sys_mkdir((const char __user __force *) "/root", 0700);
if (err < 0)
goto out;
return 0;
out:
printk(KERN_WARNING "Failed to create a rootfs\n");
return err;
}
因此skip_initramfs参数决定了加载哪一个rootfs,进入哪一个系统。
加载android系统的命令行参数
skip_initramfs rootwait ro init=/init root="/dev/dm-0 dm=system none ro,0 1 android-verity "
例如Broadcom的7252SSFFDR3参考平台的启动Android系统的参数为:
mem=1016m@0m mem=1024m@2048m bmem=339m@669m bmem=237m@2048m \
brcm_cma=784m@2288m \
ramoops.mem_address=0x3F800000 ramoops.mem_size=0x800000 ramoops.console_size=0x400000 \
buildvariant=userdebug \
veritykeyid=id:7e4333f9bba00adfe0ede979e28ed1920492b40f buildvariant=eng \
rootwait init=/init ro \
root=/dev/dm-0 dm="system none ro,0 1 android-verity PARTUUID=c49e0acb-1b38-95e5-548a-2b7260e704a4" skip_initramfs
除去rootf