kernel启动流程-head.S的执行_2.总体流程

kernel:5.10
Arch:arm64

1.前言


本专题主要基于《arm64_linux head.S的执行流程》系列文章,前者是基于3.18,本专题针对的是内核5.10。主要分析head.S的执行过程。本文将概括性的说明head.S的总体执行流程。

2. head.S执行前


bootloader在跳转到kernel前,需要确保如下设置:
MMU = off, D-cache = off, I-cache = on or off
x0 = physical address to the FDT blob
 

1.为何要保持MMU关闭?
因为此时还没有创建页表
2.为何要关闭D-cache?
因为D-cache中可能存在bootloader中带过来的数据,对于kernel阶段是无效的,因此要关闭D-cache.
3.为何I-cache可以开?因为bootloader与kernel位于不同的内存区间,不可能映射到I-cache的同一个set

在head.S执行前链接地址和物理内存布局,但是由于页表没有创建,MMU处于关闭状态,虚拟地址并未与物理地址建立映射关系
在这里插入图片描述 

 3. primary_entry执行总体流程

b    primary_entry// 跳转到primary_entry

kernel的入口在arch\arm64\kernel\head.S,它会跳转到primary_entry,primary_entry在哪里定义的呢?

SYM_CODE_START(primary_entry)
        bl      preserve_boot_args              
        bl      el2_setup                       // Drop to EL1, w0=cpu_boot_mode                                                                                                     
        adrp    x23, __PHYS_OFFSET
        and     x23, x23, MIN_KIMG_ALIGN - 1    // KASLR offset, defaults to 0                                                                                                       
        bl      set_cpu_boot_mode_flag                                                                                                                                               
        bl      __create_page_tables                                                                                                                                                 
        /*
         * The following calls CPU setup code, see arch/arm64/mm/proc.S for                                                                                                          
         * details.
         * On return, the CPU will be ready for the MMU to be turned on and                                                                                                          
         * the TCR will have been set.                                                                                                                                               
         */     
        bl      __cpu_setup                     // initialise processor                                                                                                              
        b       __primary_switch                                                                                                                                                     
SYM_CODE_END(primary_entry) 

关于SYM_CODE_START宏定义如下:

#define SYM_CODE_START(name)                            \
SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN)        

#define SYM_L_GLOBAL(name)                      .globl name
#define SYM_A_ALIGN                             ALIGN

/* SYM_START -- use only if you have to */                                                                                                                                                                                                                                                                                                            
#define SYM_START(name, linkage, align...)              \                                                                                                                            
        SYM_ENTRY(name, linkage, align)                                                                                                                                              

/* SYM_ENTRY -- use only if you have to for non-paired symbols */                                                                                                                                                                                                                                                                           
#define SYM_ENTRY(name, linkage, align...)              \                                                                                                                            
        linkage(name) ASM_NL                            \                                                                                                                            
        align ASM_NL                                    \                                                                                                                            
        name:            
#define ASM_NL           ;                                                    

因此SYM_CODE_START(primary_entry)可以转换为:

.globl primary_entry; ALIGN ;primary_entry:

下面我们来看下primary_entry的执行流程:

1. preserve_boot_args:将bootloader传递的x0, x1, x2, x3保存到boot_args数组中,其中x0保存了FDT的地址,x1, x2, x3为0
2. el2_setup:根据当前CPU处于EL1还是EL2对CPU进行设置,主要设置了端模式,VHE设置,GIC设置,定时器开启等
3 .__PHYS_OFFSET:
 

#arch/arm64/kernel/head.S
#define __PHYS_OFFSET   KERNEL_START

#arch/arm64/include/asm/memory.h
#define KERNEL_START            _text

/*                                                                                                                                                                                   
 * arm64 requires the kernel image to placed at a 2 MB aligned base address                                                                                                          
 */                                                                                                                                                                                  
#define MIN_KIMG_ALIGN          SZ_2M

booting.rst:
The Image must be placed text_offset bytes from a 2MB aligned base address anywhere in usable system RAM and called there. The region between the 2 MB aligned base address and the start of the image has no special significance to the kernel, and may be used for other purposes.At least image_size bytes from the start of the image must be free for use by the kernel.

(1)将__PHYS_OFFSET也就是kernel的入口链接地址_text保存到x23中
(2)由于kernel需2M对齐,通过and x23, x23, MIN_KIMG_ALIGN - 1,计算出对齐到2M需要的偏移量,保存到x23中,后面可以看到在kernle重定位时会通过x23进行2M对齐

