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的执行流程:
- preserve_boot_args:将bootloader传递的x0, x1, x2, x3保存到boot_args数组中,其中x0保存了FDT的地址,x1, x2, x3为0
- el2_setup:根据当前CPU处于EL1还是EL2对CPU进行设置,主要设置了端模式,VHE设置,GIC设置,定时器开启等
- __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对齐
- set_cpu_boot_mode_flag:将cpu启动的模式保存到全局变量__boot_cpu_mode中
- __create_page_tables:在idmap_pg区域为kernel创建恒等映射,在init_pg区域为kernel创建映射,这两块区域都位于vmlinux的data段,执行完__create_page_tables后得到地址映射关系如下:
id_map.text:为需要创建恒等映射的物理内存区域
id_map_pg:为恒等区域存放的页表区域
init_pg:为内核镜像存放的页表区域
- __cpu_setup:为开启mmu, 对cpu进行设置,包括设置memory attribute等
- __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来访问一致性页表了
- __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