本文主要关注 Android 系统底层的 Reboot 流程,主要涉及 Native、Kenrel、Recovery、Bootloader。

Framework 中 Reboot 流程

Reboot 在 Android 系统中主要通过物理按键或UI菜单进行触发,最终由 PowerManager 执行 Reboot 流程。下图描述了 Reboot 执行时,Framework 中相关线程的状态,最终将 Reboot 相关信息设置到属性 sys.powerctl 中。Framework 中的具体流程本文不再描述。

Init 中 Reboot 流程

Android framework 处理完 Reboot 流程后,更新了属性 sys.powerctl。Init 正是依靠该属性来执行底层 Reboot 动作。Init 对 Reboot 的处理主要为以下几个方面:

1,进程监控属性 sys.powerctl 的改变。

/system/core/init/init.cpp

void PropertyChanged(const std::string& name, const std::string& value) {
    // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
    // This is to ensure that init will always and immediately shutdown/reboot, regardless of
    // if there are other pending events to process or if init is waiting on an exec service or
    // waiting on a property.
    // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
    // commands to be executed.
    // sys.powerctl 做为特殊属性来处理,直接触发 shutdown/reboot 流程。
    if (name == "sys.powerctl") {
        trigger_shutdown(value);
    }

    if (property_triggers_enabled) {
        ActionManager::GetInstance().QueuePropertyChange(name, value);
        WakeMainInitThread();
    }

    prop_waiter_state.CheckAndResetWait(name, value);
}

2,真正 shutdown/reboot 的流程在 HandlePowerctlMessage(),对属性 sys.powerctl 进行解析,并存储相关信息。

/system/core/init/reboot.cpp

void HandlePowerctlMessage(const std::string& command) {
    unsigned int cmd = 0;
    std::vector<std::string> cmd_params = Split(command, ",");
    std::string reboot_target = "";
    bool run_fsck = false;
    bool command_invalid = false;
    bool userspace_reboot = false;

    // 解析 shutdown 参数
    if (cmd_params[0] == "shutdown") {
        cmd = ANDROID_RB_POWEROFF;
        if (cmd_params.size() >= 2) {
            if (cmd_params[1] == "userrequested") { // shutdown,userrequested
                // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
                // Run fsck once the file system is remounted in read-only mode.
                run_fsck = true;
            } else if (cmd_params[1] == "thermal") { // shutdown,thermal
                // Turn off sources of heat immediately.
                TurnOffBacklight();
                // run_fsck is false to avoid delay
                cmd = ANDROID_RB_THERMOFF;
            }
        }
    // 解析 reboot 参数
    } else if (cmd_params[0] == "reboot") {
        cmd = ANDROID_RB_RESTART2;
        if (cmd_params.size() >= 2) {
            reboot_target = cmd_params[1];
            if (reboot_target == "userspace") { // reboot,userspace
                LOG(INFO) << "Userspace reboot requested";
                userspace_reboot = true;
            }
            // adb reboot fastboot should boot into bootloader for devices not
            // supporting logical partitions.
            if (reboot_target == "fastboot" &&
                !android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
                reboot_target = "bootloader"; // 在非动态分区的系统上,reboot后进入bootloader
            }
            // When rebooting to the bootloader notify the bootloader writing
            // also the BCB.
            if (reboot_target == "bootloader") { // reboot,bootloader
                std::string err;
                if (!write_reboot_bootloader(&err)) { // 更新BCB
                    LOG(ERROR) << "reboot-bootloader: Error writing "
                                  "bootloader_message: "
                               << err;
                }
            } else if (reboot_target == "recovery") { // reboot,recovery
                bootloader_message boot = {};
                if (std::string err; !read_bootloader_message(&boot, &err)) {
                    LOG(ERROR) << "Failed to read bootloader message: " << err;
                }
                // Update the boot command field if it's empty, and preserve
                // the other arguments in the bootloader message.
                if (!CommandIsPresent(&boot)) { // 更新BCB
                    strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
                    if (std::string err; !write_bootloader_message(boot, &err)) {
                        LOG(ERROR) << "Failed to set bootloader message: " << err;
                        return;
                    }
                }
            } else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
                       reboot_target == "fastboot") { // reboot,fastboot
                std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"
                                                                          : reboot_target;
                const std::vector<std::string> options = {
                        "--" + arg,
                };
                std::string err;
                if (!write_bootloader_message(options, &err)) { // 更新BCB
                    LOG(ERROR) << "Failed to set bootloader message: " << err;
                    return;
                }
                reboot_target = "recovery"; // reboot后进入recovery
            }

            // If there are additional parameter, pass them along
            for (size_t i = 2; (cmd_params.size() > i) && cmd_params[i].size(); ++i) {
                reboot_target += "," + cmd_params[i];
            }
        }
    } else {
        command_invalid = true;
    }
    if (command_invalid) {
        LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
        return;
    }

    // We do not want to process any messages (queue'ing triggers, shutdown messages, control
    // messages, etc) from properties during reboot.
    StopSendingMessages(); // 停止所有的属性处理

    if (userspace_reboot) { // reboot,userspace 执行用户空间重启,并不重启整个系统
        HandleUserspaceReboot();
        return;
    }

    LOG(INFO) << "Clear action queue and start shutdown trigger";
    ActionManager::GetInstance().ClearQueue(); // 清空init action队列
    // Queue shutdown trigger first
    ActionManager::GetInstance().QueueEventTrigger("shutdown"); // 执行init中的shutdown action
    // Queue built-in shutdown_done
    auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
        DoReboot(cmd, command, reboot_target, run_fsck); // 执行 shutdown/reboot 动作
        return Result<void>{};
    };
    ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");

    EnterShutdown(); // 清理相关资源
}

