linux aarch64 head.S enable_mmu primary_switch primary_switched
enable_mmu
输入
X0 SCTRL_EL1 的值,见 setup_cpu 的返回值。
X1 TTBT1_EL1 的值, 见 primary_switch 964 行设置 x1
以下内容来自 https://blog.csdn.net/lgjjeff/article/details/93376624
797 ~ 800 读取ID_AA64MMFR0_EL1寄存器的值,该寄存器的定义如下。bit [31:20]表示当前支持的地址转换粒度(即页的大小),此处读取该寄存器就是为了校验寄存器中的值是否支持内核中配置的页表size。
将提取的位与内核中配置的页大小值比较
若它们不相等,说明内核配置页大小不被支持,则跳到__no_granule_support 函数处
801 该函数用于将启动状态值0赋值给变量__early_cpu_boot_status,以给后续代码使用
802 ~ 805 将idmap页表的PGD基地址idmap_pg_dir写入寄存器ttbr0_el1中。将其存入ttbr0_el1,保证内核 MMU 刚启动VA 转换时,预取的地址和指令依然有效,可以被执行。
806 ~ 807 将swapper页表的PGD基地址swapper_pg_dir写入寄存器ttbr1_el1中
809 设置sctlr_el1寄存器,其中x0在__cpu_setup函数中通过mov_q x0, SCTLR_EL1_SET设置,
SCTLR_EL1_SET定义在arch/arm64/include/asm/sysreg.h中,具体内容可以参考kernel源码。该寄存器是系统控制寄存器,其定义如下。其中第0位M即是用来使能MMU的,因此在这条命令执行完成后MMU即被使能。
784/* 785 * Enable the MMU. 786 * 787 * x0 = SCTLR_EL1 value for turning on the MMU. 788 * x1 = TTBR1_EL1 value 789 * 790 * Returns to the caller via x30/lr. This requires the caller to be covered 791 * by the .idmap.text section. 792 * 793 * Checks if the selected granule size is supported by the CPU. 794 * If it isn't, park the CPU 795 */ 796SYM_FUNC_START(__enable_mmu) 797 mrs x2, ID_AA64MMFR0_EL1 798 ubfx x2, x2, #ID_AA64MMFR0_TGRAN_SHIFT, 4 799 cmp x2, #ID_AA64MMFR0_TGRAN_SUPPORTED 800 b.ne __no_granule_support 801 update_early_cpu_boot_status 0, x2, x3 802 adrp x2, idmap_pg_dir 803 phys_to_ttbr x1, x1 804 phys_to_ttbr x2, x2 805 msr ttbr0_el1, x2 // load TTBR0 806 offset_ttbr1 x1, x3 807 msr ttbr1_el1, x1 // load TTBR1 808 isb 809 msr sctlr_el1, x0 810 isb 811 /* 812 * Invalidate the local I-cache so that any instructions fetched 813 * speculatively from the PoC are discarded, since they may have 814 * been dynamically patched at the PoU. 815 */ 816 ic iallu 817 dsb nsh 818 isb 819 ret 820SYM_FUNC_END(__enable_mmu)
primary_switch
下面是 CONFIG_RANDOMIZE_BASE CONFIG_RELOCATABLE 都没有 配置时的场景。
963 ~ 965 准备和调用 enable_mmu
998 ~ 1000 调用 primary_switched
958SYM_FUNC_START_LOCAL(__primary_switch) 963 964 adrp x1, init_pg_dir 965 bl __enable_mmu 998 ldr x8, =__primary_switched 999 adrp x0, __PHYS_OFFSET 1000 br x8 1001SYM_FUNC_END(__primary_switch)
primary_switched
以下来自 https://blog.csdn.net/lgjjeff/article/details/93376624
输入 x0 __PHYS_OFFSET
417 ~ 420 代码用于设置当前栈指针和el0的栈指针
426 ~ 428 设置异常向量表
430 将lr地址入栈
431 保存栈帧指针
437 将dtb地址从x21寄存器保存到__fdt_pointer变量中,以给后续代码使用。该值是在head.s开始时由x0寄存器暂存到x21寄存器的
439 ~ 441 计算kernel image起始地址相对于内存起始地址的偏移,并将其存放到kimage_voffset变量中
443 ~ 449 清空bss段的内容
465 ~ 467 更新栈指针,并清除lr和fp指针寄存器的值,为跳转到start_kernel做准备
468 跳转到C语言函数start_kernel
411/* 412 * The following fragment of code is executed with the MMU enabled. 413 * 414 * x0 = __PHYS_OFFSET 415 */ 416SYM_FUNC_START_LOCAL(__primary_switched) 417 adrp x4, init_thread_union 418 add sp, x4, #THREAD_SIZE 419 adr_l x5, init_task 420 msr sp_el0, x5 // Save thread_info 421 422#ifdef CONFIG_ARM64_PTR_AUTH 423 __ptrauth_keys_init_cpu x5, x6, x7, x8 424#endif 425 426 adr_l x8, vectors // load VBAR_EL1 with virtual 427 msr vbar_el1, x8 // vector table address 428 isb 429 430 stp xzr, x30, [sp, #-16]! 431 mov x29, sp 432 433#ifdef CONFIG_SHADOW_CALL_STACK 434 adr_l scs_sp, init_shadow_call_stack // Set shadow call stack 435#endif 436 437 str_l x21, __fdt_pointer, x5 // Save FDT pointer 438 439 ldr_l x4, kimage_vaddr // Save the offset between 440 sub x4, x4, x0 // the kernel virtual and 441 str_l x4, kimage_voffset, x5 // physical mappings 442 443 // Clear BSS 444 adr_l x0, __bss_start 445 mov x1, xzr 446 adr_l x2, __bss_stop 447 sub x2, x2, x0 448 bl __pi_memset 449 dsb ishst // Make zero page visible to PTW 450 451#ifdef CONFIG_KASAN 452 bl kasan_early_init 453#endif 465 add sp, sp, #16 466 mov x29, #0 467 mov x30, #0 468 b start_kernel 469SYM_FUNC_END(__primary_switched)