Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8bba52fc authored by Bowgo Tsai's avatar Bowgo Tsai
Browse files

init: support early_mount with vboot 2.0 (external/avb/libavb)

libavb requires verifying AVB metadata on all verified partitions at
once. For example, /vbmeta, /boot, /system and /vendor. We need to
invoke device_init() for those partitions even if we only want to early
mount some of them, like /vendor and /system.

This CL gets all AVB partitions and the early mount partitions from
device tree through "firmware/android/vbmeta" and "firmware/fstab",
respectively. The following is an example to early mount /vendor
partition on bullhead:

firmware {
    android {
        compatible = "android,firmware";
        vbmeta {
            compatible = "android,vbmeta";
            parts = "boot,system,vendor";
            by_name_prefix="/dev/block/platform/soc.0/f9824900.sdhci/by-name"
        };
        fstab {
            compatible = "android,fstab";
            vendor {
                compatible = "android,vendor";
                dev = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor";
                type = "ext4";
                mnt_flags = "ro,barrier=1,inode_readahead_blks=8";
                fsmgr_flags = "wait,avb";
            };
        };
    };
};

Bug: 33254008
Test: early mount /vendor with vboot 2.0 (AVB) on bullhead
Test: early mount /system without dm-verity on bullhead
Test: early mount /vendor with vboot 1.0 on sailfish

Change-Id: I89a1f77c97124f309346b33d9e700544b92ecf05
parent 80d1ad17
Loading
Loading
Loading
Loading
+177 −50
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@
#include "bootchart.h"
#include "devices.h"
#include "fs_mgr.h"
#include "fs_mgr_avb.h"
#include "import_parser.h"
#include "init_parser.h"
#include "keychords.h"
@@ -482,42 +483,73 @@ static void export_kernel_boot_props() {
    }
}

static constexpr char android_dt_dir[] = "/proc/device-tree/firmware/android";
/* Reads the content of device tree file into dt_value.
 * Returns true if the read is success, false otherwise.
 */
static bool read_dt_file(const std::string& file_name, std::string* dt_value) {
    if (android::base::ReadFileToString(file_name, dt_value)) {
        if (!dt_value->empty()) {
            dt_value->pop_back();  // Trim the trailing '\0' out.
            return true;
        }
    }
    return false;
}

static const std::string kAndroidDtDir("/proc/device-tree/firmware/android/");

static bool is_dt_compatible() {
static bool is_dt_value_expected(const std::string& dt_file_suffix,
                                 const std::string& expected_value) {
    std::string dt_value;
    std::string file_name = StringPrintf("%s/compatible", android_dt_dir);
    std::string file_name = kAndroidDtDir + dt_file_suffix;

    if (android::base::ReadFileToString(file_name, &dt_value)) {
        // trim the trailing '\0' out, otherwise the comparison
        // will produce false-negatives.
        dt_value.resize(dt_value.size() - 1);
        if (dt_value == "android,firmware") {
    if (read_dt_file(file_name, &dt_value)) {
        if (dt_value == expected_value) {
            return true;
        }
    }

    return false;
}

static bool is_dt_fstab_compatible() {
    std::string dt_value;
    std::string file_name = StringPrintf("%s/%s/compatible", android_dt_dir, "fstab");
static inline bool is_dt_compatible() {
    return is_dt_value_expected("compatible", "android,firmware");
}

    if (android::base::ReadFileToString(file_name, &dt_value)) {
        dt_value.resize(dt_value.size() - 1);
        if (dt_value == "android,fstab") {
            return true;
static inline bool is_dt_fstab_compatible() {
    return is_dt_value_expected("fstab/compatible", "android,fstab");
}

static inline bool is_dt_vbmeta_compatible() {
    return is_dt_value_expected("vbmeta/compatible", "android,vbmeta");
}

    return false;
// Gets the vbmeta config from device tree. Specifically, the 'parts' and 'by_name_prefix'.
// /{
//     firmware {
//         android {
//             vbmeta {
//                 compatible = "android,vbmeta";
//                 parts = "vbmeta,boot,system,vendor"
//                 by_name_prefix="/dev/block/platform/soc.0/f9824900.sdhci/by-name/"
//             };
//         };
//     };
//  }
static bool get_vbmeta_config_from_dt(std::string* vbmeta_partitions,
                                      std::string* device_file_by_name_prefix) {
    std::string file_name = kAndroidDtDir + "vbmeta/parts";
    if (!read_dt_file(file_name, vbmeta_partitions)) return false;

    file_name = kAndroidDtDir + "vbmeta/by_name_prefix";
    if (!read_dt_file(file_name, device_file_by_name_prefix)) return false;

    return true;
}

static void process_kernel_dt() {
    if (!is_dt_compatible()) return;

    std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dt_dir), closedir);
    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(kAndroidDtDir.c_str()), closedir);
    if (!dir) return;

    std::string dt_file;
@@ -527,7 +559,7 @@ static void process_kernel_dt() {
            continue;
        }

        std::string file_name = StringPrintf("%s/%s", android_dt_dir, dp->d_name);
        std::string file_name = kAndroidDtDir + dp->d_name;

        android::base::ReadFileToString(file_name, &dt_file);
        std::replace(dt_file.begin(), dt_file.end(), ',', '.');
@@ -920,38 +952,100 @@ static void set_usb_controller() {
    }
}

static bool early_mount_one(struct fstab_rec* rec) {
    if (rec && fs_mgr_is_verified(rec)) {
        // setup verity and create the dm-XX block device
        // needed to mount this partition
        int ret = fs_mgr_setup_verity(rec, false);
        if (ret == FS_MGR_SETUP_VERITY_FAIL) {
            PLOG(ERROR) << "early_mount: Failed to setup verity for '" << rec->mount_point << "'";
            return false;
        }
// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
static void device_init_dm_device(const std::string& dm_device) {
    const std::string device_name(basename(dm_device.c_str()));
    const std::string syspath = "/sys/block/" + device_name;

        // The exact block device name is added as a mount source by
        // fs_mgr_setup_verity() in ->blk_device as "/dev/block/dm-XX"
        // We create that device by running coldboot on /sys/block/dm-XX
        std::string dm_device(basename(rec->blk_device));
        std::string syspath = StringPrintf("/sys/block/%s", dm_device.c_str());
    device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
            if (uevent->device_name && !strcmp(dm_device.c_str(), uevent->device_name)) {
        if (uevent->device_name && device_name == uevent->device_name) {
            LOG(VERBOSE) << "early_mount: creating dm-verity device : " << dm_device;
            return COLDBOOT_STOP;
        }
        return COLDBOOT_CONTINUE;
    });
    device_close();
}

static bool vboot_1_0_mount_partitions(const std::vector<fstab_rec*>& fstab_recs) {
    if (fstab_recs.empty()) return false;

    for (auto rec : fstab_recs) {
        bool need_create_dm_device = false;
        if (fs_mgr_is_verified(rec)) {
            // setup verity and create the dm-XX block device
            // needed to mount this partition
            int ret = fs_mgr_setup_verity(rec, false /* wait_for_verity_dev */);
            if (ret == FS_MGR_SETUP_VERITY_DISABLED) {
                LOG(INFO) << "verity disabled for '" << rec->mount_point << "'";
            } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
                need_create_dm_device = true;
            } else {
                PLOG(ERROR) << "early_mount: failed to setup verity for '" << rec->mount_point
                            << "'";
                return false;
            }
        }
        if (need_create_dm_device) {
            // The exact block device name (rec->blk_device) is changed to "/dev/block/dm-XX".
            // Need to create it because ueventd isn't started during early mount.
            device_init_dm_device(rec->blk_device);
        }
        if (fs_mgr_do_mount_one(rec)) {
            PLOG(ERROR) << "early_mount: failed to mount '" << rec->mount_point << "'";
            return false;
        }
    }

    return true;
}

    if (rec && fs_mgr_do_mount_one(rec)) {
        PLOG(ERROR) << "early_mount: Failed to mount '" << rec->mount_point << "'";
static bool vboot_2_0_mount_partitions(const std::vector<fstab_rec*>& fstab_recs,
                                       const std::string& device_file_by_name_prefix) {
    if (fstab_recs.empty()) return false;

    FsManagerAvbUniquePtr avb_handle = FsManagerAvbHandle::Open(device_file_by_name_prefix);
    if (!avb_handle) {
        LOG(INFO) << "Failed to Open FsManagerAvbHandle";
        return false;
    }

    for (auto rec : fstab_recs) {
        bool need_create_dm_device = false;
        if (fs_mgr_is_avb(rec)) {
            if (avb_handle->AvbHashtreeDisabled()) {
                LOG(INFO) << "avb hashtree disabled for '" << rec->mount_point << "'";
            } else if (avb_handle->SetUpAvb(rec, false /* wait_for_verity_dev */)) {
                need_create_dm_device = true;
            } else {
                PLOG(ERROR) << "early_mount: failed to set up AVB on partition: '"
                            << rec->mount_point << "'";
                return false;
            }
        }
        if (need_create_dm_device) {
            // The exact block device name (rec->blk_device) is changed to "/dev/block/dm-XX".
            // Need to create it because ueventd isn't started during early mount.
            device_init_dm_device(rec->blk_device);
        }
        if (fs_mgr_do_mount_one(rec)) {
            PLOG(ERROR) << "early_mount: failed to mount '" << rec->mount_point << "'";
            return false;
        }
    }

    return true;
}

static bool mount_early_partitions(const std::vector<fstab_rec*>& fstab_recs,
                                   const std::string& device_file_by_name_prefix) {
    if (is_dt_vbmeta_compatible()) {  // AVB (external/avb) is used to setup dm-verity.
        return vboot_2_0_mount_partitions(fstab_recs, device_file_by_name_prefix);
    } else {
        return vboot_1_0_mount_partitions(fstab_recs);
    }
}

// Creates devices with uevent->partition_name matching one in the in/out
// partition_names. Note that the partition_names MUST have A/B suffix
// when A/B is used. Found partitions will then be removed from the
@@ -994,12 +1088,10 @@ static void early_device_init(std::set<std::string>* partition_names) {
    });
}

static bool get_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs,
                                 std::set<std::string>* out_partitions, bool* out_need_verity) {
static bool vboot_1_0_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs,
                                       std::set<std::string>* out_partitions,
                                       bool* out_need_verity) {
    std::string meta_partition;
    out_partitions->clear();
    *out_need_verity = false;

    for (auto fstab_rec : early_fstab_recs) {
        // don't allow verifyatboot for early mounted partitions
        if (fs_mgr_is_verifyatboot(fstab_rec)) {
@@ -1038,6 +1130,40 @@ static bool get_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs
    return true;
}

// a.k.a. AVB (external/avb)
static bool vboot_2_0_early_partitions(std::set<std::string>* out_partitions, bool* out_need_verity,
                                       std::string* out_device_file_by_name_prefix) {
    std::string vbmeta_partitions;
    if (!get_vbmeta_config_from_dt(&vbmeta_partitions, out_device_file_by_name_prefix)) {
        return false;
    }
    // libavb verifies AVB metadata on all verified partitions at once.
    // e.g., The vbmeta_partitions will be "vbmeta,boot,system,vendor"
    // for libavb to verify metadata, even if we only need to early mount /vendor.
    std::vector<std::string> partitions = android::base::Split(vbmeta_partitions, ",");
    std::string ab_suffix = fs_mgr_get_slot_suffix();
    for (const auto& partition : partitions) {
        out_partitions->emplace(partition + ab_suffix);
    }
    *out_need_verity = true;
    return true;
}

static bool get_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs,
                                 std::set<std::string>* out_partitions, bool* out_need_verity,
                                 std::string* out_device_file_by_name_prefix) {
    *out_need_verity = false;
    out_partitions->clear();
    out_device_file_by_name_prefix->clear();

    if (is_dt_vbmeta_compatible()) {  // AVB (external/avb) is used to setup dm-verity.
        return vboot_2_0_early_partitions(out_partitions, out_need_verity,
                                          out_device_file_by_name_prefix);
    } else {
        return vboot_1_0_early_partitions(early_fstab_recs, out_partitions, out_need_verity);
    }
}

/* Early mount vendor and ODM partitions. The fstab is read from device-tree. */
static bool early_mount() {
    // skip early mount if we're in recovery mode
@@ -1072,9 +1198,11 @@ static bool early_mount() {
    if (early_fstab_recs.empty()) return true;

    bool need_verity;
    std::string device_file_by_name_prefix;
    std::set<std::string> partition_names;
    // partition_names MUST have A/B suffix when A/B is used
    if (!get_early_partitions(early_fstab_recs, &partition_names, &need_verity)) {
    if (!get_early_partitions(early_fstab_recs, &partition_names, &need_verity,
                              &device_file_by_name_prefix)) {
        return false;
    }

@@ -1097,10 +1225,9 @@ static bool early_mount() {
                    [&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
    }

    for (auto fstab_rec : early_fstab_recs) {
        if (!early_mount_one(fstab_rec)) goto done;
    }
    if (mount_early_partitions(early_fstab_recs, device_file_by_name_prefix)) {
        success = true;
    }

done:
    device_close();