3,DoReboot() 执行 shutdown/reboot 动作

/system/core/init/reboot.cpp

static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& reboot_target,
                     bool run_fsck) {
    Timer t;
    LOG(INFO) << "Reboot start, reason: " << reason << ", reboot_target: " << reboot_target;
        
    bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;

    // 配置shutdown timeout时间,缺省是6秒
    auto shutdown_timeout = 0ms;
    if (!SHUTDOWN_ZERO_TIMEOUT) {
        constexpr unsigned int shutdown_timeout_default = 6;
        constexpr unsigned int max_thermal_shutdown_timeout = 3;
        auto shutdown_timeout_final = android::base::GetUintProperty("ro.build.shutdown_timeout",
                                                                     shutdown_timeout_default);
        if (is_thermal_shutdown && shutdown_timeout_final > max_thermal_shutdown_timeout) {
            shutdown_timeout_final = max_thermal_shutdown_timeout;
        }
        shutdown_timeout = std::chrono::seconds(shutdown_timeout_final);
    }
    ......
    // Start a thread to monitor init shutdown process
    // 启动一个reboot监控线程
    LOG(INFO) << "Create reboot monitor thread.";
    bool reboot_monitor_run = true;
    std::thread reboot_monitor_thread(&RebootMonitorThread, cmd, reboot_target, &reboot_semaphore,
                                      shutdown_timeout, &reboot_monitor_run);
    reboot_monitor_thread.detach();
    ......
    // 保存reboot原因到属性中
    std::vector<std::string> reasons = Split(reason, ",");
    if (reasons.size() >= 2 && reasons[0] == "reboot" &&
        (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
         reasons[1] == "hard" || reasons[1] == "warm")) {
        skip = strlen("reboot,");
    }
    PersistRebootReason(reason.c_str() + skip, true);
    ......
    // 安全关闭watchdogd 
    const std::set<std::string> to_starts{"watchdogd"};
    std::set<std::string> stop_first;
    for (const auto& s : ServiceList::GetInstance()) {
        ......
    }

    // remaining operations (specifically fsck) may take a substantial duration
    if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown) {
        TurnOffBacklight(); // 先关背光
    }

    // 显示shutdown animation
    Service* boot_anim = ServiceList::GetInstance().FindService("bootanim");
    Service* surface_flinger = ServiceList::GetInstance().FindService("surfaceflinger");
    if (boot_anim != nullptr && surface_flinger != nullptr && surface_flinger->IsRunning()) {
        ......
    }

    // optional shutdown step
    // 1. terminate all services except shutdown critical ones. wait for delay to finish
    if (shutdown_timeout > 0ms) { // 使用SIGTERM终止所有非关键服务
        StopServicesAndLogViolations(stop_first, shutdown_timeout / 2, true /* SIGTERM */);
    }
    // Send SIGKILL to ones that didn't terminate cleanly.
    StopServicesAndLogViolations(stop_first, 0ms, false /* SIGKILL */); // 使用SIGKILL终止所有非关键服务
    SubcontextTerminate();
    // Reap subcontext pids.
    ReapAnyOutstandingChildren();

    // 3. send volume abort_fuse and volume shutdown to vold
    Service* vold_service = ServiceList::GetInstance().FindService("vold");
    if (vold_service != nullptr && vold_service->IsRunning()) {
        // Manually abort FUSE connections, since the FUSE daemon is already dead
        // at this point, and unmounting it might hang.
        CallVdc("volume", "abort_fuse");
        CallVdc("volume", "shutdown");
        vold_service->Stop(); // 关闭vold服务
    } else {
        LOG(INFO) << "vold not running, skipping vold shutdown";
    }
    // logcat stopped here
    StopServices(kDebuggingServices, 0ms, false /* SIGKILL */);
    // 4. sync, try umount, and optionally run fsck for user shutdown
    {
        Timer sync_timer;
        LOG(INFO) << "sync() before umount...";
        sync(); // 同步文件系统
        LOG(INFO) << "sync() before umount took" << sync_timer;
    }
    // 5. drop caches and disable zram backing device, if exist
    KillZramBackingDevice(); // kill ZRAM服务

    LOG(INFO) << "Ready to unmount apexes. So far shutdown sequence took " << t;
    // 6. unmount active apexes, otherwise they might prevent clean unmount of /data.
    if (auto ret = UnmountAllApexes(); !ret.ok()) {
        LOG(ERROR) << ret.error();
    }
    UmountStat stat = // unmount
            TryUmountAndFsck(cmd, run_fsck, shutdown_timeout - t.duration(), &reboot_semaphore);
    // Follow what linux shutdown is doing: one more sync with little bit delay
    {
        Timer sync_timer;
        LOG(INFO) << "sync() after umount...";
        sync(); // 再次同步文件系统
        LOG(INFO) << "sync() after umount took" << sync_timer;
    }
    if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
    LogShutdownTime(stat, &t);

    // Send signal to terminate reboot monitor thread.
    reboot_monitor_run = false;
    sem_post(&reboot_semaphore);

    // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
    RebootSystem(cmd, reboot_target); // 执行系统reboot
    abort();
}    

