一个嵌入式系统,一般情况下都是正常启动模式,如果需要系统升级,或系统恢复出厂状态,就会需要用到recovery启动模式,它会从另外备份的kernel和rootfs启动。
在uboot中, check_recovery_mode()这个函数,就是用来实现这个功能的:
这个函数在lib_arm/board.c 的start_armboot (void)中被调用:
void start_armboot (void)
{
init_fnc_t **init_fnc_ptr;
char *s;
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
unsigned long addr;
#endif
...
#ifdef CONFIG_ANDROID_RECOVERY
check_recovery_mode();
#endif
...
}
/* NOTREACHED - no way out of command loop except booting */
}
只要定义了 CONFIG_ANDROID_RECOVERY, 这个函数就会被调用。
该函数定义如下:
/* export to lib_arm/board.c */
void check_recovery_mode(void)
{
if (check_key_pressing())
setup_recovery_env();
#if 0
else if (check_recovery_cmd_file()) {
puts("Recovery command file founded!\n");
setup_recovery_env();
}
#endif
}
check_key_pressing()就是进入recovery模式的按键检查,若对应的按键被按下,就进入recovery模式,如果没有就正常启动。
#ifdef CONFIG_MXC_KPD
#define PRESSED_HOME 0x01
#define PRESSED_POWER 0x02
#define RECOVERY_KEY_MASK (PRESSED_HOME | PRESSED_POWER)
inline int test_key(int value, struct kpp_key_info *ki)
{
return (ki->val == value) && (ki->evt == KDepress);
}
int check_key_pressing(void)
{
struct kpp_key_info *key_info;
int state = 0, keys, i;
mxc_kpp_init();
puts("Detecting HOME+POWER key for recovery ...\n");
/* Check for home + power */
keys = mxc_kpp_getc(&key_info);
if (keys < 2)
return 0;
for (i = 0; i < keys; i++) {
if (test_key(CONFIG_POWER_KEY, &key_info[i]))
state |= PRESSED_HOME;
else if (test_key(CONFIG_HOME_KEY, &key_info[i]))
state |= PRESSED_POWER;
}
free(key_info);
if ((state & RECOVERY_KEY_MASK) == RECOVERY_KEY_MASK)
return 1;
return 0;
}
#else
/* If not using mxc keypad, currently we will detect power key on board */
#define CPLD_BASE_ADDR 0xF4000000
#define KEY_STATUS 0xA
#define KEY_F1 0x1
#define KEY_F4 0x8
int check_key_pressing(void)
{
volatile unsigned short tmp = 0;
tmp = *(volatile unsigned short *)(CPLD_BASE_ADDR + KEY_STATUS);
if(((tmp & KEY_F1) != 0) && ((tmp & KEY_F4) != 0))
return 1;
return 0;
}
#endif
这里用了if -else 完成了两个按键检测功能,第一个是MXC自带的键盘初始化和检测机制,第二个是我自己写的都键盘的内存地址。
setup_recovery_env() 就是设置进入recovery模式需要的enviroment parameters
void setup_recovery_env(void)
{
char *env, *boot_args, *boot_cmd;
int bootdev = get_boot_device();
printf(" bootdev = %d..\n", bootdev);
boot_cmd = supported_reco_envs[bootdev].cmd;
boot_args = supported_reco_envs[bootdev].args;
if (boot_cmd == NULL) {
printf("Unsupported bootup device for recovery\n");
return;
}
printf("setup env for recovery..\n");
env = getenv("bootargs_sf");
/* Set env to recovery mode */
/* Only set recovery env when these env not exist, give user a
* chance to change their recovery env */
if (!env)
setenv("bootarg_sf", boot_args);
env = getenv("bootcmd_sf");
if (!env)
setenv("bootcmd_sf", boot_cmd);
setenv("bootcmd", "run bootcmd_sf");
}
这里用到一个全局变量, 用来保存recovery模式启动时的evn参数:
struct reco_envs supported_reco_envs[BOOT_DEV_NUM] = {
{
.cmd = NULL,
.args = NULL,
},
{
.cmd = NULL,
.args = NULL,
},
{
.cmd = NULL,
.args = NULL,
},
{
.cmd = NULL,
.args = NULL,
},
{
.cmd = NULL,
.args = NULL,
},
{
.cmd = CONFIG_RECOVERY_BOOTCMD_SPI_NOR,
.args = CONFIG_RECOVERY_BOOTARGS_SPI_NOR,
},
{
.cmd = NULL,
.args = NULL,
},
{
.cmd = NULL,
.args = NULL,
},
{
.cmd = NULL,
.args = NULL,
},
};
/*
* Recovery Configs
*/
#define CONFIG_ANDROID_RECOVERY
#define CONFIG_RECOVERY_BOOTARGS_SPI_NOR \
"setenv bootargs_sf setenv bootargs console=ttymxc0,115200 ip=dhcp root=/dev/mtdblock3 rootwait rw"
#define CONFIG_RECOVERY_BOOTCMD_SPI_NOR \
"setenv bootcmd_sf run bootargs_sf;sf probe 1;sf read ${loadaddr} 80000 300000;bootm ${loadaddr}"
env参数设置如下:
正常启动,从MMC启动
setenv bootcmd_mmc run bootargs_base bootargs_mmc\;mmc dev 0\;ext2load mmc 0:1 ${loadaddr} /boot/uImage\;bootm ${loadaddr}
setenv bootcmd run bootcmd_mmc
recovery模式启动,从spi-flash中启动
setenv bootargs_sf setenv bootargs console=ttymxc0,115200 ip=dhcp root=/dev/mtdblock3 rootwait rw
setenv bootcmd_sf run bootargs_sf\;sf probe 1\;sf read ${loadaddr} 80000 300000\;bootm ${loadaddr}
setenv bootcmd run bootcmd_sf