|
| 1 | +# 教程06 - UART链加载器 |
| 2 | + |
| 3 | +## tl;dr |
| 4 | + |
| 5 | +- 从SD卡上运行是一次不错的体验,但是每次都为每个新的二进制文件这样做将非常繁琐。 |
| 6 | + 因此,让我们编写一个[chainloader]。 |
| 7 | +- 这将是您需要放在SD卡上的最后一个二进制文件。 |
| 8 | + 每个后续的教程都将在`Makefile`中提供一个`chainboot`,让您方便地通过`UART`加载内核。 |
| 9 | + |
| 10 | +[chainloader]: https://en.wikipedia.org/wiki/Chain_loading |
| 11 | + |
| 12 | + |
| 13 | +## 注意 |
| 14 | + |
| 15 | +请注意,这个教程中有一些内容仅通过查看源代码很难理解。 |
| 16 | + |
| 17 | +大致的意思是,在`boot.s`中,我们编写了一段[position independent code]代码, |
| 18 | +它会自动确定固件加载二进制文件的位置(`0x8_0000`),以及链接到的位置(`0x200_0000`,参见 `kernel.ld`)。 |
| 19 | +然后,二进制文件将自身从加载地址复制到链接地址(也就是"重定位"自身),然后跳转到`_start_rust()`的重定位版本。 |
| 20 | + |
| 21 | +由于链加载程序现在已经"脱离了路径",它现在可以从`UART`接收另一个内核二进制文件,并将其复制到RPi固件的标准加载地址`0x8_0000`。 |
| 22 | +最后,它跳转到`0x8_0000`,新加载的二进制文件会透明地执行,就好像它一直从SD卡加载一样。 |
| 23 | + |
| 24 | +在我有时间详细写下这些内容之前,请耐心等待。目前,请将这个教程视为一种便利功能的启用程序,它允许快速启动以下教程。 |
| 25 | +_对于那些渴望深入了解的人,可以直接跳到第[15章](../15_virtual_mem_part3_precomputed_tables),阅读README的前半部分, |
| 26 | +其中讨论了`Load Address != Link Address`的问题_。 |
| 27 | + |
| 28 | +[position independent code]: https://en.wikipedia.org/wiki/Position-independent_code |
| 29 | + |
| 30 | +## 安装并测试它 |
| 31 | + |
| 32 | +我们的链加载程序称为`MiniLoad`,受到了[raspbootin]的启发。 |
| 33 | + |
| 34 | +您可以按照以下教程尝试它: |
| 35 | +1. 根据您的目标硬件运行命令:`make`或`BSP=rpi4 make`。 |
| 36 | +1. 将`kernel8.img`复制到SD卡中,并将SD卡重新插入您的RPi。 |
| 37 | +1. 运行命令`make chainboot`或`BSP=rpi4 make chainboot`。 |
| 38 | +1. 将USB串口连接到您的主机PC上。 |
| 39 | + - 请参考[top-level README](../README.md#-usb-serial-output)中的接线图。 |
| 40 | + - 确保您**没有**连接USB串口的电源引脚,只连接RX/TX和GND。 |
| 41 | +1. 将RPi连接到(USB)电源线。 |
| 42 | +1. 观察加载程序通过`UART`获取内核: |
| 43 | + |
| 44 | +> ❗ **注意**: `make chainboot`假设默认的串行设备名称为`/dev/ttyUSB0`。根据您的主机操作系统,设备名称可能会有所不同。 |
| 45 | +> 例如,在`macOS`上,它可能是类似于`/dev/tty.usbserial-0001`的名称。 |
| 46 | +> 在这种情况下,请明确给出设备名称: |
| 47 | +
|
| 48 | + |
| 49 | +```console |
| 50 | +$ DEV_SERIAL=/dev/tty.usbserial-0001 make chainboot |
| 51 | +``` |
| 52 | + |
| 53 | +[raspbootin]: https://github.com/mrvn/raspbootin |
| 54 | + |
| 55 | +```console |
| 56 | +$ make chainboot |
| 57 | +[...] |
| 58 | +Minipush 1.0 |
| 59 | + |
| 60 | +[MP] ⏳ Waiting for /dev/ttyUSB0 |
| 61 | +[MP] ✅ Serial connected |
| 62 | +[MP] 🔌 Please power the target now |
| 63 | + |
| 64 | + __ __ _ _ _ _ |
| 65 | +| \/ (_)_ _ (_) | ___ __ _ __| | |
| 66 | +| |\/| | | ' \| | |__/ _ \/ _` / _` | |
| 67 | +|_| |_|_|_||_|_|____\___/\__,_\__,_| |
| 68 | + |
| 69 | + Raspberry Pi 3 |
| 70 | + |
| 71 | +[ML] Requesting binary |
| 72 | +[MP] ⏩ Pushing 7 KiB ==========================================🦀 100% 0 KiB/s Time: 00:00:00 |
| 73 | +[ML] Loaded! Executing the payload now |
| 74 | + |
| 75 | +[0] mingo version 0.5.0 |
| 76 | +[1] Booting on: Raspberry Pi 3 |
| 77 | +[2] Drivers loaded: |
| 78 | + 1. BCM PL011 UART |
| 79 | + 2. BCM GPIO |
| 80 | +[3] Chars written: 117 |
| 81 | +[4] Echoing input now |
| 82 | +``` |
| 83 | + |
| 84 | +在这个教程中,为了演示目的,加载了上一个教程中的内核版本。在后续的教程中,将使用工作目录的内核。 |
| 85 | + |
| 86 | +## 测试它 |
| 87 | + |
| 88 | +这个教程中的`Makefile`有一个额外的目标`qemuasm`,它可以让你很好地观察到内核在重新定位后如何从加载地址区域(`0x80_XXX`) |
| 89 | +跳转到重新定位的代码(`0x0200_0XXX`): |
| 90 | + |
| 91 | +```console |
| 92 | +$ make qemuasm |
| 93 | +[...] |
| 94 | +N: |
| 95 | +0x00080030: 58000140 ldr x0, #0x80058 |
| 96 | +0x00080034: 9100001f mov sp, x0 |
| 97 | +0x00080038: 58000141 ldr x1, #0x80060 |
| 98 | +0x0008003c: d61f0020 br x1 |
| 99 | + |
| 100 | +---------------- |
| 101 | +IN: |
| 102 | +0x02000070: 9400044c bl #0x20011a0 |
| 103 | + |
| 104 | +---------------- |
| 105 | +IN: |
| 106 | +0x020011a0: 90000008 adrp x8, #0x2001000 |
| 107 | +0x020011a4: 90000009 adrp x9, #0x2001000 |
| 108 | +0x020011a8: f9446508 ldr x8, [x8, #0x8c8] |
| 109 | +0x020011ac: f9446929 ldr x9, [x9, #0x8d0] |
| 110 | +0x020011b0: eb08013f cmp x9, x8 |
| 111 | +0x020011b4: 54000109 b.ls #0x20011d4 |
| 112 | +[...] |
| 113 | +``` |
| 114 | + |
| 115 | +## 相比之前的变化(diff) |
| 116 | +请检查[英文版本](README.md#diff-to-previous),这是最新的。 |
0 commit comments