<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          深入MTK平臺(tái)bootloader啟動(dòng)分析筆記

          共 8057字,需瀏覽 17分鐘

           ·

          2020-11-19 00:14

          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到lk(mt6580為例)

          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ù)入口。?


          進(jìn)入源碼分析

          ./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)


          重點(diǎn)函數(shù)分析

          bldr_load_images

          函數(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;
          }

          bldr_post_process

          函數(shù)主要干的事情就是從pmic去檢查是否有電池存在,如果沒有就等待

          如下源碼分析,比較簡(jiǎn)單:

          //?就是包了一層而已.
          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的源碼分析到這就完成了.



          推薦閱讀:
          ? ??專輯|Linux文章匯總
          ? ??專輯|程序人生
          ? ??專輯|C語言

          嵌入式Linux
          微信掃描二維碼,關(guān)注我的公眾號(hào)?
          瀏覽 312
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  在线国产福利视频 | 国产欧美操逼 | 精品久久久久久久中文字幕 | 美女滋味网站 | 美女尻屄网站 |