分析
开始调用
primary_entry执行完保存参数的函数后,进入el2_setup函数
bl el2_setup // Drop to EL1, w0=cpu_boot_mode
查看调用函数的注释,含义是切换到EL1模式,出参为w0,保存进入内核时的异常等级。
看到这个注释,我联想起异常返回章节的通过sppr_elX和elr_elX切换异常等级的方法,我们分析代码看看是否这样实现的。
该函数较长,我们分段进行欣赏
分析注释
我们先看该函数的注释
.section ".idmap.text","awx"
/*
* 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 w0 if
* booted in EL1 or EL2 respectively.
*/
这表明,el2_setup在一个新的段里面,该段我们可以从vmlinux.lds看出端倪。
查看.idmap.text在内核镜像的位置
.text : {
_stext = .;
. = ALIGN(8); __irqentry_text_start = .; *(.irqentry.text) __irqentry_text_end = .;
. = ALIGN(8); __softirqentry_text_start = .; *(.softirqentry.text) __softirqentry_text_end = .;
. = ALIGN(8); __entry_text_start = .; *(.entry.text) __entry_text_end = .;
. = ALIGN(8); *(.text.hot .text.hot.*) *(.text .text.fixup) *(.text.unlikely .text.unlikely.*) *(.text.unknown .text.unknown.*) . = ALIGN(8); __noinstr_text_start = .; *(.noinstr.text) __noinstr_text_end = .; *(.text..refcount) *(.ref.text) *(.text.asan.* .text.tsan.*)
. = ALIGN(8); __sched_text_start = .; *(.sched.text) __sched_text_end = .;
. = ALIGN(8); __cpuidle_text_start = .; *(.cpuidle.text) __cpuidle_text_end = .;
. = ALIGN(8); __lock_text_start = .; *(.spinlock.text) __lock_text_end = .;
. = ALIGN(8); __kprobes_text_start = .; *(.kprobes.text) __kprobes_text_end = .;
. = ALIGN(0x00001000); __hyp_idmap_text_start = .; *(.hyp.idmap.text) __hyp_idmap_text_end = .; __hyp_text_start = .; *(.hyp.text) . = ALIGN(0x00000008); __start___kvm_ex_table = .; *(__kvm_ex_table) __stop___kvm_ex_table = .; __hyp_text_end = .;
. = ALIGN(0x00001000); __idmap_text_start = .; *(.idmap.text) __idmap_text_end = .;
. = ALIGN(0x00001000); __hibernate_exit_text_start = .; *(.hibernate_exit.text) __hibernate_exit_text_end = .;
. = ALIGN((1 << 12)); __entry_tramp_text_start = .; *(.entry.tramp.text) . = ALIGN((1 << 12)); __entry_tramp_text_end = .;
*(.fixup)
*(.gnu.warning)
. = ALIGN(16);
*(.got)
}
这说明,.idmap.text是.text的子段,至于为什么将el2_setup放到.text里面我也不清楚,但是我搜寻内核代码发现该函数多次被调用,所以猜测可能是该函数在后续阶段,也就是说MMU创建后也需要使用吧。比如
.pushsection ".idmap.text", "awx"
SYM_CODE_START(cpu_resume)
bl el2_setup // if in EL2 drop to EL1 cleanly
bl __cpu_setup
/* enable the MMU early - so we can access sleep_save_stash by va */
adrp x1, swapper_pg_dir
bl __enable_mmu
ldr x8, =_cpu_resume
br x8
SYM_CODE_END(cpu_resume)