首页 > 系统相关 >Linux_aarch64_head.S到main.c的环境建立

Linux_aarch64_head.S到main.c的环境建立

时间:2024-04-21 17:12:48浏览次数:30  
标签:__ head aarch64 mov el2 Linux x10 x0 x1

PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

环境说明

  无

前言


  最开始,我仅仅是对linux比较感兴趣,觉得其很神奇的,能够做到很多事情。后面了解到其源码也是开源的,于是抱着学习的态度,简要的看了看相关的代码,在那个时候,我还看的比较粗略,仅仅是简单的会点编译,执行linux命令等等。这期间还有一个有印象的有趣的事儿就是那个pdf《Linux 那些事儿之USB》,大概就是讲述了作者因为要看pian儿,但是U盘识别不到,所以去细读USB相关linux 内核内容的资料。这精神,虽然我看不懂,但是我大为震撼!!!

  在我工作的近几年来,逐渐的和linux打上了交道,从最开始的hisi 3520a1系列的流媒体处理开始,为其搭建了文件系统,编译内核,同时为其适配EC20 4G模块,这期间,我基本都是照着别人的教程或者说文档,渐渐的熟悉了一些linux内核的一些事务。同时这期间,我做过一些简单的字符驱动玩耍模块,只能说玩玩可以的。

  在前几年中的某段时间,我接到一个任务,要在android进程之间大量传输数据2。这个时候我调研到了一个叫做android 匿名共享内存的东西,我发现了一个binder的驱动程序和linux unix socket的功能可以在android 和 linux 里面实现进程间的文件描述符的共享,注意这个方法是通用的,不像某些功能在linux里面能够使用,在android里面不能够使用。在这个时候,我天马行空实现了一个类似 binder的驱动demo3。这可以说是我第一个为了自己写的内核及的相关代码,而且具有实际应用意义。

  在这些工作过程中,我逐渐的觉得自己学习的《操作系统原理》与现实的差别,特别想把书中知识和实际系统结合起来,经过查询,如果想要大概了解linux 内核,最好从其远古的版本读起来,因为大概的脉络没有变,新内核只是更加的结构化,多了很多现代的功能。于是乎,有了《Linux Kernel 0.12 启动简介,调试记录(Ubuntu1804, Bochs, gdb)》一文4。经过了《Linux Kernel 0.12 启动简介,调试记录(Ubuntu1804, Bochs, gdb)》一文的学习之后,我基本了解了linux kernel 0.12版本内核的基本工作原理,例如其调度,内存管理等。其次是对于x86架构下,linux kernel 0.12的启动流程有了一个简要的认知。

  在最近这段时间,我的工作有部分和ai相关,有部分和android和linux的差异相关,需要我对linux内核有更深的印象和见解。于是在以前的基础上,这次,我要实际分析我们工作中所用的最新版本的内核,再一次的去验证一个内核从上电开始,到系统完整起来的过程。由于现代linux内核非常的巨大,所以我只关注我喜欢的部分。

  本文主要是分析aarch64架构的arch/arm64/kernel/head.S 到 init/main.c 中的start_kernel的过程。这可能也是我短时间内最后一次分析这种启动的过程,因为其实道理都是相同的,大部分都是cpu初始化,虚拟内存启用,由实地址切换为虚拟地址,创建init_task,设置sp,进入start_kernel。其实这里很多都是和特定的CPU有关系,内容是固定的。但是虚拟内存启用,初始task创建,初始sp指针初始化这些和《操作系统原理》有关联,可以印证我们所学。

  本文分为两大部分,一部分是head.S到main.c的调试环境建立, 二是从上电开始到进入start_kernel的代码注释分析和部分解释。





准备


  1. AARCH64 异常等级要简单了解一下5
  2. AARCH64 内存布局要简单了解一下6
  3. 看长文警告,一定要有耐心,否则看不下去。

汇编部分的调试环境搭建


  本文的测试环境为qemu-system-aarch64 raspi3b 模拟板卡。linux内核为树莓派内核 rpi-5.15.y, 下载地址为:https://github.com/raspberrypi/linux.git



生成带调试符号的linux kernel 镜像

  在make menu的时候勾选: Kernel hacking > Compile-time checks and compiler options > Compile the kernel with debug info
通过如下命令生成镜像:

cd rpi-linux-kernel-dir
cp arch/arm64/configs/bcm2711_defconfig .config
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image modules dtbs


生成rootfs.img镜像

  下载ubuntu-base-18.04.5-base-arm64.tar.gz的基础文件系统。

# 解压文件系统到指定目录
tar -xzf ubuntu-base-18.04.5-base-arm64.tar.gz -C temp/*

# 制作裸文件镜像
dd if=/dev/zero of=linuxroot.img bs=1M count=2048
sudo mkfs.ext4 linuxroot.img
mkdir  rootfs
sudo mount linuxroot.img rootfs/
sudo cp -rfp temp/*  rootfs/
sudo umount rootfs/
e2fsck -p -f linuxroot.img
resize2fs  -M linuxroot.img
编译生成最新版qemu

  只有新版的qemu才支持raspi3b模拟板卡

# 下载qemu代码
git clone https://github.com/qemu/qemu.git
cd qemu
mkdir build
cd build
../configure --prefix=/home/sky/LinuxKernel/qemu_install --target-list=arm-softmmu,arm-linux-user,armeb-linux-user,aarch64-softmmu,aarch64-linux-user,aarch64_be-linux-user 
make


执行qemu加载镜像

  这里的linux目录是内核目录,qemu_install是qemu生成的最新可执行文件目录,当前目录有rootfs镜像linuxroot.img。

# -S              freeze CPU at startup (use 'c' to start execution)
# -s              shorthand for -gdb tcp::1234
#  注意下面命令如果要直接运行,而不是等待gdb调试,请去掉最后的-s 和 -S。
./qemu_install/bin/qemu-system-aarch64 \
	-M raspi3b \
	-kernel ./linux/arch/arm64/boot/Image \
	-dtb ./linux/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dtb \
	-drive id=hd-root,format=raw,file=./linuxroot.img \
	-m 1024M \
	-serial stdio \
	-smp 4 \
	-device usb-kbd \
	-device usb-tablet \
	-device usb-net,netdev=net0 \
	-netdev user,id=net0,hostfwd=tcp::5555-:22 \
	-append "rw earlycon=pl011,0x3f201000 console=ttyAMA0 loglevel=8 root=/dev/mmcblk0 rootwait" -S -s


gdb 连接调试

  注意请在qemu启动后面加上-s 和 -S。当连接成功时,这个时候cpu还未执行,输入ni执行到第一条指令。

# 没有gdb-multiarch自行安装
# 这里的vmlinux就是编译生成的最终镜像
gdb-multiarch  linux/vmlinux

# 在gdb cli中执行
target remote localhost:1234




汇编代码调试

  我这里把整个head.S里面重要的部分都dump下来了。跟着这个部分然后参考head.S去阅读,会有奇效。长文注释警告。

  首先是上电部分,当板卡上电后,会执行bootloader,bootloader会将内核和dtb放到特定的位置,然后按照Linux arm64 boot protocal去初始化对应的寄存器,最后进入head.S的第一条指令。

@ boot start ... ...
//Linux arm64 boot protocal
@ https://www.kernel.org/doc/Documentation/arm64/booting.txt
@ 0x08000000 FDT
/*
- 主 CPU 通用寄存器设置
  x0 = 系统 RAM 中设备树 blob (dtb) 的物理地址。
  x1 = 0(留作将来使用)
  x2 = 0(留作将来使用)
  x3 = 0(留作将来使用)
*/
//注意这里的0x18地址存放的是fdt的地址,地址为0x08000000

