Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Translation Chapter 6 #183

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions 05_drivers_gpio_uart/README.CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# 教程 05 - 驱动程序: GPIO和UART

## tl;dr

- 添加了用于真实`UART`和`GPIO`控制器的驱动程序。
- **我们将首次能够在真实硬件上运行代码** (请向下滚动查看说明)。

## 简介

在上一篇教程中,我们启用了全局安全变量,为添加第一个真实设备驱动程序奠定了基础。
我们放弃了神奇的QEMU控制台,并引入了一个`驱动程序管理器`,允许`BSP`将设备驱动程序注册到`内核`中。

## 驱动程序管理器

第一步是向内核添加一个`driver subsystem`。相应的代码将位于`src/driver.rs`中。
该子系统引入了`interface::DeviceDriver`,这是每个设备驱动程序都需要实现的通用特征,并为内核所知。
在同一文件中实例化的全局`DRIVER_MANAGER`实例(类型为`DriverManager`)作为一个中央实体,可以被调用来管理内核中的所有设备驱动程序。
例如,通过使用全局可访问的`crate::driver::driver_manager().register_driver(...)`,任何代码都可以注册一个实现了`interface::DeviceDriver`特征的具有静态生命周期的对象。

在内核初始化期间,调用`crate::driver::driver_manager().init_drivers(...)`将使驱动程序管理器遍历所有已注册的驱动程序,
并启动它们的初始化,并执行可选的`post-init callback`,该回调可以与驱动程序一起注册。
例如,此机制用于在`UART`驱动程序初始化后将其切换为主系统控制台的驱动程序。

## BSP驱动程序实现

在`src/bsp/raspberrypi/driver.rs`中,函数`init()`负责注册`UART`和`GPIO`驱动程序。
因此,在内核初始化期间,按照以下来自`main.rs`的代码,正确的顺序是:
(i)首先初始化BSP驱动程序子系统,然后(ii)调用`driver_manager()`。

```rust
unsafe fn kernel_init() -> ! {
// Initialize the BSP driver subsystem.
if let Err(x) = bsp::driver::init() {
panic!("Error initializing BSP driver subsystem: {}", x);
}

// Initialize all device drivers.
driver::driver_manager().init_drivers();
// println! is usable from here on.
```



驱动程序本身存储在`src/bsp/device_driver`中,并且可以在不同的`BSP`之间重复使用
在这些教程中添加的第一个驱动程序是`PL011Uart`驱动程序:它实现了`console::interface::*`特征,并且从现在开始用作主系统控制台。
第二个驱动程序是`GPIO`驱动程序,它根据需要将`RPii's`的`UART`映射(即将来自`SoC`内部的信号路由到实际的硬件引脚)。
请注意,`GPIO`驱动程序区分**RPi 3**和**RPi 4**。它们的硬件不同,因此我们必须在软件中进行适配。

现在,`BSP`还包含了一个内存映射表,位于`src/bsp/raspberrypi/memory.rs`中。它提供了树莓派的`MMIO`地址,
`BSP`使用这些地址来实例化相应的设备驱动程序,以便驱动程序代码知道在内存中找到设备的寄存器的位置。

## SD卡启动

由于我们现在有了真实的`UART`输出,我们可以在真实的硬件上运行代码。
由于前面提到的`GPIO`驱动程序的差异,构建过程在**RPi 3**和**RPi 4**之间有所区别。
默认情况下,所有的`Makefile`目标都将为**RPi 3**构建。
为了**RPi 4**构建,需要在每个目标前加上`BSP=rpi4`。例如:

```console
$ BSP=rpi4 make
$ BSP=rpi4 make doc
```

不幸的是,QEMU目前还不支持**RPi 4**,因此`BSP=rpi4 make qemu`无法工作。

**准备SD卡的一些步骤在RPi3和RPi4之间有所不同,请在以下操作中小心。**

### 通用步骤

1. 创建一个名为`boot`的`FAT32`分区。
2. 在SD卡上生成一个名为`config.txt`的文件,并将以下内容写入其中:

```txt
arm_64bit=1
init_uart_clock=48000000
```
### RPi 3

