天天看點

ARM記憶體管理單元--MMU

前言

我們在學習4412的時候,我們原本的中斷位址已經被IROM和IRAM占據了,并且不允許被修改,是以我們隻能去選取高端位址0xffff 0000高端位址,但是這一片位址屬于虛拟位址,是以我們需要開啟MMU

MMU定義

MMU功能,(memory management unit) 記憶體管理單元,MMU是硬體的記憶體管理器件,使用硬體方式對記憶體進行映射管理。

将實體記憶體 0X4000 0000 - 0X8000 0000,的某些指定位址,映射到虛拟記憶體位址上。 虛拟記憶體位址總計大小 0X0000 0000 – 0X FFFF FFFF 總計4GB ,他包含了0xffff 0000這位址,我們就可以去開啟異常,其中異常包含着硬體最重要的中斷

同樣的,MMU的存在可以讓計算機多出很多的空間,他要配合協處理器寄存器等方式來實作,是以我們需要使用到内聯彙編

ARM記憶體管理單元--MMU

他是一種多對一的方式,虛拟位址很多存儲在相同的實體空間中,它通過走表的方式獲得實體記憶體,下面我們詳細介紹他的走表過程

MMU的走表過程

ARM記憶體管理單元--MMU

上圖就是完整的走表過程,他經過非常精細的設計,下面我們來介紹一下他這個過程

  1. Input address,先擷取一個虛拟位址,然後去掉後面的20位,隻留前面的12位,
  2. First-level descriptor address,将他和從C2寄存器中取出來的28位拼在一起,并且在後面加上00代替,表示使用的部分流的方式,剛才虛拟位址的前12位就被當作了偏移量
  3. First-level Section descriptor,通過圖中Translation flow for a Section找到了對應的位址,
  4. Output address,這時候取他的前16位就是他的真實實體位址

協處理器來查表

協助核心工作的協處理了,他的内部有16個寄存器,cp0–cp15,他的名字是c1-c15,其中我們要用到的是c1,c2,c3,起始表項寫到c2,c1是用來使能mmu,c是用來設定使能記憶體的權限

extern void enable_mmu(u32 *ttb)
{
    __asm__ __volatile__(
        "nop\n\t"
        //将TTB基位址寫入C2
        "mcr p15, 0, %[ttb], c2, c0, 0\n\t"

        //設定C3的記憶體通路權限為最大權限,全部設為11
        "mvn r0, #0\n\t"
        "mcr p15, 0, r0, c3, c0, 0\n\t"
        //設定C1,使能MMU,順便設定異常向量表存儲在高端位址
        
        "mrc p15, 0, r0, c1, c0, 0\n\t"
        "orr r0, r0, #(0x1 << 13)\n\t"
        "orr r0, r0, #0x1\n\t"
        "mcr p15, 0, r0, c1, c0, 0\n\t"
        "nop\n\t"
        :
        :[ttb]"r"(ttb)
        :"r0"
    );

}
           

MMU功能的實作

由于頻繁使用,是以我們将走表過程寫成宏函數

/*
    TTB 由C2給定的轉換表的基地之,看成一個數組
    VA 要映射的虛拟位址的基位址, 取高12位,可以配合TTB基位址當成數組尋找表項
    PA 表項中需要指定的實體基位址,管理本位址上1M的實體位址空間的映射
*/
#define CREAT_DESCRIPTOR(TTB, VA, PA) \
    TTB[VA>>20] = ((PA&0xfff0000)|(0x2))
           

同時我們查手冊,我們需要将闆子出場的位址占用,還有真實的實體位址,不能讓他映射,方法就是自己映射自己

memset(ttb, 0x0, 4096*4);

    section_map(ttb, 0x0, 0x0cd00000,0x0);

    section_map(ttb,0x0ce00000,0x14000000,0x0ce00000);

    section_map(ttb, 0x40000000,0x80000000,0x40000000);
           

同時我們應該注意,我們映射的時候使用的是段映射,是以我們的低20位要和映射位址的低20位相同,因為在走表的時候,他們的偏移量是相同的