uboot源码——C阶段的start_armboot函数
以下內(nèi)容源于朱有鵬嵌入式課程的學(xué)習(xí),如有侵權(quán),請告知刪除。
?
一、start_armboot函數(shù)簡介
(1)start.S文件中進(jìn)行一系列的SoC內(nèi)部硬件的初始化,然后長跳轉(zhuǎn)到start_armboot 函數(shù)中;
(2)start_armboot函數(shù)
- 進(jìn)一步初始化SoC外部硬件(比如inand,網(wǎng)卡芯片等);
- 設(shè)置uboot本身內(nèi)容(命令行、環(huán)境變量、基本命令等);
(2)uboot第二階段的完結(jié),可能有兩種情形
- uboot啟動后自動運行,打印出很多信息(uboot在第一和第二階段不斷進(jìn)行初始化時,打印出來的信息),然后uboot進(jìn)入倒數(shù)bootdelay秒。如果用戶沒有干涉則會執(zhí)行bootcmd進(jìn)入自動啟動內(nèi)核流程(此時uboot就死掉了);
- 如果用戶按下回車鍵打斷uboot的自動啟動,則進(jìn)入uboot的命令行,然后uboot就一直工作在命令行下。此時uboot的命令行就是一個死循環(huán),循環(huán)體內(nèi)不斷重復(fù):接收命令、解析命令、執(zhí)行命令。
(3)start_armboot函數(shù)在uboot/lib_arm/board.c文件中。
?
二、Start_armboot解析1
1、init_fnc_t
(1)typedef int (init_fnc_t) (void); 這里定義了一個類型。
(2)init_fnc_ptr是一個二重函數(shù)指針,用來指向一個函數(shù)指針數(shù)組。
2、gd_t和gd結(jié)構(gòu)
(1)變量gd
- 這里定義了一個全局變量,名字叫g(shù)d,是指針類型。
- 用volatile修飾表示可變的,用register修飾表示這個變量要盡量放到寄存器中,后面的asm("r8")是gcc支持的一種語法,意思是把gd放到寄存器r8中。
- 綜合分析,DECLARE_GLOBAL_DATA_PTR定義了一個要放在寄存器r8中的全局變量,名字叫g(shù)d,類型是一個指向gd_t類型變量的指針。
- 這個全局變量gd,是uboot中很重要的一個全局變量,在程序中經(jīng)常被訪問,因此放在register中以提升效率。
(2)gd_t和gd結(jié)構(gòu)體,見博文:http://blog.csdn.net/oqqhutu12345678/article/details/69944499
(3)給gd和gd_base兩指針分配內(nèi)存,并把對應(yīng)的內(nèi)存初始化為0。
3、for循環(huán)執(zhí)行init_sequence
(1)init_sequence是一個函數(shù)指針數(shù)組,數(shù)組中存儲了很多個函數(shù)指針,指針指向的函數(shù)都是init_fnc_t類型(特征是接收參數(shù)是void類型,返回值是int)。
(2)init_sequence在定義時就同時給了初始化,初始化的函數(shù)指針都是一些函數(shù)名。
(3)init_fnc_ptr是一個二重函數(shù)指針,可以指向init_sequence這個函數(shù)指針數(shù)組。
(4)init_fnc_t的這些函數(shù)的返回值定義方式一樣的,都是:函數(shù)執(zhí)行正確時返回0,不正確時返回-1。
- 在遍歷時去檢查函數(shù)返回值,如果遍歷中有一個函數(shù)返回值不等于0則hang()掛起。
- 從分析hang函數(shù)可知:uboot啟動過程中初始化板級硬件時不能出任何錯誤,只要有一個錯誤整個啟動就終止,除了重啟開發(fā)板沒有任何辦法。
(5)init_sequence中的這些函數(shù),都是board級別的各種硬件初始化。
?
三、Start_armboot解析2
1、cpu_init函數(shù)
- 因為cpu相關(guān)的初始化已經(jīng)在start.S文件(四2)中做了,所以這里什么也沒有做。?
2、board_init函數(shù)(初始化了網(wǎng)卡)
?
(1)board_init函數(shù)在uboot/board/samsung/x210/x210.c文件中,是與x210開發(fā)板相關(guān)的初始化。
- 此函數(shù)中初始化了dm9000網(wǎng)卡,并且對gd->bd中的機(jī)器碼、啟動參數(shù)進(jìn)行賦值。
(2)CONFIG_DRIVER_DM9000宏在x210_sd.h中定義
- 這個宏用來配置開發(fā)板的網(wǎng)卡。
- dm9000_pre_init函數(shù)是DM9000網(wǎng)卡的初始化函數(shù)。
- 如果要移植網(wǎng)卡,主要的工作就在這里。這個函數(shù)中主要是網(wǎng)卡的GPIO和端口的配置,而不是驅(qū)動。
(3)MACH_TYPE在x210_sd.h中定義
- 值是2456,并沒有特殊含義,只是當(dāng)前開發(fā)板對應(yīng)的編號。
- 這個編號代表x210這個開發(fā)板的機(jī)器碼,將來在此開發(fā)板上面移植的linux內(nèi)核中的機(jī)器碼也必須是2456,否則就啟動不起來。
(4)#define ? MEMORY_BASE_ADDRESS ? 0x30000000
- 則x210中bi_boot_params的值為0x30000100,這個內(nèi)存地址中的數(shù)值,表示內(nèi)核啟動參數(shù)存放的首地址。
?
四、Start_armboot解析3
1、interrupt_init函數(shù)
(1)這個函數(shù)實際是用來初始化定時器Timer4。
(2)S5PC11X_TIMERS定義了一個結(jié)構(gòu)體類型,把與時鐘有關(guān)的所有寄存器都存放在這個結(jié)構(gòu)體內(nèi)。
(3)S5PC11X_GetBase_TIMERS函數(shù)的作用:把timer寄存器的基地址強(qiáng)制類型轉(zhuǎn)換為S5PC11X_TIMERS *? 類型,然后賦值給 timers變量。
(4)timers->TCFG0=0x0f00,直接賦值相當(dāng)于把0x0f00放到 TCFG0對應(yīng)的寄存器地址處。
- 寄存器必須設(shè)置為連續(xù)或者一一對應(yīng)的,否則會造成賦值的地址錯誤。
(5)剩下的代碼就和裸機(jī)的代碼一致。
- TCON的timer4的相應(yīng)控制位清0,設(shè)置為自動reload,并且第一次要手動載入,然后再清0,設(shè)置reload,開啟timer4。?
2、env_init函數(shù)
(1)此函數(shù)是與環(huán)境變量有關(guān)的初始化。
(2)為什么有很多env_init函數(shù)?
- uboot支持各種不同的啟動介質(zhì)(譬如norflash、nandflash、inand、sd卡),一般從哪里啟動就會把環(huán)境變量env放到哪里。而各種介質(zhì)存取操作env的方法都是不一樣的。因為uboot支持各種不同介質(zhì)中env的操作方法,所以有很多個env_xx開頭的c文件。
- 實際使用哪一個c文件,要根據(jù)自己開發(fā)板使用的存儲介質(zhì)來定(這些env_xx.c同時只有1個會起作用,其他是不能進(jìn)去的,通過x210_sd.h中配置的宏來決定誰被包含的)。
- x210對應(yīng)的函數(shù)是env_movi.c中的函數(shù)。
(3)此函數(shù)把common.c中初始化好的default_environment地址賦值到gd->env_addr中,env_valid 賦值為1。
- 此函數(shù)只是對內(nèi)存里維護(hù)的那一份uboot自帶的env做了基本的初始化或者說是判定(判定里面有沒有能用的環(huán)境變量)。
- 因為當(dāng)前還沒有進(jìn)行環(huán)境變量從SD卡到DDR中的重定位,因此當(dāng)前環(huán)境變量不能用。
(4)start_armboot調(diào)用env_relocate進(jìn)行環(huán)境變量從SD卡中到DDR中的重定位。(在本博文的十1中)
- 重定位之后需要環(huán)境變量時才可以從DDR中去取,重定位之前如果要使用環(huán)境變量只能從SD卡中去讀取。
- uboot自帶的環(huán)境變量以代碼的形式存儲在default_environmen[]數(shù)組中,是寫死的,如果要修改,必須修改代碼;
- 后面隨著uboot被加載到DDR中運行,從而在DDR中有了一份環(huán)境變量;
- 實際上SD卡中的環(huán)境變量才是我們需要的,因此需要將SD卡中的環(huán)境變量重定位到DDR中,取代DDR中uboot自帶的那一份環(huán)境變量。
- 疑問?環(huán)境變量占據(jù)SD卡中的一個獨立分區(qū),即參數(shù)區(qū)?YES
- 見博客http://blog.csdn.net/oqqhutu12345678/article/details/72328037
?
五、Start_armboot解析4
1、init_baudrate
(1)此函數(shù)初始化波特率,即從環(huán)境變量中獲取波特率,賦值給gd->bd->bi_baudrate、gd->baudrate。
- 從之前的環(huán)境變量初始化函數(shù)看出,實際上環(huán)境變量中的波特率就是在x210_sd.h頭文件中配置的波特率,即CONFIG_BAUDRATE。
(2)simple_strtoul函數(shù)作用是把字符串tmp中的波特率轉(zhuǎn)成十進(jìn)制數(shù)字。
(3)getenv_r函數(shù)跟蹤
- 下面的函數(shù)作用:讀取環(huán)境變量name到緩存buf中,讀取成功返回n大于0,失敗返回-1。
- 下面的函數(shù)作用:判斷環(huán)境變量是從內(nèi)存還是從sd卡中賦值的,然后返回index對應(yīng)的環(huán)境變量中的字符。
- 下面函數(shù)的作用:從內(nèi)存中讀取環(huán)境變量字符,作為返回值返回。
- 下面函數(shù)的作用是判斷*s1,是否和i2對應(yīng)的字符串相等,如果相等返回i2。
?
2、serial_init
?
(1)由于start.S中已經(jīng)初始化(六4),這里不再進(jìn)行初始化。
(2)有很多個serial_init函數(shù),x210對應(yīng)的是uboot/cpu/s5pc11x/serial.c中的serial_init函數(shù)。
(2)可以看出這函數(shù)中實際是調(diào)用了serial_setbrg函數(shù),而這個函數(shù)什么也沒有做;
?
六、Start_armboot解析5
1、console_init_f函數(shù)
(1)這是控制臺的第一階段的初始化,_f表示第一階段,_r表示第二階段。
(2)console_init_f在uboot/common/console.c中,僅僅將gd->have_console設(shè)置為1而已。
?
2、display_banner函數(shù)
(1)display_banner用來串口輸出顯示uboot的logo,打開背光。
(2)display_banner中使用printf函數(shù)向串口輸出version_string這個字符串。
根據(jù)上面的分析,console_init_f并沒有初始化好console,為什么可以printf呢?
- 通過追蹤printf的實現(xiàn),發(fā)現(xiàn)printf->puts,而puts函數(shù)中會判斷當(dāng)前uboot中console有沒有被初始化好。如果console初始化好了則調(diào)用fputs完成串口輸出(這條線才是控制臺);如果console尚未初始化好則會調(diào)用serial_puts(然后再調(diào)用serial_putc直接操作串口寄存器進(jìn)行內(nèi)容發(fā)送)。
- 由此可知,uboot中控制臺通過串口輸出,非控制臺也是通過串口輸出。
- 究竟什么是控制臺?和不用控制臺的區(qū)別在于哪里?分析代碼會發(fā)現(xiàn),控制臺就是一個用軟件虛擬出來的設(shè)備,這個設(shè)備有一套專用的通信函數(shù)(發(fā)送、接收……),控制臺的通信函數(shù)最終會映射到硬件的通信函數(shù)中來實現(xiàn)。uboot中,控制臺的通信函數(shù)直接映射到硬件串口的通信函數(shù)中,即uboot中是否使用控制臺其實并沒有本質(zhì)差別。
- 但是在別的體系中,控制臺的通信函數(shù)映射到硬件通信函數(shù)時可以用軟件來做一些中間優(yōu)化,譬如說緩沖機(jī)制。
- 操作系統(tǒng)中的控制臺都使用了緩沖機(jī)制,所以有時候printf了內(nèi)容,但是屏幕上并沒有看到輸出信息,就是因為被緩沖了。此時輸出的信息到了console的buffer中,buffer還沒有被刷新到硬件輸出設(shè)備上。這尤其體現(xiàn)在輸出設(shè)備是LCD屏幕時。
(3)U_BOOT_VERSION在uboot源代碼中找不到定義,這個變量實際上是在makefile中定義的,然后在編譯時生成的include/version_autogenerated.h中用一個宏定義來實現(xiàn)的。
?
3、print_cpuinfo函數(shù)
(1)顧名思義,此函數(shù)實現(xiàn)打印cpu的一些信息的功能。如下:
(2)具體代碼如下,包括get_ARMCLK函數(shù)、get_PLLCLK等幾個函數(shù)。
(3)get_ARMCLK函數(shù)作用:查看時鐘域24MHz經(jīng)過APLL倍頻以后,再經(jīng)過分頻器以后獲得的cpu的頻率。
(4)get_PLLCLK函數(shù)作用:獲取PLL倍頻以后的時鐘頻率:APLL、MPLL、 EPLL。
?
七、Start_armboot解析6
1、checkboard函數(shù)
- 打印“Board:?? x210”字符而已
2、init_func_i2c函數(shù)
(1)由于條件編譯,此函數(shù)實際上沒有執(zhí)行。X210的uboot中并沒有使用I2C。
(2)將來開發(fā)板如果要擴(kuò)展I2C來外接硬件,則在x210_sd.h中配置相應(yīng)的宏即可開啟。有時間可以細(xì)細(xì)看一下。
?
八、Start_armboot解析7????????????????????
1、dram_init函數(shù)
(1)真正的DDR初始化函數(shù)已經(jīng)在匯編階段中執(zhí)行,此處只是把dram的信息賦值到全局變量gd->bd中,即把chip1的首地址和大小以及chip2的首地址和大小放入全局變量中。
(2)可以擴(kuò)展chip3,只要定義了相應(yīng)的宏。
?
2、display_dram_config函數(shù)
(1)此函數(shù)作用是計算chip1、chip2一共多少內(nèi)存并輸出,即啟動信息中的“DRAM:??? 512 MB”。
(2)uboot中有一個命令叫bdinfo,此命令可以打印出gd->bd中記錄的所有與硬件相關(guān)的全局變量的值,因此可以得知DDR的配置信息。
?
九、Start_armboot解析8
1、mem_malloc_init函數(shù)??????????????
(1)mem_malloc_init函數(shù)用來初始化uboot的堆管理器。
- malloc的初始化只設(shè)置了堆的start地址和end地址、以及一個malloc_brk。
(2)uboot中維護(hù)了一段堆內(nèi)存,需要有一套代碼來管理這個堆內(nèi)存。
- 在uboot中也可以malloc、free這套機(jī)制來申請內(nèi)存和釋放內(nèi)存。我們在DDR內(nèi)存中給uboot堆預(yù)留了896KB的內(nèi)存。
2、mmc_initialize函數(shù)
(1)針對不同開發(fā)板進(jìn)行對應(yīng)的初始化。
- 三星用一套u(yù)boot同時滿足了好多個系列型號的開發(fā)板,用#if條件編譯配合CONFIG_xxx宏來選定特定的開發(fā)板,然后進(jìn)行獨有的一些初始化。
(2)mmc_initialize用來初始化SoC內(nèi)部的SD/MMC控制器,函數(shù)位于uboot/drivers/mmc/mmc.c。
(3)uboot中對硬件的操作(譬如網(wǎng)卡、SD卡……)都是借用的linux內(nèi)核中的驅(qū)動來實現(xiàn)的
- uboot根目錄底下有個drivers文件夾,這里面放的全都是從linux內(nèi)核中移植過來的各種驅(qū)動源文件。
(4)mmc_initialize是與具體硬件架構(gòu)無關(guān)的一個MMC初始化函數(shù),所有的使用了這套架構(gòu)的代碼都可以調(diào)用此函數(shù)來完成MMC的初始化。
- mmc_initialize中再調(diào)用board_mmc_init和cpu_mmc_init來完成具體的硬件的MMC控制器初始化工作。
- cpu_mmc_init在uboot/cpu/s5pc11x/cpu.c中,其中又間接的調(diào)用了drivers/mmc/s3c_mmcxxx.c中的驅(qū)動代碼來初始化硬件MMC控制器。
?
十、Start_armboot解析9
1、env_relocate函數(shù)
(1)環(huán)境變量的重定位,完成從SD卡中將環(huán)境變量讀取到DDR中的任務(wù)。
(2)環(huán)境變量到底從哪里來?
- SD卡中有一些(8個)獨立的扇區(qū)作為環(huán)境變量存儲區(qū)域的。但是我們燒錄/部署系統(tǒng)時,我們只是燒錄了uboot分區(qū)、kernel分區(qū)和rootfs分區(qū),根本不曾燒錄env分區(qū),所以當(dāng)我們燒錄完系統(tǒng)第一次啟動時ENV分區(qū)是空的。
- 本次啟動uboot時,嘗試去SD卡的ENV分區(qū)讀取環(huán)境變量時失敗(讀取回來后進(jìn)行CRC校驗時失敗)。此時uboot選擇uboot內(nèi)部代碼中設(shè)置的一套默認(rèn)的環(huán)境變量(這就是默認(rèn)環(huán)境變量);這套默認(rèn)的環(huán)境變量在本次運行時會被讀取到DDR中的環(huán)境變量中。
- 然后被寫入(也可能是你saveenv時寫入,也可能是uboot設(shè)計了第一次讀取默認(rèn)環(huán)境變量后就寫入)SD卡的ENV分區(qū)。下次再次開機(jī)時uboot就會從SD卡的ENV分區(qū)讀取環(huán)境變量到DDR中,這次讀取就不會失敗了。
(3)將環(huán)境變量從SD卡重定位到DDR中的代碼,在env_relocate_spec內(nèi)部的movi_read_env函數(shù)。
?
2、IP地址、MAC地址的確定和devices_init函數(shù)
(1)開發(fā)板的IP地址是在gd->bd中維護(hù)的,來源于環(huán)境變量ipaddr。
(2)getenv函數(shù)用來獲取字符串格式的IP地址,然后用string_to_ip將字符串格式的IP地址轉(zhuǎn)成字符串格式的點分十進(jìn)制格式。
(2)devices_ini是設(shè)備的初始化函數(shù)。
- 放在這里初始化的設(shè)備都是驅(qū)動設(shè)備,這個函數(shù)本來就是從驅(qū)動框架中衍生出來的。
- uboot中很多設(shè)備的驅(qū)動是直接移植linux內(nèi)核的(譬如網(wǎng)卡、SD卡),linux內(nèi)核中的驅(qū)動都有相應(yīng)的設(shè)備初始化函數(shù)。
- linux內(nèi)核在啟動過程中就有一個devices_init,作用就是集中執(zhí)行各種硬件驅(qū)動的init函數(shù)。
- uboot的這個函數(shù)其實就是從linux內(nèi)核中移植過來的,它的作用也是去執(zhí)行所有的從linux內(nèi)核中繼承來的那些硬件驅(qū)動的初始化函數(shù)。
?
十一、Start_armboot解析10
1、jumptable_init函數(shù)
(1)jumptable跳轉(zhuǎn)表,本身是一個函數(shù)指針數(shù)組,里面記錄了很多函數(shù)的函數(shù)名。
- 實現(xiàn)一個函數(shù)指針到具體函數(shù)的映射關(guān)系,將來通過跳轉(zhuǎn)表中的函數(shù)指針就可以執(zhí)行具體的函數(shù)。
- 這個其實就是在用C語言實現(xiàn)面向?qū)ο缶幊?#xff0c;在linux內(nèi)核中有很多這種技巧。
(2)通過分析發(fā)現(xiàn)跳轉(zhuǎn)表只是被賦值從未被引用,因此跳轉(zhuǎn)表在uboot中根本就沒使用。
?
2、console_init_r函數(shù)
(1)console_init_r是console的純軟件架構(gòu)方面的初始化(給console相關(guān)的數(shù)據(jù)結(jié)構(gòu)中填充相應(yīng)的值),所以屬于純軟件配置類型的初始化。
(2)uboot的console實際上并沒有做有意義的事情,它直接調(diào)用的串口通信的函數(shù)。
- 因此用不用console實際并沒有什么分別(但在linux內(nèi)console可以提供緩沖機(jī)制等作用,有不用console不能實現(xiàn)的東西)。
?
3、enable_interrupts函數(shù)
(1)CPSR中,總中斷標(biāo)志位的使能。
(2)因為uboot中沒有使用中斷,因此沒有定義CONFIG_USE_IRQ宏,因此此函數(shù)無用。
?
4、loadaddr、bootfile兩個環(huán)境變量
這兩個環(huán)境變量都是內(nèi)核啟動有關(guān)的,在啟動linux內(nèi)核時會參考這兩個環(huán)境變量的值。
?
5、board_late_init函數(shù)
(1)顧名思義,前面該初始化的都已經(jīng)初始化,剩下的一些初始化都在此函數(shù)中,也側(cè)面說明開發(fā)板級別的硬件軟件初始化告一段落。
(2)對于x210來說,這個函數(shù)是空的。
?
?
十二、Start_armboot解析11
1、eth_initialize函數(shù)
(1)此函數(shù)是網(wǎng)卡相關(guān)的初始化,是網(wǎng)卡芯片本身的一些初始化,而非SoC與網(wǎng)卡芯片連接時SoC這邊的初始化。
(2)對于X210(DM9000)來說,此函數(shù)為空。X210的網(wǎng)卡初始化在board_init函數(shù)中,網(wǎng)卡芯片的初始化在驅(qū)動中。
?
2、x210_preboot_init(LCD和logo顯示)
x210開發(fā)板在啟動起來之前的一些初始化,以及LCD屏幕上的logo顯示。
?
3、check_menukey_to_update_from_sd
(1)uboot啟動的最后階段設(shè)計了一個自動更新的功能。
- 我們可以將要升級的鏡像放到SD卡的固定目錄中,然后開機(jī)時在uboot啟動的最后階段檢查升級標(biāo)志(是一個按鍵,按鍵中標(biāo)志為"LEFT"的那個按鍵,此按鍵如果按下則表示update mode,如果啟動時未按下則表示boot mode)。
- 如果進(jìn)入update mode則uboot會自動從SD卡中讀取鏡像文件然后燒錄到iNand中;如果進(jìn)入boot mode則uboot不執(zhí)行update,直接啟動正常運行。
(2)這種機(jī)制能夠幫助我們快速燒錄系統(tǒng),常用于量產(chǎn)時用SD卡進(jìn)行系統(tǒng)燒錄部署。
?
4、死循環(huán)(main_loop函數(shù))
(1)解析器
(2)開機(jī)倒數(shù)自動執(zhí)行
(3)命令補(bǔ)全
總結(jié)
以上是生活随笔為你收集整理的uboot源码——C阶段的start_armboot函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 宝塔Linux面板 5.9专业版破解,付
- 下一篇: php redis 队列,Redis 实