天天看點

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

借鑒資料:http://blog.51cto.com/9291927/1792467

有道雲筆記分享位址:http://note.youdao.com/noteshare?id=b63e0101293984d08ea7a015ab202893&sub=2C6EFA378FC4444DBC8D24BBFF8074EC

一.uboot和核心到底是什麼

1、uboot是一個裸機程式

(1)uboot的本質就是一個複雜點的裸機程式。和我們在ARM裸機全集中學習的每一個裸機程式并沒有本質差別。

2、核心本身也是一個”裸機程式”

(1)作業系統核心本身就是一個裸機程式,和uboot、和其他裸機程式并沒有本質差別。

(2)差別就是作業系統運作起來後在軟體上分為核心層和應用層,分層後兩層的權限不同,記憶體通路和裝置操作的管理上更加精細(核心可以随便通路各種硬體,而應用程式隻能被限制的通路硬體和記憶體位址)。

直覺來看:uboot的鏡像是u-boot.bin,linux系統的鏡像是zImage,這兩個東西其實都是兩個裸機程式鏡像。從系統的啟動角度來講,核心其實就是一個大的複雜點裸機程式。

3、嵌入式系統的分區

**嵌入式系統部署在Flash裝置上時,對于不同SoC和Flash裝置,bootloader、kernel、rootfs的分區是不同的。三星S5PV210規定啟動裝置的分區方案如下:**

SD/MMC裝置的分區方案:
           
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

NandFlash裝置的分區方案:

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

嵌入式系統在啟動時,uboot、kernel、rootfs不能随意存放,必須存放在規劃好的相應分區,在啟動過程中uboot、kernel會到相應分區加載相應内容,確定正常啟動,是以嵌入式系統中,uboot和kernel規劃的分區和啟動裝置中uoot、kernel、rootfs的實際存儲分區是一緻的。

(1)在嵌入式系統的啟動過程中,開機時uboot運作在SoC内部的SRAM中,*uboot會在BL1階段将整個uboot拷貝到SDRAM中0xC3E00000并遠跳轉到SDRAM中的BL2運作。Uboot啟動kernel時,同樣會将kernel從啟動裝置拷貝到SDRAM中的指定kernel連結位置,最終跳轉到kernel運作。*

(2)核心也有類似要求,uboot啟動核心時将記憶體從SD卡讀取放到DDR中(其實就是個重定位的過程),不能随意放置,必須放在核心的連結位址處,否則啟動不起來。譬如我們使用的核心連結位址是0x30008000。

4、核心啟動需要必要的啟動參數

(1)uboot是無條件啟動的,從零開始啟動的。

(2)核心是不能開機自動完全從零開始啟動的,核心啟動要别人幫忙。uboot要幫助核心實作重定位(從SD卡到DDR),uboot還要給核心提供啟動參數。

二.uboot之使用兩種方式擷取鏡像啟動核心

使用兩種方式擷取鏡像

1.SD卡/iNand/Nand/NorFlash等:raw分區方式

正常啟動時各種鏡像都在SD卡中,是以uboot隻需要從SD卡的kernel分區去讀取核心鏡像到DDR中即可。讀取要使用uboot的指令來讀取(譬如X210的iNand版本是movi指令,X210的Nand版本就是Nand指令)

(2)這種啟動方式來加載ddr,使用指令:movi read kernel

30008000。0x30008000為核心連結位址,其中kernel指的是uboot中的kernel分區(就是uboot中規定的SD卡中的一個區域範圍,這個區域範圍被設計來存放kernel鏡像,就是所謂的kernel分區)

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

使用bootm指令隻能用來啟動已經放置在DDR中的核心,列印如下,開發闆看見核心已經啟動成功

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

(2)tftp、nfs等網絡下載下傳方式從遠端伺服器擷取鏡像

uboot還支援遠端啟動,也就是核心鏡像不燒錄到開發闆的SD卡中,而是放在主機的伺服器中,然後需要啟動時uboot通過網絡從伺服器中下載下傳鏡像到開發闆的DDR中。

tftp 0x30008000 zImage-qt 指令, 其中zImage-qt

名字必須與ubuntu中的檔案名一緻,再次重申一遍,0x30008000為核心連結位址

