__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
01-20 22:05