recovery如何传递启动参数

本文详细介绍了Android系统中Bootloader、Recovery模块及主系统间通过misc分区传递启动参数的过程。解析了get_args()函数如何从misc分区或/cache/recovery/command文件中获取参数,并介绍了set_bootloader_message()函数如何将参数保存回misc分区。
摘要由CSDN通过智能技术生成

如何传递启动参数

Bootloader和Recovery模块以及主系统之间的通信是通过系统的misc分区来完成的。Misc分区只有3页(Page)大小。描述misc分区的数据结构是bootloader_message,定义如下:
 

 
 
  1. struct bootloader_message {  
  2.     char command[32];  
  3.     char status[32];  
  4.     char recovery[768];  
  5. }; 

command字段中存储的是命令,如果它的值是“boot-recovery”,系统将进入Recovery模式。如果它的值是“update-radia”或者“update-hboot”,系统会进入更新firmware的模式,这个更新过程由bootloader完成。如果command中的值为NULL,则进入主系统,正常启动。

status字段存储的是更新的结果。更新结束后,由Recovery或者Bootloader将更新结果写入到这个字段中。

recovery字段存放的是recovry模块的启动参数。内容以“recovery”字符串开头,后面是recovery中执行的命令,命令之间以“\n”分割。

下面看看Recovery模块获取参数的函数get_args(),代码如下:
 

 
 
  1. static void get_args(int *argc, char ***argv) {  
  2.     struct bootloader_message boot;  
  3.     memset(&boot, 0, sizeof(boot));  
  4.     get_bootloader_message(&boot);  // 读取/misc分区中的命令到boot变量中  
  5.  
  6.     if (boot.command[0] != 0 && boot.command[0] != 255) {  
  7.         LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command);  
  8.     }  
  9.     if (boot.status[0] != 0 && boot.status[0] != 255) {  
  10.         LOGI("Boot status: %.*s\n", sizeof(boot.status), boot.status);  
  11.     }  
  12.     if (*argc <= 1) { // 如果命令行没有传递参数  
  13.         boot.recovery[sizeof(boot.recovery) - 1] = '\0';  // Ensure termination  
  14.         const char *arg = strtok(boot.recovery, "\n"); // arg指向boot变量的recvory  
  15.         if (arg != NULL && !strcmp(arg, "recovery")) {   
  16.             // 如果字符串以recovery开头,则使用从/misc分区中读取的命令串建立启动参数  
  17.             *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);  
  18.             (*argv)[0] = strdup(arg);  
  19.             for (*argc = 1; *argc < MAX_ARGS; ++*argc) {  
  20.                 if ((arg = strtok(NULL, "\n")) == NULL) break;  
  21.                 (*argv)[*argc] = strdup(arg);  
  22.             }  
  23.             LOGI("Got arguments from boot message\n");  
  24.         } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {  
  25.             LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);  
  26.         }  
  27.     }  
  28.     if (*argc <= 1) { // 如果从/misc分区中也没有读到命令  
  29.         FILE *fp = fopen_path(COMMAND_FILE, "r"); // 打开/cache/recovery/command文件  
  30.         if (fp != NULL) {  
  31.             char *token;  
  32.             char *argv0 = (*argv)[0];  
  33.             *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);  
  34.             (*argv)[0] = argv0;  // use the same program name  
  35.             char buf[MAX_ARG_LENGTH];  
  36.             // 使用读取的文件内容建立启动参数  
  37.             for (*argc = 1; *argc < MAX_ARGS; ++*argc) {  
  38.                 if (!fgets(buf, sizeof(buf), fp)) break;  
  39.                 token = strtok(buf, "\r\n");  
  40.                 if (token != NULL) {  
  41.                     (*argv)[*argc] = strdup(token);  // Strip newline.  
  42.                 } else {  
  43.                     --*argc;  
  44.                 }  
  45.             }  
  46.             check_and_fclose(fp, COMMAND_FILE);  
  47.         }  
  48.     }  
  49.     // 把启动参数也放到boot对象中  
  50.     strlcpy(boot.command, "boot-recovery", sizeof(boot.command));  
  51.     strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));  
  52.     int i;  
  53.     for (i = 1; i < *argc; ++i) {  
  54.         strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));  
  55.         strlcat(boot.recovery, "\n", sizeof(boot.recovery));  
  56.     }  
  57.     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()函数的代码如下所示:
 

 
 
  1. int get_bootloader_message(struct bootloader_message *out) {  
  2.     Volume* v = volume_for_path("/misc");    // 打开misc分区  
  3.     if (v == NULL) {  
  4.       return -1;  
  5.     }  
  6.     if (strcmp(v->fs_type, "mtd") == 0) {   // 如果文件系统类型是mtd  
  7.         return get_bootloader_message_mtd(out, v);  
  8.     } else if (strcmp(v->fs_type, "emmc") == 0) {  // 如果是emmc类型  
  9.         return get_bootloader_message_block(out, v);  
  10.     }  
  11.     return -1;  

从get_bootloader_message()函数的代码可以看到,它打开/misc分区来读取数据,函数中还区分了分区的类型是mtd还是emmc来选择不同的函数去读取数据,这些函数只是调用底层函数去读取数据块的内容,我们就不分析了。

get_args()函数的结尾调用了set_bootloader_message()函数,它的代码如下:
 

 
 
  1. int set_bootloader_message(const struct bootloader_message *in) {  
  2.     Volume* v = volume_for_path("/misc");  
  3.     if (v == NULL) {  
  4.       LOGE("Cannot load volume /misc!\n");  
  5.       return -1;  
  6.     }  
  7.     if (strcmp(v->fs_type, "mtd") == 0) {  
  8.         return set_bootloader_message_mtd(in, v);  
  9.     } else if (strcmp(v->fs_type, "emmc") == 0) {  
  10.         return set_bootloader_message_block(in, v);  
  11.     }  
  12.     return -1;  

set_bootloader_message()函数的作用是把启动参数的信息又保存到了/misc分区中。这样做的目的是防止升级过程中发生崩溃,这样重启后仍然可以从/misc分区中读取更新的命令,继续进行更新操作。这也是为什么get_args()函数要从几个地方读取启动参数的原因。

如果recovery正常退出,将调用finish_recovery()函数,这个函数将清除/misc分区的内容,这样就不会重复更新系统了。如下所示:
 

 
 
  1. static void finish_recovery(const char *send_intent) {  
  2.     ......  
  3.     struct bootloader_message boot;  
  4.     memset(&boot, 0, sizeof(boot));  
  5.     set_bootloader_message(&boot);  // 向/misc分区中写入0  
  6.     ......  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值