Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit aa7413f

Browse files
authored
Translation Chapter 09 (#191)
1 parent 2dfb9e7 commit aa7413f

File tree

1 file changed

+182
-0
lines changed

1 file changed

+182
-0
lines changed

‎09_privilege_level/README.CN.md

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# 教程 09 - 特权级别
2+
3+
## tl;dr
4+
5+
- 在早期引导代码中,我们从`Hypervisor`特权级别(AArch64中的`EL2`)过渡到`Kernel` (`EL1`)特权级别。
6+
7+
## 目录
8+
9+
- [介绍](#介绍)
10+
- [本教程的范围](#本教程的范围)
11+
- [在入口点检查EL2](#在入口点检查EL2)
12+
- [过渡准备](#过渡准备)
13+
- [从未发生的异常中返回](#从未发生的异常中返回)
14+
- [测试](#测试)
15+
- [相比之前的变化(diff)](#相比之前的变化(diff))
16+
17+
## 介绍
18+
19+
应用级别的CPU具有所谓的`privilege levels`,它们具有不同的目的:
20+
21+
| Typically used for | AArch64 | RISC-V | x86 |
22+
| ------------- | ------------- | ------------- | ------------- |
23+
| Userspace applications | EL0 | U/VU | Ring 3 |
24+
| OS Kernel | EL1 | S/VS | Ring 0 |
25+
| Hypervisor | EL2 | HS | Ring -1 |
26+
| Low-Level Firmware | EL3 | M | |
27+
28+
在AArch64中,`EL`代表`Exception Level`(异常级别)。如果您想获取有关其他体系结构的更多信息,请查看以下链接:
29+
- [x86 privilege rings](https://en.wikipedia.org/wiki/Protection_ring).
30+
- [RISC-V privilege modes](https://content.riscv.org/wp-content/uploads/2017/12/Tue0942-riscv-hypervisor-waterman.pdf).
31+
32+
在继续之前,我强烈建议您先浏览一下[Programmer’s Guide for ARMv8-A]`的第3章`。它提供了关于该主题的简明概述。
33+
34+
[Programmer’s Guide for ARMv8-A]: http://infocenter.arm.com/help/topic/com.arm.doc.den0024a/DEN0024A_v8_architecture_PG.pdf
35+
36+
## 本教程的范围
37+
38+
默认情况下,树莓派将始终在`EL2`中开始执行。由于我们正在编写一个传统的`Kernel`,我们需要过渡到更合适的`EL1`
39+
40+
## 在入口点检查EL2
41+
42+
首先,我们需要确保我们实际上是在`EL2`中执行,然后才能调用相应的代码过渡到`EL1`
43+
因此,我们在`boot.s`的顶部添加了一个新的检查,如果CPU核心不在`EL2`中,则将其停止。
44+
45+
```
46+
// Only proceed if the core executes in EL2. Park it otherwise.
47+
mrs x0, CurrentEL
48+
cmp x0, {CONST_CURRENTEL_EL2}
49+
b.ne .L_parking_loop
50+
```
51+
52+
接下来,在`boot.rs`中继续准备从`EL2``EL1`的过渡,通过调用`prepare_el2_to_el1_transition()`函数。
53+
54+
```rust
55+
#[no_mangle]
56+
pub unsafe extern "C" fn _start_rust(phys_boot_core_stack_end_exclusive_addr: u64) -> ! {
57+
prepare_el2_to_el1_transition(phys_boot_core_stack_end_exclusive_addr);
58+
59+
// Use `eret` to "return" to EL1. This results in execution of kernel_init() in EL1.
60+
asm::eret()
61+
}
62+
```
63+
64+
## 过渡准备
65+
66+
由于`EL2``EL1`更具特权,它可以控制各种处理器功能,并允许或禁止`EL1`代码使用它们。
67+
其中一个例子是访问计时器和计数器寄存器。我们已经在[tutorial 07](../07_timestamps/)中使用了它们,所以当然我们希望保留它们。
68+
因此,我们在[Counter-timer Hypervisor Control register]中设置相应的标志,并将虚拟偏移量设置为零,以获取真实的物理值。
69+
70+
[Counter-timer Hypervisor Control register]: https://docs.rs/aarch64-cpu/9.0.0/src/aarch64_cpu/registers/cnthctl_el2.rs.html
71+
72+
```rust
73+
// Enable timer counter registers for EL1.
74+
CNTHCTL_EL2.write(CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET);
75+
76+
// No offset for reading the counters.
77+
CNTVOFF_EL2.set(0);
78+
```
79+
80+
接下来,我们配置[Hypervisor Configuration Register],使`EL1``AArch64`模式下运行,而不是在`AArch32`模式下运行,这也是可能的。
81+
82+
[Hypervisor Configuration Register]: https://docs.rs/aarch64-cpu/9.0.0/src/aarch64_cpu/registers/hcr_el2.rs.html
83+
84+
```rust
85+
// Set EL1 execution state to AArch64.
86+
HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64);
87+
```
88+
89+
## 从未发生的异常中返回
90+
91+
实际上,从较高的EL过渡到较低的EL只有一种方式,即通过执行[ERET]指令。
92+
93+
[ERET]: https://docs.rs/aarch64-cpu/9.0.0/src/aarch64_cpu/asm.rs.html#92-101
94+
95+
在这个指令中,它将会将[Saved Program Status Register - EL2]的内容复制到`Current Program Status Register - EL1`,并跳转到存储在[Exception Link Register - EL2]
96+
97+
这基本上是在发生异常时所发生的相反过程。您将在即将发布的教程中了解更多相关内容。
98+
99+
[Saved Program Status Register - EL2]: https://docs.rs/aarch64-cpu/9.0.0/src/aarch64_cpu/registers/spsr_el2.rs.html
100+
[Exception Link Register - EL2]: https://docs.rs/aarch64-cpu/9.0.0/src/aarch64_cpu/registers/elr_el2.rs.html
101+
102+
```rust
103+
// Set up a simulated exception return.
104+
//
105+
// First, fake a saved program status where all interrupts were masked and SP_EL1 was used as a
106+
// stack pointer.
107+
SPSR_EL2.write(
108+
SPSR_EL2::D::Masked
109+
+ SPSR_EL2::A::Masked
110+
+ SPSR_EL2::I::Masked
111+
+ SPSR_EL2::F::Masked
112+
+ SPSR_EL2::M::EL1h,
113+
);
114+
115+
// Second, let the link register point to kernel_init().
116+
ELR_EL2.set(crate::kernel_init as *const () as u64);
117+
118+
// Set up SP_EL1 (stack pointer), which will be used by EL1 once we "return" to it. Since there
119+
// are no plans to ever return to EL2, just re-use the same stack.
120+
SP_EL1.set(phys_boot_core_stack_end_exclusive_addr);
121+
```
122+
123+
正如您所看到的,我们将`ELR_EL2`的值设置为之前直接从入口点调用的`kernel_init()`函数的地址。最后,我们设置了`SP_EL1`的堆栈指针。
124+
125+
您可能已经注意到,堆栈的地址作为函数参数进行了传递。正如您可能记得的,在`boot.s``_start()`函数中,
126+
我们已经为`EL2`设置了堆栈。由于没有计划返回到`EL2`,我们可以直接重用相同的堆栈作为`EL1`的堆栈,
127+
因此使用函数参数将其地址传递。
128+
129+
最后,在`_start_rust()`函数中调用了`ERET`指令。
130+
131+
```rust
132+
#[no_mangle]
133+
pub unsafe extern "C" fn _start_rust(phys_boot_core_stack_end_exclusive_addr: u64) -> ! {
134+
prepare_el2_to_el1_transition(phys_boot_core_stack_end_exclusive_addr);
135+
136+
// Use `eret` to "return" to EL1. This results in execution of kernel_init() in EL1.
137+
asm::eret()
138+
}
139+
```
140+
141+
## 测试
142+
143+
`main.rs`中,我们打印`current privilege level`,并额外检查`SPSR_EL2`中的掩码位是否传递到了`EL1`:
144+
145+
```console
146+
$ make chainboot
147+
[...]
148+
Minipush 1.0
149+
150+
[MP] ⏳ Waiting for /dev/ttyUSB0
151+
[MP] ✅ Serial connected
152+
[MP] 🔌 Please power the target now
153+
154+
__ __ _ _ _ _
155+
| \/ (_)_ _ (_) | ___ __ _ __| |
156+
| |\/| | | ' \| | |__/ _ \/ _` / _` |
157+
|_| |_|_|_||_|_|____\___/\__,_\__,_|
158+
159+
Raspberry Pi 3
160+
161+
[ML] Requesting binary
162+
[MP] ⏩ Pushing 14 KiB =========================================🦀 100% 0 KiB/s Time: 00:00:00
163+
[ML] Loaded! Executing the payload now
164+
165+
[ 0.162546] mingo version 0.9.0
166+
[ 0.162745] Booting on: Raspberry Pi 3
167+
[ 0.163201] Current privilege level: EL1
168+
[ 0.163677] Exception handling state:
169+
[ 0.164122] Debug: Masked
170+
[ 0.164511] SError: Masked
171+
[ 0.164901] IRQ: Masked
172+
[ 0.165291] FIQ: Masked
173+
[ 0.165681] Architectural timer resolution: 52 ns
174+
[ 0.166255] Drivers loaded:
175+
[ 0.166592] 1. BCM PL011 UART
176+
[ 0.167014] 2. BCM GPIO
177+
[ 0.167371] Timer test, spinning for 1 second
178+
[ 1.167904] Echoing input now
179+
```
180+
181+
## 相比之前的变化(diff)
182+
请检查[英文版本](README.md#diff-to-previous),这是最新的。

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /