1. U-Boot啟動過程概述
U-Boot的啟動過程分為兩個階段。
第一階段:主要是SOC內部的初始化,板級的初始化比較少,所以移植的修改量比較小。此階段由匯編語言編寫,代碼主體分布在/uboot/cpu/s5pc11x/start.S和/uboot/board/samsung/x210/lowlevel_init.S中。
第二階段:主要是板級的初始化,SOC內部的初始化比較少,移植的修改量主要在此。此階段由c語言編寫,代碼主體分布在/uboot/lib_arm/board.c中。
紅色字體部分和板級關系較大,是移植的重點修改部分。
2. U-Boot啟動代碼具體分析
2.1 第一階段(/ubootcpu/s5pc11x/start.S)
(0)頭文件包含
/uboot/include/config.h文件是在配置過程中中生成的(具體/uboot/mkconfig文件中實現),其中的內容就是“#include
version.h文件是uboot的版本信息文件,是在編譯過程中生成的。具體的U-Boot的version信息可以在/uboot/Makefile中更改
U-Boot中的頭文件包含其實都不是真正被包含的文件,它們大多是在配置編譯階段產生的符號鏈接或者是具有符號鏈接功能的頭文件(/uboot/mkconfig中創建符號鏈接)。總之,它們的功能都類似于符號鏈接,目的是讓uboot的源碼更具靈活性和移植性。
#include#include #if defined(CONFIG_ENABLE_MMU) #include #endif #include
(1)填充16字節的校驗位
uboot.bin鏡像在開頭需要加16字節的檢驗頭(詳見 irom application note文件)。這一部分主要功能是在代碼段最開始處放置16字節的填充位,即通過偽.word指令定義4個word(32位)的空間來在代碼段最開始處占16字節。
在此處只是占據16字節的空間,校驗頭的正確值在編譯階段通過mkv210image.c中計算并填充的。
#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED) .word 0x2000 .word 0x0 .word 0x0 .word 0x0 #endif
(2)設置異常向量表
.globl是globl是把_start這個標號全局化,是編譯器的操作,并不是匯編指令。_start代表程序start.S的入口。
這段代碼的功能是設置異常向量表。b reset 所處的位置是與異常向量表基地址偏移量為的0的地方,所以當復位異常發生時(開機也屬于復位異常),CPU會自動跳轉入異常表基址偏移量為0處執行復位異常程序,即跳轉執行reset部分的代碼。
這部分代碼只是構建了異常向量表,當每個異常向量指向的內容為空(除reset異常外)。因為U-Boot比較簡單,只是作為引導程序,所以不需要很細致的處理各種異常。
.globl _start _start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq
(3)禁能IRQ和FIQ,使能SVC模式
開機后程序從此時正式開始運行。
程序上電之初,異常向量表未初始化,故先禁能IRQ(普通中斷)和FIQ(快速中斷)。
使能SVC模式,即超級用戶模式。SVC 模式,主要用于 SWI(軟件中斷)和 OS(操作系統)。這個模式有額外的特權,允許你進一步控制計算機。例如,你必須進入超級用戶模式來讀取一個插件(podule)。這不能在用戶模式下完成。
/* * the actual reset code */ reset: /* * set the cpu to SVC32 mode and IRQ & FIQ disable */ @;mrs r0,cpsr @;bic r0,r0,#0x1f @;orr r0,r0,#0xd3 @;msr cpsr,r0 msr cpsr_c, #0xd3 @ I & F disable, Mode: 0x13 - SVC
(4)初始化相關全局變量
TEXT_BASE是U-Boot代碼的鏈接地址,在/uboot/board/samsung/config.mk文件中定義,該文件在/uboot/Makefile->x210_sd_config段中生成。TEXT_BASE = 0xc3e00000
_TEXT_BASE: .word TEXT_BASE
設置U-Boot在DDR中的物理地址,即運行地址,U-Boot重定位將整個U-Boot拷貝至DDR中的_TEXT_PHY_BASE。CFG_PHY_UBOOT_BASE在開發板配置文件(/uboot/include/configs/x210_sd_config)中定義,為0x33e00000
_TEXT_PHY_BASE: .word CFG_PHY_UBOOT_BASE
在開啟MMU之后,虛擬地址段0xc0000000-0xd0000000將被映射到物理地址段0x30000000-0x40000000。所以TEXT_BASE = 0xc3e00000對應的物理地址為TEXT_BASE =0x33e00000,即U-Boot的鏈接地址與運行地址一致。
(5)禁用Cache和MMU
caches是CPU的緩沖區,它的作用是存放常用的數據和指令,提高cpu與內存之間數據與指令的傳輸速率。
MMU是CPU的內存管理單元,它的作用是轉換虛擬地址與物理地址。
關閉caches的原因:上電初始,DDR未初始化,當CPU從cache中取數據時,可能導致數據預取異常。另一方面,當匯編指令讀取緩存數據,而實際物理地址對應的數據發生變化,導致CPU不能獲取最新的數據。在C語言中無需關閉caches,因為C語言中可以使用volatile關鍵字避免上述情況。
關閉MMU的原因:U-Boot的作用是硬件初始化和引導操作系統,純粹的初始化階段,開啟MMU會導致這個過程更復雜。
cpu_init_crit: ................................................. /* * disable MMU stuff and caches */ mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00002000 @ clear bits 13 (--V-) bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM) orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB mcr p15, 0, r0, c1, c0, 0
(6)判斷啟動介質
先從(PRO_ID_BASE+OMR_OFFSET)地址的寄存器讀取啟動介質信息,經過數據處理之后放入r2寄存器中。
然后通過比較r2的值來判定啟動介質,經過判斷得到當前的啟動介質為SD/MMC,在把BOOT_MMCSD宏寫入r3中。
最后將啟動介質信息從寄存器r3中寫入(INF_REG_BASE+INF_REG3_OFFSET)地址的寄存器中
/* Read booting information */ ldr r0, =PRO_ID_BASE ldr r1, [r0,#OMR_OFFSET] bic r2, r1, #0xffffffc1 ............................................ /* SD/MMC BOOT */ cmp r2, #0xc moveq r3, #BOOT_MMCSD ............................................. ldr r0, =INF_REG_BASE str r3, [r0, #INF_REG3_OFFSET]
(7)第一次初始化棧(SRAM中)
第一次設置棧,由于不設置棧的話無法使用嵌套bl跳轉指令,即雙層函數調用,因為只有一個LR寄存器,而后面的lowlevel_init就有雙層跳轉,故這里開始設置SRAM中用的棧。
這里棧設置的地址并沒有按照s5pv210的推薦地址,將0xd0036000地址下12Bytes內存空間設為棧內存,只要保證該段內存不會被其他程序占用即可。
ldr sp, =0xd0036000 /* end of sram dedicated to u-boot */ sub sp, sp, #12 /* set stack */ mov fp, #0
(8)跳轉執行lowlevel_init部分
/uboot/board/x210/lowlevel_init
1)判斷復位的類型
復位分為多種情況,如冷啟動、休眠喚醒等。
判斷復位類型的意義在于:冷啟動需要重新初始化DDR,而休眠喚醒不需要再次初始化DDR。
/* check reset status */ ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET) ldr r1, [r0] bic r1, r1, #0xfff6ffff cmp r1, #0x10000 beq wakeup_reset_pre cmp r1, #0x80000 beq wakeup_reset_from_didle
2)關看門狗
U-Boot主要功能是初始化硬件資源和引導OS,基本不會出現程序跑飛的情況。因此,為避免喂狗的麻煩,等U-Boot一切就緒正常運行后,再打開看門狗。
/* Disable Watchdog */ ldr r0, =ELFIN_WATCHDOG_BASE /* 0xE2700000 */ mov r1, #0 str r1, [r0]
3)開發板供電鎖存
設置開發板的供電按鍵鎖存功能。注意是哪個gpio,移植過程可能需要修改。
/* PS_HOLD pin(GPH0_0) set to high 開發板供電上鎖*/ ldr r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET) ldr r1, [r0] orr r1, r1, #0x300 orr r1, r1, #0x1 str r1, [r0]
4)檢測程序當前的執行位置(SRAM或DDR)
本段的功能是檢測當前代碼的執行位置。判斷是在SRAM中還是DDR中,即CPU是冷啟動還是休眠喚醒復位,從而來決定是否要跳過后面的時鐘、DDR的初始化代碼。
bic是位清除指令,其功能是:將pc中的某些位清零(r0中為1的位清零),剩下一些特殊的bit位賦值給r1。
比較鏈接地址和當前地址的特定位,即比較r1和r2。若比較鏈接地址和當前地址的特定位相等,說明當前代碼處于DDR中(休眠喚醒),則跳轉到標號1處執行后面的代碼(即跳過時鐘、DDR的初始化代碼);否則,繼續執行后續的時鐘、DDR的初始化代碼。
ldr r0, =0xff000fff bic r1, pc, r0 /* r0 <- current base addr of code */ ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */ bic r2, r2, r0 /* r0 <- current base addr of code */ cmp r1, r2 /* compare r0, r1 */ beq 1f /* r0 == r1 then skip sdram init */
5)初始化時鐘、DDR、串口
跳轉執行時鐘初始化函數system_clock_init,然后返回。時鐘的相關參數可以在開發板配置文件(/uboot/include/configs/x210_sd.h)中修改,移植過程基本不需要需改該部分代碼。
跳轉執行DDR初始化函數mem_ctrl_asm_init,然后返回。其中,DMC0,DMC1等設置很重要,直接和板子上內存的大小和分布有關,需要注意。DDR的相關參數可以在開發板配置文件(/uboot/include/configs/x210_sd.h)中修改。
跳轉執行串口初始化函數uart_asm_init,成功執行后打印‘O’,然后返回。‘O’可作為調試的幫助。
若定義了ONFIG_ONENAND,則初始化ONENAND;若定義了CONFIG_NAND,則初始化NAND。
在返回start.S前打印了‘K’,與之前的‘O’組成“OK”,這是uboot的第一條打印信息,可以用來判斷lowlevel_init是否正常運行。
把之前保存在棧中的lr值彈出到pc中,來返回到start.S。
/* init system clock */ bl system_clock_init /* Memory initialize */ bl mem_ctrl_asm_init 1: /* for UART */ bl uart_asm_init //初始化完成打印'O' bl tzpc_init //基本沒用 #if defined(CONFIG_ONENAND) bl onenandcon_init #endif #if defined(CONFIG_NAND) /* simple init for NAND */ bl nand_asm_init #endif .............................................. /* Print 'K' */ ldr r0, =ELFIN_UART_CONSOLE_BASE ldr r1, =0x4b4b4b4b str r1, [r0, #UTXH_OFFSET] pop {pc}
(9)再次供電鎖存
從lowlevel_init中跳轉回start.S中,再次進行開發板供電鎖存設置(在lowlevel_init中已經設置過一次),可能為代碼的冗余。注意引腳號。
ldr r0, =0xE010E81C /* PS_HOLD_CONTROL register */ ldr r1, =0x00005301 /* PS_HOLD output high */ str r1, [r0]
(10)第二次初始化棧(DDR中)
為了即將執行的c程序做準備,這里開始第二次設置棧,設置于DDR中。這里將棧設置在_TEXT_PHY_BASE,即U-Boot在DDR中的真正物理地址(U-Boot運行地址)。由于棧是滿減棧,所以緊挨著uboot放置也不會沖突。
/* get ready to call C functions */ ldr sp, _TEXT_PHY_BASE /* setup temp stack pointer */ sub sp, sp, #12 mov fp, #0 /* no previous frame, so fp=0 */
(11)再次檢測當前程序執行地址(SRAM或DDR)
在lowlevel_init中,檢測當前代碼的執行位置。判斷是在SRAM中還是DDR中,即cpu是冷啟動還是休眠喚醒復位,從而來決定是否要跳過后面的時鐘、DDR的初始化代碼。
在此處再次檢測當前代碼的執行位置,從而來決定是否要跳過重定位代碼。
ldr r0, =0xff000fff bic r1, pc, r0 /* r0 <- current base addr of code */ ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */ bic r2, r2, r0 /* r0 <- current base addr of code */ cmp r1, r2 /* compare r0, r1 */ beq after_copy /* r0 == r1 then skip flash copy */
(12)通過引腳來判斷啟動介質
這一段區別于之前的啟動介質判斷,本段是通過引腳來判斷啟動介質的
將0xD0037488地址的寄存器的值放入r0,該寄存器里的值可以判斷BL1是從SD/MMC哪個通道啟動的,將寄存器的值放入r1。這個寄存器的信息在irom applicationnote里有記載。
將0xEB200000這個值放入r2,該值的表示是2號方式啟動,并將寄存器的值和r2(0xEB200000)進行對比。如果相同則說明BL1是從SD卡通道2啟動,跳轉入標號mmcsd_boot處執行SD/MMC重定位相關代碼(/uboot/cpu/s5pc11x/movi.c->movi_bl2_copy)。
若不是從SD卡通道2啟動,則讀取之前存儲的啟動介質信息(INF_REG_BASE+INF_REG3_OFFSET地址的寄存器),該信息有上一次啟動介質檢測所得(見(6))。隨后,跳轉至啟動介質相應的xxx_boot代碼中執行重定位相關的代碼(/uboot/cpu/s5pc11x/movi.c->movi_bl2_copy)。
#if defined(CONFIG_EVT1) /* If BL1 was copied from SD/MMC CH2 */ ldr r0, =0xD0037488 ldr r1, [r0] ldr r2, =0xEB200000 cmp r1, r2 beq mmcsd_boot #endif ldr r0, =INF_REG_BASE ldr r1, [r0, #INF_REG3_OFFSET] cmp r1, #BOOT_NAND /* 0x0 => boot device is nand */ beq nand_boot cmp r1, #BOOT_ONENAND /* 0x1 => boot device is onenand */ beq onenand_boot cmp r1, #BOOT_MMCSD beq mmcsd_boot cmp r1, #BOOT_NOR beq nor_boot cmp r1, #BOOT_SEC_DEV beq mmcsd_boot nand_boot: mov r0, #0x1000 bl copy_from_nand b after_copy onenand_boot: bl onenand_bl2_copy b after_copy mmcsd_boot: #if DELETE ldr sp, _TEXT_PHY_BASE sub sp, sp, #12 mov fp, #0 #endif bl movi_bl2_copy b after_copy nor_boot: bl read_hword b after_copy
(13)進行U-Boot重定位
具體代碼在/uboot/cpu/s5pc11x/movi.c->movi_bl2_copy中。
定義一個函數指針類型copy_sd_mmc_to_mem。
typedef u32(*copy_sd_mmc_to_mem) (u32 channel, u32 start_block, u16 block_size, u32 *trg, u32 init);
函數返回值 | u32代表函數執行成功與否 |
u32 channel | 代表SD/MMC啟動通道號(0-4) |
u32 start_block | 起始塊地址 |
u16 block_size | 復制的塊的個數 |
u32 *trg | 復制操作的目的地址,一般是DDR內的地址 |
u32 init | init一般給0,不用多管 |
首先讀取位于0xD0037488的寄存器值至變量ch中,再一次檢查啟動介質(SD卡的通道號)。
然后0xD0037F98地址中的指針變量強制轉換為copy_sd_mmc_to_mem類型,并賦值給copy_bl2。該指針變量指向一個函數,這個函數就是CPU的IROM中固化用來復制SD/mmc中的內容至任意地址的函數。
最后,根據ch變量的值判斷SD的通道號,并將整個U-Boot從相應的SD通道號中拷貝至DDR的指定地址(CFG_PHY_UBOOT_BASE =_TEXT_PHY_BASE =0x33e00000)處。
MOVI_BL2_POS是燒錄uboot時的扇區,MOVI_BL2_BLKCNT是uboot占的扇區數,具體的定義和計算都在/uboot/include/movi.h中。CFG_PHY_UBOOT_BASE為U-Boot的在DDR中的物理地址(運行地址),具體的定義和計算都在/uboot/include/configs/x210_sd.h中。
void movi_bl2_copy(void){ ulong ch; ch = *(volatile u32 *)(0xD003A508); copy_sd_mmc_to_mem copy_bl2 = (copy_sd_mmc_to_mem) (*(u32 *) (0xD003E008)); u32 ret; if (ch == 0xEB000000) //SD卡通道0 { ret = copy_bl2(0, MOVI_BL2_POS, MOVI_BL2_BLKCNT,CFG_PHY_UBOOT_BASE, 0); } else if (ch == 0xEB200000) //SD卡通道2 { ................. } }
(14)建立虛擬地址映射表并開啟MMU
從movi_bl2_copy返回之后,將跳轉至start.S->after_copy,建立虛擬地址映射表并開啟MMU。
bl movi_bl2_copy b after_copy
TTB(TranslationTableBase),即轉換表的基地址。轉換表是MMU將虛擬地址映射為物理地址的憑據,建立整個虛擬地址映射的關鍵就是建立轉換表,此表存放在內存中,工作時不需要軟件干涉。
只要將轉換表TTB的基地址(_mmu_table_base)放入cp15的c2寄存器,MMU就能自動使用虛擬地址映射。_mmu_table_base定義在start.S其值為標號mmu_table,mmu_table在lowlevel_init中定義。
最后通過設置cp15的c1寄存器來開啟mmu,以實現虛擬地址映射和內存訪問權限管理等功能。
#if defined(CONFIG_ENABLE_MMU) /*使能域訪問*/ enable_mmu: ldr r5, =0x0000ffff mcr p15, 0, r5, c3, c0, 0 @load domain access register /*將轉換表ttb的基地址放入cp15的c2寄存器,mmu就能自動使用虛擬地址映射*/ ldr r0, _mmu_table_base ldr r1, =CFG_PHY_UBOOT_BASE ldr r2, =0xfff00000 bic r0, r0, r2 orr r1, r0, r1 mcr p15, 0, r1, c2, c0, 0 /*通過設置cp15的c1寄存器來開啟mmu,以實現虛擬地址映射和內存訪問權限管理等功能*/ mmu_on: mrc p15, 0, r0, c1, c0, 0 orr r0, r0, #1 mcr p15, 0, r0, c1, c0, 0 nop nop nop nop #endif
詳細的建表步驟在/uboot/board/x210/lowlevel_init。
映射表中規定了內存映射和管理是以塊為單位的,在ARM中支持3種塊大小,細表1KB、粗表4KB、段1MB。真正的轉換表就是由若干個轉換表單元構成的,每個單元負責1個內存塊,總體的轉換表負責整個內存空間(0-4G,32位CPU)的映射。
帶參宏將FL_SECTION_ENTRY base,ap,d,c,b定義成一個word大小的特定值,這個特定值就是轉換表的填充量。
參數分析:參數base是映射出來的段地址的基地址,從第20位開始。20位的大小正好是1MB,故此映射表采用的是段式映射;ap是訪問控制位,從第10位開始。d、c、b都是一些權限位。
#ifdef CONFIG_ENABLE_MMU #ifdef CONFIG_MCP_SINGLE .macro FL_SECTION_ENTRY base,ap,d,c,b //macro指令是匯編中宏定義 .word (base << 20) | (ap << 10) | (d << 5) | (1<<4) | (c << 3) | ( << 2) | (1<<1) .endm //即結束宏定義 .section .mmudata, "a" .align 14 .globl mmu_table mmu_table: .set __base,0 //設置變量__base值為 /*.rept 0x100相當于for循環,一共循環0x100次,所以這一塊代碼創建了0x100(256)個轉換表單元*/ .rept 0x100 /*利用剛才定義的帶參宏創建轉換表的內容,變量__base和3,0,0,0作為參數*/ FL_SECTION_ENTRY __base,3,0,0,0 .set __base,__base+1 //相當于base=base+1 .endr //結束循環 .........................................//后續繼續建表
由以上代碼分析,得出整張轉換表的設定如下。
輸入虛擬地址 | 輸出的物理地址 | 長度 |
0-10000000 | 0-10000000 | 256MB |
10000000-20000000 | 0 | 256MB |
20000000-60000000 | 20000000-60000000 | 1GB |
60000000-80000000 | 0 | 512MB |
80000000-b0000000 | 80000000-b0000000 | 768MB |
b0000000-c0000000 | b0000000-c0000000 | 256MB |
c0000000-d0000000 | 30000000-40000000 | 256MB |
d-完 | d-完 | 768MB |
由此可知,此表僅僅將c0000000開頭的256MB映射到了DMC0的30000000開頭的256MB物理內存,其他的虛擬地址空間是原樣映射的。所以uboot的鏈接地址(0xc3e00000)對應物理地址(0x33e00000),即U-Boot的鏈接地址和運行地址(CFG_PHY_UBOOT_BASE =0x33e00000)一致。
(15)第三次初始化棧(DDR中)
第三次設置棧,仍然設在DDR中。雖然上一次已經在DDR中設置過,但是是緊挨著uboot存放的,位置不合理。
所以本次將棧設置uboot鏈接地址上方2MB處,這個位置合理、緊湊、安全。
stack_setup: #if defined(CONFIG_MEMORY_UPPER_CODE) ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000
(16)清bss段
_bss_start和_bss_end是鏈接腳本中定義的。
利用循環清零。
clear_bss: ldr r0, _bss_start /* find start of bss segment */ ldr r1, _bss_end /* stop here */ mov r2, #0x00000000 /* clear */ clbss_l: str r2, [r0] /* clear loop... */ add r0, r0, #4 cmp r0, r1 ble clbss_l
(17)遠跳轉至start_armboot(U-Boot第二階段)
從SRAM中遠跳轉到DDR中函數start_armboot處,start_armboot定義在根目錄下/uboot/lib_arm/board.c中。
至此,uboot第一階段結束,進入第二階段,至DDR處執行。
ldr pc, _start_armboot _start_armboot: .word start_armboot
2.2 第二階段(/uboot/lib_arm/board.c->start_armboot)
(1)初始化全局變量
1)定義一個函數指針類型。
typedef int (init_fnc_t) (void);
2)定義并初始化init_sequence數組
定義并初始化一個全局數組init_sequence,其元素是指向init_fnc_t類型函數的指針,這些函數都是用來初始化各個功能的函數。這些函數的具體功能如下:
cpu_init:CPU初始化函數,但cpu初始化工作已在U-Boot的第一階段完成,所以該函數為空。
board_init:開始板級初始化函數,配置網卡用到的GPIO、機器碼、內存傳參地址,并填充至gd結構體。
interrupt_init:定時器的初始化函數,和中斷無關(應用,如bootdelay)。
env_init:環境變量的初始化函數,由于環境變量還沒從啟動介質中取到DDR中,故此處的初始化只是對DDR中的環境變量(U-Boot自帶的環境變量)進行一個簡單的判定,檢測其是否可用。真正的初始化在start_armboot里面。U-Boot支持多種不同的啟動介質(如norflash、nandflash、sd/mmc),而各種介質存取操作env的方法都是不同的,故U-Boot中包含在多個文件中包含了env_init函數。而此U-Boot的啟動介質為SD/MMC,故當前的env_init函數在/uboot/common/Env_movi.c文件中。
init_baudrate:串口波特率初始化函數,設置串口波特率,并填充至gd結構體。具體的串口初始化工作已在U-Boot的第一階段完成(lowlevel_init)。
serial_init:串口初始化函數,但具體的串口初始化工作已在U-Boot的第一階段完成(lowlevel_init),所以該函數為空。
console_init_f:控制臺初始化函數,名字中的_f表示這是第一階段的初始化,由于第二階段的初始化之前需要夾雜一些前提代碼,故將在start_armboot執行。
display_banner:用來通過串口控制臺顯示U-Boot的版本信息(logo)。
print_cpuinfo:打印CPU的信息。
checkboard:確認開發板信息。即,打印出當前開發板的名稱信息。
init_func_i2c:I2C初始化函數,可在開發板配置文件(/uboot/include/configs/x210_sd.h)中設置U-Boot是否使用I2C。
dram_init:DDR初始化函數,由于DDR硬件層面的初始化已在第一階段完成,故此函數只是通過宏來獲取DDR相關信息,并填充至gd結構體。DDR信息相關的宏在開發板配置文件(/uboot/include/configs/x210_sd.h)中修改。
display_dram_config:打印bd中的DDR配置信息(由dram_init獲取)
init_fnc_t *init_sequence[] = { cpu_init, /* basic cpu dependent setup */ #if defined(CONFIG_SKIP_RELOCATE_UBOOT) reloc_init, /* Set the relocation done flag, mustdo this AFTER cpu_init()*/ #endif board_init, /* basic board dependent setup */ interrupt_init, /* set up exceptions */ env_init, /* initialize environment */ init_baudrate, /* initialze baudrate settings */ serial_init, /* serial communications setup */ console_init_f, /* stage 1 init of console */ display_banner, /* say that we are here */ #if defined(CONFIG_DISPLAY_CPUINFO) print_cpuinfo, /* display cpu info (and speed) */ #endif #if defined(CONFIG_DISPLAY_BOARDINFO) checkboard, /* display board info */ #endif #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) init_func_i2c, #endif dram_init, /* configure available RAM banks */ display_dram_config, NULL, };
3)初始化全局變量結構體gd_t、bd_t
本段功能是初始化全局變量結構體。
gd_t是保存全集變量的結構體類型,在/uboot/include/asm-arm/Global_data.h文件中定義。
typedef struct global_data { bd_t *bd;//該指針指向bd_t結構體,其具體內容涉與板級硬件資源信息相關的全局變量 unsigned long flags; unsigned long baudrate; //串口波特率 unsigned long have_console;//標志位,是否使用控制臺console unsigned long reloc_off; //重定位有關偏移量 unsigned long env_addr; //環境變量結構體的偏移量 usigned long env_valid; //標志位,表示內存中的環境變量能否使用 unsigned long fb_base; //幀緩存基地址,和顯示有關 #ifdef CONFIG_VFD unsigned char vfd_type; /* display type */ #endif void **jt; /* jump table *///基本無用 } gd_t;
gd_t結構體中的bd指向一個bd_t結構體,bd_t結構體存放的是和開發板硬件相關的全局變量,在/uboot/include/asm-arm/U-Boot.h文件中定義。
typedef struct bd_info { int bi_baudrate; //串口波特率 unsigned long bi_ip_addr; //IP地址 unsigned char bi_enetaddr[6]; //MAC地址 struct environment_s *bi_env; ulong bi_arch_number; //機器碼 ulong bi_boot_params; //U-Boot給內核傳參的地址 struct //DDR相關信息 { ulong start; ulong size; } bi_dram[CONFIG_NR_DRAM_BANKS]; #ifdef CONFIG_HAS_ETH1 unsigned char bi_enet1addr[6]; #endif } bd_t;
gd(globle data)是指向gd_t結構體的指針,在/uboot/include/asm-arm/Global_data.h文件中定義。
/*register表示盡量讓cpu放在寄存器中,以提高其讀寫速度;asm (“r8”)是指定放在寄存器的r8*/ #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
給gd結構體指針分配內存地址,以后可通過gd指針訪問全局變量結構體gd_t。
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */ ulong gd_base; gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t); ............... gd = (gd_t*)gd_base; #endif
為bd_t結構體分配內存空間,將gd下的一段內存空間分配給bd_t。
清空gd_t、bd_t結構體。
memset ((void*)gd, 0, sizeof (gd_t)); gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); memset (gd->bd, 0, sizeof (bd_t))
(2)運行init_sequence數組中所有的初始化函數
利用for循環遍歷init_sequence數組,并執行初始化函數。若函數返回值為0,則說明初始化函數執行錯誤,掛起程序,進入死循環。
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } }
(3)初始化堆內存
配置堆內存的起止地址、終止地址。
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */ mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE); #else mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); #endif
(4)初始化外部存儲設備
若在開發板配置文件中配置過外部存儲設備,則進行相應的初始化。
#if defined(CONFIG_SMDKC110) #if defined(CONFIG_GENERIC_MMC) puts ("SD/MMC: "); mmc_exist = mmc_initialize(gd->bd); if (mmc_exist != 0) { puts ("0 MB "); } #endif #if defined(CONFIG_MTD_ONENAND) puts("OneNAND: "); onenand_init(); /*setenv("bootcmd", "onenand read c0008000 80000 380000;bootm c0008000");*/ #else //puts("OneNAND: (FSR layer enabled) "); #endif #if defined(CONFIG_CMD_NAND) puts("NAND: "); nand_init(); #endif #endif /* CONFIG_SMDKC110 */
(5)環境變量的重定位
環境變量的重定位,將環境變量從啟動介質中讀到DDR內,環境變量的位置是通過原始分區信息表中讀到的。
/* initialize environment */ env_relocate ();
(6)獲取IP地址和MAC地址
從重定位之后的環境變量中獲取IP地址和MAC地址(以太網地址),放到bd中的全局變量內,以供使用。
/* IP Address */ gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); /* MAC Address */ { int i; ulong reg; char *s, *e; char tmp[64]; i = getenv_r ("ethaddr", tmp, sizeof (tmp)); s = (i > 0) ? tmp : NULL; for (reg = 0; reg < 6; ++reg) { gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0; if (s) s = (*e) ? e + 1 : e; }
(7)其他函數
devices_init (); /* get the devices list going. *///設備驅動初始化,從linux中移植而來 jumptable_init ();//跳轉表初始化,其實沒用 console_init_r (); //控制臺的第二部分的初始化,有實質性的功能 enable_interrupts ();//開啟中斷,實質是一個空函數,U-Boot中并沒有使用中斷
(8)進入main_loop循環
至此,U-Boot啟動第二階段結束。即整個U-Boot啟動完成,進入main_loop循環。若控制臺有任意輸入,則進入控制臺命令解析-執行的循環。若無,則U-Boot將啟動內核。
for (;;) { main_loop (); }
至此,U-Boot啟動完成。
審核編輯:湯梓紅
-
嵌入式
+關注
關注
5068文章
19017瀏覽量
303245 -
soc
+關注
關注
38文章
4120瀏覽量
217933 -
C語言
+關注
關注
180文章
7598瀏覽量
136184 -
u-boot
+關注
關注
0文章
120瀏覽量
38200 -
代碼
+關注
關注
30文章
4747瀏覽量
68349
原文標題:u-boot 啟動內幕
文章出處:【微信號:技術讓夢想更偉大,微信公眾號:技術讓夢想更偉大】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論