日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

uboot源码——内核启动分析

發布時間:2023/12/20 编程问答 57 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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);
(4)對聯合體中的結構體參數賦值
  • params->u.core.flags = 0;
  • params->u.core.pagesize = 0;
  • params->u.core.rootdev = 0;
(5)對下一個params進行初始化
  • 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源码——内核启动分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。