paging_init 详解
paging_init主要完成初始化内核的分页机制,通过对boot阶段页表的覆盖,填充新的一级页表
建立二级页表项由 set_pte_ext 宏实现,实际上底层调用的是在内核启动之初获取的 list->processor->set_pte_ext,这是处理器相关的处理函数,对应的函数实现为 cpu_v7_set_pte_ext,在 arch/arm/mm/proc-v7-2level.S 中。
ENTRY(cpu_v7_set_pte_ext) #ifdef CONFIG_MMU str r1, [r0] @ linux version @直接把pte属性写入Linux版本页表 bic r3, r1, #0x000003f0 @r1值清除bit4~bit9存储到r3,r3作为临时变量存储硬件pte属性。 bic r3, r3, #PTE_TYPE_MASK @清除r3 bit0、bit1,没有清除bit3、bit2是因为cache、buffer这两位,hw和linux保持一致。 orr r3, r3, r2 @r3=r3|r2,r2是额外属性 orr r3, r3, #PTE_EXT_AP0 | 2 @设置hw bit4(AP0bit),设置hw bit1(根据上图,可以得知small page下bit1必须等于1) tst r1, #1 << 4 @判断r1传入的页表属性,是否设置了bit4(tex位) orrne r3, r3, #PTE_EXT_TEX(1) @如果linux属性设置了tex位,则硬件版本也要置位tex(hw bit6~8),两者bit偏移位置不一样。 eor r1, r1, #L_PTE_DIRTY @翻转r1 DIRTY位(bit6) tst r1, #L_PTE_RDONLY | L_PTE_DIRTY @判断是否设置了只读。+ orrne r3, r3, #PTE_EXT_APX @设置硬件版本hw bit9(APX值(置1只读)) tst r1, #L_PTE_USER @判断传入属性是否设置L_PTE_USR orrne r3, r3, #PTE_EXT_AP1 @设置硬件版本hw bit5(AP1bit) tst r1, #L_PTE_XN @判断传入属性是否设置XN(不可执行) orrne r3, r3, #PTE_EXT_XN @设置硬件版本hw bit0(不可执行位) tst r1, #L_PTE_YOUNG tstne r1, #L_PTE_VALID eorne r1, r1, #L_PTE_NONE tstne r1, #L_PTE_NONE moveq r3, #0 ARM( str r3, [r0, #2048]! ) THUMB( add r0, r0, #2048 ) THUMB( str r3, [r0] ) ALT_SMP(W(nop)) ALT_UP (mcr p15, 0, r0, c7, c10, 1) @ flush_pte #endif bx lr ENDPROC(cpu_v7_set_pte_ext)
在 set_pte_ext 中,除了设置地址映射部分之外,还需要处理大量标志位的设置(比如 YOUNG、DIRTY bit),毕竟针对同一个页面,需要填充一个软件二级页表项和一个硬件二级页表项,硬件二级页表项给 MMU 使用,保存在 软件二级页表项 + 2048 地址处,在上面贴出的汇编代码中也有体现