在嵌入式系統(tǒng)中,U-Boot作為引導加載程序,其啟動流程的核心環(huán)節(jié)之一就是重定位(Relocation)。對于RK3506這類基于ARM Cortex-A架構的芯片,重定位的本質是將U-Boot代碼從初始加載地址(通常是片內ROM或Flash)復制到運行效率更高的片外RAM,再切換執(zhí)行環(huán)境到RAM中運行。
本文將結合U-Boot源碼中ARM Cortex核心的啟動代碼,拆解RK3506平臺U-Boot重定位的實現(xiàn)邏輯、關鍵步驟與底層原理。
路徑:u-boot/arch/arm/cpu/armv7/start.S

一、重定位的核心目的:為何需要“搬家”?
RK3506的U-Boot啟動初期,代碼通常從片內BootROM或SPI Flash加載到片內SRAM(小容量高速內存)執(zhí)行。但SRAM容量有限(通常僅幾十KB),無法容納完整的U-Boot鏡像(包含驅動、命令、文件系統(tǒng)等模塊),也無法滿足后續(xù)加載Linux內核的需求。
重定位的核心目標:
1.釋放存儲空間:將U-Boot完整鏡像從Flash/SRAM遷移到大容量片外DDR內存;
2.提升執(zhí)行效率:DDR內存帶寬更高、容量更大,支持U-Boot運行復雜邏輯(如設備初始化、內核加載);
3.預留內存空間:為Linux內核、設備樹等后續(xù)加載的鏡像預留連續(xù)內存區(qū)域。
二、重定位的前提:啟動初期的關鍵初始化
在執(zhí)行重定位前,U-Boot必須完成一系列底層初始化,為“搬家”做好準備。結合本文提供的ARM Cortex啟動代碼,這些準備工作主要集中在reset入口函數(shù)中:
1.模式與中斷初始化
reset: b save_boot_paramssave_boot_params_ret: /* 切換到 SVC32 模式,禁用 FIQ/IRQ */ mrs r0, cpsr and r1, r0,#0x1f @ 掩碼模式位 teq r1,#0x1a @ 檢查是否為 HYP 模式 bicne r0, r0,#0x1f @ 清除模式位 orrne r0, r0,#0x13 @ 設置為 SVC 模式(管理模式) orr r0, r0,#0xc0 @ 禁用 FIQ (0x80) 和 IRQ (0x40) msr cpsr, r0
?切換到ARM特權模式(SVC32):確保U-Boot擁有訪問系統(tǒng)寄存器、修改內存配置的權限;
?禁用中斷:避免初始化過程中被外部中斷打斷,導致系統(tǒng)異常。
2.向量表初始化
/* 設置 VBAR 寄存器,指向 U-Boot 向量表 */ldr r0, =_startmcr p15,0, r0, c12, c0,0 @ 寫入VBAR(向量表基地址寄存器)
?ARMv7架構通過VBAR寄存器指定異常向量表地址;
?重定位前,向量表位于初始加載地址(如SRAM),后續(xù)重定位后需確保向量表地址同步更新(由鏈接腳本配合處理)。
3.緩存與MMU初始化(cpu_init_cp15)
重定位前需禁用MMU(內存管理單元)和緩存,避免地址映射干擾內存復制:
ENTRY(cpu_init_cp15) /* invalidate L1 I/D 緩存、TLB */ mov r0,#0 mcr p15,0, r0, c8, c7,0 @ invalidate TLBs mcr p15,0, r0, c7, c5,0 @ invalidate I-cache /* 禁用 MMU、緩存相關配置 */ mrc p15,0, r0, c1, c0,0 bic r0, r0,#0x00002000 @ 清除 V 位(向量表地址偏移) bic r0, r0,#0x00000007 @ 清除 CAM 位(緩存相關) orr r0, r0,#0x00000800 @ 啟用 BTB(分支預測)#ifdef CONFIG_SYS_ICACHE_OFF bic r0, r0,#0x00001000 @ 禁用 I-cache#else orr r0, r0,#0x00001000 @ 啟用 I-cache(重定位后生效)#endif mcr p15,0, r0, c1, c0,0
?初始化CP15寄存器(ARM系統(tǒng)控制寄存器),清空緩存和TLB(地址轉換緩存);
?禁用MMU:此時CPU訪問的是物理地址,確保內存復制過程中地址無映射偏差;
?按需啟用I-cache(指令緩存):重定位后代碼在DDR中運行時,緩存可提升執(zhí)行效率。
4.板級底層初始化(cpu_init_crit)
ENTRY(cpu_init_crit) b lowlevel_init @ 跳轉到板級初始化ENDPROC(cpu_init_crit)
?lowlevel_init是RK3506平臺的板級初始化函數(shù)(由瑞芯微適配);
?核心任務:初始化DDR內存控制器、配置PLL時鐘(提升DDR帶寬)、初始化SPI Flash等外設;
?關鍵:只有完成DDR初始化,U-Boot才能將自身復制到DDR中,這是重定位的硬件基礎。
三、重定位的核心實現(xiàn):_main函數(shù)的“搬家”邏輯
當?shù)讓映跏蓟ㄓ绕涫荄DR初始化)完成后,代碼通過bl _main跳轉到U-Boot核心初始化流程,重定位的核心邏輯就在_main函數(shù)中(位于common/main.c)。
結合RK3506的平臺特性,_main函數(shù)中重定位的關鍵步驟如下:
1.確定重定位地址(鏈接腳本定義)
U-Boot的重定位目標地址由鏈接腳本(如arch/arm/cpu/armv7/rk3506/u-boot.lds)定義,核心符號:
?_start:U-Boot初始加載地址(SRAM或Flash地址);
?__image_copy_start:鏡像復制起始地址(初始加載地址的代碼段起始);
?__image_copy_end:鏡像復制結束地址;
?__bss_start/__bss_end:BSS段起始/結束地址(重定位后需清零);
?CONFIG_SYS_TEXT_BASE:重定位目標地址(RK3506通常配置為DDR起始地址,如0x80000000)。
2.內存復制:從初始地址到DDR
重定位的核心操作是逐字節(jié)復制U-Boot鏡像到DDR目標地址,代碼邏輯簡化如下:
// 簡化自 common/main.cvoid_main(void) { // 1. 獲取鏈接腳本定義的地址符號 externulong__image_copy_start, __image_copy_end; externulong__bss_start, __bss_end; ulongdst = CONFIG_SYS_TEXT_BASE; // 重定位目標地址(DDR) ulongsrc = (ulong)&__image_copy_start; // 源地址(SRAM/Flash) // 2. 只有當源地址 != 目標地址時,才需要復制(避免自身覆蓋) if(src != dst) { memcpy((void*)dst, (void*)src, &__image_copy_end - &__image_copy_start); } // 3. 清零 BSS 段(未初始化全局變量) memset((void*)&__bss_start,0, &__bss_end - &__bss_start); // 4. 跳轉到 DDR 中的 U-Boot 繼續(xù)執(zhí)行 board_init_f_r_trampoline(dst);}
?復制范圍:從__image_copy_start到__image_copy_end,包含代碼段(.text)、數(shù)據(jù)段(.data)等已初始化部分;
?避免自身覆蓋:若源地址與目標地址重疊(如部分SRAM與DDR地址重疊),U-Boot會先復制不重疊部分,再處理重疊區(qū)域,防止復制過程中覆蓋未復制的代碼;
?BSS段清零:BSS段存儲未初始化全局變量,C語言標準要求其初始值為0,因此重定位后需手動清零。
3.跳轉至DDR執(zhí)行:地址切換
復制完成后,通過board_init_f_r_trampoline函數(shù)跳轉到DDR中的U-Boot代碼繼續(xù)執(zhí)行。此時CPU執(zhí)行的指令已從DDR讀取,重定位完成。
4.棧指針更新
重定位后,棧指針(SP)也需更新到DDR中的安全地址(避免使用SRAM棧導致溢出),由board_init_f函數(shù)初始化:
// 簡化自 common/board_f.cvoidboard_init_f(ulongboot_flags){ ulongsp = CONFIG_SYS_INIT_SP_ADDR; // DDR 中的棧地址 sp -=sizeof(structglobal_data); // 預留全局數(shù)據(jù)結構空間 gd = (structglobal_data *)sp; memset(gd,0,sizeof(structglobal_data)); // 初始化棧指針 asmvolatile("mov sp, %0": :"r"(sp) : "memory"); // 后續(xù)初始化:設備樹加載、命令初始化、內核引導等}
?CONFIG_SYS_INIT_SP_ADDR:RK3506配置為DDR中的一段連續(xù)地址,確保棧空間足夠;
?global_data:U-Boot全局數(shù)據(jù)結構,存儲系統(tǒng)狀態(tài)(如內存布局、設備信息),重定位后需在DDR中重新初始化。
四、重定位后的關鍵處理
1.向量表同步更新
重定位后,向量表地址需同步更新到DDR中的新地址,避免異常處理時跳轉到舊地址(SRAM/Flash)。由于之前已通過VBAR寄存器設置向量表基地址為_start,而_start在重定位后指向DDR地址,因此無需額外修改(鏈接腳本確保_start對應DDR目標地址)。
2.緩存重新配置
重定位完成后,U-Boot會重新啟用I-cache/D-cache(若配置),提升執(zhí)行效率。此時MMU仍處于禁用狀態(tài)(直到Linux內核啟動時啟用),CPU直接訪問DDR物理地址。
3.避免重定位后的地址錯誤
?所有全局變量、函數(shù)指針均使用位置無關代碼(PIC)編譯,確保重定位后地址正確映射;
?鏈接腳本通過TEXT_BASE強制指定目標地址,確保復制后的鏡像在DDR中地址對齊。
五、RK3506重定位的特殊注意事項
1.DDR初始化優(yōu)先級:RK3506的DDR控制器初始化是重定位的前提,需通過lowlevel_init配置DDR時序、電壓,確保DDR穩(wěn)定工作;
2.Flash訪問兼容性:若初始加載地址為SPI Flash(如0x10000000),復制時需通過RK3506的SPI控制器驅動讀取Flash數(shù)據(jù),再寫入DDR;
3.內存布局優(yōu)化:RK3506的DDR起始地址通常為0x80000000,U-Boot重定位后,會在DDR中預留后續(xù)加載Linux內核(如0x80200000)和設備樹(如0x80100000)的空間,避免地址沖突。
六、總結:重定位的完整流程
RK3506 U-Boot重定位的核心是“初始化硬件→復制鏡像→切換執(zhí)行環(huán)境”,完整流程可概括為:
1.復位入口(reset):切換SVC模式、禁用中斷、初始化向量表;
2.底層初始化:初始化CP15寄存器(緩存/ MMU)、板級硬件(DDR/PLL);
3.確定地址:通過鏈接腳本獲取源地址、目標地址(DDR);
4.鏡像復制:memcpy復制代碼段/數(shù)據(jù)段到DDR,清零BSS段;
5.切換執(zhí)行:更新棧指針,跳轉到DDR中的U-Boot繼續(xù)執(zhí)行;
6.后續(xù)初始化:加載設備樹、初始化外設、引導Linux內核。
重定位是U-Boot從“小容量初始環(huán)境”到“大容量運行環(huán)境”的關鍵一步,理解其原理不僅能幫助排查啟動故障(如DDR初始化失敗導致重定位失敗),也能為定制化U-Boot(如調整內存布局、優(yōu)化啟動速度)提供基礎。
對于RK3506開發(fā)者,建議結合鏈接腳本和lowlevel_init代碼,重點關注CONFIG_SYS_TEXT_BASE和DDR初始化參數(shù),確保重定位地址與硬件配置一致。
審核編輯 黃宇
-
u-boot
+關注
關注
0文章
123瀏覽量
39508 -
RK3506
+關注
關注
0文章
85瀏覽量
775
發(fā)布評論請先 登錄
米爾RK3506核心板SDK重磅升級,解鎖三核A7實時控制新架構
RK3506工控板核心板Wi-Fi模組性能測試報告
2025瑞芯微開發(fā)者大會萬象奧科展出RK3506郵票孔核心板
明遠智睿 RK3506 核心板:工業(yè)物聯(lián)網領域的性能 “小巨人”
RK3506開發(fā)板Linux開發(fā)板極致性價比之選
【米爾RK3506國產開發(fā)板評測試用】開箱體體驗
瑞芯微RK3506 vs NXP i.MX6ULL

深入理解?RK3506 U-Boot?重定位:從代碼到原理
評論