Bootloader和Recovery的基本介绍


Bootloader之高通LK介绍:

硬件通电后,bootloader是必须要启动运行的,由bootloader来决定运行recovery系统还是android系统(即使在64bit架构,LK依旧运行在32bit模式)
bootloader进行硬件初始化,读取Linux内核和ramdisk,设置寄存器以及内核命令行参数,并跳转至内核运行主要功能:

  • 硬件初始化:设置向量表,MMU,cache,初始化外设
  • 从储存器加载boot.img
  • kernel授权,生成boot.img的签名与校验(???)

iMX平台的bootloader用的是U-boot和高通不一样。

bootloader的信息来源:Bootloader Control Block

BCB是BootLoader和recovery的通信接口,也是BootLoader和android的通信接口,储存在MISC分区,占用3个page其本身是一个结构体:

struct bootloader_message {
    char command[32];
    char status[32];
    char recovery[1280];
    
    // 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[672];
};

char command[32]:

可能的取值:NULL,boot-recovery,others
当想要进入recovery时更新这个值,结束recovery时,清除这个值,防止重启后再次进入recovery模式

char status[32]:

在完成相应的更新后,recovery会将执行结果写入到这个字段

char recovery[1024]:

可被main system写入,也可被recovery服务程序写入。该文件的内容格式:

"recovery\n<recovery command>\n<recovery command>"

该文件储存的就是一个字符串,必须以recovery\n开头,否则这个字段的所有内容会被忽略.开头之后的部分为command命令。
recovery对其操作的过程为:先读取BCB中是否有参数,如果BCB中没有就读取/cache/recovery/command 然后将其重新写入BCB(断电续升).

在进入main system之前,recovery又会清空BCB的command域和recovery域,这样确认重启之后不再进入recovery模式

bootloader将根据区域(char command[32])内的内容,进行判断该跳转至android系统还是recovery系统.

  • android mode:正常启动模式(BCB中无命令),启动boot.img对应的系统,是android的正常工作模式.
  • recovery mode:android重启进入recovery模式之前,会向BCB中写入命令,以便在重启后告诉bootloader进入recovery模式。
  • 除了正常的加载启动系统之外,还会通过读取MISC分区(BCB)获得来自android和recovery的其他消息。

Android Recovery之系统升级:

recovery主要目的就是升级,系统进入recovery模式后会装载recovery分区,该分区包含recovery.img(与boot.img相同,包含了标准的内核和根文件系统).进入该模式后主要运行recovery服务(/sbin/recovery)

信息交互:cache/recovery/中的三个文件

  1. recovery/command:该文件保存着android传给recovery的命令行,每一行是一条命令,支持以下几种组合。

update_package=root:path
//这条命令存在时代表系统需要升级,在进入recovery模式后,将该文件中的命令读取并写入到BCB中,然后读取相应的zip包进行升级。
send_intent=anystring
//在recovery结束时在finish_recovery函数中将send_intent字符串作为参数传进来,并写入到intent中
wipe_data //擦除data和cache分区,擦除data分区时必须要擦除cache分区-> reboot
wipe_cache //擦除cache分区-> reboot

  1. recovery/log:recovery模式中的log

在recovery服务运行中,stdout和stderr会重定位到/tmp/recovery.log在recovery退出之前将其转存到/cache/recovery/log中

  1. recovery/intent:recovery传递给main system的信息,作用不详

如何进入recovery升级

  1. 系统重启并进入recovery模式之前,android会向BCB中的command域写入boot-recovery,用来告知bootloader重启后进入recovery模式(必须)。
  2. 向/cache/recovery/command中写入recovery将要进行的操作命令,或向BCB(recovery字符数组)中写入升级命令。
  3. 重启进入recovery模式后,recovery会从/cache/recovery/command中读取值并放入BCB的recovery域(如果BCB中无命令).

升级包(update.zip)

  • 标准升级包:XXX_update.zip
  • zip中包含升级可执行文件(upgrade可选)+升级文件+升级脚本(script)
  • 升级脚本script中包含了如何进行升级(img文件的解压至相应分区)
  • 定制升级包:upgrade为可执行文件,进行相应的升级流程(display/tp等)

fix.sh中update机制

  • auto_update调用到minirecovery 对应源码/hardware/***/minirecovery.c
  • /bootable/recovery/minirecovery对应生成minirecovery
  • /bootable/recovery/bootloader_message/bootloader_message.cpp该代码中将minirecovery的参数bootargs写入BCB中

recovery进程获取command源码分析

下记源码记录了/sbin/recovery进程启动后获取升级命令行参数及处理过程 :

// command line args come from, in decreasing precedence:
//   - the actual command line
//   - the bootloader control block (one per line, after "recovery")
//   - the contents of COMMAND_FILE (one per line)
static std::vector<std::string> get_args(const int argc, char** const argv) {
    CHECK_GT(argc, 0);

    bootloader_message boot = {};//定义BCB结构体
    std::string err;
    if (!read_bootloader_message(&boot, &err)) {//从BCB中读取数据
        LOG(ERROR) << err;
        // If fails, leave a zeroed bootloader_message.
        boot = {};
    }
    stage = std::string(boot.stage);

    if (boot.command[0] != 0) {
        std::string boot_command = std::string(boot.command, sizeof(boot.command));
        LOG(INFO) << "Boot command: " << boot_command;
        //此处确认BCB中是否有升级命令 log: [    0.003542] I:Boot command: boot-recovery
        //通过log能看出此处的command指示了bootloader跳转至recovery系统
    }

    if (boot.status[0] != 0) {
        std::string boot_status = std::string(boot.status, sizeof(boot.status));
        LOG(INFO) << "Boot status: " << boot_status;
    }

    std::vector<std::string> args(argv, argv + argc);//该行将recovery进程启动时的参数保存,该处通常不使用

    // --- if arguments weren't supplied, look in the bootloader control block
    if (args.size() == 1) {//size为1意味着recovery进程启动时的参数只有它自己
        boot.recovery[sizeof(boot.recovery) - 1] = '\0';  // Ensure termination
        std::string boot_recovery(boot.recovery);//将BCB中的recovery域内容保存下来
        std::vector<std::string> tokens = android::base::Split(boot_recovery, "\n");
        //将recovery域的内容以"\n"符号进行分隔
        if (!tokens.empty() && tokens[0] == "recovery") {
        //recovery域不是空的,且第一行是recovery(前面提到过需以recovery\n开头)
            for (auto it = tokens.begin() + 1; it != tokens.end(); it++) {
                // Skip empty and '\0'-filled tokens.
                if (!it->empty() && (*it)[0] != '\0') args.push_back(std::move(*it));
                //将BCB中的recovery命令追加到args后面
            }
            LOG(INFO) << "Got " << args.size() << " arguments from boot message";
        } else if (boot.recovery[0] != 0) {
            LOG(ERROR) << "Bad boot message: \"" << boot_recovery << "\"";
        }
    }

    LOG(INFO) << "commandline: "<< args[1] << " line:" << strlen(args[1].c_str());
    // --- if that doesn't work, try the command file (if we have /cache).
    if ((args.size() == 1) && has_cache) {
    //此处如果BCB中的recovery域无内容,则尝试在/cache/recovery/command中获取参数
        std::string content;
        if (ensure_path_mounted(COMMAND_FILE) == 0 &&
                android::base::ReadFileToString(COMMAND_FILE, &content)) {
            std::vector<std::string> tokens = android::base::Split(content, "\n");
            // All the arguments in COMMAND_FILE are needed (unlike the BCB message,
            // COMMAND_FILE doesn't use filename as the first argument).
            for (auto it = tokens.begin(); it != tokens.end(); it++) {
                // Skip empty and '\0'-filled tokens.
                if (!it->empty() && (*it)[0] != '\0') args.push_back(std::move(*it));
            }
            LOG(INFO) << "Got " << args.size() << " arguments from " << COMMAND_FILE;
        }
    }

    // Write the arguments (excluding the filename in args[0]) back into the
    // bootloader control block. So the device will always boot into recovery to
    // finish the pending work, until finish_recovery() is called.
    std::vector<std::string> options(args.cbegin() + 1, args.cend());
    if (!update_bootloader_message(options, &err)) {
    //将此处获取到的升级参数回写到BCB中,如果升级出现异常,再次启动还会进行升级
        LOG(ERROR) << "Failed to set BCB message: " << err;
    }

    return args;
}

比较容易弄混淆的点:

BCB中的command和/cache/recovery/command的内容并不等价,且不同类。

  • BCB中的command:决定了bootloader该去引导android系统还是recovery系统
  • BCB中的recovery:此处内容和/cache/recovery/command内容是等价的,以此来保存recovery在升级过程中的安装包是什么?是否要擦除用户数据等等参数。

to be continued

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值