Chinaunix首页 | 论坛 | 博客
  • 博客访问: 567343
  • 博文数量: 169
  • 博客积分: 2656
  • 博客等级: 少校
  • 技术积分: 1685
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-30 13:03
文章分类

全部博文(169)

文章存档

2011年(1)

2010年(135)

2009年(33)

我的朋友

分类: 嵌入式

2010-10-19 13:01:23

MMU与PTS表格

最近在FPGA上仿真调试Virgo(基于ARM11的一款处理器)芯片。MMU部分总是出错,具体的现象是查看物理地址和虚拟地址的映射时候芯片经常会挂掉。先是怀疑MMU的寄存器配置有问题,后来又怀疑MMU映射使用的PTS表格有问题,最后发现竟然是RAM没有清零导致的,唉,竟然犯了这种的错误,实在是雷人。

为了解决问题,这两天对这部分代码进行了分析和调试,担心过两天会忘掉,赶紧写下来。

1.MMU初始化代码分析
其实MMU的初始化过程就是PTS表格的初始化过程。

那么啥是PTS表格呢?

PTS表格是供MMU进行地址映射和察看内存属性信息的表格。

PTS表格主要记录了两方面的信息,第一:虚拟地址对应的物理地址在哪个位置,第二:虚拟地址的属性信息,如上面的0x402/0x40e/0x41e。

MMU详细的初始化过程参照下面的代码解释:

           b       .

 

        INCLUDE oemaddrtab_cfg.inc

 

;------------------------------------------------------------------

        ; Compute physical address of the OEMAddressTable.

20    add     r11, pc, #g_oalAddressTable - (. + 8)

    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

 

    ; 接下来这三行代码是配置ptr指针的位置,以及初始化DRAM部分物理地址在PTS映射表中的标记,即E

    ; 后面会将这个标记放置到PTS映射表中

    add     r10, r10, #0x2000       ; (r10) = ptr to 1st PTE for "unmapped space"

    mov     r0, #0x0E           ; (r0) = PTE for 0: 1MB cachable bufferable

    orr     r0, r0, #0x400      ; set kernel r/w permission

