3.8.3. primary_switch

__primary_switch:
#ifdef CONFIG_RANDOMIZE_BASE
    mov     x19, x0                             //上一步__cpu_setup中,x0保存了SCTLR_EL1_SET,
                                //此处将x0中的值保存到x19中
    mrs     x20, sctlr_el1                  //将sctlr_el1保存到x20中
#endif



    adrp    x1, init_pg_dir     //将kernel image映射的页表地址保存到x1中
    bl      __enable_mmu            //打开MMU

__enable_mmu 定义如下

主要完成以下任务

  • 将idmap_pg_dir的物理地址映射到TTBR0;

  • 将init_pg_dir的物理地址设置到TTBR1

  • 用SCTLR_EL1_SET来设置sctlr_el1来开启MMU

/*
 * Enable the MMU.
 *
 *  x0  = SCTLR_EL1 value for turning on the MMU.
 *  x1  = TTBR1_EL1 value
 *
 * Returns to the caller via x30/lr. This requires the caller to be covered
 * by the .idmap.text section.
 *
 * Checks if the selected granule size is supported by the CPU.
 * If it isn't, park the CPU
 */
SYM_FUNC_START(__enable_mmu)
        mrs     x2, ID_AA64MMFR0_EL1
        ubfx    x2, x2, #ID_AA64MMFR0_TGRAN_SHIFT, 4
        cmp     x2, #ID_AA64MMFR0_TGRAN_SUPPORTED
        b.ne    __no_granule_support
        update_early_cpu_boot_status 0, x2, x3
        adrp    x2, idmap_pg_dir
        phys_to_ttbr x1, x1
        phys_to_ttbr x2, x2
        msr     ttbr0_el1, x2                   // load TTBR0
        offset_ttbr1 x1, x3
        msr     ttbr1_el1, x1                   // load TTBR1
        isb
        msr     sctlr_el1, x0
        isb
        /*
         * Invalidate the local I-cache so that any instructions fetched
         * speculatively from the PoC are discarded, since they may have
         * been dynamically patched at the PoU.
         */
        ic      iallu
        dsb     nsh
        isb
        ret
SYM_FUNC_END(__enable_mmu)

重定位kernel

#ifdef CONFIG_RELOCATABLE
    bl      __relocate_kernel
#endif

__relocate_kernel片段1

/*
 * Iterate over each entry in the relocation table, and apply the
 * relocations in place.
 */
ldr     w9, =__rela_offset              // offset to reloc table
ldr     w10, =__rela_size               // size of reloc table

#arch/arm64/kernel/vmlinux.lds.S中定义
.rela.dyn : ALIGN(8) {
        *(.rela .rela*)
}
__rela_offset   = ABSOLUTE(ADDR(.rela.dyn) - KIMAGE_VADDR);
__rela_size     = SIZEOF(.rela.dyn);

.rela.dyn区域的起始地址相对kernel image的偏移以及大小w9, w10中

__relocate_kernel片段2

mov_q   x11, KIMAGE_VADDR               //x11保存了KIMAGE_VADDR(__text链接地址),是Kernel Image的起始虚拟地址
add     x11, x11, x23                   //通过与之计算的x23(保存了__text 2M对齐的偏移量,一般为0),进行2M对齐

add     x9, x9, x11                     //x9保存了.rela.dyn区域的链接地址
add     x10, x9, x10                    //x10保存了.rela.dyn的结束地址

__relocate_kernel片段3

0:      cmp     x9, x10
        b.hs    1f
        ldp     x12, x13, [x9], #24             //获取entry的link addr和link flag
        ldr     x14, [x9, #-8]                  //获取entry的link value
        cmp     w13, #R_AARCH64_RELATIVE        //判断link flag, 获取重定位类型
        b.ne    0b
        add     x14, x14, x23                // relocate
        str     x14, [x12, x23]             //执行重定位,用link value 修改link addr中的值
        b       0b                          //通过循环将所有的动态链接符号执行重定位

1:
        ret

备注

  • 重定位是连接符号引用与符号定义的过程。例如,程序调用函数时,关联的调用指令必须在执行时将控制权转移到正确的目标地址。

可重定位文件必须包含说明如何修改其节内容的信息。通过此信息,可执行文件和共享目标文件可包含进程的程序映像的正确信息。重定位项即这些数据.

  • 什么是.rela.dyn段?该节保存的是重定位信息,数据内容是包含带有显示加数的重定位条目,每个条目固定大小(24个字节). .rela.dyn在动态链接 的目标文件中保存的是需要被重定位的变量数据

  • .rela.dyn中包含很多entry, 每个entry有24个字节,用于描述一个可重定位符号

typedef struct {
    Elf64_Addr r_offset;  // 需要修改的地址(偏移量)
    Elf64_Xword r_info;   // 符号信息和重定位类型
    Elf64_Sxword r_addend; // 添加的常量值(如果有的话)
} Elf64_Rela;


+----------------+-----------------------------------+-----------------
|     64 bit     |      64 bit     |      64 bit     |     ...
+----------------+-----------------------------------+-----------------
| sym0 link addr | sym0 reloc flag | sym0 link value | sym1 link ...
+----------------+-----------------------------------+-----------------

备注

对于ARM64架构,常见的重定位类型包括

  • R_AARCH64_ABS64: 64位绝对地址

  • R_AARCH64_GLOB_DAT: 全局数据符号

  • R_AARCH64_JUMP_SLOT: 跳转插槽,通常用于函数指针

  • R_AARCH64_RELATIVE: 与程序基地址相关的重定位

通过反编译vmlinux,可以看到.rela.dyn段的内容

4485578 ffff800011280e20 <.rela.dyn>:
4485579 ffff800011280e20:       10044768        .word   0x10044768     //link addr
4485580 ffff800011280e24:       ffff8000        .word   0xffff8000
4485581 ffff800011280e28:       00000403        .word   0x00000403     //reloc flag
4485582 ffff800011280e2c:       00000000        .word   0x00000000
4485583 ffff800011280e30:       10047dd8        .word   0x10047dd8     //link value
4485584 ffff800011280e34:       ffff8000        .word   0xffff8000
4485585 ffff800011280e38:       10044770        .word   0x10044770
4485586 ffff800011280e3c:       ffff8000        .word   0xffff8000
4485587 ffff800011280e40:       00000403        .word   0x00000403
.......
    ldr     x8, =__primary_switched
    adrp    x0, __PHYS_OFFSET
    br      x8
ENDPROC(__primary_switch)