内核临时页表

建立临时页表:内核启动前期、段式页表映射(1M大小)
主要完成的映射关系:
1.将虚拟地址空间按照“1:1”对等映射到内核映像的第一个1M处
  作用:在启动MMU之前只能使用实地址模式运行
2.将整个内核地址空间直接映射区的代码那部分映射到SDRAM上

3.将内核地址空间的开始的1M映射到SDRAM开始的第一个1M空间,因为那里存放了内核启动参数

看图意会时间:


ARM920T内置的MMU地址转换方式:这个建议去看看ARM920T的使用手册,毕竟那才是原汁原味的


一级描述符的格式:这里主要涉及的是一级段式页表描述符


具体使用一级段式页表将MVA转换成PA的过程


代码时间到了

第一段注释:
临时页表采用的是段式页表

分析linux-2.6.30.4/arch/arm/kernel/head.S的__create_page_tables汇编代码:

/*
 * 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  = machinfo
 * r9  = cpuid
 * r10 = procinfo
 *
 * Returns:
 *  r0, r3, r6, r7 corrupted
 *  r4 = physical page table address:r4=页表的基地址,r4的值还是在内核自解压工作的时候赋给的:内核解压后映像的起始地址0x30008000
 */
__create_page_tables:
	pgtbl	r4				@ page table address
	//pgtbl是一个汇编宏定义 .macro	pgtbl, rd,通过这个宏将r4(0x30004000)设置成页表的物理基地址,往后r4值一直没有变

	/*
	 * Clear the 16K level 1 swapper page table  清零一级交换页表16K
	 * 页表将4GB的地址空间分成若干个1MB的段(section),因此页表包含4096个页表项(section entry)。每个页表项是32bits(4 bytes),因而页表占用4096*4=16k的内存空间。
	 */
	mov	r0, r4
	mov	r3, #0
	add	r6, r0, #0x4000