4. set_cpu_boot_mode_flag:将cpu启动的模式保存到全局变量__boot_cpu_mode中
5. __create_page_tables:在idmap_pg区域为kernel创建恒等映射,在init_pg区域为kernel创建映射,这两块区域都位于vmlinux的data段,执行完__create_page_tables后得到地址映射关系如下:

注意:

(1)将.idmap.text段进行恒等映射,其物理地址等于虚拟地址:

arch/arm64/kernel/head.S:

/*Create the identity mapping*/

adrp x0, idmap_pg_dir           //恒等映射区域的页表基地址
adrp x3, __idmap_text_start     //开始映射的虚拟地址,直接就是__idmap_text_start物理地址
....
adrp x6, vabits_actual          //结束映射的虚拟地址,直接等于__idmap_text_end的物理地址
adrp x5, _idmap_text_end        //idmap段结束地址
/*
* .macro map_memory tbl, rtbl, vstart, vend, flags, phys, pgds, ....
*/
//可以看出映射内存时,使用的虚拟起始地址和物理起始地址都是x3
map_memory x0, x1, x3, x6, x7, x3, x4, x10, x11, x12, x13, x14



(2)映射kernel镜像(从HEAD_TEXT段开始,一直到.got.plt段结束,参考vmlinux.lds.S)

//Map the kernel image(starting with PHYS_OFFSET)

adrp x0, init_pg_dir        //kernel镜像映射的页表基地址
//x5:映射的起始虚拟地址,为链接的虚拟地址(0xffff800000000000+BPF_JIT_REGION_SIZE+MODULES_VSIZE)
mov_q x5, KIMAGE_VADDR 
...
adrp x3, _text        //映射的起始物理地址

map_memory x0, x1, x5, x6, x7, x7 ....

之所以.idmap段可以进行恒等映射,原因是使用adrp 获取__idmap_text_start,此时获取的是相对pc的物理地址,而此时MMU未开启,故pc中的地址就是物理地址。如上述mov_q x5, KIMAGE_VADDR获取的就不是物理地址,而是符号链接接地址


在这里插入图片描述

id_map.text:为需要创建恒等映射的物理内存区域
id_map_pg:为恒等区域存放的页表区域
init_pg:为内核镜像存放的页表区域 

7. __cpu_setup:为开启mmu, 对cpu进行设置,包括设置memory attribute等
8. __primary_switch:使能MMU,使能之前会分别用idmap_pg_dir和init_pag_dir设置TTBR0和TTBR1, 他们分别是kernel image的一致性页表起始虚拟地址和kernel image页表的起始虚拟地址;将kernel image的.rela.dyn段实现重定位;
 

为何用idmap_pg_dir初始化TTBR0?因为一致性映射意味着物理地址与虚拟地址相同,由于kernel image的idmap.text段位于物理地址0x48000000地址以下,对应虚拟地址空间处于用户空间,因此需要用idmap_pg_dir初始化TTBR0,这样打开MMU时就可以通过TTBR0来访问一致性页表了

// 由于ttbr0保存用户空间页表基地址, ttbr1保存内核空间页表基地址。0x48000000明显位于用户空间(48bit: 0x0 - 0xffffffffffff), 补充一点cpu更加bit63是否为0,选择使用ttbr0(bit63=0)还是ttbr1 (bit63 != 0)作为页表基地址。

9. __primary_switched会设置init进程栈及异常向量表,保存FDT地址,最终跳转到start_kernel执行
初始化init进程的地址映射关系如下: 
在这里插入图片描述

 参考文档


http://www.wowotech.net/armv8a_arch/arm64_initialize_1.html ARM64的启动过程之(一):内核第一个脚印
http://www.wowotech.net/armv8a_arch/create_page_tables.html ARM64的启动过程之(二):创建启动阶段的页表
http://www.wowotech.net/armv8a_arch/__cpu_setup.html ARM64的启动过程之(三):为打开MMU而进行的CPU初始化
http://www.wowotech.net/armv8a_arch/turn-on-mmu.html ARM64的启动过程之(四):打开MMU
https://blog.csdn.net/jkzzxQQQ/article/details/109880584
http://www.wowotech.net/memory_management/436.html
ARM64 Kernel Image Mapping的变化
Documents/arm64/booting.rst
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值