1. 程式人生 > >uboot啟動核心過程

uboot啟動核心過程

我們都知道u-boot被締造出來的使命是 啟動核心

那麼,他是如何完成他的使命的呢!

(1)我們先來分析下Linux核心映象這個概念吧。

我們編譯核心完(編譯成功)會生成vmlinux,Image,zImage,再通過

uboot提供的工具mkimage,執行make uImage會生成uImage,那麼他

們誰是核心映象。如下圖為這3個東東:

vmlinux在kernel根目錄下:

Image和zImage和uImage在根目錄下的arch/arm/boot目錄下:

哪個是映象檔案?三個都是。

vmlinux:這個就是原始的未經任何處理加工的原版核心elf檔案,他一般載入在PC機這種的硬碟中,因為未加工(比較大),所以PC機有大硬碟不怕放不下,所以PC機的上電後直接載入vmlinux核心映象就可以運行了。比如(Ubuntu)。

Image:vmlinux經過objcopy去掉一些不需要的東西之後得到的。

zImage:Image經過壓縮得到的。原則上,Image就可以直接運行了,但是,嵌入式系統要求精簡所以把Image壓縮在加上壓縮演算法放到真正的壓縮映象前面就得到了zImage。如圖可知zImage變小了。

uImage:uImage是經過加64位元組的頭部資訊放到zImage的前面合成得到的。uImage是用於uboot引導啟動時提供的核心映象,所以uboot在編譯成功後,在根目錄下tools/下回提供一個mkimage的工具,將該工具拷貝到系統目錄下,在核心根目錄執行make uImage就可以提供zImage生成uImage。

:uboot本來只支援uImage的啟動,而編譯核心只負責生成zImage,所以uboot

自己提供了mkimage工具。但是後來uboot也可以支援直接啟動zImage,可通過

uboot的配置檔案中設定對應的巨集開關(CONFIG_ZIMAGE_BOOT)實現。

(2)uboot要啟動核心,說明核心要執行,肯定要進DDR。

那麼uboot提供了那些方法來載入核心映象到DDR?uboot提供了大概2種方法:

【1】通過uboot提供的讀取FLASH的命令從存取核心的介質載入。比如,SD卡,nandflash,iNand等等。

【2】通過網路從伺服器下載核心映象到DDR。比如uboot提供的nfs,tftp伺服器。

(3)校驗核心格式、CRC等

在uboot根目錄下的common/cmd_bootm.c檔案中的函式

int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

該函式就是實現uboot的命令bootm的。

我們在uboot命令列載入核心就是執行:bootm {DDR address}。

do_bootm函式剛開始定義了一些變數,然後用巨集來條件編譯執行了secureboot的一些程式碼(主要進行簽名認證),然後進行了一些一些細節部分操作。我們上面說過,我們可以通過配置巨集來命令uboot載入哪種核心映象。所以uboot是怎麼判斷的呢!

先來zImage啟動:

uboot回去讀取zImage的剛開始部分的資訊和zImage的模數對比是否一致。

  1. #ifdef CONFIG_ZIMAGE_BOOT  
  2. <span style="color:#000099;">#define LINUX_ZIMAGE_MAGIC   0x016f2818 <span style="background-color: rgb(255, 255, 255);"> //魔數</span></span>  
  3.     /* find out kernel image address */  
  4.     if (argc < 2) {  
  5.         addr = load_addr;  
  6.         debug ("*  kernel: default image load address = 0x%08lx\n",  
  7.                 load_addr);  
  8.     } else {  
  9.         addr = simple_strtoul(argv[1], NULL, 16);  
  10.         debug ("*  kernel: cmdline image address = 0x%08lx\n", img_addr);  
  11.     }  
  12.     <span style="color:#000066;">if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {  //獲取映象中的魔數 和上面魔數對比</span>  
  13.         printf("Boot with zImage\n");  
  14.         addr = virt_to_phys(addr);<span style="white-space:pre">      </span>//將DDR地址轉換為實體地址 (使用了MMU)  
  15.         hdr = (image_header_t *)addr;  
  16.         hdr->ih_os = IH_OS_LINUX;  
  17.         hdr->ih_ep = ntohl(addr);<span style="white-space:pre">        </span>//修改填充zImage的頭部資訊結構體  
  18.         memmove (&images.legacy_hdr_os_copy, hdr, sizeof(image_header_t));  
  19.         /* save pointer to image header */  
  20.         images.legacy_hdr_os = hdr;  
  21.         images.legacy_hdr_valid = 1;  
  22.         goto after_header_check;<span style="white-space:pre">        </span>//跳到該標號處  
  23.     }  
  24. #endif  

如果那個zImage的巨集沒有定義將會執行uImage或者裝置樹的方式。

取決於genimg_get_format (os_hdr)的結果:

  1. switch (genimg_get_format (os_hdr)) {  
  2.     case IMAGE_FORMAT_LEGACY: <span style="white-space:pre">  </span>···  
  3.     case IMAGE_FORMAT_FIT:<span style="white-space:pre">      </span>···}  

#define IMAGE_FORMAT_LEGACY 0x01/* legacy image_header based format */ #define IMAGE_FORMAT_FIT 0x02/* new, libfdt based format */

前者是uImage,後者是裝置樹。

(4)選擇對應的OS,設定引數(machid,tag)。

  1. switch (os) {  
  2.     default:            /* handled by (original) Linux case */  
  3.     case IH_OS_LINUX:       //我們是Linux  
  4.         do_bootm_linux (cmdtp, flag, argc, argv, &images);  
  5.         break;  
  6.     case IH_OS_NETBSD:  
  7.         do_bootm_netbsd (cmdtp, flag, argc, argv, &images);  
  8.         break;  
  9.     case IH_OS_RTEMS:     
  10.         ···  

因為我們是Linux,所以接下里會執行函式:

do_bootm_linux (cmdtp, flag, argc, argv, &images);

裡面主要是設定給核心的傳參。主要是machid和tag

int machid = bd->bi_arch_number;

或者

s = getenv ("machid");

machid = simple_strtoul (s, NULL, 16);

就是說如果環境變數裡面有則採用環境變數的,否則是配置裡的。

uboot最終是呼叫theKernel函式來執行linux核心的,uboot呼叫這個函式

(其實就是linux核心)時傳遞了3個引數。這3個引數就是uboot直接傳遞

給linux核心的3個引數,通過暫存器來實現傳參的。(第1個引數就放在r0

中,第二個引數放在r1中,第3個引數放在r2中)第1個引數固定為0,第2個

引數是機器碼,第3個引數傳遞的就是大片傳參tag的首地址。

(4)函式指標並傳參(跳轉到執行核心)

theKernel (0, machid, bd->bi_boot_params); /* does not return */

到此,uboot的使命就算完成了,不過核心是否啟動成功,此次啟動過程後

再也沒有uboot的身影了。