25    mov     r1, r11         ; (r1) = ptr to MemoryMap array

 

   

    ; 获取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

 

    ; g_oalAddressTable表格的最后一行是DCD     0x00000000, 0x00000000,  0 

    ; 也即r4 = 0

    cmp     r4, #0          ; End of table?

    beq     %f40

 

    ; 这里也不用说了,就是限定最大值为MB和GB

    ldr     r5, =0x1FF00000

    and     r2, r2, r5          ; VA needs 512MB, 1MB aligned.       

 

    ldr     r5, =0xFFF00000

    and     r3, r3, r5          ; PA needs 4GB, 1MB aligned.

 

    ; 值得一提的是下面的这个值,网上的争论也比较多

    ; PTS表格中的最小元素代表了MB的物理地址空间,这也是g_oalAddressTable中映射的最小单位是MB的原因

    ; 假如说有MB的DRAM需要进行映射,每MB在PTS表格中占据一个元素(四个字节的位置),最终就是:

    ; 第一个MB放置在PTS表格偏移为x0的位置,假如说这段MB DRAM 的物理地址是x3000 0000,则存放到这里的数据就是x3000 0000

    ; 第二个MB放置在PTS表格中偏移为x4的位置,数据是x3010 0000[即这MB空间的起始物理地址]

    ; 第三个MB放置在PTS表格中偏移为x8的位置,数据是x3020 0000[即这MB空间的起始物理地址]

    ; 第四个MB放置在PTS表格中偏移为xc的位置,数据是x3030 0000[即这MB空间的起始物理地址]

    ; 如果DRAM很大的话,依次类推

    ; 注意观察一下上面的偏移x0,其实可以通过(x3000 0000-0x3000 0000)>>18计算出来

    ; 注意观察一下上面的偏移x4,其实可以通过(x3010 0000-0x3000 0000)>>18计算出来

    ; 注意观察一下上面的偏移x8,其实可以通过(x3020 0000-0x3000 0000)>>18计算出来

    ; 注意观察一下上面的偏移xc,其实可以通过(x3030 0000-0x3000 0000)>>18计算出来

    ; 很明显,这个为的右移值是由PTS的最小元素所代表的物理空间大小决定的

    add     r2, r10, r2, LSR #18

    add     r0, r0, r3          ; (r0) = PTE for next physical page

 

    ; 接下来这四行代码就是将DRAM或者寄存器对应的物理地址填充到PTS表格中,r2是表格的指针,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

 

    bic     r0, r0, #0xF0000000     ; Clear Section Base Address Field

    bic     r0, r0, #0x0FF00000     ; Clear Section Base Address Field

    ; 查询g_oalAddressTable表格的下一个Element(不知道该咋翻译)

    ; 起始一个Element就对应表格g_oalAddressTable的一行,如DCD     0x93300000, 0xD0102000,  1就是一个element

    b       %b30            ; Get next element

   

    ; 下面这行代码是用来将g_oalAddressTable表格中的物理地址同时也映射到xa000 0000~0xbfff ffff这个Uncache空间中

    ; tst     r0, #8和bic     r0, r0, #0x0C是用来计算后需要填充PTS表格中的标记,其实结果就是x402

    ; 第三行add     r10, r10, #0x0800中的x0800其实就是xa000 0000在PTS表格中的相对偏移(相对于虚拟地址x8000 0000

    ; 在pts表格中位置的偏移),可以这样计算

    ; (0xa000 0000-0x8000 0000)>>18 = 0x0800

    ; 第行代码没有意义,可以删除

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 ?

 

    ; 接下来是将虚拟地址x0000 0000~0x000f ffff这段空间映射到物理RAM的前MB空间

    ; 该芯片上RAM的物理基址在x7000 0000,所以对应的就是x7000 0000~0x700f ffff

    ; 值得说明的是x7000040E,表示位于x7000 0000这MB空间的基址

    ; 而r0表示虚拟地址x0000 0000在PTS表格中的位置,其实就在PTS表格中的最开始位置

    ; 1. Setup mmu to map (VA == 0) to (PA == 0x70000000).

    ; 1-1. cached area.

    ldr     r0, =PTs        ; PTE entry for VA = 0

    ldr     r1, =0x7000040E     ; cache/buffer/rw, PA base == 0x70000000

    ;ldr     r1, =0x70000402     ; cache/buffer/rw, PA base == 0x70000000

    str     r1, [r0]

 

    ; 下面三行其实和上面的四行代码类似,表示将虚拟地址x2000 0000映射到物理地址x7000 0000

    ; 第一行代码中的x0800表示虚拟地址x2000 0000在PTS表格中的偏移

    ; 而是UNCACHE ram的标记

    ; 1-2. uncached area.

    add     r0, r0, #0x0800     ; PTE entry for VA = 0x0200.0000 , uncached    

    ldr     r1, =0x70000402     ; uncache/unbuffer/rw, base == 0x70000000

    str     r1, [r0]

   

   

    ; 接下来这段代码将虚拟地址x7000 0000映射到物理地址x7000 0000,这段映射空间的大小是MB

    ; 即DRAM空间的大小

    ; Comment:

    ; The following loop is to direct map RAM VA == PA. i.e.

    ;   VA == 0x70XXXXXX => PA == 0x70XXXXXX for Virgo

    ; Fill in 8 entries to have a direct mapping for DRAM

    ;

    ldr     r10, =PTs           ; restore address of 1st level page table

    ldr     r0,  =PHYBASE

 

    ; 下面这一行#(0x7000 / 4)同样是计算虚拟地址x7000 0000在PTS表格中的偏移

    ; 下面这段代码我没有改,抄袭了三星的做法,它们没有写好,正确的写法应该是:

    ; (0x7000 0000>>18),是不是搞得你云里雾里的,鄙视Samsung,将来Vrigo的方案

    ; 出去之后,一定要把公版BSP给改的简单易懂,要不然OEM厂家又要骂了

    add     r10, r10, #(0x7000 / 4) ; (r10) = ptr to 1st PTE for (0x70000000>>16)/sizeof(DWORD)

 

    ; 下面的#0x1E和#0x400最终组合成一个标记x40e,类似于前面的x402和x40e。

    add     r0, r0, #0x1E       ; 1MB cachable bufferable

    orr     r0, r0, #0x400      ; set kernel r/w permission

    mov     r1, #0

    mov     r3, #64          ; 128MB SDRAM

    ;mov     r3, #128        ; 128MB SDRAM

    ; 下面的r2表示当前的映射在PTS表格中的偏移

    ; 第三行代码纯属三星的人发贱,正确易懂的写法是add     r2, r10, r2, LSL2

    ; 干脆用C语言写更加易懂一些,就是r2 = r2*4 + r10,这里的左移Bit主要原因还是PTS中的每个元素是个字节

45    mov     r2, r1          ; (r2) = virtual address to map Bank at

    cmp     r2, #0x20000000:SHR:BANK_SHIFT

    add     r2, r10, r2, LSL #BANK_SHIFT-18

    strlo    r0, [r2]

    add     r0, r0, #0x00100000     ; (r0) = PTE for next physical page

    subs     r3, r3, #1

    add     r1, r1, #1

    bgt     %b45

 

 

    ; 兄弟们肯定在想,我考你在这里搞了大半天,修改的都是PTS,那MMU咋能知道呢?

    ; 呵呵,不要急,到了,下面的p15, 0, r10, c2, c0, 0不是把PTS的地址给MMU了么,哈哈,大功告成

    ; 就剩下启动MMU了

    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

 

    mcr     p15, 0, r0, c8, c7, 0   ; flush I+D TLBs

   

