ARM V8R中汇编指令MCR和MRC的详细用法
好的,我们来深入解析 ARMv8-R 架构(通常运行在 AArch32 状态)中 MCR 和 MRC 这两条指令的用法。它们是 ARM 架构中用于与协处理器(Coprocessor)通信的核心指令,是进行系统级配置和控制的钥匙。
🧠 核心概念与概述
在 ARM 架构中,除了主 CPU 核心外,还有许多功能单元:
- 系统控制协处理器 (CP15):这是最重要的一個,用于管理内存系统(MMU、缓存、地址转换)、配置保护单元、以及访问处理器标识和配置寄存器。
- 其他协处理器:如浮点单元(VFP/NEON,曾用 CP10/CP11)、调试单元等。
这些协处理器有自己的一套寄存器,无法通过普通的 STR/LDR 内存访问指令来操作。ARM 架构专门提供了两条指令来访问它们:
-
MCR(Move to Coprocessor from Register)- 功能:将ARM 通用寄存器的值移动到协处理器的寄存器中。
- 类比:
write_coprocessor_register(coproc_reg, arm_reg_value);(C语言)
-
MRC(Move to Register from Coprocessor)- 功能:将协处理器的寄存器的值移动到ARM 通用寄存器中。
- 类比:
arm_reg_value = read_coprocessor_register(coproc_reg);(C语言)
简单总结:MCR 用于写配置,MRC 用于读状态。 它们是在用户代码和系统协处理器之间操作的唯一桥梁。
为了让您快速建立整体概念,我们先通过一个表格总结它们的核心功能:
| 指令 | 全称 | 数据流方向 | 核心功能 | 典型应用 |
|---|---|---|---|---|
MCR |
Move to Coprocessor from Register | ARM -> Coprocessor | 配置协处理器寄存器 | 设置MMU页表地址、使能/禁用缓存、配置内存属性 |
MRC |
Move to Register from Coprocessor | Coprocessor -> ARM | 读取协处理器寄存器状态 | 获取处理器ID、读取缓存配置、获取MMU故障地址 |
⚙️ 语法与操作数格式
MCR 和 MRC 的语法结构是对称的,但数据流向相反。
MCR 指令语法
MCR{<cond>} <coproc>, <opc1>, <Rt>, <CRn>, <CRm>{, <opc2>}
MRC 指令语法
MRC{<cond>} <coproc>, <opc1>, <Rt>, <CRn>, <CRm>{, <opc2>}
{<cond>}:可选的条件码后缀(如EQ,NE)。<coproc>:协处理器编号。这是一个在p0到p15之间的标识符。p15:这是系统控制协处理器 (CP15) 的编号,是使用MCR/MRC最主要的对象。
<opc1>:操作码 1。与opc2一起,用于指定在目标协处理器寄存器上执行的具体操作。<Rt>:- 对于
MCR:是源寄存器,ARM 通用寄存器,其值将被写入协处理器。 - 对于
MRC:是目标寄存器,ARM 通用寄存器,协处理器的值将被读到这里。
- 对于
<CRn>:协处理器寄存器 1。这是协处理器内部的寄存器编号。<CRm>:协处理器寄存器 2。与CRn和opc1/opc2一起,用于精确选择要访问的寄存器。<opc2>:操作码 2。与opc1一起,用于指定在目标协处理器寄存器上执行的具体操作。
关键点:协处理器寄存器的功能是由 <opc1>、<CRn>、<CRm>、<opc2> 这四个参数共同唯一确定的。必须查阅芯片手册才能知道特定组合的含义。
🛠️ 详细用法与示例
由于 CP15 是主要操作对象,以下示例均以 p15 为例。
1. 读取处理器标识符
这是 MRC 的常见用法,用于识别处理器类型和版本。
MRC p15, 0, R0, c0, c0, 0 @ 读取主ID寄存器 (Main ID Register)
@ 现在 R0 中包含处理器的实现者(如ARM)、变体号和版本号。
2. 配置MMU(内存管理单元)
这是 MCR/MRC 最核心的用途之一。
@ 设置TTBR0 (Translation Table Base Register 0) - 写入页表基地址
LDR R0, =page_table_base @ 将页表的物理地址加载到 R0
MCR p15, 0, R0, c2, c0, 0 @ 将 R0 的值写入 TTBR0
@ 使能MMU - 设置SCTLR (System Control Register) 的 M bit
MRC p15, 0, R0, c1, c0, 0 @ 首先读取当前的SCTLR到R0
ORR R0, R0, #(1 << 0) @ 设置第0位 (M bit) 来使能MMU
MCR p15, 0, R0, c1, c0, 0 @ 将修改后的值写回SCTLR
DSB @ 数据同步屏障,确保配置生效
ISB @ 指令同步屏障,清空流水线
3. 配置缓存
@ 使能I-Cache (指令缓存) 和 D-Cache (数据缓存)
MRC p15, 0, R0, c1, c0, 0 @ 读取SCTLR
ORR R0, R0, #(1 << 12) @ 设置第12位 (I bit) 使能I-Cache
ORR R0, R0, #(1 << 2) @ 设置第2位 (C bit) 使能D-Cache
MCR p15, 0, R0, c1, c0, 0 @ 写回SCTLR
4. 读取异常状态信息
当发生数据中止或预取中止时,可以通过 MRC 读取相关寄存器来诊断问题。
MRC p15, 0, R0, c5, c0, 0 @ 读取数据故障状态寄存器 (DFSR)
MRC p15, 0, R1, c6, c0, 0 @ 读取数据故障地址寄存器 (DFAR)
@ 现在 R0 中包含故障状态(如访问类型、权限错误),R1 中包含出错的地址。
⚠️ 重要注意事项与原理
-
特权级别 (Privilege):
MCR和MRC是特权指令。通常只能在特权模式(如 Supervisor、IRQ、Abort 等模式,即 EL1 及以上)下执行。- 在用户模式 (User Mode, EL0) 下尝试执行这些指令会触发未定义指令异常。
-
指令屏障的使用:
- 在通过
MCR修改了会影响处理器行为的系统配置(如 MMU、缓存设置)后,必须紧跟屏障指令。 DSB(Data Synchronization Barrier):确保之前的存储操作(包括协处理器配置)对所有后续操作可见。ISB(Instruction Synchronization Barrier):清空处理器流水线,确保后续指令在新的系统配置下被获取和执行。
- 在通过
-
平台依赖性:
MCR/MRC指令的行为高度依赖于具体的 ARM 内核实现(例如 Cortex-R5, Cortex-R8, Cortex-R52 等)。- 协处理器寄存器
CRn/CRm和操作码opc1/opc2的含义因内核而异。 - 绝对权威:最权威的解释必须来自你所使用的特定芯片的技术参考手册 (TRM) 和 ARM Architecture Reference Manual for ARMv8-R。切勿凭猜测使用这些指令。
-
在 ARMv8-A/AArch64 中的变化:
- 在 ARMv8-A 的 AArch64 执行状态下,
MCR和MRC指令被弃用。 - 取而代之的是,使用
MSR和MRS指令来直接访问系统寄存器,语法更统一和直观(例如MRS X0, SCTLR_EL1、MSR TTBR0_EL1, X0)。
- 在 ARMv8-A 的 AArch64 执行状态下,
💎 总结
MCR 和 MRC 指令是 ARMv7/ARMv8-R (AArch32) 架构中进行系统级控制的底层原语。
它们的核心用途是:
- 系统配置:配置和管理内存系统(MMU、缓存、内存属性)。
- 安全与保护:配置内存保护单元 (MPU),定义内存区域的访问权限。
- 诊断与调试:读取处理器标识、配置信息和异常状态,用于调试和诊断。
使用切记:
- 权限要求:确保在正确的特权模式下执行。
- 查阅文档:操作前必须查阅芯片手册,确认寄存器编号和操作码的正确组合。
- 使用屏障:修改关键配置后使用
DSB和ISB。 - 谨慎操作:错误地配置这些寄存器可能导致系统立即崩溃或产生不可预知的行为。
简单来说:当你需要配置处理器内核最深层的功能(如内存管理、缓存)时,才会动用 MCR 和 MRC 指令。 它们是操作系统、固件和 Bootloader 开发者的工具,普通应用程序通常不会也不需要直接使用它们。
浙公网安备 33010602011771号