小編給大家分享一下如何實(shí)現(xiàn)Cortex-A9 uboot啟動(dòng)代碼,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!
專注于為中小企業(yè)提供成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站、成都外貿(mào)網(wǎng)站建設(shè)公司服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)婁星免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了數(shù)千家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
一、uboot
1. 概念
U-Boot 是一個(gè)主要用于嵌入式系統(tǒng)的引導(dǎo)加載程序,可以支持多種不同的計(jì)算機(jī)系統(tǒng)結(jié)構(gòu),包括PPC、ARM、AVR32、MIPS、x86、68k、Nios與MicroBlaze。這也是一套在GNU通用公共許可證之下發(fā)布的自由軟件。
U-Boot不僅僅支持嵌入式Linux系統(tǒng)的引導(dǎo),它還支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS, android嵌入式操作系統(tǒng)。其目前要支持的目標(biāo)操作系統(tǒng)是OpenBSD, NetBSD, FreeBSD,4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR, VxWorks, LynxOS, pSOS, QNX, RTEMS, ARTOS, android。
2. uboot基本功能
U-Boot可支持的主要功能列表:
系統(tǒng)引導(dǎo)支持NFS掛載、RAMDISK(壓縮或非壓縮)形式的根文件系統(tǒng);支持NFS掛載、從FLASH中引導(dǎo)壓縮或非壓縮系統(tǒng)內(nèi)核;
基本輔助功能強(qiáng)大的操作系統(tǒng)接口功能;可靈活設(shè)置、傳遞多個(gè)關(guān)鍵參數(shù)給操作系統(tǒng),適合系統(tǒng)在不同開發(fā)階段的調(diào)試要求與產(chǎn)品發(fā)布,尤以Linux支持最為強(qiáng)勁;支持目標(biāo)板環(huán)境參數(shù)多種存儲(chǔ)方式,如FLASH、NVRAM、EEPROM;
CRC32校驗(yàn)可校驗(yàn)FLASH中內(nèi)核、RAMDISK鏡像文件是否完好;
設(shè)備驅(qū)動(dòng)串口、SDRAM、FLASH、以太網(wǎng)、LCD、NVRAM、EEPROM、鍵盤、USB、PCMCIA、PCI、RTC等驅(qū)動(dòng)支持;
上電自檢功能SDRAM、FLASH大小自動(dòng)檢測;SDRAM故障檢測;CPU型號(hào)。
3. 常用命令
uboot命令比較多,下面只列舉網(wǎng)絡(luò)啟動(dòng)要用到的命令:
4. 配置參數(shù)舉例
以下以網(wǎng)絡(luò)下載內(nèi)核、網(wǎng)絡(luò)掛載nfs為例。
1)ubuntu環(huán)境
ubuntu ip:192.168.6.186
nfs配置:
配置文件如下:
/etc/exports
配置信息如下:
nfs
2)開發(fā)板設(shè)置
開發(fā)板ip:192.168.6.187
配置命令:
setenv ipaddr 192.168.6.187 ;板子的ip setenv serverip 192.168.6.186 ;虛擬機(jī)的ip setenv gatewayip 192.168.1.1 ;網(wǎng)關(guān) saveenv ;保存配置
加載內(nèi)核和設(shè)備樹
setenv bootcmd tftp 41000000 uImage\;tftp 42000000 exynos4412-fs4412.dtb\;bootm 41000000 - 42000000
bootcmd:uboot2啟動(dòng)之后,首先先執(zhí)行找到這個(gè)參數(shù),執(zhí)行后面的命令。
從tftp服務(wù)器下載內(nèi)核鏡像uImage到地址41000000,設(shè)備樹文件exynos4412-fs4412.dtb到42000000,并通過命令bootm加載啟動(dòng)內(nèi)核。
掛載nfs
setenv bootargs root=/dev/nfs nfsroot=192.168.6.186:/rootfs rw console=ttySAC2,115200 init=/linuxrc ip=192.168.6.187
掛載nfs文件系統(tǒng)
root=/dev/nfs
nfsroot=192.168.6.186:/rootfs nfs服務(wù)器地址192.168.6.186,目錄為/rootfs,
rw 文件系統(tǒng)操作權(quán)限為可續(xù)寫
console=ttySAC2,115200 串口名稱和波特率
init=/linuxrc 內(nèi)核啟動(dòng)后運(yùn)行的進(jìn)程為linuxrc
ip=192.168.6.187 開發(fā)板地址
二、exynos-4412 Soc 啟動(dòng)順序
要想了解exynos-4412的啟動(dòng)順序,我們首先需要了解該soc的內(nèi)存布局。
1. exynos-4412內(nèi)存布局
通常一款soc的內(nèi)存在廠家設(shè)計(jì)的時(shí)候就已經(jīng)規(guī)定死了,對(duì)于使用者來說,我們無法改變。
我們只關(guān)心和啟動(dòng)相關(guān)的一個(gè)地址,
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
iROM 在soc內(nèi)部,出廠時(shí)廠家固化了特定的程序,iROM中程序?qū)?yīng)用戶來說不可改變
iRAM 在soc內(nèi)部,速度較快,但空間不大
DMC RAM控制器,位于SOC內(nèi)部,用于驅(qū)動(dòng)RAM,大容量的RAM都需要連接到該控制器
2. Booting Sequence
不同的廠家的啟動(dòng)順序是不太一樣的,本篇主要以三星的exynos-4412 soc為基礎(chǔ),講解該基于該板子的uboot啟動(dòng)順序。
根據(jù)上圖,系統(tǒng)啟動(dòng)的大概順序:
iROM在SOC內(nèi)部,是一個(gè)64KB的ROM,他樹池化一些系統(tǒng)啟動(dòng)必須的功能。比如:時(shí)鐘、棧。
iROM負(fù)責(zé)從特殊的啟動(dòng)外設(shè)加載BL1的image到soc內(nèi)部的256KB的SRAM中。啟動(dòng)的外設(shè)由操作按鈕來決定的。根據(jù)不同按鍵的值,iROM將會(huì)對(duì)bl1 的image做不同的校驗(yàn)。
BL1初始化系統(tǒng)時(shí)鐘和DRAM控制器,然后從啟動(dòng)外設(shè)加載OS image到DRAM中。根據(jù)啟動(dòng)按鈕的值的不同,BL1會(huì)對(duì)OS做不同的校驗(yàn)。
啟動(dòng)完成之后,BL1跳轉(zhuǎn)到操作系統(tǒng)(kernel)。
iROM會(huì)根據(jù)OM 引腳的不同選擇不同的啟動(dòng)設(shè)備,對(duì)應(yīng)的OM寄存器需要提供對(duì)應(yīng)的啟動(dòng)信息。
三、內(nèi)核啟動(dòng)流程概述
1. 內(nèi)核啟動(dòng)流程 概述
uboot啟動(dòng)流程
如上圖所示:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
設(shè)備上電之后,先執(zhí)行iROM中的出廠代碼,先進(jìn)行必要硬件的初始化 去執(zhí)行uboot,
通常把kernel、設(shè)備樹文件放到flash中
程序啟動(dòng)之后,往往先從flash啟動(dòng),運(yùn)行uboot
第一步:先進(jìn)行硬件的初始化(svc模式棧、clock、內(nèi)存、串口) 第二步:自搬移:把uboot從flash中拷貝到RAM中,跳轉(zhuǎn)到RAM中執(zhí)行剩下的uboot代碼
第三步:把內(nèi)核拷貝到RAM中,執(zhí)行內(nèi)核,把控制權(quán)交給內(nèi)核。
2. 內(nèi)核啟動(dòng)詳細(xì)流程
開發(fā)板從上電到啟動(dòng)內(nèi)核的過程
四、uboot啟動(dòng)流程代碼詳解
1. lds文件
要想了解uboot整個(gè)項(xiàng)目的代碼流程,必須首先了解鏈接腳本【鏈接腳本參考《7. 從0開始學(xué)ARM-GNU偽指令,lds使用》】。
該文件決定了uboot最終生成的鏡像文件,各個(gè)段的布局。
uboot鏈接腳本如下:
u-boot-2013.01/arch/arm/cpu/u-boot.lds
文件內(nèi)容:
26 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") 27 OUTPUT_ARCH(arm) 28 ENTRY(_start) 29 SECTIONS 30 { 31 . = 0x00000000; 32 33 . = ALIGN(4); 34 .text : 35 { 36 __image_copy_start = .; 37 CPUDIR/start.o (.text*) 38 *(.text*) 39 } 40 41 . = ALIGN(4); 42 .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } 43 44 . = ALIGN(4); 45 .data : { 46 *(.data*) 47 } 48 49 . = ALIGN(4); 50 51 . = .; 52 53 . = ALIGN(4); 54 .u_boot_list : { 55 #include56 } 57 58 . = ALIGN(4); 59 60 __image_copy_end = .; 61 62 .rel.dyn : { 63 __rel_dyn_start = .; 64 *(.rel*) 65 __rel_dyn_end = .; 66 } 67 68 .dynsym : { 69 __dynsym_start = .; 70 *(.dynsym) 71 } 72 73 _end = .; 74 75 /* 76 * Deprecated: this MMU section is used by pxa at present but 77 * should not be used by new boards/CPUs. 78 */ 79 . = ALIGN(4096); 80 .mmutable : { 81 *(.mmutable) 82 } 83 84 .bss __rel_dyn_start (OVERLAY) : { 85 __bss_start = .; 86 *(.bss*) 87 . = ALIGN(4); 88 __bss_end__ = .; 89 } 90 91 /DISCARD/ : { *(.dynstr*) } 92 /DISCARD/ : { *(.dynamic*) } 93 /DISCARD/ : { *(.plt*) } 94 /DISCARD/ : { *(.interp*) } 95 /DISCARD/ : { *(.gnu*) } 96 } 97
核心內(nèi)容解釋:
27 OUTPUT_ARCH(arm) : 該鏡像運(yùn)行在arm架構(gòu)的硬件上 28 ENTRY(_start) : 程序的入口是 _start 29 SECTIONS 30 { 31 . = 0x00000000; : 程序的鏈接地址,不是運(yùn)行地址【uboot一定是位置無關(guān)碼】 34 .text : 35 { 36 __image_copy_start = .; : 宏對(duì)應(yīng)整個(gè)程序編譯好后首地址,自搬移代碼的初始位置 37 CPUDIR/start.o (.text*) : 第一個(gè)目標(biāo)文件CPUDIR/start.o中的代碼段 38 *(.text*) : 剩下的目標(biāo)文件的代碼段 39 } 60 __image_copy_end = .; : 自搬移代碼的結(jié)束為止
BSS全局未初始化變量、全局初始化為0的變量所在的段:
84 .bss __rel_dyn_start (OVERLAY) : { 85 __bss_start = .; 88 __bss_end__ = .; 89 }
2. uboot啟動(dòng)代碼流程概要
代碼只分析到uboot命令行,函數(shù)main_loop()位置。
3. 啟動(dòng)代碼詳細(xì)分析
_start入口位于以下文件:
u-boot-2013.01/arch/arm/cpu/armv7/start.S
第一階段:
第二階段
第二階段代碼從_main開始:
以上代碼詳細(xì)解釋,請結(jié)合B站視頻同步學(xué)習(xí)。
五、uboot啟動(dòng)的幾個(gè)關(guān)鍵知識(shí)點(diǎn)
1.如何判斷第一條機(jī)器指令的位置?
鏈接腳本決定了內(nèi)存的布局。
uboot鏈接腳本如下:
u-boot-2013.01/arch/arm/cpu/u-boot.lds
文件內(nèi)容:
28 ENTRY(_start) 29 SECTIONS 30 { 31 . = 0x00000000; 32
uboot的入口是_start
鏈接地址是0x00000000
2.uboot如何搬運(yùn)代碼?
代碼位于:
u-boot-2013.01/arch/arm/cpu/armv7/start.S
搬移代碼如下:
ENTRY(relocate_code) mov r4, r0 /* save addr_sp */ mov r5, r1 /* save addr of gd */ mov r6, r2 /* save addr of destination */ adr r0, _start cmp r0, r6 moveq r9, #0 /* no relocation. relocation offset(r9) = 0 */ beq relocate_done /* skip relocation */ mov r1, r6 /* r1 <- scratch for copy_loop */ ldr r3, _image_copy_end_ofs add r2, r0, r3 /* r2 <- source end address */ copy_loop: ldmia r0!, {r9-r10} /* copy from source address [r0] */ stmia r1!, {r9-r10} /* copy to target address [r1] */ cmp r0, r2 /* until source end address [r2] */ blo copy_loop
詳情參考第四章,第3節(jié)。
3.uboot中,如何判斷此次開機(jī)是從斷電狀態(tài)開機(jī)還是從休眠狀態(tài)啟動(dòng)的?
board/samsung/fs4412/lowlevel_init.S
代碼如下:
41 lowlevel_init: 54 /* AFTR wakeup reset */ 55 ldr r2, =S5P_CHECK_DIDLE 56 cmp r1, r2 57 beq exit_wakeup 58 59 /* LPA wakeup reset */ 60 ldr r2, =S5P_CHECK_LPA 61 cmp r1, r2 62 beq exit_wakeup 63 64 /* Sleep wakeup reset */ 65 ldr r2, =S5P_CHECK_SLEEP 66 cmp r1, r2 67 beq wakeup_reset 112 wakeup_reset: 113 bl system_clock_init 114 bl mem_ctrl_asm_init 115 bl tzpc_init 116 117 exit_wakeup: 118 /* Load return address and jump to kernel */ 119 ldr r0, =(EXYNOS4_POWER_BASE + INFORM0_OFFSET) 120 121 /* r1 = physical address of exynos4210_cpu_resume function */ 122 ldr r1, [r0] 123 124 /* Jump to kernel*/ 125 mov pc, r1
由上可知,當(dāng)手機(jī)因?yàn)楦鞣N原因進(jìn)入休眠時(shí),會(huì)將當(dāng)前程序執(zhí)行的上下文保護(hù)起來,并向一些pmic的寄存器中寫入指定的數(shù)據(jù),以表明此次是因?yàn)楹畏N原因進(jìn)入休眠。
而手機(jī)并沒有完全斷電,而是處于一個(gè)低功耗模式下,此時(shí)啟動(dòng)RAM仍然有數(shù)據(jù),所以在此啟動(dòng)后,只需要從特殊的寄存器中讀取相應(yīng)的值,就可以知道之前是因?yàn)槭裁丛蛐菝?,進(jìn)而回復(fù)休眠之前的上下文即可。
3.uboot代碼搬到ram之后,代碼的運(yùn)行地址發(fā)生了變化,如何保證程序跳轉(zhuǎn)不會(huì)出錯(cuò)?
除了要保證uboot代碼是基于地址無關(guān)的,此外.rel.dyn幫我們解決了,其實(shí)主要還是編譯器幫我們做了很多工作。
位置無關(guān)碼參考《15. 從0學(xué)ARM-什么是位置無關(guān)碼?》
4.設(shè)備啟動(dòng)的時(shí)候,有可能直接從ram啟動(dòng), 如何知道當(dāng)前是從flah啟動(dòng)還是ram啟動(dòng)的?
文件:
board/samsung/fs4412/lowlevel_init.S
代碼:
lowlevel_init:
85 /* 86 * If U-boot is already running in ram, no need to relocate U-Boot. 87 * Memory controller must be configured before relocating U-Boot 88 * in ram. 89 */ 90 ldr r0, =0x0ffffff /* r0 <- Mask Bits*/ 91 bic r1, pc, r0 /* pc <- current addr of code */ 92 /* r1 <- unmasked bits of pc */ 93 ldr r2, _TEXT_BASE /* r2 <- original base addr in ram */ 94 bic r2, r2, r0 /* r2 <- unmasked bits of r2*/ 95 cmp r1, r2 /* compare r1, r2 */ 96 beq 1f /* r0 == r1 then skip sdram init */
原理:RAM地址空間是:0x40000000-0xA0000000 0xA0000000-0x00000000 而iROM/iRAM地址的bit:28-31均是0,所以只需要讀取出執(zhí)行到lowlevel_init時(shí)pc的值,判斷其bit:28-31是否是0即可知道現(xiàn)在代碼是否運(yùn)行在RAM中。
看完了這篇文章,相信你對(duì)“如何實(shí)現(xiàn)Cortex-A9 uboot啟動(dòng)代碼”有了一定的了解,如果想了解更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!