Chinaunix首页 | 论坛 | 博客

  • 博客访问: 481214
  • 博文数量: 86
  • 博客积分: 2010
  • 博客等级: 大尉
  • 技术积分: 878
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-06 14:11
文章分类

全部博文(86)

文章存档

2010年(12)

2009年(60)

2008年(14)

我的朋友

分类: 服务器与存储

2009-03-03 21:02:23

    在非操作系统下使用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------------------------------[[

 

总结:先写到这,任务尚未完成。

阅读(2214) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~