uboot源码——内核启动分析
以下內(nèi)容源于朱有鵬嵌入式課程的學習,如有侵權(quán),請告知刪除。
參考資料:http://www.cnblogs.com/biaohc/p/6403863.html
總結(jié):uboot啟動linux內(nèi)核的整個流程
開機時會出現(xiàn)倒計時,當沒有按鍵按下的時候,uboot會讀取出bootcmd這個環(huán)境變量,并使用rum_command函數(shù)來執(zhí)行這個命令;
- 實質(zhì)是執(zhí)行了movi read kernel 30008000;bootm 30008000。
- movi read kernel,把sd卡中的kernel分區(qū)賦值到30008000內(nèi)存處;
- bootm 30008000,真正的傳參以及跳轉(zhuǎn)到linux內(nèi)核中執(zhí)行(實際執(zhí)行do_bootm()函數(shù))。
- bootm(do_bootm()函數(shù))首先要做的事情是判斷這個內(nèi)核鏡像類型(zImage、uImage、設備樹);
- 通過對鏡像文件的頭文件的驗證,以確定是哪種內(nèi)核鏡像,然后再把必須的信息儲存起來(linux操作系統(tǒng)、ep的值等)。
- 確定好以后調(diào)用do_bootm_linux函數(shù)來對內(nèi)核傳參并且啟動內(nèi)核。
?
一、uboot作用簡介
uboot的主要作用是用來啟動linux內(nèi)核。
- 因為CPU不能直接從塊設備中執(zhí)行代碼,需要把塊設備中的程序復制到內(nèi)存中,而復制之前還需要進行很多初始化工作,如時鐘、串口、dram等;
- 如要想讓CPU啟動linux內(nèi)核,只能通過另外的程序,進行必要的初始化工作,在把linux內(nèi)核中代碼復制到內(nèi)存中,并執(zhí)行這塊內(nèi)存中的代碼,即可啟動linux內(nèi)核;
- 一般情況下,linux鏡像儲存在塊設備中(SD卡、iNand、Nandflash等)。首先執(zhí)行uboot代碼,把塊設備中的內(nèi)核代碼復制到內(nèi)存地址0x30008000地址處,然后再執(zhí)行bootm 0x30008000命令以啟動內(nèi)核代碼。
?
二、基礎知識?
vmlinuz、Image、zImage、uImage的區(qū)別與聯(lián)系
(1)linux內(nèi)核代碼經(jīng)過編譯鏈接,生成一個elf格式的可執(zhí)行文件,即vmlinuz或者vmlinux;
- 此文件不能直接燒錄。
(2)vmlinuz文件經(jīng)過arm-linux-objcopy以后,生成一個Image鏡像文件。
- vmlinuz.elf文件大小為70M以上,而Image鏡像文件為7M左右。
(3)Image文件在進一步經(jīng)過壓縮,并添加解壓縮代碼,形成zImage文件。
- 當zImage文件作為啟動鏡像來啟動時,首先要解壓這個文件,這個解壓過程可以由uboot解壓或者zImage文件本身自解壓。
- zImage中除了linux內(nèi)核的鏡像以外,還有一些頭文件以及這部分解壓代碼,所以內(nèi)核實際上在addr地址中再加一個偏移量的位置。
(5)uImage是uboot自己專用的啟動內(nèi)核鏡像,相對于zImage他們之間頭文件有一定區(qū)別。
- uImage現(xiàn)在基本上要屬于過時的技術了,新一點的技術為設備樹的啟動方式;
?
三、UBOOT啟動內(nèi)核的代碼分析
在啟動UBOOT時,出現(xiàn)倒計時,如果沒有按鍵按下,則會自動啟動內(nèi)核。
1、start_armboot()函數(shù)末尾的main_loop
?
下面這段代碼在此main_loop函數(shù)中,作用是執(zhí)行完倒計時函數(shù)之后,啟動linux內(nèi)核。
- 啟動方式是s = getenv ("bootcmd");;
- 假定不使用HUAH_PARSER,則run_command (s, 0);實際上就是讀取環(huán)境變量bootcmd,然后執(zhí)行這個命令。
?
?
2、bootcmd命令
- bootcmd=movi read kernel 30008000; movi read rootfs 30B00000 300000; bootm 30008000 30B00000
- 這兩個命令完成linux內(nèi)核啟動。
- movi read kernel 30008000,把sd卡中kernel分區(qū)復制到30008000內(nèi)存地址處;
- bootm 30008000,到內(nèi)存地址處執(zhí)行代碼;
- 執(zhí)行bootm命令,實際執(zhí)行do_bootm函數(shù)。
??
3、do_bootm()函數(shù)
?
?
(1)因為bootm 0x30008000,參數(shù)為2,因此有addr = simple_strtoul(argv[1], NULL, 16)
- 此時addr中的值為0x30008000。
(2)接著判斷0x30008000偏移36字節(jié)以后的地址中的值
- 如果為0x016f2818,說明啟動鏡像為zImage,則輸出boot with zImage。
(3)內(nèi)核的操作系統(tǒng)類型,和內(nèi)核真正的入口
- hdr->ih_os = IH_OS_LINUX;zImage header中ih_os賦值為 IH_OS_LINUX;
- hdr->ih_ep = ntohl(addr);ih_ep中存放的是point address,這個值是真正內(nèi)核代碼的地址(起始執(zhí)行入口地址);
(4)image變量
- static bootm_headers_t?images;?
- images為uboot內(nèi)定義的一個bootm_header_t格式的全局變量,用來完成啟動過程的。
- bootm_header_t類型為一個結(jié)構(gòu)體,如下圖:
- 包含一個image_header_t類型的指針,這個指針最后指向了0x30008000處的zImage header。
- 包含一個image_header_t類型的結(jié)構(gòu)體,后面會把0x30008000處的zImage header復制一份到此結(jié)構(gòu)體;
- 包含一個標志位 legacy_hdr_valid。如果上面兩個賦值以后,把legacy_hdr_valid賦值為1;
?
(5)memmove (&images.legacy_hdr_os_copy, hdr, sizeof(image_header_t));
- 把hdr中的值復制一份到 image.legacy_hdr_os_copy中,即把內(nèi)存地址0x30008000處設置好的zImage頭復制一份到uboot的data段。
(6)直接跳轉(zhuǎn)到after_header_check處,os為IH_OS_LINUX。
4、after_header_check
- 判斷操作系統(tǒng),然后調(diào)用do_bootm_linux函數(shù)
?
5、do_bootm_linux()函數(shù)
?
?
(1)獲取環(huán)境變量bootargs;
(2)判斷全局變量images中的legacy_hdr_valid是否為1,如果為1獲取ep值,如果不為1則error。
(3)把ep強制類型換換為函數(shù)指針類型賦值給thekernel;
(4)從環(huán)境變量中讀取machid的值,賦值給s,如果s不空則machid = 環(huán)境變量中machid的值,并打印machid;
----------------------------------uboot給內(nèi)核傳參-----------------------------------------------
6、uboot給內(nèi)核傳參
- 傳參主要是uboot把與硬件有關的信息傳給linux內(nèi)核,如memory信息(多少bank、size、起始地址)、命令行信息、lcd 串口、initrd、MTD等信息。
- 如要定義了任意一個CONFIG_XXXXX,則執(zhí)行setup_start_tag(bd)
一、setup_start_tag函數(shù)
- 初始化第一塊參數(shù)tag
??
(1)params = (struct tag *) bd->bi_boot_params;?
- gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);
- 這句代碼,把uboot中的全局變量(即gd)的bi_boot_params內(nèi)存地址,強制轉(zhuǎn)換為stuct tag* 類型,然后賦值給params;
- 把PHYS_SDRAM_1+0x100這個地址設置為傳參的起始地址;??
(2)struct tag結(jié)構(gòu)體
- 此結(jié)構(gòu)體由一個stuct tag_header類型的結(jié)構(gòu)體,加上一個由一系列結(jié)構(gòu)體組成的union聯(lián)合體組成;
- 這一系列結(jié)構(gòu)體中存放的就是與board有關的參數(shù);
(3)hdr.tag 與hdr.size賦值
- params->hdr.tag = ATAG_CORE;?params->hdr.size = tag_size (tag_core);
- params->u.core.flags = 0;
- params->u.core.pagesize = 0;
- params->u.core.rootdev = 0;
- params = tag_next (params);?
- //#define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size)),把params移動sizeof(tag_core)大小繼續(xù)賦值
二、傳遞內(nèi)存參數(shù),setup_memory_tags函數(shù)
- 把內(nèi)存每個bank的信息放到這里:第一個扇區(qū)的起始地址和大小,第二個扇區(qū)的起始地址和大小……
三、傳遞命令行參數(shù),setup_commandline_tag函數(shù)
- commandline是一個char *類型,指向環(huán)境變量中的bootargs的值,即:
- #define CONFIG_BOOTARGS ?"root=/dev/mtdblock4 rootfstype=yaffs2 init=/init console=ttySAC0,115200"
?
四、setup_end_tag (bd):結(jié)束傳參
-----------------------------------------uboot給內(nèi)核傳參結(jié)束-----------------------------------------------
?7、uboot中最后一句代碼
- 通過執(zhí)行thekernel函數(shù)直接啟動linux內(nèi)核,傳遞三個參數(shù):0、machid、傳參的首地址;
- 這三個參數(shù)是通過r0、r1、r2三個寄存器來傳遞,r0傳遞0、r1傳遞machid、r2傳遞傳參的首地址。
- 這樣就把內(nèi)核啟動起來了。
總結(jié)
以上是生活随笔為你收集整理的uboot源码——内核启动分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Dev-Cpp下载及使用教程
- 下一篇: 万圣节html代码大全,《方舟:生存进化