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

Commit 35403eba authored by Sandeep Patil's avatar Sandeep Patil
Browse files

init: early_mount: add support to mount non-verity partitions early



This is done by parsing 'androidboot.fstab=<fstab>' kernel cmdline
option to get the fstab file that *only* specifies partitions to be
mounted early (i.e. in init's first stage).

Note that, the same fstab file may not be used as an argument to
mount_all later in the boot as that will cause fs_mgr to fail with
EBUSY.

TODO:
- Possibly add a new mount_mode so the same fstab can be used for
early_mount, 'mount_all --early/--late' etc.
- Add support for dm-verity enabled partitions to mount early.
- Add support for getting fstab arguments through DT instead of kernel
cmdline.

Bug: 27805372

Test:
Boot angler by passing a seperate fstab file using the kernel
cmdline option to mount vendor partition early, remove the vendor
partition entry from the main fstab file for the test.

Boot sailfish by passing a seperate fstab entry via device tree
to mount vendor partition early. Remove vendor partition entry from
the main fstab file for the test

Change-Id: I18785b893c54c8cee960ab44d5e8f83e5d624aa8
Signed-off-by: default avatarSandeep Patil <sspatil@google.com>
parent 44a3ee2c
Loading
Loading
Loading
Loading
+57 −2
Original line number Diff line number Diff line
@@ -25,6 +25,9 @@
#include <stdlib.h>
#include <string.h>

#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/properties.h>

#include "fs_mgr.h"
@@ -77,8 +80,51 @@ static int get_active_slot_suffix_from_misc(struct fstab *fstab,
    return 0;
}

// Gets slot_suffix from either the kernel cmdline / firmware or the
// misc partition. Sets |out_suffix| on success and returns 0. Returns
// finds slot_suffix in androidboot.slot_suffix kernel command line argument
// or in the device tree node at /firmware/android/slot_suffix property
static int get_active_slot_suffix_from_kernel(char *out_suffix,
                                              size_t suffix_len)
{
    std::string cmdline;
    if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
        for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
            std::vector<std::string> pieces = android::base::Split(entry, "=");
            if (pieces.size() == 2) {
                if (pieces[0] == "androidboot.slot_suffix") {
                    strncpy(out_suffix, pieces[1].c_str(), suffix_len);
                    return 0;
                }
            }
        }
    }

    // if we can't find slot_suffix in cmdline, check the DT
    static constexpr char android_dt_dir[] = "/proc/device-tree/firmware/android";
    std::string file_name = android::base::StringPrintf("%s/compatible", android_dt_dir);
    std::string dt_value;
    if (android::base::ReadFileToString(file_name, &dt_value)) {
        if (!dt_value.compare("android,firmware")) {
            LERROR << "Error finding compatible android DT node";
            return -1;
        }

        file_name = android::base::StringPrintf("%s/%s", android_dt_dir, "slot_suffix");
        if (!android::base::ReadFileToString(file_name, &dt_value)) {
            LERROR << "Error finding slot_suffix in device tree";
            return -1;
        }

        // DT entries have a terminating '\0', so 'suffix_len' is safe.
        strncpy(out_suffix, dt_value.c_str(), suffix_len);
        return 0;
    }

    // slot_suffix missing in kernel cmdline or device tree
    return -1;
}

// Gets slot_suffix from either the kernel cmdline / device tree / firmware
// or the misc partition. Sets |out_suffix| on success and returns 0. Returns
// -1 if slot_suffix could not be determined.
static int get_active_slot_suffix(struct fstab *fstab, char *out_suffix,
                                  size_t suffix_len)
