女人被狂躁到高潮视频免费无遮挡,内射人妻骚骚骚,免费人成小说在线观看网站,九九影院午夜理论片少妇,免费av永久免费网址

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

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

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

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




程序執(zhí)行流程圖

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

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

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




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的源碼的位置哦!

如果查找結果有很多,結合自己的板子信息,確定自己使用的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對應文件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(),其最核心的內容就是調用了init_sequence_f初始化序列,進行了一系列初始化的工作。

主要包括:串口、定時器、設備樹、DM驅動模型等,另外還包括global_data結構體相關對象的變量。

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

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



relocate_code重定向

重定向技術,可以說也算是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文件內,主要實現(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調用函數(shù)返回之后,就跳到了新的uboot代碼空間中。   ldr r0, [r9, #GD_RELOCADDR]     /* r0 = gd->relocaddr */ @@ 把uboot的新的地址空間放到r0寄存器中,作為relocate_code的參數(shù)  b   relocate_code @@ 跳轉到relocate_code中,在這里面實現(xiàn)了 @@                                       (1)relocate舊的uboot代碼空間到新的空間上去 @@                                       (2)修改relocate之后全局變量的label @@ 注意,由于上述已經(jīng)把lr寄存器重定義到uboot新的代碼空間中了,所以返回之后,就已經(jīng)跳到了新的代碼空間了?。。。。?!   bl  relocate_vectors @@ relocate中斷向量表
  • setup_reloc——重定向地址查看(仿真有關)

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

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


4.3Uboot重定向作用

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


board_init_r——板級后置初始化

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

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

		

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驅動初始化,等等。

最終,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:設置環(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))      //是否使用設備樹進行配置  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設置的值

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

  • abortboot(stored_bootdelay):主要用于判斷是否有按鍵按下。如果按下,則不執(zhí)行bootcmd命令,進入cli_loop命令行模式;如果不按下,則執(zhí)行bootcmd命令,跳轉到加載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ū)交流......


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