深入MTK平臺(tái)bootloader啟動(dòng)分析筆記
bootloader到kernel啟動(dòng)總邏輯流程圖
ARM架構(gòu)中,EL0/EL1是必須實(shí)現(xiàn),EL2/EL3是選配,ELx跟層級(jí)對(duì)應(yīng)關(guān)系:
EL0 -- app
EL1 -- Linux kernel 、lk
EL2 -- hypervisor(虛擬化)
EL3 -- ARM trust firmware 、pre-loader
若平臺(tái)未實(shí)現(xiàn)EL3(atf),pre-loader直接加載lk:

若平臺(tái)實(shí)現(xiàn)EL3,則需要先加載完ATF再由ATF去加載lk:

bootloader 啟動(dòng)分兩個(gè)階段,一個(gè)是pre-loader加載lk(u-boot)階段,另一個(gè)是lk加載kernel階段。
下面跟著流程圖簡(jiǎn)述第一個(gè)階段的加載流程。
1-3:設(shè)備上電起來后,跳轉(zhuǎn)到Boot ROM(不是flash)中的boot code中執(zhí)行把pre-loader加載起到ISRAM, 因?yàn)楫?dāng)前DRAM(RAM分SRAM跟DRAM,簡(jiǎn)單來說SRAM就是cache,DRAM就是普通內(nèi)存)還沒有準(zhǔn)備好,所以要先把pre-loader load到芯片內(nèi)部的ISRAM(Internal SRAM)中。
4-6:pre-loader初始化好DRAM后就將lk從flash(nand/emmc)中加載到DRAM中運(yùn)行;
7-8:解壓bootimage成ramdisk跟kernel并載入DRAM中,初始化dtb;
9-11:lk跳轉(zhuǎn)到kernl初始化, kernel初始化完成后fork出init進(jìn)程, 然后拉起ramdisk中的init程序,進(jìn)入用戶空間初始化,init進(jìn)程fork出zygote進(jìn)程..直到整個(gè)Android啟動(dòng)完成.
Pre-loader主要干的事情就是初始化某些硬件,比如:UART,GPIO,DRAM,TIMER,RTC,PMIC 等等,建立起最基本的運(yùn)行環(huán)境,最重要的就是初始化DRAM。

./bootloader/preloader/platform/mt6580/src/init/init.s
.section?.text.start
...
?
.globl?_start
...
?
????/*?set?the?cpu?to?SVC32?mode?*/
????MRS?r0,cpsr
????BIC?r0,r0,#0x1f
????ORR?r0,r0,#0xd3
????MSR?cpsr,r0
????/*?disable?interrupt?*/
????MRS?r0,?cpsr
????MOV?r1,?#INT_BIT
????ORR?r0,?r0,?r1
????MSR?cpsr_cxsf,?r0
????
...
setup_stk?:
????/*?setup?stack?*/
????LDR?r0,?stack
????LDR?r1,?stacksz
...
?
entry?:
????LDR?r0,?=bldr_args_addr
????
????/*?跳轉(zhuǎn)到C代碼?main?入口?*/
????B???main
init.s 主要干的事情是切換系統(tǒng)到管理模式(svc)(如果平臺(tái)有實(shí)現(xiàn)el3,那么pre-loader運(yùn)行在el3,否則運(yùn)行在el1),禁止irq/fiq,設(shè)置stack等, 然后jump到c代碼main函數(shù)入口。?
./bootloader/preloader/platform/mt6580/src/core/main.c
?
void?main(u32?*arg)
{
????struct?bldr_command_handler?handler;
????u32?jump_addr,?jump_arg;
?
????/*?get?the?bldr?argument?*/
????bldr_param?=?(bl_param_t?*)*arg;
?
//?初始化uart?
????mtk_uart_init(UART_SRC_CLK_FRQ,?CFG_LOG_BAUDRATE);
????
//?這里干了很多事情,包括各種的平臺(tái)硬件(timer,pmic,gpio,wdt...)初始化工作.
????bldr_pre_process();
?
????handler.priv?=?NULL;
????handler.attr?=?0;
????handler.cb???=?bldr_cmd_handler;
?
//?這里是獲取啟動(dòng)模式等信息保存到全局變量g_boot_mode和g_meta_com_type?中.
?BOOTING_TIME_PROFILING_LOG("before?bldr_handshake");
????bldr_handshake(&handler);
?BOOTING_TIME_PROFILING_LOG("bldr_handshake");
?
//?下面跟?secro?img?相關(guān),跟平臺(tái)設(shè)計(jì)強(qiáng)相關(guān).
????/*?security?check?*/
????sec_lib_read_secro();
????sec_boot_check();
????device_APC_dom_setup();
?
?BOOTING_TIME_PROFILING_LOG("sec_boot_check");
?
/*?如果已經(jīng)實(shí)現(xiàn)EL3,那么進(jìn)行tz預(yù)初始化?*/
#if?CFG_ATF_SUPPORT
????trustzone_pre_init();
#endif
?
/*?bldr_load_images
此函數(shù)要做的事情就是把lk從ROM中指定位置load到DRAM中,開機(jī)log中可以看到具體信息:
[PART]?load?"lk"?from?0x0000000001CC0200?(dev)?to?0x81E00000?(mem)?[SUCCESS]
這里準(zhǔn)備好了jump到DRAM的具體地址,下面詳細(xì)分析.
*/
????if?(0?!=?bldr_load_images(&jump_addr))?{
????????print("%s?Second?Bootloader?Load?Failed\n",?MOD);
????????goto?error;
????}
?
/*?
該函數(shù)的實(shí)現(xiàn)體是platform_post_init,這里要干的事情其實(shí)比較簡(jiǎn)單,就是通過
hw_check_battery去判斷當(dāng)前系統(tǒng)是否存在電池(判斷是否有電池ntc腳來區(qū)分),
如果不存在就陷入while(1)卡住了,所以在es階段調(diào)試有時(shí)候
需要接電源調(diào)試的,就需要改這里面的邏輯才可正常開機(jī)?
*/
????bldr_post_process();
?
//?atf?正式初始化,使用特有的系統(tǒng)調(diào)用方式實(shí)現(xiàn).
#if?CFG_ATF_SUPPORT
????trustzone_post_init();
#endif
?
/*?跳轉(zhuǎn)傳入lk的參數(shù),包括boot?time/mode/reason?等,這些參數(shù)在
?? platform_set_boot_args 函數(shù)獲取。
*/
????jump_arg?=?(u32)&(g_dram_buf->boottag);
?
?
/*?執(zhí)行jump系統(tǒng)調(diào)用,從?pre-loader?跳轉(zhuǎn)到?lk執(zhí)行,
?
?????如果實(shí)現(xiàn)了EL3情況就要
??復(fù)雜一些,需要先跳轉(zhuǎn)到EL3初始化,然后再跳回lk,pre-loader執(zhí)行在EL3,
??LK執(zhí)行在EL1)
?
???從log可以類似看到這些信息:
????[BLDR]?jump?to?0x81E00000
????[BLDR]?<0x81E00000>=0xEA000007
????[BLDR]?<0x81E00004>=0xEA0056E2
*/
#if?CFG_ATF_SUPPORT
????/*?64S3,32S1,32S1?(MTK_ATF_BOOT_OPTION?=?0)
??*?re-loader?jump?to?LK?directly?and?then?LK?jump?to?kernel?directly?*/
????if?(?BOOT_OPT_64S3?==?g_smc_boot_opt?&&
?????????BOOT_OPT_32S1?==?g_lk_boot_opt?&&
?????????BOOT_OPT_32S1?==?g_kernel_boot_opt)?{
????????print("%s?64S3,32S1,32S1,?jump?to?LK\n",?MOD);
????????bldr_jump(jump_addr,?jump_arg,?sizeof(boot_arg_t));
????}?else?{
????????//?如果?el3?使用aarch64實(shí)現(xiàn),則jump到atf.
????????print("%s?Others,?jump?to?ATF\n",?MOD);
????????bldr_jump64(jump_addr,?jump_arg,?sizeof(boot_arg_t));
????}
#else
????bldr_jump(jump_addr,?jump_arg,?sizeof(boot_arg_t));
#endif
//?如果沒有取到j(luò)ump_addr,則打印錯(cuò)誤提示,進(jìn)入while(1)等待.
error:
????platform_error_handler();
}
main 函數(shù)小結(jié):
1、各種硬件初始化(uart、pmic、wdt、timer、mem..);
2、獲取系統(tǒng)啟動(dòng)模式等,保存在全局變量中;
3、Security check,跟secro.img相關(guān);
4、如果系統(tǒng)已經(jīng)實(shí)現(xiàn)el3,則進(jìn)入tz初始化;
5、獲取lk加載到DRAM的地址(固定值),然后從ROM中找到lk分區(qū)的地址, 如果沒找到j(luò)ump_addr,則 goto error;
6、battery check,如果沒有電池就會(huì)陷入while(1);
7、jump到lk(如果有實(shí)現(xiàn)el3,則會(huì)先jump到el3,然后再回到lk)
函數(shù)主要干的事情就是找到lk分區(qū)地址和lk加載到DRAM中的地址, 準(zhǔn)備好jump到lk執(zhí)行。
如下源碼分析:
static?int?bldr_load_images(u32?*jump_addr)
{
????int?ret?=?0;
????blkdev_t?*bootdev;
????u32?addr?=?0;
????char?*name;
????u32?size?=?0;
????u32?spare0?=?0;
????u32?spare1?=?0;
?
...
/*?這個(gè)地址是一個(gè)固定值,可以查到定義在:
???./bootloader/preloader/platform/mt6580/default.mak:95:
???CFG_UBOOT_MEMADDR?:=?0x81E00000
???從log中可以看到:
???[BLDR]?jump?to?0x81E00000
*/
????addr?=?CFG_UBOOT_MEMADDR;
????
/*?然后去ROM找到lk所在分區(qū)地址?*/
????ret?=?bldr_load_part("lk",?bootdev,?&addr,?&size);
????if?(ret)
???????return?ret;
????*jump_addr?=?addr;
????
}
?
//?這個(gè)函數(shù)邏輯很簡(jiǎn)單,就不需要多說了.
int?bldr_load_part(char?*name,?blkdev_t?*bdev,?u32?*addr,?u32?*size)
{
????part_t?*part?=?part_get(name);
?
????if?(NULL?==?part)?{
????????print("%s?%s?partition?not?found\n",?MOD,?name);
????????return?-1;
????}
?
????return?part_load(bdev,?part,?addr,?0,?size);
}
?
//?真正的load實(shí)現(xiàn)是在part_load函數(shù).
int?part_load(blkdev_t?*bdev,?part_t?*part,?u32?*addr,?u32?offset,?u32?*size)
{
????int?ret;
????img_hdr_t?*hdr?=?(img_hdr_t?*)img_hdr_buf;
????part_hdr_t?*part_hdr?=?&hdr->part_hdr;
????gfh_file_info_t?*file_info_hdr?=?&hdr->file_info_hdr;
?
????/*?specify?the?read?offset?*/
????u64?src?=?part->startblk?*?bdev->blksz?+?offset;
????u32?dsize?=?0,?maddr?=?0;
????u32?ms;
?
//?檢索分區(qū)頭是否正確。
????/*?retrieve?partition?header.?*/
????if?(blkdev_read(bdev,?src,?sizeof(img_hdr_t),?(u8*)hdr,0)?!=?0)?{
????????print("[%s]bdev(%d)?read?error?(%s)\n",?MOD,?bdev->type,?part->name);
????????return?-1;
????}
?
????if?(part_hdr->info.magic?==?PART_MAGIC)?{
?
????????/*?load?image?with?partition?header?*/
????????part_hdr->info.name[31]?=?'\0';
?
????/*
????????輸出分區(qū)的各種信息,從log中可以看到:
????????[PART]?Image?with?part?header
????????[PART]?name?:?lk
????????[PART]?addr?:?FFFFFFFFh?mode?:?-1
????????[PART]?size?:?337116
????????[PART]?magic:?58881688h
????*/
????????print("[%s]Img?with?part?header\n",?MOD);
????????print("[%s]name:%s\n",?MOD,?part_hdr->info.name);
????????print("[%s]addr:%xh\n",?MOD,?part_hdr->info.maddr);
????????print("[%s]size:%d\n",?MOD,?part_hdr->info.dsize);
????????print("[%s]magic:%xh\n",?MOD,?part_hdr->info.magic);
?
????????maddr?=?part_hdr->info.maddr;
????????dsize?=?part_hdr->info.dsize;
????????src?+=?sizeof(part_hdr_t);
?
????????memcpy(part_info?+?part_num,?part_hdr,?sizeof(part_hdr_t));
????????part_num++;
????}?else?{
????????print("[%s]%s?img?not?exist\n",?MOD,?part->name);
????????return?-1;
????}
?
//?如果maddr沒有定義,那么就使用前面?zhèn)魅氲牡刂穉ddr.
????if?(maddr?==?PART_HEADER_MEMADDR/*0xffffffff*/)
????????maddr?=?*addr;
?
????if_overlap_with_dram_buffer((u32)maddr,?((u32)maddr?+?dsize));
?
????ms?=?get_timer(0);
????if?(0?==?(ret?=?blkdev_read(bdev,?src,?dsize,?(u8*)maddr,0)))
????????*addr?=?maddr;
????ms?=?get_timer(ms);
?
/*?如果一切順利就會(huì)打印出關(guān)鍵信息:
???[PART]?load?"lk"?from?0x0000000001CC0200?(dev)?to?0x81E00000?(mem)?[SUCCESS]
???[PART]?load?speed:?25324KB/s,?337116?bytes,?13ms
*/
????print("\n[%s]load?\"%s\"?from?0x%llx(dev)?to?0x%x?(mem)?[%s]\n",?MOD,
????????part->name,?src,?maddr,?(ret?==?0)???"SUCCESS"?:?"FAILED");
?
????if(?ms?==?0?)
????????ms+=1;
?
????print("[%s]load?speed:%dKB/s,%d?bytes,%dms\n",?MOD,?((dsize?/?ms)?*?1000)?/?1024,?dsize,?ms);
?
?
????return?ret;
}
//?就是包了一層而已.
static?void?bldr_post_process(void)
{
????platform_post_init();
}
?
//?重點(diǎn)是這個(gè)函數(shù):
void?platform_post_init(void)
{
????/*?normal?boot?to?check?battery?exists?or?not?*/
????if?(g_boot_mode?==?NORMAL_BOOT?&&?!hw_check_battery()?&&?usb_accessory_in())?{
...
????????pl_charging(1);
????????do?{
????????????mdelay(300);
????????????
????????????/*?檢查電池是否存在,?如果使用電源調(diào)試則需要修改此函數(shù)邏輯?*/
????????????if?(hw_check_battery())
????????????????break;
????????????/*?喂狗,以免超時(shí)被狗咬?*/
????????????platform_wdt_all_kick();
????????}?while(1);
????????/*?disable?force?charging?mode?*/
????????pl_charging(0);
????}
?
...
}
Pre-loader 到 Lk的源碼分析到這就完成了.

