1

鸿蒙轻内核源码分析:MMU协处理器

 2 years ago
source link: https://my.oschina.net/u/4526289/blog/5369974
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

摘要:本系列首先了解下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》。

v2-33f083459fc1e3ebd4b345bd59b919d0_720w.jpg

v2-3a818882a1ff259a13bdb87054f4e1b7_720w.jpg

v2-bb856b3baaefba5f405bf479c12e8934_720w.jpg

在这些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部分。

v2-8692873ce852e55efc5a70f2ec0c6581_720w.jpg

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寄存器的部分截图。

v2-4a62e9ebe5b73cb3d1d3f67098bfb6ea_720w.jpg

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 进程标识符寄存器

代码比较简单,结合下图,自行查看即可。

v2-ec39aa893538640cc41cf3c6cddb33b7_720w.jpg

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相关汇编代码。感谢阅读,有什么问题,请留言。

点击关注,第一时间了解华为云新鲜技术~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK