當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 嵌入式藝術(shù)
[導(dǎo)讀]同大多數(shù)的Bootloader一樣,uboot的啟動(dòng)過(guò)程也分為BL1、BL2兩個(gè)階段,分別對(duì)應(yīng)著SPL和Uboot。

同大多數(shù)的Bootloader一樣,uboot的啟動(dòng)過(guò)程也分為BL1、BL2兩個(gè)階段,分別對(duì)應(yīng)著SPL和Uboot。
  • SPL(BL1階段):負(fù)責(zé)開發(fā)板的基礎(chǔ)配置和設(shè)備初始化,并且搬運(yùn)Uboot到內(nèi)存中,由匯編代碼和少量的C語(yǔ)言實(shí)現(xiàn)

  • Uboot(BL2階段):主要負(fù)責(zé)初始化外部設(shè)備,引導(dǎo)Kernel啟動(dòng),由純C語(yǔ)言實(shí)現(xiàn)。

我們這篇文章,主要介紹Uboot(BL2階段)的啟動(dòng)流程,BL1階段啟動(dòng)流程的詳細(xì)分析,可以見我的后續(xù)文章。想要深入了解的,可以好好研究下!




程序執(zhí)行流程圖

我們先總體來(lái)看一下Uboot的執(zhí)行步驟,這里以EMMC作為啟動(dòng)介質(zhì),進(jìn)行分析!

無(wú)論是哪種啟動(dòng)介質(zhì),基本流程都相似,我們這就往下看!

打開圖片,結(jié)合文檔、圖片、代碼進(jìn)行理解!




u-boot.lds——Uboot的入口函數(shù)

u-boot.lds:是uboot工程的鏈接腳本文件,對(duì)于工程的編譯和鏈接有非常重要的作用,決定了uboot的組裝,并且u-boot.lds鏈接文件中的ENTRY(_start)指定了uboot程序的入口地址。

如果不知道u-boot.lds放到在哪里,可以通過(guò)find -name u-boot.lds查找,根目錄要進(jìn)入到uboot的源碼的位置哦!

如果查找結(jié)果有很多,結(jié)合自己的板子信息,確定自己使用的u-boot.lds。

當(dāng)然,準(zhǔn)確的方法是查看Makefile文件,分析出來(lái)u-boot.lds所生成的位置。

在u-boot.lds的文件中,可以看到.text段,存放的就是執(zhí)行的文本段。截取部分代碼段如下:

			

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { . = 0x00000000;    @起始地址 . = ALIGN(4);     @四字節(jié)對(duì)齊 .text : { *(.__image_copy_start)  @映像文件復(fù)制起始地址 *(.vectors)     @異常向量表 arch/arm/cpu/armv7/start.o (.text*) @啟動(dòng)函數(shù) } ...... }

  • ENTRY(_start):程序的入口函數(shù),_start在arch/arm/lib/vectors.S中定義.globl _start

  • SECTIONS定義了段,包括text文本段、data數(shù)據(jù)段、bss段等。

  • __image_copy_start在System.map和u-boot.map中均有定義

  • arch/arm/cpu/armv7/start.o對(duì)應(yīng)文件arch/arm/cpu/armv7/start.S,該文件中定義了main函數(shù)的入口。

Tip:上面只進(jìn)行大概分析,有匯編經(jīng)驗(yàn)的朋友,可以詳細(xì)進(jìn)行分析!


board_init_f——板級(jí)前置初始化

跟隨上文的程序執(zhí)行流程圖,我們看board_init_f這個(gè)函數(shù)。其位于common/board_f.c。

void board_init_f(ulong boot_flags) {  gd->flags = boot_flags;  gd->have_console = 0;   if (initcall_run_list(init_sequence_f))  hang(); }  static const init_fnc_t init_sequence_f[] = {  setup_mon_len,  ...  log_init,  arch_cpu_init,  /* basic arch cpu dependent setup */  env_init,  /* initialize environment */  ...   reloc_fdt,  reloc_bootstage,  reloc_bloblist,  setup_reloc,  ... }

