基于QEMU的vexpress-a9的初始化代码运行(一)

这个part是想详细走读一下用qemu运行kernel的最初始代码,也就是使用qemu运行kernel代码的详细逻辑,从qemu加载根目录下vmlinux镜像的逻辑,也就是运行arch/arm/kernel/head.S的整个过程,直到跳转到start_kernel,使用的kernel版本还是3.18。

初始运行状态

沿用上回《基于QEMU的vexpress-a9开发调试环境搭建》的最后一部分调试kernel汇编这一个part,我们需要将vmlinux的链接地址设置到0x60008000,设置方法是修改arch/arm/Kconfig中VMSPLIT_2G对应的值到0x60000000,并重新编译kernel,接着就可以直接b *0x60008000,并且执行到arch/arm/kernel/head.S,这样,我们就可以直接通过命令c运行到arch/arm/kernel/head.S的第一句代码(bl __hyp_stub_install),这其中跳过了compressed/vmlinux的解压内核等逻辑,后续章节再补充。
在这里插入图片描述
在这里插入图片描述
在此,我们打印一下运行到此处时的寄存器
在这里插入图片描述

// arch/arm/kernel/head.S主线代码
#ifdef CONFIG_ARM_VIRT_EXT
	bl	__hyp_stub_install    // 1:如有必要,执行hyp模式的初始化配置
#endif
	@ ensure svc mode and all interrupts masked
	safe_svcmode_maskall r9   // 2: 进入SVC模式,屏蔽中断

	mrc	p15, 0, r9, c0, c0		@ get processor id
	bl	__lookup_processor_type		@ r5=procinfo r9=cpuid   // 3.根据CPU id查找cpu执行函数
	movs	r10, r5				@ invalid processor (r5=0)?

1. __hyp_stub_install的执行

如果定义了CONFIG_ARM_VIRT_EXT宏,则进入arch/arm/kernel/head.S的第一行代码就是bl __hyp_stub_install,__hyp_stub_install首先执行store_primary_cpu_mode,这个方法是用来记录主cpu运行模式的, 只有主CPU在初始化时候才会调用这个方法,这个方法其实是将主cpu当前cpsr寄存器中存的cpu模式(bit0-bit4,当前值为0x13,代表SVC_MODE),存入到__boot_cpu_mode的位置。后面__syp_stub_install_secondary其实用来给其他CPU,检查当前cpu模式,如果和主CPU模式一致,并且也是HYP_MODE,则初始化hyp-stub(这个部分我们不展开讨论了,因为不会走到),由于我们主CPU是SVC_MODE,所以不满足第二个条件,所以在执行到114行代码时,就会retne lr,返回head.S的主代码逻辑。

在这里插入图片描述

2. safe_svcmode_maskall的执行

这部分做的是强制让cpu进入SVC模式,并屏蔽中断。

/*
 * Helper macro to enter SVC mode cleanly and mask interrupts. reg is
 * a scratch register for the macro to overwrite.
 *
 * This macro is intended for forcing the CPU into SVC mode at boot time.
 * you cannot return to the original mode.
 */
.macro safe_svcmode_maskall reg:req
#if __LINUX_ARM_ARCH__ >= 6 && !defined(CONFIG_CPU_V7M)
	mrs	\reg , cpsr  //读取cpsr到reg
	eor	\reg, \reg, #HYP_MODE //reg和HYP_MODE(0x14)进行异或,结果存到reg
	tst	\reg, #MODE_MASK // 测试是否与0x1F相同,由于cpsr中是SVC,与HYP异或后上一步结果非0,所以这里cpsr中Z位置0
	bic	\reg , \reg , #MODE_MASK //清除reg中模式位
	orr	\reg , \reg , #PSR_I_BIT | PSR_F_BIT | SVC_MODE //reg中屏蔽中断和快速中断,并设置SVC模式