0x0000000000000000:	ldr	x0, 0x18
0x0000000000000004:	mov	x1, xzr
0x0000000000000008:	mov	x2, xzr
0x000000000000000c:	mov	x3, xzr
//注意这里的0x20是存放的kernel地址,地址为0x00200000
0x0000000000000010:	ldr	x4, 0x20
0x0000000000000014:	br	x4
@ 0x00200000 head.S start ... ...
@ =======> now, go to 0x00200000

  注意,当我们进入head.S的最开始的地方的时候,有一个标准得到头如下。其中code1的部分将会跳转到真正执行的地方。

@ The decompressed kernel image contains a 64-byte header as follows:
@   u32 code0;			/* Executable code */ 
@   u32 code1;			/* Executable code */
@   u64 text_offset;		/* Image load offset, little endian */
@   u64 image_size;		/* Effective Image size, little endian */
@   u64 flags;			/* kernel flags, little endian */
@   u64 res2	= 0;		/* reserved */
@   u64 res3	= 0;		/* reserved */
@   u64 res4	= 0;		/* reserved */
@   u32 magic	= 0x644d5241;	/* Magic number, little endian, "ARM\x64" */
@   u32 res5;			/* reserved (used for PE COFF offset) */

@ 0x200000  code0
@ 0x200004  code1                            
@ 0x200008  text_offset 
@ 0x20000c                           
@ 0x200010  image_size                    
@ 0x200014                    
@ 0x200018  flags                       
@ 0x20001c                          
@ 0x200020  res2                    
@ 0x200024                          
@ 0x200028  res3                       
@ 0x20002c                      
@ 0x200030  res4                       
@ 0x200034                    
@ 0x200038  magic       
@ 0x20003c  res5
//注意,这里的code0,code1就是地址0x200000和0x200004的指令。整个0x200000到0x200040就是内核镜像的64字节头。
0x0000000000200000:	ccmp	x18, #0x0, #0xd, pl  // special NOP to identity as PE/COFF executable
0x0000000000200004:	b	0x1190000 @ =======> now, go to 0x1190000(primary_entry)

  这是整个内核启动部分最重要的函数,所有的东西都在这里做完,然后跳转到start_kernel。下面我们会来重点分析这个部分的内容。详细请看注释。

//注意这里的几个bl指令,覆盖了进入kernel_start前的所有操作
@ SYM_CODE_START(primary_entry)

//跳转过去保存boot参数
//保存x0(fdt),x1,x2,x3到符号boot_args的变量中,靠dcache_inval_poc中的ret返回到下一行指令
0x0000000001190000:	bl	0x1190020 //preserve_boot_args

//跳转过去执行不同异常等级的初始化,这里比较复杂,从开始的el2异常级别跳转到el1级别。
0x0000000001190004:	bl	0xd5d000 //init_kernel_el

//将内核镜像开始地址给x23,也就是0x200000
0x0000000001190008:	adrp	x23, 0x200000
// KASLR offset, defaults to 0
0x000000000119000c:	and	x23, x23, #0x1fffff

//跳转过去根据w0的值,保存相关的cpu boot mode,注意当前我们的cpu已经处于el1等级,w0 存的是 el2 的标识符
0x0000000001190010:	bl	0xd5d1f8

//创建页表,这里面的创建只填充了相关的页表项,并没有开启mmu
//idmap = 0x117e00, 0x117e0 = 0x117f03, 0x117f30 = 0x00c00701, 
//    注意,这时页表项表示2MB的区域。idmap区域包含了__cpu_setup和__primary_switch
//这里执行完,有两个重要的数据结构:idmap_pg_dir 和 init_pg_dir
//我们将物理地址__idmap_text_start映射到虚拟地址[__idmap_text_start, __idmap_text_end],注意观察,物理地址和虚拟地址基本是一致的。
//还将物理地址_text映射到虚拟地址[KIMAGE_VADDR + KASLR, _end]
0x0000000001190014:	bl	0x1190040

//The following calls CPU setup code, see arch/arm64/mm/proc.S
//注意,这里已经准备好了SCTLR在x0中,下面就是相关的初始化,然后准备打开mmu的参数
0x0000000001190018:	bl	0xd5d6f4

//最终的初始化,开启mmu,并跳转到kernel_start
0x000000000119001c:	b	0xd5d3d8

@ SYM_CODE_END(primary_entry)

  此部分对应保存启动参数,主要还是保存启动时,x0~x3。


