Merge "init: moving early mount logic into init_first_stage.cpp"

am: bcd36a20d4

Change-Id: I8f8f2dde261ef8322c904bfaa66ab1e0fe9c5a85
This commit is contained in:
Bowgo Tsai 2017-04-25 00:37:21 +00:00 committed by android-build-merger
commit 2d7818a104
7 changed files with 442 additions and 351 deletions

View file

@ -87,6 +87,7 @@ LOCAL_SRC_FILES:= \
bootchart.cpp \
builtins.cpp \
init.cpp \
init_first_stage.cpp \
keychords.cpp \
property_service.cpp \
reboot.cpp \

View file

@ -31,13 +31,13 @@ locations on the system, described below.
at the beginning of its execution. It is responsible for the initial
set up of the system.
Devices that mount /system, /vendor through the early mount mechanism
Devices that mount /system, /vendor through the first stage mount mechanism
load all of the files contained within the
/{system,vendor,odm}/etc/init/ directories immediately after loading
the primary /init.rc. This is explained in more details in the
Imports section of this file.
Legacy devices without the early mount mechanism do the following:
Legacy devices without the first stage mount mechanism do the following:
1. /init.rc imports /init.${ro.hardware}.rc which is the primary
vendor supplied .rc file.
2. During the mount\_all command, the init executable loads all of the
@ -506,7 +506,7 @@ There are only three times where the init executable imports .rc files:
1. When it imports /init.rc or the script indicated by the property
`ro.boot.init_rc` during initial boot.
2. When it imports /{system,vendor,odm}/etc/init/ for early mount
2. When it imports /{system,vendor,odm}/etc/init/ for first stage mount
devices immediately after importing /init.rc.
3. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
paths during mount_all.
@ -519,7 +519,7 @@ different command is to either 1) place it in an Action with an
earlier executed trigger, or 2) place it in an Action with the same
trigger within the same file at an earlier line.
Nonetheless, the defacto order for early mount devices is:
Nonetheless, the defacto order for first stage mount devices is:
1. /init.rc is parsed then recursively each of its imports are
parsed.
2. The contents of /system/etc/init/ are alphabetized and parsed

View file

