U-Boot源碼分析5—啟動過程分析start.S
- 1、`boot0.h`
- 2、`reset`
- 2.1、`vectors`
- 2.2、`ELn`
- 2.2.1 EL3
- 2.2.2、EL2、EL1
- 2.3、`SMPEN`
- 2.3、`core errate`
- 2.4、`lowlevel_init`
前面從U-Boot編譯的角度分析了其Makefile、鏈接腳本等,本章開始正式分析U-Boot啟動過程
從上一篇文章7、U-Boot源碼分析4—鏈接腳本分析,可知U-Boot啟動入口_start
在arch/arm/cpu/armv8/start.S
中
1、boot0.h
在start.S
中找到啟動入口`_start:
這里針對匯編文件的結尾*.s
和*.S
進行說明:其中小寫的s
表示文件中僅包含匯編代碼,編譯器將不進行預編譯操作;而大寫的S
表示文件中包含相關預編譯代碼,編譯器將進行預編譯操作,即判斷#ifdef
、#ifndef
、#if defined(xxx)
等語句的條件是否成立并保留相關代碼,或將#include
指令包含的文件內容替換到對應位置,等等的預編譯操作
這里搜索CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK
,發現存在定義:
因此將執行第28行的語句,即#include <asm/arch/boot0.h>
,對于某些芯片需要特殊的一些初始配置的話,可以通過配置CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK并執行相應boot0.h中的代碼來進行一些boot之前的操作,內容如下
可知其語句和其它啟動文件基本一樣,都是跳轉至reset
標號,但是增加了與AArch64切換有關的代碼,查看arch/arm/mach-sunxi/rmr_switch.S
中的說明,其實是本例所使用芯片的特殊屬性,因為該芯片在復位后會進入AArch32狀態,所以在一些時刻需要切換到AArch64狀態,這里涉及ARMv8的Reset Management Register,暫時先不分析
隨后進行 2 3 = 8 2^3=8 23=8字節對齊(.align 3
),并定義了一些符號:_end_ofs
、_bss_start_ofs
、_bss_end_ofs
,是一些關鍵段與代碼段起始位置的偏移
2、reset
跳轉進入reset
后,首先跳轉進入save_boot_params
,這里save_boot_params
通過WEAK
關鍵字定義為save_boot_params_ret
(start.S
文件最后);
因為使用了WEAK
關鍵字,當然也可以直接在其它文件中定義自己的save_boot_params
函數(需使用頭文件引用或在已有的頭文件中進行聲明)
這里搜索CONFIG_SYS_RESET_SCTRL
,發現沒有定義,因此不進行跳轉,繼續向下執行。
其實跳轉到reset_sctrl
中主要是根據當前異常等級執行對應操作:設置小端,關MMU,關I/D-cache
2.1、vectors
接下來,第67行首先將中斷向量vectors
的地址保存在x0
寄存器中,中斷向量的定義在arch/arm/cpu/armv8/excetions.S
中:
這里首先進行 2 1 1 = 2048 2^11=2048 211=2048字節即2K對齊(.align 11
),且各個中斷向量地址均以 2 7 = 128 2^7=128 27=128字節對齊;
每個中斷向量都對應其跳轉指令,比如b _do_bad_sync
,先用bl
指令跳轉到do_bad_sync
函數執行,然后再跳轉到中斷退出函數exception_exit
;而do_bad_sync
函數最終將輸出各個關鍵寄存器的值,最后調用panic
函數終止正在運行的程序并進行復位
/** do_bad_sync handles the impossible case in the Synchronous Abort vector.*/
void do_bad_sync(struct pt_regs *pt_regs, unsigned int esr)
{efi_restore_gd();printf("Bad mode in \"Synchronous Abort\" handler, esr 0x%08x\n", esr);show_regs(pt_regs);panic("Resetting CPU ...\n");
}
當然這里所有中斷都沒有打開,目前不會進入任何中斷向量,另外現在沒有初始化堆棧等,也無法執行C函數
2.2、ELn
然后第68行判斷異常等級,并根據判斷結果從不同分支開始執行
其中switch_el
在arch/arm/include/asm/macro.h
中定義
/** Branch according to exception level*/
.macro switch_el, xreg, el3_label, el2_label, el1_labelmrs \xreg, CurrentELcmp \xreg, 0xcb.eq \el3_labelcmp \xreg, 0x8b.eq \el2_labelcmp \xreg, 0x4b.eq \el1_label
.endm
其中xreg
即為傳入的x1
寄存器,這里將CurrentEL
寄存器的值放入x1
寄存器中,并依次和0xc
、0x8
、0x4
進行比較,根據比較結果進入不同的分支標號
CurrentEL
寄存器即為Current Exception Level寄存器,根據ARMv8的手冊,其定義如下:
因此0xc
、0x8
、0x4
即分別對應EL3、EL2、EL1
2.2.1 EL3
可知當復位后CPU處于EL3時,執行如下代碼
3: msr vbar_el3, x0mrs x0, scr_el3orr x0, x0, #0xf /* SCR_EL3.NS|IRQ|FIQ|EA */msr scr_el3, x0msr cptr_el3, xzr /* Enable FP/SIMD */
#ifdef COUNTER_FREQUENCYldr x0, =COUNTER_FREQUENCYmsr cntfrq_el0, x0 /* Initialize CNTFRQ */
#endifb 0f
首先將x0
寄存器中的中斷向量vectors
的地址放入vbar_el3
寄存器:Vector Base Address Register (EL3)
其中低11位(0~10)保留為0,對應前面中斷向量最開始的2K字節對齊(.align 11
)
隨后的3行語句,首先將scr_el3
寄存器的值保存到x0
寄存器中,然后將x0
寄存器或操作上0xF
即將低4位置1,然后保存到x0
寄存器中,最后將x0
寄存器的值再賦給scr_el3
寄存器,即scr_el3
寄存器低4為置1
scr_el3
寄存器bit3對應EA,即任何異常等級下發生的External Abort 和 SError 中斷都由EL3進行處理;bit2對應FIQ,即任何異常等級下發生的硬件上的FIQ (快速中斷請求)都由EL3進行處理;bit1對應IRQ,即任何異常等級下發生的硬件上的IRQ (中斷請求)都由EL3進行處理;bit0對應NS,置1表示任何低于EL3的異常等級(EL0、EL1等)都是非安全狀態,無法訪問安全內存空間
然后使用xzr
(64位全零寄存器)將cptr_el3
寄存器清零,
即(TCPAC)打開EL2訪問CPTR_EL2
、HCPTR
寄存器的權限,打開EL2、EL1訪問CPACR_EL1
、CPACR
寄存器的權限;(TAM)打開EL2、EL1訪問Activity Monitor registers的權限;(TTA)打開系統寄存器訪問trace
寄存器的權限;(TFP)使能SVE、SIMD和浮點運算;但(EZ)又把SVE關了
因此這一行代碼的功能也就是注釋說的使能SIMD和FP運算
然后由于在include/configs/sunxi-common.h
中定義了COUNTER_FREQUENCY
的值為24000000,因此將此值由x0
寄存器賦給cntfrq_el0
寄存器,即配置系統計數器的時鐘值,此系統計數器一般用于精確計時
結合上述分析,EL3等級對應的操作即為將中斷向量地址放入vbar_el3
寄存器,設置相關中斷請求僅由EL3實現,使能SIMD和FP運算,設置系統計數器的時鐘值
2.2.2、EL2、EL1
對應執行的代碼為,均為將中斷向量地址放入對應的vbar_eln
寄存器,使能SIMD和FP運算等,這里不再詳細分析
2: msr vbar_el2, x0mov x0, #0x33ffmsr cptr_el2, x0 /* Enable FP/SIMD */b 0f
1: msr vbar_el1, x0mov x0, #3 << 20msr cpacr_el1, x0 /* Enable FP/SIMD */
2.3、SMPEN
接下來的語句是開啟多核相關的操作,這里雖然沒有定義CONFIG_ARMV8_SET_SMPEN
,但注釋里建立用于Cortex-A53時使能,因此這里也分析一下
/** Enable SMPEN bit for coherency.* This register is not architectural but at the moment* this bit should be set for A53/A57/A72.*/
#ifdef CONFIG_ARMV8_SET_SMPENswitch_el x1, 3f, 1f, 1f
3:mrs x0, S3_1_c15_c2_1 /* cpuectlr_el1 */orr x0, x0, #0x40msr S3_1_c15_c2_1, x0
1:
#endif
這里的操作也一樣,首先判斷所處的異常等級,僅在EL3時進行操作:首先將S3_1_c15_c2_1
也即cpuectlr_el1
寄存器的值放入x0
寄存器,然后通過orr
指令將第6位置1;這里cpuectlr_el1
寄存器在Cortex-A53手冊中(ARMv8架構定義了一些必須實現的寄存器,同時預留一些空間給不同處理器來實現各自的特色功能等)
第6位即SMPEN置1,表示使能各核心之間的數據一致性功能(主要與cache有關,對于多核系統來說,一般各個核具有自己的L1-cache,簇內共享L2-cache,還有外部的L3-cache,這樣不同核心上的cache或ram對于同一個數據可能有多個副本,會導致數據觀察者(CPU/GPU/DMA等)能看到的數據不一致,因此設置會再處理器中設置硬件來維護數據的一致性,具體內容可參考【Cache篇】一文總結ARMv8架構中關于Cache的知識點)
2.3、core errate
隨后進行核心勘誤,目前僅支持Cortex-A57進行核心勘誤表的設置,這里跳過
/* Apply ARM core specific erratas */bl apply_core_errata
....
WEAK(apply_core_errata)mov x29, lr /* Save LR *//* For now, we support Cortex-A57 specific errata only *//* Check if we are running on a Cortex-A57 core */branch_if_a57_core x0, apply_a57_core_errata
0:mov lr, x29 /* Restore LR */retapply_a57_core_errata:#ifdef CONFIG_ARM_ERRATA_828024mrs x0, S3_1_c15_c2_0 /* cpuactlr_el1 *//* Disable non-allocate hint of w-b-n-a memory type */orr x0, x0, #1 << 49/* Disable write streaming no L1-allocate threshold */orr x0, x0, #3 << 25/* Disable write streaming no-allocate threshold */orr x0, x0, #3 << 27msr S3_1_c15_c2_0, x0 /* cpuactlr_el1 */
#endif#ifdef CONFIG_ARM_ERRATA_826974mrs x0, S3_1_c15_c2_0 /* cpuactlr_el1 *//* Disable speculative load execution ahead of a DMB */orr x0, x0, #1 << 59msr S3_1_c15_c2_0, x0 /* cpuactlr_el1 */
#endif#ifdef CONFIG_ARM_ERRATA_833471mrs x0, S3_1_c15_c2_0 /* cpuactlr_el1 *//* FPSCR write flush.* Note that in some cases where a flush is unnecessary thiscould impact performance. */orr x0, x0, #1 << 38msr S3_1_c15_c2_0, x0 /* cpuactlr_el1 */
#endif#ifdef CONFIG_ARM_ERRATA_829520mrs x0, S3_1_c15_c2_0 /* cpuactlr_el1 *//* Disable Indirect Predictor bit will prevent this erratumfrom occurring* Note that in some cases where a flush is unnecessary thiscould impact performance. */orr x0, x0, #1 << 4msr S3_1_c15_c2_0, x0 /* cpuactlr_el1 */
#endif#ifdef CONFIG_ARM_ERRATA_833069mrs x0, S3_1_c15_c2_0 /* cpuactlr_el1 *//* Disable Enable Invalidates of BTB bit */and x0, x0, #0xEmsr S3_1_c15_c2_0, x0 /* cpuactlr_el1 */
#endifb 0b
ENDPROC(apply_core_errata)
2.4、lowlevel_init
接下來就到了大名鼎鼎的lowlevel_init
,本章先不分析,留待下章
本章分析完畢~
完結撒花??ヽ(°▽°)ノ?