@@ -94,6 +140,15 @@ static int get_active_slot_suffix(struct fstab *fstab, char *out_suffix,
        return 0;
    }

    // if the property is not set, we are either being invoked too early
    // or the slot suffix in mentioned in the misc partition. If its
    // "too early", try to find the slotsuffix ourselves in the kernel command
    // line or the device tree
    if (get_active_slot_suffix_from_kernel(out_suffix, suffix_len) == 0) {
        LINFO << "Using slot suffix '" << out_suffix << "' from kernel";
        return 0;
    }

    // If we couldn't get the suffix from the kernel cmdline, try the
    // the misc partition.
    if (get_active_slot_suffix_from_misc(fstab, out_suffix, suffix_len) == 0) {
+1 −0
Original line number Diff line number Diff line
@@ -123,6 +123,7 @@ int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab);
int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab);
int fs_mgr_is_notrim(struct fstab_rec *fstab);
int fs_mgr_is_formattable(struct fstab_rec *fstab);
int fs_mgr_is_slotselect(struct fstab_rec *fstab);
int fs_mgr_is_nofail(struct fstab_rec *fstab);
int fs_mgr_is_latemount(struct fstab_rec *fstab);
int fs_mgr_is_quota(struct fstab_rec *fstab);
+21 −3
Original line number Diff line number Diff line
@@ -62,7 +62,7 @@ static const char *firmware_dirs[] = { "/etc/firmware",

extern struct selabel_handle *sehandle;

static int device_fd = -1;
static android::base::unique_fd device_fd;

struct perms_ {
    char *name;
@@ -341,6 +341,19 @@ static void remove_platform_device(const char *path)
    }
}

static void destroy_platform_devices() {
    struct listnode* node;
    struct listnode* n;
    struct platform_node* bus;

    list_for_each_safe(node, n, &platform_names) {
        list_remove(node);
        bus = node_to_item(node, struct platform_node, list);
        free(bus->path);
        free(bus);
    }
}

/* Given a path that may start with a PCI device, populate the supplied buffer
 * with the PCI domain/bus number and the peripheral ID and return 0.
 * If it doesn't start with a PCI device, or there is some error, return -1 */
@@ -507,7 +520,7 @@ static char **get_block_device_symlinks(struct uevent *uevent)
        return NULL;
    memset(links, 0, sizeof(char *) * 4);

    LOG(INFO) << "found " << type << " device " << device;
    LOG(VERBOSE) << "found " << type << " device " << device;

    snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device);

@@ -990,7 +1003,7 @@ void device_init(const char* path, coldboot_callback fn) {
    selinux_status_open(true);

    /* is 256K enough? udev uses 16MB! */
    device_fd = uevent_open_socket(256*1024, true);
    device_fd.reset(uevent_open_socket(256*1024, true));
    if (device_fd == -1) {
        return;
    }
@@ -1024,6 +1037,11 @@ void device_init(const char* path, coldboot_callback fn) {
    LOG(INFO) << "Coldboot took " << t;
}

void device_close() {
    destroy_platform_devices();
    device_fd.reset();
}

int get_device_fd() {
    return device_fd;
}
+1 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ struct uevent {
typedef std::function<coldboot_action_t(struct uevent* uevent)> coldboot_callback;
extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr);
extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr);
extern void device_close();

