鴻蒙輕核心原始碼分析:MMU協處理器

語言: CN / TW / HK
摘要:本系列首先了解下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相關彙編程式碼。感謝閱讀,有什麼問題,請留言。

 

點選關注,第一時間瞭解華為雲新鮮技術~