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



Pre

同大多數(shù)的Bootloader一樣,uboot的啟動過程也分為BL1、BL2兩個階段,分別對應(yīng)著SPL和Uboot。

  • SPL(BL1階段):負(fù)責(zé)開發(fā)板的基礎(chǔ)配置和設(shè)備初始化,并且搬運Uboot到內(nèi)存中,由匯編代碼和少量的C語言實現(xiàn)

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

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




程序執(zhí)行流程圖

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

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

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




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

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

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

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

當(dāng)然,準(zhǔn)確的方法是查看Makefile文件,分析出來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é)對齊 .text : { *(.__image_copy_start)  @映像文件復(fù)制起始地址 *(.vectors)     @異常向量表 arch/arm/cpu/armv7/start.o (.text*) @啟動函數(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對應(yīng)文件arch/arm/cpu/armv7/start.S,該文件中定義了main函數(shù)的入口。

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


board_init_f——板級前置初始化

跟隨上文的程序執(zhí)行流程圖,我們看board_init_f這個函數(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è)備樹、DM驅(qū)動模型等,另外還包括global_data結(jié)構(gòu)體相關(guān)對象的變量。

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

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



relocate_code重定向

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

4.1為什么需要重定向呢?

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

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

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


4.2Uboot是如何重定向的?

Uboot的重定向有如下幾個步驟:

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

  • 計算uboot代碼空間到relocate的位置的偏移

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

  • relocateUboot

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

  • relocate中斷向量表

運行大致流程:

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

ENTRY(_main)  bl  board_init_f @@ 在board_init_f里面實現(xiàn)了 @@                             (1)對relocate進(jìn)行空間規(guī)劃 @@                             (2)計算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 @@ 計算返回地址在新的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中,在這里面實現(xiàn)了 @@                                       (1)relocate舊的uboot代碼空間到新的空間上去 @@                                       (2)修改relocate之后全局變量的label @@ 注意,由于上述已經(jīng)把lr寄存器重定義到uboot新的代碼空間中了,所以返回之后,就已經(jīng)跳到了新的代碼空間了!?。。。?!   bl  relocate_vectors @@ relocate中斷向量表
  • setup_reloc——重定向地址查看(仿真有關(guān))

在這里我們說明一下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ī)的地址仿真的話,我們可能訪問到錯誤的內(nèi)存空間,通過setup_reloc的Relocating to %08lx打印,我們可以得到重定向后的地址,方便我們仿真。

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


4.3Uboot重定向作用

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


board_init_r——板級后置初始化

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

board_init_r這個函數(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相同,同樣有一個init_sequence_r初始化列表,包括:initr_dmDM模型初始化,initr_mmcMMC驅(qū)動初始化,等等。

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



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

該函數(shù)為Uboot的最終執(zhí)行函數(shù),無論是加載kernel還是uboot的命令行體系,均由此實現(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)境變量,兩個參數(shù)分別為name和value

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

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

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

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


6.1bootdelay_process

記得對照文章開始的執(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時間  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ù)運行的指令

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


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來處理用戶的輸入、輸出信息。

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

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

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


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

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

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

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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