head.S中的MMU分析

参考文章:https://www.cnblogs.com/sky-heaven/p/4846835.html
linux内核版本:4.15.0

在这里插入图片描述

PAGE_OFFSET 在 arch\arm\include\asm\memory中定义。

#define PAGE_OFFSET		UL(CONFIG_PAGE_OFFSET)

CONFIG_PAGE_OFFSET 在内核根目录下的配置文件 .config 中定义

CONFIG_PAGE_OFFSET =0xC0000000

代码:
开头的一些宏定义。

 @TEXT_OFFSET 是内核代码(解压后)相对于RAM起始的偏移
 @PAGE_OFFSET 内核虚拟地址空间的起始地址(链接地址)
 @(suniv 处理器)这里PAGE_OFFSET=0xC0000000
#define KERNEL_RAM_VADDR	(PAGE_OFFSET + TEXT_OFFSET)  @计算得到内核虚拟起始地址0xc0008000,必须8000结尾
#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000		@最后16位必须是0x8000
#error KERNEL_RAM_VADDR must start at 0xXXXX8000
#endif

#ifdef CONFIG_ARM_LPAE	@未定义
	/* LPAE requires an additional page for the PGD */
#define PG_DIR_SIZE	0x5000
#define PMD_ORDER	3
#else
#define PG_DIR_SIZE	0x4000		@设置MMU页表大小为16k (4Byte * 4095)
#define PMD_ORDER	2
#endif

	.globl	swapper_pg_dir		@页表虚拟起始地址
	.equ	swapper_pg_dir, KERNEL_RAM_VADDR - PG_DIR_SIZE	@计算页表起始虚拟地址
@计算页表的起始物理地址,计算结果返回到rd
	.macro	pgtbl, rd, phys 
	add	\rd, \phys, #TEXT_OFFSET	@phys+TEXT_OFFSET ,内核起始物理地址
	sub	\rd, \rd, #PG_DIR_SIZE		@phys+TEXT_OFFSET-PG_DIR_SIZE ,页表起始地址
	.endm

。。。。。。。。。。。。部分代码省略。

创建特定映射,以满足__enable_mmu的需求。
此特定映射将被paging_init()删除。

/*
 * 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 = physical page table address
 */
__create_page_tables:
	pgtbl	r4, r8				@ 获取页表起始物理地址

	/*
	 * Clear the swapper page table 清空16k页表
	 */
		/*
			* 清零16K的一级初始页表区
			* 这些页表在内核自解压时被设置过
			* (此时MMU已关闭)
			*/

	mov	r0, r4
	mov	r3, #0
	add	r6, r0, #PG_DIR_SIZE
1:	str	r3, [r0], #4	
	str	r3, [r0], #4
	str	r3, [r0], #4
	str	r3, [r0], #4
	teq	r0, r6
	bne	1b


	/*
		 * 获取节描述符的默认配置(除节基址外的其他配置)
		 * 这个数据依构架而不同,数据是用汇编文件配置的:
		 * arch/arm/mm/proc-xxx.S
		 * (此时MMU已关闭)
		 */

	ldr	r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags

	/*
		 * 创建特定映射,以满足__enable_mmu的需求。
		 * 此特定映射将被paging_init()删除。
		 * 
		 * 其实这个特定的映射就是仅映射__enable_mmu功能函数区的页表
		 * 以保证在启用mmu时代码的正确执行--1:1映射(物理地址=虚拟地址)
		 */
	/*
	 * Create identity mapping to cater for __enable_mmu.
	 * This identity mapping will be removed by paging_init().
	 */
	adr	r0, __turn_mmu_on_loc	@获取当前虚拟地址到r0
	ldmia	r0, {r3, r5, r6}	@r3中存放当前物理地址,通过“.”符号获取
	sub	r0, r0, r3			@ virt->phys offset		@ 获取编译时确定的虚拟地址到当前物理地址的偏移(r0-r3)
	add	r5, r5, r0			@ phys __turn_mmu_on	@ 计算__enable_mmu的当前物理地址
	add	r6, r6, r0			@ phys __turn_mmu_on_end	@计算 __enable_mmu_end的当前物理地址
	mov	r5, r5, lsr #SECTION_SHIFT	@右移20位,计算 __enable_mmu的节基址
	mov	r6, r6, lsr #SECTION_SHIFT	@右移20位,计算 __enable_mmu_end的节基址
	@对两物理地址右移20位,取高12位,作为存入页表的高121:	orr	r3, r7, r5, lsl #SECTION_SHIFT	@ flags + kernel base	@ 生成节描述符:flags + 节基址
	@r7存有mm_mmuflags,r5左移20位,或上mm_mmuflags,存入r3,即为应该要存入页表的值

	str	r3, [r4, r5, lsl #PMD_ORDER]	@ identity mapping	@ 设置节描述符,1:1映射(物理地址=虚拟地址)
	@存入页表,这里链接地址=物理地址,指定页表(页表基址r4)偏移r5(此为物理地址的高12位)左移2位(页表每一页为4字节)
	@可以表述为把r3存入 r4+(r5<<2)这个地址。偏移r5*4个地址,因为页表每页描述符占4字节。
	
	cmp	r5, r6	@ 完成映射?(理论上一次就够了,这个函数应该不会大于1M吧~)(比较r5与r6是否在同一页内)
	addlo	r5, r5, #1			@ next section  r5和r6不在同一页,继续映射下一页
	blo	1b

为.bbs 段做映射。
为内核代码段创建映射。

@下面为.bbs 段做映射

	/*
	 * Map our RAM from the start to the end of the kernel .bss section.
	 */
	add	r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER)  @把链接地址映射到相同的物理地址空间0xc0000000
	ldr	r6, =(_end - 1)
	orr	r3, r8, r7
	add	r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)
