SYM_INNER_LABEL(init_el1, SYM_L_LOCAL)
mov_q x0, INIT_SCTLR_EL1_MMU_OFF
msr sctlr_el1, x0
isb '因为前面修改了系统控制器'
mov_q x0, INIT_PSTATE_EL1
msr spsr_el1, x0
msr elr_el1, lr
mov w0, #BOOT_CPU_MODE_EL1
eret 'Return from exception'
SYM_INNER_LABEL(init_el2, SYM_L_LOCAL) --- 'EL2切向EL1'
......
msr elr_el1, x0
eret
1:
......
mov w0, #BOOT_CPU_MODE_EL2
eret
__cpu_stick_to_vhe:
mov x0, #HVC_VHE_RESTART
hvc #0
mov x0, #BOOT_CPU_MODE_EL2
ret
SYM_FUNC_END(init_kernel_el)
2.3 set_cpu_boot_mode_flag
??根据w0中传递的cpu启动模式设置__boot_cpu_mode标志。
/*
* Sets the __boot_cpu_mode flag depending on the CPU boot mode passed
* in w0. See arch/arm64/include/asm/virt.h for more info.
*/
SYM_FUNC_START_LOCAL(set_cpu_boot_mode_flag)
adr_l x1, __boot_cpu_mode //x1记录__boot_cpu_mode[]的地址
cmp w0, #BOOT_CPU_MODE_EL2 //w0记录启动时的异常等级
b.ne 1f //如果不是从EL2启动,则跳转到1处
add x1, x1, #4 // 如果是从EL2启动,地址指向__boot_cpu_mode[1]
1: str w0, [x1] // Save CPU boot mode 保存启动模式到x1指向的地址,如果是从EL1启动,地址指向__boot_cpu_mode[0]
dmb sy // 保证str指令执行完成
dc ivac, x1 // Invalidate potentially stale cache line 使高速缓存失效
ret
SYM_FUNC_END(set_cpu_boot_mode_flag)
2.4 __create_page_tables
/*
* Setup the initial page tables. We only setup the barest amount which is
* required to get the kernel running. The following sections are required:
* - identity mapping to enable the MMU (low address, TTBR0) (1)恒等映射
* - first few MB of the kernel linear mapping to jump to once the MMU has
* been enabled (2)内核image映射
*/
SYM_FUNC_START_LOCAL(__create_page_tables)
...
SYM_FUNC_END(__create_page_tables)
2.4.1 保存LR值
mov x28, lr //#把LR的值存放到X28
2.4.2 使初始化页表无效、并清空初始化页表
/*
* Invalidate the init page tables to avoid potential dirty cache lines
* being evicted. Other page tables are allocated in rodata as part of
* the kernel image, and thus are clean to the PoC per the boot
* protocol.
*/
adrp x0, init_pg_dir //把init_pg_dir的物理地址赋值给x0
adrp x1, init_pg_end //把init_pg_end的物理地址赋值给x1
bl dcache_inval_poc //把init_pg_dir页表对应的高速缓存清掉(入参是x0和x1)
/*
* Clear the init page tables.//把这个页表内容清零
*/
adrp x0, init_pg_dir
adrp x1, init_pg_end
sub x1, x1, x0
1: stp xzr, xzr, [x0], #16 //xzr是零寄存器
stp xzr, xzr, [x0], #16
stp xzr, xzr, [x0], #16
stp xzr, xzr, [x0], #16
subs x1, x1, #64
b.ne 1b
(1)init_pg_dir和init_pg_end定义在arch/arm64/kernel/vmlinux.lds.S链接文件中:
#arch/arm64/kernel/vmlinux.lds.S
. = ALIGN(PAGE_SIZE);
init_pg_dir = .;
. += INIT_DIR_SIZE;
init_pg_end = .;
(2)adrp指令
作用:以页为单位的大范围的地址读取指令,这里的P就是page的意思。
原理:符号扩展一个21位的offset(immhi+immlo), 向左移动12位,PC的值的低12位清零,然后把这两者相加,结果写入到Xd寄存器,用来得到一块含有lable的4KB对齐内存区域的base地址(也就是说lable所在的地址,一定落在这个4KB的内存区域里,指令助记符里Page也就是这个意思), 可用来寻址 +/- 4GB的范围(2^33次幂)。
通俗来讲,ADRP指令就是先进行PC+imm(偏移值)。然后找到lable所在的一个4KB的页,然后取得label的基址,再进行偏移去寻址。
ADRP {cond} Rd label
其中:Rd加载的目标寄存器。lable为地址表达式。
(3)使用adrp指令获取init_pg_dir和init_pg_end的地址,页大小为4KB,由于内核启动的时候MMU还未打开(PC为物理地址),因此此时获取的地址也为物理地址。
(4)adrp通过当前PC地址的偏移地址计算目标地址,和实际的物理地址无关,因此属于位置无关码。
?
2.4.3 保存SWAPPER_MM_MMUFLAGS到x7寄存器
mov_q x7, SWAPPER_MM_MMUFLAGS
SWAPPER_MM_MMUFLAGS宏描述了段映射的属性,它实现在arch/arm64/include/asm/kernel-pgtable.h头文件中:
/*
* Initial memory map attributes.
*/
#define SWAPPER_PTE_FLAGS (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED | PTE_UXN)
#define SWAPPER_PMD_FLAGS (PMD_TYPE_SECT