android-13 super 分区挂载流程
文章目录
前言
从 androidd 10 开始,google 将 vendor/system/product 分区整合到一个 super 分区中,编译后的最终镜像不再有这些单独的 image,取而代之的是一个总的 super.img。
本文基于 android-13_r1 版本、使能 A/B 分区为基础,介绍了 super 分区的结构挂载以及编译的整个流程。
一、为什么用 super 分区代替独立的分区?
- 传统的分区方式将 system、vendor、product、分区各自独立,在实际的项目开发中,通常每个分区都会留有部分空间,三个(可能没有 product) 分区累计就需要预留不少的空间,并且各个分区的大小被固定,不能灵活调整,比如 system 分区容量不够了,但是 vendor 分区又有多余的空间。
- 统一合并成一个分区后,使用 super 动态调整分区功能可以灵活调整内部分区大小,从而能更好的利用存储空间。
二、super 分区结构
支持的动态分区包括:
- 系统
- Vendor
- Product
- System Ext
- ODM
对于 A/B 设备,super 分区的大小需要包括两个槽位的大小,super 分区会在内部处理 A/B 槽位,因此 A/B 设备不需要单独的 super_a 和 super_b 分区。
通过下面的命令可以 dump 出 super.img 的结构:
- 下面的信息显示此 super.img 包含 odm_dlkm_a/b、product_a/b、system_a/b、vendor_a/b、vendor_dlkm_a/b 共 10 个子分区。
- Super partition layout 这一段描述了各个分区的大小
## lpdump super_raw.img
xxx@xxxx:/Android-13$ lpdump super_raw.img
Slot 0:
Metadata version: 10.2
Metadata size: 1128 bytes
Metadata max size: 65536 bytes
Metadata slot count: 3
Header flags: virtual_ab_device
Partition table: // 这里列出了 super 中包含了哪些分区
------------------------
Name: odm_dlkm_a
Group: main_a
Attributes: readonly
Extents:
0 .. 679 linear super 2048
------------------------
Name: odm_dlkm_b
Group: main_b
Attributes: readonly
Extents:
------------------------
Name: product_a
Group: main_a
Attributes: readonly
Extents:
0 .. 4617239 linear super 4096
------------------------
Name: product_b
Group: main_b
Attributes: readonly
Extents:
------------------------
Name: system_a
Group: main_a
Attributes: readonly
Extents:
0 .. 3241831 linear super 4622336
------------------------
Name: system_b
Group: main_b
Attributes: readonly
Extents:
0 .. 279991 linear super 7864320
------------------------
Name: vendor_a
Group: main_a
Attributes: readonly
Extents:
0 .. 1562079 linear super 8144896
------------------------
Name: vendor_b
Group: main_b
Attributes: readonly
Extents:
------------------------
Name: vendor_dlkm_a
Group: main_a
Attributes: readonly
Extents:
0 .. 86831 linear super 9707520
------------------------
Name: vendor_dlkm_b
Group: main_b
Attributes: readonly
Extents:
------------------------
Super partition layout: // 这里列出了各个分区的的大小及 offset,在创建 dm-linear 节点时需要此信息
------------------------
super: 2048 .. 2728: odm_dlkm_a (680 sectors)
super: 4096 .. 4621336: product_a (4617240 sectors)
super: 4622336 .. 7864168: system_a (3241832 sectors)
super: 7864320 .. 8144312: system_b (279992 sectors)
super: 8144896 .. 9706976: vendor_a (1562080 sectors)
super: 9707520 .. 9794352: vendor_dlkm_a (86832 sectors)
------------------------
Block device table: // 此分区(super)的大小
------------------------
Partition name: super
First sector: 2048
Size: 9663676416 bytes
Flags: none
------------------------
Group table: // 2 个 group (就是 2 个 solt,就是A/B solt,推测不确定)
------------------------
Name: default
Maximum size: 0 bytes
Flags: none
------------------------
Name: main_a
Maximum size: 9661579264 bytes
Flags: none
------------------------
Name: main_b
Maximum size: 9661579264 bytes
Flags: none
------------------------
lpdump 只能解析非 sparse 格式的 image,而系统编译的 super.img 是 sparse 格式,需要使用 simg2img 将 Android 编译生成的 sparse 格式的 super.img 转换成 raw 格式。
simg2img out/target/product/xxx/super.img super_raw.img
simg2img 的源码位于:
/Android-13/system/core/libsparse/simg2img.cpp
lpdump 的源码位于:/Android-13/system/extras/partition_tools/lpdumpd.cc
编译后的路径位于:/Android-13/out/host/linux-x86/bin/
三、super 分区挂载流程
截取 fstab 文件相关的内容(只含有 first_stage_mount 属性)放在这,方便后面查找。
# cat /vendor/etc/fstab.emmc
# 1 "vendor/mediatek/proprietary/hardware/fstab/mt6789/fstab.in.emmc"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 341 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "vendor/mediatek/proprietary/hardware/fstab/mt6789/fstab.in.emmc" 2
# 156 "vendor/mediatek/proprietary/hardware/fstab/mt6789/fstab.in.emmc"
system /system ext4 ro wait,slotselect,avb=vbmeta_system,logical,first_stage_mount,avb_keys=/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey
system_ext /system_ext ext4 ro wait,slotselect,avb=vbmeta_system,logical,first_stage_mount,avb_keys=/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey
vendor /vendor ext4 ro wait,slotselect,avb,logical,first_stage_mount
product /product ext4 ro wait,slotselect,avb,logical,first_stage_mount
vendor_dlkm /vendor_dlkm ext4 ro wait,slotselect,avb,logical,first_stage_mount
odm_dlkm /odm_dlkm ext4 ro wait,slotselect,avb,logical,first_stage_mount
/dev/block/by-name/metadata /metadata ext4 noatime,nosuid,nodev,discard wait,check,formattable,first_stage_mount
挂载流程从 init 进程 main() 函数开始。
// Android-13/system/core/init/init.cpp
int main(int argc, char** argv) {
// 调用 FirstStageMain() 挂载各个文件系统
return FirstStageMain(argc, argv);
}
// Android-13/system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
// ...
// ...
if (!DoFirstStageMount(!created_devices)) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}
}
// Android-13/system/core/init/first_stage_mount.cpp
// Mounts partitions specified by fstab in device tree.
bool DoFirstStageMount(bool create_devices) {
// Skips first stage mount if we're in recovery mode.
if (IsRecoveryMode()) {
LOG(INFO) << "First stage mount skipped (recovery mode)";
return true;
}
// <1> Create 从 dtbt 或者 fstab 文件中获取需要挂载的分区
auto fsm = FirstStageMount::Create();
if (!fsm.ok()) {
LOG(ERROR) << "Failed to create FirstStageMount " << fsm.error();
return false;
}
// <2> 创建设备节点
if (create_devices) {
if (!(*fsm)->DoCreateDevices()) return false;
}
// <3> 执行最终的 mount 操作
return (*fsm)->DoFirstStageMount();
}
static Result<Fstab> ReadFirstStageFstab() {
Fstab fstab;
// 先尝试从 dtbt 中读取
if (!ReadFstabFromDt(&fstab)) {
// 如果失败则从 fstab 文件中读取
// 这里只关注有 first_stage_mount 属性的节点
// 提个问题:此时无论是 system 还是 vendor 分区都没有挂载,而设备上只有 /vendor/etc/fstab.platform 一个文件
// 试问, 这个 fstab 文件会在哪里呢?
if (ReadDefaultFstab(&fstab)) {
fstab.erase(std::remove_if(fstab.begin(), fstab.end(),
[](const auto& entry) {
return !entry.fs_mgr_flags.first_stage_mount;
}),
fstab.end());
} else {
return Error() << "failed to read default fstab for first stage mount";
}
}
return fstab;
}
bool FirstStageMount::DoCreateDevices() {
if (!InitDevices()) return false;
// Mount /metadata before creating logical partitions, since we need to
// know whether a snapshot merge is in progress.
auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
return entry.mount_point == "/metadata";
});
// 如果有 metadata 分区,先挂载它,这里有 AVB 启动需要的 key.
if (metadata_partition != fstab_.end()) {
if (MountPartition(metadata_partition, true /* erase_same_mounts */)) {
// Copies DSU AVB keys from the ramdisk to /metadata.
// Must be done before the following TrySwitchSystemAsRoot().
// Otherwise, ramdisk will be inaccessible after switching root.
CopyDsuAvbKeys();
}
}
// 创建逻辑分区
if (!CreateLogicalPartitions()) return false;
return true;
}
bool FirstStageMount::CreateLogicalPartitions() {
if (!IsDmLinearEnabled()) {
return true;
}
if (super_path_.empty()) {
LOG(ERROR) << "Could not locate logical partition tables in partition "
<< super_partition_name_;
return false;
}
if (SnapshotManager::IsSnapshotManagerNeeded()) {
auto sm = SnapshotManager::NewForFirstStageMount();
if (!sm) {
return false;
}
if (sm->NeedSnapshotsInFirstStageMount()) {
return CreateSnapshotPartitions(sm.get());
}
}
// 这里的 super_path_ 就是 super 分区的实际路径,比如:/dev/block/mmcblk0p57
// 通过 ReadCurrentMetadata() 从 super 分区中解析出 metadata.
auto metadata = android::fs_mgr::ReadCurrentMetadata(super_path_);
if (!metadata) {
LOG(ERROR) << "Could not read logical partition metadata from " << super_path_;
return false;
}
if (!InitDmLinearBackingDevices(*metadata.get())) {
return false;
}
// 根据上面解析出的 metadata,创建逻辑分区节点 /dev/block/dm-X
return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path_);
}
// Android-13/system/core/fs_mgr/fs_mgr_dm_linear.cpp
bool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& super_device) {
CreateLogicalPartitionParams params = {
.block_device = super_device,
.metadata = &metadata,
};
// 遍历 metadata 中所有的逻辑分区
for (const auto& partition : metadata.partitions) {
if (!partition.num_extents) {
LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
continue;
}
if (partition.attributes & LP_PARTITION_ATTR_DISABLED) {
LINFO << "Skipping disabled partition: " << GetPartitionName(partition);
continue;
}
params.partition = &partition;
// 真正的创建节点
std::string ignore_path;
if (!CreateLogicalPartition(params, &ignore_path)) {
LERROR << "Could not create logical partition: " << GetPartitionName(partition);
return false;
}
}
return true;
}
// 根据 params 参数创建 dev-X 节点
// 这个函数的功能就是根据 metadata 中的分区信息创建 dm-X 节点
// 至于内部流程后续在 device-mapper 原理再另起新篇幅介绍。
bool CreateLogicalPartition(CreateLogicalPartitionParams params, std::string* path) {
CreateLogicalPartitionParams::OwnedData owned_data;
if (!params.InitDefaults(&owned_data)) return false;
// 根据 params 设置 dmTable
DmTable table;
if (!CreateDmTableInternal(params, &table)) {
return false;
}
// 调用 dm 方法创建节点
DeviceMapper& dm = DeviceMapper::Instance();
if (!dm.CreateDevice(params.device_name, table, path, params.timeout_ms)) {
return false;
}
LINFO << "Created logical partition " << params.device_name << " on device " << *path;
return true;
}
// 根据 params 设置 dmTable
bool CreateDmTableInternal(const CreateLogicalPartitionParams& params, DmTable* table) {
const auto& super_device = params.block_device;
uint64_t sector = 0;
for (size_t i = 0; i < params.partition->num_extents; i++) {
const auto& extent = params.metadata->extents[params.partition->first_extent_index + i];
std::unique_ptr<DmTarget> target;
switch (extent.target_type) {
case LP_TARGET_TYPE_ZERO:
target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
break;
case LP_TARGET_TYPE_LINEAR: { // 所有的逻辑分区都是 linear
const auto& block_device = params.metadata->block_devices[extent.target_source];
std::string dev_string;
if (!GetPhysicalPartitionDevicePath(params, block_device, super_device,
&dev_string)) {
LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
return false;
}
target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, dev_string,
extent.target_data);
break;
}
default:
LOG(ERROR) << "Unknown target type in metadata: " << extent.target_type;
return false;
}
if (!table->AddTarget(std::move(target))) {
return false;
}
sector += extent.num_sectors;
}
if (params.partition->attributes & LP_PARTITION_ATTR_READONLY) {
table->set_readonly(true);
}
if (params.force_writable) {
table->set_readonly(false);
}
return true;
}
上面的整个流程 LOG 如下图所示,最终将 super 分区映射成了 6 个逻辑分区。
dm-0 dm-1 等设备的顺序由 kernel 按照顺序产生,而 ReadCurrentMetadata() 函数又是按照前后顺序解析 metadata,所以最终这些顺序就是以在 metadata 中出现的顺序产生。
到目前位置,只是创建了 dm-X 这些节点,还并没有 mount,下面继续次流程。
bool FirstStageMount::DoFirstStageMount() {
if (!IsDmLinearEnabled() && fstab_.empty()) {
// Nothing to mount.
LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
return true;
}
if (!MountPartitions()) return false;
return true;
}
// 这里是真正的挂载流程
bool FirstStageMount::MountPartitions() {
// 这里首先挂载 /system 分区(逻辑分区)
if (!TrySwitchSystemAsRoot()) return false;
if (!SkipMountingPartitions(&fstab_, true /* verbose */)) return false;
// 遍历 fstab 中所有的逻辑节点
for (auto current = fstab_.begin(); current != fstab_.end();) {
// We've already mounted /system above.
if (current->mount_point == "/system") {
++current;
continue;
}
// Handle overlayfs entries later.
if (current->fs_type == "overlay") {
++current;
continue;
}
// Skip raw partition entries such as boot, dtbo, etc.
// Having emmc fstab entries allows us to probe current->vbmeta_partition
// in InitDevices() when they are AVB chained partitions.
if (current->fs_type == "emmc") {
++current;
continue;
}
Fstab::iterator end;
if (!MountPartition(current, false /* erase_same_mounts */, &end)) {
if (current->fs_mgr_flags.no_fail) {
LOG(INFO) << "Failed to mount " << current->mount_point
<< ", ignoring mount for no_fail partition";
} else if (current->fs_mgr_flags.formattable) {
LOG(INFO) << "Failed to mount " << current->mount_point
<< ", ignoring mount for formattable partition";
} else {
PLOG(ERROR) << "Failed to mount " << current->mount_point;
return false;
}
}
current = end;
}
for (const auto& entry : fstab_) {
if (entry.fs_type == "overlay") {
fs_mgr_mount_overlayfs_fstab_entry(entry);
}
}
// If we don't see /system or / in the fstab, then we need to create an root entry for
// overlayfs.
if (!GetEntryForMountPoint(&fstab_, "/system") && !GetEntryForMountPoint(&fstab_, "/")) {
FstabEntry root_entry;
if (GetRootEntry(&root_entry)) {
fstab_.emplace_back(std::move(root_entry));
}
}
// heads up for instantiating required device(s) for overlayfs logic
auto init_devices = [this](std::set<std::string> devices) -> bool {
for (auto iter = devices.begin(); iter != devices.end();) {
if (android::base::StartsWith(*iter, "/dev/block/dm-")) {
if (!block_dev_init_.InitDmDevice(*iter)) {
return false;
}
iter = devices.erase(iter);
} else {
iter++;
}
}
return InitRequiredDevices(std::move(devices));
};
MapScratchPartitionIfNeeded(&fstab_, init_devices);
fs_mgr_overlayfs_mount_all(&fstab_);
return true;
}
// If system is in the fstab then we're not a system-as-root device, and in
// this case, we mount system first then pivot to it. From that point on,
// we are effectively identical to a system-as-root device.
bool FirstStageMount::TrySwitchSystemAsRoot() {
UseDsuIfPresent();
// Preloading all AVB keys from the ramdisk before switching root to /system.
PreloadAvbKeys();
auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
return entry.mount_point == "/system";
});
if (system_partition == fstab_.end()) return true;
if (use_snapuserd_) {
SaveRamdiskPathToSnapuserd();
}
// 调用挂载的 mount 函数
if (MountPartition(system_partition, false /* erase_same_mounts */)) {
if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
LOG(ERROR) << "check_most_at_once forbidden on external media";
return false;
}
SwitchRoot("/system");
} else {
PLOG(ERROR) << "Failed to mount /system";
return false;
}
return true;
}
// 挂载一个节点
bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
Fstab::iterator* end) {
// Sets end to begin + 1, so we can just return on failure below.
if (end) {
*end = begin + 1;
}
// 创建挂载目录
if (!fs_mgr_create_canonical_mount_point(begin->mount_point)) {
return false;
}
if (begin->fs_mgr_flags.logical) {
// 逻辑分区则需要修改一下挂载的 blk_device 名字
// 因为 fstab 指定的是 system, 现在需要修改为 dm-6
if (!fs_mgr_update_logical_partition(&(*begin))) {
return false;
}
if (!block_dev_init_.InitDmDevice(begin->blk_device)) {
return false;
}
}
// 根据 metadata 中的相关信息设者 dm-verity 节点
// 以上面的 system 分区为例:
// 第一步 通过上面的 CreateLogicalPartition() 函数映射了 super 分区中 dm-2 为 system 分区
// 在这里再次映射 dm-2 为 dm-6, dm-6 即为最终的挂载 blk_device
// 至于内部流程后续在 device-mapper 原理再另起新篇幅介绍。
if (!SetUpDmVerity(&(*begin))) {
PLOG(ERROR) << "Failed to setup verity for '" << begin->mount_point << "'";
return false;
}
// 这里调用最终的 mount 函数
bool mounted = (fs_mgr_do_mount_one(*begin) == 0);
// Try other mounts with the same mount point.
Fstab::iterator current = begin + 1;
for (; current != fstab_.end() && current->mount_point == begin->mount_point; current++) {
if (!mounted) {
// blk_device is already updated to /dev/dm-<N> by SetUpDmVerity() above.
// Copy it from the begin iterator.
current->blk_device = begin->blk_device;
mounted = (fs_mgr_do_mount_one(*current) == 0);
}
}
if (erase_same_mounts) {
current = fstab_.erase(begin, current);
}
if (end) {
*end = current;
}
return mounted;
}
// Android-13/system/core/fs_mgr/fs_mgr.cpp
// wrapper to __mount() and expects a fully prepared fstab_rec,
// unlike fs_mgr_do_mount which does more things with avb / verity etc.
int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& alt_mount_point) {
// First check the filesystem if requested.
if (entry.fs_mgr_flags.wait && !WaitForFile(entry.blk_device, 20s)) {
LERROR << "Skipping mounting '" << entry.blk_device << "'";
}
auto& mount_point = alt_mount_point.empty() ? entry.mount_point : alt_mount_point;
// Run fsck if needed
prepare_fs_for_mount(entry.blk_device, entry, mount_point);
int ret = __mount(entry.blk_device, mount_point, entry);
if (ret) {
ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
}
return ret;
}
从下面的 LOG 可以看出,最终的挂载点为 /dev/block/dm-6,而建立 dm-2 和 dm-6 的映射则是 SetUpDmVerity() 中下面的信息定义
[libfs_avb]Built verity table: '1 /dev/block/dm-2 /dev/block/dm-2 4096 4096 398815 398815 sha256 5c78d69ea0b3cfd1af20c8d793afa1509acbe823e1674a06133aceeeeb46f184 1a57eb751
最终挂载完就是下面这个样子。
从 super 分区映射出 dm-2 设备称为 dm-linear
再从 dm-2 映射到 dm-6 设备称为 dm-verify,这个都基于 device-mapper ,后续介绍 device-mapper 中再细聊天。
四、super 分区编译过程
1.从 buidl/core/Makefile 开始
# If BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT is set, super.img is built from images in the
# $(PRODUCT_OUT) directory, and is built to $(PRODUCT_OUT)/super.img. Also, it will
# be built for non-dist builds. This is useful for devices that uses super.img directly, e.g.
# virtual devices.
ifeq (true,$(BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT))
$(INSTALLED_SUPERIMAGE_TARGET): $(INSTALLED_SUPERIMAGE_DEPENDENCIES)
$(call pretty,"Target super fs image for debug: $@")
$(call build-superimage-target,$(INSTALLED_SUPERIMAGE_TARGET),\
$(call intermediates-dir-for,PACKAGING,superimage_debug)/misc_info.txt)
# Build super.img by using $(INSTALLED_*IMAGE_TARGET) to $(1)
# $(1): built image path
# $(2): misc_info.txt path; its contents should match expectation of build_super_image.py
define build-superimage-target
mkdir -p $(dir $(2))
rm -rf $(2)
$(call dump-super-image-info,$(2))
$(foreach p,$(BOARD_SUPER_PARTITION_PARTITION_LIST), \
echo "$(p)_image=$(INSTALLED_$(call to-upper,$(p))IMAGE_TARGET)" >> $(2);)
$(if $(BUILDING_SYSTEM_OTHER_IMAGE), $(if $(filter system,$(BOARD_SUPER_PARTITION_PARTITION_LIST)), \
echo "system_other_image=$(INSTALLED_SYSTEMOTHERIMAGE_TARGET)" >> $(2);))
mkdir -p $(dir $(1))
PATH=$(dir $(LPMAKE)):$$PATH \
$(BUILD_SUPER_IMAGE) -v $(2) $(1)
endef
- 经过上面的层层调用后,最终调用到此应用打包 super.img $(BUILD_SUPER_IMAGE) -v $(2) $(1) ,这个命令最终为
out/host/linux-x86/bin/build_super_image -v \ out/target/product/xxx/obj/PACKAGING/target_files_intermediates/misc_info.txt \ out/target/product/xxx/super.img
备注:super.img 只是将 system/vendor 等 image 打包在一起,所以 super.img 需要依赖于 system.img、vendor.img、product.img 等这些镜像。
2. build_super_image.py
上一步调用到的 build_super_image 代码在 /Android-13/build/make/tools/releasetools/build_super_image.py 中,
def BuildSuperImageFromDict(info_dict, output):
cmd = [info_dict["lpmake"],
"--metadata-size", "65536",
"--super-name", info_dict["super_metadata_device"]]
ab_update = info_dict.get("ab_update") == "true"
virtual_ab = info_dict.get("virtual_ab") == "true"
virtual_ab_retrofit = info_dict.get("virtual_ab_retrofit") == "true"
retrofit = info_dict.get("dynamic_partition_retrofit") == "true"
block_devices = shlex.split(info_dict.get("super_block_devices", "").strip())
groups = shlex.split(info_dict.get("super_partition_groups", "").strip())
if ab_update and retrofit:
cmd += ["--metadata-slots", "2"]
elif ab_update:
cmd += ["--metadata-slots", "3"]
else:
cmd += ["--metadata-slots", "2"]
if ab_update and retrofit:
cmd.append("--auto-slot-suffixing")
if virtual_ab and not virtual_ab_retrofit:
cmd.append("--virtual-ab")
for device in block_devices:
size = info_dict["super_{}_device_size".format(device)]
cmd += ["--device", "{}:{}".format(device, size)]
append_suffix = ab_update and not retrofit
has_image = False
for group in groups:
group_size = info_dict["super_{}_group_size".format(group)]
if append_suffix:
cmd += ["--group", "{}_a:{}".format(group, group_size),
"--group", "{}_b:{}".format(group, group_size)]
else:
cmd += ["--group", "{}:{}".format(group, group_size)]
partition_list = shlex.split(
info_dict["super_{}_partition_list".format(group)].strip())
for partition in partition_list:
image = info_dict.get("{}_image".format(partition))
if image:
has_image = True
if not append_suffix:
cmd += GetArgumentsForImage(partition, group, image)
continue
# For A/B devices, super partition always contains sub-partitions in
# the _a slot, because this image should only be used for
# bootstrapping / initializing the device. When flashing the image,
# bootloader fastboot should always mark _a slot as bootable.
cmd += GetArgumentsForImage(partition + "_a", group + "_a", image)
other_image = None
if partition == "system" and "system_other_image" in info_dict:
other_image = info_dict["system_other_image"]
has_image = True
cmd += GetArgumentsForImage(partition + "_b", group + "_b", other_image)
if info_dict.get("build_non_sparse_super_partition") != "true":
cmd.append("--sparse")
cmd += ["--output", output]
common.RunAndCheckOutput(cmd)
if retrofit and has_image:
logger.info("Done writing images to directory %s", output)
else:
logger.info("Done writing image %s", output)
return True
上面的 python 函数通过 misc_info.txt 设置一些参数,最后调用 lpmake 应用,调用 lpmake 参数如下
可以看到,参数中有 10 个 --partition 项,和第一节 lpdump 出来的 partition 内容一致。
lpmake --metadata-size 65536
--super-name super
--metadata-slots 3
--virtual-ab
--device super:9663676416
--group main_a:9661579264
--group main_b:9661579264
--partition odm_dlkm_a:readonly:348160:main_a --image odm_dlkm_a=out/target/product/xxxxx/merged/temp/targetfiles-kv2fxhj8/IMAGES/odm_dlkm.img
--partition odm_dlkm_b:readonly:0:main_b
--partition product_a:readonly:2363891712:main_a --image product_a=out/target/product/xxxxx/merged/temp/targetfiles-kv2fxhj8/IMAGES/product.img
--partition product_b:readonly:0:main_b
--partition system_a:readonly:1650864128:main_a --image system_a=out/target/product/xxxxx/merged/temp/targetfiles-kv2fxhj8/IMAGES/system.img
--partition system_b:readonly:143355904:main_b --image system_b=out/target/product/xxxxx/merged/temp/targetfiles-kv2fxhj8/IMAGES/system_other.img
--partition vendor_a:readonly:799784960:main_a --image vendor_a=out/target/product/xxxxx/merged/temp/targetfiles-kv2fxhj8/IMAGES/vendor.img
--partition vendor_b:readonly:0:main_b
--partition vendor_dlkm_a:readonly:44457984:main_a --image vendor_dlkm_a=out/target/product/xxxxx/merged/temp/targetfiles-kv2fxhj8/IMAGES/vendor_dlkm.img
--partition vendor_dlkm_b:readonly:0:main_b
--sparse
--output out/target/product/xxxxx/super.img
3. 最后的 lpmake
lpmake 的代码位于 /Android-13/system/extras/partition_tools/lpmake.cc
// /Android-13/system/core/fs_mgr/liblp/images.cpp
bool ImageBuilder::Build() {
if (sparse_file_add_fill(device_images_[0].get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
LERROR << "Could not add initial sparse block for reserved zeroes";
return false;
}
std::string geometry_blob = SerializeGeometry(geometry_);
std::string metadata_blob = SerializeMetadata(metadata_);
metadata_blob.resize(geometry_.metadata_max_size);
// Two copies of geometry, then two copies of each metadata slot.
all_metadata_ += geometry_blob + geometry_blob;
for (size_t i = 0; i < geometry_.metadata_slot_count * 2; i++) {
all_metadata_ += metadata_blob;
}
// 上面就是 super.img meta 数据的生成过
// 头部由连续的 2 个 geometry,后面跟随 6 个 meta 组成(非 A/B 分区就是 3 个。
// 具体看下面的 dump 数据
///
}
super.img 整体结构如以及关键字段定义如下图:
实际 dump super.img 所看到的布局。
各个字段的定义在下面的代码中都有详细阐述。
/* Default name of the physical partition that holds logical partition entries.
* The layout of this partition will look like:
*
* +--------------------+
* | Disk Geometry |
* +--------------------+
* | Geometry Backup |
* +--------------------+
* | Metadata |
* +--------------------+
* | Backup Metadata |
* +--------------------+
* | Logical Partitions |
* +--------------------+
*/
/* This structure is stored at block 0 in the first 4096 bytes of the
* partition, and again in the following block. It is never modified and
* describes how logical partition information can be located.
*/
typedef struct LpMetadataGeometry {
/* 0: Magic signature (LP_METADATA_GEOMETRY_MAGIC). */
uint32_t magic;
/* 4: Size of the LpMetadataGeometry struct. */
uint32_t struct_size;
/* 8: SHA256 checksum of this struct, with this field set to 0. */
uint8_t checksum[32];
/* 40: Maximum amount of space a single copy of the metadata can use. This
* must be a multiple of LP_SECTOR_SIZE.
*/
uint32_t metadata_max_size;
/* 44: Number of copies of the metadata to keep. For A/B devices, this
* will be 2. For an A/B/C device, it would be 3, et cetera. For Non-A/B
* it will be 1. A backup copy of each slot is kept, so if this is "2",
* there will be four copies total.
*/
uint32_t metadata_slot_count;
/* 48: Logical block size. This is the minimal alignment for partition and
* extent sizes, and it must be a multiple of LP_SECTOR_SIZE. Note that
* this must be equal across all LUNs that comprise the super partition,
* and thus this field is stored in the geometry, not per-device.
*/
uint32_t logical_block_size;
} __attribute__((packed)) LpMetadataGeometry;
/* The logical partition metadata has a number of tables; they are described
* in the header via the following structure.
*
* The size of the table can be computed by multiplying entry_size by
* num_entries, and the result must not overflow a 32-bit signed integer.
*/
typedef struct LpMetadataTableDescriptor {
/* 0: Location of the table, relative to end of the metadata header. */
uint32_t offset;
/* 4: Number of entries in the table. */
uint32_t num_entries;
/* 8: Size of each entry in the table, in bytes. */
uint32_t entry_size;
} __attribute__((packed)) LpMetadataTableDescriptor;
/* Binary format for the header of the logical partition metadata format.
*
* The format has three sections. The header must occur first, and the
* proceeding tables may be placed in any order after.
*
* +-----------------------------------------+
* | Header data - fixed size |
* +-----------------------------------------+
* | Partition table - variable size |
* +-----------------------------------------+
* | Partition table extents - variable size |
* +-----------------------------------------+
*
* The "Header" portion is described by LpMetadataHeader. It will always
* precede the other three blocks.
*
* All fields are stored in little-endian byte order when serialized.
*
* This struct is versioned; see the |major_version| and |minor_version|
* fields.
*/
typedef struct LpMetadataHeader {
/* 0: Four bytes equal to LP_METADATA_HEADER_MAGIC. */
uint32_t magic;
/* 4: Version number required to read this metadata. If the version is not
* equal to the library version, the metadata should be considered
* incompatible.
*/
uint16_t major_version;
/* 6: Minor version. A library supporting newer features should be able to
* read metadata with an older minor version. However, an older library
* should not support reading metadata if its minor version is higher.
*/
uint16_t minor_version;
/* 8: The size of this header struct. */
uint32_t header_size;
/* 12: SHA256 checksum of the header, up to |header_size| bytes, computed as
* if this field were set to 0.
*/
uint8_t header_checksum[32];
/* 44: The total size of all tables. This size is contiguous; tables may not
* have gaps in between, and they immediately follow the header.
*/
uint32_t tables_size;
/* 48: SHA256 checksum of all table contents. */
uint8_t tables_checksum[32];
/* 80: Partition table descriptor. */
LpMetadataTableDescriptor partitions;
/* 92: Extent table descriptor. */
LpMetadataTableDescriptor extents;
/* 104: Updateable group descriptor. */
LpMetadataTableDescriptor groups;
/* 116: Block device table. */
LpMetadataTableDescriptor block_devices;
/* Everything past here is header version 1.2+, and is only included if
* needed. When liblp supporting >= 1.2 reads a < 1.2 header, it must
* zero these additional fields.
*/
/* 128: See LP_HEADER_FLAG_ constants for possible values. Header flags are
* independent of the version number and intended to be informational only.
* New flags can be added without bumping the version.
*/
uint32_t flags;
/* 132: Reserved (zero), pad to 256 bytes. */
uint8_t reserved[124];
} __attribute__((packed)) LpMetadataHeader;
/* This struct defines a logical partition entry, similar to what would be
* present in a GUID Partition Table.
*/
typedef struct LpMetadataPartition {
/* 0: Name of this partition in ASCII characters. Any unused characters in
* the buffer must be set to 0. Characters may only be alphanumeric or _.
* The name must include at least one ASCII character, and it must be unique
* across all partition names. The length (36) is the same as the maximum
* length of a GPT partition name.
*/
char name[36];
/* 36: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
uint32_t attributes;
/* 40: Index of the first extent owned by this partition. The extent will
* start at logical sector 0. Gaps between extents are not allowed.
*/
uint32_t first_extent_index;
/* 44: Number of extents in the partition. Every partition must have at
* least one extent.
*/
uint32_t num_extents;
/* 48: Group this partition belongs to. */
uint32_t group_index;
} __attribute__((packed)) LpMetadataPartition;