本文建议在对高通8155车机系统有一定的了解下进行阅读
前言
高通SA8155的基建默认采用的是A/B系统升级作为OTA升级方案。
然而,Android系统有自成体系的升级方案,尤其是动态分区的引入,详见官方参考,使得Host QNX和Guest Android两个不同系统的AB分区能否保持一致成了值得探究的问题。
本文探索了,Host QNX中的资源管理模块VMM(Virtual Machine Manager)在启动Android虚拟机的时候是如何处理Android系统的动态分区信息。
一、VMM中的关键CODES
以第一篇探索里的第二章节,VMM中的关键CODES,作为引子(文末有参考链接)。
在vmm_fsm.c文件中的launch_gvm接口里,有一行特别的注释:“Dynamic Parition is Enabled only for LA GVM”
static int launch_gvm(struct gvm_context *ctx)
{
...
if (ctx->vm_avb_version == 2) {
pthread_barrier_wait(&ctx->config_barrier);
if (!ctx->bank || ctx->slot_switch_config == SYMMETRIC_SLOT_SWITCH) {
ctx->bank = ctx->swdl_slot;
}
...
/* Dynamic Parition is Enabled only for LA GVM */
if (ctx->vmid == 2) {
if (ctx->qvb.is_dp_enabled == true) {
_argv[3] = (ctx->bank == 'a')? ctx->qvb.dp_enabled_a_conf_fname: ctx->qvb.dp_enabled_b_conf_fname;
vmm_info("guest target slot : %c host current slot: %c Config slot :%s ",ctx->bank, ctx->swdl_slot, _argv[3]);
ctx->swdl_slot = '\0';
} else {
_argv[3] = ctx->qvb.dp_disabled_conf_fname;
}
}
} else if (ctx->vm_avb_version == 1 && ctx->is_recovery) {
...
} else {
...
}
...
}
这行注释以及前后代码说明了:启动LA GVM的时候,VMM模块会考虑Android系统的动态分区是否使能,即context中的qvb成员is_dp_enable是否为真,传入不同的配置文件。
同时,qvb成员dp_enabled_a_conf_fname,dp_enabled_b_conf_fname,dp_disabled_conf_fname变量名也说明了需要考虑动态分区是否使能的情况。
那么,
- qvb成员is_dp_enable的赋值来自哪里呢?
- qvb成员dp_enabled_a_conf_fname,dp_enabled_b_conf_fname,dp_disabled_conf_fname的赋值来自哪里呢?
- 最后,为什么需要考虑Android的动态分区功能是否打开呢?
二、动态分区处理
1. 动态分区状态
先看看is_dp_enable的赋值问题,答案其实在上篇中,分析misc分区的时候,已经获得。来到驱动文件vmm_drv.c中:
static int parse_gvm_list_and_populate_ctx(struct vmm_dev_t *vmm_dev)
{
struct gvm_context *ctx;
...
while (count < vmm_dev->num_gvms) {
ctx = calloc(1, sizeof(*ctx));
...
if (ctx->vm_avb_version == 2) {
...
if (strstr(vm_partition_prefix, "la1")) {
...
} else if (strstr(vm_partition_prefix, "la")) {
ctx->get_config = &get_config_la;
ctx->qvb.is_dp_enabled = check_for_partition("/dev/disk/la_super");
ctx->misc_partition_name = check_for_partition("/dev/disk/la_misc")? "la_misc": NULL;
} else if (strstr(vm_partition_prefix, "agl")) {
...
} else if (strstr(vm_partition_prefix, "lv")) {
...
} else if (strstr(vm_partition_prefix, "qnx")) {
...
} else {
...
}
...
} else {
...
}
if (ctx->qvb.is_dp_enabled == true) {
...
dp_enabled_a_conf_fname = fdt_get_dp_enabaled_a_config(vmid);
...
dp_enabled_b_conf_fname = fdt_get_dp_enabaled_b_config(vmid);
...
} else {
...
dp_disabled_conf_fname = fdt_get_dp_disabled_config(vmid);
...
}
...
count++;
}
...
return -1;
}
可以发现,通过检查la_super分区获得的结果,对is_dp_enable进行赋值。
那么,接口check_for_partition对分区做了什么检查呢?
static bool check_for_partition(char* path)
{
bool ret = true;
/* Check for the partition for GVM is available */
int fd = open(path, O_RDONLY);
if (fd != -1) {
vmm_info("%s partition is available",path);
close(fd);
} else {
vmm_err("%s partition is not available",path);
ret = false;
}
return ret;
}
检查逻辑十分简单,是检查文件存在与否的常用实现方法,在这里就是检查分区是否存在。
得出信息:如果la_super分区存在,Android系统的动态分区被使能;如果la_super分区不存在,动态分区没有被使能。
进行推论:如果需要打开Android动态分区,分区表里必须存在名字为la_super的分区;如果要关闭动态分区功能,分区表里不能存在名字为la_super的分区。
此外,qvb成员dp_enabled_a_conf_fname,dp_enabled_b_conf_fname,dp_disabled_conf_fname变量也在上述代码中找到了赋值的地方,通过fdt_get_*一系列接口获取。这些接口来自fdt_utils模块:
const char* fdt_get_dp_enabaled_a_config(uint32_t vmid)
{
for ( int i = 0 ; i < _g_desc->nvms ; i++ ) {
if ( vmid == _g_desc->cfg[i].vmid ) {
if ( _g_desc->cfg[i].dp_enabled_a_config ) {
return _g_desc->cfg[i].dp_enabled_a_config;
}
}
}
return NULL;
}
const char* fdt_get_dp_enabaled_b_config(uint32_t vmid)
{
for ( int i = 0 ; i < _g_desc->nvms ; i++ ) {
if ( vmid == _g_desc->cfg[i].vmid ) {
if ( _g_desc->cfg[i].dp_enabled_b_config ) {
return _g_desc->cfg[i].dp_enabled_b_config;
}
}
}
return NULL;
}
const char* fdt_get_dp_disabled_config(uint32_t vmid)
{
for ( int i = 0 ; i < _g_desc->nvms ; i++ ) {
if ( vmid == _g_desc->cfg[i].vmid ) {
if ( _g_desc->cfg[i].dp_disabled_config ) {
return _g_desc->cfg[i].dp_disabled_config;
}
}
}
return NULL;
}
代码实现比较简单,类似查表功能,从nvms中找出指定vmid的配置,指定vmid的配置中如果存在查找的key,就返回其value值。
但是fdt_utils模块到底是做什么用的呢?头文件libfdt.h可以找到线索:
/*
* libfdt - Flat Device Tree manipulation
* Copyright (C) 2006 David Gibson, IBM Corporation.
*
* libfdt is dual licensed: you can use it either under the terms of
* the GPL, or the BSD license, at your option.
*
* ...
*
*/
原来是Linux内核中大名鼎鼎的设备树(Flattened Device Tree,参考Linux and the Devicetree)。
得出信息:fdt_utils模块用于解析和获取虚拟机的设备树描述,在这里就是Android虚拟机的设备树描述。
进行推论:dp_enabled_a_conf_fname,dp_enabled_b_conf_fname,dp_disabled_conf_fname变量的值来自设备树描述。
那么,在设备树描述里,是怎么描述这几个变量的?
2. FDT设备树
深入设备树,在基建中搜索,可以找到多个描述文件,这里以sdm-host_la.dts举例:
qcom_gvms {
qcom_gvm1 {
...
vm_name = "la";
/* 'vm_partition_prefix' is used in vmm_service and qvb_service
to identify GVM i.e LA, LV, or QNX. Should not be Modified */
vm_partition_prefix = "la";
...
dp_enabled_a_config = "/vm/images/la_dp_enabled_a.config";
dp_enabled_b_config = "/vm/images/la_dp_enabled_b.config";
dp_disabled_config = "/vm/images/la_dp_disabled.config";
vm_avb_version = <2>;
...
};
描述比较晦涩,继续深挖变量值/vm/images/la_dp_*.config,在文件system.build.tmpl中找到答案:
###########################################
# When Dynamic Parition is not enabled #
###########################################
/vm/images/la_dp_disabled.config={...
# vbmeta /dev/block/vdh
vdev vdev-virtio-blk.so loc 0x1c140000 intr gic:49 hostdev /dev/disk/la_vbmeta# Misc /dev/block/vdg
vdev vdev-virtio-blk.so loc 0x1c130000 intr gic:48 hostdev /dev/disk/la_misc...
# user-data /dev/block/vdc
vdev vdev-virtio-blk.so loc 0x1c0f0000 intr gic:43 threads 4 hostdev /dev/disk/la_vendor# user-data /dev/block/vdb
vdev vdev-virtio-blk.so loc 0x1c0e0000 vmid 2 intr gic:39 hostdev /dev/disk/la_userdata dio enable max-io-size 512k# system /dev/block/vda
vdev vdev-virtio-blk.so loc 0x1c0b0000 intr gic:40 threads 4 hostdev /dev/disk/la_system}
############################################
# When Dynamic Partition is enabled slot_a #
############################################/vm/images/la_dp_enabled_a.config={
# Vbmeta /dev/block/vdi
vdev vdev-virtio-blk.so loc 0x1c160000 intr gic:86 threads 4 hostdev /dev/disk/la_vbmeta_b# Vbmeta /dev/block/vdh
vdev vdev-virtio-blk.so loc 0x1c140000 intr gic:48 threads 4 hostdev /dev/disk/la_vbmeta_a# Misc /dev/block/vdg
vdev vdev-virtio-blk.so loc 0x1c130000 intr gic:46 threads 4 hostdev /dev/disk/la_misc...
# metadata /dev/block/vdc
vdev vdev-virtio-blk.so loc 0x1c0f0000 intr gic:49 threads 4 hostdev /dev/disk/la_metadata# user-data /dev/block/vdb
vdev vdev-virtio-blk.so loc 0x1c0e0000 vmid 2 intr gic:39 threads 4 hostdev /dev/disk/la_userdata dio enable max-io-size 512k# system /dev/block/vda
vdev vdev-virtio-blk.so loc 0x1c0b0000 intr gic:40 threads 4 hostdev /dev/disk/la_super}
############################################
# When Dynamic Partition is enabled slot_b #
############################################/vm/images/la_dp_enabled_b.config={
# Vbmeta /dev/block/vdi
vdev vdev-virtio-blk.so loc 0x1c160000 intr gic:86 threads 4 hostdev /dev/disk/la_vbmeta_a# Vbmeta /dev/block/vdh
vdev vdev-virtio-blk.so loc 0x1c140000 intr gic:48 threads 4 hostdev /dev/disk/la_vbmeta_b# Misc /dev/block/vdg
vdev vdev-virtio-blk.so loc 0x1c130000 intr gic:46 threads 4 hostdev /dev/disk/la_misc...
# metadata /dev/block/vdc
vdev vdev-virtio-blk.so loc 0x1c0f0000 intr gic:49 threads 4 hostdev /dev/disk/la_metadata# user-data /dev/block/vdb
vdev vdev-virtio-blk.so loc 0x1c0e0000 vmid 2 intr gic:39 threads 4 hostdev /dev/disk/la_userdata dio enable max-io-size 512k# system /dev/block/vda
vdev vdev-virtio-blk.so loc 0x1c0b0000 intr gic:40 threads 4 hostdev /dev/disk/la_super}
Voila! Android虚拟机的分区表配置原来在这,可以获取到以下信息:
- la_super分区只出现在动态分区使能的配置中(前面的推论是正确的)
- 动态分区不使能的配置中,竟然没有ab分区
- 动态分区使能的配置中,存在ab分区
- 动态分区使能的情况下,slot_a和slot_b的配置其实是一样的
得出以下推论:
-
动态分区使能的情况下,Android系统存在ab分区,符合ab系统的布局!
-
动态分区不使能的情况下,Android系统没有ab分区,难道不是ab系统升级吗?(奇怪!)
还是需要到Android官网查看动态分区的相关资料。
3. Android的动态分区
For devices launching with Android 10, create a partition called
super
. Thesuper
partition handles A/B slots internally, so A/B devices don't need separatesuper_a
andsuper_b
partitions. All read-only AOSP partitions that aren't used by the bootloader must be dynamic and must be removed from the GUID Partition Table (GPT). Vendor-specific partitions don't have to be dynamic and may be placed in the GPT.
从Android 10开始,引入了super分区,这个super分区在内部处理A/B slots,所以A/B设备不需要拆分出super_a 和super_b分区。所有只读的以及不被bootloader使用的AOSP分区必须是动态的,并且可以从GUID分区表中移除。参考以下图片:
从这张GUID分区表图中,可以发现,system、vendor、product的所有ab分区被super分区替代了。
此时的system、vendor、product分区在原来的分区表上已经不存在了,也就是说,这些分区被super分区接管了。
由此可以推断:
- QNX无法直接访问到Android的system、vendor、product等分区。
- VMM无法管理Android动态分区中的ab分区。
显然,Android动态分区的开启,让两个系统的ab分区保持一致的问题,变的更为复杂了,因为还得考虑到动态分区内的ab分区情况。
总结
在启动Android虚拟机的时候,QNX中的VMM模块会通过super分区是否存在来判断动态分区功能是否打开,并且通过Android虚拟机的设备树描述,传入启动所需的分区表配置文件。
在动态分区关闭的时候,Android系统不存在ab分区,所以无法管理slot状态;在动态分区开启的时候,Android系统不仅存在ab分区,还存在super分区,这个分区自己处理slot的状态。
回到如何保证两个不同系统的ab分区一致的问题,我们可以基于本文分析出的,以及第一篇的相关信息,做进一步的探索。