3. 从[Raspberry Pi firmware repo](https://github.com/raspberrypi/firmware/tree/master/boot)中将以下文件复制到SD卡上:
- [bootcode.bin](https://github.com/raspberrypi/firmware/raw/master/boot/bootcode.bin)
- [fixup.dat](https://github.com/raspberrypi/firmware/raw/master/boot/fixup.dat)
- [start.elf](https://github.com/raspberrypi/firmware/raw/master/boot/start.elf)
4. 运行`make`命令。

### RPi 4

3. 从[Raspberry Pi firmware repo](https://github.com/raspberrypi/firmware/tree/master/boot)中将以下文件复制到SD卡上:
- [fixup4.dat](https://github.com/raspberrypi/firmware/raw/master/boot/fixup4.dat)
- [start4.elf](https://github.com/raspberrypi/firmware/raw/master/boot/start4.elf)
- [bcm2711-rpi-4-b.dtb](https://github.com/raspberrypi/firmware/raw/master/boot/bcm2711-rpi-4-b.dtb)
4. 运行`BSP=rpi4 make`命令。


_**注意**: 如果在您的RPi4上无法正常工作,请尝试将`start4.elf`重命名为`start.elf` (不带4)
并复制到SD卡上。_

### 再次通用步骤

5. 将`kernel8.img`复制到SD卡上,并将SD卡插入RPi。
6. 运行`miniterm` target,在主机上打开UART设备:

```console
$ make miniterm
```

> ❗ **注意**: `Miniterm`假设默认的串行设备名称为`/dev/ttyUSB0`。Depending on your
> 根据您的主机操作系统,设备名称可能会有所不同。例如,在`macOS`上,它可能是
> `/dev/tty.usbserial-0001`之类的。在这种情况下,请明确提供设备名称:


```console
$ DEV_SERIAL=/dev/tty.usbserial-0001 make miniterm
```

7. 将USB串口连接到主机PC。
- 请参考[top-level README](../README.md#-usb-serial-output)中的接线图。
- **注意**: TX(发送)线连接到RX(接收)引脚。
- 确保您**没有**连接USB串口的电源引脚,只连接RX/TX和GND引脚。
8. 将RPi连接到(USB)电源线,并观察输出。

```console
Miniterm 1.0

[MT] ⏳ Waiting for /dev/ttyUSB0
[MT] ✅ Serial connected
[0] mingo version 0.5.0
[1] Booting on: Raspberry Pi 3
[2] Drivers loaded:
1. BCM PL011 UART
2. BCM GPIO
[3] Chars written: 117
[4] Echoing input now
```

8. 通过按下<kbd>ctrl-c</kbd>退出。

## 相比之前的变化(diff)
请检查[英文版本](README.md#diff-to-previous),这是最新的。
18 changes: 9 additions & 9 deletions 05_drivers_gpio_uart/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ The drivers themselves are stored in `src/bsp/device_driver`, and can be reused
first driver added in these tutorials is the `PL011Uart` driver: It implements the
`console::interface::*` traits and is from now on used as the main system console. The second driver
is the `GPIO` driver, which pinmuxes (that is, routing signals from inside the `SoC` to actual HW
pins) the RPi's PL011 UART accordingly. Note how the `GPIO` driver differentiates between **RPi3**
and **RPi4**. Their HW is different, so we have to account for it in SW.
pins) the RPi's PL011 UART accordingly. Note how the `GPIO` driver differentiates between **RPi 3**
and **RPi 4**. Their HW is different, so we have to account for it in SW.

The `BSP`s now also contain a memory map in `src/bsp/raspberrypi/memory.rs`. It provides the
Raspberry's `MMIO` addresses which are used by the `BSP` to instantiate the respective device
Expand All @@ -64,18 +64,18 @@ drivers, so that the driver code knows where to find the device's registers in m
## Boot it from SD card

Since we have real `UART` output now, we can run the code on the real hardware. Building is
differentiated between the **RPi 3** and the **RPi4** due to before mentioned differences in the
differentiated between the **RPi 3** and the **RPi 4** due to before mentioned differences in the
`GPIO` driver. By default, all `Makefile` targets will build for the **RPi 3**. In order to build
for the the **RPi4**, prepend `BSP=rpi4` to each target. For example:
for the the **RPi 4**, prepend `BSP=rpi4` to each target. For example:

```console
$ BSP=rpi4 make
$ BSP=rpi4 make doc
```

Unfortunately, QEMU does not yet support the **RPi4**, so `BSP=rpi4 make qemu` won't work.
Unfortunately, QEMU does not yet support the **RPi 4**, so `BSP=rpi4 make qemu` won't work.

**Some steps for preparing the SD card differ between RPi3 and RPi4, so be careful in the
**Some steps for preparing the SD card differ between RPi 3 and RPi 4, so be careful in the
following.**

### Common for both
Expand All @@ -87,15 +87,15 @@ following.**
arm_64bit=1
init_uart_clock=48000000
```
### Pi 3
### RPi 3

3. Copy the following files from the [Raspberry Pi firmware repo](https://github.com/raspberrypi/firmware/tree/master/boot) onto the SD card:
- [bootcode.bin](https://github.com/raspberrypi/firmware/raw/master/boot/bootcode.bin)
- [fixup.dat](https://github.com/raspberrypi/firmware/raw/master/boot/fixup.dat)
- [start.elf](https://github.com/raspberrypi/firmware/raw/master/boot/start.elf)
4. Run `make`.

### Pi 4
### RPi 4

3. Copy the following files from the [Raspberry Pi firmware repo](https://github.com/raspberrypi/firmware/tree/master/boot) onto the SD card:
- [fixup4.dat](https://github.com/raspberrypi/firmware/raw/master/boot/fixup4.dat)
Expand All @@ -104,7 +104,7 @@ init_uart_clock=48000000
4. Run `BSP=rpi4 make`.


_**Note**: Should it not work on your RPi4, try renaming `start4.elf` to `start.elf` (without the 4)
_**Note**: Should it not work on your RPi 4, try renaming `start4.elf` to `start.elf` (without the 4)
on the SD card._

### Common again
Expand Down
116 changes: 116 additions & 0 deletions 06_uart_chainloader/README.CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# 教程06 - UART链加载器

## tl;dr

- 从SD卡上运行是一次不错的体验,但是每次都为每个新的二进制文件这样做将非常繁琐。
因此,让我们编写一个[chainloader]。
- 这将是您需要放在SD卡上的最后一个二进制文件。
每个后续的教程都将在`Makefile`中提供一个`chainboot`,让您方便地通过`UART`加载内核。

[chainloader]: https://en.wikipedia.org/wiki/Chain_loading


## 注意

请注意,这个教程中有一些内容仅通过查看源代码很难理解。

大致的意思是,在`boot.s`中,我们编写了一段[position independent code]代码,
它会自动确定固件加载二进制文件的位置(`0x8_0000`),以及链接到的位置(`0x200_0000`,参见 `kernel.ld`)。
然后,二进制文件将自身从加载地址复制到链接地址(也就是"重定位"自身),然后跳转到`_start_rust()`的重定位版本。

由于链加载程序现在已经"脱离了路径",它现在可以从`UART`接收另一个内核二进制文件,并将其复制到RPi固件的标准加载地址`0x8_0000`。
最后,它跳转到`0x8_0000`,新加载的二进制文件会透明地执行,就好像它一直从SD卡加载一样。

在我有时间详细写下这些内容之前,请耐心等待。目前,请将这个教程视为一种便利功能的启用程序,它允许快速启动以下教程。
_对于那些渴望深入了解的人,可以直接跳到第[15章](../15_virtual_mem_part3_precomputed_tables),阅读README的前半部分,
其中讨论了`Load Address != Link Address`的问题_。

[position independent code]: https://en.wikipedia.org/wiki/Position-independent_code

## 安装并测试它

我们的链加载程序称为`MiniLoad`,受到了[raspbootin]的启发。

您可以按照以下教程尝试它:
1. 根据您的目标硬件运行命令:`make`或`BSP=rpi4 make`。
1. 将`kernel8.img`复制到SD卡中,并将SD卡重新插入您的RPi。
1. 运行命令`make chainboot`或`BSP=rpi4 make chainboot`。
1. 将USB串口连接到您的主机PC上。
- 请参考[top-level README](../README.md#-usb-serial-output)中的接线图。
- 确保您**没有**连接USB串口的电源引脚,只连接RX/TX和GND。
1. 将RPi连接到(USB)电源线。
1. 观察加载程序通过`UART`获取内核:

> ❗ **注意**: `make chainboot`假设默认的串行设备名称为`/dev/ttyUSB0`。根据您的主机操作系统,设备名称可能会有所不同。
> 例如,在`macOS`上,它可能是类似于`/dev/tty.usbserial-0001`的名称。
> 在这种情况下,请明确给出设备名称:


```console
$ DEV_SERIAL=/dev/tty.usbserial-0001 make chainboot
```

[raspbootin]: https://github.com/mrvn/raspbootin

```console
$ make chainboot
[...]
Minipush 1.0

[MP] ⏳ Waiting for /dev/ttyUSB0
[MP] ✅ Serial connected
[MP] 🔌 Please power the target now

__ __ _ _ _ _
| \/ (_)_ _ (_) | ___ __ _ __| |
| |\/| | | ' \| | |__/ _ \/ _` / _` |
|_| |_|_|_||_|_|____\___/\__,_\__,_|

Raspberry Pi 3

[ML] Requesting binary
[MP] ⏩ Pushing 7 KiB ==========================================🦀 100% 0 KiB/s Time: 00:00:00
[ML] Loaded! Executing the payload now

[0] mingo version 0.5.0
[1] Booting on: Raspberry Pi 3
[2] Drivers loaded:
1. BCM PL011 UART
2. BCM GPIO
[3] Chars written: 117
[4] Echoing input now
```

在这个教程中,为了演示目的,加载了上一个教程中的内核版本。在后续的教程中,将使用工作目录的内核。

## 测试它

这个教程中的`Makefile`有一个额外的目标`qemuasm`,它可以让你很好地观察到内核在重新定位后如何从加载地址区域(`0x80_XXX`)
跳转到重新定位的代码(`0x0200_0XXX`):

```console
$ make qemuasm
[...]
N:
0x00080030: 58000140 ldr x0, #0x80058
0x00080034: 9100001f mov sp, x0
0x00080038: 58000141 ldr x1, #0x80060
0x0008003c: d61f0020 br x1

----------------
IN:
0x02000070: 9400044c bl #0x20011a0

----------------
IN:
0x020011a0: 90000008 adrp x8, #0x2001000
0x020011a4: 90000009 adrp x9, #0x2001000
0x020011a8: f9446508 ldr x8, [x8, #0x8c8]
0x020011ac: f9446929 ldr x9, [x9, #0x8d0]
0x020011b0: eb08013f cmp x9, x8
0x020011b4: 54000109 b.ls #0x20011d4
[...]
```

## 相比之前的变化(diff)
请检查[英文版本](README.md#diff-to-previous),这是最新的。