摘要:本系列首先了解下ARM CP15協處理器的知識,接着介紹下協處理器相關的匯編指令,最后分析下MMU相關匯編代碼。
本文分享自華為雲社區《鴻蒙輕內核A核源碼分析系列六 MMU協處理器》,作者:zhushy。
1、 ARM C15 協處理器
在ARM嵌入式應用系統中, 很多系統控制由ARM CP15協處理器來完成的。CP15協處理器包含編號0-15的16個32位的寄存器。例如,ARM處理器使用C15協處理器的寄存器來控制cache、TCM(Tightly-Coupled Memory)和存儲器管理。CP15的各個寄存器的概要信息如下圖,圖片來自官方資料《ARM® Cortex™-A Series Version: 4.0 Programmer’s Guide》。
在這些C15寄存器中和MMU關系較大的有C2、C7、C17寄存器,這些寄存器的作用,從上圖可以看出,分別是:
- CP15 C2寄存器
Memory protection and control registers,內存保護和控制寄存器,包含Translation Table Base Register 0 (TTBR0)、Translation Table Base Register 1 (TTBR1)和Translation Table Base Control Register (TTBCR)。TTBR0、TTBR1是L1轉換頁表的基地址,TTCR控制TTBR0和TTBR1的使用。
- CP15 C7寄存器
Cache and branch predictor maintenance functions、Data and instruction barrier operations用於高速緩存和寫緩存控制。
- CP15 C13寄存器
Context ID Register (CONTEXTIDR)、Software thread ID registers用於保存進程標識符(asid地址空間編號)。
2、ARM C15 協處理器匯編指令
訪問CP15寄存器的指令主要是MCR和MRC這兩個指令。本小節詳細介紹下這2個匯編指令。先看下指令的含義,MCR是ARM處理器寄存器到協處理器寄存器的數據傳送指令,英文為Move CPU register to coprocessor register,MRC是協處理器寄存器到ARM處理器寄存器的數據傳送指令,英文為Move from coprocessor register to CPU register。這2個指令的語義格式如下,可以看出語義格式是一樣的,但是讀取寫入含義會有差異。MCR是讀取Rt寄存器寫入協處理器寄存器CRn、CRm,而MRC是讀取協處理器寄存器CRn、CRm寫入Rt寄存器。
MCR{cond} coproc, #opcode1, Rt, CRn, CRm{, #opcode2}
MRC{cond} coproc, #opcode1, Rt, CRn, CRm{, #opcode2}
MCR詳細的語義介紹如下:
Syntax MCR{cond} coproc, #opcode1, Rt, CRn, CRm{, #opcode2} where: cond is an optional condition code. 可選的條件碼。 coproc is the name of the coprocessor the instruction is for. The standard name is pn, where n is an integer in the range 0 to 15.協處理器的名稱,標准名稱為pn,其中n為0-15,例如p14、p15。 opcode1 is a 3-bit coprocessor-specific opcode. 3位的操作碼。 opcode2 is an optional 3-bit coprocessor-specific opcode.可選的3位操作碼。 Rt is an ARM source register. Rt must not be PC. 要讀取的ARM寄存器,不能為PC寄存器。 CRn, CRm are coprocessor registers.要寫入的協處理器寄存器。
MRC詳細的語義介紹如下:
Syntax MRC{cond} coproc, #opcode1, Rt, CRn, CRm{, #opcode2} where: cond is an optional condition code. 可選的條件碼。 coproc is the name of the coprocessor the instruction is for. The standard name is pn, where n is an integer in the range 0 to 15.協處理器的名稱,標准名稱為pn,其中n為0-15,例如p14、p15。 opcode1 is a 3-bit coprocessor-specific opcode.3位的操作碼。 opcode2 is an optional 3-bit coprocessor-specific opcode.可選的3位操作碼 Rt is the ARM destination register. Rt must not be PC.要寫入的ARM寄存器,不能為PC寄存器。 Rt can be APSR_nzcv. This means that the coprocessor executes an instruction that changes the value of the condition flags in the APSR. Rt也可以為APSR_nzcv。 CRn, CRm are coprocessor registers.要讀取的協處理器寄存器。
3、MMU匯編代碼
在arch\arm\arm\include\arm.h文件中,封裝了CP15協處理器相關的寄存器操作匯編函數。我們主要看下MMU相關的部分。
3.1 CP15 C2 TTBR轉換表基地址寄存器
代碼比較簡單,結合下圖,自行查看即可。該圖來自《ARM Cortex-A9 Technical Reference Manual r4p1》CP15 system control registers grouped by CRn order部分。
STATIC INLINE UINT32 OsArmReadTtbr(VOID) { UINT32 val; __asm__ volatile("mrc p15, 0, %0, c2,c0,0" : "=r"(val)); return val; } STATIC INLINE VOID OsArmWriteTtbr(UINT32 val) { __asm__ volatile("mcr p15, 0, %0, c2,c0,0" ::"r"(val)); __asm__ volatile("isb" ::: "memory"); } STATIC INLINE UINT32 OsArmReadTtbr0(VOID) { UINT32 val; __asm__ volatile("mrc p15, 0, %0, c2,c0,0" : "=r"(val)); return val; } STATIC INLINE VOID OsArmWriteTtbr0(UINT32 val) { __asm__ volatile("mcr p15, 0, %0, c2,c0,0" ::"r"(val)); __asm__ volatile("isb" ::: "memory"); } STATIC INLINE UINT32 OsArmReadTtbr1(VOID) { UINT32 val; __asm__ volatile("mrc p15, 0, %0, c2,c0,1" : "=r"(val)); return val; } STATIC INLINE VOID OsArmWriteTtbr1(UINT32 val) { __asm__ volatile("mcr p15, 0, %0, c2,c0,1" ::"r"(val)); __asm__ volatile("isb" ::: "memory"); } STATIC INLINE UINT32 OsArmReadTtbcr(VOID) { UINT32 val; __asm__ volatile("mrc p15, 0, %0, c2,c0,2" : "=r"(val)); return val; } STATIC INLINE VOID OsArmWriteTtbcr(UINT32 val) { __asm__ volatile("mcr p15, 0, %0, c2,c0,2" ::"r"(val)); __asm__ volatile("isb" ::: "memory"); }
3.2 CP15 C7 高速緩存寄存器
代碼比較簡單,結合下圖,自行查看即可。該圖是C7寄存器的部分截圖。
STATIC INLINE UINT32 OsArmReadBpiall(VOID) { UINT32 val; __asm__ volatile("mrc p15, 0, %0, c7,c5,6" : "=r"(val)); return val; } STATIC INLINE VOID OsArmWriteBpiall(UINT32 val) { __asm__ volatile("mcr p15, 0, %0, c7,c5,6" ::"r"(val)); __asm__ volatile("isb" ::: "memory"); } STATIC INLINE UINT32 OsArmReadBpiallis(VOID) { UINT32 val; __asm__ volatile("mrc p15, 0, %0, c7,c1,6" : "=r"(val)); return val; } STATIC INLINE VOID OsArmWriteBpiallis(UINT32 val) { __asm__ volatile("mcr p15, 0, %0, c7,c1,6" ::"r"(val)); __asm__ volatile("isb" ::: "memory"); }
3.3 CP15 C13 進程標識符寄存器
代碼比較簡單,結合下圖,自行查看即可。
STATIC INLINE UINT32 OsArmReadContextidr(VOID) { UINT32 val; __asm__ volatile("mrc p15, 0, %0, c13,c0,1" : "=r"(val)); return val; } STATIC INLINE VOID OsArmWriteContextidr(UINT32 val) { __asm__ volatile("mcr p15, 0, %0, c13,c0,1" ::"r"(val)); __asm__ volatile("isb" ::: "memory"); }
4 MMU上下文切換
在之前的系列,我們了解到每個用戶進程都有獨立的進程空間。在進程切換時,MMU上下文也會切換,相應的函數為LOS_ArchMmuContextSwitch()。快速分析下該函數的代碼。
⑴處讀取TTBCR寄存器的狀態值,如果傳入參數archMmu不為空,執行⑵使能TTBR0,否則執行⑶使其失能TTBR0。⑷處把內核地址空間的進程空間標識符asid寫入C13寄存器。⑸處更新TTB頁表基地址和TTB狀態信息到相應寄存器。⑹處把進程空間的進程標識符寫入C13寄存器。
VOID LOS_ArchMmuContextSwitch(LosArchMmu *archMmu) { UINT32 ttbr; ⑴ UINT32 ttbcr = OsArmReadTtbcr(); if (archMmu) { ⑵ ttbr = MMU_TTBRx_FLAGS | (archMmu->physTtb); /* enable TTBR0 */ ttbcr &= ~MMU_DESCRIPTOR_TTBCR_PD0; } else { ⑶ ttbr = 0; /* disable TTBR0 */ ttbcr |= MMU_DESCRIPTOR_TTBCR_PD0; } #ifdef LOSCFG_KERNEL_VM /* from armv7a arm B3.10.4, we should do synchronization changes of ASID and TTBR. */ ⑷ OsArmWriteContextidr(LOS_GetKVmSpace()->archMmu.asid); ISB; #endif ⑸ OsArmWriteTtbr0(ttbr); ISB; OsArmWriteTtbcr(ttbcr); ISB; #ifdef LOSCFG_KERNEL_VM if (archMmu) { ⑹ OsArmWriteContextidr(archMmu->asid); ISB; } #endif }
小結
本文介紹了ARM CP15協處理器的知識,接着介紹下協處理器相關的匯編指令,最后分析下MMU相關匯編代碼。感謝閱讀,有什么問題,請留言。