- 论坛徽章:
- 0
|
在非操作系统下使用mmu,需要手动建立和管理页表。最近一段时间在师兄的带领下实现了arm926ej的mmu的开启,页表的手动创建和管理。确定虚实地址的对应关系是痛苦的,为此我用mfc编写了一个页表转换的小程序,通过输入虚拟地址和物理地址、基址寄存器、2级页表基地址等信息得到1级页表和2级页表的地址和描述符,代码比较简单,在此就不献丑了。
代码运行在linux下的arm处理器模型facsim上,地址空间由0x00000000-0x001fffff,一共2M,为了实现在页错异常中管理页表,把abort模式的堆栈指针指向0x1ffffc,并把从0x1ff000开始的页的虚拟地址映射到同物理地址的页帧,即这个页的物理地址与虚拟地址相同,作为abort模式下的堆栈。这样在页错异常后,开关mmu不影响abort堆栈的访问。
本试验中,将发生第一次页表错误的页帧放到spm中,虚拟地址不变,通过改变页表将虚拟地址映射到spm中。
下面介绍一下页表的创建过程:
1.首先建立好各个模式下的堆栈指针,注意把abort模式下的堆栈指针指向0x1ffffc!
AREA BOOT, CODE, READONLY
ENTRY
ldr sp, =0x301ffefc ;init sp_svc
mov R4, #0xD2 ;chmod to irq and init sp_irq
msr cpsr_cf, R4
ldr sp, =0x301ffdfc
mov R4, #0XD1 ;chomod to fiq and init sp_fiq
msr cpsr_cf, R4
ldr sp, =0x301ffcfc
mov R4, #0XD7 ;chomod to abt and init sp_ABT
msr cpsr_cf, R4
ldr sp, =0x1ffffc
mov R4, #0XDB ;chomod to undf and init sp_UNDF
msr cpsr_cf, R4
ldr sp, =0x301ffafc
;chomod to abt and init sp_sys
mov R4, #0xDF ;all interrupts disabled
msr cpsr_cxsf, R4 ;SYSTEM mode, @32-bit code mode
ldr sp, =0x301ff0fc
mov R4, #0XD3 ;chmod to svc modle, CPSR IRQ bit is disable
msr cpsr_c, R4
2.关闭I/DCache,挤干write buffer,关闭mmu
mov r0,#0
mcr p15,0,r0,c7,c7,0 ; 失效I/DCache
mcr p15,0,r0,c7,c10,4 ; 挤干WB
mcr p15,0,r0,c8,c7,0 ; 失效I/D TLBs
mrc p15,0,r0,c1,c0,0 ; 加载控制寄存器C1到R0
bic r0,r0,#0x000f ; 将R0的W(WB)C(Cache)A(Align)M(MMU)位清零
bic r0,r0,#0x1100 ; 将R0的I(ICache)S(System Protection)位清零
mcr p15,0,r0,c1,c0,0 ; 将R0写到控制寄存器C1
3.将页表区域清零,页表的基地址是0x4000,结束地址是0x7ffc
ldr r4,=0x4000 ; 页表的开始地址
mov r0,r4 ; 保存页表开始地址到R0,为后面循坏递增准备
ldr r3, =0x0
ldr r6,=0x7ffc ; 调用循环将0x4000~0x8000清零,准备写页表
1 str r3, [r0], #4
teq r0, r6
bne %b1
4.写入1级页表。代码从0x30008000开始执行,对应的1级页表地址为0x4c00,0x30100000对应1级页表地址为0x4c04,一共2M地址空间,所以只用到两个1级页表项。
ldr r4,=0x4c00 ; R0寄存器加载第一个段的1级页表项
mov r0,r4 ;
ldr r3,=0x6011 ; 1级页表描述符,意义:粗页表,IMP=100,Domain=0,粗页表基址=
str r3, [r0], #4 ;
ldr r3,=0x6431 ; 1级页表描述符,负责映射VA0x3010,0000到0x0010,0000
str r3, [r0]
5.写入2级页表项。2级页表基地址是0x6000,2级页表项最后两位是01,所以对应的是4k大页。
写入2级页表
ldr r4,=0x6000 ; 2级页表开始位置
mov r0,r4 ;
ldr r3, =0xffe ; 准备2级页表项,意义:4KB页,CB位打开AP位全部置位
add r6, r0, #0x800 ; 将2级页表结束地址保存到r6中
1 str r3, [r0], #4 ; 循环装载2级页表
add r3, r3, #0x1000 ; 每次加0x1000,为了页号每次自加(偏移)1页
teq r0, r6 ; 和0x6800对比,等于则意味着完成虚地址0x3000,0000~0x3020,0000共2MB的映射
bne %b1
6.为了产生页错异常,将数据段的2级页表项的最后两位设成00,这样mmu取到2级页表项的时候将发生数据异常,在异常处理函数中我们就可以重新修改页表了。注意代码中的红色部分!
ldr r4,=0x60c0 ; 2级页表(数据段)的开始位置
mov r0,r4 ;
ldr r3, =0x30ffc ;
add r6, r0, #0x740 ;
1 str r3, [r0], #4 ;
add r3, r3, #0x1000
teq r0, r6
bne %b1
7.创建一些特殊的页表。注意前面部分我们的1级页表项是从0x4c00开始放的,我们在0x4000处放置0x0000开始的页帧和0x8000开始的页帧的一级页表项,将这两个页帧的虚实地址映射为相等,2级页表放在0x4400和0x4420处。同理,为了实现abort异常的堆栈虚实地址相等,我们也需要单独设置这个页帧的页表。改页帧的1级页表放在0x4004,2级页表放在0x47fe。地址的映射关系在此就不详述了。
ldr r4,=0x4000 ; 1级页表
ldr r5,=0x4471 ; 1级页表项,意义:二级页表的基址应该是0x4400,IMP=b100,Domain=b0011
str r5,[r4]
ldr r4,=0x4400 ; 为映射0页的2即页表地址
ldr r5,=0xffe ; 0页的2级页表项
str r5,[r4]
ldr r4,=0x4420 ; 0x8000地址对应页2级页表地址
ldr r5,=0x8FFE ; 0x8000地址对应页2级页表项
str r5,[r4]
ldr r4,=0x4c08 ;
;--------------------映射0x1ff000 到 0x1ff000
ldr r4,=0x4004 ; 1级页表
ldr r5,=0x4471 ; 1级页表项,意义:二级页表的基址应该是0x4400,IMP=b100,Domain=b0011
str r5,[r4]
ldr r4,=0x47fe ; 0x1ff000地址对应页2级页表地址
ldr r5,=0x1ffffe ; 0x1ff000地址对应页2级页表项
str r5,[r4]
8.开启mmu。初始化过程结束。
ldr r0, =0x0
ldr r1, =0x40000055 ; 准备填写Domain域,由于上面用到0和3两个域,所以需要给0x55,最前面的4问题不大
mcr p15,0,r1,c3,c0,0 ; 写入Domain域
ldr r1, =0x4005147D ; 准备填写C1控制寄存器
IMPORT __main ; 准备打开MMU后跳向的地址
ldr r5,=__main
mcr p15,0,r1,c1,c0,0 ; 打开MMU和Cache等
add pc,r5,#0 ; 跳PC到刚才准备的__main的虚地址地址继续执行
mov r0,r0 ; 填充流水线
mov r0,r0
mov r0,r0
mov r0,r0
mov r0,r0
END
9.下面开始讨论abort异常处理程序
ABORT_DATA_DO
;-----------------------数据中止异常------------------------
;-----------------------压栈--------------------------------==
subs lr, lr, #8 ;获得数据中止时pc值
stmfd sp!,{r0-r11, lr} ;压榨
;----------------------------end---------------------------==
10.关闭mmu
;------------------------关闭mmu---------------------------++
mrc p15,0,r9,c6,c0,0 ; 从错误地址寄存器中拿到FAR,放入R9保存
mov r0,#0
mcr p15,0,r0,c7,c7,0 ; 失效I/DCaches
mcr p15,0,r0,c7,c10,4 ; 挤干WB
mcr p15,0,r0,c8,c7,0 ; 失效I/D TLBs
mrc p15,0,r0,c1,c0,0 ; 加载控制寄存器C1到R0
bic r0,r0,#0x000f ; 将R0的W(WB)C(Cache)A(Align)M(MMU)位清零
bic r0,r0,#0x1100 ; 将R0的I(ICache)S(System Protection)位清零
mcr p15,0,r0,c1,c0,0 ; 将R0写到控制寄存器C1
;-----------------------------end---------------------------++
11.修改页表。先将所有的数据段的2级页表项置为01,然后将发生页错异常的段的物理地址映射到spm中,地址为0x04000000,最后从sdram中搬运数据到spm,这样下次访问该页帧时就可以从spm中访问数据了,而不需要到sdram中。
;---------------修改页的2级页表描述符(测试)--------------------##
ldr r4,=0x60c0 ; 2级页表(数据段)的开始位置
mov r0,r4 ;
ldr r3, =0x30ffe ;
add r6, r0, #0x740 ;
1 str r3, [r0], #4 ;
add r3, r3, #0x1000
teq r0, r6
bne %b1
;-------------------------end--------------------------------##
;-----------------------修改页表映射---------------------------**
; 查找该页原来的页表
mov r1, r9 ; 加载保留了FAR寄存器中错误地址的R9寄存器的值到R1
mov r8, r1 ; 将R1中的错误地址保存到R8
mrc p15,0,r0,c2,c0,0 ; 加载页表基址寄存器C2到R0,准备软件找页表
orr r0,r0,r1,LSR #18 ; 使用错误地址产生1级页表,由R0保存
and r0,r0,#0xFFFFFFFC ;
ldr r2,[r0] ; 加载1级页表描述符到r2
;and r2,r2,#0xfffff800 ; ++++++++新增,取2级页表基地址?
and r1,r1,#0x000ff000 ; 得到2级页表索引r1
orr r2,r2,r1,LSR #10 ; --------得到2级页表地址r2,问什么用orr??
;add r2,r2,r1,LSR #10 ; ++++++++得到2级页表地址
and r2,r2,#0xfffffffc ; 清除低2位数据
; Generate the pagetable entry for linear mapping
ldr r3,[r2] ;R3中是二级页表描述符
and r3,r3,#0xff0 ;修改2级页表
orr r3,r3,#0x04000000 ;修改2级页表大页地址为0x0400
orr r3,r3,#0xe ;2级页表改为小页
; str r3,[r2] ;保存新页表
; Copy data from sdram to spm
ldr r6,=0xFFFFF000
and r1,r3,r6 ;小页地址保存到R1
and r2,r8,r6 ;页错地址得到1级索引和2级索引
add r6,r1,#0x1000
1 ldr r3,[r2]
str r3,[r1],#4
teq r1,r6
add r2,r2,#4
bne %b1
;endof 1 page SPM in total
;-------------------------end--------------------------------**
12.开mmu。
;-------------------------开mmu -----------------------------&&
ldr r1, =0x4005147D
mcr p15,0,r1,c1,c0,0
mov r0,#0x0
;--------------------------end-------------------------------&&
13.出栈。
;--------------------------出栈------------------------------[[
ldmfd sp!,{r0-r11, pc}^
;--------------------------end------------------------------[[
总结:先写到这,任务尚未完成。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/84450/showart_1850301.html |
|