- bootloader在跳转到kernel前,需要确保如下设置:
MMU = off, D-cache = off, I-cache = on or off
x0 = physical address to the FDT blob
- kernel的入口在arch\arm64\kernel\head.S中。
b
stext // 跳转到stext
- stext。
ENTRY(stext)
mov x21, x0 // x21=FDT 将x0的值(device tree的地址)暂存在x21寄存器中
bl el2_setup // Drop to EL1, w20=cpu_boot_mode 从EL2或者non-secure EL1回退到EL1;bl 带返回的跳转指令
// 补充知识:ARMv8-a 划分了4 个Exception level,EL0归属于non-privilege level,EL1/2/3属于privilege level。Application位于特权等级最低的EL0,Guest OS(Linux kernel、window等)位于EL1,提供虚拟化支持的Hypervisor位于EL2(可选),提供Security支持的Seurity Monitor位于EL3(可选)。EL0,EL1,EL2,El3之前的切换,分别通过指令svc,hvc,smc。
bl __calc_phys_offset // x24=PHYS_OFFSET, x28=PHYS_OFFSET-PAGE_OFFSET 计算出起始物理地址并保存在x24中
bl set_cpu_boot_mode_flag // 将cpu启动的模式保存到全局变量__boot_cpu_mode中
mrs x22, midr_el1 // Move from State register to Register,x22=cpuid 获取当前cpu id
mov x0, x22 // 将当前cpuid作为参数传递给lookup_processor_type
bl lookup_processor_type // 查看cpu类型
mov x23, x0 // x23=current cpu_table
/*
* __error_p may end up out of range for cbz if text areas are
* aligned up to section sizes.
*/
cbnz x23, 1f // invalid processor (x23=0)? // x23非0,跳转到标号1处
b __error_p
1:
bl __vet_fdt // 检查device tree的合法性
bl __create_page_tables // x25=TTBR0, x26=TTBR1 // 创建临时页表
/*
* The following calls CPU specific code in a position independent
* manner. See arch/arm64/mm/proc.S for details. x23 = base of
* cpu_info structure selected by lookup_processor_type above.
* On return, the CPU will be ready for the MMU to be turned on and
* the TCR will have been set.
*/
ldr x27, __switch_data // address to jump to after // 由函数__enable_mmu中调用
// MMU has been enabled
adrp lr, __enable_mmu // return (PIC) address //
// adr伪指令用于将一个地址加载到寄存器,取到的是相对于PC寄存器的地址,由于此刻PC寄存器中值是物理地址,所以lr中取到的即是标号
__enable_mmu
处的物理地址(页对齐)。
add lr, lr, #:lo12:__enable_mmu // 加上
__enable_mmu的页内偏移
ldr x12, [x23, #CPU_INFO_SETUP] // x23保存的是
cpu_table,
CPU_INFO_SETUP =
offsetof(struct cpu_info, cpu_setup),那么x12就等于
__cpu_setup
add x12, x12, x28 // __virt_to_phys // 转化成物理地址
br x12 // initialise processor // 跳转到
__cpu_setup继续执行,由于lr等于
__enable_mmu,因此从
__cpu_setup时,会跳转到
__enable_mmu继续执行
ENDPROC(stext)
- el2_setup。
/*
* If we're fortunate enough to boot at EL2, ensure that the world is
* sane before dropping to EL1.
*
* Returns either BOOT_CPU_MODE_EL1 or BOOT_CPU_MODE_EL2 in x20 if
* booted in EL1 or EL2 respectively.
*/
ENTRY(el2_setup)
mrs x0, CurrentEL // 当前的exception level保存在PSTATE中,程序可以通过MRS或者MSR来访问PSTATE,CurrentEL就是获取PSTATE中current exception level域的特殊寄存器。
// 补充知识:MRS Move to Register from State register, MSR Move from State register to Register, 分别用于对状态寄存器(AARCH64对应PSTATE,AARCH32对应CPSP)进行读写。
cmp x0, #CurrentEL_EL2 // #define CurrentEL_EL2 (2 << 2)
b.ne 1f // 当cpu不处于EL2时跳转到标号1
mrs x0, sctlr_el2 // 读取EL2状态的系统控制寄存器,该寄存器可以控制整个系统的行为
CPU_BE( orr x0, x0, #(1 << 25) ) // Set the EE bit for EL2 // 大端置位EE位
CPU_LE( bic x0, x0, #(1 << 25) ) // Clear the EE bit for EL2
// 小端清除EE位
msr sctlr_el2, x0 // 写入
EL2状态的系统控制寄存器
b 2f
1: mrs x0, sctlr_el1
// 读取EL1状态的系统控制寄存器
CPU_BE( orr x0, x0, #(3 << 24) ) // Set the EE and E0E bits for EL1 // 大端置位EE位和EOE位
CPU_LE( bic x0, x0, #(3 << 24) ) // Clear the EE and E0E bits for EL1 // 小端清除EE位和EOE位
msr sctlr_el1, x0
// 写入
EL1状态的系统控制寄存器
mov w20, #BOOT_CPU_MODE_EL1 // This cpu booted in EL1 // #define BOOT_CPU_MODE_EL1 (0xe11) // 将
BOOT_CPU_MODE_EL1保存到通用寄存器w20
isb // 指令同步屏障,确保之前的指令已执行完成
ret // 当cpu处于EL1,在这里就返回了
/* Hyp configuration. */
2: mov x0, #(1 << 31) // 64-bit EL1
msr hcr_el2, x0 // Hypervisor
Configuration
Register // Controls virtualization settings and trapping of exceptions to
EL2