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/中的三个文件
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
recovery/log:recovery模式中的log
在recovery服务运行中,stdout和stderr会重定位到/tmp/recovery.log在recovery退出之前将其转存到/cache/recovery/log中
recovery/intent:recovery传递给main system的信息,作用不详
如何进入recovery升级
系统重启并进入recovery模式之前,android会向BCB中的command域写入boot-recovery,用来告知bootloader重启后进入recovery模式(必须)。
向/cache/recovery/command中写入recovery将要进行的操作命令,或向BCB(recovery字符数组)中写入升级命令。
重启进入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在升级过程中的安装包是什么?是否要擦除用户数据等等参数。
————————————————
版权声明:本文为CSDN博主「Jonsin`」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_43399953/article/details/114320587