下面将详细叙述MMU的设置,也是本人花费时间最多的一部分内容,无论是2410、6410甚至是Cortex-A8核的ARM,MMU的设置基本都一样,所以移植时这部分可以直接搬过来,只需要更改全局内存映射表的映射关系即可。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
先说说为什么在EBoot要设置MMU?其实有大牛们讨论过这个话题,在系统启动时会对页表进行重新映射,包括二级页表的设置,而在EBoot中只进行了一级页表的设置,最后也没有给出明确的答案,有的说是WinCE规定的,这里先不追究了,等以后研究了系统启动后的代码,再来讨论这个问题。OAL作为WinCE的开始需要启用虚拟内存,需要为MMU设置正确的页表进行地址映射,另外WinCE编译系统产生的二进制文件.bib使用的内存地址都是虚拟地址,这些虚拟地址是编译系统对二进制代码进行地址重定位的重要依据,所以在Eboot中对MMU进行设置,定义内存映射表的依据是全局内存映射表,在PLATFORM\SMDK6410\SRC\INC\oemaddrtab_cfg.inc文件中定义,内容如下:
g_oalAddressTable
; mDDR 128 MB
;DCD 0x80000000, 0x50000000, 64 ; 64 MB DRAM
[ SMDK6410_X5D
DCD 0x80000000, 0x60000000, 64 ; 64 MB DRAM
|
DCD 0x80000000, 0x50000000, 128 ; 128 MB DRAM
]
DCD 0x90000000, 0x70000000, 4 ; SROM SFR
DCD 0x00000000, 0x00000000, 0 ; end of table
; mDDR 128 MB
;DCD 0x80000000, 0x50000000, 64 ; 64 MB DRAM
[ SMDK6410_X5D
DCD 0x80000000, 0x60000000, 64 ; 64 MB DRAM
|
DCD 0x80000000, 0x50000000, 128 ; 128 MB DRAM
]
DCD 0x90000000, 0x70000000, 4 ; SROM SFR
DCD 0x00000000, 0x00000000, 0 ; end of table
它是以g_oalAddressTable宏开始的,DCD用来分配一片连续的字存储单元,第一个参数是虚拟地址值,第二个参数是对应的物理地址值,第三个参数指明分配的大小,在映射表的最后,将三个参数的值均设为0,表示是映射表的结束。下面看startup.s文件中的源码。
;------------------------------------
; Initialize MMU Table
;------------------------------------
;----------------------------
; Compute physical address of the OEMAddressTable.
20
add r11, pc, #g_oalAddressTable -(. + 8)
ldr r10, =PT_1ST_BASE ; (r10) = 1st level page table
; Initialize MMU Table
;------------------------------------
;----------------------------
; Compute physical address of the OEMAddressTable.
20
add r11, pc, #g_oalAddressTable -(. + 8)
ldr r10, =PT_1ST_BASE ; (r10) = 1st level page table
上面的代码将R11赋值为内存映射表的地址,R10赋值为页表存储的地址,PT_1ST_BASE在最开始处已经进行了定义,是页表基地址0x50010000,解释一下R11的赋值语句。ARM处理器是流水线结构,允许指令预取,所以PC一般等于当前执行指令下面的第2条指令的地址,即PC=当前指令地址值+8字节,而“.”代表的是当前指令的地址值,PC=(. + 8),这样R11获得的是g_oalAddressTable的地址。那为什么不直接mov r11 #g_oalAddressTable进行赋值呢?确实不能这样,这样可以编译通过,但是执行不通过,只能采用和PC的相对值来换算。
add r10, r10, #0x2000 ; (r10) = ptr to 1st PTE for "unmapped space"
这一条代码,将R10的值增加了0x2000,因为第1级MMU的入口是地址值的高14为(过滤掉低18位),g_oalAddressTable中看出,DRAM的起始虚拟地址为0x80000000,而0x80000000>>18=0x2000,正好是0x80000000地址对应的第1级MMU入口的偏移值,也就是说R10现在存储的是0x80000000虚拟地址对应的页表的存储地址。那么为什么要右移18位?第1级页表将4GB的地址空间划分为多个1MB的段,对应的就有4096个页表项,而ARM地址映射时,虚拟地址被分为两部分:高位+低位,高位用来表示虚拟地址对应的页表针对页表首地址的偏移值,而低位表示在页表1MB地址空间中的偏移量,页表中的一个页表项可以描述4字节的虚拟页(因为ARM是32位的,每次都是读取4字节的数据),那么1MB的空间就需要256KB个这样的页表项才可以描述(256K*4=1M),而要表示256KB的大小,需要虚拟地址的低18位,剩下的高14位就可以用来计算虚拟地址对应页表相对于页表首地址的偏移量了。
mov r0, #0x0E ; (r0) = PTE for 0: 1MB cachable bufferable
orr r0, r0, #0x400 ; set kernel r/w permission
orr r0, r0, #0x400 ; set kernel r/w permission
上面的两条代码用来设置页表项的高速缓冲、写缓冲以及读写属性,具体的设置请参看CP15协处理器C1寄存器的功能,以后有时间会整理一篇关于CP15协处理器的说明。其实读者如果仔细看会发现,设置后R0=0x40E,和文件开始声明的PT_1ST_ENTRY_CNB变量的值是一样的,所以也可以用mov r0 PT_1ST_ENTRY_CNB来代替两条语句,但是本人觉得用两条语句更能表明设置了什么,语义更明确。
25
mov r1, r11 ; (r1) = ptr to MemoryMap array
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
mov r1, r11 ; (r1) = ptr to MemoryMap array
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
首先将R11也就是g_oalAddressTable指向的全局内存映射表的地址赋给R1(这就是高手的代码书写习惯,不会直接操作R11),然后依次将R1指向的地址处的数据赋值给R2、R3和R4寄存器,“#4”是表示每次操作完后,R1的地址值增加4字节,指向下一个数据,其实对照oemaddrtab_cfg.inc的映射表,很容易发现,R2获得的是虚拟地址的值,R3获得的是对应物理地址的值,R4则是这段存储空间的大小。
cmp r4, #0 ; End of table?
beq %F40
beq %F40
上面两条代码用来判断是否到了oemaddrtab_cfg.in中映射表的结尾,上面已经介绍过,映射表的最后一条的三个参数全部都为0,标识映射表的结束。beq是一条跳转语句,表示如果相等,则跳转,%F40表明向后寻找名称为40的标号(F=After)。
转载于:https://blog.51cto.com/jazka/572644