kernel启动流程-head.S的执行__8._primary_switch

1. 前言

kernel版本:5.10
平台:arm64

本专题主要基于《arm64_linux head.S的执行流程》系列文章,前者是基于3.18,本专题针对的是内核5.10。主要分析head.S的执行过程。本文主要记录head.S的__primary_switch执行过程。

2. __primary_switch

2.1 保存设定值SCTLR_EL1_SET

#ifdef CONFIG_RANDOMIZE_BASE                                                                                                                                                         
        mov     x19, x0                         // preserve new SCTLR_EL1 value                                                                                                      
        mrs     x20, sctlr_el1                  // preserve old SCTLR_EL1 value                                                                                                      
#endif

上一步从__cpu_setup返回前,x0保存了SCTLR_EL1_SET,此处将x0保存到x19中,并将原来的sctlr_el1保存到x20中

2.2 __enable_mmu

        adrp    x1, init_pg_dir                                                                                                                                                      
        bl      __enable_mmu   

__enable_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)

__enable_mmu主要完成如下的工作:

  1. 将idmap_pg_dir的物理地址设置到TTBR0;idmap_pg_dir为kernel image一致性页表的起始虚拟地址;
  2. 将init_pg_dir的物理地址设置到TTBR1;init_pg_dir为kernel image页表的起始虚拟地址
  3. 用SCTLR_EL1_SET设置sctlr_el1来开启MMU

Documentation/arm64/memory.rst:
User addresses have bits 63:48 set to 0 while the kernel addresses have the same bits set to 1. TTBRx selection is given by bit 63 of the virtual address. The swapper_pg_dir contains only kernel (global) mappings while the user pgd contains only user (non-global) mappings.The swapper_pg_dir address is written to TTBR1 and never written to TTBR0.

如上由于idmap_pg页表中虚拟地址与物理地址相同,自然bit63为0,因此CPU访存时会使用TTBR0中保存的PGD页表基址;而init_pg页表中使用的虚拟地址是链接地址,自然bit63为1,因此CPU访存时会使用TTBR1中保存的PGD页表基址

2.3 __relocate_kernel

#ifdef CONFIG_RELOCATABLE                                                                                                                                                                                                                                                                                                                                         
        bl      __relocate_kernel                                                                                                                                                                                                                                                                                                                  
#endif              

__relocate_kernel定义如下(红色代码):

__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

__rela_offset和__rela_size在vmlinux.lds.S中定义,代表重定位段的起始地址和大小

#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               // default virtual offset
        add     x11, x11, x23                   // actual virtual offset
        add     x9, x9, x11                     // __va(.rela)
        add     x10, x9, x10                    // __va(.rela) + sizeof(.rela)

x11保存了KIMAGE_VADDR地址,KIMAGE_VADDR为_text的链接地址,为kernel image的起始虚拟地址,通过与之间计算的x23(保存了2M对齐的偏移)相加,进行2M对齐;
x9保存了.rela.dyn区域的链接地址
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:
#endif
        ret

通过循环将可所有的动态链接符号执行重定位

引自:https://www.entry0.cn/2020/05/09/771.html
什么是重定位?
重定位是连接符号引用与符号定义的过程。例如,程序调用函数时,关联的调用指令必须在执行时将控制权转移到正确的目标地址。可重定位文件必须包含说明如何修改其节内容的信息。通过此信息,可执行文件和共享目标文件可包含进程的程序映像的正确信息。重定位项即是这些数据。
什么是.rela.dyn段?
该节保存的是重定位信息,数据内容是包含带有显式加数的重定位条目,每个条目固定大小(24个字节)。.rela.dyn在动态链接的目标文件中保存的是需要被重定位的变量数据。

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

 4485578 ffff800011280e20 <.rela.dyn>:
 4485579 ffff800011280e20:       10044768        .word   0x10044768
 4485580 ffff800011280e24:       ffff8000        .word   0xffff8000
 4485581 ffff800011280e28:       00000403        .word   0x00000403
 4485582 ffff800011280e2c:       00000000        .word   0x00000000
 4485583 ffff800011280e30:       10047dd8        .word   0x10047dd8
 4485584 ffff800011280e34:       ffff8000        .word   0xffff8000
 4485585 ffff800011280e38:       10044770        .word   0x10044770
 4485586 ffff800011280e3c:       ffff8000        .word   0xffff8000
 4485587 ffff800011280e40:       00000403        .word   0x00000403
 .......

可以看出.rela.dyn段包含很多的entry,每个entry有24个字节构成,用于描述一个可重定位符号:

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

如上例中:

4485585 ffff800011280e38:       10044770        .word   0x10044770
4485586 ffff800011280e3c:       ffff8000        .word   0xffff8000
4485587 ffff800011280e40:       00000403        .word   0x00000403

组成了一个entry,sym link addr, sym reloc flag, sym link value分别为:
0x10044770, 0xffff8000, 0x00000403

__relocate_kernel片段4

ldr     x8, =__primary_switched
adrp    x0, __PHYS_OFFSET
br      x8

__primary_switch的最后将跳转到__primary_switched执行,首先它会通过adrp将__PHYS_OFFSET的地址(kernel image物理地址)保存到x0作为__primary_switched的参数

3. 总结

__primary_switch主要完成了如下的工作:

  1. 使能MMU,使能之前会分别用idmap_pg_dir和init_pag_dir设置TTBR0和TTBR1;
  2. 将kernel image的.rela.dyn段实现重定位
  3. 跳转到__primary_switched执行

参考文档

  1. ELF for the ARM® 64-bit Architecture (AArch64)
  2. https://www.entry0.cn/2020/05/09/771.html
    ELF的.RELA.DYN节
  3. https://amir.rachum.com/blog/2016/09/17/shared-libraries/
    Shared Libraries: Understanding Dynamic Loading
  4. arm64_linux启动流程分析07_开启MMU切换到虚拟地址
  5. http://sp1.wikidot.com/elfobjfile
    目的档格式(ELF)
  6. Documentation/arm64/memory.rst
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值