MMU及PTS说明

来源:互联网 发布:点卡寄售平台源码 编辑:程序博客网 时间:2024/05/01 05:41

MMUPTS表格

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

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

1MMU初始化代码分析

其实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

 

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

    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的原因

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

    ; 第一个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, #8bic     r0, r0, #0x0C是用来计算后需要填充PTS表格中的标记,其实结果就是x402

    ; 第三行add     r10, r10, #0x0800中的x0800其实就是xa000 0000PTS表格中的相对偏移(相对于虚拟地址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 0000MB空间的基址

    ; r0表示虚拟地址x0000 0000PTS表格中的位置,其实就在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 0000PTS表格中的偏移

    ; 而是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 0000PTS表格中的偏移

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

    ; (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,类似于前面的x402x40e

    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,终于把MMUenable了,可以用了,哈哈,爽

    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下面的变量和数组比较多的话,一定要调整下面

        ; Samsungwhimory.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

PTs0x70010000

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 ffff0x7000 0000~0x700f ffff的映射如下

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

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

 

4>  0xa000 0000~0xa3ff ffff0x7000 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

 

累死我了,终于写完了。

如果有没写清楚的地方,欢迎发邮件到guopeixin@126.com或者在此留言。