SylixOS中ARM架構的MMU實現分析
1.1 快表(TLB)與頁表
在虛擬頁式存儲管理中設置了快表(TLB),用於保存正在運行進程頁表的子集,通常快表存放在高速緩沖存儲器(Cache)中。
而頁表存放在內存中,並通過特殊功能寄存器(TTB)等告知系統頁表存儲在內存中的基址。
1.2 一級頁表格式
1.2.1 一級頁表描述符地址轉換
ARM的一級頁表的描述符中的地址區域可以指向一個段描述符的地址,也可以指向二級頁表的地址。
1.2.2 一級頁表描述符內容
SylixOS中采用了Coarse page table(粗粒度二級頁表),即一級頁表的描述符中的地址區域指向的是二級頁表的基址。
- Bits[1:0]:表示描述符的類型,0b01表示是一個粗粒度頁表描述符。
- Bits[4:2]:未定義,為0。
- Bits[8:5]:頁表描述符所指定的頁的域屬性。
- Bit[9]:未定義。
- Bits[31:10]:指向二級頁表訪問的基址,需1KB對齊。
1.3 二級頁表格式
1.3.1 使用粗粒度二級頁表的轉換過程
1) 通過“轉換表基址”與“虛擬地址”,得到一級頁表描述符的地址;
2) 即可以得到一級描述符;
3) 將一級描述符中的“二級頁表基址部分”與“虛擬地址的[19:12]位”合並,得到二級頁表描述符的地址;
4) 即可以得到二級描述符。
1.3.2 二級頁表描述符內容
SylixOS使用了Extended small page,即每個頁以4KB為對齊。
- Bits[1:0]:表示描述符的類型,0b1Xn表示是一個擴展的小頁表描述符。
- Bits[3:2]:可緩沖標誌位。
- Bits[5:4]:訪問權限位。
- Bits[31:12]:小頁的頁面基地址,4KB對齊。
1.4 虛擬地址到物理地址的轉換過程
1) 通過“轉換表基址”與“虛擬地址”,得到一級頁表描述符的地址,即可得到一級描述符;
2) 將一級描述符中的“二級頁表基址部分”與“虛擬地址的[19:12]位”合並,得到二級頁表描述符的地址,即可得到二級描述符。
3) 將二級描述符中的“頁基址”與“虛擬地址的[11:0]位”合並,得到轉換後的物理地址。
#2. ARM架構實現的MMU接口
系統接口 | ARM架構接口 | 功能描述 |
---|---|---|
__VMM_MMU_MEM_INIT | armMmuMemInit | 初始化MMU頁表內存 |
__VMM_MMU_GLOBAL_INIT | armMmuGlobalInit | 體系相關的關鍵數據初始化 |
__VMM_MMU_PGD_ALLOC | armMmuPgdAlloc | 分配一級頁表描述符 |
__VMM_MMU_PGD_OFFSET | armMmuPgdOffset | 獲取一級頁表描述符地址 |
__VMM_MMU_PTE_ALLOC | armMmuPteAlloc | 分配二級頁表描述符 |
__VMM_MMU_MAKE_TRANS | armMmuMakeTrans | 設置頁面映射關系 |
__VMM_MMU_INV_TLB | armMmuInvTLB | 無效快表操作 |
__VMM_MMU_MAKE_CURCTX | armMmuMakeCurCtx | 設置MMU當前上下文 |
2.1 初始化MMU頁表內存
在SylixOS中,一級頁表全局公有,這樣在進程切換的過程中無需切換頁表,保證了實時性。
armMmuMemInit函數的功能有如下幾點:
1、 開辟一級頁表內存,默認的一級頁表大小為16KB,頁表數量為1個;
2、 開辟二級頁表內存,默認的二級頁表大小為1KB,頁表數量為2048個;
3、 開辟的內存都采用定長分區的管理方式,根據對應頁表數量進行定長分區初始化。
2.2 體系相關的關鍵數據初始化
armMmuGlobalInit函數內根據ARM架構的特殊性,對其定義的相關特殊功能寄存器進行了設置,如:
1、 快表置無效;
2、 設置域屬性。
此函數對應不同平臺時,需要設置的內容項會不同。
2.3 一級頁表相關操作
2.3.1 分配一級頁表描述符
static LW_PGD_TRANSENTRY *armMmuPgdAlloc (PLW_MMU_CONTEXT pmmuctx,
addr_t ulAddr)
{
REGISTER LW_PGD_TRANSENTRY *p_pgdentry =
(LW_PGD_TRANSENTRY *)API_PartitionGet(_G_hPGDPartition);
if (!p_pgdentry) {
return (LW_NULL);
}
lib_bzero(p_pgdentry, PGD_BLOCK_SIZE); /* 新的 PGD 無有效的頁表項 */
/* 獲取一級頁表描述符的地址 */
p_pgdentry = (LW_PGD_TRANSENTRY *)((addr_t)p_pgdentry
| ((ulAddr >> LW_CFG_VMM_PGD_SHIFT) << 2));
return (p_pgdentry);
}
此函數在初始化的過程中調用,對其操作的分析:
1、 獲取一級頁表對應的定長分區基址,之後將一級頁表內所有內容置空;
2、 當ulAddr為0時,返回的是一級頁表的基地址;否則返回的是對應的一級頁表描述符的地址,其地址轉換的規則如圖 1.1所示。
2.3.2 獲取一級頁表描述符地址
static LW_PGD_TRANSENTRY *armMmuPgdOffset (PLW_MMU_CONTEXT pmmuctx,
addr_t ulAddr)
{
REGISTER LW_PGD_TRANSENTRY *p_pgdentry = pmmuctx->MMUCTX_pgdEntry;
p_pgdentry = (LW_PGD_TRANSENTRY *)((addr_t)p_pgdentry
| ((ulAddr >> LW_CFG_VMM_PGD_SHIFT) << 2)); /* 獲得一級頁表描述符地址 */
return (p_pgdentry);
}
此函數返回的是一級頁表描述符的地址,其地址轉換的規則如圖 1.1所示。
2.3.3 構建一級頁表描述符
static LW_PGD_TRANSENTRY armMmuBuildPgdesc (UINT32 uiBaseAddr,
UINT8 ucAP,
UINT8 ucAP2,
UINT8 ucDomain,
UINT8 ucCB,
UINT8 ucTEX,
UINT8 ucXN,
UINT8 ucType)
{
LW_PGD_TRANSENTRY uiDescriptor;
switch (ucType) {
case COARSE_TBASE: /* 指向二級頁表粗粒度描述符 */
uiDescriptor = (uiBaseAddr & 0xFFFFFC00)
| (ucDomain << 5)
| (VMSA_NS << 3)
| ucType;
break;
……
default:
uiDescriptor = 0; /* 訪問失效 */
break;
}
return (uiDescriptor);
}
armMmuBuildPgdesc函數按照格式構建了一級頁表描述符的內容,其為指向一個二級頁表的粗粒度描述符。
2.4 二級頁表相關操作
2.4.1 分配二級頁表描述符
static LW_PTE_TRANSENTRY *armMmuPteAlloc (PLW_MMU_CONTEXT pmmuctx,
LW_PMD_TRANSENTRY *p_pmdentry,
addr_t ulAddr)
{
#if LW_CFG_CACHE_EN > 0
INTREG iregInterLevel;
#endif /* LW_CFG_CACHE_EN > 0 */
UINT8 ucAP; /* 存儲權限 */
UINT8 ucDomain; /* 域 */
UINT8 ucCB; /* CACHE 與緩沖區控制 */
UINT8 ucAP2; /* 存儲權限 */
UINT8 ucTEX; /* CACHE 與緩沖區控制 */
UINT8 ucXN; /* 永不執行位 */
LW_PTE_TRANSENTRY *p_pteentry =
(LW_PTE_TRANSENTRY *)API_PartitionGet(_G_hPTEPartition);
if (!p_pteentry) {
return (LW_NULL);
}
lib_bzero(p_pteentry, PTE_BLOCK_SIZE);
armMmuFlags2Attr(LW_VMM_FLAG_VALID |
LW_VMM_FLAG_ACCESS |
LW_VMM_FLAG_GUARDED |
LW_VMM_FLAG_WRITABLE|
LW_VMM_FLAG_EXECABLE,
&ucAP, &ucAP2,
&ucDomain,
&ucCB, &ucTEX,
&ucXN);
/* 設置一級頁表描述符的內容,即設置二級頁面基地址 */
*p_pmdentry = (LW_PMD_TRANSENTRY)armMmuBuildPgdesc((UINT32)p_pteentry,
ucAP, ucAP2,
ucDomain,
ucCB, ucTEX,
ucXN,
COARSE_TBASE);
#if LW_CFG_CACHE_EN > 0
iregInterLevel = KN_INT_DISABLE();
armDCacheFlush((PVOID)p_pmdentry, (PVOID)p_pmdentry, 32);/*第三個參數無影響*/
KN_INT_ENABLE(iregInterLevel);
#endif /* LW_CFG_CACHE_EN > 0 */
return (armMmuPteOffset(p_pmdentry, ulAddr));
}
此函數操作的分析:
- armMmuFlags2Attr函數的作用是將SylixOS使用的MMU標誌按照ARM架構進行對應轉換。
- 調用armMmuBuildPgdesc構建一級頁表描述符的內容。
- 調用armMmuPteOffset獲取二級頁表描述符地址。
2.4.2 獲取二級頁表描述符地址
static LW_PTE_TRANSENTRY *armMmuPteOffset (LW_PMD_TRANSENTRY *p_pmdentry,
addr_t ulAddr)
{
REGISTER LW_PTE_TRANSENTRY *p_pteentry;
REGISTER UINT32 uiTemp;
uiTemp = (UINT32)(*p_pmdentry); /* 獲得二級頁表描述符 */
/* 二級頁表基地址為一級描述符的【31-10】位 */
p_pteentry = (LW_PTE_TRANSENTRY *)(uiTemp & (~(LW_CFG_KB_SIZE - 1)));
/* 獲得粗粒度二級頁表基地址 */
/* 二級頁表描述符地址為 二級頁表基址 + 虛擬地址的【19-12】位,見1.3.1 */
p_pteentry = (LW_PTE_TRANSENTRY *)((addr_t)p_pteentry
| ((ulAddr >> 10) & 0x3FC)); /* 獲得對應虛擬地址頁表描述符 */
return (p_pteentry);
}
2.4.3 構建二級頁表描述符
static LW_PTE_TRANSENTRY armMmuBuildPtentry (UINT32 uiBaseAddr,
UINT8 ucAP,
UINT8 ucAP2,
UINT8 ucDomain,
UINT8 ucCB,
UINT8 ucTEX,
UINT8 ucXN,
UINT8 ucType)
{
LW_PTE_TRANSENTRY uiDescriptor;
switch (ucType) {
case SMALLPAGE_DESC: /* 小頁描述符 */
uiDescriptor = (uiBaseAddr & 0xFFFFF000)
| (VMSA_nG << 11)
| (VMSA_S << 10)
| (ucAP2 << 9)
| (ucTEX << 6)
| (ucAP << 4)
| (ucCB << 2)
| (ucXN << 0)
| ucType;
break;
default:
uiDescriptor = 0; /* 訪問失效 */
break;
}
return (uiDescriptor);
}
2.5 其他MMU的操作接口
2.5.1 設置MMU當前上下文
static VOID armMmuMakeCurCtx (PLW_MMU_CONTEXT pmmuctx)
{
REGISTER LW_PGD_TRANSENTRY *p_pgdentry;
if (LW_NCPUS > 1) {
/*
* Set location of level 1 page table
* ------------------------------------
* 31:14 - Base addr
* 13:7 - 0x0
* 6 - IRGN[0] 0x1 (Normal memory,
* Inner Write-Back Write-Allocate Cacheable)
* 5 - NOS 0x0 (Outer Shareable)
* 4:3 - RGN 0x1 (Normal memory,
* Outer Write-Back Write-Allocate Cacheable)
* 2 - IMP 0x0
* 1 - S 0x1 (Shareable)
* 0 - IRGN[1] 0x0 (Normal memory,
* Inner Write-Back Write-Allocate Cacheable)
*/
p_pgdentry = (LW_PGD_TRANSENTRY *)((ULONG)pmmuctx->MMUCTX_pgdEntry
| (1 << 6)
| ((!VMSA_S) << 5)
| (1 << 3)
| (0 << 2)
| (VMSA_S << 1)
| (0 << 0));
} else {
/*
* Set location of level 1 page table
* ------------------------------------
* 31:14 - Base addr
* 13:7 - 0x0
* 5 - NOS 0x1 (Outer NonShareable)
* 4:3 - RGN 0x1 (Normal memory,
* Outer Write-Back Write-Allocate Cacheable)
* 2 - IMP 0x0
* 1 - S 0x0 (NonShareable)
* 0 - C 0x1 (Inner cacheable)
*/
p_pgdentry = (LW_PGD_TRANSENTRY *)((ULONG)pmmuctx->MMUCTX_pgdEntry
| ((!VMSA_S) << 5)
| (1 << 3)
| (0 << 2)
| (VMSA_S << 1)
| (1 << 0));
}
armMmuSetTTBase(p_pgdentry);
armMmuSetTTBase1(p_pgdentry);
}
TTBR0、TTBR1的格式如下圖所示。
TTBR0的格式:
TTBR1的格式:
SylixOS中ARM架構的MMU實現分析