4,通过RebootSystem() 执行系统 Reboot 调用。

/system/core/init/reboot_utils.cpp

void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
    LOG(INFO) << "Reboot ending, jumping to kernel";

    if (!IsRebootCapable()) {
        // On systems where init does not have the capability of rebooting the
        // device, just exit cleanly.
        exit(0);
    }

    switch (cmd) {
        case ANDROID_RB_POWEROFF: // 执行关机
            reboot(RB_POWER_OFF);
            break;

        case ANDROID_RB_RESTART2: // 执行重启
            syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                    LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
            break;

        case ANDROID_RB_THERMOFF: // 过热保护,根据属性来执行关机或重起
            if (android::base::GetBoolProperty("ro.thermal_warmreset", false)) {
                LOG(INFO) << "Try to trigger a warm reset for thermal shutdown";
                static constexpr const char kThermalShutdownTarget[] = "shutdown,thermal";
                syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                        LINUX_REBOOT_CMD_RESTART2, kThermalShutdownTarget);
            } else {
                reboot(RB_POWER_OFF);
            }
            break;
    }
    // In normal case, reboot should not return.
    PLOG(ERROR) << "reboot call returned";
    abort();
}

属性 sys.powerctl 的值决定了shutdown/reboot的行为,其格式为:[mode],[reason]。mode 为 reboot 或 shutdown,常见reason如下:

shutdown,[reason]userrequestedthermal<null>
用户请求关机,需要运行fsck检查温度异常引起的关机执行基本关机流程
reboot,[reason]userspacefastbootbootloaderrecoverysideloadsideload-auto-rebootcold / warm / hard / <null>
用户空间软重启,用于更新应用重启到fastboot模式。不支持逻辑分区时,重启到bootloader模式。写入BCB重启到bootloader模式。写入BCB重启进入recvoery。写入BCB重启进入recovery,执行sideload,用于本地升级系统。写入BCBsideload完成后自动重启。写入BCB执行基本重启流程

内核中 Reboot 流程

Android Native 中最终执行了 reboot 系统调用,对应在内核中的入口为:

/kernel/reboot.c
    
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
        void __user *, arg)
{
    ......
    mutex_lock(&system_transition_mutex);
    switch (cmd) {
    case LINUX_REBOOT_CMD_RESTART:
        kernel_restart(NULL);
        break;
    ........
    case LINUX_REBOOT_CMD_POWER_OFF: // 关机
        kernel_power_off();
        do_exit(0);
        break;

    case LINUX_REBOOT_CMD_RESTART2: // 重启
        ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
        if (ret < 0) {
            ret = -EFAULT;
            break;
        }
        buffer[sizeof(buffer) - 1] = '\0';

        kernel_restart(buffer);
        break;
    ........
}

内核通过 kernel_power_off() 完成关机动作,通过 kernel_restart() 完成重启动作。

/kernel/reboot.c

void kernel_restart(char *cmd)
{
    kernel_restart_prepare(cmd); // 执行重启的准备工作:调用reboot通知队列,关闭usermodehelper,关闭所有设备
    migrate_to_reboot_cpu(); // 迁移所有任务到cpu0上
    syscore_shutdown(); // 关闭syscore设备
    if (!cmd)
        pr_emerg("Restarting system\n");
    else
        pr_emerg("Restarting system with command '%s'\n", cmd);
    kmsg_dump(KMSG_DUMP_SHUTDOWN);
    machine_restart(cmd); // 调用machine_restart()
}
EXPORT_SYMBOL_GPL(kernel_restart);
......
void kernel_power_off(void)
{
    kernel_shutdown_prepare(SYSTEM_POWER_OFF); // 执行重启的准备工作:调用reboot通知队列,关闭usermodehelper,关闭所有设备
    if (pm_power_off_prepare)
        pm_power_off_prepare();
    migrate_to_reboot_cpu(); // 迁移所有任务到cpu0上
    syscore_shutdown(); // 关闭syscore设备
    pr_emerg("Power down\n");
    kmsg_dump(KMSG_DUMP_SHUTDOWN);
    machine_power_off(); // 调用machine_power_off()
}
EXPORT_SYMBOL_GPL(kernel_power_off);

Reboot 和 Power Off 的大致流程是一样的,主要区别在调用reboot通知队列的传参不同和machine执行函数不同。这里简单看一下 ARM64 的 machine_restart() 函数。

/arch/arm64/kernel/process.c

void machine_restart(char *cmd)
{
    /* Disable interrupts first */
    local_irq_disable(); // 关闭中断
    smp_send_stop(); // 停止当前处理器外的所有处理器

    /*
     * UpdateCapsule() depends on the system being reset via
     * ResetSystem().
     */
    if (efi_enabled(EFI_RUNTIME_SERVICES))
        efi_reboot(reboot_mode, NULL); // EFI系统时

    /* Now call the architecture specific reboot code. */
    do_kernel_restart(cmd); // 调用restart处理队列

    /*
     * Whoops - the architecture was unable to reboot.
     */
    printk("Reboot failed -- System halted\n");
    while (1);
}


/kernel/reboot.c

/**
 *    do_kernel_restart - Execute kernel restart handler call chain
 *
 *    Calls functions registered with register_restart_handler.
 *
 *    Expected to be called from machine_restart as last step of the restart
 *    sequence.
 *
 *    Restarts the system immediately if a restart handler function has been
 *    registered. Otherwise does nothing.
 */
void do_kernel_restart(char *cmd)
{
    atomic_notifier_call_chain(&restart_handler_list, reboot_mode, cmd);
}

内核中的 Reboot 流程比较简单,核心就是处理内核、芯片、外设的状态,然后进行重启。

Reboot 后的流程

重启后,硬件相当于重新上电,最先进入 Bootloader,Bootloader 会根据 Reboot Reason 进入到不同的系统状态。通常来说,Bootloader 会分为多级,每家芯片原厂的实现都会有些区别,这里不去分析客制化的代码,只看一下 Android 在 U-boot 中对 Reboot 的处理。

/u-boot/common/android_bootloader.c

int android_bootloader_boot_flow(const char* iface_str,
                 const char* dev_str,
                 struct blk_desc *dev_desc,
                 const struct disk_partition *misc_part_info,
                 const char *slot,
                 bool verify,
                 unsigned long kernel_address,
                 struct blk_desc *persistant_dev_desc)
{
    ......
    /* Determine the boot mode and clear its value for the next boot if
     * needed.
     */
    // 根据misc分区信息获取启动模式
    mode = android_bootloader_load_and_clear_mode(dev_desc, misc_part_info);
    printf("ANDROID: reboot reason: \"%s\"\n", android_boot_mode_str(mode));
    // TODO (rammuthiah) fastboot isn't suported on cuttlefish yet.
    // Once it is, these lines can be removed.
    if (mode == ANDROID_BOOT_MODE_BOOTLOADER) {
        mode = ANDROID_BOOT_MODE_NORMAL;
    }
    bool normal_boot = (mode == ANDROID_BOOT_MODE_NORMAL);
    switch (mode) {
    case ANDROID_BOOT_MODE_NORMAL: // 正常启动
#ifdef CONFIG_ANDROID_SYSTEM_AS_ROOT
        /* In normal mode, we load the kernel from "boot" but append
         * "skip_initramfs" to the cmdline to make it ignore the
         * recovery initramfs in the boot partition.
         */
        mode_cmdline = "skip_initramfs"; // System-as-root时跳过boot分区中的initramfs
#endif
        break;
    case ANDROID_BOOT_MODE_RECOVERY: // 进入recovery
#if defined(CONFIG_ANDROID_SYSTEM_AS_ROOT) || defined(CONFIG_ANDROID_USES_RECOVERY_AS_BOOT)
        /* In recovery mode we still boot the kernel from "boot" but
         * don't skip the initramfs so it boots to recovery.
         * If on Android device using Recovery As Boot, there is no
         * recovery partition.
         */
         // System-as-root时使用boot分区中的initramfs,Recovery-as-root时没有recovery分区
#else
        boot_partition = ANDROID_PARTITION_RECOVERY;
#endif
        break;
    case ANDROID_BOOT_MODE_BOOTLOADER: // 进入bootloader·
        /* Bootloader mode enters fastboot. If this operation fails we
         * simply return since we can't recover from this situation by
         * switching to another slot.
         */
        return android_bootloader_boot_bootloader(); // 启动进入bootloader
    }
    ......
    /* Load the kernel from the desired "boot" partition. */
    // 获取boot分区信息,用于加载kernel
    boot_part_num =
        android_part_get_info_by_name_suffix(dev_desc, boot_partition,
                         slot_suffix, &boot_part_info);
    /* Load the vendor boot partition if there is one. */
    // 获取vendor boot分区信息。当使用GKI时,boot分区存储GKI kernel,vendor boot供应商客制化的boot代码
    vendor_boot_part_num =
        android_part_get_info_by_name_suffix(dev_desc, vendor_boot_partition,
                         slot_suffix,
                         &vendor_boot_part_info);
    struct disk_partition *bootconfig_part_info_ptr = NULL;
    ......
    // 加载boot镜像
    struct andr_boot_info* boot_info = android_image_load(dev_desc, &boot_part_info,
                 vendor_boot_part_info_ptr,
                 kernel_address, slot_suffix, normal_boot, avb_bootconfig,
                 persistant_dev_desc, bootconfig_part_info_ptr,
                 verified_boot_img, verified_vendor_boot_img);
    ......
    /* Assemble the command line */
    // 整合boot信息到command line中,传递给kernel
    command_line = android_assemble_cmdline(slot_suffix, mode_cmdline, normal_boot,
                            android_image_get_kernel_cmdline(boot_info),
                            android_image_is_bootconfig_used(boot_info),
                            avb_cmdline);
    env_set("bootargs", command_line);
    debug("ANDROID: bootargs: \"%s\"\n", command_line);
    android_bootloader_boot_kernel(boot_info); // 启动进入kernel
    ......
}

Bootloader 的启动流程也比较清晰,先解析启动需要的信息,然后加载镜像进行启动。启动信息是通过 MISC 分区读取的,MISC 分区存储的正是 Android 系统关机过程中需要更新的 BCB。

BCB(Bootloader Control Block)是 Android 系统中定义的一个启动控制区域,以 RAW 格式进行存储,用于在 Android 用户空间和 Android 兼容的 bootloader 之间交换交换信息。在 Bootloader 中,BCB 的读写代码如下,

/u-boot/common/android_bootloader.c
    
static int android_bootloader_message_load(
    struct blk_desc *dev_desc,
    const struct disk_partition *part_info,
    struct bootloader_message *message)
{
    ulong message_blocks = sizeof(struct bootloader_message) /
        part_info->blksz;
    if (message_blocks > part_info->size) {
        printf("misc partition too small.\n");
        return -1;
    }
    if (blk_dread(dev_desc, part_info->start, message_blocks, message) !=
        message_blocks) {
        printf("Could not read from misc partition\n");
        return -1;
    }
    debug("ANDROID: Loaded BCB, %lu blocks.\n", message_blocks);
    return 0;
}
static int android_bootloader_message_write(
    struct blk_desc *dev_desc,
    const struct disk_partition *part_info,
    struct bootloader_message *message)
{
    ulong message_blocks = sizeof(struct bootloader_message) /
        part_info->blksz;
    if (message_blocks > part_info->size) {
        printf("misc partition too small.\n");
        return -1;
    }
    if (blk_dwrite(dev_desc, part_info->start, message_blocks, message) !=
        message_blocks) {
        printf("Could not write to misc partition\n");
        return -1;
    }
    debug("ANDROID: Wrote new BCB, %lu blocks.\n", message_blocks);
    return 0;
}
......
static enum android_boot_mode android_bootloader_load_and_clear_mode(
    struct blk_desc *dev_desc,
    const struct disk_partition *misc_part_info)
{
    struct bootloader_message bcb;
#ifdef CONFIG_FASTBOOT
    char *bootloader_str;
    /* Check for message from bootloader stored in RAM from a previous boot.
     */
    bootloader_str = (char *)CONFIG_FASTBOOT_BUF_ADDR; // fastboot模式先先检查RAM中的boot信息
    if (!strcmp("reboot-bootloader", bootloader_str)) {
        bootloader_str[0] = '\0';
        return ANDROID_BOOT_MODE_BOOTLOADER;
    }
#endif
    /* Check and update the BCB message if needed. */
    // 从Misc分区中加载BCB信息
    if (android_bootloader_message_load(dev_desc, misc_part_info, &bcb) <
        0) {
        printf("WARNING: Unable to load the BCB.\n");
        return ANDROID_BOOT_MODE_NORMAL;
    }
    // bootonce-bootloader意味着要启动计入bootloader,此时擦除BCB内容。
    if (!strcmp("bootonce-bootloader", bcb.command)) {
        /* Erase the message in the BCB since this value should be used
         * only once.
         */
        memset(bcb.command, 0, sizeof(bcb.command));
        android_bootloader_message_write(dev_desc, misc_part_info,
                         &bcb);
        return ANDROID_BOOT_MODE_BOOTLOADER;
    }
    if (!strcmp("boot-recovery", bcb.command))
        return ANDROID_BOOT_MODE_RECOVERY;
    return ANDROID_BOOT_MODE_NORMAL;
}

BCB在 Android bootloader 中定义为一个结构体数据,在 Flash 中以 RAW 格式存储。其结构定义为,

/u-boot/include/android_bootloader_message.h

// Spaces used by misc partition are as below:
// 0   - 2K     For bootloader_message
// 2K  - 16K    Used by Vendor's bootloader (the 2K - 4K range may be optionally used
//              as bootloader_message_ab struct)
// 16K - 64K    Used by uncrypt and recovery to store wipe_package for A/B devices
// Note that these offsets are admitted by bootloader,recovery and uncrypt, so they
// are not configurable without changing all of them.
static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0;
static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
/* Bootloader Message (2-KiB)
 *
 * This structure describes the content of a block in flash
 * that is used for recovery and the bootloader to talk to
 * each other.
 *
 * The command field is updated by linux when it wants to
 * reboot into recovery or to update radio or bootloader firmware.
 * It is also updated by the bootloader when firmware update
 * is complete (to boot into recovery for any final cleanup)
 *
 * The status field was used by the bootloader after the completion
 * of an "update-radio" or "update-hboot" command, which has been
 * deprecated since Froyo.
 *
 * The recovery field is only written by linux and used
 * for the system to send a message to recovery or the
 * other way around.
 *
 * The stage field is written by packages which restart themselves
 * multiple times, so that the UI can reflect which invocation of the
 * package it is.  If the value is of the format "#/#" (eg, "1/3"),
 * the UI will add a simple indicator of that status.
 *
 * We used to have slot_suffix field for A/B boot control metadata in
 * this struct, which gets unintentionally cleared by recovery or
 * uncrypt. Move it into struct bootloader_message_ab to avoid the
 * issue.
 */
struct bootloader_message {
    char command[32];
    char status[32];
    char recovery[768];
    // The 'recovery' field used to be 1024 bytes.  It has only ever
    // been used to store the recovery command line, so 768 bytes
    // should be plenty.  We carve off the last 256 bytes to store the
    // stage string (for multistage packages) and possible future
    // expansion.
    char stage[32];
    // The 'reserved' field used to be 224 bytes when it was initially
    // carved off from the 1024-byte recovery field. Bump it up to
    // 1184-byte so that the entire bootloader_message struct rounds up
    // to 2048-byte.
    char reserved[1184];
};

BCB主要的功能如下:

  • 实现 Android 特定的 bootloader 流程。
  • 在用户空间和 bootloader 之间传递 boot reason,并控制对应的行为。
  • 传递 Recovery 系统需要的 commands。

Android 用户空间(normal / recovery) 也是读写BCB来控制启动行为,如上文中 Init 的 Reboot 过程中就会更新BCB。BCB的读写函数如下,

/bootable/recovery/bootloader_message/bootloader_message.cpp
    
bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
                                  std::string* err) {
  return read_misc_partition(boot, sizeof(*boot), misc_blk_device,
                             BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
}

// 从Misc分区读取BCB
bool read_bootloader_message(bootloader_message* boot, std::string* err) {
  std::string misc_blk_device = get_misc_blk_device(err);
  if (misc_blk_device.empty()) {
    return false;
  }
  return read_bootloader_message_from(boot, misc_blk_device, err);
}

bool write_bootloader_message_to(const bootloader_message& boot, const std::string& misc_blk_device,
                                 std::string* err) {
  return write_misc_partition(&boot, sizeof(boot), misc_blk_device,
                              BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
}

// 写BCB到Misc分区
bool write_bootloader_message(const bootloader_message& boot, std::string* err) {
  std::string misc_blk_device = get_misc_blk_device(err);
  if (misc_blk_device.empty()) {
    return false;
  }
  return write_bootloader_message_to(boot, misc_blk_device, err);
}

// 清空BSC
bool clear_bootloader_message(std::string* err) {
  bootloader_message boot = {};
  return write_bootloader_message(boot, err);
}

// 写recovery commands到BCB
bool write_bootloader_message(const std::vector<std::string>& options, std::string* err) {
  bootloader_message boot = {};
  update_bootloader_message_in_struct(&boot, options);

  return write_bootloader_message(boot, err);
}

bool write_bootloader_message_to(const std::vector<std::string>& options,
                                 const std::string& misc_blk_device, std::string* err) {
  bootloader_message boot = {};
  update_bootloader_message_in_struct(&boot, options);

  return write_bootloader_message_to(boot, misc_blk_device, err);
}

// 更新recovery commands
bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
  bootloader_message boot;
  if (!read_bootloader_message(&boot, err)) {
    return false;
  }
  update_bootloader_message_in_struct(&boot, options);

  return write_bootloader_message(boot, err);
}

bool update_bootloader_message_in_struct(bootloader_message* boot,
                                         const std::vector<std::string>& options) {
  if (!boot) return false;
  // Replace the command & recovery fields.
  memset(boot->command, 0, sizeof(boot->command));
  memset(boot->recovery, 0, sizeof(boot->recovery));

  strlcpy(boot->command, "boot-recovery", sizeof(boot->command));

  std::string recovery = "recovery\n";
  for (const auto& s : options) {
    recovery += s;
    if (s.back() != '\n') {
      recovery += '\n';
    }
  }
  strlcpy(boot->recovery, recovery.c_str(), sizeof(boot->recovery));
  return true;
}

// 将重启到bootloader的命令写入到BCB,这里是bootonce-bootloader
bool write_reboot_bootloader(std::string* err) {
  bootloader_message boot;
  if (!read_bootloader_message(&boot, err)) {
    return false;
  }
  if (boot.command[0] != '\0') {
    *err = "Bootloader command pending.";
    return false;
  }
  strlcpy(boot.command, "bootonce-bootloader", sizeof(boot.command));
  return write_bootloader_message(boot, err);
}

戈壁老王
143 声望61 粉丝

做为一个不称职的老年码农,一直疏忽整理笔记,开博记录一下,用来丰富老年生活,