rust-raspberrypi-OS-tutorials/06_uart_chainloader/README.md

594 lines
19 KiB
Markdown
Raw Normal View History

2021-03-20 09:47:39 +00:00
# Tutorial 06 - UART Chainloader
2019-10-17 07:33:42 +00:00
## tl;dr
2020-10-04 20:27:28 +00:00
- Running from an SD card was a nice experience, but it would be extremely tedious to do it for
every new binary. So let's write a [chainloader].
2020-10-04 20:27:28 +00:00
- This will be the last binary you need to put on the SD card. Each following tutorial will provide
a `chainboot` target in the `Makefile` that lets you conveniently load the kernel over `UART`.
2019-10-17 07:33:42 +00:00
[chainloader]: https://en.wikipedia.org/wiki/Chain_loading
2020-03-28 12:26:27 +00:00
2020-10-28 15:29:50 +00:00
## Note
Please note that there is stuff going on in this tutorial that is very hard to grasp by only looking
at the source code changes.
The gist of it is that in `boot.s`, we are writing a piece of [position independent code] which
automatically determines where the firmware has loaded the binary (`0x8_0000`), and where it was
linked to (`0x200_0000`, see `link.ld`). The binary then copies itself from loaded to linked address
(aka "relocating" itself), and then jumps to the relocated version of `_start_rust()`.
Since the chainloader has put itself "out of the way" now, it can now receive another kernel binary
from the `UART` and copy it to the standard load address of the RPi firmware at `0x8_0000`. Finally,
it jumps to `0x8_0000` and the newly loaded binary transparently executes as if it had been loaded
from SD card all along.
2020-10-28 15:29:50 +00:00
Please bear with me until I find the time to write it all down here elaborately. For the time being,
please see this tutorial as an enabler for a convenience feature that allows booting the following
tutorials in a quick manner.
[position independent code]: https://en.wikipedia.org/wiki/Position-independent_code
2020-03-28 12:26:27 +00:00
## Install and test it
Our chainloader is called `MiniLoad` and is inspired by [raspbootin].
2019-10-17 07:33:42 +00:00
You can try it with this tutorial already:
1. Depending on your target hardware, run:`make` or `BSP=rpi4 make`.
2021-03-27 19:44:50 +00:00
1. Copy `kernel8.img` to the SD card and put the SD card back into your RPi.
1. Run `make chainboot` or `BSP=rpi4 make chainboot`.
1. Connect the USB serial to your host PC.
- Wiring diagram at [top-level README](../README.md#-usb-serial-output).
- Make sure that you **DID NOT** connect the power pin of the USB serial. Only RX/TX and GND.
1. Connect the RPi to the (USB) power cable.
1. Observe the loader fetching a kernel over `UART`:
2019-10-21 20:11:52 +00:00
> ❗ **NOTE**: `make chainboot` assumes a default serial device name of `/dev/ttyUSB0`. Depending on
> your host operating system, the device name might differ. For example, on `macOS`, it might be
> something like `/dev/tty.usbserial-0001`. In this case, please give the name explicitly:
```console
$ DEV_SERIAL=/dev/tty.usbserial-0001 make chainboot
```
2020-04-11 20:07:59 +00:00
2020-03-28 12:26:27 +00:00
[raspbootin]: https://github.com/mrvn/raspbootin
2019-10-21 20:11:52 +00:00
```console
2020-03-28 12:26:27 +00:00
$ make chainboot
2019-10-21 20:11:52 +00:00
[...]
Minipush 1.0
[MP] ⏳ Waiting for /dev/ttyUSB0
[MP] ✅ Serial connected
[MP] 🔌 Please power the target now
2019-12-30 23:00:09 +00:00
__ __ _ _ _ _
2019-10-21 20:11:52 +00:00
| \/ (_)_ _ (_) | ___ __ _ __| |
| |\/| | | ' \| | |__/ _ \/ _` / _` |
|_| |_|_|_||_|_|____\___/\__,_\__,_|
Raspberry Pi 3
[ML] Requesting binary
[MP] ⏩ Pushing 6 KiB ==========================================🦀 100% 0 KiB/s Time: 00:00:00
2019-10-21 20:11:52 +00:00
[ML] Loaded! Executing the payload now
2021-04-04 20:30:40 +00:00
[0] mingo version 0.5.0
[1] Booting on: Raspberry Pi 3
[2] Drivers loaded:
2020-03-28 12:26:27 +00:00
1. BCM GPIO
2. BCM PL011 UART
2021-04-04 20:30:40 +00:00
[3] Chars written: 117
[4] Echoing input now
2019-10-21 20:11:52 +00:00
```
2019-10-17 07:33:42 +00:00
In this tutorial, a version of the kernel from the previous tutorial is loaded for demo purposes. In
subsequent tutorials, it will be the working directory's kernel.
2019-10-17 07:33:42 +00:00
2019-12-30 23:00:09 +00:00
## Test it
2019-10-17 07:33:42 +00:00
The `Makefile` in this tutorial has an additional target, `qemuasm`, that lets you nicely observe
how the kernel, after relocating itself, jumps the load address region (`0x80_XXX`) to the relocated
code at (`0x0200_0XXX`):
2019-10-17 07:33:42 +00:00
```console
2020-03-28 12:26:27 +00:00
$ make qemuasm
2019-10-17 07:33:42 +00:00
[...]
N:
0x00080030: 58000140 ldr x0, #0x80058
0x00080034: 9100001f mov sp, x0
0x00080038: 58000141 ldr x1, #0x80060
0x0008003c: d61f0020 br x1
----------------
2019-10-17 07:33:42 +00:00
IN:
0x02000070: 9400044c bl #0x20011a0
2019-10-17 07:33:42 +00:00
----------------
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
2019-10-17 07:33:42 +00:00
[...]
```
## Diff to previous
2020-03-28 14:42:42 +00:00
```diff
2021-04-04 20:30:40 +00:00
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 = "2018"
2021-03-20 09:47:39 +00:00
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
2020-03-28 14:42:42 +00:00
2021-03-20 09:47:39 +00:00
diff -uNr 05_drivers_gpio_uart/Makefile 06_uart_chainloader/Makefile
--- 05_drivers_gpio_uart/Makefile
+++ 06_uart_chainloader/Makefile
@@ -25,6 +25,7 @@
2021-03-12 22:44:10 +00:00
READELF_BINARY = aarch64-none-elf-readelf
2020-04-12 20:22:29 +00:00
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
2020-04-12 20:22:29 +00:00
+ CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi3.img
2020-03-28 14:42:42 +00:00
else ifeq ($(BSP),rpi4)
2020-04-12 20:22:29 +00:00
TARGET = aarch64-unknown-none-softfloat
2020-04-16 20:46:11 +00:00
KERNEL_BIN = kernel8.img
@@ -36,6 +37,7 @@
2021-03-12 22:44:10 +00:00
READELF_BINARY = aarch64-none-elf-readelf
2020-04-12 20:22:29 +00:00
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
2020-04-12 20:22:29 +00:00
+ CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi4.img
2020-03-28 14:42:42 +00:00
endif
# Export for build.rs
2021-04-29 20:54:57 +00:00
@@ -68,19 +70,22 @@
DOCKER_ARG_DEV = --privileged -v /dev:/dev
DOCKER_QEMU = $(DOCKER_CMD_INTERACT) $(DOCKER_IMAGE)
2021-04-29 22:03:55 +00:00
+DOCKER_TEST = $(DOCKER_CMD) -t $(DOCKER_ARG_DIR_UTILS) $(DOCKER_IMAGE)
2021-04-29 20:54:57 +00:00
DOCKER_TOOLS = $(DOCKER_CMD) $(DOCKER_IMAGE)
# Dockerize commands that require USB device passthrough only on Linux
2020-11-08 22:37:17 +00:00
ifeq ($(UNAME_S),Linux)
DOCKER_CMD_DEV = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_DEV)
- DOCKER_MINITERM = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_UTILS) $(DOCKER_IMAGE)
2020-04-12 20:22:29 +00:00
+ DOCKER_CHAINBOOT = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_UTILS) $(DOCKER_IMAGE)
2020-11-08 22:37:17 +00:00
endif
2020-03-28 14:42:42 +00:00
2021-04-29 20:54:57 +00:00
-EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE)
2020-11-08 22:37:17 +00:00
-EXEC_MINITERM = ruby ../utils/miniterm.rb
2021-04-29 20:54:57 +00:00
+EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE)
+EXEC_MINIPUSH = ruby ../utils/minipush.rb
+EXEC_QEMU_MINIPUSH = ruby tests/qemu_minipush.rb
2020-11-08 22:37:17 +00:00
2020-11-08 22:43:56 +00:00
-.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu miniterm clippy clean readelf objdump nm check
2020-04-16 20:46:11 +00:00
+.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu qemuasm chainboot clippy clean readelf objdump nm \
+ check
2020-03-28 14:42:42 +00:00
2020-04-16 20:46:11 +00:00
all: $(KERNEL_BIN)
2021-04-29 20:54:57 +00:00
@@ -96,16 +101,26 @@
@$(DOC_CMD) --document-private-items --open
ifeq ($(QEMU_MACHINE_TYPE),)
-qemu:
+qemu test:
$(call colorecho, "\n$(QEMU_MISSING_STRING)")
else
2020-04-16 20:46:11 +00:00
qemu: $(KERNEL_BIN)
2021-03-12 22:44:10 +00:00
$(call colorecho, "\nLaunching QEMU")
2020-04-16 20:46:11 +00:00
@$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN)
2020-03-28 14:42:42 +00:00
+
2020-04-16 20:46:11 +00:00
+qemuasm: $(KERNEL_BIN)
2021-03-12 22:44:10 +00:00
+ $(call colorecho, "\nLaunching QEMU with ASM output")
2020-04-16 20:46:11 +00:00
+ @$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN) -d in_asm
2021-04-29 20:54:57 +00:00
+
+test: $(KERNEL_BIN)
+ $(call colorecho, "\nTesting chainloading - $(BSP)")
+ @$(DOCKER_TEST) $(EXEC_QEMU_MINIPUSH) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) \
+ -kernel $(KERNEL_BIN) $(CHAINBOOT_DEMO_PAYLOAD)
+
2020-03-28 14:42:42 +00:00
endif
2020-11-08 22:37:17 +00:00
-miniterm:
- @$(DOCKER_MINITERM) $(EXEC_MINITERM) $(DEV_SERIAL)
2020-03-28 14:42:42 +00:00
+chainboot:
+ @$(DOCKER_CHAINBOOT) $(EXEC_MINIPUSH) $(DEV_SERIAL) $(CHAINBOOT_DEMO_PAYLOAD)
2020-11-08 22:37:17 +00:00
2020-03-28 14:42:42 +00:00
clippy:
2021-03-12 22:44:10 +00:00
@RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(CLIPPY_CMD)
2020-03-28 14:42:42 +00:00
2021-03-20 09:47:39 +00:00
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
bss_init_loop:
cmp x0, x1
- b.eq prepare_rust
+ b.eq relocate_binary
stp xzr, xzr, [x0], #16
b bss_init_loop
+ // Next, relocate the binary.
+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
2020-10-28 22:10:01 +00:00
+
+copy_loop:
+ ldr x3, [x0], #8
+ str x3, [x1], #8
+ cmp x1, x2
+ b.lo copy_loop
+
// Prepare the jump to Rust code.
-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").
parking_loop:
2020-03-28 14:42:42 +00:00
2021-03-20 09:47:39 +00:00
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
@@ -144,7 +144,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
2021-03-20 09:47:39 +00:00
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
2021-01-28 22:25:57 +00:00
@@ -279,7 +279,7 @@
2020-03-28 14:42:42 +00:00
}
/// 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.
2021-01-28 22:25:57 +00:00
@@ -294,12 +294,7 @@
}
2020-03-28 14:42:42 +00:00
// Read one character.
- let mut ret = self.registers.DR.get() as u8 as char;
2020-03-28 14:42:42 +00:00
-
- // 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;
2021-01-28 22:25:57 +00:00
@@ -379,14 +374,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())
}
2020-03-28 14:42:42 +00:00
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()
{}
2020-03-28 14:42:42 +00:00
}
2021-03-20 09:47:39 +00:00
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
@@ -16,7 +16,8 @@
2020-03-28 14:42:42 +00:00
SECTIONS
{
2021-03-16 21:36:06 +00:00
- . = __rpi_load_addr;
+ /* Set the link address to 32 MiB */
+ . = 0x2000000;
/* ^ */
/* | stack */
/* | growth */
@@ -26,6 +27,7 @@
2021-03-16 21:36:06 +00:00
/***********************************************************************************************
* Code + RO Data + Global Offset Table
***********************************************************************************************/
+ __binary_nonzero_start = .;
2020-03-28 14:42:42 +00:00
.text :
{
2021-03-16 21:36:06 +00:00
KEEP(*(.text._start))
@@ -42,6 +44,10 @@
***********************************************************************************************/
.data : { *(.data*) } :segment_rw
2020-03-28 14:42:42 +00:00
+ /* 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 : ALIGN(16)
{
2020-03-28 14:42:42 +00:00
2021-03-20 09:47:39 +00:00
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 @@
2021-03-16 21:36:06 +00:00
/// The board's physical memory map.
#[rustfmt::skip]
pub(super) mod map {
2021-03-16 21:36:06 +00:00
+ pub const BOARD_DEFAULT_LOAD_ADDRESS: usize = 0x8_0000;
2020-09-29 19:43:31 +00:00
- pub const GPIO_OFFSET: usize = 0x0020_0000;
- pub const UART_OFFSET: usize = 0x0020_1000;
2020-09-29 19:43:31 +00:00
+ 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
+//--------------------------------------------------------------------------------------------------
+
2020-09-29 19:43:31 +00:00
+/// The address on which the Raspberry firmware loads every binary by default.
+#[inline(always)]
2020-10-28 15:29:50 +00:00
+pub fn board_default_load_addr() -> *const u64 {
+ map::BOARD_DEFAULT_LOAD_ADDRESS as _
+}
2021-03-20 09:47:39 +00:00
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
@@ -105,6 +105,7 @@
//! 2. Once finished with architectural setup, the arch code calls `kernel_init()`.
2020-10-28 22:10:01 +00:00
2021-04-29 21:08:49 +00:00
#![allow(clippy::upper_case_acronyms)]
2020-10-28 22:10:01 +00:00
+#![feature(asm)]
#![feature(const_fn_fn_ptr_basics)]
2020-10-28 22:10:01 +00:00
#![feature(format_args_nl)]
#![feature(global_asm)]
@@ -142,38 +143,56 @@
2021-03-31 20:48:02 +00:00
kernel_main()
}
+const MINILOAD_LOGO: &str = r#"
+ __ __ _ _ _ _
+| \/ (_)_ _ (_) | ___ __ _ __| |
+| |\/| | | ' \| | |__/ _ \/ _` / _` |
+|_| |_|_|_||_|_|____\___/\__,_\__,_|
+"#;
+
/// The main function running after the early init.
2020-03-28 14:42:42 +00:00
fn kernel_main() -> ! {
use bsp::console::console;
2020-03-28 14:42:42 +00:00
use console::interface::All;
- use driver::interface::DriverManager;
2021-04-04 20:30:40 +00:00
-
- 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!(
2021-04-04 20:30:40 +00:00
- "[3] Chars written: {}",
- bsp::console::console().chars_written()
- );
2021-04-04 20:30:40 +00:00
- println!("[4] Echoing input now");
+ println!("{}", MINILOAD_LOGO);
+ println!("{:^37}", bsp::board_name());
+ println!();
+ println!("[ML] Requesting binary");
+ console().flush();
- // Discard any spurious received characters before going into echo mode.
2021-04-04 20:30:40 +00:00
+ // Discard any spurious received characters before starting with the loader protocol.
console().clear_rx();
- loop {
- let c = bsp::console::console().read_char();
- bsp::console::console().write_char(c);
2021-04-04 20:30:40 +00:00
+
+ // Notify `Minipush` to send the binary.
+ for _ in 0..3 {
+ console().write_char(3 as char);
}
+
2020-03-28 14:42:42 +00:00
+ // 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;
+
+ // Trust it's not too big.
+ console().write_char('O');
+ console().write_char('K');
+
2020-09-29 19:43:31 +00:00
+ let kernel_addr: *mut u8 = bsp::memory::board_default_load_addr() as *mut u8;
2020-03-28 14:42:42 +00:00
+ unsafe {
+ // Read the kernel byte by byte.
+ for i in 0..size {
2020-10-28 15:29:50 +00:00
+ core::ptr::write_volatile(kernel_addr.offset(i as isize), console().read_char() as u8)
+ }
2021-04-04 20:30:40 +00:00
+ }
+
2020-03-28 14:42:42 +00:00
+ println!("[ML] Loaded! Executing the payload now\n");
+ console().flush();
+
+ // Use black magic to create a function pointer.
2020-10-28 22:10:01 +00:00
+ let kernel: fn() -> ! = unsafe { core::mem::transmute(kernel_addr) };
+
2020-03-28 14:42:42 +00:00
+ // Jump to loaded kernel!
+ kernel()
}
2021-04-29 20:54:57 +00:00
diff -uNr 05_drivers_gpio_uart/tests/qemu_minipush.rb 06_uart_chainloader/tests/qemu_minipush.rb
--- 05_drivers_gpio_uart/tests/qemu_minipush.rb
+++ 06_uart_chainloader/tests/qemu_minipush.rb
2021-04-29 21:01:22 +00:00
@@ -0,0 +1,80 @@
2021-04-29 20:54:57 +00:00
+# 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 '../../utils/minipush'
+require 'expect'
+require 'timeout'
+
+# Match for the last print that 'demo_payload_rpiX.img' produces.
+EXPECTED_PRINT = 'Echoing input now'
+
+# The main class
+class QEMUMiniPush < MiniPush
+ TIMEOUT_SECS = 3
+
+ # override
+ def initialize(qemu_cmd, binary_image_path)
+ super(nil, binary_image_path)
+
+ @qemu_cmd = qemu_cmd
+ end
+
+ private
+
+ def quit_qemu_graceful
+ Timeout.timeout(5) do
+ pid = @target_serial.pid
+ Process.kill('TERM', pid)
+ Process.wait(pid)
+ end
+ end
+
+ # override
+ def open_serial
+ @target_serial = IO.popen(@qemu_cmd, 'r+', err: '/dev/null')
+
+ # Ensure all output is immediately flushed to the device.
+ @target_serial.sync = true
+
+ puts "[#{@name_short}] ✅ Serial connected"
+ end
+
+ # override
+ def terminal
+ result = @target_serial.expect(EXPECTED_PRINT, TIMEOUT_SECS)
+ exit(1) if result.nil?
+
+ puts result
+
+ quit_qemu_graceful
+ end
+
+ # override
+ def connetion_reset; end
+
+ # override
+ def handle_reconnect(error)
+ handle_unexpected(error)
+ end
+end
+
+##--------------------------------------------------------------------------------------------------
+## Execution starts here
+##--------------------------------------------------------------------------------------------------
+puts
+puts 'QEMUMiniPush 1.0'.cyan
+puts
+
+# CTRL + C handler. Only here to suppress Ruby's default exception print.
+trap('INT') do
+ # The `ensure` block from `QEMUMiniPush::run` will run after exit, restoring console state.
+ exit
+end
+
+binary_image_path = ARGV.pop
+qemu_cmd = ARGV.join(' ')
+
+QEMUMiniPush.new(qemu_cmd, binary_image_path).run
2021-03-20 09:47:39 +00:00
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
+
2021-03-20 09:47:39 +00:00
+cd ../05_drivers_gpio_uart
+BSP=rpi4 make
2021-03-20 09:47:39 +00:00
+cp kernel8.img ../06_uart_chainloader/demo_payload_rpi4.img
+make
2021-03-20 09:47:39 +00:00
+cp kernel8.img ../06_uart_chainloader/demo_payload_rpi3.img
+rm kernel8.img
2020-03-28 14:42:42 +00:00
```