(tftp伺服器環境搭建可參考

二.網絡指令ping開發搭建使用&tftp伺服器的安裝&nfs網絡伺服器的安裝)

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

分析總結:最終結果要的是核心鏡像到DDR中特定位址即可,不管核心鏡像是怎麼到DDR中的。以上2種方式各有優劣。産品出廠時會設定為從SD卡中啟動(客戶不會還要搭建tftp伺服器才能用···);tftp下載下傳遠端啟動這種方式一般用來開發。

三.uboot之編譯核心生成zImage和uImage

1、bootm指令對應do_bootm函數

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

(1)指令名前加do_即可構成這個指令對應的函數,是以當我們bootm指令執行時,uboot實際執行的函數叫do_bootm函數,在cmd_bootm.c。

(2)do_bootm剛開始定義了一些變量,然後用宏來條件編譯執行了secureboot的一些代碼(主要進行簽名認證),先不管他;然後進行了一些一些細節部分操作,也不管他。然後到了CONFIG_ZIMAGE_BOOT,用這個宏來控制進行條件編譯一段代碼,這段代碼是用來支援zImage格式的核心啟動的。

2、vmlinuz和zImage和uImage

(1)uboot經過編譯直接生成的elf格式的可執行程式是u-boot,這個程式類似于windows下的exe格式,在作業系統下是可以直接執行的。但是這種格式不能用來燒錄下載下傳。我們用來燒錄下載下傳的是u-boot.bin,這個東西是由u-boot使用arm-linux-objcopy工具進行加工(主要目的是去掉一些無用的)得到的。這個u-boot.bin就叫鏡像(image),鏡像就是用來燒錄到iNand中執行的。

(2)原則上Image就可以直接被燒錄到Flash上進行啟動執行(類似于u-boot.bin),但是實際上并不是這麼簡單。實際上linux的作者們覺得Image還是太大了是以對Image進行了壓縮,并且在image壓縮後的檔案的前端附加了一部分解壓縮代碼。構成了一個壓縮格式的鏡像就叫zImage。

(3)uboot為了啟動linux核心,還發明了一種核心格式叫uImage。uImage是由zImage加工得到的,uboot中有一個工具,可以将zImage加工生成uImage。

(4)原則上uboot啟動時應該給他uImage格式的核心鏡像,但是實際上uboot中也可以支援zImage,是否支援就看x210_sd.h中是否定義了LINUX_ZIMAGE_MAGIC這個宏。是以大家可以看出:有些uboot是支援zImage啟動的,有些則不支援。但是所有的uboot肯定都支援uImage啟動。

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

1.出現的問題,沒有配置編譯生成核心鏡像,無法實驗編譯核心得到uImage

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

解決方法:配置生成核心鏡像檔案

  • make distclean
  • * make x210ii_qt_defconfig *
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
  • make menuconfig 顯示一下錯誤
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

錯誤為 沒有沒有nurses庫,不支援圖像界面出不來

需要**

ubuntu安裝nurses庫**

參考:https://blog.csdn.net/mingtianwendy/article/details/51328562

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

輸入指令:sudo apt-get install libncurses5-dev

即等待安裝好nurses庫,安裝完成後會顯示如下:這是配置核心的,選擇預設就行(即Exit推出即可)

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
  • make uImage指令
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

2.編譯核心得到zImage再生成uImage鏡像去啟動

  • 如圖,上面的操作已經生成了zImage
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
  • 再由zImage生成uImage

(1)如果直接在kernel底下去make uImage會提供mkimage command not

found。解決方案是去uboot/tools下cp mkimage

/usr/local/bin/,複制mkimage工具到系統目錄下。再去make uImage即可

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
  • 最後在序列槽控制台上分别輸入一下指令,使用TFTP下載下傳uImage到開發闆
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

到這裡,即将uImage鏡像通過TFTP下載下傳到了開發闆,下節即分析上圖中列印的這些資訊。

四.uboot之zImage啟動源碼細節分析

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
**\#define LINUX_ZIMAGE_MAGIC0x016f2818**

**if (argc \< 2) {**

**addr = load_addr;**//如果不指定核心位址,則使用CFG_LOAD_ADDR

**} else {**

**addr = simple_strtoul(argv[1], NULL, 16);**//使用指定核心位址

**}**

