2821 字
14 分钟
使用Rust在树莓派上编写操作系统 - 06 - UART的链式加载
WARNING

本文所有内容均为翻译,原文:Tutorial 06 - UART Chainloader;原项目:Operating System development tutorials in Rust on the Raspberry Pi

概述#

  • 用SD卡上的镜像启动树莓派感觉很棒,但如果对每个新的二进制文件,都需要手动将其放在SD卡上就会非常麻烦。因此,本章我们将编写一个链式加载器
  • 这是我们最后一次手动拷贝镜像到SD卡上。后面的每章教程都会在Makefile中提供一个chainboot目标,以便通过UART加载内核。

注意#

请注意,若仅查看源码差异,则很难掌握本章教程中出现的一些新功能。

关键就在boot.s中,我们正在编写一段位置无关代码,代码能够自动确定固件从什么位置(0x8_0000)加载二进制文件,以及应当链接到什么位置(0x200_0000,请参阅link.ld)。然后二进制文件将自身从加载地址复制到链接地址(即“重定位”自身),再跳转到重定位版本的_start_rust()中。

由于链式加载器自己已经“让出位置”,它现在就可以从UART接收另一个内核二进制文件,并将其复制到树莓派固件的标准加载地址0x8_0000。最后,它跳转到0x8_0000再透明的执行新加载的二进制文件,仿佛该文件是从SD卡中加载似的。

请耐心等待,等我一有时间,就会将这个过程写成详尽的文档。不过目前,请参阅本章教程看做一个方便的启动器,该驱动器允许我们快速启动后续教程章节中新的内核二进制文件。

安装并测试#

我们的链式加载器叫做MiniLoad,其灵感来自raspbootin

在本章教程中试用MiniLoad

  1. 根据目标硬件,运行:make或者BSP=rpi4 make
  2. kernel8.img复制到SD卡,并将SD卡插回到树莓派。
  3. 根据目标硬件,运行make chainboot或者BSP=rpi4 make chainboot
  4. 连接USB串口线到宿主PC。
    • 按照本项目的README接线。
    • 一定确保没有连接USB串口的电源引脚。只连接了RX/TX和GND。
  5. 为树莓派连接(USB)电源。
  6. 观察加载程序通过UART获取内核:

注意make chainboot的默认串行设备名称为/dev/ttyUSB0。对于不同操作系统的宿主机,设备名称可能会有所不同。例如,在macOS上,名称可能类似于/dev/tty.usbserial-0001。这种情况下,需要明确给出名称:

Terminal window
$ DEV_SERIAL=/dev/tty.usbserial-0001 make chainboot
Terminal window
$ 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 6 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 GPIO
2. BCM PL011 UART
[3] Chars written: 117
[4] Echoing input now

实验#

本章教程中的Makefile有一个额外的目标——qemuasm——可以让您更好地观察内核内核在重定位后,是如何从加载地址0x80_XXX跳转到位于0x0200_0XXX的重定位代码处的:

