入口點:arch/arm/cpu/armv8/start.S
1. 判斷是否定義了鉤子,如有則執行,否則往下走。執行save_boot_params,本質就是保存一些寄存器的值。
2. 對齊修復位置無關碼的偏移
??????假設U-Boot鏈接時基址為0x10000,但實際加載到0x20000:
????? 基址偏移 x9 = 0x20000 - 0x10000 = 0x10000。
??????若某個重定位項的r_offset = 0x11000(鏈接時地址),r_addend = 0x200:
????? 運行時r_offset 修正為0x11000 + 0x10000 = 0x21000。
3. 若定義了CONFIG_SYS_RESET_SCTRL,則執行reset_sctrl,也是一個鉤子函數,復位操作相關。
4. 異常向量表配置及EL3常規配置
?????a. 異常向量表地址(即vector)存到x0
?????b. 判斷當前EL幾,然后執行對應的分支,以EL3舉例
???????1)把x0的值,寫到vbar_el3。
?????? 2)配置scr_el3 的低4位,包括安全位的設置,以及配置“物理IRQ中斷會不會被路由到EL3”“物理 FIQ 中斷會不會被路由到EL3”“external aborts和SError中斷會不會被路由到EL3”,以上全部配置為“會”。
???????3)使能浮點計算;使能單指令多數據。
???????4)初始cntfrq_el的值。這個變量是咱們用的定時器的那個頻率。在Uboot中,不是測量出來的,是直接寫死的。
?5. 配置sctlr,使能指令cache
??????c. CR_I,一個宏定義,表示指令cache在sctlr中的bit位。
??????d. 通過switch_el宏指令,判斷當前是EL幾,然后進入相應分支。以el3舉例。
??????e. 把CR_I寫入sctlr,即,使能指令cache。這個指令cache,是EL0和EL1的使能位。
?6. 使能多核之間的數據一致性
??????f. 先是isb指令,流水線同步操作。
??????g. 然后操作cpuectlr寄存器,bit6使能,即保證多核數據一致性。
?7. 截至這里,cache、TLB都是無效的。執行lowlevel_init
??????h. 執行bl lowlevel_init時,bl指令,會把bl后邊的指令的地址,存在LR中,以便執行完lowlevel_init的ret時返回。但是由于lowlevel_init中,也會調用bl,導致lr會被覆蓋,所以在lowlevel_init中,先存一下LR的值。可以看到,上圖最后3行,在執行ret 時,先把x29的值,恢復到lr中,在執行ret。
??????i. 如果IRQ被設置為y,則進行中斷控制器的配置:branch_if_slave可以判斷是否是主核,若是主核,則執行ldr x0, = GICD_BASE 和bl gic_init_secure,然后跳轉;若不是主核,則直接跳轉。
???????1)若是主核,x0存儲了中斷控制器寄存器基地址。
???????2)x0作為參數,調用gic_init_secure:arch/arm/lib/gic_64.S。
???????3)上圖代碼走GICV2分支。先是gicd_ctlr的bit0和bit1置1,表示使能Grp0和Grp1的中斷上送。
???????4)讀取GICD_TYPER中的ITLinesNumber。
?????????需要了解的是,當ITLinesNumber = 0時,表示最大中斷數為32 * (0 + 1) = 32,即 ID 為0 ~ 31。但只有32個中斷,就意味著沒有SPI(共享中斷),如下圖,SPI的編號,從32開始。
?????????所以,cbz w10 1f 指令的含義,是當沒有SPI中斷時,跳轉到1:處,直接返回。
???????5)通過循環,將所有中斷都劃到group1中。
???????6)gic_init_secure結束,返回上一層。跳轉到gic_ini_secure_percpu去,同時傳進去的參數分別是GIC distributor寄存器組的基地址和cpu interface組的基地址。把SGI 和PPI劃分到group1(其實在上一個函數里,已經做過一遍了)。使能SGI(多核通信要用);使能通過interface上報中斷。
??????j. 從核等待主核SGI0中斷喚醒。
8. 最后進入_main
異常觸發時,根據異常的類型(SE同步異常、irq中斷、fiq快速中斷等),硬件會跳轉到vector 的偏移。比如,IRQ跳轉到vector+0x80*5,其實就是,vector中的條目,每個條目按128字節對齊(align 7),所以就是,每個條目最多128字節。