# Arm Programmer's Guide III - Processor Modes and Registers 學習筆記
# 1. 前言
此筆記為學習 [ARM® Cortex™-A Series Programmer's Guide
Version: 4.0 中第三章 ARM Architecture and Processors ](https://developer.arm.com/documentation/den0013/d/ARM-Processor-Modes-and-Registers?lang=en) 的心得筆記。主旨在解釋 Armv7 Aarch32 下的核心模式 (Processor Mode) 和寄存器 (Register)。另外此為此學習筆記系列的第一篇,原文的第一篇 [Introducing](https://developer.arm.com/documentation/den0013/d/Introduction?lang=en) 和第二篇 [Arm Architecture and Processors](https://developer.arm.com/documentation/den0013/d/ARM-Architecture-and-Processors?lang=en) 不會有相關的學習筆記。
# 2. Processor Mode
Armv7 核心總共有九種模式、三層特權階級 (PL, Privilege Level),然後又分為正常 (Normal 或 Non-Secure) 和安全 (Secure) 世界 (World)。他們的關係大致如下圖:

## 2.1 Normal / Secure World
上圖藍色區域為 Non-Secure (或稱 Normal) World、而黃色區域為 Secure World。Arm 的 TrustZone Scurity Extension 技術將所有的軟硬體資源分成 Non-Secure 和 Secure 兩類,當核心處於 Non-Secure 狀態下,他便無法存取任何的 Secure 軟硬體資源。所以可以將一些較機密或敏感的資料放在 Secure World 中 (像手機裡的指紋辨識相關資料或技術),然後將和一般使用者互動的作業系統以及應用程式放在 Non-Secure World 中,這樣即使惡意軟體或駭客害入了作業系統,他也沒有權限去觸碰到 Secure World 中受保護的資料。
兩個世界依靠上圖中最底層的 Secure Monitor 來做切換。
## 2.2 特權階級 (Privilege Level)
兩個世界又各自被特權階級分割。特權數字越大表是擁有越多的特權,能存取越多的資源。舉例來說,無論是 Normal 還是 Secure World 的 PL0 都無法存取系統相關的資源 (因此特權階級 0 其實是沒有特權的),像是 MMU (Memory Management Unit)、Cache 等等資源的存取與操作都必須在 PL1 (或更高的特權階級中) 中才有辦法進行。
所以通常來說,如上圖所示,作業系統的範圍應該會包含 PL0 和 PL1,其中 PL0 是用來跑最上層應用程式 (如 Linux 的 User space);PL1 則是用來跑作業系統的核心 (如 Linux 的 Kernel space)。又如果有實作虛擬化 (Virtulization Extension),那麼虛擬機會跑在各作業系統之下的 PL2 位置 (只有 Normal World 有這個階級,所以只有 Normal World 支援虛擬技術)。
另外,特權階級對於核心來說是一個概念,並沒有一個寄存器或者特定的 BIT 去指定核心的特權階級。能夠被指定的是核心模式,而不同的核心模式固定屬於特定的特權階級 (其對應關係可以參考上圖)。
## 2.3 核心模式
核心模式和特權階級息息相關,但相對於特權階級所表達的擁有特權多寡的概念,核心模式的區分更是偏向功能性的。舉例來說,FIQ 模式就是為了處理 FIQ 異常、而 MON 模式則是為了 Normal / Secure World 的切換。
| Mode | Encoding | 說明 |
| ---------------------------- | -------- | -------- |
| USR (User) | 10000 | 應用程式所在的無特權階級。 |
| FIQ (Fast Interrupt Request) | 10001 | 由 FIQ 中斷異常進入。 |
| IRQ (Interrupt Request) | 10010 | 由 IRQ 中斷異常進入。 |
| SVC (Supervisor) | 10011 | Reset 後或者在指令 SVC (Supervisor Call instruction) 執行後進入。應用程式通常透過 SVC 來像核心索取特權服務或資源。|
| MON (Monitor) | 10110 | Security Extension 實作的一部分,負責 Normal 和 Secure World 之間的切換,也是 Normal World 進到 Secure World 的進入點。 |
| ABT (Abort) | 10111 | 由記憶體存取異常進入。 |
| HYP (Hypervisor) | 11010 | Virtualization Extention 實作的一部分,用來虛擬化 PL1 中的多個客作業系統。|
| UND (Undefined) | 11011 | 由執行未定義指令異常進入。|
| SYS (System) | 11111 | 基本上就是有特權的 USR 模式,和 USR 共用所有寄存器。為內核平常的運行模式。|
若想改變核心的模式,除了透過異常之外,可以直接改寫 CPSR (Current Program Status Register) 裡的 BIT M 來達到。而各模式對應的值則為上表的 Encoding 值。這會在稍後的 [3.2.1 CPSR](https://hackmd.io/@uMqav-XESBCsrtZEx_Jpug/Arm_Programmer_Guide_Processor_Modes_and_Registers_Study_note#321-CPSR) 章節再次提到。
# 3. 寄存器 (Registers)
寄存器是 CPU 內部的儲存硬體,對他們的存取速度會遠大於對於外部記憶體的存取速度。他們有的用來存放運算資料供 CPU 計算、有的則是存放跟核心有關的功能資訊。
## 3.1 通用寄存器
ARM 架構提供 16 個 32 bit 的通用寄存器 (R0 ~ R15) 給軟體使用。
* R0 ~ R12 沒有特殊意義,可以用作一般目的的資料存放。
* R13 又稱為 SP (Stack Pointer),除了可以用作一般目的的資料存放外,也會指向 Stack 的頂點位置。舉例來說,當執行 Push 指令將資料推進 Stack 時,資料實際上是被推進 R13 (或 SP) 指向的位置。
* R14 又稱為 LR (Line Register),一樣也可以用作一般目的的資料存放,但同時也會被用來存放返回位置。舉例來說,當執行 BL 指令跳轉去其他位置執行時, R14 (或 LR) 的值會自動被改寫成 BL 指令的下一行。
* R15 又稱為 PC (Program Counter),沒有辦法用作一般目的的資料存放。它的值表示核心要執行的位置,一般來說他會依序遞增,讓核心能依序執行所有程式內容,如果修改他,那麼核心就會打斷原本的執行流程,轉而跳往新的 PC 值位置去執行。
雖然說 ARM 提供了 16 個通用寄存器,但是對於不同的核心模式,他們的同編號寄存器有可能實際對應到的是不同的硬體空間,這種情況我們稱為 Banking。下圖中的藍灰色寄存器都是 Banked 的,也是該寄存器實際上是有別於 User 寄存器的。舉裡來說,SYS 模式下的所有通用寄存器都和 User 是共用的,但是 FIQ 模式下只有 R0 ~ R7 是和 User 共用,FIQ 模式擁有自己的 R8 ~ R15。

## 3.2 Program Status Registers
上圖中最下面一排有顯示 CPSR、APSR 和 SPSR。他們分別代表 Current / Application / Saved Program Status Register。
### 3.2.1 CPSR
CPSR 是一個特殊功能的寄存器,用來記錄、控制核心的狀態和設定,其格式如下:

* **BIT N (Negative)**: 當進行 ALU (Arithmetic logic Unit) 運算 (也就是加減乘除、AND、OR、NOT、XOR 或位移這些運算) 且結果為負時, BIT N 會舉起為 1。
* **BIT Z (Zero)**: 當進行 ALU 運算且結果為 0 時,BIT Z 會舉起為 1。
* **BIT C (Carry out)**: 當進行 ALU 運算且若將其值當成無符號 (Unsigned) 類型,結果發生進位時,BIT C 會舉起為 1。舉例來說,```ADD R0, R1, #1``` 其中 R1 為 0xFFFFFFFF,R1 和 1 相加會超過 32 bit 的最大值發生進位 (Carry),導致 BIT C 舉起為 1。
* **BIT V (Overflow)**: 當進行 ALU 運算且若將其值當成有符號 (Signed) 類型,結果發生益位時,BIT V 會舉起為 1。舉例來說,```ADD R0, R1, #1``` , 其中 R1 為 0x7FFFFFFF、為有符號類型的最大值 2147483647,和 1 相加後會發生溢位,導致 R0 值為 0x80000000、有符號情況下為 -2147483648,這時 BIT V 舉起為 1 (但因為沒有發生進位,所以 BIT C 不會舉起)。
* **BIT Q (Cumulative)**: 當進行飽和 (Saturation) 運算且達到飽和值,BIT Q 會被舉起為 1。飽和運算會將結果的值限制在最大和最小值之間,若結果會超過,則維持最大或最小值。舉例來說,```UADD R0, R1, #1``` , 其中 UADD 表示無符號的飽和加法、R1 為 0xFFFFFFFF,其加上 1 會超過無符號的上限,所以飽和加發會把它限制在最大值,最終 R0 為 0xFFFFFFFF,然後 BIT Q 會被舉起為 1。
* **BIT J (Jazelle)**: 控制核心是否跑在 Jazelle 模式。Jazelle 是一種硬體加速技術,可以提升 Arm 執行 Java 的速度、並減少對 JVM (Java Virtual Machine) 的依賴。但由於現今的 JVM 效能已經獲得大幅優化,Jazelle 已逐漸被淘汰。
* **BIT GE (Greater than or Equal)**: 紀錄「非 NEON」 SIMD (Single Instruction Multiple Data) 指令的結果。 SIMD 是一種併行運算技術,允許單個指令同時對多個數據進行運算,以提升效率。舉例來說:
```
MOV R0, #0x11223344
MOV R1, #0x55667788
// 將 GE 設成 0b1100
MRS R3, CPSR
ORR R3, R3, #(0b1010 << 16)
MSR CPSR_c, R3
SEL R2, R0, R1
```
SEL (Select based on GE flags) 會將兩個 32 bit 操作數 (Operand) R0 和 R1 切成 4 個 8 bit 資料,並根據 CPSR 的 GE Bit 在兩個操作數中選擇其中一邊,將其存入目的寄存器 R2。也就是說,當 GE 為 1 時,SEL 會選 R0、相反則選 R1。最後因為上例中 GE 為 0b1010,所以最高八 bit 會來自 R0、BIT[23:16] 會來自 R1、BIT[15:8] 會來自 R0,最後最低 8 bit 會來自 R1,由此得到結果 R2 的值會是 0x11663388。
* **BIT IT (If-Then)**: 總共有 7 個 bit (但上圖可以看到,七個 bit 並不連續),用來控制 IT 等 if-then-else 指令。 IT 指令就是 Arm 指令上的加速版 If-then,直接舉例來說,如果你想要達到這樣的邏輯:
```clike=
if(R0 == R1)
{
R3 = R4 + R5;
R3 = R3 >> 1;
}
else
{
R3 = R6 + R7;
R3 = R3 >> 1;
}
```
那麼可以因為 if-else 中的指令都很少,就可以利用 IT 來實作:
```clike=
CMP R0, R1
ITTEE // TT (ThenThen) 表示接下來兩個指令是在前面判斷成立執行、EE (ElseElse) 表示後兩條指令是在前面判斷為否下執行
ADDEQ R3, R4, R5 // EQ 表示相等時做
ASREQ R4, R3, #1 // SR 表示 Shift Right,EQ 表示相等時做
ADDNE R3, R6, R7 // NE 表示不相等時做
ASRNE R3, R3, #1 // SR 表示 Shift Right,NQ 表示不相等時做
```
(參考來源: https://blog.csdn.net/whealther/article/details/43194761)
* **BIT E (Endianness)**: 設定讀寫 (Load/Store) 記憶體的端序。
* **BIT A (Abort)**: 控制是否屏蔽非同步中止異常 (Asynchronous Aborts)。更多 Abort 的細節可以參考:[ Arm Programmer's Guide X - Memory Ordering 學習筆記](https://hackmd.io/@uMqav-XESBCsrtZEx_Jpug/Arm_Programmer_Guide_Exception_Handling_Study_Note)。
* **BIT I (IRQ)**: 控制是否屏蔽 IRQ (Interrupt Request) 中斷異常。
* **BIT F (FIQ)**: 控制是否屏蔽 FIQ (Fast Interrupt Request) 中斷異常。
* **BIT T (FIQ)**: 控制核心是否跑在 Thumb 模式。Thumb 模式讓指令長度縮小到 16 bit,能提供更小的尺寸和更高的效能。
* **BIT M (Mode)**: 控制核心跑在哪個模式下 (值可以參考 [2.3 章節](https://hackmd.io/@uMqav-XESBCsrtZEx_Jpug/Arm_Programmer_Guide_Processor_Modes_and_Registers_Study_note#23-%E6%A0%B8%E5%BF%83%E6%A8%A1%E5%BC%8F)表格中的 Encoding 欄位)。
### 3.2.2 APSR
APSR (Application Program Status Register) 是功能受限版本的 CPSR。由 [3.1 通用寄存器](https://hackmd.io/@uMqav-XESBCsrtZEx_Jpug/Arm_Programmer_Guide_Processor_Modes_and_Registers_Study_note#31-%E9%80%9A%E7%94%A8%E5%AF%84%E5%AD%98%E5%99%A8) 最後圖片的左下角可以看出,APSR 就是無特權模式 User mode 所使用的 CPSR。 User mode 只能修改 APSR 旗標 (也就是 BIT NZCVQ) 還有 BIT GE 而已,其他所有的像是 IRQ、FIQ 的屏蔽都是沒有辦法在 User mode 下修改的。
### 3.2.3 SPSR
每一種異常模式 (User 和 SYS 以外的所有模式) 都有一個 SPSR (Saved Program Status Register),用來記錄前一個模式的 CPSR。舉例來說,若 SYS 模式因為 IRQ 中斷而跳去 IRQ 模式的話,那麼 SPSR_irq 就會等於異常發生前的 CPSR_sys。
# 4. Coprocessor 15
15 號協處理器 CP15 為系統控制協處理器 (System Control coprocessor),顧名思義是用來提供還多的核心功能控制。表面上它提供了 c0 ~ c15 16 個 32 bit 的寄存器來做設定,但其實同樣的 cN 可以搭配不同的 Opcode ,來達到更多的控制組合。在舉實際例子之前,先了解一下怎麼讀寫協處理器,讀寫的格式如下:
```clike=
MRC p15, Op1, Rt, CRn, CRm, Op2 // Read a CP15 register into an Arm register
MCR p15, Op1, Rt, CRn, CRm, Op2 // Write a CP15 register into an Arm register
```
* CRn 為想互動的協處理暫存器「組」。
* Rt 為拿來讀或寫的 Arm 暫存器。
* Op1 和 Op2 用來從 CRn 的暫存器組中選定暫存器。
* CRm 根據協處理器的定義,可以做為額外的附加訊息用。若用不到則設為 c0 就好 (大部分時候都這樣)。
舉例來說: (可參考 [TTBR0](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/TTBR0--Translation-Table-Base-Register-0?lang=en)、[TTBR1](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/TTBR1--Translation-Table-Base-Register-1?lang=en) 或[第九章學習筆記的 4.1 First Level Translation Table](https://hackmd.io/@uMqav-XESBCsrtZEx_Jpug/Arm_Programmer_Guide_Memory_Management_Unit_Studying_Note#41-First-Level-Translation-Table))
```clike=
// Read TTBR0 from CP15 C2, then save it in r0.
MRC p15, 0, r0, c2, c0, 0
// Write r1 to CP15 C2's TTBR1
MCR p15, 0, r1, c2, c0, 1
```
以下為完整的 CP15 C0 ~ C15 資訊:
<table>
<tr>
<th>Register</th>
<th>Description</th>
</tr>
<tr>
<th colspan='2'>CP15 c0</th>
</tr>
<tr>
<td>Main ID Register (MIDR)</td>
<td>提供核心資訊 (包含 Part number 和 Revision)</td>
</tr>
<tr>
<td>Multiprocessor Affinity Register (MPIDR)</td>
<td>用來區分同一叢 (Cluster) 中的不同核心 (processor)</td>
</tr>
<tr>
<th colspan='2'>CP15 c2/c3, 記憶體保護和控制寄存器 (參考 <a href="https://hackmd.io/@uMqav-XESBCsrtZEx_Jpug/Arm_Programmer_Guide_Memory_Management_Unit_Studying_Note#41-First-Level-Translation-Table"> MMU 學習筆記</a>)</th>
</tr>
<tr>
<td>Translation Table Base Register 0 (TTBR0)</td>
<td>Base address of level 1 translation table</td>
</tr>
<tr>
<td>Translation Table Base Register 1 (TTBR1)</td>
<td>Base address of level 1 translation table</td>
</tr>
<tr>
<td>Translation Table Base Control Register (TTBCR)</td>
<td>Controls use of TTB0 and TTB1 </td>
</tr>
<tr>
<th colspan='2'>CP15 c5/c6, memory system fault registers (參考 <a href="https://hackmd.io/@uMqav-XESBCsrtZEx_Jpug/Arm_Programmer_Guide_Exception_Handling_Study_Note#71-Abort-Handler"> Exception Handling 學習筆記第 7.1 章</a>)</th>
</tr>
<tr>
<td>Data Fault Status Register (DFSR)</td>
<td>Gives status information about the last data fault</td>
</tr>
<tr>
<td>Instruction Fault Status Register (IFSR)</td>
<td>Gives status information about the last instruction fault</td>
</tr>
<tr>
<td>Data Fault Address Register (DFAR)</td>
<td>Gives the virtual address of the access that caused the most recent precise data abort </td>
</tr>
<tr>
<td>Instruction Fault Address Register (IFAR)</td>
<td>Gives the virtual address of the access that caused the most recent precise prefetch abort</td>
</tr>
<tr>
<th colspan='2'>CP15 c7, cache maintenance and other functions (參考 <a href="https://hackmd.io/@uMqav-XESBCsrtZEx_Jpug/Arm_Programmer_Guide_Caches_Study_note#10-Invalidating-and-cleaning-cache-memory"> Caches 學習筆記</a>)</th>
</tr>
<tr>
<td>Cache and branch predictor maintenance functions</td>
<td>控制快取和預讀指令的功能</td>
</tr>
<tr>
<td>Data and instruction barrier operations</td>
<td>控制記憶體屏障的功能</td>
</tr>
<tr>
<th colspan='2'>CP15 c8, TLB maintenance operations</th>
</tr>
<tr>
<th colspan='2'>CP15 c9, performance monitors</th>
</tr>
<tr>
<th colspan='2'>CP15 c12, Security Extensions registers (參考 <a href="https://hackmd.io/@uMqav-XESBCsrtZEx_Jpug/Arm_Programmer_Guide_Exception_Handling_Study_Note#34-Exception-generating-instructions"> Exception Handling 學習筆記第 3.4 章</a>)</th>
</tr>
<tr>
<td>Vector Base Address Register (VBAR)</td>
<td>Provides the exception base address for exceptions that are not handled in Monitor mode.</td>
</tr>
<tr>
<td>Monitor Vector Base Address Register (MVBAR)</td>
<td>Holds the exception base address for all exceptions that are taken to Monitor mode.</td>
</tr>
<tr>
<th colspan='2'>CP15 c13, process, context and thread ID registers</th>
</tr>
<tr>
<td>Context ID Register (CONTEXTIDR)</td>
<td></td>
</tr>
<tr>
<td>Software thread ID registers</td>
<td></td>
</tr>
<tr>
<th colspan='2'>CP15 c15, implementation defined registers</th>
</tr>
<tr>
<td>Configuration Base Address Register (CBAR)</td>
<td>Provides a base address for the GIC and Local timer type peripherals.</td>
</tr>
</table>
## 4.1 SCTLR (System Control Register)
SCTLR 是 CP15 的 C1 寄存器。在系統控制上扮演重要的角色。以下為其格式:

* **BIT TE (Thumb Exception enable)**: 控制異常 (Exception) 處理是否跑在 Thumb 模式下。
* **BIT NMFI (Non-Maskable FIQ)**: 當 NMFI 被開啟,那麼核心會在每次 Reset 去檢查一根特定的 Input Pin,並確定是否啟用 NMFI,也就是讓 FIQ 無法被屏蔽的功能。
* **BIT EE (Exception Endianness)**: 控制異常處理時的 Endianness。
* **BIT U (Aligment Model)**: 是否支援 Armv6 後引入的新對齊存取模式,也就是 Lagacy 或 ARMv6+ 模式:
* Lagacy 對齊模式: 資料存取的位址一定要是對齊的,否則雖然不會發生 Data Abort 異常,但會發生不可預期的結果。
* ARMv6+ 對齊模式: 資料存取的位址可以是未對齊的。
* **BIT A (Aligment Check)**: 如果 BIT A 為 1,則不管 U 的值為多少,未對齊的存取都會造成 Data Abort。
* **BIT FI (FIQ Configuration)**: 只有在 Secure Extension 有被實作的情況下有用。當 BIT FI 為 1,則 FIQ 專屬於 Secure World (也就是 FIQ 的異常處理永遠都在 Secure World 中進行,即使 FIQ 是在 Normal World 中發起),且也只有 Secure 可以控制 FIQ (Normal World 沒有辦法修改 CPSR.F 來屏蔽 FIQ)。
* **BIT V (Vector table)**: 控制異常向量表 (Exception Vector Table) 的位置。當 BIT V 是 0,異常向量表的位置由特定的暫存器控制 (一般來說是 VBAR, 但 HYP 模式用 HVBAR 、 MON 模式用 MVBAR);如果 BIT V 是 1,則不再使用 VBAR ,改將 Exception Vector 固定放在 0xFFFF0000 (稱為 HIVECS) 的位置。
* **BIT I (Instruction Cache)**: 控制是否要快取指令。如果 BIT I 為 0,則指令永遠都會跳過快取 (Cache),直接從主記憶體讀取。
* **BIT Z (Branch Prediction)**: 控制是否要啟用分支預設,也就是讓處理器去預測未來的分支走向,並提前加載指令以提升效率。
* **BIT C (Cache Enable)**: 控制是否要啟用快取。
* **BIT M (MMU Enable)**: 控制是否使用 MMU,若沒有開啟,則沒有辦法使用虛擬體地址。
關於 SCTLR 更完整的內容請見: [SCTLR, System Control Register](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/SCTLR--System-Control-Register?lang=en)。
---
下一篇: [Arm Programmer's Guide VI - Floating-Point 學習筆記](https://hackmd.io/@uMqav-XESBCsrtZEx_Jpug/Arm_Programmer_Guide_Floating_Point_Study_note)