platform:rk3399
OS:Android 7.1
參考:
1.Younix 《Android啟動(dòng)流程分析》
概述
? 本系列簡要介紹Android開機(jī)流程,用于整體了解Android的啟動(dòng)流程。進(jìn)一步為開機(jī)優(yōu)化,系統(tǒng)裁剪,啟動(dòng)時(shí)相關(guān)功能開發(fā),bug調(diào)試提供理論支持。
系統(tǒng)上電
系統(tǒng)電源上電順序:
VDD_LOG&VDD_CENTER --->
PLL_AVDD_0V9& PMU_VDD_0V9 --->
PLL_AVDD_1V8& PMU_VDD_1V8 --->
PMUIO1_VDD_1V8 --->
VCC_CPU_L&VCC_CPU_B --->
VDD_GPU --->
VCC_DDR&VCC_DDRC
上電原則:相同模塊 低壓先上,高壓后上,相同電壓同時(shí)上
不同的平臺(tái)和板子上電順序可能有差別,但是基本的上電原則都是差不多的。了解上電原則及上電順序有助于排查板子無法上電開機(jī)時(shí)到底是哪一步,哪個(gè)模塊出現(xiàn)了問題。
Loader 加載
第一次啟動(dòng)或者需要重新燒錄固件的時(shí)候,會(huì)進(jìn)入燒錄下載模式,需要將Loader及其他image下載到板子中。燒錄完成后一般會(huì)重啟進(jìn)入第一次固件啟動(dòng)流程。
正常開機(jī)啟動(dòng),此時(shí)不需要燒錄,系統(tǒng)會(huì)依次加載emmc(或ufs)中的image,逐步初始化,最后使整個(gè)系統(tǒng)跑起來。
第一次啟動(dòng)(燒錄)
FDL1即BL1(Bootloader 1),也就是第一階段的Bootloader,也有的廠商稱之為SPI。不同的廠商有不同的名稱,但是功能都是類似的。
正常啟動(dòng)
正常啟動(dòng)不需要下載image,只需要將image按流程從emmc拷貝到flash并逐步完成初始化和啟動(dòng)即可。
Bootloader啟動(dòng)
? Bootloader有非常多種,現(xiàn)在使用得比較廣泛的是UBoot,而不同的廠商都會(huì)對(duì)Uboot進(jìn)行不同程度的定制,增加一些特有的功能或?qū)崿F(xiàn)。所以不同廠商Bootloader啟動(dòng)流程會(huì)有一些不同,但是大致的流程和功能是差不多的,這里以RK3399的7.1.2版本的SDK做一個(gè)簡要說明。
正常啟動(dòng)
一般ROM Code是固化在芯片內(nèi)部的,BL1是有廠商提供的bin文件,并不會(huì)開放源代碼,所以從BL2開始。
從系統(tǒng)啟動(dòng)打印的信息可以找到BL2的入口如下,關(guān)鍵是_start函數(shù)。
//log
Load uboot, ReadLba = 2000 //加載uboot (BL2)
Load OK, addr=0x200000, size=0x873d4 //uboot 地址和大小 mainload結(jié)束
...
INFO: Entry point address = 0x200000 //UBoot 入口地址
//u-boot 符號(hào)表
4019: 0000000000200000 0 NOTYPE GLOBAL DEFAULT 1 _start
arch/arm/cpu/armv8/start.S
.globl _start
_start: //uboot入口
nop
b reset
....
reset:
#ifdef CONFIG_ROCKCHIP
/*
* check loader tag
*/
ldr x0, =__loader_tag
ldr w1, [x0]
ldr x0, =LoaderTagCheck
ldr w2, [x0]
cmp w1, w2
b.eq checkok
ret /* return to maskrom or miniloader */
...
master_cpu:
/* On the master CPU */
#endif /* CONFIG_ARMV8_MULTIENTRY */
bl _main //跳轉(zhuǎn)到_main執(zhí)行
arch/arm/lib/crt0_64.S
ENTRY(_main)
...
bl board_init_f /*調(diào)用board_init_f,硬件準(zhǔn)備*/
....
b board_init_r /*調(diào)用board_init_r,最終完成環(huán)境初始化*/
/*無返回*/
ENDPROC(_main)
- board_init_f
common/board.c
void board_innit_f(ulong boot_flags)
{
...
//依次調(diào)用初始化函數(shù)
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
...
}
列舉初始化函數(shù)如下:
init_fnc_t *init_sequence[] = {
arch_cpu_init, /* basic arch cpu dependent setup */
mark_bootstage,
#ifdef CONFIG_OF_CONTROL
fdtdec_check_fdt,
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_F)
board_early_init_f,
#endif
timer_init, /* initialize timer */
#ifdef CONFIG_BOARD_POSTCLK_INIT
board_postclk_init,
#endif
#ifdef CONFIG_FSL_ESDHC
get_clocks,
#endif
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
print_cpuinfo, /* display cpu info (and speed) */
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)
init_func_i2c,
#endif
dram_init, /* configure available RAM banks */
NULL,
};
- board_init_r
common/board.c
void board_init_r(gd_t *new_gd, ulong dest_addr)
{
...
serial_initialize();
...
power_init_board();
...
interrupt_init();
....
board_late_init(); //RK自定義初始化內(nèi)容
....
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop();
}
/* NOTREACHED - no way out of command loop except booting */
}
執(zhí)行RK自己定義的board_late_init();函數(shù)
board/rockchip/rk33xx/rk33xx.c
int board_late_init(void)
{
debug("board_late_init\n");
board_init_adjust_env();
load_disk_partitions();
#ifdef CONFIG_RK_PWM_REMOTE
RemotectlInit();
#endif
debug("rkimage_prepare_fdt\n");
rkimage_prepare_fdt();
#ifdef CONFIG_RK_KEY
debug("key_init\n");
key_init();
#endif
#ifdef CONFIG_RK_POWER
debug("fixed_init\n");
fixed_regulator_init();
debug("pmic_init\n");
pmic_init(0); /*pmic init*/
#if defined(CONFIG_POWER_PWM_REGULATOR)
debug("pwm_regulator_init\n");
pwm_regulator_init();
#endif
rkclk_set_apll_high();
rkclk_dump_pll();
debug("fg_init\n");
fg_init(0); /*fuel gauge init*/
debug("charger init\n");
plat_charger_init(); /*charger init*/
#endif /* CONFIG_RK_POWER */
#ifdef CONFIG_OPTEE_CLIENT
load_attestation_key();
#endif
debug("idb init\n");
//TODO:set those buffers in a better way, and use malloc?
rkidb_setup_space(gd->arch.rk_global_buf_addr);
/* after setup space, get id block data first */
rkidb_get_idblk_data();
/* Secure boot check after idb data get */
SecureBootCheck();
if (rkidb_get_bootloader_ver() == 0) {
printf("\n#Boot ver: %s\n", bootloader_ver);
}
char tmp_buf[32];
/* rk sn size 30bytes, zero buff */
memset(tmp_buf, 0, 32);
if (rkidb_get_sn(tmp_buf)) {
setenv("fbt_sn#", tmp_buf);
}
debug("fbt preboot\n");
board_fbt_preboot(); //進(jìn)入preboot
return 0;
}
board/rockchip/common/rkboot/fastboot.c
/*
* Determine if we should enter fastboot mode based on board specific
* key press or parameter left in memory from previous boot.
*/
void board_fbt_preboot(void)
{
//判斷啟動(dòng)模式
....
//判斷是否為uboot-charge并處理相關(guān)邏輯
....
rockchip_show_logo(); //顯示logo
//各種啟動(dòng)模式分支...
.....
}
初始化完成之后進(jìn)入main_loop
void main_loop(void)
{
const char *s;
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
modem_init();
cli_init();
//執(zhí)行preboot設(shè)置的command
run_preboot_environment_command();
s = bootdelay_process();
//安全的執(zhí)行給定boot command
if (cli_process_fdt(&s))
cli_secure_boot_cmd(s);
//執(zhí)行command list
autoboot_command(s);
cli_loop();
}
common/cmd_bootrk.c
int do_bootrk(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
...
//啟動(dòng)linux內(nèi)核
puts("bootrk: do_bootm_linux...\n");
do_bootm_linux(0, 0, NULL, &images);
...
}
總結(jié):
進(jìn)行軟硬件初始化
執(zhí)行指定的功能(cmd)
加載并啟動(dòng)kernel
charger mode
在Uboot啟動(dòng)并執(zhí)行command的過程中可以實(shí)現(xiàn)非常多的功能,啟動(dòng)關(guān)機(jī)充電是非常常見的功能,該功能就是在uboot中實(shí)現(xiàn)的。
主要文件目錄
include/configs/rk33plat.h //配置文件,相關(guān)功能宏
tools/resource_tool/resources //關(guān)機(jī)圖片資源
kernel/arch/arm64/boot/dts/rockchip //相關(guān)設(shè)備dts配置
board/rockchip/common/rkboot/fastboot.c //低電量判斷;啟動(dòng)模式判斷
common/cmd_charge.c //do_charge 關(guān)機(jī)充電循環(huán)
drivers/power/power_rockchip.c //充電相關(guān)設(shè)備注冊框架,狀態(tài)查詢及更新
drivers/power/charge/bq25700_charger.c //充電狀態(tài)
drivers/power/fuel_gauge/fg_cw201x.c //電量狀態(tài),電池狀態(tài)
drivers/power/mfd/fusb302.c //typec usb 設(shè)備識(shí)別
初始化
board/rockchip/rk33xx/rk33xx.c
int board_late_init(void)
{
...
//初始化RK定義的pmic框架
pmic_init(0); /*pmic init*/
//注冊電量計(jì)
fg_init(0); /*fuel gauge init*/
debug("charger init\n");
//注冊充電IC及fusb302 typec管理IC
plat_charger_init(); /*charger init*/
....
}
在board_late_init中注冊電量計(jì)cw2015,充電IC bq25700以及typec管理IC fusb302。這三個(gè)設(shè)備注冊成功后,如果此時(shí)充電器處于插上狀態(tài),則已經(jīng)開始正常進(jìn)行充電了。實(shí)現(xiàn)邏輯與kernel中的充電類似。
充電邏輯
common/cmd_charge.c
int do_charge(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
...
//一直進(jìn)行關(guān)機(jī)充電,除非條件不滿足退出
while (1) {
//step 1: 檢查充電狀態(tài)
exit_type = check_charging();
//step 2: 判斷是否亮屏
if (IS_BRIGHT(g_state.brightness))
//step 3: 檢查是否有按鍵按下
key_state = power_key_pressed();
//step 4:顯示關(guān)機(jī)動(dòng)畫
if (uboot_brightness)
update_image();
}
exit:
//正常啟動(dòng)
if (exit_type == EXIT_BOOT) {
printf("booting...\n");
return 1;
} else if (exit_type == EXIT_SHUTDOWN) {
shut_down(); //關(guān)機(jī)
}
}
上面只是RK提供的一套業(yè)務(wù)邏輯,我們可以根據(jù)自己的需求實(shí)現(xiàn)自己的關(guān)機(jī)充電業(yè)務(wù)邏輯。
recovery
除了正常的啟動(dòng),我們還可以讓系統(tǒng)進(jìn)入recovery模式。
簡介
Recovery 模式指的是一種可以對(duì)安卓內(nèi)部的數(shù)據(jù)或系統(tǒng)進(jìn)行修改的模式(類似于windows PE或DOS).在這個(gè)模式下我們可以刷入新的安卓系統(tǒng),或者對(duì)已有的系統(tǒng)進(jìn)行備份升級(jí),恢復(fù)出廠設(shè)置等操作。
Bootloader根據(jù)某些判定條件決定是否進(jìn)入recovery模式。Recovery模式會(huì)裝載recovery分區(qū),該分區(qū)包含了recovery.img。recovery.img包含了標(biāo)準(zhǔn)內(nèi)核(和boot.img中的相同)以及recovery根文件系統(tǒng)。
組成及通信
Android recovery分為三個(gè)部分兩個(gè)接口,recovery的工作需要整個(gè)軟件平臺(tái)的配合,從架構(gòu)的角度看,有三個(gè)部分:
- Main System:用boot.img啟動(dòng)的linux系統(tǒng),Android的正常工作模式
- recovery:用recovery.ing啟動(dòng)的linux系統(tǒng),主要運(yùn)行recovery程序
- Bootloader:除了加載,啟動(dòng)系統(tǒng),還會(huì)通過讀取flash的misc分區(qū)獲得來自main system和recovery的消息,并以此決定作出何種操作。
兩個(gè)通信接口:
- /cache/recovery: command、log、intent
- BCB(Bootloader Control Block): misc分區(qū)
示意圖如下:
Main System 如何進(jìn)入 Recovery 模式:
當(dāng)我們在 Main System 使用 update.zip 包進(jìn)行升級(jí)時(shí),系統(tǒng)會(huì)重啟并進(jìn)入recovery模式。在系統(tǒng)重啟前,我們可以看到Main System定會(huì)向recovery域?qū)懭隻oot-recovery(粉紅色線),用來告知bootloader重啟后進(jìn)入Rcovery模式。這一步是必須的,至于Main System是否會(huì)向recovery域?qū)懭胫滴覀冊谠创a中不能肯定這一點(diǎn)。即便如此,重啟進(jìn)入Recovery模式后,Bootloader會(huì)從/cache/recovery/command中讀取值并放入到BCB的recovery域。而Main System在重啟之前肯定會(huì)向/cache/recovery/command中寫入Recovery將要進(jìn)行的操作命令。
下圖是Main System進(jìn)入Recovery模式調(diào)用接口的流程圖:

1.installPackage:RecoverySystem的接口,完成升級(jí)包路徑轉(zhuǎn)換,并調(diào)用bootCommand。
2.bootCommand:RecoverySystem的接口,將命令寫入/cache/recovery/command,并調(diào)用pm.reboot.
3.Pm.reboot:PowerManager的接口,重啟并進(jìn)入Recovery模式。
進(jìn)入recovery
void board_fbt_preboot(void)
{
...
//獲取reboot_type
frt = board_fbt_get_reboot_type();
...
//進(jìn)入recovery相應(yīng)操作
if (frt == FASTBOOT_REBOOT_RECOVERY) {
printf("\n%s: starting recovery img because of reboot flag\n", __func__);
#if 1
board_fbt_set_recovery_for_hrr_32();
#endif
board_fbt_run_recovery();
} else if (frt == FASTBOOT_REBOOT_RECOVERY_WIPE_DATA) {
printf("\n%s: starting recovery img to wipe data "
"because of reboot flag\n", __func__);
/* we've not initialized most of our state so don't
* save env in this case
*/
board_fbt_run_recovery_wipe_data();
}
...
個(gè)人博客:https://www.letcos.top/