参考:http://blog.chinaunix.net/u1/38994/showart_1881778.html
http://blog.youkuaiyun.com/alien75/archive/2009/12/07/4955156.aspx
这几天研究了一下MMU和Cache,小有所得,现在把设置一级页表的汇编文件做详细的整理和解释以便后续查看。
32位的的CPU虚拟内存为232次方=4G,一个页表描述符(页表项)对应1M的虚拟内存空间,则4G对应4096个一级页表项,一个页表项又占用4字节,需要4096×4=16K的物理内存用于存放一级页表。但bootloader没有必要完整的构造4096个页表项,只要够用就可以了。
startup所构造的页表先要覆盖0x80000000-0x9FFFFFFF(允许cache)的VA,容量为512M,对应512个页表项,所需存储空间是2K,即物理RAM地址为0x30012000-0x300127FF。而0xA0000000-0xBFFFFFFF(禁止cache)对应页表项占用的物理地址为0x30012800-0x30012FFF。
内存中存放页表项的起始地址为pagetablestart(PTS). 参考:http://blog.youkuaiyun.com/chinesedragon2010/archive/2010/08/08/5796534.aspx
每一个页表项pageentry(PTE)包含四个字节的数据。一级页表项如下图所示:
使用一级页表把VA转化成PA的过程是:
1.VA的31-20位为索引值,19-0位为偏移量。
2.CP15的C2寄存器31-14位(PTS)与索引值右移18位,后两位置0,组成一个物理地址就是页表项的物理地址
3.根据这个物理地址找到页表项
4.页表项的31-20位为段基地址,剩余二十位如上图所示。
5.VA对应的PA为段基地址与偏移量的结合。
PA= *((VA(31-20))>>18 + PTS) & 0xFFF0000 +VA(19-0)
蓝色标注部分为页表项。
按位与0xFFF0000的涵义在于取页表项的31-20位,即段基地址。
; Define RAM space for the Page Tables:
;
PHYBASE EQU 0x30000000 ; physical start 物理地址起始处
PTs EQU 0x30010000 ; 1st level page table address (PHYBASE + 0x10000) 一级页表的起始地址
; Compute physical address of the OEMAddressTable.
20 add r11, pc, #g_oalAddressTable - (. + 8) 把g_oalAddressTable 的地址放到R0中
ARM处理器是流水线结构的,允许指令预取。在CPU执行当前指令的同时,可以从存储器中预取指令,所以当用户读取PC时,PC指向的是正在取指的指令,而非当前执行的指令,在ARM中,一般是当前执行指令下面的第2条指令(+8个字节)。所以这里的+8就不难理解了。
.是指当前指针,而.=pc-8; pc+g_oalAddressTable - (. + 8) =g_oalAdressTable.
参考:http://topic.youkuaiyun.com/u/20070122/16/fc8ec835-e373-41d2-885e-6360348d58b2.html
ldr r10, =PTs ; (r10) = 1st level page table
; Setup 1st level page table (using section descriptor)
; Fill in first level page table entries to create "un-mapped" regions
; from the contents of the MemoryMap array.
;
; (r10) = 1st level page table
; (r11) = ptr to MemoryMap array
add r10, r10, #0x2000 ; (r10) = ptr to 1st PTE for "unmapped space" r10中存放0x80000000VA在PTR中对应的PA
PTs保存在物理内存中的地址是0x30010000 ,0x2000是什么意义呢?我们知道没有缓存的虚拟地址起始地址是0x80000000,那这个虚拟地址对应的物理地址是多少呢?根据VA映射到PA的规则,见下图:
从0x80000000中取出[31:20]位,也即0x800来左移2位之后为0x2000,这个就是上面这个值的来由)加上translation base(在这里是PTs),add r10, r10, #0x2000这行语句的意义,执行这行语句之后r0=0x30012000,这就是计算了4G虚拟地址空间中从0x80000000地址开始的虚拟地址对应的物理起始地址是0x30012000,也就是section base address=0x30012000。(bootloader只使用一级页表见P32李大为的书)
mov r0, #0x0E ; (r0) = PTE for 0: 1MB cachable bufferable R0存放页表项 使能cache&write buffer
orr r0, r0, #0x400 ; set kernel r/w permission AP值
25
mov r1, r11 ; (r1) = ptr to MemoryMap array r1指向g_oalAddressTable 的指针
30
ldr r2, [r1], #4 ; (r2) = virtual address to map Bank at
ldr r3, [r1], #4 ; (r3) = physical address to map from
ldr r4, [r1], #4 ; (r4) = num MB to map
cmp r4, #0 ; End of table?
beq %f40
表示cachable地址0x80000000~0xa0000000映射MMU创建完毕,表结尾,前跳到40标号,创建对应的uncached映射MMU
ldr r5, =0x1FF00000
and r2, r2, r5 ; VA needs 512MB, 1MB aligned. 对va虚拟地址进行512M对齐
//也就是r2的上限值不能超过0x20000000即512M,因为r10现在指向1级映射表的0x80000000虚拟地址,
//又因为0x80000000+0x20000000=0xa0000000开始地址存放的是uncached地址,所以这里要做512M数据限制
ldr r5, =0xFFF00000
and r3, r3, r5 ; PA needs 4GB, 1MB aligned. 对pa物理地址进行4G对齐
add r2, r10, r2, LSR #18 VA右移18位再加上PTS(页表项的基地址)
add r0, r0, r3 ; (r0) = PTE for next physical page 获取下一个物理页的入口地址
35
str r0, [r2], #4 //存到r2对应的PTR偏移中
add r0, r0, #0x00100000 ; (r0) = PTE for next physical page
//1M递增,因为1<<18=256K,而每一个PTE描述4k页,所以最终描述256k*4k=1M地址空间,一个页表项对应虚拟内存1M空间。
sub r4, r4, #1 ; Decrement number of MB left
cmp r4, #0
bne %b35 ; Map next MB //遍历到该region结束
bic r0, r0, #0xF0000000 ; Clear Section Base Address Field
bic r0, r0, #0x0FF00000 ; Clear Section Base Address Field //清空页表项的31-20位,即段基地址
b %b30 ; Get next element //继续创建oemaddrtab_cfg.inc描述的下一region,直到cachedVA对应的PTE全部完成
//创建对应的0xa0000000开始的uncached映射MMU表
40
tst r0, #8
//比较C高速缓存是否仍然置位,如果仍然置位了,那么说明还没有执行uncached创建,如果C位已经清0,那么说明,uncached循环也执行完毕了,所以跳回到25标号继续创建
bic r0, r0, #0x0C ; clear cachable & bufferable bits in PTE //清除B写缓冲和C高速缓存
add r10, r10, #0x0800 ; (r10) = ptr to 1st PTE for "unmapped uncached space"
r10现在对应0xA0000000虚拟地址的PTR起始地址,因为原来r10为80000000,现在只需要再加上20000000就好了,hex(0x20000000>>18)为0x800,
//所以r10 = r10 + 0x800;之后r10指向了0xa0000000虚拟地址对应的PTR起始地址
bne %b25 ; go setup PTEs for uncached space
//ok,现在0x80000000 cachable空间和0xa0000000 uncached空间都已经创建完毕了
sub r10, r10, #0x3000 ; (r10) = restore address of 1st level page table
//该句好像有问题,不过还好后面用到r10之前,又对r10进行了重新赋值,所以也更说明,该句操作无意义
——————————————————————————————————————————————————从这里开始不怎么明白了,后续解决
//接下来为虚拟0地址建立cachable和uncached映射
; 1. Setup mmu to map (VA == 0) to (PA == 0x30000000).
; 1-1. cached area.
ldr r0, =PTs ; PTE entry for VA = 0
ldr r1, =0x3000040E ; cache/buffer/rw, PA base == 0x30000000//以C,B均开启的方式映射虚拟地址0到pa物理地址0x30000000
str r1, [r0]
; 1-2. uncached area.
add r0, r0, #0x0800 ; PTE entry for VA = 0x0200.0000 , uncached //接下来的0x2000000虚拟地址被映射为虚拟0地址的uncached起始地址
ldr r1, =0x30000402 ; uncache/unbuffer/rw, base == 0x30000000//以C,B均不开启的方式映射虚拟地址0到pa物理地址0x30000000
str r1, [r0]
//经过上面设置可以看到
//虚拟地址0对应0x30000000的cachable
//虚拟地址0x2000000对应0x30000000的uncached.
; Comment:
; The following loop is to direct map RAM VA == PA. i.e.
; VA == 0x30XXXXXX => PA == 0x30XXXXXX for S3C2400
; Fill in 8 entries to have a direct mapping for DRAM
;
ldr r10, =PTs ; restore address of 1st level page table
ldr r0, =PHYBASE//PHYBASE为0x30000000,将虚拟地址映射到相同的物理地址
add r10, r10, #(0x3000 / 4) ; (r10) = ptr to 1st PTE for 0x30000000//就是(0x30000000 >> 18
//现在r10指向了和DDR地址相等的PA地址对应的PTR地址处?????不明白
add r0, r0, #0x1E ; 1MB cachable bufferable
orr r0, r0, #0x400 ; set kernel r/w permission
mov r1, #0
mov r3, #64
//映射64M空间,因为我们的物理DDR只有64M(见上面的oemaddrtab_cfg.inc表图),不能大于512,因为下面只支持到512次循环(r1为循环次数,每循环1次,创建1M空间映射表)
45
mov r2, r1 ; (r2) = virtual address to map Bank at
cmp r2, #0x20000000:SHR:BANK_SHIFT //0x20000000>>20=512M,
add r2, r10, r2, LSL #BANK_SHIFT-18 //add r2, r10, r2, 4即r2 = r10 + r2*4;正好为PTR操作时的4字节对齐边界
strlo r0, [r2] //如果还没有操作到512M,那么填充PTR,如果现在映射操作已经操作过了512M大小,那么忽略
add r0, r0, #0x00100000 ; (r0) = PTE for next physical page
subs r3, r3, #1
add r1, r1, #1
bgt %b45
ldr r10, =PTs ; (r10) = restore address of 1st level page table
; The page tables and exception vectors are setup.
; Initialize the MMU and turn it on.
mov r1, #1
mcr p15, 0, r1, c3, c0, 0 ; setup access to domain 0
mcr p15, 0, r10, c2, c0, 0 将页表基地址存储在CP15的C2寄存器中
mcr p15, 0, r0, c8, c7, 0 ; flush I+D TLBs //flush所有MMU相关空间
mrc p15,0,r1,c1,c0,0
orr r1, r1, #0x0071 ; Enable: MMU //设置打开MMU标志到r1
orr r1, r1, #0x0004 ; Enable the cache //设置打开cache标志到r1
ldr r0, =VirtualStart //取出VirtualStart编译期间得到的虚拟地址值,在0x30038000~0x30038000+eboot_size之间
cmp r0, #0 ; make sure no stall on "mov pc,r0" below //执行该比较,使r0踏实的读入VirtualStart数值
mcr p15, 0, r1, c1, c0, 0 //将MMU设置标志写入p15协处理器,生效设置
mov pc, r0 ; & jump to new virtual address //跳转过去,此刻之后MMU已经打开,所有允许操作的虚拟地址都在PLATFORM/SMDK2440A/Src/Inc/oemaddrtab_cfg.inc中定义
nop
; MMU & caches now enabled.
; (r10) = physcial address of 1st level page table
;
VirtualStart
mov sp, #0x80000000 ; have to be modefied. refer oemaddrtab_cfg.inc, DonGo //设置栈空间,从这里来看,sp栈顶对应的DDR物理地址为0x30000000,然后向下生长,从boot.bib中
/*
ARGS 80020800 00000800 RESERVED
RAM 80021000 0000B000 RAM
STACK 8002c000 0000A000 RESERVED 2c000+a000=36000,保留2000,即8K空间做EBOOT的SP栈空间
EBOOT 80038000 00040000 RAMIMAGE
BINFS 80080000 00021000 RESERVED
*/
add sp, sp, #0x30000 ; arbitrary initial super-page stack pointer
b main 跳转到MAIN函数
ENTRY_END