extern int add_dev_perms(const char *name, const char *attr,
                         mode_t perm, unsigned int uid,
+131 −8
Original line number Diff line number Diff line
@@ -638,14 +638,135 @@ static std::string import_cmdline_fstab() {
}

/* Early mount vendor and ODM partitions. The fstab info is read from kernel cmdline. */
static void early_mount() {
    // TODO: read fstab entries from device tree, so early mount
    // entries can be specified for A/B devices where system = root
    std::string fstab = import_cmdline_fstab();
    if (fstab.empty()) {
static bool early_mount() {
    // TODO:  read fstab entries from device tree instead of
    // kernel cmdline
    std::string fstab_file = import_cmdline_fstab();
    if (fstab_file.empty()) {
        LOG(INFO) << "Early mount skipped (missing fstab argument)";
        return;
        return true;
    }

    std::unique_ptr<struct fstab, void(*)(fstab*)> tab(fs_mgr_read_fstab(fstab_file.c_str()),
                                                       fs_mgr_free_fstab);
    if (!tab) {
        LOG(ERROR) << "Early mount failed to read fstab: " << fstab_file;
        // continue to allow booting normally if this happened.
        return true;
    }

    // find out fstab records for odm, system and vendor
    fstab_rec* odm_rec = fs_mgr_get_entry_for_mount_point(tab.get(), "/odm");
    fstab_rec* system_rec = fs_mgr_get_entry_for_mount_point(tab.get(), "/system");
    fstab_rec* vendor_rec = fs_mgr_get_entry_for_mount_point(tab.get(), "/vendor");
    if (!odm_rec && !system_rec && !vendor_rec) {
        // nothing to early mount
        return true;
    }

    // assume A/B device if we find 'slotselect' in any fstab entry
    bool is_ab = ((odm_rec && fs_mgr_is_slotselect(odm_rec)) ||
                  (system_rec && fs_mgr_is_slotselect(system_rec)) ||
                  (vendor_rec && fs_mgr_is_slotselect(vendor_rec)));
    bool found_odm = !odm_rec;
    bool found_system = !system_rec;
    bool found_vendor = !vendor_rec;
    int count_odm = 0, count_vendor = 0, count_system = 0;

    // create the devices we need..
    device_init(nullptr,
        [&](uevent* uevent) -> coldboot_action_t {
            if (!strncmp(uevent->subsystem, "firmware", 8)) {
                return COLDBOOT_CONTINUE;
            }

            // we need platform devices to create symlinks
            if (!strncmp(uevent->subsystem, "platform", 8)) {
                return COLDBOOT_CREATE;
            }

            // Ignore everything that is not a block device
            if (strncmp(uevent->subsystem, "block", 5)) {
                return COLDBOOT_CONTINUE;
            }

            coldboot_action_t ret;
            bool create_this_node = false;
            if (uevent->partition_name) {
                // prefix match partition names so we create device nodes for
                // A/B-ed partitions
                if (!found_odm && !strncmp(uevent->partition_name, "odm", 3)) {
                    LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name
                                 << ") partition";

                    // wait twice for A/B-ed partitions
                    count_odm++;
                    if (!is_ab) {
                        found_odm = true;
                    } else if (count_odm == 2) {
                        found_odm = true;
                    }

                    create_this_node = true;
                } else if (!found_system && !strncmp(uevent->partition_name, "system", 6)) {
                    LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name
                                 << ") partition";

                    count_system++;
                    if (!is_ab) {
                        found_system = true;
                    } else if (count_system == 2) {
                        found_system = true;
                    }

                    create_this_node = true;
                } else if (!found_vendor && !strncmp(uevent->partition_name, "vendor", 6)) {
                    LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name
                                 << ") partition";
                    count_vendor++;
                    if (!is_ab) {
                        found_vendor = true;
                    } else if (count_vendor == 2) {
                        found_vendor = true;
                    }

                    create_this_node = true;
                }
            }

            // if we found all other partitions already, create this
            // node and stop coldboot. If this is a prefix matched
            // partition, create device node and continue. For everything
            // else skip the device node
            if (found_odm && found_system && found_vendor) {
                ret = COLDBOOT_STOP;
            } else if (create_this_node) {
                ret = COLDBOOT_CREATE;
            } else {
                ret = COLDBOOT_CONTINUE;
            }

            return ret;
        });

    // TODO: add support to mount partitions w/ verity

    int ret = 0;
    if (odm_rec &&
            (ret = fs_mgr_do_mount(tab.get(), odm_rec->mount_point, odm_rec->blk_device, NULL))) {
        PLOG(ERROR) << "early_mount: fs_mgr_do_mount returned error for mounting odm";
        return false;
    }

    if (vendor_rec &&
            (ret = fs_mgr_do_mount(tab.get(), vendor_rec->mount_point, vendor_rec->blk_device, NULL))) {
        PLOG(ERROR) << "early_mount: fs_mgr_do_mount returned error for mounting vendor";
        return false;
    }

    device_close();

    return true;
}

int main(int argc, char** argv) {
@@ -694,8 +815,10 @@ int main(int argc, char** argv) {
    LOG(INFO) << "init " << (is_first_stage ? "first" : "second") << " stage started!";

    if (is_first_stage) {
        // Mount devices defined in android.early.* kernel commandline
        early_mount();
        if (!early_mount()) {
            LOG(ERROR) << "Failed to mount required partitions early ...";
            panic();
        }

        // Set up SELinux, loading the SELinux policy.
        selinux_initialize(true);