硬件通电后,bootloader是必须要启动运行的,由bootloader来决定运行recovery系统还是android系统(即使在64bit架构,LK依旧运行在32bit模式)
bootloader进行硬件初始化,读取Linux内核和ramdisk,设置寄存器以及内核命令行参数,并跳转至内核运行主要功能:
iMX平台的bootloader用的是U-boot和高通不一样。
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系统.
recovery主要目的就是升级,系统进入recovery模式后会装载recovery分区,该分区包含recovery.img(与boot.img相同,包含了标准的内核和根文件系统).进入该模式后主要运行recovery服务(/sbin/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
在recovery服务运行中,stdout和stderr会重定位到/tmp/recovery.log在recovery退出之前将其转存到/cache/recovery/log中
下记源码记录了/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的内容并不等价,且不同类。
to be continued