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