Merge "init: moving early mount logic into init_first_stage.cpp"
am: bcd36a20d4
Change-Id: I8f8f2dde261ef8322c904bfaa66ab1e0fe9c5a85
This commit is contained in:
commit
2d7818a104
7 changed files with 442 additions and 351 deletions
|
@ -87,6 +87,7 @@ LOCAL_SRC_FILES:= \
|
|||
bootchart.cpp \
|
||||
builtins.cpp \
|
||||
init.cpp \
|
||||
init_first_stage.cpp \
|
||||
keychords.cpp \
|
||||
property_service.cpp \
|
||||
reboot.cpp \
|
||||
|
|
|
@ -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
|
||||
|
|
352
init/init.cpp
352
init/init.cpp
|
@ -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
381
init/init_first_stage.cpp
Normal 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
22
init/init_first_stage.h
Normal 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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue