AVB源码学习(六):AVB2.0 Device Mapper和Dm verity详解

简介: AVB源码学习(六):AVB2.0 Device Mapper和Dm verity详解


一、Device Mapper

1. Device Mapper概述

Device mapper是LINUX提供的一种逻辑设备到物理设备的映射框架,中间传递消息的是用户自定义的target driver插件,用户可以编写好具体的IO请求的target driver就行,用户层可以使用ioctl命令的方式向底层进行通讯。

target driver主要定义对IO请求的处理规则,在device mapper中对target driver的操作已定义好了统一的接口,可实现几个常见的方法就行。同时device mapper提供了一种底层消息上报的机制,通过target driver将事件消息传递到用户空间。

在android的init启动的第一阶段中主要调用了InitDeviceMapper函数,来进行device mapper的初始化

2. Device Mapper的使用

在android的init启动的第一阶段中主要调用了InitDeviceMapper函数,来进行device mapper的初始化。

分析一下代码@block_dev_initializer.cpp

bool FirstStageMount::InitRequiredDevices(std::set<std::string> devices) {
    if (!block_dev_init_.InitDeviceMapper()) {
        return false;
    }
    if (devices.empty()) {
        return true;
    }
    return block_dev_init_.InitDevices(std::move(devices));
}
bool BlockDevInitializer::InitDeviceMapper() {
    const std::string dm_path = "/devices/virtual/misc/device-mapper";
    bool found = false;
    auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
        if (uevent.path == dm_path) {
            device_handler_->HandleUevent(uevent);
            found = true;
            return ListenerAction::kStop;
        }
        return ListenerAction::kContinue;
    };
    uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
    if (!found) {
        Timer t;
        uevent_listener_.Poll(dm_callback, 10s);
    }
    if (!found) {
         return false;
    }
    return true;
}

调用InitDmDevice函数初始化Device mapper 设备。通过代码分析来看,直观的功能是通过uevent上报的节点,在其中去查找dtsi中配置的各个分区;

如果找到了就从device集合中删除,添加个10秒的等待,如果10秒内都找不到这个分区,说明此分区可能没有挂载到文件系统,或者配置出错。

bool BlockDevInitializer::InitDmDevice(const std::string& device) {
auto uevent_callback = [&device_name, &device, this, &found](const Uevent& uevent) {
        if (uevent.device_name == device_name) {
device_handler_->HandleUevent(uevent);
            found = true;
            return ListenerAction::kStop;
        }
        return ListenerAction::kContinue;
    };
uevent_listener_.RegenerateUeventsForPath(syspath, uevent_callback);
    if (!found) {
Timer t;
        uevent_listener_.Poll(uevent_callback, 10s);
    }
    return true;
}
ListenerAction BlockDevInitializer::HandleUevent(const Uevent& uevent,
                                                 std::set<std::string>* devices) {
    auto name = uevent.partition_name;
    if (name.empty()) {
        size_t base_idx = uevent.path.rfind('/');
        if (base_idx == std::string::npos) {
            return ListenerAction::kContinue;
        }
        name = uevent.path.substr(base_idx + 1);
    }
    auto iter = devices->find(name);
    if (iter == devices->end()) {
        return ListenerAction::kContinue;
    }
    devices->erase(iter);
    device_handler_->HandleUevent(uevent);
    return devices->empty() ? ListenerAction::kStop :     ListenerAction::kContinue;
}

二、Dm Verity

1. Dm Verity验证思想

上层应用通过文件系统读取某个block时,调用dm-verity读取对应的block,

dm-verity会根据verity-table调用block_device读取对应的block,然后逐层计算到根block,

计算得到root-hash和kernel中保存的root hash进行对比,hash值进行比较,如果相等,则说明该块没有被修改过,一切正常。

2. Hashtree脚本处理

2.1 镜像编译

编译system.img时调用了build_image.py脚本

define build-systemimage-target
  @echo "Target system fs image: $(1)"
  $(call generate-userimage-prop-dictionary,   $(systemimage_intermediates)/system_image_info.txt, \
      skip_fsck=true)
  $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
      build/make/tools/releasetools/build_image.py \
      $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \

2.2 创建verity tree

/build/tools/releasetools/build_image.py中,调用build_verity_tree程序

def BuildImage(in_dir, prop_dict, out_file, target_out=None):
if verity_supported and is_verity_partition:
    if not MakeVerityEnabledImage(out_file, verity_fec_supported, prop_dict):
      return False
def MakeVerityEnabledImage(out_file, fec_supported, prop_dict):
   # build the verity tree and get the root hash and salt
  if not BuildVerityTree(out_file, verity_image_path, prop_dict):
    return False
 def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict):
  cmd = ["build_verity_tree", "-A", FIXED_SALT, sparse_image_path,
         verity_image_path]
  output, exit_code = RunCommand(cmd)
  if exit_code != 0:
    print("Could not build verity tree! Error: %s" % output)
    return False
  root, salt = output.split()
  prop_dict["verity_root_hash"] = root
  prop_dict["verity_salt"] = salt
  return True

调用到system\extras\verity\build_verity_tree.cpp,

过程包括:

根据block_size大小和hash_size大小,计算得到一个block可以存放的hash个数从level 0开始,计算需要多少个level;

创建数组verity_tree_levels, 用于指示每一个level在verity_tree中的起始地址;

创建数组verity_tree_level_blocks, 用于指示每一个level在verity_tree中的长度;

计算ext4 image中各个块的hash值存到verity_tree的level0,然后逐层往上计算各层的hash值,并填入verity_tree中相应的level。

