我考,不会用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重新编辑 ]