diff --git a/05_drivers_gpio_uart/README.CN.md b/05_drivers_gpio_uart/README.CN.md new file mode 100644 index 000000000..00179fc7f --- /dev/null +++ b/05_drivers_gpio_uart/README.CN.md @@ -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. 通过按下ctrl-c退出。 + +## 相比之前的变化(diff) +请检查[英文版本](README.md#diff-to-previous),这是最新的。 \ No newline at end of file diff --git a/05_drivers_gpio_uart/README.md b/05_drivers_gpio_uart/README.md index 563af1a73..a25eae6c8 100644 --- a/05_drivers_gpio_uart/README.md +++ b/05_drivers_gpio_uart/README.md @@ -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 @@ -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 @@ -87,7 +87,7 @@ 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) @@ -95,7 +95,7 @@ init_uart_clock=48000000 - [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) @@ -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 diff --git a/06_uart_chainloader/README.CN.md b/06_uart_chainloader/README.CN.md new file mode 100644 index 000000000..e1c6fea16 --- /dev/null +++ b/06_uart_chainloader/README.CN.md @@ -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),这是最新的。 \ No newline at end of file