board_init_f(),其最核心的內(nèi)容就是調(diào)用了init_sequence_f初始化序列,進(jìn)行了一系列初始化的工作。

主要包括:串口、定時(shí)器、設(shè)備樹、DM驅(qū)動(dòng)模型等,另外還包括global_data結(jié)構(gòu)體相關(guān)對(duì)象的變量。

詳細(xì)分析,可以看文末的參考文章[1]

我們需要注意的一點(diǎn)就是,在初始化隊(duì)列末尾,執(zhí)行了幾個(gè)reloc_xxx的函數(shù),這幾個(gè)函數(shù)實(shí)現(xiàn)了Uboot的重定向功能。



relocate_code重定向

重定向技術(shù),可以說(shuō)也算是Uboot的一個(gè)重點(diǎn)了,也就是將uboot自身鏡像拷貝到ddr上的另外一個(gè)位置的動(dòng)作。

4.1為什么需要重定向呢?

一般需要重定向的條件如下:

  • uboot存儲(chǔ)在只讀存儲(chǔ)器上,比如ROM、Nor flash,需要將代碼拷貝到DDR上,才能完整運(yùn)行Uboot。

  • 為Kernel騰空間,Kernel一般會(huì)放在DDR的地段地址上,所以要把Uboot重定向到頂端地址,避免沖突。


4.2Uboot是如何重定向的?

Uboot的重定向有如下幾個(gè)步驟:

  • 對(duì)relocate進(jìn)行空間劃分

  • 計(jì)算uboot代碼空間到relocate的位置的偏移

  • relocate舊的global_data到新的global_data空間上

  • relocateUboot

  • 修改relocate后的全局變量的label

  • relocate中斷向量表

運(yùn)行大致流程:

arch/arm/lib/crt0.S文件內(nèi),主要實(shí)現(xiàn)了:

ENTRY(_main)  bl  board_init_f @@ 在board_init_f里面實(shí)現(xiàn)了 @@                             (1)對(duì)relocate進(jìn)行空間規(guī)劃 @@                             (2)計(jì)算uboot代碼空間到relocation的位置的偏移 @@                             (3)relocate舊的global_data到新的global_data的空間上   ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */  bic sp, sp, #7  /* 8-byte alignment for ABI compliance */  ldr r9, [r9, #GD_BD]        /* r9 = gd->bd */  sub r9, r9, #GD_SIZE        /* new GD is below bd */ @@ 把新的global_data地址放在r9寄存器中   adr lr, here  ldr r0, [r9, #GD_RELOC_OFF]     /* r0 = gd->reloc_off */  add lr, lr, r0 @@ 計(jì)算返回地址在新的uboot空間中的地址。b調(diào)用函數(shù)返回之后,就跳到了新的uboot代碼空間中。   ldr r0, [r9, #GD_RELOCADDR]     /* r0 = gd->relocaddr */ @@ 把uboot的新的地址空間放到r0寄存器中,作為relocate_code的參數(shù)  b   relocate_code @@ 跳轉(zhuǎn)到relocate_code中,在這里面實(shí)現(xiàn)了 @@                                       (1)relocate舊的uboot代碼空間到新的空間上去 @@                                       (2)修改relocate之后全局變量的label @@ 注意,由于上述已經(jīng)把lr寄存器重定義到uboot新的代碼空間中了,所以返回之后,就已經(jīng)跳到了新的代碼空間了?。。。。?!   bl  relocate_vectors @@ relocate中斷向量表
  • setup_reloc——重定向地址查看(仿真有關(guān))

在這里我們說(shuō)明一下board_init_f里面的setup_reloc初始化函數(shù)

