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

电子说

1.3w人已加入

描述

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 汇编代码

在 archarmarmincludearm.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 }

 



审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分