zoukankan      html  css  js  c++  java
  • ARM中MMU地址转换理解

    首先,我们要分清ARM CPU上的三个地址:虚拟地址(VA,Virtual Address)、变换后的虚拟地址(MVA,Modified Virtual Address)、物理地址(PA,Physical Address)

        启动MMU后,CPU核对外发出虚拟地址VA,VA被转换为MVA供MMU使用,在这里MVA被转换为PA;最后通过PA读写实际设备 

        MMU的作用就是负责虚拟地址(virtual address)转化成物理地址(physical address)。 32位的CPU的虚拟地址空间达到4GB,在一级页表中使用4096个描述符来表示这4GB的空间,每个描述符代表1M的虚拟地址要么存储了它的对应物理地址的起始地址,要么存储了下一级页表的地址。使用MVA[31:20]来索引一级页表(4096个描述符)(因为全用MVA的高12位来索引,因此大小为 2^12 = 4096)

        由协处理器CP15中的寄存器C2(高18位,即[31:14]为转换表基地址,低14位为0)一级转换表基地址,指向2^14=16KB整除的存储器即16K对齐,这个存储区称为一级转换表;MVA的高12位,即位[31:20]作为一级转换表的地址索引,因此一级转换表具有2^12=4096项,每一项的地址为32位,最高的18位[31:14]为寄存器C2的高18位,中间12位为MVA的高12位[31:20],最低2位为0b00。每一项的内容称为一个描述符,在段(Section)下,一级描述符的高12位为大小为1MB的段基地址,段内地址(偏移地址)为MVA的低20位,即段内每个存储器的地址是这样组成高12位为一级描述符的高12位,低20位MVA的低20位。这样,借助于寄存器C2和一级描述符,将一个MVA转换成一个PA。(在这里一定要注意:MVA的高12位是用来索引4096个项的,然后使用项的内容(即描述符)的高12位为段的高12位,类似于指针里面存放地址,4096项类似指针,描述符类似指针里面的内容

         虚拟地址(注意:是一个确定的地址,不是一个空间)被MMU分成2个部分,第一部分是4096页号索引(descriptor index)即用选择4096(2^12)个号中的某个页号,比喻description index为768,页号768中保存的是物理地址的某个页框的起始地址(0x300),第二部分则是相对于section base(0x300)为起始地址空间为1M的偏移量(offset)(如下图)例如: 假设现在执行指令MOV REG, 0x30100013,虚拟地址的二进制码为00110000 00010000 00000000 00010011,前12位是Descriptor Index  = 2^9+2^8+1 = 769,找到769项对应的内容0x301,偏移量为0000 00000000 00010011=13,那么段地址为0x3000000D。

    5.地址转换的总体流程:
         第一阶段:
              (1)从虚拟地址取出前面的31-20位,作为索引。
              (2)根据索引在translation table(一级页表)中找到相应的表项。
              (3)根据表项最低两位的值决定第二阶段的转换方式。
                        00:转换无效
                        01:粗页转换
                        10:段转换
                        11:细页转换
              (4)linux系统一般用细页转换,也有一定的处理器和操作系统用段转换,很少用粗页转换。。


    6.关于TTB(translation table base)
    (1)translation table存放在内存中。
    (2)由程序员制造,故程序员知道其基地址(TTB)。
    (3)程序员将TTB写入cp15的c2寄存器(TTB寄存器)。
    (4)MMU工作的时候从c2寄存器去到TTB,从而找到translation table,进而利用虚拟地址的31-20位可以在该表中找到相应的表项,开始虚拟地址到物理地址的转换。。

    范例代码:

    #define GPKCON (volatile unsigned long*)0xA0008820
    #define GPKDAT (volatile unsigned long*)0xA0008824
    
    /* 
     * 用于段描述符的一些宏定义
     */ 
    #define MMU_FULL_ACCESS     (3 << 10)   /* 段的访问权限 AP*/
    #define MMU_DOMAIN          (0 << 5)    /* 属于哪个域 */
    #define MMU_SPECIAL         (1 << 4)    /* 必须是1 */
    #define MMU_CACHEABLE       (1 << 3)    /* cacheable 快速访问*/
    #define MMU_BUFFERABLE      (1 << 2)    /* bufferable 缓冲区 */
    #define MMU_SECTION         (2)         /* 表示这是段描述符 */
    #define MMU_SECDESC         (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_SECTION)
    #define MMU_SECDESC_WB      (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)
    
    
    void create_page_table(void) 
    /*
    1. 建立页表
    2. 写入TTB (cp15 c2)
    3. 使能MMU 
    */
    {
        unsigned long *ttb = (unsigned long *)0x50000000;
        unsigned long vaddr, paddr;
    
        vaddr = 0xA0000000;
        paddr = 0x7f000000;
        *(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC;
    
        vaddr = 0x50000000; /* 映射内存 */
        paddr = 0x50000000;
        while (vaddr < 0x54000000)
        {
            *(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC_WB;
            vaddr += 0x100000;  /* 每一个表项只能映射1M */
            paddr += 0x100000;
        }
        
    }
    
    
    void mmu_init()
    {
       __asm__(
        
        /*设置TTB 写入cp15的c2中*/
        "ldr    r0, =0x50000000
    "                  
        "mcr    p15, 0, r0, c2, c0, 0
    "    
        
        /*不进行权限检查 域的访问权限取决于cp15的c3寄存器*/
        "mvn    r0, #0
    "                   
        "mcr    p15, 0, r0, c3, c0, 0
    "    
        
        
       /*使能MMU*/
        "mrc    p15, 0, r0, c1, c0, 0
    "    
        "orr    r0, r0, #0x0001
    "          
        "mcr    p15, 0, r0, c1, c0, 0
    "    
        : 
        : 
      );
    }
    
    int gboot_main()
    {
        create_page_table();
        mmu_init();
        
        *(GPKCON) = 0x1111;
        *(GPKDAT) = 0xe;
        
        return 0;    
    }
  • 相关阅读:
    qt程序编译错误:could not exec ‘/usr/lib/x86_64-linux-gnu/qt4/bin/qmake’
    安装 yaml-cpp,MP4V2
    安装cmake 和 opencv 4.0.0
    windows系统,boost编译安装
    messageQ 消息队列
    fflush 和 fsync 的区别
    开源一个 PDF 小工具集软件【使用 PDFium 库实现】
    封装 libjpeg 库
    纯 C++ 代码实现的 INI 文件读写类
    C++11 —— 使用 thread 实现线程池
  • 原文地址:https://www.cnblogs.com/chd-zhangbo/p/5284966.html
Copyright © 2011-2022 走看看