1:	str	r3, [r0], #1 << PMD_ORDER
	add	r3, r3, #1 << SECTION_SHIFT
	cmp	r0, r6
	bls	1b

#ifdef CONFIG_XIP_KERNEL
/*
   * 如果是XIP技术的内核,上面的映射只能映射内核代码和只读数据部分
   * 这里我们再映射一些RAM来作为 .data and .bss 空间.
   */

@下面为内核代码创建MMU映射
	/*
		 * 现在创建内核的逻辑映射区页表(节映射)
		 * 创建范围:KERNEL_START---KERNEL_END
		 * KERNEL_START:内核最终运行的虚拟地址
		 * KERNEL_END:内核代码结束的虚拟地址(bss段之后,但XIP不是)
		 */

	/*
	 * Map the kernel image separately as it is not located in RAM.
	 */
#define XIP_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
	mov	r3, pc	@ 获取当前物理地址
	mov	r3, r3, lsr #SECTION_SHIFT	@ r3 = 当前物理地址的节基址
	orr	r3, r7, r3, lsl #SECTION_SHIFT	 @ r3 为当前物理地址的节描述符


	/*
		 * 下面是为了确定页表项的入口地址
		 * 其实页表入口项的偏移就反应了对应的虚拟地址的高位
		 *
		 * 由于ARM指令集的8bit位图问题,只能分两次得到
		 * XIP_START:内核最终运行的虚拟地址
		 
		 * 当前所在的页乘上4才是当前页表的节地址(每个节地址表述符占用4个字节)。
		 */

	add	r0, r4,  #(XIP_START & 0xff000000) >> (SECTION_SHIFT - PMD_ORDER)
	str	r3, [r0, #((XIP_START & 0x00f00000) >> SECTION_SHIFT) << PMD_ORDER]! @设置节描述符
	ldr	r6, =(_edata_loc - 1)	@_edata_loc内核结束地址
	add	r0, r0, #1 << PMD_ORDER  @r0指向页表的下一页
	add	r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)	@ r6 = 内核逻辑映射结束的节基址
1:	cmp	r0, r6	@到最后一页?表示映射完成
	add	r3, r3, #1 << SECTION_SHIFT		@ 生成节描述符(只需做基址递增)
	strls	r3, [r0], #1 << PMD_ORDER	@设置节描述符,r0指向下一页
	bls	1b
#endif

映射启动参数区

/*
		 * 然后映射启动参数区(现在r2中的atags物理地址) 
		 * 或者
		 * 如果启动参数区的虚拟地址没有确定(或者无效),则会映射RAM的头1MB.
		 */

	/*
	 * Then map boot params address in r2 if specified.
	 * We map 2 sections in case the ATAGs/DTB crosses a section boundary.
	 */
	
	 
	@映射r2中的引导参数地址(如果指定的话)
	mov	r0, r2, lsr #SECTION_SHIFT
	@r0存放着启动参数区的物理地址页偏移(要把这个偏移存入页表,建立页表映射)
	movs	r0, r0, lsl #SECTION_SHIFT  @r0不为0,则执行下面这条指令
	@比较r0是否为0(为0表示未指定)
	@当r0为0时,Z标志被置1
	@如果r2没有指定则不进行映射,下面带ne的指令都不执行
	subne	r3, r0, r8		
	@Z标志为0(r2存在指定参数)才执行,计算与起始物理地址的偏移量
	@r8是物理内存(SDRAM)的起始地址(在s3c2440中是 0x30000 0000)
	
	addne	r3, r3, #PAGE_OFFSET		@ 转换为虚拟地址
	@计算后r3中存放的是参数区起始虚拟地址

	addne	r3, r4, r3, lsr #(SECTION_SHIFT - PMD_ORDER)	@ 确定页表项(节描述符)入口地址
	@Z标志为0(r2存在指定参数)才执行,
	@计算参数区的页表项地址(虚拟地址所在页),存放到r3
	
	orrne	r6, r7, r0		@ 生成节描述符 (物理偏移页生成节描述符,存入r6)
	strne	r6, [r3], #1 << PMD_ORDER		@ 设置节描述符 (设置参数区虚拟地址页到物理地址页的映射)
	@虚拟偏移页加1
	addne	r6, r6, #1 << SECTION_SHIFT  @物理偏移页加1
	strne	r6, [r3]	@再继续映射一页
	@一共映射了2M空间,每次1M

