前言:
linux-arm架构
kernel版本:2.6.22.6
head.S首先确定了processor type和 machine type,之后就是创建页表。
通过前面的两步,我们已经确定了processor type 和 machine type。
此时,一些特定寄存器的值如下所示:
r8 = machine info (struct machine_desc的基地址)
r9 = cpu id (通过cp15协处理器获得的cpu id)
r10 = procinfo (struct proc_info_list的基地址)
接下来就是通过__create_page_tables建立页表了。
bl __create_page_tables
函数中出现的宏,及其解释:
宏 默认值 定义
KERNEL_RAM_VADDR 0xC0008000 内核在内存中的虚拟地址
PAGE_OFFSET 0xC0000000 内核虚拟地址空间的起始地址
TEXT_OFFSET 0x00008000 内核起始位置相对于内存起始位置的偏移
PHYS_OFFSET 构架相关 物理内存的起始地址
代码分析:
bl __create_page_tables会跳转到下面代码段中,建立表单
.type __create_page_tables, %function
__create_page_tables:
/*
*宏定义
*.macropgtbl, rd
*ldr \rd, =(KERNEL_RAM_PADDR - 0x4000)
*.endm
*
*分析:内存为 4G = 4*1024 MB;需要4096个表单表示,每一页表单为4bytes;因此需要16K内存,即0x4000;
* 根据 =(KERNEL_RAM_PADDR - 0x4000)得,页表存放于-内核在内存中的物理地址之前。
*/
pgtbl r4 @ page table address
/*
* 按16个bytes一次,将页表清空
*/
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
/*
*r10 = proc_info_list类型结构体的基地址
*PROCINFO_MM_MMUFLAGS 8 /* offsetof(struct proc_info_list, __cpu_mm_mmu_flags) @ */
*
*因此,r7为结构体中__cpu_mm_mmu_flags的数值
*/
ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
/*
*下面代码建立kernel对应的section页表项。
*
*1. 通过PC值的高12位(右移20位),得到kernel的section,并存储在r6中。
*2. 获取32bit的页表表单值
*3. 将页表表单值存放在页表内存区中。
*
*注意点:
*a. lsr 20 因为虚拟地址分区中,后20位为相对地址,前12位为段地址
*b. lsl 2因为每一个页表项为4字节,所以需要左移2位
*/
mov r6, pc, lsr #20 @ start of kernel section
orr r3, r7, r6, lsl #20 @ flags + kernel base
str r3, [r4, r6, lsl #2]@ identity mapping
/*
* 下面的 add r0, r4, #(KERNEL_START & 0xff000000) >> 18 涉及到一个立即数的概念:
* 关于arm汇编立即数可以参考相关文章
*
* 下面这段代码就是存储kernel物理地址。建立页表,虚拟地址和物理地址之间建立连接。
* 即:将内核中所有的物理地址(1M为单位)都存放到了页表中,与虚拟地址一一对应。
*/
add r0, r4, #(KERNEL_START & 0xff000000) >> 18
str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!
ldr r6, =(KERNEL_END - 1)
add r0, r0, #4
add r6, r4, r6, lsr #18
1: cmp r0, r6
add r3, r3, #1 << 20
strls r3, [r0], #4
bls 1b
/*
* XIP介绍:
* XIP是指 (EXECUTE IN PLACE) 是指直接从存放代码的位置上启动运行。
* 非XIP方式是指在运行之前需对代码进行重定位。该类型的内核以非压缩方式存放在Flash中,启动时由Bootloader加载到内存后运行。
*
* 如果是XIP技术的内核,上面的映射只能映射内核代码和只读数据部分
* 这里我们再映射一些RAM来作为.data and .bss空间。
*/
#ifdef CONFIG_XIP_KERNEL
orr r3, r7, #(KERNEL_RAM_PADDR & 0xff000000)
.if (KERNEL_RAM_PADDR & 0x00f00000)
orr r3, r3, #(KERNEL_RAM_PADDR & 0x00f00000)
.endif
add r0, r4, #(KERNEL_RAM_VADDR & 0xff000000) >> 18
str r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
ldr r6, =(_end - 1)
add r0, r0, #4
add r6, r4, r6, lsr #18
1: cmp r0, r6
add r3, r3, #1 << 20
strls r3, [r0], #4
bls 1b
#endif
/*
* 下面的代码用来设置RAM中大小为1M虚拟地址的页表。之所以要设置这个页表项的原因是该区域存储着boot params。
* 因此需要为它建立map,这样开启MMU后就可以访问
*/
add r0, r4, #PAGE_OFFSET >> 18
orr r6, r7, #(PHYS_OFFSET & 0xff000000)
.if (PHYS_OFFSET & 0x00f00000)
orr r6, r6, #(PHYS_OFFSET & 0x00f00000)
.endif
str r6, [r0]
/*
* 结束
*/
mov pc, lr
.ltorg
结果图: