www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

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



Pre

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

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

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

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




程序執(zhí)行流程圖

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

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

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




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

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

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

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

當然,準確的方法是查看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)  @映像文件復制起始地址 *(.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īng)驗的朋友,可以詳細進行分析!


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初始化序列,進行了一系列初始化的工作。

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

詳細分析,可以看文末的參考文章[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進行空間劃分

  • 計算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進行空間規(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)跳到了新的代碼空間了?。。。。。?/span>   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進行了重定向,所以按照常規(guī)的地址仿真的話,我們可能訪問到錯誤的內(nèi)存空間,通過setup_reloc的Relocating to %08lx打印,我們可以得到重定向后的地址,方便我們仿真。

Uboot的重定向也有相當大的一部分知識點,上面也僅僅是簡單介紹了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,進而執(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í)行預定義的環(huán)境變量的命令

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

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


6.1bootdelay_process

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

詳細解釋標注于代碼中......

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è)備樹進行配置  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

詳細解釋標注于代碼中......

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命令,進入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的啟動流程了然于胸了吧!

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

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


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

LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設(shè)備,其驅(qū)動電源的性能直接關(guān)系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅(qū)動電源設(shè)計中至關(guān)重要的兩個環(huán)節(jié),集成化方案的設(shè)計成為提升電機驅(qū)動性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機 驅(qū)動電源

LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設(shè)備的使用壽命。然而,在實際應(yīng)用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設(shè)計、生...

關(guān)鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計 驅(qū)動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術(shù)之一是電機驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅(qū)動系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動汽車的動力性能和...

關(guān)鍵字: 電動汽車 新能源 驅(qū)動電源

在現(xiàn)代城市建設(shè)中,街道及停車場照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動電源 LED

LED通用照明設(shè)計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

關(guān)鍵字: LED 驅(qū)動電源 開關(guān)電源

LED驅(qū)動電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動電源
關(guān)閉