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)