主题 : arch/arm/boot/compressed/head.S页表初始化代码分析 复制链接 | 浏览器收藏 | 打印
级别: 侠客
UID: 54261
精华: 9
发帖: 43
金钱: 665 两
威望: 133 点
贡献值: 9 点
综合积分: 266 分
注册时间: 2011-08-27
最后登录: 2014-08-08
楼主  发表于: 2013-05-17 16:13

 arch/arm/boot/compressed/head.S页表初始化代码分析

我考,不会用BBS,发贴格式太乱了...

学习Linux解压代码中页表创建过程
1.
# 以cache_on函数为例
# 输入条件 r4 = kernel execution address
cache_on:        mov    r3, #8 @ cache_on function               # r3实际上是call_cache_fn函数的一个参数,用于确定要执行的具体操作(cache_on/ cache_off/ cache_flush)
                           b    call_cache_fn                    # 调用call_cache_fn函数    
2.        
# 各个ARM版本的cache操作各不相同(函数名分别带有__armv7/__armv6等前缀),通过查表法调用适合当前ARM版本的cache函数        
call_cache_fn:    adr    r12, proc_types                         # proc_types 是cache操作函数表,其具体格式在第3节分析
#ifdef CONFIG_CPU_CP15                                           # 从协处理器或编译系统获取Processor_ID,以便解析出ARM的版本,解析方法有点傻叉
                    mrc    p15, 0, r9, c0, c0    @ get processor ID
#else
                    ldr    r9, =CONFIG_PROCESSOR_ID    
