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