1:	str	r3, [r0], #4
	str	r3, [r0], #4
	str	r3, [r0], #4
	str	r3, [r0], #4
	teq	r0, r6
	bne	1b

	ldr	r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags 获得proc_info_list的__cpu_mm_mmu_flags的值,并存储到r7中,看第二段注释
	//r7 = __cpu_mm_mmu_flags = 0x00000C1E
	/*
	 * Create identity mapping for first MB of kernel to
	 * cater for the MMU enable.  This identity mapping
	 * will be removed by paging_init().  We use our current program
	 * counter to determine corresponding section base address.
	 * 接下来是为内核空间的直接映射区第一个1MB建立页表,这都是为之后MMU的启动,之后会被paging_init()函数移除销毁
	 * 我们通过当前的程序计数寄存器PC值来获得段基址
	 */
	mov	r6, pc, lsr #20				@ start of kernel section    r6 = 0x300
	orr	r3, r7, r6, lsl #20			@ flags + kernel base(PA)    r3 = (r7) | ((r6)<<20)   r3里边放的内容就是一级描述符
	str	r3, [r4, r6, lsl #2]		@ identity mapping           [0x30004000+0x300<<2] = r3   将一级描述符放到页目录中
                                                                 @不过这里为什么要左移两位(乘于4)???为什么又是存放到这个地址处
                                                                 @想到了:r4里边是ttb基址(启动MMU时会被写到cp15的c2寄存器),r6里边的是ttb表中对应1M的索引值,每个描述符是4字节
                                                                 @实际上只是将pc的值右移了18位
	@上面这一段代码实现了VA=PA(1:1)的映射:根据ARM920T的MMU一级地址转换,4G的虚拟地址空间被分成4096个条目(描述符,每个4字节),因此每一个条目对应1M=4G/4096的地址空间映射。
	@到这里第一个映射关系建立完成
	
	/*
	 * Now setup the pagetables for our kernel direct
	 * mapped region.
	 * 现在为了内核直接映射区来设置页表
	 * 即为kernel镜像所占有空间,即KERNL_START到KERNEL_END建立内存映射
	 * 由于这块内核虚拟空间要映射到SDRAM中内核映像0x30008000开始处,所以第一个1M的描述符(保存到r3寄存器中)和上面的是样的
	 * 这里是将整个内核空间的直接映射区全部映射完毕--以段的方式(1M)
	 */
	add	r0, r4,  #(KERNEL_START & 0xff000000) >> 18      @取内核空间地址(VA)高8位,arm的立即数只能是8位
	str	r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!	 @再取内核空间地址(VA)高8位往后4位,和上一句加起来共12位,“!”将地址写到r0
	ldr	r6, =(KERNEL_END - 1)                            @r4=ttb基址  r0=ttb中kernel第1M描述符检索值
	add	r0, r0, #4
	add	r6, r4, r6, lsr #18                              @r6= 用来标识ttb中内核地址空间结束的检索地址
1:	cmp	r0, r6
	add	r3, r3, #1 << 20   ;将描述符的段偏移地址加1,即物理段基地址加1
	strls	r3, [r0], #4   ;表示小于等于,即只要r0<=r6,strls就会执行
	bls	1b

#ifdef CONFIG_XIP_KERNEL  ;没有定义CONFIG_XIP_KERNEL,注释掉这个条件分支,XIP技术就是内核代码可以不用拷贝到SDRAM而立地执行
	
.......
	
#endif

	/*
	 * Then map first 1MB of ram in case it contains our boot params.
	 * 接下来建立内核临时页表的最后一个页表描述符,即SDRAM开始1M的地址空间,那里保存了uboot传递给内核的启动参数
	 */
	add	r0, r4, #PAGE_OFFSET >> 18   ;PAGE_OFFSET被定义为SDRAM起始地址0x30000000
	orr	r6, r7, #(PHYS_OFFSET & 0xff000000)    ;r6 = PA section base addr + flags
	.if	(PHYS_OFFSET & 0x00f00000)
	orr	r6, r6, #(PHYS_OFFSET & 0x00f00000)
	.endif
	str	r6, [r0]  ;将描述符写入到对应的页表位置,第三个页表描述符建立完成
	
	;........中间省略一段代码,因为条件编译不成立,相当于注释掉...............
	
	mov	pc, lr  ; 从__create_page_tables返回
ENDPROC(__create_page_tables)

#include "head-common.S"

第二段注释:
在arch/arm/include/asm/procinfo.h头文件定义处理器相关信息数据结构

struct proc_info_list {
	unsigned int		cpu_val;
	unsigned int		cpu_mask;
	unsigned long		__cpu_mm_mmu_flags;	/* used by head.S */这个值在哪里被赋值?往下看
	unsigned long		__cpu_io_mmu_flags;	/* used by head.S */
	unsigned long		__cpu_flush;		/* used by head.S */
	const char		*arch_name;
	const char		*elf_name;
	unsigned int		elf_hwcap;
	const char		*cpu_name;
	struct processor	*proc;
	struct cpu_tlb_fns	*tlb;
	struct cpu_user_fns	*user;
	struct cpu_cache_fns	*cache;
};
在文件arch/arm/kernel/vmlinux.lds.S中定义了一个“.proc.info.init”段属性
      __proc_info_begin = .;
         *(.proc.info.init)
      __proc_info_end = .;
因此,以关键字“.proc.info.init”在工程里面搜索,找到arch/arm/mm/proc-arm920.S文件,第三行的定义就是对__cpu_mm_mmu_flags“映射属性标识”的赋值
__arm920_proc_info:
	.long	0x41009200
	.long	0xff00fff0
	.long   PMD_TYPE_SECT | \
		PMD_SECT_BUFFERABLE | \
		PMD_SECT_CACHEABLE | \
		PMD_BIT4 | \
		PMD_SECT_AP_WRITE | \
		PMD_SECT_AP_READ
	.long   PMD_TYPE_SECT | \
		PMD_BIT4 | \
		PMD_SECT_AP_WRITE | \
		PMD_SECT_AP_READ
	b	__arm920_setup
	.long	cpu_arch_name
	.long	cpu_elf_name
	.long	HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB
	.long	cpu_arm920_name
	.long	arm920_processor_functions
	.long	v4wbi_tlb_fns
	.long	v4wb_user_fns
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
	.long	arm920_cache_fns
#else
	.long	v4wt_cache_fns
#endif
	.size	__arm920_proc_info, . - __arm920_proc_info
在arch/arm/include/asm/pgtable-hwdef.h找到上面用到的宏的定义:

#define PMD_TYPE_SECT		(2 << 0) 采用段式地址映射
#define PMD_SECT_BUFFERABLE	(1 << 2)
#define PMD_SECT_CACHEABLE	(1 << 3)
#define PMD_BIT4			(1 << 4)
#define PMD_SECT_AP_WRITE	(1 << 10)
#define PMD_SECT_AP_READ	(1 << 11)
.long是分配长整型长度的空间,在arm9处理器占4个字节(32位)
按位或之后:__cpu_mm_mmu_flags = 0x00000C1E


从临时页表创建原理到具体的代码实现过程可以说到这里我已经消化了。突然又想到一个问题:三张页表描述符创建好了,MMU也启动了,cpu执行过程中究竟是怎么知道什么时候使用对等映射描述表符,什么时候使用内核空间与内核映像的那个描述符,又是什么时候要知道切换使用启动参数那块的映射描述符???

仔细想想就可以知道了,我们知道ttb的“选择子”就是MVA(这里就相当于VA)的高12位,这12位的变化就是cpu自动选择ttb中哪一个描述符的根本原因。arm9从cpu上电开始工作到在SDRAM取指执行时(MMU未开启)PC指针都是0x3打头的,在开始真正运行内核那段head.S代码并且C语言运行环境未设置好时,必须使用位置无关码来实现汇编函数跳转,也就是说这就是为了保持PC是0x3打头的,这样一来,即使之后启动了MMU,我的PC保持原有作风就可以让我的“选择子”等于“0x300”选中1:1的对等映射描述符表项。同理只要往后pc的值不再是0x3打头就会选中0xC打头的描述符表项,具体选中哪一个就看0xCxx了。


内核临时页表的创建过程分析完毕微笑,linux之路又前进了一小步,加油!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值