|
|
|
@ -4,7 +4,7 @@
|
|
|
|
|
|
|
|
|
|
> 参考:ARM Architecture Reference Manual ARMv8, for ARMv8-A architecture profile, capture D1.1, D1.7, D1.10, D1.11, D1.13, D1.14, D1.16.
|
|
|
|
|
|
|
|
|
|
在 AArch64 中,各种中断被统称为异常(exception),包括:
|
|
|
|
|
在 AArch64 中,各种中断被统称为**异常**(exception),包括:
|
|
|
|
|
|
|
|
|
|
* Reset.
|
|
|
|
|
* Interrupts.
|
|
|
|
@ -13,16 +13,16 @@
|
|
|
|
|
* Supervisor calls (SVCs), Secure Monitor calls (SMCs), and hypervisor calls (HVCs).
|
|
|
|
|
* Debug exceptions.
|
|
|
|
|
|
|
|
|
|
这些异常可分为同步异常(synchronous exception)与异步异常(asynchronous exception)两大类:
|
|
|
|
|
这些异常可分为**同步异常**(synchronous exception)与**异步异常**(asynchronous exception)两大类:
|
|
|
|
|
|
|
|
|
|
* 同步异常:发生在执行一条特定的指令时,包括执行系统调用指令(`svc`、`hvc`)、断点指令(debug exceptions)、Instruction Aborts、Data Aborts 等。
|
|
|
|
|
* 异步异常:发生的时机不确定,又被称为中断(interrupt),是发送给处理机的信号,包括 SError、IRQ、FIQ 等。
|
|
|
|
|
* 异步异常:发生的时机不确定,又被称为**中断**(interrupt),是发送给处理机的信号,包括 SError、IRQ、FIQ 等。
|
|
|
|
|
|
|
|
|
|
### 异常处理
|
|
|
|
|
|
|
|
|
|
当发生异常时,CPU 会根据异常的类型,跳转到特定的地址执行,该地址被称为异常向量(exception vector)。
|
|
|
|
|
当发生异常时,CPU 会根据异常的类型,跳转到特定的地址执行,该地址被称为**异常向量**(exception vector)。
|
|
|
|
|
|
|
|
|
|
不同类型异常的异常向量通过统一的向量基地址寄存器(Vector Base Address Register, VBAR)加上不同的偏移得到。在 EL1、EL2、EL3 下各有一个 VBAR 寄存器 `VBAR_ELx`。此时异常被分为 4 大类,每一类根据异常来源的不同又可分为 4 类,于是共有 16 个异常向量。
|
|
|
|
|
不同类型异常的异常向量通过统一的**向量基地址寄存器**(Vector Base Address Register, VBAR)加上不同的偏移得到。在 EL1、EL2、EL3 下各有一个 VBAR 寄存器 `VBAR_ELx`。此时异常被分为 4 大类,每一类根据异常来源的不同又可分为 4 类,于是共有 16 个异常向量。
|
|
|
|
|
|
|
|
|
|
4 种类型的异常分别为:
|
|
|
|
|
|
|
|
|
@ -47,11 +47,11 @@
|
|
|
|
|
| LowerAArch64 | `0x400` | `0x480` | `0x500` | `0x580` |
|
|
|
|
|
| LowerAArch32 | `0x600` | `0x680` | `0x700` | `0x780` |
|
|
|
|
|
|
|
|
|
|
如果该异常是 Synchronous 或 SError,异常症状寄存器(Exception Syndrome Register, ESR)将被设置,用于记录具体的异常类别 EC (exception class) 与 ISS (Instruction Specific Syndrome)。在 EL1、EL2、EL3 下各有一个 ESR 寄存器 `ESR_ELx`。具体的 EC、ISS 编码见官网文档 ARMv8 Reference Manual D1.10.4 节。
|
|
|
|
|
如果该异常是 Synchronous 或 SError,**异常症状寄存器**(Exception Syndrome Register, ESR)将被设置,用于记录具体的异常类别 EC (exception class) 与 ISS (Instruction Specific Syndrome)。在 EL1、EL2、EL3 下各有一个 ESR 寄存器 `ESR_ELx`。具体的 EC、ISS 编码见官网文档 ARMv8 Reference Manual D1.10.4 节。
|
|
|
|
|
|
|
|
|
|
### 异常屏蔽
|
|
|
|
|
|
|
|
|
|
某些异常可以被屏蔽(mask),即发生时不跳转到相应的异常向量。可被屏蔽的异常包括所有异步异常与调试时的同步异常(debug exceptions),共 4 种,分别由 `DAIF` 4 个位控制:
|
|
|
|
|
某些异常可以被**屏蔽**(mask),即发生时不跳转到相应的异常向量。可被屏蔽的异常包括所有异步异常与调试时的同步异常(debug exceptions),共 4 种,分别由 `DAIF` 4 个位控制:
|
|
|
|
|
|
|
|
|
|
1. `D`: Debug exception
|
|
|
|
|
2. `A`: SError interrupt
|
|
|
|
@ -62,21 +62,21 @@
|
|
|
|
|
|
|
|
|
|
### 异常返回
|
|
|
|
|
|
|
|
|
|
当发生异常时,异常返回地址会被设置,保存在异常链接寄存器(Exception Link Register, ELR) `ELR_ELx` 中。
|
|
|
|
|
当发生异常时,异常返回地址会被设置,保存在**异常链接寄存器**(Exception Link Register, ELR) `ELR_ELx` 中。
|
|
|
|
|
|
|
|
|
|
异常返回使用 `eret` 指令完成。当异常返回时,`PC` 会根据当前特权级被恢复为 `ELR_ELx` 中的,`PSTATE` (process state)也会被恢复为 `SPSR_ELx` 中的。`PSTATE` 是当前进程状态信息的抽象,保存在 `SPSR_ELx` 中,包含条件标志位 `NZCV`、异常屏蔽位 `DAIF`、当前特权级等信息。如果修改了 `SPSR_ELx` 中相应的位并进行异常返回,就能实现异常级别切换、异常开启/屏蔽等功能。
|
|
|
|
|
异常返回使用 **`eret`** 指令完成。当异常返回时,`PC` 会根据当前特权级被恢复为 `ELR_ELx` 中的,**进程状态** `PSTATE` (process state)也会被恢复为 `SPSR_ELx` 中的。`PSTATE` 是当前进程状态信息的抽象,保存在 `SPSR_ELx` 中,包含条件标志位 `NZCV`、异常屏蔽位 `DAIF`、当前特权级等信息。如果修改了 `SPSR_ELx` 中相应的位并进行异常返回,就能实现异常级别切换、异常开启/屏蔽等功能。
|
|
|
|
|
|
|
|
|
|
### 系统调用
|
|
|
|
|
|
|
|
|
|
一般使用 `svc` 指令(supervisor call)完成,将触发一个同步异常。
|
|
|
|
|
一般使用 **`svc`** 指令(supervisor call)完成,将触发一个同步异常。
|
|
|
|
|
|
|
|
|
|
## RustOS 中的实现
|
|
|
|
|
|
|
|
|
|
中断与异常部分的代码主要位于 `kernel/src/arch/aarch64/interrupt/` 中。
|
|
|
|
|
中断与异常部分的代码主要位于模块 [kernel/src/arch/aarch64/interrupt](../../../kernel/src/arch/aarch64/interrupt/) 中。
|
|
|
|
|
|
|
|
|
|
### 异常启用与屏蔽
|
|
|
|
|
|
|
|
|
|
在 `interrupt/mod.rs` 中使用了 `DAIFClr` 与 `DAIFSet` 特殊目的寄存器分别实现了异常的启用与屏蔽(仅针对 IRQ),分别为 `enable()` 与 `disable()` 函数,代码如下:
|
|
|
|
|
在 [interrupt/mod.rs](../../../kernel/src/arch/aarch64/interrupt/mod.rs#L24) 中使用了 `DAIFClr` 与 `DAIFSet` 特殊目的寄存器分别实现了异常的启用与屏蔽(仅针对 IRQ),分别为 `enable()` 与 `disable()` 函数,代码如下:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
/// Enable the interrupt (only IRQ).
|
|
|
|
@ -92,13 +92,13 @@ pub unsafe fn disable() {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
此外,也可在异常返回前通过修改保存的 `SPSR` 寄存器启用或屏蔽异常,详见 `interrupt/context.rs` 中的 `TrapFrame::new_kernel_thread()` 与 `TrapFrame::new_user_thread()` 函数。
|
|
|
|
|
此外,也可在异常返回前通过修改保存的 `SPSR` 寄存器启用或屏蔽异常,详见 [interrupt/context.rs](../../../kernel/src/arch/aarch64/interrupt/context.rs#L26) 中的 `TrapFrame::new_kernel_thread()` 与 `TrapFrame::new_user_thread()` 函数。
|
|
|
|
|
|
|
|
|
|
### 异常向量
|
|
|
|
|
|
|
|
|
|
全局符号 `__vectors` 定义了异常向量基址,并在 `interrupt/mod.rs` 的 `init()` 函数中通过 `msr vbar_el1, x0` 指令,将 `VBAR_EL1` 设为了 `__vectors`。
|
|
|
|
|
全局符号 `__vectors` 定义了异常向量基址,并在 [interrupt/mod.rs](../../../kernel/src/arch/aarch64/interrupt/mod.rs#L13) 的 `init()` 函数中通过 `msr vbar_el1, x0` 指令,将 `VBAR_EL1` 设为了 `__vectors`。
|
|
|
|
|
|
|
|
|
|
16 个异常向量分别通过宏 `HANDLER source kind` 定义在 `interrupt/vector.S` 中,代码如下:
|
|
|
|
|
16 个异常向量分别通过宏 `HANDLER source kind` 定义在 [interrupt/vector.S](../../../kernel/src/arch/aarch64/interrupt/vector.S) 中,代码如下:
|
|
|
|
|
|
|
|
|
|
```armasm
|
|
|
|
|
.macro HANDLER source kind
|
|
|
|
@ -112,7 +112,7 @@ pub unsafe fn disable() {
|
|
|
|
|
|
|
|
|
|
不同的异常向量对应的异常处理例程结构相同,仅有 `source` 和 `kind` 不同。`source` 与 `kind` 将会被合并成一个整数并存到寄存器 `x0` 中,以便作为参数传给 Rust 编写的异常处理函数。
|
|
|
|
|
|
|
|
|
|
由于不同异常向量的间距较少(仅为 `0x80` 字节),所以不在 `HANDLER` 中做细致的处理,而是统一跳转到 `__alltraps` 中进行处理。
|
|
|
|
|
由于不同异常向量的间距较少(仅为 `0x80` 字节),所以不在 `HANDLER` 中做细致的处理,而是统一跳转到 [trap.S](../../../kernel/src/arch/aarch64/interrupt/trap.S#L92) 的 `__alltraps` 中进行处理。
|
|
|
|
|
|
|
|
|
|
### 异常处理
|
|
|
|
|
|
|
|
|
@ -141,7 +141,7 @@ __trapret:
|
|
|
|
|
3. 当该函数返回时,通过宏 `RESTORE_ALL` 从 `TrapFrame` 中恢复各寄存器;
|
|
|
|
|
4. 最后通过 `eret` 指令进行异常返回。
|
|
|
|
|
|
|
|
|
|
`TrapFrame` 定义在 `interrupt/context.rs` 中,结构如下:
|
|
|
|
|
`TrapFrame` 定义在 [interrupt/context.rs](../../../kernel/src/arch/aarch64/interrupt/context.rs#L12)中,结构如下:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
pub struct TrapFrame {
|
|
|
|
@ -159,9 +159,9 @@ pub struct TrapFrame {
|
|
|
|
|
|
|
|
|
|
目前保存的寄存器包括:通用寄存器 `x0~x30`、异常返回地址 `ELR`、栈指针 `SP`、进程状态 `SPSR`。由于在 `aarch64-blog_os.json` 中禁用了 `NEON` 指令,不需要保存 `q0~q31` 这些 SIMD/FP 寄存器。
|
|
|
|
|
|
|
|
|
|
`rust_trap()` 函数定义在 `interrupt/handler.rs` 中。首先判断传入的 `kind`:
|
|
|
|
|
`rust_trap()` 函数定义在 [interrupt/handler.rs](../../../kernel/src/arch/aarch64/interrupt/handler.rs#L43) 中。首先判断传入的 `kind`:
|
|
|
|
|
|
|
|
|
|
* 如果是 `Synchronous`:在 `interrupt/syndrome.rs` 中解析 `ESR`,根据具体的异常类别分别处理断点指令、系统调用、缺页异常等。
|
|
|
|
|
* 如果是 `Synchronous`:在 [interrupt/syndrome.rs](../../../kernel/src/arch/aarch64/interrupt/syndrome.rs) 中解析 `ESR`,根据具体的异常类别分别处理断点指令、系统调用、缺页异常等。
|
|
|
|
|
* 如果是 `IRQ`:调用 `handle_irq()` 函数处理 IRQ。
|
|
|
|
|
* 其他类型的异常(SError interrupt、Debug exception)暂不做处理,直接调用 `crate::trap::error()`。
|
|
|
|
|
|
|
|
|
@ -169,7 +169,7 @@ pub struct TrapFrame {
|
|
|
|
|
|
|
|
|
|
如果 `ESR` 的异常类别是 `SVC`,则说明该异常由系统调用指令 `svc` 触发,紧接着会调用 `handle_syscall()` 函数。
|
|
|
|
|
|
|
|
|
|
RustOS 的系统调用方式如下(实现在 `user/ucore-lib/src/syscall.rs` 中):
|
|
|
|
|
RustOS 的系统调用方式如下(实现在 [user/ucore-ulib/src/syscall.rs](../../../user/ucore-ulib/src/syscall.rs#L47) 中):
|
|
|
|
|
|
|
|
|
|
* 将系统调用号保存在寄存器 `x8`,将 6 参数分别保存在寄存器 `x0~x5` 中;
|
|
|
|
|
* 执行系统调用指令 `svc 0`;
|
|
|
|
@ -189,13 +189,13 @@ RustOS 的系统调用方式如下(实现在 `user/ucore-lib/src/syscall.rs` 中
|
|
|
|
|
* TLB conflict abort.
|
|
|
|
|
* ...
|
|
|
|
|
|
|
|
|
|
其中 level 表示在第几级页表产生异常。当状态码是 translation fault、access flag fault、permission fault 时,将被判断为是缺页异常,并调用 `handle_page_fault()` 处理缺页异常。
|
|
|
|
|
其中 level 表示在第几级翻译表产生异常。当状态码是 translation fault、access flag fault、permission fault 时,将被判断为是缺页异常,并调用 `handle_page_fault()` 处理缺页异常。
|
|
|
|
|
|
|
|
|
|
发生 Instruction Abort 与 Data Abort 的虚拟地址将会被保存到 `FAR_ELx` 系统寄存器中。此时再调用 `crate::memory::page_fault_handler(addr)` 来做具体的缺页处理。
|
|
|
|
|
|
|
|
|
|
#### IRQ
|
|
|
|
|
|
|
|
|
|
如果该异常是 IRQ,则会调用 `kernel/src/arch/aarch64/board/raspi3/irq.rs` 中的 `handle_irq()` 函数。该函数与 board 相关,即使都是在 aarch64 下,不同 board 的 IRQ 处理方式也不一样,所以放到了 `kernel/src/arch/board/raspi3` 中,表示是 RPi3 特有的 IRQ 处理方式。
|
|
|
|
|
如果该异常是 IRQ,则会调用 [kernel/src/arch/aarch64/board/raspi3/irq.rs](../../../kernel/src/arch/aarch64/board/raspi3/irq.rs#L8) 中的 `handle_irq()` 函数。该函数与 board 相关,即使都是在 aarch64 下,不同 board 的 IRQ 处理方式也不一样,所以放到了模块 [kernel/src/arch/aarch64/board/raspi3](../../../kernel/src/arch/aarch64/board/raspi3/) 中,表示是 RPi3 特有的 IRQ 处理方式。
|
|
|
|
|
|
|
|
|
|
该函数首先会判断是否有时钟中断,如果有就先处理时钟中断:
|
|
|
|
|
|
|
|
|
@ -207,7 +207,7 @@ if controller.is_pending() {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
其中使用了 crate bcm2837,位于 `crate/bcm2837` 中,是一个封装良好的访问 RPi3 底层外围设备的库。
|
|
|
|
|
其中使用了 crate bcm2837,位于 [crate/bcm2837](../../../crate/bcm2837/) 中,是一个封装良好的访问 RPi3 底层外围设备的库。
|
|
|
|
|
|
|
|
|
|
然后会遍历所有其他未处理的 IRQ,如果该 IRQ 已注册,就调用它的处理函数:
|
|
|
|
|
|
|
|
|
|