《转》 MMU代码分析(作者:wogoyixikexie@gliet)

MMU代码分析(作者:wogoyixikexie@gliet

    ——以前我一直误以为MMU在OAL才开的,在bootloader是不开的,真是大错特错。这个也不怪我,因为有很多书都说在bootloader阶段是不开的。现在看看代码真是感慨万千。

————————————————————————

    这两天,重新看了MMU和cache,对它有了一点了解,现在再把疑问放到论坛上来,希望大家帮忙(前几天参与押宝,现在分数还没有回来,没有分发帖了,以后有分再补回吧,sorry)
——————————————————————————————————————
我看了的ADS bootloader部分,发现一个很奇怪的问题,它居然只是对一级页表做了配置,并且把虚拟内存和物理内存映射成相等
不知道为什么会这样。这不是多此一举吗?
看如下代码(三星提供)


C/C++ code

/************************************************
  NAME    : MMU.C
  DESC      :
  Revision: 2002.2.28 ver 0.0
 ************************************************/

#include "def.h"
#include "option.h"
#include "2440addr.h"
#include "2440lib.h"
#include "2440slib.h"
#include "mmu.h"

// 1) Only the section table is used.
// 2) The cachable/non-cachable area can be changed by MMT_DEFAULT value.
//    The section size is 1MB.

 

void MMU_Init(void)
{
    int i,j;
    //========================== IMPORTANT NOTE =========================
    //The current stack and code area can't be re-mapped in this routine.
    //If you want memory map mapped freely, your own sophiscated MMU
    //initialization code is needed.
    //===================================================================

    MMU_DisableDCache();
    MMU_DisableICache();

    //If write-back is used,the DCache should be cleared.
    for(i=0;i<64;i++)
        for(j=0;j<8;j++)
            MMU_CleanInvalidateDCacheIndex((i<<26)|(j<<5));
    MMU_InvalidateICache();
   
    #if 0
    //To complete MMU_Init() fast, Icache may be turned on here.
    MMU_EnableICache();
    #endif
   
    MMU_DisableMMU();
    MMU_InvalidateTLB();

    //MMU_SetMTT(int vaddrStart,int vaddrEnd,int paddrStart,int attr)
    MMU_SetMTT(0x00000000,0x07f00000,0x00000000,RW_CNB);  //bank0
    MMU_SetMTT(0x08000000,0x0ff00000,0x08000000,RW_CNB);  //bank1
    MMU_SetMTT(0x10000000,0x17f00000,0x10000000,RW_NCNB); //bank2
    MMU_SetMTT(0x18000000,0x1ff00000,0x18000000,RW_NCNB); //bank3
    //MMU_SetMTT(0x20000000,0x27f00000,0x20000000,RW_CB); //bank4
    MMU_SetMTT(0x20000000,0x27f00000,0x20000000,RW_CNB); //bank4 for STRATA Flash
    MMU_SetMTT(0x28000000,0x2ff00000,0x28000000,RW_NCNB); //bank5
    MMU_SetMTT(0x30000000,0x30f00000,0x30000000,RW_CB);      //bank6-1
    MMU_SetMTT(0x31000000,0x33e00000,0x31000000,RW_NCNB); //bank6-2
    MMU_SetMTT(0x33f00000,0x33f00000,0x33f00000,RW_CB);   //bank6-3
    MMU_SetMTT(0x38000000,0x3ff00000,0x38000000,RW_NCNB); //bank7
   
    MMU_SetMTT(0x40000000,0x47f00000,0x40000000,RW_NCNB); //SFR
    MMU_SetMTT(0x48000000,0x5af00000,0x48000000,RW_NCNB); //SFR
    MMU_SetMTT(0x5b000000,0x5b000000,0x5b000000,RW_NCNB); //SFR
    MMU_SetMTT(0x5b100000,0xfff00000,0x5b100000,RW_FAULT);//not used

   
    MMU_SetTTBase(_MMUTT_STARTADDRESS);
    MMU_SetDomain(0x55555550|DOMAIN1_ATTR|DOMAIN0_ATTR);
        //DOMAIN1: no_access, DOMAIN0,2~15=client(AP is checked)
    MMU_SetProcessId(0x0);
    MMU_EnableAlignFault();
       
    MMU_EnableMMU();
    MMU_EnableICache();
    MMU_EnableDCache(); //DCache should be turned on after MMU is turned on.
}   


// attr=RW_CB,RW_CNB,RW_NCNB,RW_FAULT
void ChangeRomCacheStatus(int attr)
{
    int i,j;
    MMU_DisableDCache();
    MMU_DisableICache();
    //If write-back is used,the DCache should be cleared.
    for(i=0;i<64;i++)
        for(j=0;j<8;j++)
            MMU_CleanInvalidateDCacheIndex((i<<26)|(j<<5));
    MMU_InvalidateICache();
    MMU_DisableMMU();
    MMU_InvalidateTLB();
    MMU_SetMTT(0x00000000,0x07f00000,0x00000000,attr);    //bank0
    MMU_SetMTT(0x08000000,0x0ff00000,0x08000000,attr);    //bank1
    MMU_EnableMMU();
    MMU_EnableICache();
    MMU_EnableDCache();
}   
   

void MMU_SetMTT(int vaddrStart,int vaddrEnd,int paddrStart,int attr)
{
    volatile U32 *pTT;
    volatile int i,nSec;
    pTT=(U32 *)_MMUTT_STARTADDRESS+(vaddrStart>>20);
    nSec=(vaddrEnd>>20)-(vaddrStart>>20);
    for(i=0;i<=nSec;i++)*pTT++=attr |(((paddrStart>>20)+i)<<20);
}


但是在eboot中使用 的映射就是使用OEMAddresstable来初始化页表,这是wince真正的虚实映射
说道这里OEMAddresstable的虚拟起始地址0x80000000是wince规定的还是本来ARM决定的?
我觉得这个虚拟地址是可以改变的,所以才会可以把物理地址和虚拟地址映射成相等的?
——————————————————————————————————————————

引用 1 楼 xajhuang 的回复:
个人认为,虚拟内存和物理内存是否相等完全没有关系,ADS下没有操作系统,不需要那么复杂的保护操作,映射成相等可以方便使用,开启了MMU后的cache对系统性能提高不止一点,为的就在开启了MMU的情况下还能向使用物理地址一样使用寄存器地址。
 


有道理。
——————
并且由于ADS 下没有操作系统,所以只需要使用一级页表就完全够用了,
eboot调用了大量微软的函数库,以及源代码,所以不得不和微软的映射方法相同。
不过我觉得既然可以禁用MMU,为什么还要使用它呢?据我所知,禁用MMU之后虚拟地址和物理地址是相等的。why?
——————————————————————————————————————————

开启 MMU, 主要利用它的告诉 CACHE,这样可以大大加快系统速度,你不信比较下不开MMU时侯的系统运行的情况就明白了

——————————————————————————————————————————

wince的架构已经固定了,所以这个映射表肯定是固定的,因为微软是不开源的,我们在里面自己来一套自定义映射表,就是自找麻烦了。

——谢谢大家的积极参与
关于这个MMU和cache我已经写了两三篇博客了,不过我在文章中大量引用英文,只是加了少许注释,对这个文章很不满意,相当于写给自己看的。
下面这些书讲的比较好,大家如果想了解MMU、cache的工作原理以及硬件结构。看了他们真的会很有帮助。
ARM System Developer's Guide: Designing and Optimizing System Software
——ARM System Developer's Guide: Designing and Optimizing System Software——ARM嵌入式系统开发:软件设计与优化的英文原版——我个人感觉这是国内翻译ARM书籍最好的一本之一,比杜XX的ARM体系结构与编程好千倍。 本书虽然说软件设计与优化,但是讲的硬件也很多,比如MMU和cache等,讲的精彩纷呈:我刚才想写关于MMU和cache的博客,发现太庞大,看来这段时间要重新看看这本书才能写。
下载地址:http://download.csdn.net/source/904273

ARM920T Technical Reference Manual——不多说了,想了解2440等的bootloader的人一定要看这个东西了,一些协处理器指令讲的很详细
下载地址:http://download.csdn.net/source/903240

ARM Architecture Reference Manual(2nd Edition) ——比较有价值的英文ARM书籍
下载地址http://download.csdn.net/source/901433
————————继续,看看二级页表是怎么设置的。

 

————————————————————————————————————————

 

引用 14 楼 Ricky_hu 的回复:
0x80000000 - 0x9FFFFFFF represents a CACHED address.
0xA0000000 - 0xBFFFFFFF represents and UNCACHED address.

这是wince里面的定义!bsp里面直接引用就可以了吧,没有找到定义的地方!
 


这个完全是由OEMAddresstable决定了,至于为什么UNCACHED 要加上0x20000000而不是别的,我还是我原来的看法。
就是这个是由于硬件原因。这个ARM貌似没有说明。知道的来知会一声。
这个再次证明了我的观点OEMAddresstable是可以自己定义的,但是微软他定了这套标准,并且所有的代码都是围绕着这个转的
有些还不开源,所以我们是不能自己乱改的。
OEMAddresstable是以cached 形式写的,在程序中我们使用unchache的时候就要自己手动加上即可,这个微软都有源代码转换的
请看C:/WINCE500/PLATFORM/COMMON/SRC/INC/oal_memory.C

————————————————————————————————————————

哈哈,越来越明朗了
——————————————————
在这里,我声明我以前在论坛的一次错误回答:有个人问bootloader下的map.a(2440 4.2BSP)和OAL下的map.a是一模一样的,问我是否有用,我回答说:没有用,现在更正。——因为我的5.0 BSP 的bootloader下是没有 OEMAddresstable的,刚才看了一下sources文件,发现他链接了OEMAddresstable进来,——我在这里为我的粗心感到抱歉,希望他也能看到这个帖子。
————还有好多呢,继续,继续。
咱们CSDN的程序员最缺乏的就是硬件的一些东西了。

————————————————————————————————————————

引用 24 楼 singlerace 的回复:
0x80000000当然是CE的设计要求的,ARM架构没理由做这个限制。CE这样设计的目的是简化底层代码的开发。如果你仔细看wince设置页表的代码(在startup.s中),可以发现OEMAddresstable中相同的物理地址被映射了两遍,第一遍是cached address,第二遍是uncached address。 (这个的确如此,我看了。不过操作系统后面可以自己访问的时候设置的。)
在一个有操作系统的平台上,使用虚拟地址有很多好处:一个是通过限制进程的可以访问地址空间把不同的进程隔离开,防止它们互相影响;另外一个是可以捕捉程序错误,比如说C/C++程序中很常见的一个错误是访问空指针的内容。访问空指针其实是访问地址零里的内容,在ARM架构中物理地址零是一个有效地址(否则系统无法启动),访问地址零不会引发错误;另一方面一般在C/C++应用程序中访问空指针基本上肯定是程序员的错误,这种错误使用物理地址无法捕捉。启用虚拟地址后,一般进程空间的虚拟地址零不会映射到任何物理页面,程序对空指针地址的访问就会触发Page Fault异常,导致操作系统把它终结。当然启用cache也是另外一个好处。(学习,这个指针这里的确是有这么一手)
 


————谢谢 singlerace  学习了。
————————————————————————————
这么吧,我把cache以及MMU的作用贴上来吧(免得大伙还以为我想问cache以及MMU的作用呢。)
cache:一个和CPU很近的高速存储器,用来存储一些不是经常变化的数据,提高速度。在经常改变的数据的时候不适合启用,否则效率会更低
比如我们访问GPIO等不能使用cached 地址,就是这个原因,经常替换,效率很低的。(这个东西,也是我们PC的CPU的重要指标)
MMU:用在多任务操作系统中,给每个任务提供独立的虚拟地址空间,其实现原理是:在主存中存贮页表等数据,通过MMU映射到CPU,然后CPU就可以使用虚拟地址调度任务,访问外设等,虚拟地址和物理地址映射是固定的,这样操作系统比较安全稳定。

————————————————————————————————————————————

引用 43 楼 gooogleman 的回复:
我已经找到二级页表设置的地方,哎,可惜是元旦,还要出去购物
 


在 OAL的startup.s里面有bl KernelStart
然后在C:/WINCE500/PRIVATE/WINCEOS/COREOS/NK/KERNEL/ARM/armtrap.s(386):; KernelStart - kernel main entry point
找到我二级页表设置的地方。先看看先吧。哈哈。

————————————————————————————————————————————

Assembly code
;——在OAL的startup.s有如下:
;add     r0, pc, #g_oalAddressTable - (. + 8)
;        bl      KernelStart
;——以前真的没有仔细看过这些汇编,看过才能知道这个wince的流程到底是怎么回事。
;这相当于一到OAL阶段就要重新初始化页表,MMU,cache使能控制等
;在C:/WINCE500/PRIVATE/WINCEOS/COREOS/NK/KERNEL/ARM/armtrap.s有KernelStart,现在好好分析一下。
;----------------------------------------------------------------------------------------
; KernelStart - kernel main entry point
;
;       The OEM layer will setup any platform or CPU specific configuration that is
; required for the kernel to have access to ROM and DRAM and jump here to start up
; the system. Any processor specific cache or MMU initialization should be completed.
; The MMU and caches should not enabled.
;
;       This routine will initialize the first-level page table based up the contents of
; the MemoryMap array and enable the MMU and caches.
;
; NOTE: Until the MMU is enabled, kernel symbolic addresses are not valid and must be
;       translated via the MemoryMap array to find the correct physical address.
;
;       Entry   (r0) = pointer to MemoryMap array in physical memory
;       Exit    returns if MemoryMap is invalid
;-------------------------------------------------------------------------------
        LEAF_ENTRY KernelStart
;add     r0, pc, #g_oalAddressTable - (. + 8)
        mov     r11, r0                         ; (r11) = &MemoryMap (save pointer)

        ; figure out the virtual address of OEMAddressTable
        mov     r1, r11                         ; (r1) = &MemoryMap (2nd argument to VaFromPa)
        bl      VaFromPa
        mov     r6, r0                          ; (r6) = VA of MemoryMap

        ; convert base of PTs to Physical address
        ldr     r4, =PTs                        ; (r4) = virtual address of FirstPT
        mov     r0, r4                          ; (r0) = virtual address of FirstPT
        mov     r1, r11                         ; (r1) = &MemoryMap (2nd argument to PaFromVa)
        bl      PaFromVa

        mov     r10, r0                         ; (r10) = ptr to FirstPT (physical)

;       Zero out page tables & kernel data page

        mov     r0, #0                          ; (r0-r3) = 0's to store
        mov     r1, #0
        mov     r2, #0
        mov     r3, #0
        mov     r4, r10                         ; (r4) = first address to clear
        add     r5, r10, #KDEnd-PTs             ; (r5) = last address + 1
18      stmia   r4!, {r0-r3}
        stmia   r4!, {r0-r3}
        cmp     r4, r5
        blo     %B18


;       Setup 2nd level page table to map the high memory area which contains the
; first level page table, 2nd level page tables, kernel data page, etc.

        add     r4, r10, #HighPT-PTs            ; (r4) = ptr to high page table
        orr     r0, r10, #0x051                 ; (r0) = PTE for 64K, kr/w kr/w r/o r/o page, uncached unbuffered
        str     r0, [r4, #0xD0*4]               ; store the entry into 8 consecutive slots
        str     r0, [r4, #0xD1*4]
        str     r0, [r4, #0xD2*4]
        str     r0, [r4, #0xD3*4]
        add     r8, r10, #ExceptionVectors-PTs  ; (r8) = ptr to vector page
        bl      OEMARMCacheMode                 ; places C and B bit values in r0 as set by OEM
        mov     r2, r0
        orr     r0, r8, #0x002                  ; construct the PTE
        orr     r0, r0, r2
        str     r0, [r4, #0xF0*4]               ; store entry for exception vectors
        orr     r0, r0, #0x500                  ; (r0) = PTE for 4k r/o r/o kr/w kr/w C+B page
        str     r0, [r4, #0xF4*4]               ; store entry for abort stack
        str     r0, [r4, #0xF6*4]               ; store entry for FIQ stack  (access permissions overlap for abort and FIQ stacks, same 1k)
        orr     r0, r8, #0x042
        orr     r0, r0, r2                      ; (r0)= PTE for 4K r/o kr/w r/o r/o (C+B as set by OEM)
        str     r0, [r4, #0xF2*4]               ; store entry for interrupt stack
        add     r9, r10, #KPage-PTs             ; (r9) = ptr to kdata page
        orr     r0, r9, #0x002
        orr     r0, r0, r2                      ; (r0)=PTE for 4K (C+B as set by OEM)
        orr     r0, r0, #0x250                  ; (r0) = set perms kr/w kr/w kr/w+ur/o r/o
        str     r0, [r4, #0xFC*4]               ; store entry for kernel data page
        orr     r0, r4, #0x001                  ; (r0) = 1st level PTE for high memory section
        add     r1, r10, #0x4000
        str     r0, [r1, #-4]                   ; store PTE in last slot of 1st level table
  IF {FALSE}
        mov     r0, r4
        mov     r1, #256                        ; dump 256 words
        CALL    WriteHex
  ENDIF

;       Fill in first level page table entries to create "un-mapped" regions
; from the contents of the MemoryMap array.
;
;       (r9) = ptr to KData page
;       (r10) = ptr to 1st level page table
;       (r11) = ptr to MemoryMap array

        add     r10, r10, #0x2000               ; (r10) = ptr to 1st PTE for "unmapped space"
        mov     r7, #2                          ; (r7) = pass counter

        mov     r0, #0x02
        orr     r0, r0, r2                      ; (r0)=PTE for 0: 1MB (C+B as set by OEM)
        orr     r0, r0, #0x400                  ; set kernel r/w permission
20      mov     r1, r11                         ; (r1) = ptr to MemoryMap array


25      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

        cmp     r4, #0                          ; End of table?
        beq     %F29

        ldr     r5, =0x1FF00000
        and     r2, r2, r5                      ; VA needs 512MB, 1MB aligned.

        ldr     r5, =0xFFF00000
        and     r3, r3, r5                      ; PA needs 4GB, 1MB aligned.

        add     r2, r10, r2, LSR #18
        add     r0, r0, r3                      ; (r0) = PTE for next physical page

28      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     %B28                            ; Map next MB

        bic     r0, r0, #0xF0000000             ; Clear Section Base Address Field
        bic     r0, r0, #0x0FF00000             ; Clear Section Base Address Field
        b       %B25                            ; Get next element


29
        bic     r0, r0, #0x0C                   ; clear cachable & bufferable bits in PTE
        add     r10, r10, #0x0800               ; (r10) = ptr to 1st PTE for "unmapped uncached space"
        subs    r7, r7, #1                      ; decrement pass counter
        bne     %B20                            ; go setup PTEs for uncached space if we're not done
        sub     r10, r10, #0x3000               ; (r10) = restore address of 1st level page table
  IF {FALSE}
        mov     r0, r10
        mov     r1, #4096                       ; dump 4096 words
        CALL    WriteHex
  ENDIF
————————————————————————————————————————————

http://www.cnblogs.com/we-hjb/archive/2008/10/12/1309596.html
——这篇博客也讲得比较好,值得参考。

引用 73 楼 Reallyu 的回复:
这个问题也一直弄的不是很清楚,趁着这个机会学习下.据我的理解,OEMAddresstable并不是一个完整的页表,它只把虚拟地址的一部分映射到物理地址.
系统为每一个进程都会维护一份页表,在进程切换的时候把不同的页表基地址传MMU.OEMAddresstable的作用是系统在想访问某个确认的物理地址的时候,可以很容易的找到它的虚拟地址,而不需要关心mmu里现在放的是哪个页表.
 


谢谢Reallyu 老兄,好久不见你。
————————————————
我仔细看了几天,发现这个OEMAddresstable只是用来初始化一级页表,就是所谓的段(section)描述,每个段是1MB,分为4096个段,总共4G——虚拟内存空间4G就是由此而来。
并且这个OEMAddresstable可以用在查表法中用来转换虚拟地址、物理地址(相互转换都可以),这个在上面的代码已经体现出来了。在memory.c也有C语言用它来实现虚拟地址、物理地址之间的相互转换。
并且,这个一级页表可以用来存储二级页表的目录,看上面的图就知道是怎么实现的,在armtrap.s中就使用了这种方法。
——————
对于一些操作系统所谓的64K/4K分页等,我还是不是很明白,估计现在还没有哪能力强制吞下去。armtrap.s使用了一些莫名奇妙的宏指令,给看代码造成很大麻烦,现在再看看。也许就是一步之遥了。哈哈。Come on!

——————————————————————————————————————————————

MACRO
        mtc15   $cpureg, $cp15reg
        mcr     p15,0,$cpureg,$cp15reg,c0,0
        MEND
        MACRO
        mfc15   $cpureg, $cp15reg
        mrc     p15,0,$cpureg,$cp15reg,c0,0
        MEND
; The page tables and exception vectors are setup. Initialize the MMU and turn it on.
;页表和异常向量表建立之后,初始化MMU并打开

        mov     r1, #1
        mtc15   r1, c3                          ; Setup access to domain 0 and clear other
                                                ;  domains including 15 for PSL calls (see above)
        mtc15   r10, c2                         ;;mtc15 是宏定义 把TTB(L1转换基地址放到C2)

        mov     r0, #0
        mcr     p15, 0, r0, c8, c7, 0           ; Flush the I&D TLBs

        mfc15   r1, c1                          ;mfc15 宏定义 读出C1的值到r1
        orr     r1, r1, #0x007F ; changed to read-mod-write for ARM920 Enable: MMU, Align, DCache, WriteBuffer
        orr     r1, r1, #0x3200                 ; vector adjust, ICache, ROM protection
        ldr     r0, VirtualStart                ;这个很关键在代码中有  VirtualStart DCD VStart
        cmp     r0, #0                          ; make sure no stall on "mov pc,r0" below
        mtc15   r1, c1                          ; enable the MMU & Caches
        mov     pc, r0                          ;  & jump to new virtual address 跳到VStart运行
        nop

 

总结:填写好二级页表,把二级页表的首地址存放到一级页表的表项最后最后;根据OEMAddresstable初始化L1页表;把L1的转换表的基地址放到协处理器的c1的寄存器。启动MMU等功能即可。
——现在基本没有什么疑问了,完成博客,今天晚上回去结贴。——目前对于一些domain、AP的设置等还是不是很明白。不过都是一个样,看看数据手册即可。阿门!大功告成。


引用 77 楼 iwillbeback008 的回复:
总结:填写好二级页表,把二级页表的首地址存放到一级页表的表项最后最后;根据OEMAddresstable初始化L1页表;把L1的转换表的基地址放到协处理器的c1的寄存器。启动MMU等功能即可。
------
那本《Windows CE工程实践完全解析》中有讲到"二级页表的首地址存放到一级页表的表项最后"!
在第32、第61~第62页
 


噢噢,my god!这本书我上一周购买了,我看是六点0的,就没有看。准备储藏到明年才看。
噢噢,害我看了好几天啊。不过这样也好,锻炼一下,塞翁失马,焉知非福。

转载请标明:作者wogoyixikexie@gliet.桂林电子科技大学一系科协,原文地址:http://blog.csdn.net/gooogleman——如有错误,希望能够留言指出;如果你有更加好的方法,也请在博客后面留言,我会感激你的批评和分享


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/gooogleman/archive/2009/01/04/3705173.aspx

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值