__create_page_tables 在head.S中,为start_kernel前执行的一段汇编代码,作用是初始化页表项
/* * 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 @ page table address /* * 清空页表项 */ 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 ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags /* * 创建__turn_mmu_on代码段的映射 */ adr r0, __turn_mmu_on_loc ldmia r0, {r3, r5, r6} sub r0, r0, r3 @ virt->phys offset add r5, r5, r0 @ phys __turn_mmu_on add r6, r6, r0 @ phys __turn_mmu_on_end mov r5, r5, lsr #SECTION_SHIFT mov r6, r6, lsr #SECTION_SHIFT 1: orr r3, r7, r5, lsl #SECTION_SHIFT @ flags + kernel base str r3, [r4, r5, lsl #PMD_ORDER] @ identity mapping cmp r5, r6 addlo r5, r5, #1 @ next section blo 1b /* * 创建kernel镜像的映射 */ add r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER) 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 /* * 创建dtb镜像的映射 */ 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]
ret lr ENDPROC(__create_page_tables)
1.pdtbl是什么?
- pgtbl是一个宏,TEXT_OFFSET为kernel代码段相对phys_offset的偏移,一般为0x8000(32K),PG_DIR_SIZE为一级页表PGD的大小,为0x4000(16K)
- 最终r4保存PGD页表的物理基地址
pgtbl r4, r8 @ page table address
.macro pgtbl, rd, phys
add \rd, \phys, #TEXT_OFFSET
sub \rd, \rd, #PG_DIR_SIZE
.endm
2.procinfo结构体包含什么信息?
- r7保存mm_mmuflags,r10指向proc_info_list结构体,PROCINFO_MM_MMUFLAGS为mm_mmuflags相对r10的偏移量
- PMD_TYPE_SECT表明PGD页表是section类型,即段式映射
/* 结构体的定义如下,在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; };
/*cortex-A9的procinfo的定义如下,在proc-v7.S文件中,标黄的那段即r7保存的mmu状态位*/
.type __v7_ca9mp_proc_info, #object __v7_ca9mp_proc_info: .long 0x410fc090 .long 0xff0ffff0 __v7_proc __v7_ca9mp_proc_info, __v7_ca9mp_setup, proc_fns = ca9mp_processor_functions .size __v7_ca9mp_proc_info, . - __v7_ca9mp_proc_info /* * Standard v7 proc info content */ .macro __v7_proc name, 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 initfn \initfunc, \name .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
3.一级页表项如何创建?
- 由mmuflag可知,PGD为段式映射,一级页表项的内容如下(ARM® Architecture Reference Manual )
- 低2位代表页表的类型,0b00为invalid;0b01为page table;0b10为section或supersection;0b11为section或supersection,需支持PXN
- [31:20]保存的是物理地址的高12位,一个section映射1M的物理内存
- 其他位为该section的属性位
- 如下就是填充页表项的过程
/* adr为位置无关代码,保存__turn_mmu_on_loc的运行地址,这里mmu尚未打开,r0保存物理地址
* r3,r5,r6分别保存__turn_mmu_on_loc、__turn_mmu_on、__turn_mmu_on_end的虚拟地址
* 通过__turn_mmu_on_loc 标签的VA-PA的差值可以得到__turn_mmu_on段的物理地址
* 一级页表项【31:20】保存映射区间物理地址的高12位,这里先右移20位
*/
adr r0, __turn_mmu_on_loc ldmia r0, {r3, r5, r6} sub r0, r0, r3 @ virt->phys offset add r5, r5, r0 @ phys __turn_mmu_on add r6, r6, r0 @ phys __turn_mmu_on_end mov r5, r5, lsr #SECTION_SHIFT mov r6, r6, lsr #SECTION_SHIFT
__turn_mmu_on_loc: .long . .long __turn_mmu_on .long __turn_mmu_on_end
/* r5 << 20 | r7,地址高12位+flag完成页表项内容
* 每个页表项占用4个字节,总共4k个页表项,r5目前保存的是段(1M)偏移,需要*4 + 基地址来表示待填充的页表的地址
*/
1: orr r3, r7, r5, lsl #SECTION_SHIFT @ flags + kernel base str r3, [r4, r5, lsl #PMD_ORDER] @ identity mapping cmp r5, r6 addlo r5, r5, #1 @ next section blo 1b