**if (\*(ulong \*)(addr + 9\*4) == LINUX_ZIMAGE_MAGIC)
{**//如果目前kernel是zImage

**printf("Boot with zImage\\n");**//列印資訊

**addr = virt_to_phys(addr);**//将虛拟位址轉換為實體位址

**hdr = (image_header_t \*)addr;**//将核心位址指定到結構體種?

**hdr-\>ih_os = IH_OS_LINUX;**//指定核心版本

**hdr-\>ih_ep = ntohl(addr);**//指定核心入口

**memmove (&images.legacy_hdr_os_copy, hdr, sizeof(image_header_t));**

//将核心的資訊指派給目前uboot啟動核心的images變量

**images.legacy_hdr_os = hdr;**

**images.legacy_hdr_valid = 1;**

**goto after_header_check;**

**}**
           

**通過讀取zImage的頭部的第36位元組開始的四個位元組與LINUX_ZIMAGE_MAGIC(0x016f2818)标志位對比,如果相等則目前的kernel是zImage。如果目前kernel是zImage,則列印出”Boot

with

zImage”,并對目前uboot啟動核心鏡像的變量images進行指派,指定核心版本和核心入口位址。**

(1)do_bootm函數中一直到397行的after_header_check這個符号處,都是在進行鏡像的頭部資訊校驗。校驗時就要根據不同種類的image類型進行不同的校驗。是以do_bootm函數的核心就是去分辨傳進來的image到底是什麼類型,然後按照這種類型的頭資訊格式去校驗。校驗通過則進入下一步準備啟動核心;如果校驗失敗則認為鏡像有問題,是以不能啟動。

1、LINUX_ZIMAGE_MAGIC

(1)這個是一個定義的魔數,這個數等于0x016f2818,表示這個鏡像是一個zImage。也就是說zImage格式的鏡像中在頭部的一個固定位置存放了這個數作為格式标記。如果我們拿到了一個image,去他的那個位置去取4位元組判斷它是否等于LINUX_ZIMAGE_MAGIC,則可以知道這個鏡像是不是一個zImage。

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

(2)指令 bootm 0x30008000,是以do_boom的argc=2,argv[0]=bootm

argv[1]=0x30008000。但是實際bootm指令還可以不帶參數執行。如果不帶參數直接bootm,則會從CFG_LOAD_ADDR位址去執行(定義在x210_sd.h中)。

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

(3)zImage頭部開始的第36-40位元組處存放着zImage标志魔數,從這個位置取出然後對比LINUX_ZIMAGE_MAGIC。可以用二進制閱讀軟體來打開zImage檢視,就可以證明。很多軟體都可以打開二進制檔案,如winhex、UltraEditor。

這裡可以看出,**36到40位元組就等于**LINUX_ZIMAGE_MAGIC0x016f2818。

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

2、image_header_t

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

(1)這個資料結構是我們uboot啟動核心使用的一個标準啟動資料結構,zImage頭資訊也是一個image_header_t,但是在實際啟動之前需要進行一些改造。hdr->ih_os

= IH_OS_LINUX;

hdr->ih_ep = ntohl(addr);這兩句就是在進行改造。

(2)images全局變量是do_bootm函數中使用,用來完成啟動過程的。zImage的校驗過程其實就是先确認是不是zImage,确認後再修改zImage的頭資訊到合适,修改後用頭資訊去初始化images這個全局變量,然後就完成了校驗。

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

五.uboot之uImage鏡像啟動細節

1、uImage啟動

(1)uImage的啟動校驗主要在boot_get_kernel函數中,主要任務就是校驗uImage的頭資訊,并且得到真正的kernel的起始位置去啟動。

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

列印出uImage鏡像資訊的函數如下 :image_print_contents函數

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

2.uImage校驗頭資訊結束後,下一階段主要任務是啟動linux核心,調用do_bootm_linux函數來完成。

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

六.uboot之uImage啟動之do_bootm_linux函數流程分析

找到do_bootm_linux函數(do_bootm_linux啟動核心)

函數的主要功能是擷取環境變量中的核心傳遞參數,擷取目前uboot啟動kernel的images變量中的kernel入口位址,擷取uboot中的機器碼,準備向kernel傳遞的參數,最後跳轉到kernel執行,uboot執行完畢。uboot在執行完成前列印了”Starting

kernel …”資訊。如果uboot實際啟動kernel過程中列印出了”Starting kernel

…”資訊,則表明uboot在加載、校驗kernel是正确的。如果uboot最終啟動kenel失敗,則大部分原因是uboot向核心傳遞參數錯誤導緻的。

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

(1)函數在uboot/lib_arm/bootm.c中。

void do_bootm_linux (cmd_tbl_t \*cmdtp, int flag, int argc, char \*argv[],

     bootm_headers_t \*images)

{

ulonginitrd_start, initrd_end;

ulongep = ;

bd_t\*bd = gd-\>bd;

char\*s;

intmachid = bd-\>bi_arch_number;**//uboot的機器碼**

void(\*theKernel)(int zero, int arch, uint params);

intret;

\#ifdef CONFIG_CMDLINE_TAG

char \*commandline = getenv ("bootargs");

**//擷取環境變量中的核心傳遞參數變量**

\#endif

if (images-\>legacy_hdr_valid) {

ep = image_get_ep
(\&images-\>legacy_hdr_os_copy);**//擷取images變量中的核心入口位址**

} else {

puts ("Could not find kernel entry point!\\n");

goto error;

}

theKernel = (void (\*)(int, int, uint))ep;

s = getenv ("machid");**//環境變量中的機器碼**

if (s) {**//如果環境變量中定義了機器碼變量,使用環境變量中的機器碼**

machid = simple_strtoul (s, NULL, );

printf ("Using machid 0x%x from environment\\n", machid);

}

ret = boot_get_ramdisk (argc, argv, images, IH_ARCH_ARM,

&initrd_start, \&initrd_end);

if (ret)

goto error;

show_boot_progress ();

**//uboot準備給kernel傳遞參數**

\#if defined (CONFIG_SETUP_MEMORY_TAGS) \|\| \\

    defined (CONFIG_CMDLINE_TAG) \|\| \\

    defined (CONFIG_INITRD_TAG) \|\| \\

    defined (CONFIG_SERIAL_TAG) \|\| \\

    defined (CONFIG_REVISION_TAG) \|\| \\

    defined (CONFIG_LCD) \|\| \\

    defined (CONFIG_VFD) \|\| \\

    defined (CONFIG_MTDPARTITION)

setup_start_tag (bd);

\#ifdef CONFIG_SERIAL_TAG

setup_serial_tag (ms);

\#endif

\#ifdef CONFIG_REVISION_TAG

setup_revision_tag (ms);

\#endif

\#ifdef CONFIG_SETUP_MEMORY_TAGS

setup_memory_tags (bd);

\#endif

\#ifdef CONFIG_CMDLINE_TAG

setup_commandline_tag (bd, commandline);

\#endif

\#ifdef CONFIG_INITRD_TAG

if (initrd_start && initrd_end)

setup_initrd_tag (bd, initrd_start, initrd_end);

\#endif

\#if defined (CONFIG_VFD) \|\| defined (CONFIG_LCD)

setup_videolfb_tag ((gd_t \*) gd);

\#endif

\#ifdef CONFIG_MTDPARTITION

setup_mtdpartition_tag();

\#endif

setup_end_tag (bd);

\#endif

printf ("\\nStarting kernel ...\\n\\n");**//列印資訊Starting kernel ...**

\#ifdef CONFIG_USB_DEVICE

{

extern void udc_disconnect (void);

udc_disconnect ();

}

\#endif

cleanup_before_linux ();

theKernel (, machid, bd-\>bi_boot_params);**//跳轉到kernel執行**

return;

error:

do_reset (cmdtp, flag, argc, argv);

return;

}
           

2、鏡像的entrypoint

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

(1)ep就是entrypoint的縮寫,就是程式入口。一個鏡像檔案的起始執行部分不是在鏡像的開頭(鏡像開頭有n個位元組的頭資訊),真正的鏡像檔案執行時第一句代碼在鏡像的中部某個位元組處,相當于頭是有一定的偏移量的。這個偏移量記錄在頭資訊中。

(2)一般執行一個鏡像都是:

  • 第一步先讀取頭資訊,然後在頭資訊的特定位址找MAGIC_NUM,由此來确定鏡像種類;
  • 第二步對鏡像進行校驗;
  • 第三步再次讀取頭資訊,由特定位址知道這個鏡像的各種資訊(鏡像長

    度、鏡像種類、入口位址);

  • 第四步就去entrypoint處開始執行鏡像。

(3)theKernel = (void (*)(int, int,

uint))ep;将ep指派給theKernel,則這個函數指向就指向了記憶體中加載的OS鏡像的真正入口位址(就是作業系統的第一句執行的代碼)。

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

3、機器碼的再次确定

(1)uboot在啟動核心時,機器碼要傳給核心。uboot傳給核心的機器碼是怎麼确定的?第一順序備選是環境變量machid,第二順序備選是gd->bd->bi_arch_num(x210_sd.h中寫死配置的)

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

4、傳參并啟動概述

(1)從110行到144行就是uboot在給linux核心準備傳遞的參數處理。下節詳細分析傳參過程。

(2)Starting kernel …

這個是uboot中最後一句列印出來的東西。這句如果能出現,說明uboot整個是成功的,也成功的加載了核心鏡像,也校驗通過了,也找到入口位址了,也試圖去執行了。如果這句後序列槽就沒輸出了,說明核心并沒有被成功執行。原因一般是:傳參(80%)、核心在DDR中的加載位址·······

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

七.uboot之uImage啟動之do_bootm_linux函數傳參詳解

(1)從110行到144行就是uboot在給linux核心準備傳遞的參數處理

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

1、tag方式傳參

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

(1)uboot使用tag方式傳參,tag是一種資料結構,與linux

kernel中的tag是相同的資料結構。tag結構體包含tag_header和tag_xxxx成員,tag_header結構體包含tag的大小和類型編碼,kernel接收到tag參數後根據頭資訊中類型編碼确定tag的類型。

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

(2)tag_header和tag_xxx。tag_header中有這個tag的size和類型編碼,kernel拿到一個tag後先分析tag_header得到tag的類型和大小,然後将tag中剩餘部分當作一個tag_xxx來處理。

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

(3)tag_start與tag_end。kernel接收到的傳參是若幹個tag構成的,這些tag由tag_start起始,到tag_end結束。

2、x210_sd.h中配置傳參宏

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制
九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

(1)CONFIG_SETUP_MEMORY_TAGS,tag_mem,傳參内容是記憶體配置資訊。

(2)CONFIG_CMDLINE_TAG,tag_cmdline,傳參内容是啟動指令行參數,也就是uboot環境變量的bootargs.

(3)CONFIG_INITRD_TAG

(4)CONFIG_MTDPARTITION,傳參内容是iNand/SD卡的分區表。

(5)起始tag是ATAG_CORE、結束tag是ATAG_NONE,其他的ATAG_XXX都是有效資訊tag。

思考:核心如何拿到這些tag?

uboot最終是調用theKernel函數來執行linux核心的,uboot調用這個函數(其實就是linux核心)時傳遞了3個參數。這3個參數就是uboot直接傳遞給linux核心的3個參數,通過寄存器來實作傳參的。(第1個參數就放在r0中,第二個參數放在r1中,第3個參數放在r2中)第1個參數固定為0,第2個參數是機器碼,第3個參數傳遞的就是大片傳參tag的首位址。

九.linux開發之uboot移植(九)——uboot源碼分析3-uboot啟動核心機制

4、移植時注意事項

(1)uboot移植時一般隻需要配置相應的宏即可

(2)kernel啟動不成功,注意傳參是否成功。傳參不成功首先看uboot中bootargs設定是否正确,其次看uboot是否開啟了相應宏以支援傳參。

八.uboot啟動核心的總結

1、啟動4步驟

第一步:将核心搬移到DDR中

第二步:校驗核心格式、CRC等

第三步:準備傳參

第四步:跳轉執行核心

2、涉及到的主要函數是:do_boom和do_bootm_linux

3、uboot能啟動的核心格式:zImage uImage fdt方式

4、跳轉與函數指針的方式運作核心