static int setup_reloc(void) {  if (gd->flags & GD_FLG_SKIP_RELOC) {  debug("Skipping relocation due to flag\n");  return 0;  }  #ifdef CONFIG_SYS_TEXT_BASE #ifdef ARM  gd->reloc_off = gd->relocaddr - (unsigned long)__image_copy_start; #elif defined(CONFIG_M68K)  /*  * On all ColdFire arch cpu, monitor code starts always  * just after the default vector table location, so at 0x400  */  gd->reloc_off = gd->relocaddr - (CONFIG_SYS_TEXT_BASE + 0x400); #elif !defined(CONFIG_SANDBOX)  gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE; #endif #endif  memcpy(gd->new_gd, (char *)gd, sizeof(gd_t));   debug("Relocation Offset is: %08lx\n", gd->reloc_off);  if (is_debug_open()) {  printf("Relocating to %08lx, new gd at %08lx, sp at %08lx\n",  gd->relocaddr, (ulong)map_to_sysmem(gd->new_gd),  gd->start_addr_sp);  }   return 0; }

于,Uboot進(jìn)行了重定向,所以按照常規(guī)的地址仿真的話,我們可能訪問到錯(cuò)誤的內(nèi)存空間,通過(guò)setup_reloc的Relocating to %08lx打印,我們可以得到重定向后的地址,方便我們仿真。

Uboot的重定向也有相當(dāng)大的一部分知識(shí)點(diǎn),上面也僅僅是簡(jiǎn)單介紹了relocate的基本步驟和流程,后續(xù)看大家需要,如果大家想了解,我再補(bǔ)上這一部分。


4.3Uboot重定向作用

總之,Uboot重定向之后,把Uboot整體搬運(yùn)到了高端內(nèi)存區(qū),為Kernel的加載提供空間,避免內(nèi)存踐踏。


board_init_r——板級(jí)后置初始化

我們接著跟著流程圖往下看,重定向之后,Uboot運(yùn)行于新的地址空間,接著我們執(zhí)行board_init_r,主要作為Uboot運(yùn)行的最后初始化步驟。

board_init_r這個(gè)函數(shù),同樣位于common/board_f.c,主要用于初始化各類外設(shè)信息

		

void board_init_r(gd_t *new_gd, ulong dest_addr) { if (initcall_run_list(init_sequence_r)) hang(); /* NOTREACHED - run_main_loop() does not return */ hang(); } static init_fnc_t init_sequence_r[] = { initr_reloc, initr_reloc_global_data, board_init, /* Setup chipselects */ initr_dm, initr_mmc, ... run_main_loop }

與board_init_f相同,同樣有一個(gè)init_sequence_r初始化列表,包括:initr_dmDM模型初始化,initr_mmcMMC驅(qū)動(dòng)初始化,等等。

最終,uboot就運(yùn)行到了run_main_loop,進(jìn)而執(zhí)行main_loop這個(gè)函數(shù)。



main_loop——Uboot主循環(huán)

該函數(shù)為Uboot的最終執(zhí)行函數(shù),無(wú)論是加載kernel還是uboot的命令行體系,均由此實(shí)現(xiàn)。

		

void main_loop(void) { const char *s; bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop"); if (IS_ENABLED(CONFIG_VERSION_VARIABLE)) env_set("ver", version_string);  /* set version variable */ cli_init(); if (IS_ENABLED(CONFIG_USE_PREBOOT)) run_preboot_environment_command(); if (IS_ENABLED(CONFIG_UPDATE_TFTP)) update_tftp(0UL, NULL, NULL); s = bootdelay_process(); if (cli_process_fdt(&s)) cli_secure_boot_cmd(s); autoboot_command(s); cli_loop(); panic("No CLI available"); }

env_set:設(shè)置環(huán)境變量,兩個(gè)參數(shù)分別為name和value

cli_init:用于初始化hash shell的一些變量

run_preboot_environment_command:執(zhí)行預(yù)定義的環(huán)境變量的命令

bootdelay_process:加載延時(shí)處理,一般用于Uboot啟動(dòng)后,有幾秒的倒計(jì)時(shí),用于進(jìn)入命令行模式。

cli_loop:命令行模式,主要作用于Uboot的命令行交互。


6.1bootdelay_process

記得對(duì)照文章開始的執(zhí)行流程圖哦!

詳細(xì)解釋標(biāo)注于代碼中......

const char *bootdelay_process(void) {  char *s;  int bootdelay;   bootcount_inc();   s = env_get("bootdelay");        //先判斷是否有bootdelay環(huán)境變量,如果沒有,就使用menuconfig中配置的CONFIG_BOOTDELAY時(shí)間  bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;   if (IS_ENABLED(CONFIG_OF_CONTROL))      //是否使用設(shè)備樹進(jìn)行配置  bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay",  bootdelay);   debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);   if (IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW))  bootdelay = menu_show(bootdelay);  bootretry_init_cmd_timeout();  #ifdef CONFIG_POST  if (gd->flags & GD_FLG_POSTFAIL) {  s = env_get("failbootcmd");  } else #endif /* CONFIG_POST */  if (bootcount_error())  s = env_get("altbootcmd");  else  s = env_get("bootcmd");        //獲取bootcmd環(huán)境變量,用于后續(xù)的命令執(zhí)行   if (IS_ENABLED(CONFIG_OF_CONTROL))  process_fdt_options(gd->fdt_blob);  stored_bootdelay = bootdelay;   return s; }
6.2autoboot_command

詳細(xì)解釋標(biāo)注于代碼中......

void autoboot_command(const char *s) {  debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "");   if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {  bool lock;  int prev;   lock = IS_ENABLED(CONFIG_AUTOBOOT_KEYED) &&  !IS_ENABLED(CONFIG_AUTOBOOT_KEYED_CTRLC);  if (lock)  prev = disable_ctrlc(1); /* disable Ctrl-C checking */   run_command_list(s, -1, 0);   if (lock)  disable_ctrlc(prev); /* restore Ctrl-C checking */  }   if (IS_ENABLED(CONFIG_USE_AUTOBOOT_MENUKEY) &&  menukey == AUTOBOOT_MENUKEY) {  s = env_get("menucmd");  if (s)  run_command_list(s, -1, 0);  } } 

我們看一下判斷條件stored_bootdelay != -1 && s && !abortboot(stored_bootdelay

  • stored_bootdelay:為環(huán)境變量的值,或者menuconfig設(shè)置的值

  • s:為環(huán)境變量bootcmd的值,為后續(xù)運(yùn)行的指令

  • abortboot(stored_bootdelay):主要用于判斷是否有按鍵按下。如果按下,則不執(zhí)行bootcmd命令,進(jìn)入cli_loop命令行模式;如果不按下,則執(zhí)行bootcmd命令,跳轉(zhuǎn)到加載Linux啟動(dòng)。


6.3cli_loop
void cli_loop(void)
{
    bootstage_mark(BOOTSTAGE_ID_ENTER_CLI_LOOP);
#ifdef CONFIG_HUSH_PARSER
    parse_file_outer();
    /* This point is never reached */
    for (;;); //死循環(huán)
#elif defined(CONFIG_CMDLINE)
    cli_simple_loop();
#else
    printf("## U-Boot command line is disabled. Please enable CONFIG_CMDLINE\n");
#endif /*CONFIG_HUSH_PARSER*/
}

如上代碼,程序只執(zhí)行parse_file_outer來(lái)處理用戶的輸入、輸出信息。

好啦,基本到這里,我們已經(jīng)對(duì)Uboot的啟動(dòng)流程了然于胸了吧!

當(dāng)然,更深層次的不建議去深入了解,有時(shí)間可以慢慢去研究。

大家有疑問,可以評(píng)論區(qū)交流......


本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