如何传递启动参数
Bootloader和Recovery模块以及主系统之间的通信是通过系统的misc分区来完成的。Misc分区只有3页(Page)大小。描述misc分区的数据结构是bootloader_message,定义如下:
- struct bootloader_message {
- char command[32];
- char status[32];
- char recovery[768];
- };
command字段中存储的是命令,如果它的值是“boot-recovery”,系统将进入Recovery模式。如果它的值是“update-radia”或者“update-hboot”,系统会进入更新firmware的模式,这个更新过程由bootloader完成。如果command中的值为NULL,则进入主系统,正常启动。
status字段存储的是更新的结果。更新结束后,由Recovery或者Bootloader将更新结果写入到这个字段中。
recovery字段存放的是recovry模块的启动参数。内容以“recovery”字符串开头,后面是recovery中执行的命令,命令之间以“\n”分割。
下面看看Recovery模块获取参数的函数get_args(),代码如下:
- static void get_args(int *argc, char ***argv) {
- struct bootloader_message boot;
- memset(&boot, 0, sizeof(boot));
- get_bootloader_message(&boot); // 读取/misc分区中的命令到boot变量中
- if (boot.command[0] != 0 && boot.command[0] != 255) {
- LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command);
- }
- if (boot.status[0] != 0 && boot.status[0] != 255) {
- LOGI("Boot status: %.*s\n", sizeof(boot.status), boot.status);
- }
- if (*argc <= 1) { // 如果命令行没有传递参数
- boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
- const char *arg = strtok(boot.recovery, "\n"); // arg指向boot变量的recvory
- if (arg != NULL && !strcmp(arg, "recovery")) {
- // 如果字符串以recovery开头,则使用从/misc分区中读取的命令串建立启动参数
- *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
- (*argv)[0] = strdup(arg);
- for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
- if ((arg = strtok(NULL, "\n")) == NULL) break;
- (*argv)[*argc] = strdup(arg);
- }
- LOGI("Got arguments from boot message\n");
- } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
- LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);
- }
- }
- if (*argc <= 1) { // 如果从/misc分区中也没有读到命令
- FILE *fp = fopen_path(COMMAND_FILE, "r"); // 打开/cache/recovery/command文件
- if (fp != NULL) {
- char *token;
- char *argv0 = (*argv)[0];
- *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
- (*argv)[0] = argv0; // use the same program name
- char buf[MAX_ARG_LENGTH];
- // 使用读取的文件内容建立启动参数
- for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
- if (!fgets(buf, sizeof(buf), fp)) break;
- token = strtok(buf, "\r\n");
- if (token != NULL) {
- (*argv)[*argc] = strdup(token); // Strip newline.
- } else {
- --*argc;
- }
- }
- check_and_fclose(fp, COMMAND_FILE);
- }
- }
- // 把启动参数也放到boot对象中
- strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
- strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
- int i;
- for (i = 1; i < *argc; ++i) {
- strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
- strlcat(boot.recovery, "\n", sizeof(boot.recovery));
- }
- set_bootloader_message(&boot);
- }
get_args()函数的主要作用是建立recovery的启动参数,如果系统启动recovery时已经传递了启动参数,那么这个函数只是把启动参数的内容复制到函数的参数boot对象中,否则函数会首先从/misc分区中获取命令字符串来构建启动参数。如果/misc分区下没有内容,则尝试打开/cache/recovery/command文件并读取文件的内容来建立启动参数。从这个函数我们可以看到,更新系统最简单的方式是把更新命令写到/cache/recovery/command文件中。get_args()函数是通过get_bootloader_message()函数来读取/misc分区的数据的,get_bootloader_message()函数的代码如下所示:
- int get_bootloader_message(struct bootloader_message *out) {
- Volume* v = volume_for_path("/misc"); // 打开misc分区
- if (v == NULL) {
- return -1;
- }
- if (strcmp(v->fs_type, "mtd") == 0) { // 如果文件系统类型是mtd
- return get_bootloader_message_mtd(out, v);
- } else if (strcmp(v->fs_type, "emmc") == 0) { // 如果是emmc类型
- return get_bootloader_message_block(out, v);
- }
- return -1;
- }
从get_bootloader_message()函数的代码可以看到,它打开/misc分区来读取数据,函数中还区分了分区的类型是mtd还是emmc来选择不同的函数去读取数据,这些函数只是调用底层函数去读取数据块的内容,我们就不分析了。
get_args()函数的结尾调用了set_bootloader_message()函数,它的代码如下:
- int set_bootloader_message(const struct bootloader_message *in) {
- Volume* v = volume_for_path("/misc");
- if (v == NULL) {
- LOGE("Cannot load volume /misc!\n");
- return -1;
- }
- if (strcmp(v->fs_type, "mtd") == 0) {
- return set_bootloader_message_mtd(in, v);
- } else if (strcmp(v->fs_type, "emmc") == 0) {
- return set_bootloader_message_block(in, v);
- }
- return -1;
- }
set_bootloader_message()函数的作用是把启动参数的信息又保存到了/misc分区中。这样做的目的是防止升级过程中发生崩溃,这样重启后仍然可以从/misc分区中读取更新的命令,继续进行更新操作。这也是为什么get_args()函数要从几个地方读取启动参数的原因。
如果recovery正常退出,将调用finish_recovery()函数,这个函数将清除/misc分区的内容,这样就不会重复更新系统了。如下所示:
- static void finish_recovery(const char *send_intent) {
- ......
- struct bootloader_message boot;
- memset(&boot, 0, sizeof(boot));
- set_bootloader_message(&boot); // 向/misc分区中写入0
- ......
- }