#endif
1:                 ldr    r1, [r12, #0] @ get value               # 根据Processor_ID解析ARM版本的基本思路是:特定ARM版本的Processor_ID的某几位是固定值
                    ldr    r2, [r12, #4] @ get mask               # mask指定Processor_ID的某几位,value指定这几位的值
                    eor    r1, r1, r9    @ (real ^ match)         # 如果满足(Processor_ID ^ value) & mask == 0,则匹配成功
                    tst    r1, r2    @ & mask                     # ARM版本匹配成功后,就确定了要调用哪一组函数(也就确定了函数前缀__armv7/ __armv6/ __armv5等)
ARM(         addeq    pc, r12, r3    ) @ call cache function      # r3 确定要执行的具体功能(选择 cache_on/ cache_off/ cache_flush),跳转执行了
THUMB(   addeq    r12, r3    )  
THUMB(   moveq    pc, r12    ) @ call cache function
                    add    r12, r12, #PROC_ENTRY_SIZE             # 如果没匹配成功,就尝试匹配下一个表项
                    b    1b
3.
# 这一节分析函数跳转表格式
# 实际上,可以把函数跳转表理解成一个结构体数组
# 数组的每个元素就是一个结构体,列出特定ARM版本的mask、value以及函数指针
# 结构体定义如下:
/*
* Table for cache operations. This is basically:
* - CPU ID match                                                  # 用于匹配Processor_ID的value
* - CPU ID mask                                                   # 用于匹配Processor_ID的mask
* - 'cache on' method instruction                                 # 函数指针
* - 'cache off' method instruction
* - 'cache flush' method instruction
*
* We match an entry using: ((real_id ^ match) & mask) == 0
*
* Writethrough caches generally only need 'on' and 'off'
* methods. Writeback caches _must_ have the flush method
* defined.
*/
                    .align    2
                    .type    proc_types,#object
proc_types:
                    .word    0x00000000    @ old ARM ID
                    .word    0x0000f000
                    mov    pc, lr
THUMB(    nop    )
                    mov    pc, lr
THUMB(    nop    )
                    mov    pc, lr
THUMB(    nop    )
    ...
    ...
    ...
                # armv6函数跳转表
                    .word    0x0007b000    @ ARMv6                # value
                    .word    0x000ff000                           # mask    
                    W(b)    __armv6_mmu_cache_on                  # 函数指针
                    W(b)    __armv4_mmu_cache_off                 # 函数指针
                    W(b)    __armv6_mmu_cache_flush               # 函数指针
                    # armv7函数跳转表
                    .word    0x000f0000    @ new CPU Id    
                    .word    0x000f0000
                    W(b)    __armv7_mmu_cache_on
                    W(b)    __armv7_mmu_cache_off
                    W(b)    __armv7_mmu_cache_flush            
4.
# 查表后跳转到__armv7_mmu_cache_on
# 输入条件: r4 = 内核执行地址
__armv7_mmu_cache_on:
                    mov    r12, lr
#ifdef CONFIG_MMU
                    mrc    p15, 0, r11, c0, c1, 4    @ read ID_MMFR0
                    tst    r11, #0xf @ VMSA
                    movne    r6, #CB_BITS | 0x02 @ !XN            # RAM页表项的CB位定义
                    blne    __setup_mmu                           # 设置页表,在第5节分析
                    mov    r0, #0                        
                    mcr    p15, 0, r0, c7, c10, 4    @ drain write buffer
                    tst    r11, #0xf @ VMSA
                    mcrne    p15, 0, r0, c8, c7, 0    @ flush I,D TLBs
#endif
                    mrc    p15, 0, r0, c1, c0, 0    @ read control reg
                    bic    r0, r0, #1 << 28 @ clear SCTLR.TRE
                    orr    r0, r0, #0x5000 @ I-cache enable, RR cache replacement
                    orr    r0, r0, #0x003c @ write buffer
                    bic    r0, r0, #2 @ A (no unaligned access fault)
                    orr    r0, r0, #1 << 22 @ U (v6 unaligned access model)
                    @ (needed for ARM1176)
#ifdef CONFIG_MMU
#ifdef CONFIG_CPU_ENDIAN_BE8
                    orr    r0, r0, #1 << 25 @ big-endian page tables
#endif
                    mrcne p15, 0, r6, c2, c0, 2 @ read ttb control reg
                    orrne    r0, r0, #1 @ MMU enabled
                    movne    r1, #0xfffffffd @ domain 0 = client
                    bic r6, r6, #1 << 31 @ 32-bit translation system
                    bic r6, r6, #3 << 0 @ use only ttbr0
                    mcrne    p15, 0, r3, c2, c0, 0    @ load page table pointer
                    mcrne    p15, 0, r1, c3, c0, 0    @ load domain access control
                    mcrne p15, 0, r6, c2, c0, 2 @ load ttb control
#endif
                    mcr    p15, 0, r0, c7, c5, 4    @ ISB
                    mcr    p15, 0, r0, c1, c0, 0    @ load control register
                    mrc    p15, 0, r0, c1, c0, 0    @ and read it back
                    mov    r0, #0
                    mcr    p15, 0, r0, c7, c5, 4    @ ISB
                    mov    pc, r12
5.
# 设置页表
__setup_mmu:    sub    r3, r4, #16384 @ Page directory size          # r3 = 内核起始地址 - 16k
                    bic    r3, r3, #0xff @ Align the pointer         # r3 &= ~16k,向下16KB对齐后作为页表起始地址
                    bic    r3, r3, #0x3f00
/*
* Initialise the page tables, turning on the cacheable and bufferable
* bits for the RAM area only.
*/
                    mov    r0, r3                                    # r0 = 页表起始地址
                    mov    r9, r0, lsr #18                           # r9 = r0 & ~256k, 页表起始地址向下256k对齐后作为RAM起始地址
                    mov    r9, r9, lsl #18 @ start of RAM
                    add    r10, r9, #0x10000000 @ a reasonable RAM size          # r10 = 页表起始地址 + 256M,作为RAM结束地址,这里假定RAM地址为256MB
                    mov    r1, #0x12 @ XN|U + section mapping                    # 页表项的初始值:最低两位为10,说明是section map,1MB映射;CB两位为00,默认禁止缓存和缓冲
                    orr    r1, r1, #3 << 10 @ AP=11                              # 页表项的初始值:访问控制位设置为11,完全允许访问;31-20位为0x000,section物理基址为0x000
                    add    r2, r3, #16384                                        # r2 = 页表起始地址 + 16k,作为页表结束地址
1:                 cmp    r1, r9    @ if virt > start of RAM                     # 判断section的物理基址是否落在RAM范围内:因为对RAM应该开启缓存和缓冲
                    cmphs    r10, r1    @ && end of RAM > virt
                    bic    r1, r1, #0x1c @ clear XN|U + C + B                    # 清除XN|U和CB位,XN指execute never
                    orrlo    r1, r1, #0x10 @ Set XN|U for non-RAM                # 如果落在RAM范围外,不设置CB位,但设置XN|U位
                    orrhs    r1, r1, r6    @ set RAM section settings            # 如果落在RAM范围内,不设置XN|U位,但设置CB位, 这里r6 = #CB_BITS | 0x02,见第4节
                    str    r1, [r0], #4 @ 1:1 mapping                            # 把页表项写入页表,并跳转页表指针
                    add    r1, r1, #1048576                                      # 把页表项指向下一个section的物理基址
                    teq    r0, r2                                                # 检查是否到了页表结束地址,如果是则说明页表初始化完成了
                    bne    1b                                                    # 否则写入下一个页表项
[ 此帖被mei5150在2013-05-17 16:50重新编辑 ]
*無鈳取玳
级别: 论坛版主
UID: 27
精华: 12
发帖: 5398
金钱: 40120 两
威望: 17929 点
贡献值: 71 点
综合积分: 11036 分
注册时间: 2008-01-16
最后登录: 2014-11-22
1楼  发表于: 2013-05-17 16:48
谢谢分享
"If you have an apple and I have an apple and we exchange apples, then you and I will
still each have one apple. But if you have an idea and I have an idea and we exchange
these ideas, then each of us will have two ideas."