将串口IO内存之上的所有地址都进行映射

@______________________________________________________________________________________
@ 将串口IO内存之上的所有地址都进行映射


#ifdef CONFIG_DEBUG_LL
/*
	 * 下面是调试信息的输出函数区
	 * 这里做了IO内存空间的节映射
	 * 为串口调试映射IO内存空间(将串口IO内存之上的所有地址都映射了)
	 * 这允许调试信息(在paging_init之前)从串口控制台输出
	 * 
	 */
@---------------------------------------------------------------------------------------

#if !defined(CONFIG_DEBUG_ICEDCC) && !defined(CONFIG_DEBUG_SEMIHOSTING)
	/*
	 * Map in IO space for serial debugging.
	 * This allows debug messages to be output
	 * via a serial console before paging_init.
	 */

	addruart r7, r3, r0		@ 宏代码,位于arch/arm/mach-xxx/include/mach/debug-macro.S
	@ 作用是将串口控制寄存器的基址放入r7(物理地址)r3(虚拟地址)

	mov	r3, r3, lsr #SECTION_SHIFT
	mov	r3, r3, lsl #PMD_ORDER
	@计算虚拟地址页偏移
	add	r0, r4, r3  @通过r3计算页表项入口地址
	mov	r3, r7, lsr #SECTION_SHIFT @获得物理地址页偏移,存入r3
	ldr	r7, [r10, #PROCINFO_IO_MMUFLAGS] @获取 io_mmuflags
	orr	r3, r7, r3, lsl #SECTION_SHIFT  @ 用r3和r7相与,生成节描述符 
@当前r3存放节描述符 
@当前r0存放表项入口地址

@CONFIG_ARM_LPAE未定义	-----------
 #ifdef CONFIG_ARM_LPAE
/*	mov	r7, #1 << (54 - 32)		@ XN
 #ifdef CONFIG_CPU_ENDIAN_BE8
	str	r7, [r0], #4
	str	r3, [r0], #4
#else
	str	r3, [r0], #4	@建立一页映射,uart虚拟地址映射到物理地址
	str	r7, [r0], #4
#endif
*/
#else @---------------------------

	orr	r3, r3, #PMD_SECT_XN @修改节描述符
	str	r3, [r0], #4	@设置节描述符 ,并且指向下一页
#endif

#else /* CONFIG_DEBUG_ICEDCC || CONFIG_DEBUG_SEMIHOSTING */
	/* we don't need any serial debugging mappings */
	ldr	r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
	
#endif

/*#if !defined(CONFIG_DEBUG_ICEDCC) && !defined(CONFIG_DEBUG_SEMIHOSTING)结束*/
@---------------------------------------------------------------------------------------
/*
#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)
	/*
	 * If we're using the NetWinder or CATS, we also need to map
	 * in the 16550-type serial port for the debug messages
	 */
/*	 
	add	r0, r4, #0xff000000 >> (SECTION_SHIFT - PMD_ORDER)
	orr	r3, r7, #0x7c000000
	str	r3, [r0]
#endif
@---------------------------------------------------------------------------------------

#ifdef CONFIG_ARCH_RPC
	/*
	 * Map in screen at 0x02000000 & SCREEN2_BASE
	 * Similar reasons here - for debug.  This is
	 * only for Acorn RiscPC architectures.
	 */
/*	 
	add	r0, r4, #0x02000000 >> (SECTION_SHIFT - PMD_ORDER)
	orr	r3, r7, #0x02000000
	str	r3, [r0]
	add	r0, r4, #0xd8000000 >> (SECTION_SHIFT - PMD_ORDER)
	str	r3, [r0]
#endif
@---------------------------------------------------------------------------------------
*/
#endif
@______________________________________________________________________________________

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yfw&武

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值