ARM Linux 内核启动总结 之 创建临时页表
2016-08-11 14:47
351 查看
硬件平台:S5PV210 内核版本:Linux2.6.32 文件:head.S(linux/arch/arm/kernel/)
#include <**********>
#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)
#define KERNEL_RAM_PADDR (PHYS_OFFSET + TEXT_OFFSET)
//PAGE_OFFSET是内核虚拟地址空间的起始地址,一般为0xC000_0000
//PHYS_OFFSET 是硬件物理内存的起始地址,在文件memory.h(linux/arch/arm/mach-s5pv210/include/mach/)中定义,为0x2000_0000
//TEXT_OFFSET是内核代码相对于起始地址的偏移量,一般为0x8000
//KERNEL_RAM_VADDR=0xC000_8000; KERNEL_RAM_PADDR=0x2000_8000
//刚开始的这32k(0x8000)空间用来存放页表,启动参数等
.globl swapper_pg_dir
.equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000
//equ相当于C语言中的#define
.macro pgtbl, rd
ldr \rd, =(KERNEL_RAM_PADDR - 0x4000)
.endm
//这里定义了一个宏,把物理地址0x2000_4000赋值给rd寄存器,一般这个地址用来存放临时页表
#ifdef CONFIG_XIP_KERNEL(未定义)
#define KERNEL_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
#define KERNEL_END _edata_loc
#else
#define KERNEL_START KERNEL_RAM_VADDR
#define KERNEL_END _end
#endif
//KERNEL_START=0xC000_8000, KERNEL_END=具体是多少没搞懂
.section ".text.head", "ax"
ENTRY(stext)
*********************************
bl __create_page_tables
//跳过其他的启动过程,直接看创建页表的过程__create_page_tables
__create_page_tables:
pgtbl r4 //宏展开,把物理地址0x2000_4000赋值给r4寄存器
mov r0, r4 //r0=r4=0x2000_4000
mov r3, #0 //r3=0
add r6, r0, #0x4000 //r6=0x2000_8000; r0和r6一起限定了页表的范围
1: str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
teq r0, r6
bne 1b //将0x2000_4000开始到0x2000_8000之间的物理内存初始化为0,为后面的填充页表做准备
ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] //r10存放的是procinfo结构的地址,里面包含了内存管理相关的参数
mov r6, pc //这时候PC值是物理值,因为还没有开MMU。内核启动阶段,PC值应该处于物理内存开始的部分,是0x200*_****
mov r6, r6, lsr #20 //右移20位,r6=0x200,用r6来指示内核所处的段。
orr r3, r7, r6, lsl #20 //把内存管理的一些参数加进去赋值给r3。注意r6还是0x200,r3=0x2000_****(flags)
str r3, [r4, r6, lsl #2]
//从0x2000_4000开始存放页表,这里使用的是段页表,4G=4K*1M,所以一共有4096个页表项,每个页表项代表1M空间。虚拟地址的高12位用来在这个页表中找到具体的页表项,2^12=4096,也就是0x000~0xfff。每个页表项占4个字节,刚刚好是4*4096=16K,也就是0x2000_4000到(0x2000_8000-1)这个区间大小。虚拟地址的低20位用来在1M的空间里定位某个具体的字节2^20=1M。
//为什么还要把物理地址进行映射呢?我在其他高手的博客中看到过,好像是因为:CPU流水线取指令,在开MMU后,PC地址应该全部是虚拟地址,但是预取指令功能导致开MMU的时候既有虚拟地址,又有物理地址,如果我们只建立虚拟地址的映射,那么PC物理地址送到MMU之后就会找不到相应的页表,会出错。大概是这样,这部分理解还有待深入确认。
//注意一下这里的 r6, lsl #2,就是r6*4=0x200*4=0x800,0x200相对于0x000的偏移量是0x200,每个页表项占4个字节,所以这个1M的段对应的页表项地址就是0x2000_4800,最后把r3的值存进这个页表项中。
add r0, r4, #(KERNEL_START & 0xff000000) >> 18 //KERNEL_START=0xC000_8000,所以r0=0x2000_4000+0xC00*4=0x2000_7000
//提取内核虚拟地址空间的段地址,右移18位就是右移20位,然后左移2位,找到对应的页表项
str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]! //KERNEL_START & 0x00f00000=0,对0x2000_7000处的页表项进行填充
ldr r6, =(KERNEL_END - 1) //r6存放要映射的虚拟地址的终点,假设r6=0xC03*_****
add r0, r0, #4 //r0=0x2000_7000+4=0x2000_7004
add r6, r4, r6, lsr #18 //找到虚拟地址终点所在段对应的页表项地址r6=0x2000_4000+0xC03*4=0x2000_700C
1: cmp r0, r6
add r3, r3, #1 << 20 //0xC00段对应0x200段,0xC01段对应0x201段,0xC02段对应0x202段。。。
strls r3, [r0], #4
bls 1b //对整个内核代码段(表述不确切,这个范围由KERNEL_END 决定)进行映射,填充相应的页表项
add r0, r4, #PAGE_OFFSET >> 18 //PAGE_OFFSET=0xC000_0000,r0=0x2000_7000
orr r6, r7, #(PHYS_OFFSET & 0xff000000) //r7里面存放的是内存管理参数,r6=0x2000_****
.if (PHYS_OFFSET & 0x00f00000)
orr r6, r6, #(PHYS_OFFSET & 0x00f00000)
.endif
str r6, [r0] //对0x2000_7000处的页表项再次进行填充。填充的数值是一样的,都是0x2000_****(flags)
//对于上面的重复操作,摘录了“海枫”博客中的解释 (http://blog.csdn.net/linyt/article/details/6582399 )
通常kernel的启动参数由bootloader放到了物理内存的第1个M上,所以需要为RAM上的第1个M建立映射。
* 上面已为PHYS_OFFSET + TEXT_OFFSET建立了映射,如果TEXT_OFFSET小于0x00100000的话,
* 上面代码应该也为SDRAM的第一个M建立了映射,但如果大于0x0010000则不会。
* 所以这里无论如何均为SDRAM的第一个M建立映射(不知分析对否,还请指正)。
mov pc, lr //结束
转自 :http://blog.163.com/q_n_b/blog/static/214798216201301495346679/
#include <**********>
#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)
#define KERNEL_RAM_PADDR (PHYS_OFFSET + TEXT_OFFSET)
//PAGE_OFFSET是内核虚拟地址空间的起始地址,一般为0xC000_0000
//PHYS_OFFSET 是硬件物理内存的起始地址,在文件memory.h(linux/arch/arm/mach-s5pv210/include/mach/)中定义,为0x2000_0000
//TEXT_OFFSET是内核代码相对于起始地址的偏移量,一般为0x8000
//KERNEL_RAM_VADDR=0xC000_8000; KERNEL_RAM_PADDR=0x2000_8000
//刚开始的这32k(0x8000)空间用来存放页表,启动参数等
.globl swapper_pg_dir
.equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000
//equ相当于C语言中的#define
.macro pgtbl, rd
ldr \rd, =(KERNEL_RAM_PADDR - 0x4000)
.endm
//这里定义了一个宏,把物理地址0x2000_4000赋值给rd寄存器,一般这个地址用来存放临时页表
#ifdef CONFIG_XIP_KERNEL(未定义)
#define KERNEL_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
#define KERNEL_END _edata_loc
#else
#define KERNEL_START KERNEL_RAM_VADDR
#define KERNEL_END _end
#endif
//KERNEL_START=0xC000_8000, KERNEL_END=具体是多少没搞懂
.section ".text.head", "ax"
ENTRY(stext)
*********************************
bl __create_page_tables
//跳过其他的启动过程,直接看创建页表的过程__create_page_tables
__create_page_tables:
pgtbl r4 //宏展开,把物理地址0x2000_4000赋值给r4寄存器
mov r0, r4 //r0=r4=0x2000_4000
mov r3, #0 //r3=0
add r6, r0, #0x4000 //r6=0x2000_8000; r0和r6一起限定了页表的范围
1: str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
teq r0, r6
bne 1b //将0x2000_4000开始到0x2000_8000之间的物理内存初始化为0,为后面的填充页表做准备
ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] //r10存放的是procinfo结构的地址,里面包含了内存管理相关的参数
mov r6, pc //这时候PC值是物理值,因为还没有开MMU。内核启动阶段,PC值应该处于物理内存开始的部分,是0x200*_****
mov r6, r6, lsr #20 //右移20位,r6=0x200,用r6来指示内核所处的段。
orr r3, r7, r6, lsl #20 //把内存管理的一些参数加进去赋值给r3。注意r6还是0x200,r3=0x2000_****(flags)
str r3, [r4, r6, lsl #2]
//从0x2000_4000开始存放页表,这里使用的是段页表,4G=4K*1M,所以一共有4096个页表项,每个页表项代表1M空间。虚拟地址的高12位用来在这个页表中找到具体的页表项,2^12=4096,也就是0x000~0xfff。每个页表项占4个字节,刚刚好是4*4096=16K,也就是0x2000_4000到(0x2000_8000-1)这个区间大小。虚拟地址的低20位用来在1M的空间里定位某个具体的字节2^20=1M。
//为什么还要把物理地址进行映射呢?我在其他高手的博客中看到过,好像是因为:CPU流水线取指令,在开MMU后,PC地址应该全部是虚拟地址,但是预取指令功能导致开MMU的时候既有虚拟地址,又有物理地址,如果我们只建立虚拟地址的映射,那么PC物理地址送到MMU之后就会找不到相应的页表,会出错。大概是这样,这部分理解还有待深入确认。
//注意一下这里的 r6, lsl #2,就是r6*4=0x200*4=0x800,0x200相对于0x000的偏移量是0x200,每个页表项占4个字节,所以这个1M的段对应的页表项地址就是0x2000_4800,最后把r3的值存进这个页表项中。
add r0, r4, #(KERNEL_START & 0xff000000) >> 18 //KERNEL_START=0xC000_8000,所以r0=0x2000_4000+0xC00*4=0x2000_7000
//提取内核虚拟地址空间的段地址,右移18位就是右移20位,然后左移2位,找到对应的页表项
str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]! //KERNEL_START & 0x00f00000=0,对0x2000_7000处的页表项进行填充
ldr r6, =(KERNEL_END - 1) //r6存放要映射的虚拟地址的终点,假设r6=0xC03*_****
add r0, r0, #4 //r0=0x2000_7000+4=0x2000_7004
add r6, r4, r6, lsr #18 //找到虚拟地址终点所在段对应的页表项地址r6=0x2000_4000+0xC03*4=0x2000_700C
1: cmp r0, r6
add r3, r3, #1 << 20 //0xC00段对应0x200段,0xC01段对应0x201段,0xC02段对应0x202段。。。
strls r3, [r0], #4
bls 1b //对整个内核代码段(表述不确切,这个范围由KERNEL_END 决定)进行映射,填充相应的页表项
add r0, r4, #PAGE_OFFSET >> 18 //PAGE_OFFSET=0xC000_0000,r0=0x2000_7000
orr r6, r7, #(PHYS_OFFSET & 0xff000000) //r7里面存放的是内存管理参数,r6=0x2000_****
.if (PHYS_OFFSET & 0x00f00000)
orr r6, r6, #(PHYS_OFFSET & 0x00f00000)
.endif
str r6, [r0] //对0x2000_7000处的页表项再次进行填充。填充的数值是一样的,都是0x2000_****(flags)
//对于上面的重复操作,摘录了“海枫”博客中的解释 (http://blog.csdn.net/linyt/article/details/6582399 )
通常kernel的启动参数由bootloader放到了物理内存的第1个M上,所以需要为RAM上的第1个M建立映射。
* 上面已为PHYS_OFFSET + TEXT_OFFSET建立了映射,如果TEXT_OFFSET小于0x00100000的话,
* 上面代码应该也为SDRAM的第一个M建立了映射,但如果大于0x0010000则不会。
* 所以这里无论如何均为SDRAM的第一个M建立映射(不知分析对否,还请指正)。
mov pc, lr //结束
转自 :http://blog.163.com/q_n_b/blog/static/214798216201301495346679/
相关文章推荐
- ARM Linux 内核启动总结 之 创建临时页表
- ARM Linux 内核启动总结 之 创建临时页表
- ARM Linux 内核启动总结 之 创建临时页表
- ARM Linux 内核启动总结 之 创建临时页表
- ARM Linux 内核启动总结 之 创建临时页表
- [kernel 启动流程] (第五章)第一阶段之——临时内核页表的创建
- [kernel 启动流程] (第五章)第一阶段之——临时内核页表的创建
- Linux 内核临时页表的创建
- ARM架构内核启动分析-head.S(1.3、stext分析之内存临时页表建立)
- linux内核启动时,第一次创建页表
- WebLogic使用总结(六)——WebLogic创建虚拟主机和修改启动端口号
- mini6410基于linux2.6.36内核通过NFS启动根文件系统总结(一搭建开发环境——建立NFS服务器)
- 第3阶段——内核启动分析之创建si工程和分析stext启动内核函数(4)
- 内核启动总结(最全的个人总结)
- arm linux 临时页表的建立
- Android Activity 创建&启动流程总结
- mini6410基于linux2.6.36内核通过NFS启动根文件系统总结(一搭建开发环境——建立NFS服务器)
- Windows内核学习笔记(六)-- [总结]创建IRP的四种不同方式
- 20135202闫佳歆--week6 分析Linux内核创建一个新进程的过程——实验及总结
- ARM LINUX 内核启动