2.3 创建metadata

def build_verity_metadata(data_blocks, metadata_image, root_hash, salt,
        block_device, signer_path, signing_key, signer_args=None,
        verity_disable=False):
    # build the verity table
    verity_table = build_verity_table(block_device, data_blocks, root_hash, salt)
    # build the verity table signature
    signature = sign_verity_table(verity_table, signer_path, signing_key, signer_args)
    # build the metadata block
    metadata_block = build_metadata_block(verity_table, signature, verity_disable)
    # write it to the outfile
    with open(metadata_image, "wb") as f:
        f.write(metadata_block)

build_verity_table函数中:

  • 第一个参数block_device描述了dm-verity设备对应了那个底层的block
  • 第二个参数block_device指定了hash-tree存在于哪个block设备上,

BLOCK_SIZE参数为默认的4k, data_blocks描述了有多少个block,data_blocks + (METADATA_SIZE / BLOCK_SIZE)表示hash-tree在对应block设备上的偏移位置,

最后可找到hash-tree,root_hash为hash-tree的根hash。

另外,verity table是存在分区中的super block(超级块)之后的位置,便于找到校验的位置。

def build_verity_table(block_device, data_blocks, root_hash, salt):
    table = "1 %s %s %s %s %s %s sha256 %s %s"
    table %= (  block_device,
                block_device,
                BLOCK_SIZE,
                BLOCK_SIZE,
                data_blocks,
                data_blocks,
                root_hash,
                salt)
    return table

3. Dm verity设备的创建

3.1 SetUpDmVerity函数

调用的入口在Init的第一阶段的SetUpDmVerity函数。

Fstab::iterator end;
if (!MountPartition(current, false /* erase_same_mounts */, &end)) {
}
bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
Fstab::iterator* end) {
    if (!SetUpDmVerity(&(*begin))) {
        PLOG(ERROR) << "Failed to setup verity for '" << begin->mount_point << "'";
        return false;
    }
}
else if (fstab_entry->fs_mgr_flags.avb) {
        //InitAvbHandle完成此分区的AVB验证
        if (!InitAvbHandle()) return false;
#然后执行创建hash tree table并建立与driver侧的ioctl交互连接。
        hashtree_result =
                avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);

相关代码在:system\core\fs_mgr\libfs_avb\ avb_util.cpp

3.2 hash table处理

Hash table传入的格式如下,中间做了序列号处理,其实就是一串长值拼成的字串。

调用的入口在Init的第一阶段的SetUpDmVerity函数。

android::dm::DmTargetVerity target(
            0, hashtree_desc.image_size / 512, hashtree_desc.dm_verity_version, blk_device,
            blk_device, hashtree_desc.data_block_size, hashtree_desc.hash_block_size,
            hashtree_desc.image_size / hashtree_desc.data_block_size,
            hashtree_desc.tree_offset / hashtree_desc.hash_block_size, hash_algorithm.str(),
            hashtree_desc.root_digest, hashtree_desc.salt);

CreateDevice函数里面有三个函数,分别实现了上述的几个ioctl命令,把拼接好的hashtable传到驱动侧。

bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
                                const std::chrono::milliseconds& timeout_ms) {
    std::string uuid = GenerateUuid();
    if (!CreateDevice(name, uuid)) {
        return false;
    }
    std::string unique_path;
    if (!LoadTableAndActivate(name, table)    || !GetDeviceUniquePath(name, &unique_path) ||
        !GetDmDevicePathByName(name, path)) {
        DeleteDevice(name);
        return false;
    }

对应的IOCTL命令

ioctl(fd_, DM_DEV_CREATE, &io)
ioctl(fd_, DM_TABLE_LOAD, io)
ioctl(fd_, DM_DEV_SUSPEND, io)

好了,以上就是Device Mapper和Dm verity的简介了,实际上的内容会更多,大家学习的过程中多多发现吧。

小结(白话文+实战)

ing

目录
相关文章
|
6月前
|
安全 Linux Android开发
AVB源码学习(七):AVB2.0-Super动态分区介绍
AVB源码学习(七):AVB2.0-Super动态分区介绍
394 0
|
6月前
|
算法 Android开发
AVB源码学习(四):AVB2.0-libavb库介绍1
AVB源码学习(四):AVB2.0-libavb库介绍1
325 0
|
6月前
|
存储 Linux Android开发
Rockchip系列之VendorStorage uboot/kernel/user space 阶段接口使用介绍(2)
Rockchip系列之VendorStorage uboot/kernel/user space 阶段接口使用介绍(2)
361 0
|
6月前
|
安全 Android开发
AVB源码学习(三):AVB2.0 Init阶段安全启动流程
AVB源码学习(三):AVB2.0 Init阶段安全启动流程
331 0
|
Unix Linux 内存技术
Buildroot系列开发(七)block device9(上)
Buildroot系列开发(七)block device
126 1
Buildroot系列开发(七)block device9(上)
|
内存技术
嵌入式 VFS: Cannot open root device "mtdblock2" or unknown-block(2,0)
系统启动后,虽然nand驱动表现正常,但是最后挂载rootfs时候出错: Kernel command line: root=/dev/mtdblock2 rw init=/linuxrc console=ttyAMA1,115200 mem=64M rootfstype=yaffs2。
2266 0
|
Linux
Linux Buffer I/O error on device dm-4, logical block
Linux服务器日志(Oracle Linux Server release 5.7)里面出现了一些"Buffer I/O error on device dm-4, logical block 0"之类的错误,如下所示: Jul 3 02:33:24 localhost kernel: Buf...
7667 0