@ -54,15 +54,13 @@
#include <fstream>
#include <memory>
#include <set>
#include <vector>
#include "action.h"
#include "bootchart.h"
#include "devices.h"
#include "fs_mgr.h"
#include "fs_mgr_avb.h"
#include "import_parser.h"
#include "init_first_stage.h"
#include "init_parser.h"
#include "keychords.h"
#include "log.h"
@ -494,71 +492,10 @@ static void export_kernel_boot_props() {
}
}
/* 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_value_expected(const std::string& dt_file_suffix,
const std::string& expected_value) {
std::string dt_value;
std::string file_name = kAndroidDtDir + dt_file_suffix;
if (read_dt_file(file_name, &dt_value)) {
if (dt_value == expected_value) {
return true;
}
}
return false;
}
static inline bool is_dt_compatible() {
return is_dt_value_expected("compatible", "android,firmware");
}
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");
}
// 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;
if (!is_android_dt_value_expected("compatible", "android,firmware")) {
return;
}
std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(kAndroidDtDir.c_str()), closedir);
if (!dir) return;
@ -963,285 +900,6 @@ static void set_usb_controller() {
}
}
// 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;
device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
if (uevent->device_name == 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;
}
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;
}
setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
for (auto rec : fstab_recs) {
bool need_create_dm_device = false;
if (fs_mgr_is_avb(rec)) {
if (avb_handle->hashtree_disabled()) {
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
// partition_names for caller to check which devices are NOT created.
static void early_device_init(std::set<std::string>* partition_names) {
if (partition_names->empty()) {
return;
}
device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
// we need platform devices to create symlinks
if (uevent->subsystem == "platform") {
return COLDBOOT_CREATE;
}
// Ignore everything that is not a block device
if (uevent->subsystem != "block") {
return COLDBOOT_CONTINUE;
}
if (!uevent->partition_name.empty()) {
// match partition names to create device nodes for partitions
// both partition_names and uevent->partition_name have A/B suffix when A/B is used
auto iter = partition_names->find(uevent->partition_name);
if (iter != partition_names->end()) {
LOG(VERBOSE) << "early_mount: found partition: " << *iter;
partition_names->erase(iter);
if (partition_names->empty()) {
return COLDBOOT_STOP; // found all partitions, stop coldboot
} else {
return COLDBOOT_CREATE; // create this device and continue to find others
}
}
}
// Not found a partition or find an unneeded partition, continue to find others
return COLDBOOT_CONTINUE;
});
}
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;
for (auto fstab_rec : early_fstab_recs) {
// don't allow verifyatboot for early mounted partitions
if (fs_mgr_is_verifyatboot(fstab_rec)) {
LOG(ERROR) << "early_mount: partitions can't be verified at boot";
return false;
}
// check for verified partitions
if (fs_mgr_is_verified(fstab_rec)) {
*out_need_verity = true;
}
// check if verity metadata is on a separate partition and get partition
// name from the end of the ->verity_loc path. verity state is not partition
// specific, so there must be only 1 additional partition that carries
// verity state.
if (fstab_rec->verity_loc) {
if (!meta_partition.empty()) {
LOG(ERROR) << "early_mount: more than one meta partition found: " << meta_partition
<< ", " << basename(fstab_rec->verity_loc);
return false;
} else {
meta_partition = basename(fstab_rec->verity_loc);
}
}
}
// includes those early mount partitions and meta_partition (if any)
// note that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used
for (auto fstab_rec : early_fstab_recs) {
out_partitions->emplace(basename(fstab_rec->blk_device));
}
if (!meta_partition.empty()) {
out_partitions->emplace(std::move(meta_partition));
}
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
if (access("/sbin/recovery", F_OK) == 0) {
LOG(INFO) << "Early mount skipped (recovery mode)";
return true;
}
// first check if device tree fstab entries are compatible
if (!is_dt_fstab_compatible()) {
LOG(INFO) << "Early mount skipped (missing/incompatible fstab in device tree)";
return true;
}
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> tab(
fs_mgr_read_fstab_dt(), fs_mgr_free_fstab);
if (!tab) {
LOG(ERROR) << "Early mount failed to read fstab from device tree";
return false;
}
// find out fstab records for odm, system and vendor
std::vector<fstab_rec*> early_fstab_recs;
for (auto mount_point : {"/odm", "/system", "/vendor"}) {
fstab_rec* fstab_rec = fs_mgr_get_entry_for_mount_point(tab.get(), mount_point);
if (fstab_rec != nullptr) {
early_fstab_recs.push_back(fstab_rec);
}
}
// nothing to 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,
&device_file_by_name_prefix)) {
return false;
}
bool success = false;
// create the devices we need..
early_device_init(&partition_names);
// early_device_init will remove found partitions from partition_names
// So if the partition_names is not empty here, means some partitions
// are not found
if (!partition_names.empty()) {
LOG(ERROR) << "early_mount: partition(s) not found: "
<< android::base::Join(partition_names, ", ");
goto done;
}
if (need_verity) {
// create /dev/device mapper
device_init("/sys/devices/virtual/misc/device-mapper",
[&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
}
if (mount_early_partitions(early_fstab_recs, device_file_by_name_prefix)) {
success = true;
}
done:
device_close();
return success;
}
static void install_reboot_signal_handlers() {
// Instead of panic'ing the kernel as is the default behavior when init crashes,
// we prefer to reboot to bootloader on development builds, as this will prevent
@ -1320,7 +978,7 @@ int main(int argc, char** argv) {
LOG(INFO) << "init first stage started!";
if (!early_mount()) {
if (!DoFirstStageMount()) {
LOG(ERROR) << "Failed to mount required partitions early ...";
panic();
}

381
init/init_first_stage.cpp Normal file
View file

@ -0,0 +1,381 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "init_first_stage.h"
#include <stdlib.h>
#include <unistd.h>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include "devices.h"
#include "fs_mgr.h"
#include "fs_mgr_avb.h"
#include "util.h"
class FirstStageMount {
public:
FirstStageMount();
virtual ~FirstStageMount() = default;
// The factory method to create either FirstStageMountVBootV1 or FirstStageMountVBootV2
// based on device tree configurations.
static std::unique_ptr<FirstStageMount> Create();
bool DoFirstStageMount(); // Mounts fstab entries read from device tree.
protected:
void InitRequiredDevices(std::set<std::string>* devices_partition_names);
void InitVerityDevice(const std::string& verity_device);
bool MountPartitions();
virtual bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
bool* out_need_dm_verity) = 0;
virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
// Device tree fstab entries.
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
// Eligible first stage mount candidates, only allow /system, /vendor and/or /odm.
std::vector<fstab_rec*> mount_fstab_recs_;
};
class FirstStageMountVBootV1 : public FirstStageMount {
public:
FirstStageMountVBootV1() = default;
~FirstStageMountVBootV1() override = default;
protected:
bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
bool* out_need_dm_verity) override;
bool SetUpDmVerity(fstab_rec* fstab_rec) override;
};
class FirstStageMountVBootV2 : public FirstStageMount {
public:
FirstStageMountVBootV2();
~FirstStageMountVBootV2() override = default;
protected:
bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
bool* out_need_dm_verity) override;
bool SetUpDmVerity(fstab_rec* fstab_rec) override;
bool InitAvbHandle();
std::string device_tree_vbmeta_parts_;
std::string device_tree_by_name_prefix_;
FsManagerAvbUniquePtr avb_handle_;
};
FirstStageMount::FirstStageMount() : device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) {
if (!device_tree_fstab_) {
LOG(ERROR) << "Failed to read fstab from device tree";
return;
}
for (auto mount_point : {"/system", "/vendor", "/odm"}) {
fstab_rec* fstab_rec =
fs_mgr_get_entry_for_mount_point(device_tree_fstab_.get(), mount_point);
if (fstab_rec != nullptr) {
mount_fstab_recs_.push_back(fstab_rec);
}
}
}
std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
if (is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta")) {
return std::make_unique<FirstStageMountVBootV2>();
} else {
return std::make_unique<FirstStageMountVBootV1>();
}
}
bool FirstStageMount::DoFirstStageMount() {
// Nothing to mount.
if (mount_fstab_recs_.empty()) return true;
bool need_dm_verity;
std::set<std::string> devices_partition_names;
// The partition name in devices_partition_names MUST have A/B suffix when A/B is used.
if (!GetRequiredDevices(&devices_partition_names, &need_dm_verity)) return false;
if (need_dm_verity) {
device_init("/sys/devices/virtual/misc/device-mapper",
[&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
}
bool success = false;
InitRequiredDevices(&devices_partition_names);
// InitRequiredDevices() will remove found partitions from devices_partition_names.
// So if it isn't empty here, it means some partitions are not found.
if (!devices_partition_names.empty()) {
LOG(ERROR) << __FUNCTION__ << "(): partition(s) not found: "
<< android::base::Join(devices_partition_names, ", ");
} else {
success = true;
}
if (MountPartitions()) success = true;
device_close();
return success;
}
// Creates devices with uevent->partition_name matching one in the in/out
// devices_partition_names. Found partitions will then be removed from the
// devices_partition_names for the caller to check which devices are NOT created.
void FirstStageMount::InitRequiredDevices(std::set<std::string>* devices_partition_names) {
if (devices_partition_names->empty()) {
return;
}
device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
// We need platform devices to create symlinks.
if (uevent->subsystem == "platform") {
return COLDBOOT_CREATE;
}
// Ignores everything that is not a block device.
if (uevent->subsystem != "block") {
return COLDBOOT_CONTINUE;
}
if (!uevent->partition_name.empty()) {
// Matches partition name to create device nodes.
// Both devices_partition_names and uevent->partition_name have A/B
// suffix when A/B is used.
auto iter = devices_partition_names->find(uevent->partition_name);
if (iter != devices_partition_names->end()) {
LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter;
devices_partition_names->erase(iter);
if (devices_partition_names->empty()) {
return COLDBOOT_STOP; // Found all partitions, stop coldboot.
} else {
return COLDBOOT_CREATE; // Creates this device and continue to find others.
}
}
}
// Not found a partition or find an unneeded partition, continue to find others.
return COLDBOOT_CONTINUE;
});
}
// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
void FirstStageMount::InitVerityDevice(const std::string& verity_device) {
const std::string device_name(basename(verity_device.c_str()));
const std::string syspath = "/sys/block/" + device_name;
device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
if (uevent->device_name == device_name) {
LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
return COLDBOOT_STOP;
}
return COLDBOOT_CONTINUE;
});
device_close();
}
bool FirstStageMount::MountPartitions() {
for (auto fstab_rec : mount_fstab_recs_) {
if (!SetUpDmVerity(fstab_rec)) {
PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'";
return false;
}
if (fs_mgr_do_mount_one(fstab_rec)) {
PLOG(ERROR) << "Failed to mount '" << fstab_rec->mount_point << "'";
return false;
}
}
return true;
}
bool FirstStageMountVBootV1::GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
bool* out_need_dm_verity) {
std::string meta_partition;
*out_need_dm_verity = false;
for (auto fstab_rec : mount_fstab_recs_) {
// Don't allow verifyatboot in the first stage.
if (fs_mgr_is_verifyatboot(fstab_rec)) {
LOG(ERROR) << "Partitions can't be verified at boot";
return false;
}
// Checks for verified partitions.
if (fs_mgr_is_verified(fstab_rec)) {
*out_need_dm_verity = true;
}
// Checks if verity metadata is on a separate partition and get partition
// name from the end of the ->verity_loc path. Verity state is not partition
// specific, so there must be only one additional partition that carries
// verity state.
if (fstab_rec->verity_loc) {
if (meta_partition.empty()) {
meta_partition = basename(fstab_rec->verity_loc);
} else if (meta_partition != fstab_rec->verity_loc) {
LOG(ERROR) << "More than one meta partition found: " << meta_partition << ", "
<< basename(fstab_rec->verity_loc);
return false;
}
}
}
// Includes those fstab partitions and meta_partition (if any).
// Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
for (auto fstab_rec : mount_fstab_recs_) {
out_devices_partition_names->emplace(basename(fstab_rec->blk_device));
}
if (!meta_partition.empty()) {
out_devices_partition_names->emplace(std::move(meta_partition));
}
return true;
}
bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
if (fs_mgr_is_verified(fstab_rec)) {
int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */);
if (ret == FS_MGR_SETUP_VERITY_DISABLED) {
LOG(INFO) << "Verity disabled for '" << fstab_rec->mount_point << "'";
} else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
// The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
// Needs to create it because ueventd isn't started in init first stage.
InitVerityDevice(fstab_rec->blk_device);
} else {
return false;
}
}
return true; // Returns true to mount the partition.
}
// FirstStageMountVBootV2 constructor.
// Gets the vbmeta configurations from device tree.
// Specifically, the 'parts' and 'by_name_prefix' below.
// /{
// firmware {
// android {
// vbmeta {
// compatible = "android,vbmeta";
// parts = "vbmeta,boot,system,vendor"
// by_name_prefix = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/"
// };
// };
// };
// }
FirstStageMountVBootV2::FirstStageMountVBootV2() : avb_handle_(nullptr) {
if (!read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts_)) {
PLOG(ERROR) << "Failed to read vbmeta/parts from device tree";
return;
}
// TODO: removes by_name_prefix to allow partitions on different block devices.
if (!read_android_dt_file("vbmeta/by_name_prefix", &device_tree_by_name_prefix_)) {
PLOG(ERROR) << "Failed to read vbmeta/by_name_prefix from dt";
return;
}
}
bool FirstStageMountVBootV2::GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
bool* out_need_dm_verity) {
*out_need_dm_verity = false;
// fstab_rec->blk_device has A/B suffix.
for (auto fstab_rec : mount_fstab_recs_) {
if (fs_mgr_is_avb(fstab_rec)) {
*out_need_dm_verity = true;
}
out_devices_partition_names->emplace(basename(fstab_rec->blk_device));
}
// libavb verifies AVB metadata on all verified partitions at once.
// e.g., The device_tree_vbmeta_parts_ will be "vbmeta,boot,system,vendor"
// for libavb to verify metadata, even if there is only /vendor in the
// above mount_fstab_recs_.
if (*out_need_dm_verity) {
if (device_tree_vbmeta_parts_.empty()) {
LOG(ERROR) << "Missing vbmeta parts in device tree";
return false;
}
std::vector<std::string> partitions = android::base::Split(device_tree_vbmeta_parts_, ",");
std::string ab_suffix = fs_mgr_get_slot_suffix();
for (const auto& partition : partitions) {
// out_devices_partition_names is of type std::set so it's not an issue to emplace
// a partition twice. e.g., /vendor might be in both places:
// - device_tree_vbmeta_parts_ = "vbmeta,boot,system,vendor"
// - mount_fstab_recs_: /vendor_a
out_devices_partition_names->emplace(partition + ab_suffix);
}
}
return true;
}
bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
if (fs_mgr_is_avb(fstab_rec)) {
if (!InitAvbHandle()) return false;
if (avb_handle_->hashtree_disabled()) {
LOG(INFO) << "avb hashtree disabled for '" << fstab_rec->mount_point << "'";
} else if (avb_handle_->SetUpAvb(fstab_rec, false /* wait_for_verity_dev */)) {
// The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
// Needs to create it because ueventd isn't started in init first stage.
InitVerityDevice(fstab_rec->blk_device);
} else {
return false;
}
}
return true; // Returns true to mount the partition.
}
bool FirstStageMountVBootV2::InitAvbHandle() {
if (avb_handle_) return true; // Returns true if the handle is already initialized.
avb_handle_ = FsManagerAvbHandle::Open(device_tree_by_name_prefix_);
if (!avb_handle_) {
PLOG(ERROR) << "Failed to open FsManagerAvbHandle";
return false;
}
// Sets INIT_AVB_VERSION here for init to set ro.boot.avb_version in the second stage.
setenv("INIT_AVB_VERSION", avb_handle_->avb_version().c_str(), 1);
return true;
}
// Public functions
// ----------------
// Mounts /system, /vendor, and/or /odm if they are present in the fstab provided by device tree.
bool DoFirstStageMount() {
// Skips first stage mount if we're in recovery mode.
if (access("/sbin/recovery", F_OK) == 0) {
LOG(INFO) << "First stage mount skipped (recovery mode)";
return true;
}
// Firstly checks if device tree fstab entries are compatible.
if (!is_android_dt_value_expected("fstab/compatible", "android,fstab")) {
LOG(INFO) << "First stage mount skipped (missing/incompatible fstab in device tree)";
return true;
}
std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create();
if (!handle) {
LOG(ERROR) << "Failed to create FirstStageMount";
return false;
}
return handle->DoFirstStageMount();
}

