ARM64的启动过程之(一):内核第一个脚印
一、前言
kernel的整個(gè)啟動(dòng)過(guò)程涉及的內(nèi)容很多,不可能每一個(gè)細(xì)節(jié)都描述清楚,因此我打算針對(duì)部分和ARM64相關(guān)的啟動(dòng)步驟進(jìn)行學(xué)習(xí)、整理,并方便后續(xù)查閱。本文實(shí)際上描述在系統(tǒng)啟動(dòng)最開(kāi)始的時(shí)候,bootloader和kernel的交互以及kernel如何保存bootloader傳遞的參數(shù)并進(jìn)行校驗(yàn),此外,還有一些最基礎(chǔ)的硬件初始化的內(nèi)容。
本文中的source來(lái)自4.1.10內(nèi)核,這是一個(gè)long term的版本,后續(xù)一段時(shí)間的文章都會(huì)基于這個(gè)long term版本進(jìn)行。
二、bootloader和kernel的交互
系統(tǒng)啟動(dòng)過(guò)程中,linux kernel不是一個(gè)人在戰(zhàn)斗,在kernel之前bootloader會(huì)進(jìn)行下面的動(dòng)作
1、初始化系統(tǒng)中的RAM并將RAM的信息告知kernel
2、準(zhǔn)備好device tree blob的信息并將dtb的首地址告知kernel
3、解壓內(nèi)核(可選)
4、將控制權(quán)轉(zhuǎn)交給內(nèi)核。當(dāng)然,bootloader和kernel的交互的時(shí)候需求如下:
MMU = off, D-cache = off, I-cache = on or off?
?? x0 = physical address to the FDT blob
更詳細(xì)的ARM64 boot protocol請(qǐng)參考Documentation/arm64/booting.txt文檔。
三、參數(shù)的保存和校驗(yàn)
最開(kāi)始的ARM64啟動(dòng)代碼位于arch/arm64/kernel/head.S文件中,代碼如下:
ENTRY(stext)?
??? bl??? preserve_boot_args?
??? bl??? el2_setup??????????? // Drop to EL1, w20=cpu_boot_mode?
??? adrp??? x24, __PHYS_OFFSET?
??? bl??? set_cpu_boot_mode_flag
??? bl??? __vet_fdt??
??? ……?
ENDPROC(stext)
1、preserve_boot_args
preserve_boot_args:?
??? mov??? x21, x0------將x0的值暫存在x21寄存器中,后面要使用x0
??? adr_l??? x0, boot_args---x0保存了boot_args變量的地址?
??? stp??? x21, x1, [x0]----保存x0和x1的值到boot_args[0]和boot_args[1]?
??? stp??? x2, x3, [x0, #16] ---保存x2和x3的值到boot_args[2]和boot_args[3]
??? dmb??? sy---------full system data memory barrier
??? add??? x1, x0, #0x20----x0和x1是傳遞給__inval_cache_range的參數(shù)?
??? b??? __inval_cache_range?
ENDPROC(preserve_boot_args)
由于MMU = off, D-cache = off,因此寫入boot_args變量的操作都是略過(guò)data cache的,直接寫入了RAM中,為了安全起見(jiàn)(也許bootloader中打開(kāi)了D-cache并操作了boot_args這段memory),將boot_args變量對(duì)應(yīng)的cache line進(jìn)行清除并設(shè)置無(wú)效。在調(diào)用__inval_cache_range之前,x0是boot_args這段memory的首地址,x1是末尾的地址(boot_args變量長(zhǎng)度是4x8byte=32byte,也就是0x20了)。
為何要保存x0~x3這四個(gè)寄存器呢?因?yàn)锳RM64 boot protocol對(duì)啟動(dòng)時(shí)候的x0~x3這四個(gè)寄存器有嚴(yán)格的限制:x0是dtb的物理地址,x1~x3必須是0。在后續(xù)setup_arch函數(shù)執(zhí)行的時(shí)候會(huì)訪問(wèn)boot_args并進(jìn)行校驗(yàn)。
最后一個(gè)小細(xì)節(jié)是如何訪問(wèn)boot_args這個(gè)符號(hào)的,這個(gè)符號(hào)是一個(gè)虛擬地址,但是,現(xiàn)在沒(méi)有建立好頁(yè)表,也沒(méi)有打開(kāi)MMU,如何訪問(wèn)它呢?這是通過(guò)adr_l這個(gè)宏來(lái)完成的。這個(gè)宏實(shí)際上是通過(guò)adrp這個(gè)匯編指令完成,通過(guò)該指令可以將符號(hào)地址變成運(yùn)行時(shí)地址(通過(guò)PC relative offset形式),因此,當(dāng)運(yùn)行的MMU OFF mode下,通過(guò)adrp指令可以獲取符號(hào)的物理地址。不過(guò)adrp是page對(duì)齊的(adrp中的p就是page的意思),boot_args這個(gè)符號(hào)當(dāng)然不會(huì)是page size對(duì)齊的,因此不能直接使用adrp,adr_l這個(gè)宏進(jìn)行處理,如果讀者有興趣可以自己看source code。
2、el2_setup
程序執(zhí)行至此,CPU處于哪一個(gè)exception level呢?根據(jù)ARM64 boot protocol,CPU要么處于EL2(推薦)或者non-secure EL1。如果在EL1,情形有些類似過(guò)去arm處理器的感覺(jué),處于EL2稍微復(fù)雜一些,需要對(duì)virtualisation extensions進(jìn)行基本的設(shè)定,然后將cpu退回到EL1。代碼太長(zhǎng)了,我們分成兩段來(lái)閱讀,第一段如下:
ENTRY(el2_setup)?
??? mrs??? x0, CurrentEL------------------------(1)?
??? cmp??? x0, #CurrentEL_EL2------判斷是否處于EL2?
??? b.ne??? 1f--------------不是的話,跳到1f?
??? mrs??? x0, sctlr_el2-------------------------(2)?
CPU_BE(??? orr??? x0, x0, #(1 << 25)??? )??? // Set the EE bit for EL2?
CPU_LE(??? bic??? x0, x0, #(1 << 25)??? )??? // Clear the EE bit for EL2?
??? msr??? sctlr_el2, x0----寫回sctlr_el2寄存器?
??? b??? 2f?
1:??? mrs??? x0, sctlr_el1-------------------------(3)?
CPU_BE(??? orr??? x0, x0, #(3 << 24)??? )??? // Set the EE and E0E bits for EL1?
CPU_LE(??? bic??? x0, x0, #(3 << 24)??? )??? // Clear the EE and E0E bits for EL1?
??? msr??? sctlr_el1, x0?
??? mov??? w20, #BOOT_CPU_MODE_EL1----w20寄存器保存了cpu啟動(dòng)時(shí)候的Eexception level?
??? isb---------instruction memory barrier?
??? ret
2:??? mov??? x0, #(1 << 31) ------------------------(4)?
??? msr??? hcr_el2, x0
??? mrs??? x0, cnthctl_el2 -------------------------(5)?
??? orr??? x0, x0, #3???????????????? // Enable EL1 physical timers?
??? msr??? cnthctl_el2, x0?
??? msr??? cntvoff_el2, xzr??????? // Clear virtual offset
??? mrs??? x0, id_aa64pfr0_el1 -----------------------(6)?
??? ubfx??? x0, x0, #24, #4 ----取出24 bit開(kāi)始的4個(gè)bit的值并將該值賦給x0?
??? cmp??? x0, #1?
??? b.ne??? 3f -----不支持system register接口
??? mrs_s??? x0, ICC_SRE_EL2?
??? orr??? x0, x0, #ICC_SRE_EL2_SRE??? // Set ICC_SRE_EL2.SRE==1?
??? orr??? x0, x0, #ICC_SRE_EL2_ENABLE??? // Set ICC_SRE_EL2.Enable==1?
??? msr_s??? ICC_SRE_EL2, x0?
??? isb??????????????????? // Make sure SRE is now set?
??? msr_s??? ICH_HCR_EL2, xzr??????? // Reset ICC_HCR_EL2 to defaults
3:?????????????? ……
(1)當(dāng)前的exception level保存在PSTATE中,程序可以通過(guò)MRS或者M(jìn)SR來(lái)訪問(wèn)PSTATE,當(dāng)然需要傳遞一個(gè)Special-purpose register做為參數(shù),CurrentEL就是獲取PSTATE中current exception level域的特殊寄存器。
(2)sctlr_el2也是一個(gè)可以通過(guò)MRS/MSR指令訪問(wèn)的寄存器,當(dāng)CPU處于EL2狀態(tài)的時(shí)候,該寄存器可以控制整個(gè)系統(tǒng)的行為。當(dāng)然,這里僅僅是設(shè)定EL2下的數(shù)據(jù)訪問(wèn)和地址翻譯過(guò)程中的endianess配置,也就是EE bit[25]。根據(jù)配置,CPU_BE和CPU_LE包圍的指令只會(huì)保留一行。對(duì)于little endian而言,實(shí)際上就是將sctlr_el2寄存器的EE(bit 25)設(shè)定為0。順便說(shuō)一下,這個(gè)bit不僅僅控制EL2數(shù)據(jù)訪問(wèn)的endianess以及EL2 stage 1的地址翻譯過(guò)程中的endianess(當(dāng)然,EL2只有stage 1),還可以控制EL1和EL0 stage 2地址翻譯的過(guò)程的endianess(這時(shí)候有兩個(gè)stage的地址翻譯過(guò)程)。
(3)執(zhí)行到這里說(shuō)明CPU處于EL1,這種狀態(tài)下沒(méi)有權(quán)限訪問(wèn)sctlr_el2,只能是訪問(wèn)sctlr_el1。sctlr_el1可以通過(guò)EE和E0E來(lái)控制EL1和EL0狀態(tài)下是little endian還是big endian。EE bit控制了EL1下的數(shù)據(jù)訪問(wèn)以及EL1和EL0 stage 1地址翻譯的過(guò)程的endianess。E0E bit用來(lái)控制EL0狀態(tài)下的數(shù)據(jù)訪問(wèn)的endianess。此外,需要注意的是:由于修改了system control register(設(shè)定endianess狀態(tài)),因此需要一個(gè)isb來(lái)同步(具體包括兩部分的內(nèi)容,一是確認(rèn)硬件已經(jīng)執(zhí)行完畢了isb之前的所有指令,包括修改system control寄存器的那一條指令,另外一點(diǎn)是確保isb之后的指令從新來(lái)過(guò),例如取指,校驗(yàn)權(quán)限等)。
(4)執(zhí)行到這里說(shuō)明CPU處于EL2,首先設(shè)定的是hcr_el2寄存器,Hypervisor Configuration Register。該寄存器的大部分bit 值在reset狀態(tài)的時(shí)候就是0值,只不過(guò)bit 31(Register Width Control)是implementation defined,因此這里set 31為1,確保Low level的EL1也是Aarch64的
(5)這一段代碼是對(duì)Generic timers進(jìn)行配置。要想理解這段代碼,我們需要簡(jiǎn)單的了解一些ARMv8上Generic timer的運(yùn)作邏輯。一個(gè)全局范圍的system counter、各個(gè)PE上自己專屬的local timer以及連接這些組件之間的bus或者信息傳遞機(jī)制組成了Generic Timer。對(duì)于PE而言,通過(guò)寄存器訪問(wèn),它能看到的是physical counter(實(shí)際的system counter計(jì)數(shù))、virtual counter(physical counter基礎(chǔ)上的offset)、physical timer、virtual timer等。NTHCTL_EL2,Counter-timer Hypervisor Control register,用來(lái)控制系統(tǒng)中的physical counter和virutal counter如何產(chǎn)生event stream以及在EL1和EL0狀態(tài)訪問(wèn)physical counter和timer的硬件行為的。在EL1(EL0)狀態(tài)的時(shí)候訪問(wèn)physical counter和timer有兩種配置,一種是允許其訪問(wèn),另外一種就是trap to EL2。這里的設(shè)定是:不陷入EL2(對(duì)應(yīng)的bit設(shè)置為1)。更詳細(xì)的信息可以參考ARMv8 ARM文檔。cntvoff_el2是virtual counter offset,所謂virtual counter,其值就是physical counter的值減去一個(gè)offset的值(也就是cntvoff_el2的值了),這里把offset值清零,因此virtual counter的計(jì)數(shù)和physical counter的計(jì)數(shù)是一樣的。
(6)這一段代碼是對(duì)GIC V3進(jìn)行配置。ID_AA64PFR0_EL1,AArch64 Processor Feature Register 0,該寄存器描述了PE實(shí)現(xiàn)的feature。GIC bits [27:24]描述了該P(yáng)E是否實(shí)現(xiàn)了system register來(lái)訪問(wèn)GIC,如果沒(méi)有(GIC bits 等于0)那么就略過(guò)GIC V3的設(shè)定。ICC_SRE_EL2,Interrupt Controller System Register Enable register (EL2),該寄存器用來(lái)(在EL2狀態(tài)時(shí)候)控制如何訪問(wèn)GIC CPU interface模塊的,可以通過(guò)memory mapped方式,也可以通過(guò)system register的方式。將SRE bit設(shè)定為1確保通過(guò)system register方式進(jìn)行GIC interface cpu寄存器的訪問(wèn)。將enable bit設(shè)定為1確保在EL1狀態(tài)的時(shí)候可以通過(guò)ICC_SRE_EL1寄存器對(duì)GIC進(jìn)行配置而不是陷入EL2。
下面我們進(jìn)入第二段代碼:
??? mrs??? x0, midr_el1 -----------------------------(1)?
??? mrs??? x1, mpidr_el1?
??? msr??? vpidr_el2, x0?
??? msr??? vmpidr_el2, x1
??? mov??? x0, #0x0800??????????? // Set/clear RES{1,0} bits ---------------(2)?
CPU_BE(??? movk??? x0, #0x33d0, lsl #16??? )??? // Set EE and E0E on BE systems?
CPU_LE(??? movk??? x0, #0x30d0, lsl #16??? )??? // Clear EE and E0E on LE systems?
??? msr??? sctlr_el1, x0
??? mov??? x0, #0x33ff-------Disable Coprocessor traps to EL2?
??? msr??? cptr_el2, x0??????????? // Disable copro. traps to EL2
#ifdef CONFIG_COMPAT-----是否支持64 bit kernel上運(yùn)行32bit 的application?
??? msr??? hstr_el2, xzr??????????? // Disable CP15 traps to EL2?
#endif
??? mrs??? x0, pmcr_el0------------------------------(3)?
??? ubfx??? x0, x0, #11, #5??????????? // to EL2 and allow access to?
??? msr??? mdcr_el2, x0??????????? // all PMU counters from EL1??
??? msr??? vttbr_el2, xzr ----清除Stage-2 translation table base address register
??? adrp??? x0, __hyp_stub_vectors?
??? add??? x0, x0, #:lo12:__hyp_stub_vectors?
??? msr??? vbar_el2, x0 ---------------設(shè)定EL2的異常向量表的基地址
??? mov??? x0, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\?
????????????? PSR_MODE_EL1h)?
??? msr??? spsr_el2, x0 ------------------------------(4)?
??? msr??? elr_el2, lr?
??? mov??? w20, #BOOT_CPU_MODE_EL2??????? // This CPU booted in EL2?
??? eret-------------------------------------(5)
ENDPROC(el2_setup)
(1)midr_el1和mpidr_el1都屬于標(biāo)識(shí)該P(yáng)E信息的read only寄存器。MIDR_EL1,Main ID Register主要給出了該P(yáng)E的architecture信息,Implementer是誰(shuí)等等信息。MPIDR_EL1,Multiprocessor Affinity Register,該寄存器保存了processor ID。vpidr_el2和vmpidr_el2是上面的兩個(gè)寄存器是對(duì)應(yīng)的,只不過(guò)是for virtual processor的。
(2)這段代碼實(shí)際上是將0x33d00800(BE)或者0x30d00800(LE)寫入sctlr_el1寄存器。BE和LE的設(shè)定和上面第一段代碼中的描述是類似的,其他bit的設(shè)定請(qǐng)參考ARMv8 ARM文檔
(3)PMCR_EL0,Performance Monitors Control Register,該寄存器的[15:11]標(biāo)識(shí)了支持的Performance Monitors counter的數(shù)目,并將其設(shè)定到MDCR_EL2(Monitor Debug Configuration Register (EL2))中。MDCR_EL2中其他的bit都設(shè)定為0,其結(jié)果就是允許EL0和EL1進(jìn)行debug的操作(而不是trap to EL2),允許EL1訪問(wèn)Performance Monitors counter(而不是trap to EL2)。
(4)當(dāng)系統(tǒng)發(fā)生了異常并進(jìn)入EL2,SPSR_EL2,Saved Program Status Register (EL2)會(huì)保存處理器狀態(tài),ELR_EL2,Exception Link Register (EL2)會(huì)保存返回發(fā)生exception的現(xiàn)場(chǎng)的返回地址。這里是設(shè)定SPSR_EL2和ELR_EL2的初始值。w20寄存器保存了cpu啟動(dòng)時(shí)候的Eexception level ,因此w20被設(shè)定為BOOT_CPU_MODE_EL2。
(5)eret指令是用來(lái)返回發(fā)生exception的現(xiàn)場(chǎng)。實(shí)際上,這個(gè)指令僅僅是模擬了一次異常返回而已,SPSR_EL2和ELR_EL2都已經(jīng)設(shè)定OK,執(zhí)行該指令會(huì)使得CPU返回EL1狀態(tài),并且將SPSR_EL2的值賦給PSTATE,ELR_ELR就是返回地址(實(shí)際上也恰好是函數(shù)的返回地址)。
完成了el2_setup這個(gè)函數(shù)分析之后,我們?cè)倩仡^思考這樣的問(wèn)題:為何是el2_setup?為了沒(méi)有el3_setup?當(dāng)一個(gè)SOC的實(shí)現(xiàn)在包括了EL3的支持,那么CPU CORE缺省應(yīng)該進(jìn)入EL3狀態(tài),為何這里只是判斷EL2還是EL1,從而執(zhí)行不同的流程,如果是EL3狀態(tài),代碼不就有問(wèn)題了嗎?實(shí)際上,即便是由于SOC支持TrustZone而導(dǎo)致cpu core上電后進(jìn)入EL3,這時(shí)候,接管cpu控制的一定不是linux kernel(至少目前來(lái)看linux kernel不會(huì)做Secure monitor),而是Secure Platform Firmware(也就是傳說(shuō)中的secure monitor),它會(huì)進(jìn)行硬件平臺(tái)的初始化,loading trusted OS等等,等到完成了secure world的構(gòu)建之后,把控制權(quán)轉(zhuǎn)交給non-secure world,這時(shí)候,CPU core多半處于EL2(如果支持虛擬化)或者EL1(不支持虛擬化)。因此,對(duì)于linux kernel而言,它感知不到secure world(linux kernel一般也不會(huì)做Trusted OS),僅僅是在non-secure world中呼風(fēng)喚雨,可以是Hypervisor或者rich OS。
3、set_cpu_boot_mode_flag
在進(jìn)入這個(gè)函數(shù)的時(shí)候,有一個(gè)前提條件:w20寄存器保存了cpu啟動(dòng)時(shí)候的Eexception level ,具體代碼如下:
ENTRY(set_cpu_boot_mode_flag)?
??? adr_l??? x1, __boot_cpu_mode?
??? cmp??? w20, #BOOT_CPU_MODE_EL2?
??? b.ne??? 1f?
??? add??? x1, x1, #4?
1:??? str??? w20, [x1]??????????? // This CPU has booted in EL1?
??? dmb??? sy?
??? dc??? ivac, x1??????????? // Invalidate potentially stale cache line?
??? ret?
ENDPROC(set_cpu_boot_mode_flag)
由于系統(tǒng)啟動(dòng)之后仍然需要了解cpu啟動(dòng)時(shí)候的Eexception level,因此,有一個(gè)全局變量__boot_cpu_mode用來(lái)保存啟動(dòng)時(shí)候的CPU mode。代碼很簡(jiǎn)單,大家自行體會(huì)就OK了。
4、__vet_fdt
在進(jìn)入具體函數(shù)之前,x21和x24都被設(shè)定成了指定的值。x21被設(shè)定為fdt在RAM中的物理地址(參考preserve_boot_args函數(shù)),x24被設(shè)定為_(kāi)_PHYS_OFFSET,定義為:
#define __PHYS_OFFSET??? (KERNEL_START - TEXT_OFFSET)
#define KERNEL_START??? _text
KERNEL_START是kernel開(kāi)始運(yùn)行的虛擬地址,更確切的說(shuō)是內(nèi)核正文段開(kāi)始的虛擬地址。 在鏈接腳本文件中(參考arch/arm64/kernel下的vmlinux.lds.S),KERNEL_START被設(shè)定為:
. = PAGE_OFFSET + TEXT_OFFSET;
.head.text : {?
??? _text = .;?
??? HEAD_TEXT?
}
因此,KERNEL_START的值和PAGE_OFFSET以及TEXT_OFFSET這兩個(gè)offset的設(shè)定有關(guān)。TEXT_OFFSET標(biāo)識(shí)了內(nèi)核正文段的offset,其實(shí)如果該宏被定義為KERNEL_TEXT_OFFSET會(huì)更好理解。我們知道,操作系統(tǒng)運(yùn)行在內(nèi)核空間,應(yīng)用程序運(yùn)行在用戶空間,假設(shè)內(nèi)核空間的首地址是x(一般也是RAM的首地址),那么是否讓kernel運(yùn)行在x地址呢?對(duì)于arm,在內(nèi)核空間的開(kāi)始有32kB(0x00008000)的空間用于保存內(nèi)核的頁(yè)表(也就是進(jìn)程0的PGD)以及bootload和kernel之間參數(shù)的傳遞,對(duì)于ARM64,在其Makefile中定義了這個(gè)offset是512KB(0x00080000)。
ifeq ($(CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET), y)?
TEXT_OFFSET := $(shell awk 'BEGIN {srand(); printf "0x%03x000\n", int(512 * rand())}')?
else?
TEXT_OFFSET := 0x00080000?
endif
CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET是randomize內(nèi)核的啟動(dòng)地址,估計(jì)是防止黑客入侵之類的,我們這里簡(jiǎn)化處理,就是固定為0x00080000好了。
搞定了TEXT_OFFSET,我們?cè)賮?lái)看看PAGE_OFFSET,在arch/arm64/include/asm/memory.h中,PAGE_OFFSET被定義為:
#define VA_BITS??????????? (CONFIG_ARM64_VA_BITS)?
#define PAGE_OFFSET??????? (UL(0xffffffffffffffff) << (VA_BITS - 1))
VA_BITS定義了用戶空間虛擬地址的bit數(shù)(該值也就是定義了用戶態(tài)程序能夠訪問(wèn)的地址空間的size),假設(shè)VA_BITS被設(shè)定為39個(gè)bit,那么PAGE_OFFSET就是0xffffffc0-00000000。PAGE_OFFSET的名字也不好(個(gè)人觀點(diǎn),可能有誤),OFFSET表明的是一個(gè)偏移,內(nèi)核空間被劃分成一個(gè)個(gè)的page,PAGE_OFFSET看起來(lái)應(yīng)該是定義以page為單位的偏移。但是,以什么為基準(zhǔn)的偏移呢?PAGE_OFFSET的名字中沒(méi)有給出,當(dāng)然實(shí)際上,這個(gè)符號(hào)是定義以整個(gè)address space的起始地址(也就是0)為基準(zhǔn)。另外,雖然這個(gè)地址是要求page對(duì)齊,但是實(shí)際上,這個(gè)符號(hào)仍然定義的是虛擬地址的offset(而不是page的offset)。根據(jù)上面的理由,我覺(jué)得定義成KERNEL_IMG_OFFSET會(huì)更好理解一些。一句話總結(jié):PAGE_OFFSET定義了將kernel image安放在虛擬地址空間的哪個(gè)位置上。
OK,經(jīng)過(guò)漫長(zhǎng)的說(shuō)明之后,__PHYS_OFFSET實(shí)際上就是kernel image的首地址(并不是__PHYS_OFFSET的位置開(kāi)始就是真實(shí)的kernel image,實(shí)際上從__PHYS_OFFSET開(kāi)始,首先是TEXT_OFFSET的保留區(qū)域,然后才是真正的kernel image)。實(shí)際上,__PHYS_OFFSET定義的是一個(gè)虛擬地址而不是物理地址,這里的PHYS嚴(yán)重影響了該符號(hào)的含義,實(shí)際上adrp這條指令可以將一個(gè)虛擬地址轉(zhuǎn)換成物理地址(在沒(méi)有打開(kāi)MMU的時(shí)候)。而函數(shù)__vet_fdt主要是對(duì)這個(gè)bootloader傳遞給kernel的fdt參數(shù)進(jìn)行驗(yàn)證,看是否OK,主要驗(yàn)證的內(nèi)容包括:
(1)是否是8字節(jié)對(duì)齊的
(2)是否在kernel space的前512M內(nèi)
__vet_fdt:?
??? tst??? x21, #0x7----是否是8字節(jié)對(duì)齊的?
??? b.ne??? 1f?
??? cmp??? x21, x24-----是否在小于kernel space的首地址?
??? b.lt??? 1f?
??? mov??? x0, #(1 << 29)?
??? add??? x0, x0, x24?
??? cmp??? x21, x0?
??? b.ge??? 1f-------是否大于kernel space的首地址+512M?
??? ret?
1:?
??? mov??? x21, #0----------傳遞的fdt地址有誤,清零?
??? ret?
ENDPROC(__vet_fdt)
四、參考文獻(xiàn)
1、Documentation/arm64/booting.txt
2、ARM Architecture Reference Manual
change log:
1、2015-11-30,增加對(duì)el2_setup的思考。
2、2015-12-1,(1)修正對(duì)PAGE_OFFSET的描述。(2)增加adrp和adr_l的描述(3)增加對(duì)__PHYS_OFFSET符號(hào)名字的置疑
原文地址: http://www.wowotech.net/linux_kenrel/arm64_initialize_1.html
總結(jié)
以上是生活随笔為你收集整理的ARM64的启动过程之(一):内核第一个脚印的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 图解Android - Zygote,
- 下一篇: ARM64的启动过程之(二):创建启动阶