THUMB(	orr	\reg , \reg , #PSR_T_BIT	)
	bne	1f //由于cpsr中Z为0,跳转到1:的位置。
	orr	\reg, \reg, #PSR_A_BIT //将r0,第8位,exception位清零
	adr	lr, BSYM(2f) //badr是设置lr到2:这个位置,也就是跳出这个函数了。
	msr	spsr_cxsf, \reg //将hyp模式的spsr,设置成新的svc的cpsr
	__MSR_ELR_HYP(14) //这两句是thumb语句,ignore
	__ERET
1:	msr	cpsr_c, \reg //将reg中的结果,设置到cpsr_c,也就是cpsr的低8位里
2:
......

通过上面代码分析可知,如果当前cpu模式为HYP模式,需要额外将新的cpsr设置到HYP模式的spsr中,否则,就只是屏蔽中断/快中断,并让cpu进入svc模式。下面图可以看出HYP模式有自己的spsr寄存器。
在这里插入图片描述

3 __lookup_processor_type的执行

这部分是在.proc.info.init数据中,查找对应cpu的私有执行方法。具体步骤为:

  1. 调用CP15寄存器方法(mrc p15, 0, r9, c0, c0),获得cpu id。
  2. 通过cpu id,到.proc.init段中查找对应的cpu的私有执行方法。
  3. 找到后存入r5寄存器返回
/*
 * Read processor ID register (CP#15, CR0), and look up in the linker-built
 * supported processor list.  Note that we can't use the absolute addresses
 * for the __proc_info lists since we aren't running with the MMU on
 * (and therefore, we are not in the correct address space).  We have to
 * calculate the offset.
 *
 *	r9 = cpuid
 * Returns:
 *	r3, r4, r6 corrupted
 *	r5 = proc_info pointer in physical address space
 *	r9 = cpuid (preserved)
 */
__lookup_processor_type:
	adr	r3, __lookup_processor_type_data // r3存放__lookup_processor_type_data的物理地址
	ldmia	r3, {r4 - r6}   //r4存放__lookup_processor_type_data的链接地址,r5存放__proc_info_start链接地址,r6存放__proc_info_end链接地址
	sub	r3, r3, r4			@ get offset between virt&phys //用r3-r4,__lookup_processor_type_data的物理地址减去链接地址,r3存放物理地址与链接地址的差值
	add	r5, r5, r3			@ convert virt addresses to//r5存放__proc_info_start物理地址
	add	r6, r6, r3			@ physical address space //r6存放__proc_info_end物理地址
1:	ldmia	r5, {r3, r4}			@ value, mask // r3存放proc_info的cpu id,r4存掩码
	and	r4, r4, r9			@ mask wanted bits  //将r9存的cpu id与掩码与一下
	teq	r3, r4				//与r9存放的cpu id比较
	beq	2f				    //相等则找到, r5存的就是CPU私有执行方法,条转到2:
	add	r5, r5, #PROC_INFO_SZ		@ sizeof(proc_info_list) //找不到,则将r5移动一个sizeof(proc_info_list),尝试下一个proc_info_list
	cmp	r5, r6				//对比一下r5和r6
	blo	1b				    //如果不相等,则跳转到1:尝试下一个proc_info_list
	mov	r5, #0				@ unknown processor //如果相等,则比较过了所有在proc-v7.S中的配置项,没找到可用的cpu执行方法,r5存0,表示没找到
2:	ret	lr                  // 方法返回
ENDPROC(__lookup_processor_type)
/*
 * Look in <asm/procinfo.h> for information about the __proc_info structure.
 */
	.align	2
	.type	__lookup_processor_type_data, %object
__lookup_processor_type_data:
	.long	.
	.long	__proc_info_begin //在arch/arm/kernel/vmlinux.lds.S中标注,存放.proc.info.init数据
	.long	__proc_info_end
	.size	__lookup_processor_type_data, . - __lookup_processor_type_data

在arch/arm/kernel/vmlinux.lds.S可知,__proc_info_begin~__proc_info_end存放着.proc.info.init内容。

而实际的CPU执行方法配置项放在arch/arm/mm/proc-v7.S中:

	.section ".proc.info.init", #alloc, #execinstr

//定义__v7_proc宏,放置一系列CPU处理函数的位置
.macro __v7_proc initfunc, mm_mmuflags = 0, io_mmuflags = 0, hwcaps = 0, proc_fns = v7_processor_functions
	ALT_SMP(.long	PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \
			PMD_SECT_AF | PMD_FLAGS_SMP | \mm_mmuflags)
	ALT_UP(.long	PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \
			PMD_SECT_AF | PMD_FLAGS_UP | \mm_mmuflags)
	.long	PMD_TYPE_SECT | PMD_SECT_AP_WRITE | \
		PMD_SECT_AP_READ | PMD_SECT_AF | \io_mmuflags
	W(b)	\initfunc
	.long	cpu_arch_name
	.long	cpu_elf_name
	.long	HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_FAST_MULT | \
		HWCAP_EDSP | HWCAP_TLS | \hwcaps
	.long	cpu_v7_name
	.long	\proc_fns
	.long	v7wbi_tlb_fns
	.long	v6_user_fns
	.long	v7_cache_fns
.endm

#ifndef CONFIG_ARM_LPAE
	/*
	 * ARM Ltd. Cortex A5 processor.
	 */
	.type   __v7_ca5mp_proc_info, #object
__v7_ca5mp_proc_info:
	.long	0x410fc050   //v7 a5的cpu id
	.long	0xff0ffff0	 //v7 a5的cpu掩码
	__v7_proc __v7_ca5mp_setup
	.size	__v7_ca5mp_proc_info, . - __v7_ca5mp_proc_info

	/*
	 * ARM Ltd. Cortex A9 processor.
	 */
	.type   __v7_ca9mp_proc_info, #object
__v7_ca9mp_proc_info:
	.long	0x410fc090 //v7 a9的cpu id,这个就是对应vexpress-9的cpu id
	.long	0xff0ffff0 //v7 a9的cpu掩码
	__v7_proc __v7_ca9mp_setup, proc_fns = ca9mp_processor_functions
	.size	__v7_ca9mp_proc_info, . - __v7_ca9mp_proc_info

#endif	/* CONFIG_ARM_LPAE */
...

接下来我们继续看arch/arm/kernel/head.S的主线代码:

	......
#ifndef CONFIG_XIP_KERNEL
	adr	r3, 2f				//r3 存放跳转2的地址
	ldmia	r3, {r4, r8}
	sub	r4, r3, r4			@ (PHYS_OFFSET - PAGE_OFFSET)
	add	r8, r8, r4			@ PHYS_OFFSET
#else
	ldr	r8, =PLAT_PHYS_OFFSET		@ always constant in this case
#endif
	/*
	 * r1 = machine no, r2 = atags or dtb,
	 * r8 = phys_offset, r9 = cpuid, r10 = procinfo
	 * 这里打印一下我当前寄存这几个值
	 * (gdb) i r r1 r2 r8 r9 r10
			r1             0x8e0    2272
			r2             0x68000000       1744830464
			r8             0x68000000       1744830464
			r9             0x410fc090       1091551376
			r10            0x6047d478       1615320184
	 */
	bl	__vet_atags //1:
#ifdef CONFIG_SMP_ON_UP
	bl	__fixup_smp //2: 
#endif
#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
	bl	__fixup_pv_table //3:
#endif
	bl	__create_page_tables //4:
	/*
	 * The following calls CPU specific code in a position independent
	 * manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of
	 * xxx_proc_info structure selected by __lookup_processor_type
	 * above.  On return, the CPU will be ready for the MMU to be
	 * turned on, and r0 will hold the CPU control register value.
	 */
	ldr	r13, =__mmap_switched		@ address to jump to after
						@ mmu has been enabled
	adr	lr, BSYM(1f)			@ return (PIC) address
	mov	r8, r4				@ set TTBR1 to swapper_pg_dir
 ARM(	add	pc, r10, #PROCINFO_INITFUNC	) //将PROCINFO_INITFUNC所在位置赋值给r10(0x6047d478)
 THUMB(	add	r12, r10, #PROCINFO_INITFUNC	)
 THUMB(	ret	r12				)
1:	b	__enable_mmu //单向跳转到__enable_mmu

4.__vet_atags

这部分直接看注释就能明白,这里是判断r2有效性的,r2存放的要么是DTB数据,要么是ATA_CORE数据,否则就是非法数据,r2赋值为0后返回。

/* Determine validity of the r2 atags pointer.  The heuristic requires
 * that the pointer be aligned, in the first 16k of physical RAM and
 * that the ATAG_CORE marker is first and present.  If CONFIG_OF_FLATTREE
 * is selected, then it will also accept a dtb pointer.  Future revisions
 * of this function may be more lenient with the physical address and
 * may also be able to move the ATAGS block if necessary.
 *
 * Returns:
 *  r2 either valid atags pointer, valid dtb pointer, or zero
 *  r5, r6 corrupted
 */
__vet_atags:
	tst	r2, #0x3			@ aligned? //是否2bit对齐?
	bne	1f					//如果Z位为0,表示没有对齐,则跳转到1:

	ldr	r5, [r2, #0]		//r5存放r2地址上的值
#ifdef CONFIG_OF_FLATTREE
	ldr	r6, =OF_DT_MAGIC		@ is it a DTB?  //r6存一下OF_DT_MAGIC值
	cmp	r5, r6					//判断r2地址上的值是否等于OF_DT_MAGIC,以判断是否是DTB
	beq	2f						//是则跳转到2
#endif
	cmp	r5, #ATAG_CORE_SIZE		@ is first tag ATAG_CORE? //再判断是否是合法的ATA_CORE信息
	cmpne	r5, #ATAG_CORE_SIZE_EMPTY
	bne	1f
	ldr	r5, [r2, #4]
	ldr	r6, =ATAG_CORE
	cmp	r5, r6
	bne	1f

2:	ret	lr				@ atag/dtb pointer is ok

1:	mov	r2, #0 			//错误处理:r2存的非DTB或ATA_CORE数据或者r2没有对齐,设置r2为0
	ret	lr
ENDPROC(__vet_atags)

5. __fixup_smp

这部分是判断CPU架构是否支持smp(Symmetric Multiprocessing,即对称多处理),如果支持,但硬件为单核,则做指令替换,将所有ALT_SMP(xxx)指令替换为ALT_UP指令。否则不做任何替换处理。具体可以参考

  1. https://stackoverflow.com/questions/17083941/what-does-alt-smp-and-alt-up-does
  2. https://blog.csdn.net/fh400/article/details/8282841
#ifdef CONFIG_SMP_ON_UP
	__HEAD
__fixup_smp:
	and	r3, r9, #0x000f0000	@ architecture version 
	teq	r3, #0x000f0000		@ CPU ID supported?  //通过CPU id判断是否支持
	bne	__fixup_smp_on_up	@ no, assume UP  //不支持,跳转到支持UP(即单核CPU系统)的处理

	bic	r3, r9, #0x00ff0000
	bic	r3, r3, #0x0000000f	@ mask 0xff00fff0
	mov	r4, #0x41000000
	orr	r4, r4, #0x0000b000
	orr	r4, r4, #0x00000020	@ val 0x4100b020
	teq	r3, r4			@ ARM 11MPCore?
	reteq	lr			@ yes, assume SMP //如果是 ARM 11MPCore这里返回

	mrc	p15, 0, r0, c0, c0, 5	@ read MPIDR //MPIDR是个描述多核亲缘性的寄存器,读到R0寄存器,参考//https://developer.arm.com/documentation/ddi0406/b/System-Level-Architecture/Protected-Memory-System-Architecture--PMSA-/CP15-registers-for-a-PMSA-implementation/c0--Multiprocessor-Affinity-Register--MPIDR-
	and	r0, r0, #0xc0000000	@ multiprocessing extensions and
	teq	r0, #0x80000000		@ not part of a uniprocessor system?
	bne    __fixup_smp_on_up	@ no, assume UP //如果MPIDR表明不支持多核

	@ Core indicates it is SMP. Check for Aegis SOC where a single
	@ Cortex-A9 CPU is present but SMP operations fault.
	mov	r4, #0x41000000		
	orr	r4, r4, #0x0000c000
	orr	r4, r4, #0x00000090	//
	teq	r3, r4			@ Check for ARM Cortex-A9
	retne	lr			@ Not ARM Cortex-A9, //直接判断是否是cortex-a9处理器,不是就返回

	@ If a future SoC *does* use 0x0 as the PERIPH_BASE, then the
	@ below address check will need to be #ifdef'd or equivalent
	@ for the Aegis platform.
	mrc	p15, 4, r0, c15, c0	@ get SCU base address  
	teq	r0, #0x0		@ '0' on actual UP A9 hardware //r0读出来是0x1e000000
	beq	__fixup_smp_on_up	@ So its an A9 UP
	ldr	r0, [r0, #4]		@ read SCU Config  //这里读出r0是0x10
ARM_BE8(rev	r0, r0)			@ byteswap if big endian
	and	r0, r0, #0x3		@ number of CPUs	// r0读出是0,也就是只有一个核
	teq	r0, #0x0		@ is 1?
	retne	lr

__fixup_smp_on_up:  //处理单核程序,将ALT_SMP(xx)指令替换为ALT_UP(xx)
	adr	r0, 1f
	ldmia	r0, {r3 - r5}
	sub	r3, r0, r3
	add	r4, r4, r3
	add	r5, r5, r3
	b	__do_fixup_smp_on_up //执行替换
ENDPROC(__fixup_smp)

	.align
1:	.word	.
	.word	__smpalt_begin //对应.alt.smp.init段
	.word	__smpalt_end

	.pushsection .data
	.globl	smp_on_up
smp_on_up:
	ALT_SMP(.long	1)
	ALT_UP(.long	0)
	.popsection
#endif

	.text
__do_fixup_smp_on_up: //逐个替换
	cmp	r4, r5
	reths	lr
	ldmia	r4!, {r0, r6}
 ARM(	str	r6, [r0, r3]	)
 THUMB(	add	r0, r0, r3	)
#ifdef __ARMEB__
 THUMB(	mov	r6, r6, ror #16	)	@ Convert word order for big-endian.
#endif
 THUMB(	strh	r6, [r0], #2	)	@ For Thumb-2, store as two halfwords
 THUMB(	mov	r6, r6, lsr #16	)	@ to be robust against misaligned r3.
 THUMB(	strh	r6, [r0]	)
	b	__do_fixup_smp_on_up
ENDPROC(__do_fixup_smp_on_up)

CPUID寄存器内容:
字段名:Implementer(venter 销售ID)|Variant(大版本号) | Architecture(架构版本)| Part Num(产品代码)|Revision(小版本号)
基址偏移量: [31-24] | [23-20] | [19-16] | [15-4] | [3-0]

6. __create_page_tables

这个是给kernel创建临时页表,我们知道,要执行速度快,需要开启mmu和cache,前提是要创建页表做地址映射。这一步主要是段映射,可以参考(https://zhuanlan.zhihu.com/p/578336642)

/*
 * Setup the initial page tables.  We only setup the barest
 * amount which are required to get the kernel running, which
 * generally means mapping in the kernel code.
 *
 * r8 = phys_offset, r9 = cpuid, r10 = procinfo
 *
 * Returns:
 *  r0, r3, r5-r7 corrupted
 *  r4 = page table (see ARCH_PGD_SHIFT in asm/memory.h)
 */
 /* 当前相关寄存器读值
 	r8             0x60000000
    r9             0x410fc090
    r10           0x6047d478
 */
__create_page_tables:
	pgtbl	r4, r8				@ page table address //根据0x60000000计算出page table所在位置0x60004000,作为一级页表的地址。

	/*
	 * Clear the swapper page table
	 */
	mov	r0, r4  // r0为0x60004000
	mov	r3, #0
	add	r6, r0, #PG_DIR_SIZE // r6为0x0x60008000
1:	str	r3, [r0], #4 //将0x60004000到0x60008000都设置成0x0,也就是将一级页表需要的16K全部清零。由于每个地址需要4个字节,则有4K个地址,又每个地址对应1M地址,所以16K地址能够总共映射4K*1M=4G地址。
	str	r3, [r0], #4
	str	r3, [r0], #4
	str	r3, [r0], #4
	teq	r0, r6
	bne	1b

#ifdef CONFIG_ARM_LPAE //CONFIG_ARM_LPAE未设置
...
#endif

	ldr	r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags //读取proc_info 的PROCINFO_MM_MMUFLAGS为0xc0e,读到r7

	/*
	 * Create identity mapping to cater for __enable_mmu.
	 * This identity mapping will be removed by paging_init().
	 */
	adr	r0, __turn_mmu_on_loc  
	ldmia	r0, {r3, r5, r6} //将__turn_mmu_on的链接地址读到r5,__turn_mmu_on_end链接地址放到r6, r3放当前实际运行地址。
	sub	r0, r0, r3			@ virt->phys offset //计算得到r0, r5,r6的实际运行地址,也就是待映射的物理地址
	add	r5, r5, r0			@ phys __turn_mmu_on  // r5实际运行地址: 0x6047c8f0
	add	r6, r6, r0			@ phys __turn_mmu_on_end // r6实际运行地址: 0x6047c910
	mov	r5, r5, lsr #SECTION_SHIFT // r5, r6右移1M,都变成0x604,这个是一级页表中映射r5,r6的index
	mov	r6, r6, lsr #SECTION_SHIFT

1:	orr	r3, r7, r5, lsl #SECTION_SHIFT	@ flags + kernel base // r3:0x60400c0e = 0x604<<20 + 0xc0e
	str	r3, [r4, r5, lsl #PMD_ORDER]	@ identity mapping // 将r3(0x60400c0e)值放到0x60005810=0x60004000+(0x604<<2)地址上,相当于配置了__turn_mmu_on 开始的页表
	//以上相当于将__turn_mmu_on开始的实际运行地址所对应的页面地址,放到页表index为0x604对应的地址上
	cmp	r5, r6
	addlo	r5, r5, #1			@ next section
	blo	1b //判断__turn_mmu_on  到__turn_mmu_on_end是否映射完毕

	/*
	 * Map our RAM from the start to the end of the kernel .bss section.
	 * 开始映射物理地址开始到kernel镜像结尾,开始物理地址为0x60000000,kernel结尾为_end
	 */
	add	r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER) // r0 = r4 + (0x60000000>>(20-2))=0x60004000+0x1800=0x60005800
	ldr	r6, =(_end - 1)  //_end为kernel镜像尾部链接地址 = 0x6067eb33
	orr	r3, r8, r7 // r3 = 0x60000000 | 0xc0e = 0x60000c0e
	add	r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER) // r6 = 0x60004000 + 0x6067eb33>>(20-2) = 0x60005819
1:	str	r3, [r0], #1 << PMD_ORDER //把r3的值放到0x60005800上,并且r0的值+4, r0 = 0x60005804
	add	r3, r3, #1 << SECTION_SHIFT //  r3= r3+1<<20 = 0x60100c0f
	cmp	r0, r6 // 让r0(0x60005804)和r6(0x60005819)比较,看有没有将kernel映射完
	bls	1b
//从上面我们可以看到段映射的规律。
// 1. 每个页表映射1M,映射4G,需要0x4000 = 16K地址空间,每个页表地址占4个字节。0x400占一共14个bit,32-14=18,所以一级页表高18位是固定位。作为页表基地址范围。
// 2. 实际待映射的地址的高12位,对应的就是页表index,比如kernel开始实际得地址0x60000000,取高12位0x600,再左移2位=0x1800,0x60004000+0x1800=0x60005800就是一级页表所在的实际地址。
// 3. 一级页表内容的高(12)位为实际物理地址的高(12)位,与虚地址的低(20)位拼接后,得到虚地址映射的实际地址。一级页表内容的低(20)位内容参考下图字段解释。
// 4. 综上,整个映射流程demo:
// 如果页表首地址为0x60000000,得到一个虚地址是0x12345678,则取虚地址高12位0x123,左移2位得到0x48c,页表首地址的高18位:0x60000000,相或,得到这个地址的页表地址为0x6000048c。
// 如果地址0x6000048c存的值为0xabcdef12,则取其高12位,与实际地址相加为0xabc00000 + 0x45678 = 0xabc45678为实际物理地址。0xabcdef12中的低20位是mmu的设置状态位
#ifdef CONFIG_XIP_KERNEL
   ...
#endif

	/*
	 * Then map boot params address in r2 if specified.
	 * We map 2 sections in case the ATAGs/DTB crosses a section boundary.
	 */
	//下面映射r2(0x68000000)存放dtb的内存,只映射一个页1M空间就足够了
	mov	r0, r2, lsr #SECTION_SHIFT
	movs	r0, r0, lsl #SECTION_SHIFT
	subne	r3, r0, r8
	addne	r3, r3, #PAGE_OFFSET
	addne	r3, r4, r3, lsr #(SECTION_SHIFT - PMD_ORDER)
	orrne	r6, r7, r0
	strne	r6, [r3], #1 << PMD_ORDER
	addne	r6, r6, #1 << SECTION_SHIFT
	strne	r6, [r3]

#if defined(CONFIG_ARM_LPAE) && defined(CONFIG_CPU_ENDIAN_BE8)
...
#endif

#ifdef CONFIG_DEBUG_LL
....
#endif
#ifdef CONFIG_ARM_LPAE
...
#endif
	ret	lr  //返回,映射完毕3个区域__turn_mmu_on的操作方法,kernel和dtb
ENDPROC(__create_page_tables)
	.ltorg
	.align
__turn_mmu_on_loc:
	.long	.
	.long	__turn_mmu_on
	.long	__turn_mmu_on_end

以上,一级页表中低20位字段内容参考(https://www.cnblogs.com/sky-heaven/p/15941382.html):一级页表中低20位字段内容
做完页表的映射,接下来就做开启mmu前的准备工作,并使能mmu,并在__mmap_switched中跳转到startKernel正式执行C代码。这部分由于和ARM强相关,并且大多都是配置CP15寄存器。我们下一章再详细分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/344068.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IT大侦“碳”:Concept Luna向循环设计持续演进

今天聊点轻松的话题。上个月&#xff0c;小编用来吃饭的家伙开始闹罢工&#xff0c;笔记本的触控和键盘突然没了反应&#xff0c;电脑虽然还能打开&#xff0c;但嗡嗡直叫的风扇让我意识到这件事并不简单。 你问我为什么电脑会出问题&#xff1f;好吧&#xff0c;那我得先搞清楚…

领势 跨越|美创科技数据安全管理平台2023年度回顾

领势 跨越 数据安全管理平台的2023 以数字化安全平台为基础的数据安全体系是未来安全建设的重要方向。 研究咨询机构IDC指出&#xff1a;未来&#xff0c;数据安全基础设施管理平台将逐步发展成为各组织数据安全建设的基础设施。 Gartner《中国数据安全治理解析》预测&…

141:vue+leaflet 利用高德逆地理编码,点击地图标记marker,popup地址信息

第141个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中利用高德逆地理编码,点击地图标记marker,popup地址信息 。主要利用高德地图的api将坐标转化为地址,然后在点击的位置,弹出弹窗,在里面显示出地址信息。 直接复制下面的 vue+leaflet源代码,操作2分钟…

cad二次开发autolisp(二)

目录 一、选择集1.1 选择集的创建1.2 选择集的编辑1.3 操作选择集 二、命令行设置对话框2.1 设置图层2.2 加载线型2.3 设置字体样式2.4 设置标注样式&#xff08;了解即可&#xff09; 三、符号表3.1 简介3.2 符号表查找3.2 符号表删改增 一、选择集 定义&#xff1a;批量选择…

【数据结构与算法】4.自主实现单链表的增删查改

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢迎各位大佬指点&…

在SpringBoot中基于CanvasLabel的地震基础信息展示实践

目录 前言 一、数据库设计 1、数据库设计 2、sql脚本 3、数据记录 二、SpringBoot后台设计与实现 1、Mapper访问层及实体定义 2、Service层实现 3、控制层实现 三、地震信息展示 1、展示数据接入 2、最终效果 总结 前言 在上一篇博客中&#xff0c;对于在Leafle…

直播录屏工具哪家强?让你的直播更精彩!

随着网络技术的不断发展&#xff0c;直播行业逐渐兴起。无论是游戏直播、教育直播还是娱乐直播&#xff0c;人们都希望能够记录这些精彩瞬间。因此&#xff0c;一款好用的直播录屏工具显得尤为重要。本文将详细介绍两款流行的直播录屏工具&#xff0c;通过这些工具&#xff0c;…

亚马逊、eBay、TikTok等平台的综合运营实用工具分享!

亚马逊、eBay等电商平台为卖家提供了广阔的销售机会&#xff0c;但同时也带来了运营管理的挑战。为了提高运营效率和销售业绩&#xff0c;卖家需要借助一些实用工具。本文将介绍一些在亚马逊、eBay等平台上综合运营中非常有用的工具&#xff0c;帮助卖家更高效地管理店铺&#…

flutter底层架构初探

本文出处&#xff1a;​​​​​​​​​​​​​Flutter 中文开发者网站 架构 embedder嵌入层 提供程序入口&#xff08;其他原生应用也采用此方式&#xff09;&#xff0c;程序由此和底层操作系统协调&#xff08;surface渲染、辅助功能和输入服务&#xff0c;管理事件循环…

HTTP3/QUIC 性能测试与配套组件

背景 最近一年很多关于QUIC的文章层出&#xff0c;但是发现一个问题&#xff0c;这些文章都是在介绍QUIC或HTTP3是怎样的一个东西&#xff0c;以及它的优点和机制&#xff0c;将它夸的近乎上天了。然而有心的人估计会亲手做一些测试&#xff0c;就会发现这个被捧上天的东西性能…

如何预防服务器IP被劫持,危害有什么?

服务器IP被劫持是一种严重的网络安全问题&#xff0c;攻击者通过篡改服务器的IP地址&#xff0c;将网络流量重定向到恶意服务器或网站&#xff0c;导致用户无法正常访问目标服务器&#xff0c;并可能面临数据泄露、恶意软件感染等安全风险。了解服务器IP被劫持的危害和预防措施…

探案录 | 细说与人大金仓有关的“神秘数字”

近日&#xff0c;福尔摩斯•K发现&#xff0c;涉及医疗数字化的相关新闻都被人撕毁了&#xff0c;只留下日期和一串神秘数字&#xff1a;301、700、3000、3700……这背后隐瞒了什么呢&#xff1f;快跟着大侦探去揭开真相吧。 News&#xff1a;11月16日 全国首例&#xff0c;301…

上位机图像处理和嵌入式模块部署(自定义算法)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 我们在使用opencv的时候&#xff0c;虽然大部分算法都不需要我们自己重头开始编写&#xff0c;但是总有一些关于我们自己产品的know-how&#xff0…

说说 typescript 的数据类型有哪些?

文章目录 一、是什么二、有哪些# boolean# number# string# array# tuple# enum# any# null 和 和 undefined# void# never# object 三、总结参考文献 一、是什么 typescript 和 javascript几乎一样&#xff0c;拥有相同的数据类型&#xff0c;另外在javascript基础上提供了更…

SSM项目集成Spring Security 4.X版本(使用spring-security.xml 配置文件方式)

目录 前言 实战开发&#xff1a; 一、Spring Security整合到SSM项目 1. pom文件引入包 2. web.xml 配置 3. 添加 spring-security.xml 文件 二、Spring Security实战应用 1. 项目结构 2. pom文件引入 3. web.xml 配置 4. Spring 配置 applicationContext.xml 5. sp…

ffplay 之 Invalid data found when processing input

调试rtsp 通信协议时&#xff0c;发现使用 ffplay -i rtsp://127.0.0.1:554 tcp通信会先返回OPTIONS、DESCRIBE 2个指令&#xff0c;当返回SETUP指令给ffplay.exe程序时&#xff0c;会出现&#xff1a; 仔细查看代码&#xff0c;未支持UDP&#xff1a; 故,找到原因 ffplay -…

java web mvc-07-Vaadin 入门介绍

拓展阅读 Spring Web MVC-00-重学 mvc mvc-01-Model-View-Controller 概览 web mvc-03-JFinal web mvc-04-Apache Wicket web mvc-05-JSF JavaServer Faces web mvc-06-play framework intro web mvc-07-Vaadin web mvc-08-Grails 开源 The jdbc pool for java.(java …

计算机网络 第3章(数据链路层)

系列文章目录 计算机网络 第1章&#xff08;概述&#xff09; 计算机网络 第2章&#xff08;物理层&#xff09; 计算机网络 第3章&#xff08;数据链路层&#xff09; 文章目录 系列文章目录1. 数据链路层概述1.1 概述1.2 三个重要问题 2. 封装成帧2.1 介绍2.2 透明传输2.3 总…

深入理解——面向对象和面向过程

OOP 谈到面向对象逃不掉的一个问题就是面向对象和面向过程的区别和联系&#xff1a; 从时间的线性角度来说&#xff0c;面向对象是面向过程的下一个发展阶段&#xff0c;从二者的逻辑角度来说&#xff0c;则并没有纯粹的优劣&#xff0c;而是需要编码者根据特定的情况来选择特…

Parallels Desktop 18 for Mac(pd虚拟机) 激活版

Parallels Desktop 18是一款功能强大的虚拟机软件&#xff0c;可以在Mac操作系统上同时运行多种操作系统&#xff0c;包括Windows、Linux、Android等。该软件提供了多种高级功能&#xff0c;如支持DirectX 11游戏、3D图形和OpenGL应用程序&#xff0c;以及运行Windows和Mac应用…