Terminal window
$ 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 -uNr 05_drivers_gpio_uart/Cargo.toml 06_uart_chainloader/Cargo.toml
--- 05_drivers_gpio_uart/Cargo.toml
+++ 06_uart_chainloader/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "mingo"
-version = "0.5.0"
+version = "0.6.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
edition = "2021"
Binary files 05_drivers_gpio_uart/demo_payload_rpi3.img and 06_uart_chainloader/demo_payload_rpi3.img differ
Binary files 05_drivers_gpio_uart/demo_payload_rpi4.img and 06_uart_chainloader/demo_payload_rpi4.img differ
diff -uNr 05_drivers_gpio_uart/Makefile 06_uart_chainloader/Makefile
--- 05_drivers_gpio_uart/Makefile
+++ 06_uart_chainloader/Makefile
@@ -23,27 +23,29 @@
# BSP-specific arguments.
ifeq ($(BSP),rpi3)
- TARGET = aarch64-unknown-none-softfloat
- KERNEL_BIN = kernel8.img
- QEMU_BINARY = qemu-system-aarch64
- QEMU_MACHINE_TYPE = raspi3
- QEMU_RELEASE_ARGS = -serial stdio -display none
- OBJDUMP_BINARY = aarch64-none-elf-objdump
- NM_BINARY = aarch64-none-elf-nm
- READELF_BINARY = aarch64-none-elf-readelf
- LINKER_FILE = src/bsp/raspberrypi/link.ld
- RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
+ TARGET = aarch64-unknown-none-softfloat
+ KERNEL_BIN = kernel8.img
+ QEMU_BINARY = qemu-system-aarch64
+ QEMU_MACHINE_TYPE = raspi3
+ QEMU_RELEASE_ARGS = -serial stdio -display none
+ OBJDUMP_BINARY = aarch64-none-elf-objdump
+ NM_BINARY = aarch64-none-elf-nm
+ READELF_BINARY = aarch64-none-elf-readelf
+ LINKER_FILE = src/bsp/raspberrypi/link.ld
+ RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
+ CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi3.img
else ifeq ($(BSP),rpi4)
- TARGET = aarch64-unknown-none-softfloat
- KERNEL_BIN = kernel8.img
- QEMU_BINARY = qemu-system-aarch64
- QEMU_MACHINE_TYPE =
- QEMU_RELEASE_ARGS = -serial stdio -display none
- OBJDUMP_BINARY = aarch64-none-elf-objdump
- NM_BINARY = aarch64-none-elf-nm
- READELF_BINARY = aarch64-none-elf-readelf
- LINKER_FILE = src/bsp/raspberrypi/link.ld
- RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
+ TARGET = aarch64-unknown-none-softfloat
+ KERNEL_BIN = kernel8.img
+ QEMU_BINARY = qemu-system-aarch64
+ QEMU_MACHINE_TYPE =
+ QEMU_RELEASE_ARGS = -serial stdio -display none
+ OBJDUMP_BINARY = aarch64-none-elf-objdump
+ NM_BINARY = aarch64-none-elf-nm
+ READELF_BINARY = aarch64-none-elf-readelf
+ LINKER_FILE = src/bsp/raspberrypi/link.ld
+ RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
+ CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi4.img
endif
QEMU_MISSING_STRING = "This board is not yet supported for QEMU."
@@ -75,8 +77,8 @@
-O binary
EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE)
-EXEC_TEST_DISPATCH = ruby ../common/tests/dispatch.rb
-EXEC_MINITERM = ruby ../common/serial/miniterm.rb
+EXEC_TEST_MINIPUSH = ruby tests/chainboot_test.rb
+EXEC_MINIPUSH = ruby ../common/serial/minipush.rb
##------------------------------------------------------------------------------
## Dockerization
@@ -95,7 +97,7 @@
ifeq ($(shell uname -s),Linux)
DOCKER_CMD_DEV = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_DEV)
- DOCKER_MINITERM = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_COMMON) $(DOCKER_IMAGE)
+ DOCKER_CHAINBOOT = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_COMMON) $(DOCKER_IMAGE)
endif
@@ -103,7 +105,7 @@
##--------------------------------------------------------------------------------------------------
## Targets
##--------------------------------------------------------------------------------------------------
-.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu miniterm clippy clean readelf objdump nm check
+.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu chainboot clippy clean readelf objdump nm check
all: $(KERNEL_BIN)
@@ -132,7 +134,7 @@
##------------------------------------------------------------------------------
ifeq ($(QEMU_MACHINE_TYPE),) # QEMU is not supported for the board.
-qemu:
+qemu qemuasm:
$(call colorecho, "\n$(QEMU_MISSING_STRING)")
else # QEMU is supported.
@@ -140,13 +142,18 @@
qemu: $(KERNEL_BIN)
$(call colorecho, "\nLaunching QEMU")
@$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN)
+
+qemuasm: $(KERNEL_BIN)
+ $(call colorecho, "\nLaunching QEMU with ASM output")
+ @$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN) -d in_asm
+
endif
##------------------------------------------------------------------------------
-## Connect to the target's serial
+## Push the kernel to the real HW target
##------------------------------------------------------------------------------
-miniterm:
- @$(DOCKER_MINITERM) $(EXEC_MINITERM) $(DEV_SERIAL)
+chainboot: $(KERNEL_BIN)
+ @$(DOCKER_CHAINBOOT) $(EXEC_MINIPUSH) $(DEV_SERIAL) $(CHAINBOOT_DEMO_PAYLOAD)
##------------------------------------------------------------------------------
## Run clippy
@@ -210,7 +217,8 @@
##------------------------------------------------------------------------------
test_boot: $(KERNEL_BIN)
$(call colorecho, "\nBoot test - $(BSP)")
- @$(DOCKER_TEST) $(EXEC_TEST_DISPATCH) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN)
+ @$(DOCKER_TEST) $(EXEC_TEST_MINIPUSH) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) \
+ -kernel $(KERNEL_BIN) $(CHAINBOOT_DEMO_PAYLOAD)
test: test_boot
diff -uNr 05_drivers_gpio_uart/src/_arch/aarch64/cpu/boot.s 06_uart_chainloader/src/_arch/aarch64/cpu/boot.s
--- 05_drivers_gpio_uart/src/_arch/aarch64/cpu/boot.s
+++ 06_uart_chainloader/src/_arch/aarch64/cpu/boot.s
@@ -18,6 +18,17 @@
add \register, \register, #:lo12:\symbol
.endm
+// Load the address of a symbol into a register, absolute.
+//
+// # Resources
+//
+// - https://sourceware.org/binutils/docs-2.36/as/AArch64_002dRelocations.html
+.macro ADR_ABS register, symbol
+ movz \register, #:abs_g2:\symbol
+ movk \register, #:abs_g1_nc:\symbol
+ movk \register, #:abs_g0_nc:\symbol
+.endm
+
.equ _core_id_mask, 0b11
//--------------------------------------------------------------------------------------------------
@@ -39,23 +50,35 @@
// If execution reaches here, it is the boot core.
// Initialize DRAM.
- ADR_REL x0, __bss_start
- ADR_REL x1, __bss_end_exclusive
+ ADR_ABS x0, __bss_start
+ ADR_ABS x1, __bss_end_exclusive
.L_bss_init_loop:
cmp x0, x1
- b.eq .L_prepare_rust
+ b.eq .L_relocate_binary
stp xzr, xzr, [x0], #16
b .L_bss_init_loop
+ // Next, relocate the binary.
+.L_relocate_binary:
+ ADR_REL x0, __binary_nonzero_start // The address the binary got loaded to.
+ ADR_ABS x1, __binary_nonzero_start // The address the binary was linked to.
+ ADR_ABS x2, __binary_nonzero_end_exclusive
+
+.L_copy_loop:
+ ldr x3, [x0], #8
+ str x3, [x1], #8
+ cmp x1, x2
+ b.lo .L_copy_loop
+
// Prepare the jump to Rust code.
-.L_prepare_rust:
// Set the stack pointer.
- ADR_REL x0, __boot_core_stack_end_exclusive
+ ADR_ABS x0, __boot_core_stack_end_exclusive
mov sp, x0
- // Jump to Rust code.
- b _start_rust
+ // Jump to the relocated Rust code.
+ ADR_ABS x1, _start_rust
+ br x1
// Infinitely wait for events (aka "park the core").
.L_parking_loop:
diff -uNr 05_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs 06_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
--- 05_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
+++ 06_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
@@ -148,7 +148,7 @@
// Make an educated guess for a good delay value (Sequence described in the BCM2837
// peripherals PDF).
//
- // - According to Wikipedia, the fastest Pi3 clocks around 1.4 GHz.
+ // - According to Wikipedia, the fastest RPi4 clocks around 1.5 GHz.
// - The Linux 2837 GPIO driver waits 1 µs between the steps.
//
// So lets try to be on the safe side and default to 2000 cycles, which would equal 1 µs
diff -uNr 05_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
--- 05_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
+++ 06_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
@@ -278,7 +278,7 @@
}
/// Retrieve a character.
- fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<char> {
+ fn read_char(&mut self, blocking_mode: BlockingMode) -> Option<char> {
// If RX FIFO is empty,
if self.registers.FR.matches_all(FR::RXFE::SET) {
// immediately return in non-blocking mode.
@@ -293,12 +293,7 @@
}
// Read one character.
- let mut ret = self.registers.DR.get() as u8 as char;
-
- // Convert carrige return to newline.
- if ret == '\r' {
- ret = '\n'
- }
+ let ret = self.registers.DR.get() as u8 as char;
// Update statistics.
self.chars_read += 1;
@@ -378,14 +373,14 @@
impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
self.inner
- .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
+ .lock(|inner| inner.read_char(BlockingMode::Blocking).unwrap())
}
fn clear_rx(&self) {
// Read from the RX FIFO until it is indicating empty.
while self
.inner
- .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
+ .lock(|inner| inner.read_char(BlockingMode::NonBlocking))
.is_some()
{}
}
diff -uNr 05_drivers_gpio_uart/src/bsp/raspberrypi/link.ld 06_uart_chainloader/src/bsp/raspberrypi/link.ld
--- 05_drivers_gpio_uart/src/bsp/raspberrypi/link.ld
+++ 06_uart_chainloader/src/bsp/raspberrypi/link.ld
@@ -3,8 +3,6 @@
* Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
*/
-__rpi_phys_dram_start_addr = 0;
-
/* The physical address at which the the kernel binary will be loaded by the Raspberry's firmware */
__rpi_phys_binary_load_addr = 0x80000;
@@ -28,7 +26,8 @@
SECTIONS
{
- . = __rpi_phys_dram_start_addr;
+ /* Set the link address to 32 MiB */
+ . = 0x2000000;
/***********************************************************************************************
* Boot Core Stack
@@ -45,6 +44,7 @@
/***********************************************************************************************
* Code + RO Data + Global Offset Table
***********************************************************************************************/
+ __binary_nonzero_start = .;
.text :
{
KEEP(*(.text._start))
@@ -61,6 +61,10 @@
***********************************************************************************************/
.data : { *(.data*) } :segment_data
+ /* Fill up to 8 byte, b/c relocating the binary is done in u64 chunks */
+ . = ALIGN(8);
+ __binary_nonzero_end_exclusive = .;
+
/* Section is zeroed in pairs of u64. Align start and end to 16 bytes */
.bss (NOLOAD) : ALIGN(16)
{
diff -uNr 05_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs 06_uart_chainloader/src/bsp/raspberrypi/memory.rs
--- 05_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs
+++ 06_uart_chainloader/src/bsp/raspberrypi/memory.rs
@@ -11,9 +11,10 @@
/// The board's physical memory map.
#[rustfmt::skip]
pub(super) mod map {
+ pub const BOARD_DEFAULT_LOAD_ADDRESS: usize = 0x8_0000;
- pub const GPIO_OFFSET: usize = 0x0020_0000;
- pub const UART_OFFSET: usize = 0x0020_1000;
+ pub const GPIO_OFFSET: usize = 0x0020_0000;
+ pub const UART_OFFSET: usize = 0x0020_1000;
/// Physical devices.
#[cfg(feature = "bsp_rpi3")]
@@ -35,3 +36,13 @@
pub const PL011_UART_START: usize = START + UART_OFFSET;
}
}
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// The address on which the Raspberry firmware loads every binary by default.
+#[inline(always)]
+pub fn board_default_load_addr() -> *const u64 {
+ map::BOARD_DEFAULT_LOAD_ADDRESS as _
+}
diff -uNr 05_drivers_gpio_uart/src/main.rs 06_uart_chainloader/src/main.rs
--- 05_drivers_gpio_uart/src/main.rs
+++ 06_uart_chainloader/src/main.rs
@@ -141,38 +141,56 @@
kernel_main()
}
+const MINILOAD_LOGO: &str = r#"
+ __ __ _ _ _ _
+| \/ (_)_ _ (_) | ___ __ _ __| |
+| |\/| | | ' \| | |__/ _ \/ _` / _` |
+|_| |_|_|_||_|_|____\___/\__,_\__,_|
+"#;
+
/// The main function running after the early init.
fn kernel_main() -> ! {
use bsp::console::console;
use console::interface::All;
- use driver::interface::DriverManager;
- println!(
- "[0] {} version {}",
- env!("CARGO_PKG_NAME"),
- env!("CARGO_PKG_VERSION")
- );
- println!("[1] Booting on: {}", bsp::board_name());
-
- println!("[2] Drivers loaded:");
- for (i, driver) in bsp::driver::driver_manager()
- .all_device_drivers()
- .iter()
- .enumerate()
- {
- println!(" {}. {}", i + 1, driver.compatible());
+ println!("{}", MINILOAD_LOGO);
+ println!("{:^37}", bsp::board_name());
+ println!();
+ println!("[ML] Requesting binary");
+ console().flush();
+
+ // Discard any spurious received characters before starting with the loader protocol.
+ console().clear_rx();
+
+ // Notify `Minipush` to send the binary.
+ for _ in 0..3 {
+ console().write_char(3 as char);
}
- println!(
- "[3] Chars written: {}",
- bsp::console::console().chars_written()
- );
- println!("[4] Echoing input now");
+ // Read the binary's size.
+ let mut size: u32 = u32::from(console().read_char() as u8);
+ size |= u32::from(console().read_char() as u8) << 8;
+ size |= u32::from(console().read_char() as u8) << 16;
+ size |= u32::from(console().read_char() as u8) << 24;
- // Discard any spurious received characters before going into echo mode.
- console().clear_rx();
- loop {
- let c = bsp::console::console().read_char();
- bsp::console::console().write_char(c);
+ // Trust it's not too big.
+ console().write_char('O');
+ console().write_char('K');
+
+ let kernel_addr: *mut u8 = bsp::memory::board_default_load_addr() as *mut u8;
+ unsafe {
+ // Read the kernel byte by byte.
+ for i in 0..size {
+ core::ptr::write_volatile(kernel_addr.offset(i as isize), console().read_char() as u8)
+ }
}
+
+ println!("[ML] Loaded! Executing the payload now\n");
+ console().flush();
+
+ // Use black magic to create a function pointer.
+ let kernel: fn() -> ! = unsafe { core::mem::transmute(kernel_addr) };
+
+ // Jump to loaded kernel!
+ kernel()
}
diff -uNr 05_drivers_gpio_uart/tests/boot_test_string.rb 06_uart_chainloader/tests/boot_test_string.rb
--- 05_drivers_gpio_uart/tests/boot_test_string.rb
+++ 06_uart_chainloader/tests/boot_test_string.rb
@@ -1,3 +0,0 @@
-# frozen_string_literal: true
-
-EXPECTED_PRINT = 'Echoing input now'
diff -uNr 05_drivers_gpio_uart/tests/chainboot_test.rb 06_uart_chainloader/tests/chainboot_test.rb
--- 05_drivers_gpio_uart/tests/chainboot_test.rb
+++ 06_uart_chainloader/tests/chainboot_test.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+# SPDX-License-Identifier: MIT OR Apache-2.0
+#
+# Copyright (c) 2020-2021 Andre Richter <andre.o.richter@gmail.com>
+
+require_relative '../../common/serial/minipush'
+require_relative '../../common/tests/boot_test'
+require 'pty'
+
+# Match for the last print that 'demo_payload_rpiX.img' produces.
+EXPECTED_PRINT = 'Echoing input now'
+
+# Extend BootTest so that it listens on the output of a MiniPush instance, which is itself connected
+# to a QEMU instance instead of a real HW.
+class ChainbootTest < BootTest
+ MINIPUSH = '../common/serial/minipush.rb'
+ MINIPUSH_POWER_TARGET_REQUEST = 'Please power the target now'
+
+ def initialize(qemu_cmd, payload_path)
+ super(qemu_cmd, EXPECTED_PRINT)
+
+ @test_name = 'Boot test using Minipush'
+
+ @payload_path = payload_path
+ end
+
+ private
+
+ # override
+ def post_process_and_add_output(output)
+ temp = output.join.split("\r\n")
+
+ # Should a line have solo carriage returns, remove any overridden parts of the string.
+ temp.map! { |x| x.gsub(/.*\r/, '') }
+
+ @test_output += temp
+ end
+
+ def wait_for_minipush_power_request(mp_out)
+ output = []
+ Timeout.timeout(MAX_WAIT_SECS) do
+ loop do
+ output << mp_out.gets
+ break if output.last.include?(MINIPUSH_POWER_TARGET_REQUEST)
+ end
+ end
+ rescue Timeout::Error
+ @test_error = 'Timed out waiting for power request'
+ rescue StandardError => e
+ @test_error = e.message
+ ensure
+ post_process_and_add_output(output)
+ end
+
+ # override
+ def setup
+ pty_main, pty_secondary = PTY.open
+ mp_out, _mp_in = PTY.spawn("ruby #{MINIPUSH} #{pty_secondary.path} #{@payload_path}")
+
+ # Wait until MiniPush asks for powering the target.
+ wait_for_minipush_power_request(mp_out)
+
+ # Now is the time to start QEMU with the chainloader binary. QEMU's virtual tty is connected
+ # to the MiniPush instance spawned above, so that the two processes talk to each other.
+ Process.spawn(@qemu_cmd, in: pty_main, out: pty_main)
+
+ # The remainder of the test is done by the parent class' run_concrete_test, which listens on
+ # @qemu_serial. Hence, point it to MiniPush's output.
+ @qemu_serial = mp_out
+ end
+end
+
+##--------------------------------------------------------------------------------------------------
+## Execution starts here
+##--------------------------------------------------------------------------------------------------
+payload_path = ARGV.pop
+qemu_cmd = ARGV.join(' ')
+
+ChainbootTest.new(qemu_cmd, payload_path).run
diff -uNr 05_drivers_gpio_uart/update.sh 06_uart_chainloader/update.sh
--- 05_drivers_gpio_uart/update.sh
+++ 06_uart_chainloader/update.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+cd ../05_drivers_gpio_uart
+BSP=rpi4 make
+cp kernel8.img ../06_uart_chainloader/demo_payload_rpi4.img
+make
+cp kernel8.img ../06_uart_chainloader/demo_payload_rpi3.img
+rm kernel8.img
使用Rust在树莓派上编写操作系统 - 06 - UART的链式加载
https://zlotus.github.io/2021/08/30/writing-os-in-rust-on-rpi-06/
作者
Z
发布于
2021-08-30
许可协议
CC BY-NC-SA 4.0