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


    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就是地址0x2000000x200004的指令。整个0x2000000x200040就是内核镜像的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, 每个表5120x0000000001190104:	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)存入二级页表地址(0x117f0000x0000000001190138:	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, 每个表5120x0000000001190158:	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 读取重定位表中的第一项的目的地址内容,
    //       可以发现为80,需要我们来手动填写符号重定位地址
    @ 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        // #-2705829396490xd5d39c        movk   x11, #0x800, lsl #160xd5d3a0        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, #0x4030xd5d3c4        b.ne   0xd5d3b0  // b.any    
    //// relocate,这里主要是修正[offset + KASLR offset] = KASLR offset + append的值,也就是符号重定位了                                                                                                                             │
    0xd5d3c8        add    x14, x14, x23                                                                                                                                      │
    0xd5d3cc        str    x14, [x12, x23]                                                                                                                                    │
    0xd5d3d0        b      0xd5d3b00xd5d3d4        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的链接地址,
    //    注意这里的链接地址已经是虚拟地址了(例子值为:0xffffffc008f902a00xd5d3ec        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 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 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                          
    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 .73085+2040>                     
    0xffffffc008f902f0      str    x21, [x5, #920]                      
    
    // Save the offset between                                                                              
    0xffffffc008f902f4      adrp   x4, 0xffffffc008b70000                                                                               
    0xffffffc008f902f8      ldr    x4, [x4]       
    // the kernel virtual and                                                                                                    
    0xffffffc008f902fc      sub    x4, x4, x0                                    
    // physical mappings                                                                     
    0xffffffc008f90300      adrp   x5, 0xffffffc008ebb000 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 24>                                                                             
    0xffffffc008f90318      add    x2, x2, #0xf8                                                                                                      
    0xffffffc008f9031c      sub    x2, x2, x0                                                                                                         
    0xffffffc008f90320      bl     0xffffffc008664200                                                                                         
    0xffffffc008f90324      dsb    ishst    
    
    // pass FDT address in x0                                                                                                          
    0xffffffc008f90328      mov    x0, x21    
    // Try mapping the FDT early                                                                                                        
    0xffffffc008f9032c      bl     0xffffffc008f946c8     
    // Parse cpu feature overrides                                                                    
    0xffffffc008f90330      bl     0xffffffc008f96354     
    //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 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                                                                                  
    0xffffffc008f90354      ldp    x29, x30, [sp], #16                  
    //跳转到kernel_start                                                                              
    0xffffffc008f90358      bl     0xffffffc008f90c40                                                                                   
    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: 有问题请留言,看到后我会第一时间回复。

  • 相关阅读:
    如果不富有,那就像有钱人一样去行动吧_《有钱人和你想的不一样》读书笔记
    第2关:ACL访问控制列表
    H41H-64C止回阀型号解析
    Savepoints
    高效率开发APP中的常见问题
    基于django的购物商城系统
    go的context.WithTimeout学习
    Apache Doris的Colocation join本地join实现
    深度分页、唯一索引的坑、分库分表、查询分离、连接池、bufferpool优化等
    Vue2进阶篇学习笔记
  • 原文地址:https://www.cnblogs.com/Iflyinsky/p/18149167