[Boot]硬件上電到Bootloader

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é):

  1. 進(jìn)行軟硬件初始化

  2. 執(zhí)行指定的功能(cmd)

  3. 加載并啟動(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è)部分:

  1. Main System:用boot.img啟動(dòng)的linux系統(tǒng),Android的正常工作模式
  2. recovery:用recovery.ing啟動(dòng)的linux系統(tǒng),主要運(yùn)行recovery程序
  3. Bootloader:除了加載,啟動(dòng)系統(tǒng),還會(huì)通過讀取flash的misc分區(qū)獲得來自main system和recovery的消息,并以此決定作出何種操作。

兩個(gè)通信接口:

  1. /cache/recovery: command、log、intent
  2. 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/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容