@ SYM_CODE_START_LOCAL(preserve_boot_args)
//x21保存dtb物理地址
0x0000000001190020:	mov	x21, x0
//将dtb,x1,x2,x3物理地址存放到变量arch/arm64/kernel/setup.c:u64 __cacheline_aligned boot_args[4];
0x0000000001190024:	adrp	x0, 0x1545000
0x0000000001190028:	add	x0, x0, #0x0
//存放dtb,x1
0x000000000119002c:	stp	x21, x1, [x0]
//存放x2,x3
0x0000000001190030:	stp	x2, x3, [x0, #16]

@ 刷新cache
0x0000000001190034:	dmb	sy
0x0000000001190038:	add	x1, x0, #0x20
0x000000000119003c:	b	0x2346a8

@ SYM_CODE_END(preserve_boot_args)

  此部分很长,其实主要是汇编代码稍微复杂,其基本的作用就是创建页表,这里面的创建只填充了相关的页表项。分别创建了这里执行完,有两个重要的数据结构:idmap_pg_dir和init_pg_dir的数据结构。这里用的是两级映射,第一级是全局映射,第二级是每个项2MB的映射。这里的idmap_pg_dir映射的是__cpu_setup和__primary_switch部分的内容,这部分主要涉及到mmu开启的过程,需要将物理地址和虚拟地址对应起来。init_pg_dir主要是映射的是kernel虚拟地址和kernel镜像地址。

@ SYM_FUNC_START_LOCAL(__create_page_tables)
//保存返回值到x28
0x0000000001190040:	mov	x28, x30
//加载init_pg_dir 到x0
0x0000000001190044:	adrp	x0, 0x181d000
//加载init_pg_end 到x1
0x0000000001190048:	adrp	x1, 0x1820000
@ 刷新cache
0x000000000119004c:	bl	0x2346a8

//加载init_pg_dir 到x0
0x0000000001190050:	adrp	x0, 0x181d000
//加载init_pg_end 到x1
0x0000000001190054:	adrp	x1, 0x1820000
//求出init_pg的大小放入x1中count
0x0000000001190058:	sub	x1, x1, x0
//向x0中写入0,然后x1 -= 64,当x1等于0时候,所有pg清理完毕。
0x000000000119005c:	stp	xzr, xzr, [x0], #16
0x0000000001190060:	stp	xzr, xzr, [x0], #16
0x0000000001190064:	stp	xzr, xzr, [x0], #16
0x0000000001190068:	stp	xzr, xzr, [x0], #16
0x000000000119006c:	subs	x1, x1, #0x40
0x0000000001190070:	b.ne	0x119005c  // b.any

//根据配置加载不同的flag, SWAPPER_MM_MMUFLAGS
0x0000000001190074:	mov	x7, #0x701                 	// #1793

// 获取idmap的页表基地址,idmap_pg_dir
0x0000000001190078:	adrp	x0, 0x117e000
@ 获取idmap的代码段虚地址,__idmap_text_start
0x000000000119007c:	adrp	x3, 0xd5d000
@ 将系统地址线位数给x5, VA_BITS_MIN
0x0000000001190080:	mov	x5, #0x27                  	// #39
//获取变量地址到x6, vabits_actual
0x0000000001190084:	adrp	x6, 0x1728000
0x0000000001190088:	add	x6, x6, #0x10
//将39写入变量vabits_actual
0x000000000119008c:	str	x5, [x6]
0x0000000001190090:	dmb	sy
0x0000000001190094:	dc	ivac, x6

@ 判断虚拟地址空间是否够IDmap来映射
0x0000000001190098:	adrp	x5, 0xd5d000
0x000000000119009c:	clz	x5, x5
0x00000000011900a0:	cmp	x5, #0x19
@ 这里要跳转,不需要扩展虚拟地址
0x00000000011900a4:	b.ge	0x11900e0  // b.tcont

@ 这部分是虚拟地址扩展的相关操作,这里不做详解
0x00000000011900a8:	adrp	x6, 0x1555000
0x00000000011900ac:	add	x6, x6, #0xcc8
0x00000000011900b0:	str	x5, [x6]
0x00000000011900b4:	dmb	sy
0x00000000011900b8:	dc	ivac, x6
0x00000000011900bc:	mov	x4, #0x200                 	// #512
0x00000000011900c0:	add	x5, x0, #0x1, lsl #12
0x00000000011900c4:	mov	x6, x5
0x00000000011900c8:	orr	x6, x6, #0x3
0x00000000011900cc:	lsr	x5, x3, #39
0x00000000011900d0:	sub	x4, x4, #0x1
0x00000000011900d4:	and	x5, x5, x4
0x00000000011900d8:	str	x6, [x0, x5, lsl #3]
0x00000000011900dc:	add	x0, x0, #0x1, lsl #12

@ 从上面不需要扩展虚拟地址跳转而来,0x00000000011900a4
@ 将512 个 pgd entry存入x4
0x00000000011900e0:	adrp	x4, 0x1555000
0x00000000011900e4:	ldr	x4, [x4, #3280]

@ 将__idmap_text_end放入x6
0x00000000011900e8:	adrp	x6, 0xd5d000
0x00000000011900ec:	add	x6, x6, #0x7e0

@ 这里开始映射[__idmap_text_start, __idmap_text_end] 到 idmap_pg_dir中,
@	且,这部分内容就是cpu_setup部分的内容,恰好对应开启mmu的代码。
//tbl:    x0 = idmap_pg_dir = 0x117e000
//rtbl:   x1 = 0 
//vstart: x3 = __idmap_text_start = 0xd5d000
//vend:   x6 = __idmap_text_end = 0xd5d7e0
//flags:  x7 = SWAPPER_MM_MMUFLAGS
//phys:   x3 = __idmap_text_start = 0xd5d000
//pgds:   x4 = idmap_ptrs_per_pgd = 512
//tmp regs: x10, x11, x12, x13, x14
@ macro map_memory start ...
@ __idmap_text_end - 1 是idmap映射结束的地方
0x00000000011900f0:	sub	x6, x6, #0x1
@ x1 = idmap的页表基地址(idmap_pg_dir) + 2^12 ,并指向了下一个page entry
0x00000000011900f4:	add	x1, x0, #0x1, lsl #12
@ 将x1 保存到 x14
0x00000000011900f8:	mov	x14, x1
@ 将count赋值为0
0x00000000011900fc:	mov	x13, #0x0                   	// #0

// vstart:	x3 = __idmap_text_start = 0xd5d000
// vend:	x6 = __idmap_text_end = 0xd5d7e0
// shift:	30
// ptrs:	x4 = idmap_ptrs_per_pgd = 512
// istart:	x10
// iend:	
// count:	x13
@ compute_indices  start ...
//将vend右逻辑偏移shift(30)位
0x0000000001190100:	lsr	x11, x6, #30
//将ptrs(number of entries in page table)存放到istart, 每个表512项
0x0000000001190104:	mov	x10, x4
//pte 数量减一
0x0000000001190108:	sub	x10, x10, #0x1

//此时算出来vend的pgd index,根据虚拟地址右移30位,还剩9位,恰好表示512个项的id。
// iend = (vend >> shift) & (ptrs - 1)
0x000000000119010c:	and	x11, x11, x10
0x0000000001190110:	mov	x10, x4
0x0000000001190114:	mul	x10, x10, x13
// iend += count * ptrs
0x0000000001190118:	add	x11, x11, x10

//将vstart右逻辑偏移shift位
0x000000000119011c:	lsr	x10, x3, #30
0x0000000001190120:	mov	x13, x4
0x0000000001190124:	sub	x13, x13, #0x1
// istart = (vstart >> shift) & (ptrs - 1), 此时算出来vstart的pgd index
0x0000000001190128:	and	x10, x10, x13
//计算出多少项page entry
0x000000000119012c:	sub	x13, x11, x10
@ compute_indices  end ...



@ populate_entries start ... 
0x0000000001190130:	mov	x12, x1
//给当前entry设置内存属性
0x0000000001190134:	orr	x12, x12, #0x3
@ 向一级页表idmap_pg_dir(0x117e000)存入二级页表地址(0x117f000)
0x0000000001190138:	str	x12, [x0, x10, lsl #3]
0x000000000119013c:	add	x1, x1, #0x1, lsl #12
0x0000000001190140:	add	x10, x10, #0x1
0x0000000001190144:	cmp	x10, x11
0x0000000001190148:	b.ls	0x1190130  // b.plast
@ populate_entries end ... 

//注意,这里相当于tbl=tbl+PAGE_SIZE,主要还是指向了二级页表
//相当于现在一级页表为:idmap_pg_dir(0x117e000),里面存放的是0x0117f003
//这里的x14是二级页表的地址,为0x0117f000
0x000000000119014c:	mov	x0, x14
//sv = rtbl = tbl+PAGE_SIZE+PAGE_SIZE,相当于指向了三级页表
0x0000000001190150:	mov	x14, x1

@ compute_indices  start ...
//将vend右逻辑偏移shift位
0x0000000001190154:	lsr	x11, x6, #21
//将ptrs(number of entries in page table)存放到istart, 每个表512项
0x0000000001190158:	mov	x10, #0x200                 	// #512
//pte 数量减一
0x000000000119015c:	sub	x10, x10, #0x1

//此时算出来vend的pgd index
// iend = (vend >> shift) & (ptrs - 1)
0x0000000001190160:	and	x11, x11, x10
0x0000000001190164:	mov	x10, #0x200                 	// #512
0x0000000001190168:	mul	x10, x10, x13
// iend += count * ptrs
0x000000000119016c:	add	x11, x11, x10

//将vstart右逻辑偏移shift位
0x0000000001190170:	lsr	x10, x3, #21
0x0000000001190174:	mov	x13, #0x200                 	// #512
0x0000000001190178:	sub	x13, x13, #0x1
// istart = (vstart >> shift) & (ptrs - 1), 此时算出来vstart的pgd index
0x000000000119017c:	and	x10, x10, x13
//计算出多少项page entry
0x0000000001190180:	sub	x13, x11, x10
@ compute_indices  end ...

@ x13 = 0xc00000, x3 = 0xd5d000
0x0000000001190184:	and	x13, x3, #0xffffffffffe00000

@ populate_entries start ... 
@ x12 = 0xc00000
0x0000000001190188:	mov	x12, x13
//给当前entry设置内存属性
0x000000000119018c:	orr	x12, x12, x7
@ 向二级页表(0x117f000 + 6*8 = 0x117f030)存入地址0xc00701
0x0000000001190190:	str	x12, [x0, x10, lsl #3]
0x0000000001190194:	add	x13, x13, #0x200, lsl #12
0x0000000001190198:	add	x10, x10, #0x1
0x000000000119019c:	cmp	x10, x11
0x00000000011901a0:	b.ls	0x1190188  // b.plast
@ populate_entries end ... 

@ init_pg_dir  给x0
0x00000000011901a4:	adrp	x0, 0x181d000
@ KIMAGE_VADDR 给x5
0x00000000011901a8:	mov	x5, #0xffffffc0ffffffff    	// #-270582939649
0x00000000011901ac:	movk	x5, #0x800, lsl #16
0x00000000011901b0:	movk	x5, #0x0
// add KASLR displacement
0x00000000011901b4:	add	x5, x5, x23
@ 将页表项数目给x4
0x00000000011901b8:	mov	x4, #0x200                 	// #512
@ _end 给x6
0x00000000011901bc:	adrp	x6, 0x1820000
@ _start 给x3
0x00000000011901c0:	adrp	x3, 0x200000
@ 求出_end-_start
0x00000000011901c4:	sub	x6, x6, x3
@ 算出基于KIMAGE_VADDR和KASLR的偏移
0x00000000011901c8:	add	x6, x6, x5

//tbl:    x0 = init_pg_dir
//rtbl:   x1 = 0
//vstart: x5 = KIMAGE_VADDR + KASLR
//vend:   x6 = _end
//flags:  x7 = SWAPPER_MM_MMUFLAGS
//phys:   x3 = _text
//pgds:   x4 = PTRS_PER_PGD
//tmp regs: x10, x11, x12, x13, x14
@ macro map_memory start ...
@ 开始填充页表init_pg_dir
0x00000000011901cc:	sub	x6, x6, #0x1
0x00000000011901d0:	add	x1, x0, #0x1, lsl #12
0x00000000011901d4:	mov	x14, x1
0x00000000011901d8:	mov	x13, #0x0                   	// #0
0x00000000011901dc:	lsr	x11, x6, #30
0x00000000011901e0:	mov	x10, x4
0x00000000011901e4:	sub	x10, x10, #0x1
0x00000000011901e8:	and	x11, x11, x10
0x00000000011901ec:	mov	x10, x4
0x00000000011901f0:	mul	x10, x10, x13
0x00000000011901f4:	add	x11, x11, x10
0x00000000011901f8:	lsr	x10, x5, #30
0x00000000011901fc:	mov	x13, x4
0x0000000001190200:	sub	x13, x13, #0x1
0x0000000001190204:	and	x10, x10, x13
0x0000000001190208:	sub	x13, x11, x10
0x000000000119020c:	mov	x12, x1
0x0000000001190210:	orr	x12, x12, #0x3
0x0000000001190214:	str	x12, [x0, x10, lsl #3]
0x0000000001190218:	add	x1, x1, #0x1, lsl #12
0x000000000119021c:	add	x10, x10, #0x1
0x0000000001190220:	cmp	x10, x11
0x0000000001190224:	b.ls	0x119020c  // b.plast
0x0000000001190228:	mov	x0, x14
0x000000000119022c:	mov	x14, x1
0x0000000001190230:	lsr	x11, x6, #21
0x0000000001190234:	mov	x10, #0x200                 	// #512
0x0000000001190238:	sub	x10, x10, #0x1
0x000000000119023c:	and	x11, x11, x10
0x0000000001190240:	mov	x10, #0x200                 	// #512
0x0000000001190244:	mul	x10, x10, x13
0x0000000001190248:	add	x11, x11, x10
0x000000000119024c:	lsr	x10, x5, #21
0x0000000001190250:	mov	x13, #0x200                 	// #512
0x0000000001190254:	sub	x13, x13, #0x1
0x0000000001190258:	and	x10, x10, x13
0x000000000119025c:	sub	x13, x11, x10
0x0000000001190260:	and	x13, x3, #0xffffffffffe00000
0x0000000001190264:	mov	x12, x13
0x0000000001190268:	orr	x12, x12, x7
0x000000000119026c:	str	x12, [x0, x10, lsl #3]
0x0000000001190270:	add	x13, x13, #0x200, lsl #12
0x0000000001190274:	add	x10, x10, #0x1
0x0000000001190278:	cmp	x10, x11
0x000000000119027c:	b.ls	0x1190264  // b.plast
@ macro map_memory end ...


//内存屏障
0x0000000001190280:	dmb	sy
@ 刷新cache
0x0000000001190284:	adrp	x0, 0x117e000
0x0000000001190288:	adrp	x1, 0x1181000
0x000000000119028c:	bl	0x2346a8
@ 刷新cache
0x0000000001190290:	adrp	x0, 0x181d000
0x0000000001190294:	adrp	x1, 0x1820000
0x0000000001190298:	bl	0x2346a8
@ 返回到bl	__cpu_setup
0x000000000119029c:	ret	x28


@ SYM_FUNC_END(__create_page_tables)


  这部分是刷新i/d cache


@ dcache_inval_poc start ...
0x00000000002346a8:	mrs	x3, ctr_el0
0x00000000002346ac:	nop
0x00000000002346b0:	ubfx	x3, x3, #16, #4
0x00000000002346b4:	mov	x2, #0x4                   	// #4
0x00000000002346b8:	lsl	x2, x2, x3
0x00000000002346bc:	sub	x3, x2, #0x1
0x00000000002346c0:	tst	x1, x3
0x00000000002346c4:	bic	x1, x1, x3
0x00000000002346c8:	b.eq	0x2346d0  // b.none
0x00000000002346cc:	dc	civac, x1
0x00000000002346d0:	tst	x0, x3
0x00000000002346d4:	bic	x0, x0, x3
0x00000000002346d8:	b.eq	0x2346e4  // b.none
0x00000000002346dc:	dc	civac, x0
0x00000000002346e0:	b	0x2346e8
0x00000000002346e4:	dc	ivac, x0
0x00000000002346e8:	add	x0, x0, x2
0x00000000002346ec:	cmp	x0, x1
0x00000000002346f0:	b.cc	0x2346e4  // b.lo, b.ul, b.last
0x00000000002346f4:	dsb	sy
0x00000000002346f8:	ret
@ dcache_inval_poc end ...

  这部分就是对应的是开始的在el2模式下初始化,并返回到el1,并保存启动参数。

@ SYM_FUNC_START(init_kernel_el)
//读取当前的异常等级
0x0000000000d5d000:	mrs	x0, currentel
//判断是否为异常等级2
0x0000000000d5d004:	cmp	x0, #0x8
//跳转到el2(qemu 模拟机器执行路径), init_el2
0x0000000000d5d008:	b.eq	0xd5d034  // b.none
0x0000000000d5d00c:	mov	x0, #0x30500000            	// #810549248
0x0000000000d5d010:	movk	x0, #0x800
0x0000000000d5d014:	msr	sctlr_el1, x0
0x0000000000d5d018:	isb
0x0000000000d5d01c:	movz	x0, #0x0, lsl #16
0x0000000000d5d020:	movk	x0, #0x3c5
0x0000000000d5d024:	msr	spsr_el1, x0
0x0000000000d5d028:	msr	elr_el1, x30
0x0000000000d5d02c:	mov	w0, #0xe11                 	// #3601
0x0000000000d5d030:	eret

@ init_el2 start ... ...
//配置hcr_el2寄存器,HCR(Hypervisor Configuration Register)
0x0000000000d5d034:	mov	x0, #0x100000000000000     	// #72057594037927936
0x0000000000d5d038:	movk	x0, #0x300, lsl #32
0x0000000000d5d03c:	movk	x0, #0x8000, lsl #16
0x0000000000d5d040:	movk	x0, #0x0
0x0000000000d5d044:	msr	hcr_el2, x0
//ISB. 指令同步屏障
0x0000000000d5d048:	isb

//初始化el2下的各种状态
/*
.macro init_el2_state
	__init_el2_sctlr
	__init_el2_timers
	__init_el2_debug
	__init_el2_lor
	__init_el2_stage2
	__init_el2_gicv3
	__init_el2_hstr
	__init_el2_nvhe_idregs
	__init_el2_nvhe_cptr
	__init_el2_nvhe_sve
	__init_el2_fgt
	__init_el2_nvhe_prepare_eret
.endm
*/

//__init_el2_sctlr
0x0000000000d5d04c:	mov	x0, #0x30c50000            	// #818216960
0x0000000000d5d050:	movk	x0, #0x830
0x0000000000d5d054:	msr	sctlr_el2, x0
0x0000000000d5d058:	isb

//__init_el2_timers
0x0000000000d5d05c:	mov	x0, #0x3                   	// #3
0x0000000000d5d060:	msr	cnthctl_el2, x0
0x0000000000d5d064:	msr	cntvoff_el2, xzr

//__init_el2_debug
0x0000000000d5d068:	mrs	x1, id_aa64dfr0_el1
0x0000000000d5d06c:	sbfx	x0, x1, #8, #4
0x0000000000d5d070:	cmp	x0, #0x1
0x0000000000d5d074:	b.lt	0xd5d080  // b.tstop
0x0000000000d5d078:	mrs	x0, pmcr_el0
0x0000000000d5d07c:	ubfx	x0, x0, #11, #5
0x0000000000d5d080:	csel	x2, xzr, x0, lt  // lt = tstop
0x0000000000d5d084:	ubfx	x0, x1, #32, #4
0x0000000000d5d088:	cbz	x0, 0xd5d0a8
0x0000000000d5d08c:	mrs	x0, pmbidr_el1
0x0000000000d5d090:	and	x0, x0, #0x10
0x0000000000d5d094:	cbnz	x0, 0xd5d0a0
0x0000000000d5d098:	mov	x0, #0x50                  	// #80
0x0000000000d5d09c:	msr	pmscr_el2, x0
0x0000000000d5d0a0:	mov	x0, #0x3000                	// #12288
0x0000000000d5d0a4:	orr	x2, x2, x0
0x0000000000d5d0a8:	ubfx	x0, x1, #44, #4
0x0000000000d5d0ac:	cbz	x0, 0xd5d0c4
0x0000000000d5d0b0:	mrs	x0, s3_0_c9_c11_7
0x0000000000d5d0b4:	and	x0, x0, #0x10
0x0000000000d5d0b8:	cbnz	x0, 0xd5d0c4
0x0000000000d5d0bc:	mov	x0, #0x3000000             	// #50331648
0x0000000000d5d0c0:	orr	x2, x2, x0
0x0000000000d5d0c4:	msr	mdcr_el2, x2

//__init_el2_lor
0x0000000000d5d0c8:	mrs	x1, id_aa64mmfr1_el1
0x0000000000d5d0cc:	ubfx	x0, x1, #16, #4
0x0000000000d5d0d0:	cbz	x0, 0xd5d0d8
0x0000000000d5d0d4:	msr	s3_0_c10_c4_3, xzr

//__init_el2_stage2
0x0000000000d5d0d8:	msr	vttbr_el2, xzr

//__init_el2_gicv3
0x0000000000d5d0dc:	mrs	x0, id_aa64pfr0_el1
0x0000000000d5d0e0:	ubfx	x0, x0, #24, #4
0x0000000000d5d0e4:	cbz	x0, 0xd5d108
0x0000000000d5d0e8:	mrs	x0, s3_4_c12_c9_5
0x0000000000d5d0ec:	orr	x0, x0, #0x1
0x0000000000d5d0f0:	orr	x0, x0, #0x8
0x0000000000d5d0f4:	msr	s3_4_c12_c9_5, x0
0x0000000000d5d0f8:	isb
0x0000000000d5d0fc:	mrs	x0, s3_4_c12_c9_5
0x0000000000d5d100:	tbz	w0, #0, 0xd5d108
0x0000000000d5d104:	msr	s3_4_c12_c11_0, xzr

//__init_el2_hstr
0x0000000000d5d108:	msr	hstr_el2, xzr

//__init_el2_nvhe_idregs
0x0000000000d5d10c:	mrs	x0, midr_el1
0x0000000000d5d110:	mrs	x1, mpidr_el1
0x0000000000d5d114:	msr	vpidr_el2, x0
0x0000000000d5d118:	msr	vmpidr_el2, x1

//__init_el2_nvhe_cptr
0x0000000000d5d11c:	mov	x0, #0x33ff                	// #13311
0x0000000000d5d120:	msr	cptr_el2, x0

//__init_el2_nvhe_sve
0x0000000000d5d124:	mrs	x1, id_aa64pfr0_el1
0x0000000000d5d128:	ubfx	x1, x1, #32, #4
0x0000000000d5d12c:	cbz	x1, 0xd5d144
0x0000000000d5d130:	and	x0, x0, #0xfffffffffffffeff
0x0000000000d5d134:	msr	cptr_el2, x0
0x0000000000d5d138:	isb
0x0000000000d5d13c:	mov	x1, #0x1ff                 	// #511
0x0000000000d5d140:	msr	zcr_el2, x1

//__init_el2_fgt
0x0000000000d5d144:	mrs	x1, id_aa64mmfr0_el1
0x0000000000d5d148:	ubfx	x1, x1, #56, #4
0x0000000000d5d14c:	cbz	x1, 0xd5d18c
0x0000000000d5d150:	mov	x0, xzr
0x0000000000d5d154:	mrs	x1, id_aa64dfr0_el1
0x0000000000d5d158:	ubfx	x1, x1, #32, #4
0x0000000000d5d15c:	cmp	x1, #0x3
0x0000000000d5d160:	b.lt	0xd5d168  // b.tstop
0x0000000000d5d164:	orr	x0, x0, #0x4000000000000000
0x0000000000d5d168:	msr	s3_4_c3_c1_4, x0
0x0000000000d5d16c:	msr	s3_4_c3_c1_5, x0
0x0000000000d5d170:	msr	s3_4_c1_c1_4, xzr
0x0000000000d5d174:	msr	s3_4_c1_c1_5, xzr
0x0000000000d5d178:	msr	s3_4_c1_c1_6, xzr
0x0000000000d5d17c:	mrs	x1, id_aa64pfr0_el1
0x0000000000d5d180:	ubfx	x1, x1, #44, #4
0x0000000000d5d184:	cbz	x1, 0xd5d18c
0x0000000000d5d188:	msr	s3_4_c3_c1_6, xzr

// __init_el2_nvhe_prepare_eret
0x0000000000d5d18c:	mov	x0, #0x3c5                 	// #965
0x0000000000d5d190:	msr	spsr_el2, x0


//加载el2的中断向量表
0x0000000000d5d194:	adrp	x0, 0xd4f000
0x0000000000d5d198:	add	x0, x0, #0x0
0x0000000000d5d19c:	msr	vbar_el2, x0
0x0000000000d5d1a0:	isb

/*
	* Fruity CPUs seem to have HCR_EL2.E2H set to RES1,
	* making it impossible to start in nVHE mode. Is that
	* compliant with the architecture? Absolutely not!
*/
0x0000000000d5d1a4:	mrs	x0, hcr_el2
0x0000000000d5d1a8:	and	x0, x0, #0x400000000
//跳转到1f(0xd5d1d0)位置继续执行
0x0000000000d5d1ac:	cbz	x0, 0xd5d1d0

0x0000000000d5d1b0:	mov	x0, #0x30500000            	// #810549248
0x0000000000d5d1b4:	movk	x0, #0x800
0x0000000000d5d1b8:	msr	sctlr_el12, x0
0x0000000000d5d1bc:	mov	x0, #0x3c9                 	// #969
0x0000000000d5d1c0:	msr	spsr_el1, x0
0x0000000000d5d1c4:	adr	x0, 0xd5d1e8
0x0000000000d5d1c8:	msr	elr_el1, x0
0x0000000000d5d1cc:	eret

//将x0写入sctlr_el1
0x0000000000d5d1d0:	mov	x0, #0x30500000            	// #810549248
0x0000000000d5d1d4:	movk	x0, #0x800
0x0000000000d5d1d8:	msr	sctlr_el1, x0

//将当前的lr(x30)地址放到elr_el2中,后续eret到el1时,跳转到此地址执行
0x0000000000d5d1dc:	msr	elr_el2, x30
//将flag写入w0中
0x0000000000d5d1e0:	mov	w0, #0xe12                 	// #3602
//注意这里的返回值,这里返回到elr_el2指向的地方,
//也就是adrp	x23, __PHYS_OFFSET, 0x0000000001190008
0x0000000000d5d1e4:	eret


0x0000000000d5d1e8:	mov	x0, #0x3                   	// #3
0x0000000000d5d1ec:	hvc	#0x0
0x0000000000d5d1f0:	mov	x0, #0xe12                 	// #3602
0x0000000000d5d1f4:	ret

@ SYM_FUNC_END(init_kernel_el)


@ SYM_FUNC_START_LOCAL(set_cpu_boot_mode_flag)
//加载__boot_cpu_mode符号地址,此地址存放
0x0000000000d5d1f8:	adrp	x1, 0x1728000
0x0000000000d5d1fc:	add	x1, x1, #0x0
0x0000000000d5d200:	cmp	w0, #0xe12
0x0000000000d5d204:	b.ne	0xd5d20c  // b.any
0x0000000000d5d208:	add	x1, x1, #0x4
0x0000000000d5d20c:	str	w0, [x1]
0x0000000000d5d210:	dmb	sy
0x0000000000d5d214:	dc	ivac, x1
@ 返回到0x0000000001190014
0x0000000000d5d218:	ret

@ SYM_FUNC_END(set_cpu_boot_mode_flag)

  这里包含3个步骤:

  1. 这里其实包含了开启mmu,mmu开启时,tbbr0_el1是idmap_pg_dir,tbbr1_el1是init_pg_dir,还是使用的物理地址,所以这个时候的地址翻译由页表idmap_pg_dir来支持。
  2. 当完成mmu开启后,就会将.rela.dyn段实现重定位,因为这部分的符号的实际地址为0,需要我们填写为实际的符号地址。
  3. 当重定位完成后,会加载__primary_switched的虚拟地址,其地址在内核空间,当执行blr 跳转到__primary_switched的时候,这个时候的地址返回由页表init_pg_dir来支持。
@ SYM_FUNC_START(__enable_mmu)
/*
符合ARMv8的PE最大支持的物理地址宽度也是48个bit,当然,具体的实现可以自己定义
(不能超过48个bit),具体的配置可以通过ID_AA64MMFR0_EL1 
(AArch64 Memory Model Feature Register 0)这个RO寄存器获取
*/
0xd5d308        mrs    x2, id_aa64mmfr0_el1     
@ 判断物理地址宽度是否满足最小和最大的要求。
0xd5d30c        ubfx   x2, x2, #28, #4                                        
0xd5d310        cmp    x2, #0x0                                               
0xd5d314        b.lt   0xd5d36c  // b.tstop                                   
0xd5d318        cmp    x2, #0x7                                               
0xd5d31c        b.gt   0xd5d36c     

//update_early_cpu_boot_status
0xd5d320        mov    x3, #0x0                        // #0                  
0xd5d324        adrp   x2, 0x1728000                                          
0xd5d328        add    x2, x2, #0x8                                           
0xd5d32c        str    x3, [x2]                                               
0xd5d330        dmb    sy                                                     
0xd5d334        dc     ivac, x2   

//加载idmap_pg_dir到ttbr0
//注意,跳转过来时,x1是init_gdir
0xd5d338        adrp   x2, 0x117e000                                          
0xd5d33c        mov    x1, x1                                                 
0xd5d340        mov    x2, x2           

//加载两个映射表到tbbr0和1,在el1模式下面
0xd5d344        msr    ttbr0_el1, x2                                          
0xd5d348        msr    ttbr1_el1, x1                                          
0xd5d34c        isb     

//打开mmu
0xd5d350        msr    sctlr_el1, x0                                          
0xd5d354        isb                                                           
0xd5d358        ic     iallu                                                  
0xd5d35c        dsb    nsh                                                    
0xd5d360        isb                                                           
0xd5d364        ret  
@ SYM_FUNC_END(__enable_mmu)

//此部分作用为解决符号重定位问题
//readelf -r vmlinux 读取重定位表
//readelf -S vmlinux 读取所有section头
//hexdump -s 0x24000 -n 16 vmlinux 读取重定位表中的第一项的目的地址内容,
//       可以发现为8个0,需要我们来手动填写符号重定位地址
@ SYM_FUNC_START_LOCAL(__relocate_kernel)
0xd5d390        ldr    w9, 0xd5d438   // offset to reloc table                                                                                                                                    │
0xd5d394        ldr    w10, 0xd5d43c  // size of reloc table  
// default virtual offset                                                                                                                                  │
0xd5d398        mov    x11, #0xffffffc0ffffffff        // #-270582939649                                                                                                  │
0xd5d39c        movk   x11, #0x800, lsl #16                                                                                                                               │
0xd5d3a0        movk   x11, #0x0     
// actual virtual offset                                                                                                                                     │
0xd5d3a4        add    x11, x11, x23   
// x9保存了.rela.dyn区域的链接地址
// x10保存了.rela.dyn区域的结束地址                                                                                                                                   │
0xd5d3a8        add    x9, x9, x11                                                                                                                                        │
0xd5d3ac        add    x10, x9, x10                                                                                                                                       │
0xd5d3b0        cmp    x9, x10                                                                                                                                            │
0xd5d3b4        b.cs   0xd5d3d4  // b.hs, b.nlast 
//获取一项offset和info+flag                                                                                                                        │
0xd5d3b8        ldp    x12, x13, [x9], #24      
//获取Addend值                                                                                                                          │
0xd5d3bc        ldur   x14, [x9, #-8]                                                                                                                                     │
0xd5d3c0        cmp    w13, #0x403                                                                                                                                        │
0xd5d3c4        b.ne   0xd5d3b0  // b.any    
//// relocate,这里主要是修正[offset + KASLR offset] = KASLR offset + append的值,也就是符号重定位了                                                                                                                             │
0xd5d3c8        add    x14, x14, x23                                                                                                                                      │
0xd5d3cc        str    x14, [x12, x23]                                                                                                                                    │
0xd5d3d0        b      0xd5d3b0                                                                                                                                           │
0xd5d3d4        ret    
@ SYM_FUNC_END(__relocate_kernel)

@ SYM_FUNC_START_LOCAL(__primary_switch)
0xd5d3d8        mov    x19, x0         // preserve new SCTLR_EL1 value         
0xd5d3dc        mrs    x20, sctlr_el1  // preserve old SCTLR_EL1 value         
0xd5d3e0        adrp   x1, 0x181d000   // 将init_pg_dir 给x1  
//跳转过去初始化mmu                                                            
0xd5d3e4        bl     0xd5d308        //__enable_mmu        
//将kernel image的.rela.dyn段实现重定位                  
0xd5d3e8        bl     0xd5d390        //__relocate_kernel       

//注意ldr指令加载的是__primary_switched的链接地址,
//    注意这里的链接地址已经是虚拟地址了(例子值为:0xffffffc008f902a0)              
0xd5d3ec        ldr    x8, 0xd5d448    //__primary_switched给x8                
0xd5d3f0        adrp   x0, 0x200000    //__PHYS_OFFSET给x0   
//注意这里的跳转指令,这个时候mmu已经生效了,由于目标地址为0xffffffc008f902a0,
//      所以这个时候查询的页表为init_pg_dir                  
0xd5d3f4        blr    x8  //跳转到__primary_switched                          
0xd5d3f8        isb                                                           
0xd5d3fc        msr    sctlr_el1, x20                                         
0xd5d400        isb                                                           
0xd5d404        bl     0x1190040                                              
0xd5d408        tlbi   vmalle1                                                
0xd5d40c        dsb    nsh                                                    
0xd5d410        isb                                                           
0xd5d414        msr    sctlr_el1, x19                                         
0xd5d418        isb                                                           
0xd5d41c        ic     iallu                                                  
0xd5d420        dsb    nsh                                                    
0xd5d424        isb                                                           
0xd5d428        bl     0xd5d390                                               
0xd5d42c        ldr    x8, 0xd5d448                                           
0xd5d430        adrp   x0, 0x200000                                           
0xd5d434        br     x8        
@ SYM_FUNC_END(__primary_switch)

  这部分主要是在页表初始化好后,进行el1的一些cpu设置,并准备好mmu开启参数。

@ 	.pushsection ".idmap.text", "awx"
@ SYM_FUNC_START(__cpu_setup)
// Invalidate local TLB
0x0000000000d5d6f4:	tlbi	vmalle1
0x0000000000d5d6f8:	dsb	nsh

0x0000000000d5d6fc:	mov	x1, #0x300000              	// #3145728
// Enable FP/ASIMD
0x0000000000d5d700:	msr	cpacr_el1, x1
// Reset mdscr_el1 and disable
0x0000000000d5d704:	mov	x1, #0x1000                	// #4096
// access to the DCC from EL0
0x0000000000d5d708:	msr	mdscr_el1, x1
// Unmask debug exceptions now,
0x0000000000d5d70c:	isb


0x0000000000d5d710:	msr	daifclr, #0x8
0x0000000000d5d714:	mrs	x1, id_aa64dfr0_el1
0x0000000000d5d718:	sbfx	x1, x1, #8, #4
0x0000000000d5d71c:	cmp	x1, #0x1
0x0000000000d5d720:	b.lt	0xd5d728  // b.tstop
0x0000000000d5d724:	msr	pmuserenr_el0, xzr
0x0000000000d5d728:	mrs	x1, id_aa64pfr0_el1
0x0000000000d5d72c:	ubfx	x1, x1, #44, #4
0x0000000000d5d730:	cbz	x1, 0xd5d738
0x0000000000d5d734:	msr	s3_3_c13_c2_3, xzr
0x0000000000d5d738:	mov	x17, #0x400000000           	// #17179869184
0x0000000000d5d73c:	movk	x17, #0x44, lsl #16
0x0000000000d5d740:	movk	x17, #0xffff
0x0000000000d5d744:	mov	x16, #0x40000000000000      	// #18014398509481984
0x0000000000d5d748:	movk	x16, #0x30, lsl #32
0x0000000000d5d74c:	movk	x16, #0xb559, lsl #16
0x0000000000d5d750:	movk	x16, #0x3519
0x0000000000d5d754:	mrs	x9, midr_el1
0x0000000000d5d758:	mov	x5, #0xffffffffffefffff    	// #-1048577
0x0000000000d5d75c:	movk	x5, #0xffff
0x0000000000d5d760:	and	x9, x9, x5
0x0000000000d5d764:	mov	x5, #0x460f0000            	// #1175388160
0x0000000000d5d768:	movk	x5, #0x10
0x0000000000d5d76c:	cmp	x9, x5
0x0000000000d5d770:	b.ne	0xd5d788  // b.any
0x0000000000d5d774:	mov	x5, #0x60000000000000      	// #27021597764222976
0x0000000000d5d778:	movk	x5, #0x0, lsl #32
0x0000000000d5d77c:	movk	x5, #0x0, lsl #16
0x0000000000d5d780:	movk	x5, #0x0
0x0000000000d5d784:	bic	x16, x16, x5
0x0000000000d5d788:	adrp	x9, 0x1555000
0x0000000000d5d78c:	ldr	x9, [x9, #3272]
0x0000000000d5d790:	bfxil	x16, x9, #0, #6
0x0000000000d5d794:	mrs	x5, id_aa64mmfr0_el1
0x0000000000d5d798:	ubfx	x5, x5, #0, #3
0x0000000000d5d79c:	mov	x6, #0x5                   	// #5
0x0000000000d5d7a0:	cmp	x5, x6
0x0000000000d5d7a4:	csel	x5, x6, x5, hi  // hi = pmore
0x0000000000d5d7a8:	bfi	x16, x5, #32, #3


0x0000000000d5d7ac:	mrs	x9, id_aa64mmfr1_el1
0x0000000000d5d7b0:	and	x9, x9, #0xf
0x0000000000d5d7b4:	cbz	x9, 0xd5d7bc
0x0000000000d5d7b8:	orr	x16, x16, #0x8000000000


0x0000000000d5d7bc:	msr	mair_el1, x17
0x0000000000d5d7c0:	msr	tcr_el1, x16

/*
  * Prepare SCTLR, INIT_SCTLR_EL1_MMU_ON 给 x0
  */
0x0000000000d5d7c4:	mov	x0, #0x200000000000000     	// #144115188075855872
0x0000000000d5d7c8:	movk	x0, #0x20, lsl #32
0x0000000000d5d7cc:	movk	x0, #0x34f4, lsl #16
0x0000000000d5d7d0:	movk	x0, #0xd91d

// return to head.S
0x0000000000d5d7d4:	ret
0x0000000000d5d7d8:	msr	tpidr_el2, x13
0x0000000000d5d7dc:	msr	disr_el1, xzr
@ SYM_FUNC_END(__cpu_setup)

  当我们进入__primary_switched的时候,这个时候用的是内核地址。同时地址翻译是由init_pg_dir来完成。此过程初始化init_task,将

@ SYM_FUNC_START_LOCAL(__primary_switched)

//将init_task给x4
0xffffffc008f902a0      adrp   x4, 0xffffffc00934f000 <inet6_offloads+1376>                        
0xffffffc008f902a4      add    x4, x4, #0xb80   

//init_cpu_task                                                   
0xffffffc008f902a8      msr    sp_el0, x4                                                          
0xffffffc008f902ac      ldr    x5, [x4, #24]                                                       
0xffffffc008f902b0      add    sp, x5, #0x4, lsl #12                                               
0xffffffc008f902b4      sub    sp, sp, #0x150                                                      
0xffffffc008f902b8      stp    xzr, xzr, [sp, #304]                                                
0xffffffc008f902bc      add    x29, sp, #0x130                                                     
0xffffffc008f902c0      adrp   x5, 0xffffffc009349000 <event_hash+616>                             
0xffffffc008f902c4      add    x5, x5, #0x7f8                                                      
0xffffffc008f902c8      ldr    w6, [x4, #64]                                                       
0xffffffc008f902cc      ldr    x5, [x5, x6, lsl #3]                                                
0xffffffc008f902d0      msr    tpidr_el1, x5          

//加载异常向量表地址                                                                             
0xffffffc008f902d4      adrp   x8, 0xffffffc008010000 <bcm2835_handle_irq>                         
0xffffffc008f902d8      add    x8, x8, #0x800                                                      
0xffffffc008f902dc      msr    vbar_el1, x8                                                        
0xffffffc008f902e0      isb 

//将x29,x30放入到sp
0xffffffc008f902e4      stp    x29, x30, [sp, #-16]!                                               
0xffffffc008f902e8      mov    x29, sp  

// Save FDT pointer                                                                                                        
0xffffffc008f902ec      adrp   x5, 0xffffffc009001000 <tmp_cmdline.73085+2040>                     
0xffffffc008f902f0      str    x21, [x5, #920]                      

// Save the offset between                                                                              
0xffffffc008f902f4      adrp   x4, 0xffffffc008b70000 <kimage_vaddr>                                                                              
0xffffffc008f902f8      ldr    x4, [x4]       
// the kernel virtual and                                                                                                    
0xffffffc008f902fc      sub    x4, x4, x0                                    
// physical mappings                                                                     
0xffffffc008f90300      adrp   x5, 0xffffffc008ebb000 <rt_sched_class+192>                                                                        
0xffffffc008f90304      str    x4, [x5, #3392]       

//Clear BSS                                                                                             
0xffffffc008f90308      adrp   x0, 0xffffffc009529000 <__kvm_nvhe_cur>                                                                            
0xffffffc008f9030c      add    x0, x0, #0x0                                                                                                       
0xffffffc008f90310      mov    x1, xzr                                                                                                            
0xffffffc008f90314      adrp   x2, 0xffffffc00961c000 <gssp_stats+24>                                                                             
0xffffffc008f90318      add    x2, x2, #0xf8                                                                                                      
0xffffffc008f9031c      sub    x2, x2, x0                                                                                                         
0xffffffc008f90320      bl     0xffffffc008664200 <memset>                                                                                        
0xffffffc008f90324      dsb    ishst    

// pass FDT address in x0                                                                                                          
0xffffffc008f90328      mov    x0, x21    
// Try mapping the FDT early                                                                                                        
0xffffffc008f9032c      bl     0xffffffc008f946c8 <early_fdt_map>    
// Parse cpu feature overrides                                                                    
0xffffffc008f90330      bl     0xffffffc008f96354 <init_feature_override>    
//already running randomized?                                                                     
0xffffffc008f90334      tst    x23, #0xffffffffffe00000                                                                                           
0xffffffc008f90338      b.ne   0xffffffc008f90350 <__primary_switched+176>  // b.any
// parse FDT for KASLR options                                                              
0xffffffc008f9033c      bl     0xffffffc008f96b80 <kaslr_early_init>  
// KASLR disabled? just proceed          这里直接跳转到 0xffffffc008f90350                                                              
0xffffffc008f90340      cbz    x0, 0xffffffc008f90350 <__primary_switched+176>  
// record KASLR offset                                                                  
0xffffffc008f90344      orr    x23, x23, x0  
// we must enable KASLR, return                                                                                                     
0xffffffc008f90348      ldp    x29, x30, [sp], #16      
// to __primary_switch()                                                                                         
0xffffffc008f9034c      ret  

// Prefer VHE if possible
0xffffffc008f90350      bl     0xffffffc008021e6c <switch_to_vhe>                                                                                 
0xffffffc008f90354      ldp    x29, x30, [sp], #16                  
//跳转到kernel_start                                                                              
0xffffffc008f90358      bl     0xffffffc008f90c40 <start_kernel>                                                                                  
0xffffffc008f9035c      brk    #0x800                                                                                                             
0xffffffc008f90360      msr    tpidr_el2, x5  
@ SYM_FUNC_END(__primary_switched)

  注意,阅读本文这部分时,需要对照着head.S来看,这样才有一个基本的认识。





后记


  本文得到的基本流程为,上电,保存上电后的基础寄存器,在el2的模式下初始化cpu,保存启动标志,初始化两个页表(注意,这部分是精华,我这部分注释也最多),然后初始化el1模式下的cpu,并准备好开启mmu,最后在__primary_switch里面开启mmu,当mmu开启后,这个时候的地址还是物理地址,所以我们需要一个映射物理地址和虚拟地址相等的页表(idmap_pg_dir),重定位符号表,最后加载__primary_switched的虚拟地址(其虚拟地址在kernel logical memory map中),跳转到执行(此时查表init_pg_dir),然后初始化init_task,最后进入start_kernel。

  本文也没有尝试去完全注释每句代码,太繁杂了,对于我来说也没有意义,特别是一些特别的初始化,只有以后遇到了才去查询。Linux现代内核太大了,后面可能就要去看个人喜欢的模块,这样才有收获,不要尝试去阅读全部内容,那样太难了。

  每个人对于这部分的关注可能都是不一样的,如果本文没有写的地方,可以尝试搜索对应的关键字,可以得到更多的信息。

参考文献

[1]https://blog.csdn.net/u011728480/article/details/79498816 [2]https://blog.csdn.net/u011728480/article/details/88420467 [3]https://blog.csdn.net/u011728480/article/details/88553602 [4]https://blog.csdn.net/u011728480/article/details/114491022 [5]https://developer.arm.com/documentation/100933/0100 [6]https://www.kernel.org/doc/html/latest/arm64/memory.html


打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)
qrc_img

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

标签:__,head,aarch64,mov,el2,Linux,x10,x0,x1
From: https://www.cnblogs.com/Iflyinsky/p/18149167

相关文章

  • linux进程相关命令
    知道一个程序的PID,可以进入目录/proc/PID查看进程的具体信息。PSps命令是一个用于显示进程信息的常用命令。以下是ps命令的一些常用选项:-e:显示所有进程,包括系统进程。-f:显示完整的进程信息,包括进程的详细信息。-l:显示更多的列,包括进程的状态、CPU使用情况等。-u:显示指......
  • linux系统文字编码,通过命令设置为utf8编码
    转自:https://worktile.com/kb/ask/489959.html要将Linux系统设置为UTF-8编码,可以通过以下步骤进行:1.检查系统当前的默认字符集。在终端中输入以下命令并查看输出:“`locale“`如果输出中的LC_ALL或LANG字段不以UTF-8结尾,则系统默认字符集不是UTF-8。2.编辑本地......
  • linux9-
    1.新建会话 tmuxnew-s会话名2.显示会话 tmuxls3.隐藏会话tmuxdetach4.找到会话tmuxattach-t会话名5.上下多窗格tmuxsplit-window6.左右多窗格tmuxsplit-window-h7.杀死会话 tmuxkill-session-t  会话id //不实用还不如exit8.上下左右切换tmuxs......
  • PT Application Inspector 4.5 (Linux) - 静态、动态和交互式应用程序安全测试
    PTApplicationInspector4.5(Linux)-静态、动态和交互式应用程序安全测试唯一一款提供高质量分析和便捷工具以自动确认漏洞的源代码分析器请访问原文链接:PTApplicationInspector4.5(Linux)-静态、动态和交互式应用程序安全测试,查看最新版。原创作品,转载请保留出处。......
  • linux shell 编程学习总结
    1文件和数组1.1读文件并将文件内容保存到数组,遍历数组src.f文件内容./src/xxx_1.md./src/xxx_2.md./src/xxx_3.md./src/xxx_4.md./src/xxx_5.mdrun.sh#!/bin/bash###readflisttoarraysrc_array=()whilereadline;dosrc_array+=("$line")done<$1##......
  • 在Linux中,发行版和内核有什么区别?
    在Linux世界中,内核版本和发行版之间的区别是理解Linux操作系统生态体系的关键点。以下是两者之间详尽的区别:1.Linux内核:内核是Linux操作系统的核心组件,它是操作系统中最基础的部分,负责管理和调度计算机硬件资源,如处理器、内存、硬盘驱动器和其他外设。它的主要职责包括:硬件抽......
  • 在Linux中,开源软件和自由软件的区别?
    在Linux和更广泛的软件领域中,开源软件(OpenSourceSoftware)和自由软件(FreeSoftware)是两个经常被提及且容易混淆的概念。尽管它们共享一些相似之处,但它们在理念和哲学上存在一些关键的区别。1.开源软件开源软件强调的是软件的源代码对用户可见,用户可以查看、修改和分发软件的源......
  • 在Linux中,什么是Linux shell?
    在Linux中,Shell是一个命令行解释器,它为用户提供了一个与操作系统交互的文本界面。用户可以通过Shell输入命令,Shell会解释这些命令并将其转换为操作系统能够理解的信号或进程,从而执行相应的操作。1.主要特点命令解释器:Shell读取用户输入的命令,将其转换为操作系统能够执行......
  • 在Linux中,Unix和Linux之间的关系是什么?
    Unix和Linux之间的关系是既有联系又有区别的。它们都是操作系统,但在设计哲学、版权和发展历程上存在一些差异。1.Unix起源:Unix最早是在20世纪70年代由AT&T的贝尔实验室开发的一种多用户、多任务的操作系统。设计:Unix以其简洁、模块化的设计和强大的文本处理能力而闻名。......
  • NanoPi-NEO 全志H3移植Ubuntu 22.04 LTS、u-boot、Linux内核/内核树、mt7601u USB-Wi-
    前言想在NanoPi-NEO上开发屏幕驱动,但是看了下文件目录发现没有内核树,导致最基础的file_operations结构体都无法使用,于是寻找内核树安装方法。但官方提供的内核为4.14太旧了apt找不到对应的linux-source版本(其实后面发现不需要用apt,可以在kernel.org上下载,但反正都装了那就当学习......