uboot step 11 內存一下子變大了 ----MMU的配置與使用
本文結構如下:
MMU 介紹
MMU 相關寄存器
MMU 內存訪問權限控制
MMU 使能
-
MMU 頁表描述
- 一級頁表描述符地址
- 一級頁表描述符格式
- 二級頁表描述符格式
- 虛擬地址到物理地址轉化 (重要)
- 1M段式
- 16M段式
- 64KB大頁
- 4KB小頁
代碼實現:使用虛擬地址去控制LED的例子
參考
http://www.it165.net/embed/html/201408/2633.html
http://comm.chinaaet.com/adi/blogdetail/40052.html
在前面初始化的過程中,由于MMU與Cache會影響我們進行初始化的工作,因此關閉了MMU與Cache,在那篇文章中已經說過了MMU的作用,這里再重新說下:
- 1.完成虛擬地址到物理地址的轉換
- 2.對內存的訪問權限進行控制
首先還是先來簡要說下相關與MMU相關的寄存器,先有個大概印象,后面用到了再去詳細的看:
名稱 | 描述 | 簡寫 |
---|---|---|
Translation Table Base Register 0 | 保存一級轉換表的物理地址 | TTBR0 |
Translation Table Base Register 1 | 保存一級轉換表的物理地址 | TTBR1 |
Translation Table Base Control Register | 一級轉換表控制相關 | TTBCR |
Domain Access Control Register | 域訪問權限控制 | |
C1 Control Register | MMU和cache使能 |
MMU使能
- 1.編程所有相關的CP15寄存器正確的值
- 2.編程建立所要求的一級頁表,二級頁表
- 3.關閉I-cache
- 4.使能MMU
MMU 內存訪問權限控制
-
Domains 域控制
一個域被聯系到一些內存區域,在頁表描述符中有個Domain域來標識此區域內存屬于哪一個域Domian來控制,ARM1176 支持16個Domains,域訪問權限控制是由C3來設定的,如下圖:
c3.png
每個域由兩位來控制,32位寄存器,總共16個域
- 00 無權限,任何訪問都會產生domain fault
- 01 Client 檢查TLB頁表描述符的權限位是否允許訪問
- 10 保留,產生domain fault
- 11 Manager 不檢查訪問權限,不會產生權限錯誤
-
訪問權限控制
在域控制中,提到會檢查TLB頁表描述符的權限位,這個權限位就是APX,AP位,在頁表描述符中有這幾個域
ap.png -
可執行區域XN位
XN.png- 如果含有可執行區域,XN=0
- XN=1時,在此內存區域任何試圖去執行指令的行為會產生權限錯誤
MMU 頁表描述
為了支持段和頁的映射方式,MMU使用兩級頁表描述符,一級頁表描述符決定訪問的是一個分段還是一個分頁式的表,如果訪問的是一個分頁式的頁表,處理器MMU決定頁表類型是大頁還是小頁并找到二級頁表。
一級頁表描述符地址
ARM1176 包含 兩個轉換表基地址寄存器TTBR0和TTBR1,一個轉換表基地址控制寄存器,當一個TLB未命中時,虛擬地址最高位決定使用的基地址寄存器是哪一個,采用兩個轉換表基地址期望去減少OS上下文切換的花費,每個獨立的任務或進程,有他自己的頁表而不用消耗大量內存。整個虛擬內存空間被分為兩個部分,用戶空間和內核空間
0x0 -> 1<<(32-N) that TTBR0 controls
1<<(32-N) -> 4GB that TTBR1 controls.
N的值在TTBCR寄存器中進行設定,N的大小決定了內核空間和用戶空間的分界線,當N=0時,表示只使用了TTBR0寄存器
一級頁表描述符格式分析
如上圖所示:
- bit[1:0]: 映射類型,分段式還是分頁式。
- 00 忽略
- 11 無效,返回Translation fault
- 10 分段式
- 01 分頁式
- nG : 0 轉換表被標記為全局的; 1 轉換表屬于特定進程
- S : 共享位,0 非共享; 1 共享內存
- XN: 0 包含可執行代碼; 1 不包含可執行代碼
- APX,AP位: 權限訪問控制位
- Doman : 域標識,屬于哪個域
- P: ECC校驗
- TEX,C,B: 此區域是否采用緩沖buffer,cache,還是直接訪問,一般外設采用無緩沖,內存采用緩沖方式(個人理解)
- NS:No-Secure 屬性
二級頁表描述符格式分析
如上圖所示,和一級頁表描述符格式相似,相應的位的功能是一樣的。不同的是最后兩位代表了兩種不同的分頁類型。
- bit[1:0] : 01 時,采用粗粒度大頁64K進行映射 1X時,采用小(細)頁4K進行映射
虛擬地址到物理地址的轉化
MMU對于剛接觸的人可能會感覺到有些不知所措,因為它有那么多相關的寄存器,還有什么TLB,一級頁表,二級頁表,粗細粒度,等等,完全一個大寫的懵,認為MMU的使用是個很難的過程,當你從了解地址轉化流程開始入手學習,慢慢了解了之后,便會覺得其實還是蠻簡單的,下面來看下S3c6410中MMU的地址轉化流程:
- TLB:Translation Lookaside Buffer,可以稱為快表,當要進行虛擬地址到物理地址轉化的時候,MMU便會去查詢這個表來確定虛擬地址所對應的物理地址是多少,而這個表也被我們保存在了內存的某個地址,這個地址稱為轉換表基地址,我們需要將這個基地址寫到轉換表基地址寄存器中。
下面分四種情況對映射轉化過程作下說明:
- 分段式映射,大小1M
- 分段式映射,大小16M
- 分頁式映射,大小64KB 粗頁
- 分頁式映射,大小4KB 細頁
1M分段式映射
- 頁表基地址(TTBRx寄存器中)18bits[31:14]+虛擬地址12bits[31:20] +2[00] 構成了一級頁表描述符的地址
- 取得了一級頁表描述符的地址,訪問這個地址從中可以得到真實的物理段基地址[31:20]
- 將得到的物理段基地址[31:20]加上虛擬地址中的[19:0]位偏移地址構成了真正的物理地址
每個虛擬地址可以索引 2^12 個一級描述符地址,每個一級描述符可以包含 2^20 個物理地址,總共可以索引4G空間
16M分段映射
與1M的段映射相似,只是有些地址線的范圍發生了改變,另外一級描述符的第18位為1表示16M的段映射,0表示1M段映射
64KB分頁映射
- 頁表基地址+虛擬地址的[31:20]位得到了一級描述符的地址
- 一級描述符的[31:10]位為二級描述符的基地址
- 二級描述符的基地址+虛擬地址的[19:16]位,最后幾位補0構成了二級頁表描述符的地址
- 二級頁表描述符的第[31:16]位為物理基地址+虛擬地址的[15:0]頁內偏移地址構成了真正的物理地址
注:上圖映射是從內核手冊中截取的,有些錯誤,二級頁表描述符的Second-level tabel index 應該為96位,50位應為0
4KB分頁映射
- 頁表基地址+虛擬地址的[31:20]位得到了一級描述符的地址
- 一級描述符的[31:10]位為二級描述符的基地址
- 二級描述符的基地址+虛擬地址的[19:12]位,最后幾位補0構成了二級頁表描述符的地址
- 二級頁表描述符的第[31:12]位為物理基地址+虛擬地址的[11:0]頁內偏移地址構成了真正的物理地址
代碼實現-控制led
#define GPKCON (volatile unsigned long*)0xA0008800
#define GPKDAT (volatile unsigned long*)0xA0008808
/*
* 用于段描述符的一些宏定義
*/
#define MMU_FULL_ACCESS (3 << 10) // 訪問權限 AP
#define MMU_DOMAIN (0 << 5) // 屬于哪個域 Domain
#define MMU_SPECIAL (1 << 4) // 必須是1 XN
#define MMU_CACHEABLE (1 << 3) // cacheable C
#define MMU_BUFFERABLE (1 << 2) // bufferable B
#define MMU_SECTION (2) // 表示這是段描述符 10
#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)
{
unsigned long *ttb = (unsigned long *)0x50000000;
unsigned long vaddr, paddr;
vaddr = 0xA0000000;
paddr = 0x7f000000;
*(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC; //將虛擬地址A000映射到led所對應的物理地址區間
vaddr = 0x50000000;
paddr = 0x50000000;
while (vaddr < 0x54000000)//64M的區間地址虛擬地址與物理地址映射相同, 或者說相當于沒有進行地址轉化
{
*(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC_WB;
vaddr += 0x100000;
paddr += 0x100000;
}
}
void mmu_init()
{
__asm__(
/*設置TTB Base addr*/
"ldr r0, =0x50000000\n"
"mcr p15, 0, r0, c2, c0, 0\n"
/*不進行權限檢查 Domain*/
"mvn r0, #0\n"
"mcr p15, 0, r0, c3, c0, 0\n"
/*使能MMU*/
"mrc p15, 0, r0, c1, c0, 0\n"
"orr r0, r0, #0x0001\n"
"mcr p15, 0, r0, c1, c0, 0\n"
:
:
);
}
int main()
{
create_page_table();
mmu_init();
*(GPKCON) = 0x11110000;
*(GPKDAT) = 0xa0;
return 0;
}
此去經年
zhaiyk@sina.cn
August 10, 2016