;    mrc     p15,0,r1,c1,c0,0

   

    orr     r1, r1, #0x0071         ; Enable: MMU

    orr     r1, r1, #0x0004     ; Enable the cache

 

 

    ldr     r0, =VirtualStart

 

    cmp     r0, #0          ; make sure no stall on "mov pc,r0" below

    ; OK,终于把MMU给enable了,可以用了,哈哈,爽

    mcr     p15, 0, r1, c1, c0, 0

 

    mov     pc, r0          ;  & jump to new virtual address

    nop

 

 

    ; MMU & caches now enabled.

    ;   (r10) = physcial address of 1st level page table

    ;

;------------------------------------------------------------------

 

VirtualStart

 

        mrs     r0, cpsr

 

        ; 下面这段是堆栈的配置,如果你发现EBoot下面的变量和数组比较多的话,一定要调整下面

        ; 如Samsung的whimory.eboot就需要相当大的Stack空间,小的话就会出莫名其妙的问题

        ; 哦,对了,差点忘了,Stack是从上朝下增长的,而Ram是从从下朝上增长的,不要越界了

        bic     r0, r0, #Mode_MASK

        orr     r1, r0, #Mode_IRQ | NOINT

        msr     cpsr_cxsf, r1               ; IRQMode

        mov     sp, #0x80000000

        add     sp, sp, #0x3d000        ;

 

        bic     r0, r0, #Mode_MASK | NOINT

        orr     r1, r0, #Mode_SVC

        msr     cpsr_cxsf, r1               ; SVCMode      

        mov     sp, #0x80000000

        add     sp, sp, #0x40000        ;

        b       main

 

        ENTRY_END

 

        LTORG

2.最终生成的PTS表格
上面的代码太抽象了,我把PTS表格Dump出来之后用表格列写了以下,如下:

其中,第四列表示虚拟地址,第三列表示虚拟地址对应物理地址,第一列表示PTS表格中的位置偏移,而第二列为PTS表格中存放的数据。每1MB的虚拟地址在下面的表格中都对应一行。

address
 value
 physical address
 VIRTUAL ADD
 CHIP
 SPACE
 
0X70012000
 0X7000040E
 0X70000000
 0x80000000
 DDR
 Cached Space
 
0X70012004
 0X7010040E
 0X70100000
  
 
0X70012008
 0X7020040E
 0X70200000
  
 
0X7001200C
 0X7030040E
 0X70300000
  
 

 …
 …
  
 
0X700121FC
 0X77F0040E
 0X77F00000
  
 
0x700124C8
 0xD010040E
 0xD0101000
 0x93200000
 UART0
 
0X70012800
 0x70000402
 0X70000000
 0xA0000000
 DDR
 UNCACHED SPACE
 
0X70012804
 0x70100402
 0X70100000
 0xA0100000
 

 …
 …
  
 
0x70012CC8
 0xD0100402
 0xD0101000
 0xA3200000
 UART0
 
0X70010000
 0X7000040E
 0X70000000
 0X00000000
 DDR 
  映射0地址到物理内存的开始位置,这里只映射1MB的空间,属性为Cache
 
0X70010800
 0xD0100402
 0X70000000
 0X20000000
 DDR
  映射0x20000000地址到物理内存的开始位置,这里也是仅仅映射1MB的空间,属性为UnCache
 
 
  
  
  
  
  
 
0X70011C00
 0X7000041E
 0X70000000
  X70000000
  

DDR 

 

 

 
 映射地址0X70000000到物理内存开始的位置 
 
0X70011C04
 0X7010041E
 0X70100000
 0X70100000
  
 
0X70011C08
 0X7020041E
 0X70200000
 0X70200000
  
 

 …
 …
 …
  
 
0X70011DFC
 0X77F0041E
 0X77F00000
 0X77F00000
  
 

最终赋值给MMU的值如下:

Value
 MMU.SFR
 About
 
1
 c3&c0
 Open MMU
 
PTs(0x70010000)
 c2&c0
 set up access to domain 0
 
7800041e
 c8&c7
 flush I+D TLBs
 
5007d
 c0&c1
 Enable: MMU and cache
 

3.物理地址和虚拟地址映射关系图形化显示
感觉上面的物理地址和虚拟地址的映射不够形象,我把他们的映射关系用下面的图形表示。

1>  0x0000 0000~0x000f ffff和0x7000 0000~0x700f ffff的映射如下

2>  0x2000 0000~0x200f ffff和0x7000 0000~0x700f ffff的映射如下

3>  0x8000 0000~0x83ff ffff与0x7000 0000~0x73ff ffff的映射如下
 

4>  0xa000 0000~0xa3ff ffff与0x7000 0000~0x73ff ffff的映射如下

5>  UART寄存器的映射如下

 

4.附g_oalAddressTable表格
; Export Definition

 

        EXPORT  g_oalAddressTable[DATA]

 

;------------------------------------------------------------------------------

;

; TABLE FORMAT

;       cached address, physical address, size

;------------------------------------------------------------------------------

 

g_oalAddressTable

 

      DCD     0x80000000, 0x70000000, 64     ; 512 MB DRAM BANK        

DCD     0x93200000, 0xD0101000,  1      ; uart0 slv register

       DCD     0x00000000, 0x00000000,  0      ; end of table

;------------------------------------------------------------------------------

        END

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