22
init/init_first_stage.h Normal file
View file

@ -0,0 +1,22 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _INIT_FIRST_STAGE_H
#define _INIT_FIRST_STAGE_H
bool DoFirstStageMount();
#endif

View file

@ -369,3 +369,26 @@ std::ostream& operator<<(std::ostream& os, const Timer& t) {
os << t.duration_s() << " seconds";
return os;
}
// Reads the content of device tree file under kAndroidDtDir directory.
// Returns true if the read is success, false otherwise.
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
const std::string file_name = kAndroidDtDir + sub_path;
if (android::base::ReadFileToString(file_name, dt_content)) {
if (!dt_content->empty()) {
dt_content->pop_back(); // Trims the trailing '\0' out.
return true;
}
}
return false;
}
bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content) {
std::string dt_content;
if (read_android_dt_file(sub_path, &dt_content)) {
if (dt_content == expected_content) {
return true;
}
}
return false;
}

View file

@ -29,6 +29,8 @@
#define COLDBOOT_DONE "/dev/.coldboot_done"
const std::string kAndroidDtDir("/proc/device-tree/firmware/android/");
using android::base::boot_clock;
using namespace std::chrono_literals;
@ -72,4 +74,8 @@ bool expand_props(const std::string& src, std::string* dst);
void panic() __attribute__((__noreturn__));
// Reads or compares the content of device tree file under kAndroidDtDir directory.
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
#endif