ldr r5, =0x1FF00000
and r2, r2, r5 ; VA needs 512MB, 1MB aligned.
原来R2中存储的是虚拟地址值(如0x80000000),通过上面的代码,将R2中数据的[29-21]位置为1,前面已经说过将空间划分成了多个1MB的段,那么就必须以1MB对齐,而且WinCE最多只能支持512MB的物理内存,所以虚拟地址的合法区间是0x80000000~0x9FFFFFFF,正好是512MB的大小(这段是带缓冲的虚拟地址,相应的不带缓冲的虚拟地址是0xA0000000~0xBFFFFFFF)。R2中的数据正是虚拟地址对应的页表相对于页表首地址的偏移地址,也就是R2用来计算该虚拟地址对应的页表的存储地址,从下面的代码会看出这点。
ldr r5, =0xFFF00000
and r3, r3, r5 ; PA needs 4GB, 1MB aligned.
原来R3中存储的是物理地址的地址值,页表项的内容由两部分组成:物理地址的高12位(高12位)+缓冲读写属性(低12位),这里的代码正是保留了物理地址的高12位,清空低20位,正是为了形成页表项内容进行准备。
add r2, r10, r2, LSR #18
add r0, r0, r3 ; (r0) = PTE for next physical page
这两条代码正是在前面计算得基础上,用R2中虚拟地址的高14位的偏移量+R10中存储的页表的基地址,从而将该虚拟地址对应的页表要存储的地址计算出来,并赋值给R2。而R0存储的数据是关于页表对应的虚拟页的缓冲读写属性,再加上R3中存储的物理地址的高12位,从而形成了相应页表项的内容,并赋值给R0。
35
str r0, [r2], #4
add r0, r0, #0x00100000 ; (r0) = PTE for next physical page
sub r4, r4, #1 ; Decrement number of MB left
cmp r4, #0
bne %B35 ; Map next MB
上面的第一条代码,通过str指令将R0中存储的页表项内容,写入R2指向的地址中,同时R2指向下一个地址。接下来将页表项的内容增加1MB的大小,从oemaddrtab_cfg.inc的映射表可以看出,最后一个参数表示的分配存储空间的大小,是以MB为单位,所以R0指向的是下一个物理地址页(单位1M)的页表项的内容。之后将R4中空间大小的值减1,表示剩余要构造的页表数目,如果不为0,则跳转到标号35处,继续构造页表,知道这一个页表条目完成为止(或者说这一段存储空间全部映射完成为止)。
bic r0, r0, #0xF0000000 ; Clear Section Base Address Field
bic r0, r0, #0x0FF00000 ; Clear Section Base Address Field
b %B30 ; Get next element
这部分代码简单的说就是清空R0中的高12位,即清空上一个物理地址的高12位,因为要用来存储下一个物理地址的高12位,同时保留低20位中的缓冲读写属性,完成后跳转到标号30处,继续读取全局内存映射表的内容,构造地址映射的页表。其中%B30表示向前寻找标号为30的地址(B=Before)。
40
tst r0, #8
bic r0, r0, #0x0C ; clear cachable & bufferable bits in PTE
add r10, r10, #0x0800 ; (r10) = ptr to 1st PTE for "unmapped uncached space"
bne %B25 ; go setup PTEs for uncached space
sub r10, r10, #0x3000 ; (r10) = restore address of 1st level page table
一般WinCE会根据全局内存映射表进行两份虚实映射,一份带缓冲的虚拟地址和不带缓冲的虚拟地址。到这里的时候,说明0x80000000~0x9FFFFFFF这段512M的虚拟地址的页表已经构造完成了,这段地址是带缓冲的,下面需要对不带缓冲的虚拟地址构造页表。
第一条tst语句表示判断R0的位[7]的值是否为1,从而影响bne跳转语句的结果。R0的第7位在页表项内容中表示的是缓冲的属性,如果为1,表明还没有开始构造不带缓冲的虚拟地址对应的页表,如果不为0,说明不带缓冲的虚拟地址对应的页表也已经构造完成了(是不是有点晕啊,呵呵,因为当第二次循环运行这条语句的时候,已经就完成了不带缓冲的页表的构造,不行你自己走一遍代码就知道了)。第二条bic的语句很简单,就是为了将高速缓冲和写缓冲的属性值去掉。第三条语句有点意思,其实就是为了将R10指向存储不带缓冲的虚拟地址对应的页表的存储地址(0xA0000000),但是采用的方法是计算偏移量,当前R10中存储的是带缓冲的虚拟地址对应的页表的存储地址(0x80000000),而0xA0000000于0x8000000相差0x20000000,那么把0x20000000>>18得到的结果就是0x0800,所以在R10的基础加上0x0800就是页表应该存储的地址(其实就是依据虚拟地址高14位代表的是相对于页表首地址的偏移量,所以也可以直接用0xA0000000>>18位来计算页表的存储地址,不信你试试,结果是一样的)。第四条语句就简单了,向前跳转到标号25处,像缓冲虚拟地址的页表构造的方法对不带缓冲虚拟地址的页表进行构造。第五条语句为下面的代码做准备,将R10指向的地址重新指向页表基地址,即PT_1ST_BASE,网上有人说这句有问题,其实没有问题,不过这一句确实没什么用处,因为下面对R10进行了重新赋值,即将PT_1ST_BASE重新赋给了R10。不过为了理解明白,本人还是解释一下这句是怎么计算出来的?首先在最开始R10增加了0x2000,上这里有两次增加了0x0800(为什么是两次呢?好好看代码,上面的第三条语句会被执行两次),加起来正好是0x3000,所以减去这个值得到的正是页表的基地址。
;----------------------------------------------
; Setup mmu to map (VA == 0) to (PA == 0x30000000).
; cached area
ldr r0, =PT_1ST_BASE ; PTE entry for VA = 0
ldr r1, =PT_1ST_ENTRY_CNB ; Cache/Unbuffer/RW
str r1, [r0]
上面是将0x0的带缓冲的虚拟地址映射到0x30000000的物理地址处, 0x0的虚拟地址对应的页表的存储地址就在PT_1ST_BASE处(0x0>>18得到的是偏移量,说明就在页表基地址处)。第二条语句是设置页表项的内容:物理地址的高12位+缓冲读写属性。第三条语句str将页表项的内容写入到对应的页表存储地址处。
; uncached area.
add r0, r0, #0x0800 ; PTE entry for VA = 0x02000000
ldr r1, =PT_1ST_ENTRY_NCNB ; Uncache/Unbuffer/RW
这部分用来映射不带缓冲的虚拟地址0x20000000(后面的注释不对)到物理地址0x30000000,上之前分析的一样,采用计算偏移量的方法,第二条和第三条语句和上一部分的一样,这里不再赘述。
本文转自jazka 51CTO博客,原文链接:http://blog.51cto.com/jazka/572626,如需转载请自行联系原作者