Android reboot recovery and normal 的一种实现流程
Uboot 判断
uboot\common\main.c
void main_loop(void) 中调用了几个重要的函数
void main_loop(void)
{
run_preboot_environment_command
bootdelay_process
autoboot_command
}
main_loop调用了run_preboot_environment_command, 该函数执行了 preboot定义的行为
static void run_preboot_environment_command(void)
{
#ifdef CONFIG_PREBOOT
char *p;
p = getenv("preboot");
if (p != NULL) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(1); /* disable Control C checking */
# endif
run_command_list(p, -1, 0);
# ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */
# endif
}
#endif /* CONFIG_PREBOOT */
}
preboot 的定义在 这里uboot\include\env_default.h
#ifdef CONFIG_PREBOOT
"preboot=" CONFIG_PREBOOT "\0"
#endif
所以有一些预先设定的操作可以定义在 CONFIG_PREBOOT 这个宏当中,来看看我所用平台中定义的行为
#define CONFIG_PREBOOT \
"run bcb_cmd; "\
"run factory_reset_poweroff_protect;"\
"run upgrade_check;"\
"run init_display;"\
"run storeargs;"\
"run switch_bootmode;"
CONFIG_PREBOOT 中有两个流程需要关注 bcb_cmd 和 switch_bootmode
bcb_cmd
先介绍下 bcb
BCB是Bootloader与Recovery的通信接口,也是Bootloader与Main system之间的通信接口。存储在flash中的MISC分区。占用三个page,其本身就是一个结构体,详细成员以及各成员含义例如以下,位于/bootloader/revocery/bootloader.h文件里:
struct bootloader_message{
char command[32];
char status[32];
char recovery[1024];
};
l command字段:当要重新启动进入Recovery模式或更新radio、bootloader固件时。linux会更新这个值。当固件更新完毕后Bootloader也会更新这个值。另外在成功更新后结束Recovery时。会清除这个成员的值,防止重新启动时再次进入Recovery模式。
l status字段:在完毕对应的更新后。Bootloader会将运行结果写入到这个字段。
l recovery字段:可被Main System写入,也可被Recovery服务程序写入。该文件的内容格式为:
“recovery\n
<recovery command>\n
<recovery command>”
该文件存储的就是一个字符串,必须以recovery\n开头,否则这个字段的全部内容域会被忽略。“recovery\n”之后的部分,是/cache/recovery/command支持的命令。
能够将其理解为Recovery操作过程中对命令操作的备份。Recovery对其操作的过程为:先读取BCB然后读取/cache/recovery/command,然后将二者又一次写回BCB。这样在进入Main system之前。确保操作被运行。在操作之后进入Main system之前。Recovery又会清空BCB的command域和recovery域,这样确保重新启动后不再进入Recovery模式。
而定义的 bcb_cmd=get_valid_slot 其介绍为
This command will choose valid slot to boot up which saved it misc partition by mark to decide whether execute command
switch_bootmode
"switch_bootmode="\
"get_rebootmode;"\
"if test ${reboot_mode} = factory_reset; then "\
"run recovery_from_flash;"\
"else if test ${reboot_mode} = update; then "\
"run update;"\
"else if test ${reboot_mode} = cold_boot; then "\
"run powermode_check;"\
"else if test ${reboot_mode} = fastboot; then "\
"fastboot;"\
"fi;fi;fi;fi;"\
"\0" \
1.先获取rebootmode
get_rebootmode do_get_rebootmode
int do_get_rebootmode (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
uint32_t reboot_mode_val;
reboot_mode_val = ((readl(AO_SEC_SD_CFG15) >> 12) & 0xf);
debug("reboot_mode(0x%x)=0x%x\n", AO_SEC_SD_CFG15, reboot_mode_val);
}
通过读取寄存器值返回 reboot_mode对应如下
#define XXX_COLD_BOOT 0
#define XXX_NORMAL_BOOT 1
#define XXX_FACTORY_RESET_REBOOT 2
#define XXX_UPDATE_REBOOT 3
#define XXX_FASTBOOT_REBOOT 4
#define XXX_SUSPEND_REBOOT 5
#define XXX_HIBERNATE_REBOOT 6
#define XXX_BOOTLOADER_REBOOT 7 /* fastboot bootloader */
#define XXX_SHUTDOWN_REBOOT 8
#define XXX_CRASH_REBOOT 11
#define XXX_KERNEL_PANIC 12
#define XXX_WATCHDOG_REBOOT 13
2、根据reboot mode 切换对应流程
switch_bootmode 中用到的reboot mode 是 factory_reset update cold_boot fastboot 这几种
factory_reset是进recovery 界面
update 是选择了ota 包后直接进入的recovery 升级流程
以reboot mode 为update caes 为例,其流程如下
"update="\
/*first usb burning, second sdc_burn, third ext-sd autoscr/recovery, last udisk autoscr/recovery*/\
"run usb_burning; "\
"run sdc_burning; "\
"if mmcinfo; then "\
"run recovery_from_sdcard;"\
"fi;"\
"if usb start 0; then "\
"run recovery_from_udisk;"\
"fi;"\
"run recovery_from_flash;"\
"\0"\
"recovery_from_sdcard="\
"if fatload mmc 0 ${loadaddr} xxx_autoscript; then autoscr ${loadaddr}; fi;"\
"if fatload mmc 0 ${loadaddr} recovery.img; then "\
"if fatload mmc 0 ${dtb_mem_addr} dtb.img; then echo sd dtb.img loaded; fi;"\
"wipeisb; "\
"bootm ${loadaddr};fi;"\
"\0"\
"recovery_from_udisk="\
"if fatload usb 0 ${loadaddr} xxx_autoscript; then autoscr ${loadaddr}; fi;"\
"if fatload usb 0 ${loadaddr} recovery.img; then "\
"if fatload usb 0 ${dtb_mem_addr} dtb.img; then echo udisk dtb.img loaded; fi;"\
"wipeisb; "\
"bootm ${loadaddr};fi;"\
"\0"\
"recovery_from_flash="\
"setenv bootargs ${bootargs} xxx_dt=${xxx_dt} recovery_part={recovery_part} recovery_offset={recovery_offset};"\
"if imgread kernel ${recovery_part} ${loadaddr} ${recovery_offset}; then wipeisb; bootm ${loadaddr}; fi"\
"\0"\
可以看到做了一些前置的判断后 是通过 bootm ${loadaddr}; 跳转到下一个流程的启动地址
以recovery_from_flash 为例,设置bootargs 参数 和dt 以及recovery 地址,然后使用 imgread kernel 可以 recovery 参数后启动kernel
正常开机流程
recovery的流程看完了,看下正常开机的流程,即main_loop 中 bootdelay_process() 和autoboot_command
bootdelay_process()
{
// ...
s = getenv("bootcmd");
}
这个bootcmd 也是有个默认的宏定义值
uboot\include\env_default.h
#ifdef CONFIG_BOOTCOMMAND
"bootcmd=" CONFIG_BOOTCOMMAND "\0"
#endif
在我的工程文件中定义的 CONFIG_BOOTCOMMAND如下
#define CONFIG_BOOTCOMMAND "run storeboot"
"storeboot="\
"if imgread kernel ${boot_part} ${loadaddr}; then bootm ${loadaddr}; fi;"\
"run update;"\
"\0"\
所以可以看到传给autoboot_command 的字符串是 "run storeboot"
void autoboot_command(const char *s)
{
printf("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
int prev = disable_ctrlc(1); /* disable Control C checking */
#endif
run_command_list(s, -1, 0);
#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
disable_ctrlc(prev); /* restore Control C checking */
#endif
}
#ifdef CONFIG_MENUKEY
if (menukey == CONFIG_MENUKEY) {
s = getenv("menucmd");
if (s)
run_command_list(s, -1, 0);
}
#endif /* CONFIG_MENUKEY */
}
这里介绍下其中的 autoboot_command调用的的 abortboot , 如下两个函数会判断是否有对应的输入来决定是否进入boot console
static int abortboot(int bootdelay)
{
#ifdef CONFIG_AUTOBOOT_KEYED
return abortboot_keyed(bootdelay); /* delaykey[] = {
{ .str = getenv("bootdelaykey"), .retry = 1 },
{ .str = getenv("bootdelaykey2"), .retry = 1 },
{ .str = getenv("bootstopkey"), .retry = 0 },
{ .str = getenv("bootstopkey2"), .retry = 0 },
};*/
#else
return abortboot_normal(bootdelay); // Hit Enter or space or Ctrl+C key to stop autoboot --
#endif
}
Kernel 设定reboot mode
Kernel 3.14
kernel reboot.c 会调用 machine_power_off 和 machine_restart
这边的平台是arm64 平台,其对应函数实现是在 arch/arm64/kernel/processs.c
processs.c 中有调用了 pm_power_off arm_pm_restart
所以可以通过注册了 pm 的全局方法 pm_power_off arm_pm_restart来实现设定对应的寄存器值
static int aml_restart_probe(struct platform_device *pdev)
{
pm_power_off = do_xxx_poweroff;
arm_pm_restart = do_xxx_restart;
return 0;
}
static void do_xxx_poweroff(void)
{
/* TODO: Add poweroff capability */
meson_common_restart('h', "charging_reboot");
}
void meson_common_restart(char mode, const char *cmd)
{
u32 reboot_reason = MESON_NORMAL_BOOT;
if (cmd) {
if (strcmp(cmd, "charging_reboot") == 0)
reboot_reason = MESON_CHARGING_REBOOT;
else if (strcmp(cmd, "recovery") == 0 ||
strcmp(cmd, "factory_reset") == 0)
reboot_reason = MESON_FACTORY_RESET_REBOOT;
else if (strcmp(cmd, "update") == 0)
reboot_reason = MESON_UPDATE_REBOOT;
else if (strcmp(cmd, "report_crash") == 0)
reboot_reason = MESON_CRASH_REBOOT;
else if (strcmp(cmd, "factory_testl_reboot") == 0)
reboot_reason = MESON_FACTORY_TEST_REBOOT;
else if (strcmp(cmd, "switch_system") == 0)
reboot_reason = MESON_SYSTEM_SWITCH_REBOOT;
else if (strcmp(cmd, "safe_mode") == 0)
reboot_reason = MESON_SAFE_REBOOT;
else if (strcmp(cmd, "lock_system") == 0)
reboot_reason = MESON_LOCK_REBOOT;
else if (strcmp(cmd, "usb_burner_reboot") == 0)
reboot_reason = MESON_USB_BURNER_REBOOT;
else if (strcmp(cmd, "uboot_suspend") == 0)
reboot_reason = MESON_UBOOT_SUSPEND;
}
xxx_write_aobus(AO_RTI_STATUS_REG1, reboot_reason);
if (is_meson_m8m2_cpu())
xxx_write_cbus(WATCHDOG_TC, (1<<19) | 100);
if (is_meson_m8_cpu())
xxx_write_cbus(WATCHDOG_TC, (1<<22) | 100);
}
Android Framework流程
这里是Android的标准流程,这里可以参考这份资料
https://www.jb51.net/article/79844.htm
网友评论