Use miniterm instead of screen

pull/84/head
Andre Richter 4 years ago
parent 098e19ecc4
commit e1473099ff
No known key found for this signature in database
GPG Key ID: 2116C1AB102F615E

@ -5,6 +5,12 @@
# Default to the RPi3 # Default to the RPi3
BSP ?= rpi3 BSP ?= rpi3
# Default to a serial device name that is common in Linux.
DEV_SERIAL ?= /dev/ttyUSB0
# Query the host system's kernel name
UNAME_S = $(shell uname -s)
# BSP-specific arguments # BSP-specific arguments
ifeq ($(BSP),rpi3) ifeq ($(BSP),rpi3)
TARGET = aarch64-unknown-none-softfloat TARGET = aarch64-unknown-none-softfloat
@ -51,13 +57,23 @@ KERNEL_ELF = target/$(TARGET)/release/kernel
DOCKER_IMAGE = rustembedded/osdev-utils DOCKER_IMAGE = rustembedded/osdev-utils
DOCKER_CMD = docker run --rm -v $(shell pwd):/work/tutorial -w /work/tutorial DOCKER_CMD = docker run --rm -v $(shell pwd):/work/tutorial -w /work/tutorial
DOCKER_CMD_INTERACT = $(DOCKER_CMD) -i -t DOCKER_CMD_INTERACT = $(DOCKER_CMD) -i -t
DOCKER_ARG_DIR_UTILS = -v $(shell pwd)/../utils:/work/utils
DOCKER_ARG_DEV = --privileged -v /dev:/dev
DOCKER_QEMU = $(DOCKER_CMD_INTERACT) $(DOCKER_IMAGE) DOCKER_QEMU = $(DOCKER_CMD_INTERACT) $(DOCKER_IMAGE)
DOCKER_ELFTOOLS = $(DOCKER_CMD) $(DOCKER_IMAGE) DOCKER_ELFTOOLS = $(DOCKER_CMD) $(DOCKER_IMAGE)
EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) # Dockerize commands that require USB device passthrough only on Linux
ifeq ($(UNAME_S),Linux)
DOCKER_CMD_DEV = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_DEV)
DOCKER_MINITERM = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_UTILS) $(DOCKER_IMAGE)
endif
EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE)
EXEC_MINITERM = ruby ../utils/miniterm.rb
.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu clippy clean readelf objdump nm check .PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu chainboot clippy clean readelf objdump nm check
all: $(KERNEL_BIN) all: $(KERNEL_BIN)
@ -78,6 +94,9 @@ qemu: $(KERNEL_BIN)
@$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN) @$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN)
endif endif
miniterm:
@$(DOCKER_MINITERM) $(EXEC_MINITERM) $(DEV_SERIAL)
clippy: clippy:
RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(CLIPPY_CMD) RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(CLIPPY_CMD)

@ -66,18 +66,29 @@ on the SD card._
5. Insert the SD card into the RPi and connect the USB serial to your host PC. 5. Insert the SD card into the RPi and connect the USB serial to your host PC.
- Wiring diagram at [top-level README](../README.md#usb-serial). - Wiring diagram at [top-level README](../README.md#usb-serial).
6. Run `screen` (you might need to install it first): 6. Run the `miniterm` target, which opens the UART device on the host:
```console ```console
$ sudo screen /dev/ttyUSB0 230400 $ make miniterm
``` ```
> ❗ **NOTE**: Depending on your host operating system, the serial device name might differ. > ❗ **NOTE**: `Miniterm` assumes a default serial device name of `/dev/ttyUSB0`. Depending on your
> For example, on `macOS`, it might be something like `/dev/tty.usbserial-0001`. > 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:
7. Hit <kbd>Enter</kbd> to kick off the kernel boot process. Observe the output:
```console ```console
$ DEV_SERIAL=/dev/tty.usbserial-0001 make miniterm
```
7. Hit <kbd>Enter</kbd> after seeing "`Connected`" to kick off the kernel boot process and observe
the output:
```console
Miniterm 1.0
[MT] ✅ Connected
[0] Booting on: Raspberry Pi 3 [0] Booting on: Raspberry Pi 3
[1] Drivers loaded: [1] Drivers loaded:
1. BCM GPIO 1. BCM GPIO
@ -86,7 +97,7 @@ $ sudo screen /dev/ttyUSB0 230400
[3] Echoing input now [3] Echoing input now
``` ```
8. Exit screen by pressing <kbd>ctrl-a</kbd> <kbd>ctrl-d</kbd> or disconnecting the USB serial. 8. Exit by pressing <kbd>ctrl-c</kbd>.
## Diff to previous ## Diff to previous
```diff ```diff
@ -116,6 +127,59 @@ diff -uNr 05_safe_globals/Cargo.toml 06_drivers_gpio_uart/Cargo.toml
[target.'cfg(target_arch = "aarch64")'.dependencies] [target.'cfg(target_arch = "aarch64")'.dependencies]
cortex-a = { version = "4.x.x" } cortex-a = { version = "4.x.x" }
diff -uNr 05_safe_globals/Makefile 06_drivers_gpio_uart/Makefile
--- 05_safe_globals/Makefile
+++ 06_drivers_gpio_uart/Makefile
@@ -5,6 +5,12 @@
# Default to the RPi3
BSP ?= rpi3
+# Default to a serial device name that is common in Linux.
+DEV_SERIAL ?= /dev/ttyUSB0
+
+# Query the host system's kernel name
+UNAME_S = $(shell uname -s)
+
# BSP-specific arguments
ifeq ($(BSP),rpi3)
TARGET = aarch64-unknown-none-softfloat
@@ -51,13 +57,23 @@
DOCKER_IMAGE = rustembedded/osdev-utils
DOCKER_CMD = docker run --rm -v $(shell pwd):/work/tutorial -w /work/tutorial
DOCKER_CMD_INTERACT = $(DOCKER_CMD) -i -t
+DOCKER_ARG_DIR_UTILS = -v $(shell pwd)/../utils:/work/utils
+DOCKER_ARG_DEV = --privileged -v /dev:/dev
DOCKER_QEMU = $(DOCKER_CMD_INTERACT) $(DOCKER_IMAGE)
DOCKER_ELFTOOLS = $(DOCKER_CMD) $(DOCKER_IMAGE)
-EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE)
+# Dockerize commands that require USB device passthrough only on Linux
+ifeq ($(UNAME_S),Linux)
+ DOCKER_CMD_DEV = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_DEV)
+
+ DOCKER_MINITERM = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_UTILS) $(DOCKER_IMAGE)
+endif
+
+EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE)
+EXEC_MINITERM = ruby ../utils/miniterm.rb
-.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu clippy clean readelf objdump nm check
+.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu chainboot clippy clean readelf objdump nm check
all: $(KERNEL_BIN)
@@ -78,6 +94,9 @@
@$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN)
endif
+miniterm:
+ @$(DOCKER_MINITERM) $(EXEC_MINITERM) $(DEV_SERIAL)
+
clippy:
RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(CLIPPY_CMD)
diff -uNr 05_safe_globals/src/_arch/aarch64/cpu.rs 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs diff -uNr 05_safe_globals/src/_arch/aarch64/cpu.rs 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs
--- 05_safe_globals/src/_arch/aarch64/cpu.rs --- 05_safe_globals/src/_arch/aarch64/cpu.rs
+++ 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs +++ 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs

@ -103,20 +103,7 @@ Binary files 06_drivers_gpio_uart/demo_payload_rpi4.img and 07_uart_chainloader/
diff -uNr 06_drivers_gpio_uart/Makefile 07_uart_chainloader/Makefile diff -uNr 06_drivers_gpio_uart/Makefile 07_uart_chainloader/Makefile
--- 06_drivers_gpio_uart/Makefile --- 06_drivers_gpio_uart/Makefile
+++ 07_uart_chainloader/Makefile +++ 07_uart_chainloader/Makefile
@@ -5,6 +5,12 @@ @@ -21,7 +21,8 @@
# Default to the RPi3
BSP ?= rpi3
+# Default to a serial device name that is common in Linux.
+DEV_SERIAL ?= /dev/ttyUSB0
+
+# Query the host system's kernel name
+UNAME_S = $(shell uname -s)
+
# BSP-specific arguments
ifeq ($(BSP),rpi3)
TARGET = aarch64-unknown-none-softfloat
@@ -15,7 +21,8 @@
OBJDUMP_BINARY = aarch64-none-elf-objdump OBJDUMP_BINARY = aarch64-none-elf-objdump
NM_BINARY = aarch64-none-elf-nm NM_BINARY = aarch64-none-elf-nm
LINKER_FILE = src/bsp/raspberrypi/link.ld LINKER_FILE = src/bsp/raspberrypi/link.ld
@ -126,7 +113,7 @@ diff -uNr 06_drivers_gpio_uart/Makefile 07_uart_chainloader/Makefile
else ifeq ($(BSP),rpi4) else ifeq ($(BSP),rpi4)
TARGET = aarch64-unknown-none-softfloat TARGET = aarch64-unknown-none-softfloat
KERNEL_BIN = kernel8.img KERNEL_BIN = kernel8.img
@@ -25,7 +32,8 @@ @@ -31,7 +32,8 @@
OBJDUMP_BINARY = aarch64-none-elf-objdump OBJDUMP_BINARY = aarch64-none-elf-objdump
NM_BINARY = aarch64-none-elf-nm NM_BINARY = aarch64-none-elf-nm
LINKER_FILE = src/bsp/raspberrypi/link.ld LINKER_FILE = src/bsp/raspberrypi/link.ld
@ -136,34 +123,25 @@ diff -uNr 06_drivers_gpio_uart/Makefile 07_uart_chainloader/Makefile
endif endif
# Export for build.rs # Export for build.rs
@@ -51,13 +59,24 @@ @@ -67,13 +69,14 @@
DOCKER_IMAGE = rustembedded/osdev-utils ifeq ($(UNAME_S),Linux)
DOCKER_CMD = docker run --rm -v $(shell pwd):/work/tutorial -w /work/tutorial DOCKER_CMD_DEV = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_DEV)
DOCKER_CMD_INTERACT = $(DOCKER_CMD) -i -t
+DOCKER_ARG_DIR_UTILS = -v $(shell pwd)/../utils:/work/utils - DOCKER_MINITERM = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_UTILS) $(DOCKER_IMAGE)
+DOCKER_ARG_DEV = --privileged -v /dev:/dev
DOCKER_QEMU = $(DOCKER_CMD_INTERACT) $(DOCKER_IMAGE)
DOCKER_ELFTOOLS = $(DOCKER_CMD) $(DOCKER_IMAGE)
-EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE)
+# Dockerize commands that require USB device passthrough only on Linux
+ifeq ($(UNAME_S),Linux)
+ DOCKER_CMD_DEV = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_DEV)
+
+ DOCKER_CHAINBOOT = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_UTILS) $(DOCKER_IMAGE) + DOCKER_CHAINBOOT = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_UTILS) $(DOCKER_IMAGE)
+endif endif
-.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu clippy clean readelf objdump nm check EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE)
+EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -EXEC_MINITERM = ruby ../utils/miniterm.rb
+EXEC_MINIPUSH = ruby ../utils/minipush.rb +EXEC_MINIPUSH = ruby ../utils/minipush.rb
+
-.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu chainboot clippy clean readelf objdump nm check
+.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu qemuasm chainboot clippy clean readelf objdump nm \ +.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu qemuasm chainboot clippy clean readelf objdump nm \
+ check + check
all: $(KERNEL_BIN) all: $(KERNEL_BIN)
@@ -71,13 +90,19 @@ @@ -87,15 +90,18 @@
$(DOC_CMD) --document-private-items --open $(DOC_CMD) --document-private-items --open
ifeq ($(QEMU_MACHINE_TYPE),) ifeq ($(QEMU_MACHINE_TYPE),)
@ -178,13 +156,14 @@ diff -uNr 06_drivers_gpio_uart/Makefile 07_uart_chainloader/Makefile
+ @$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN) -d in_asm + @$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN) -d in_asm
endif endif
-miniterm:
- @$(DOCKER_MINITERM) $(EXEC_MINITERM) $(DEV_SERIAL)
+chainboot: +chainboot:
+ @$(DOCKER_CHAINBOOT) $(EXEC_MINIPUSH) $(DEV_SERIAL) $(CHAINBOOT_DEMO_PAYLOAD) + @$(DOCKER_CHAINBOOT) $(EXEC_MINIPUSH) $(DEV_SERIAL) $(CHAINBOOT_DEMO_PAYLOAD)
+
clippy: clippy:
RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(CLIPPY_CMD) RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(CLIPPY_CMD)
@@ -107,7 +113,10 @@
@@ -88,7 +113,10 @@
readelf --headers $(KERNEL_ELF) readelf --headers $(KERNEL_ELF)
objdump: $(KERNEL_ELF) objdump: $(KERNEL_ELF)

@ -5,60 +5,26 @@
# #
# Copyright (c) 2020 Andre Richter <andre.o.richter@gmail.com> # Copyright (c) 2020 Andre Richter <andre.o.richter@gmail.com>
require 'rubygems' require_relative 'miniterm'
require 'bundler/setup'
require 'io/console'
require 'colorize'
require 'ruby-progressbar' require 'ruby-progressbar'
require 'serialport'
require 'timeout'
require_relative 'minipush/progressbar_patch' require_relative 'minipush/progressbar_patch'
require 'timeout'
class ConnectionError < StandardError; end
class ProtocolError < StandardError; end class ProtocolError < StandardError; end
# The main class # The main class
class MiniPush class MiniPush < MiniTerm
def initialize(serial_name, binary_image_path) def initialize(serial_name, binary_image_path)
@target_serial_name = serial_name super(serial_name)
@target_serial = nil
@name_short = 'MP'
@binary_image_path = binary_image_path @binary_image_path = binary_image_path
@binary_size = nil @binary_size = nil
@binary_image = nil @binary_image = nil
@host_console = IO.console
end end
private private
def serial_connected?
File.exist?(@target_serial_name)
end
def wait_for_serial
loop do
break if serial_connected?
print "\r[MP] ⏳ Waiting for #{@target_serial_name}"
sleep(1)
end
end
def open_serial
wait_for_serial
@target_serial = SerialPort.new(@target_serial_name, 230_400, 8, 1, SerialPort::NONE)
# Ensure all output is immediately flushed to the device.
@target_serial.sync = true
rescue Errno::EACCES => e
puts
puts "[MP] 🚫 #{e.message} - Maybe try with 'sudo'"
exit
else
puts
puts '[MP] ✅ Connected'
end
# The three characters signaling the request token are expected to arrive as the last three # The three characters signaling the request token are expected to arrive as the last three
# characters _at the end_ of a character stream (e.g. after a header print from Miniload). # characters _at the end_ of a character stream (e.g. after a header print from Miniload).
def wait_for_binary_request def wait_for_binary_request
@ -92,7 +58,7 @@ class MiniPush
def send_binary def send_binary
pb = ProgressBar.create( pb = ProgressBar.create(
total: @binary_size, total: @binary_size,
format: '[MP] ⏩ Pushing %k KiB %b🦀%i %p%% %r KiB/s %a', format: "[#{@name_short}] ⏩ Pushing %k KiB %b🦀%i %p%% %r KiB/s %a",
rate_scale: ->(rate) { rate / 1024 }, rate_scale: ->(rate) { rate / 1024 },
length: 92 length: 92
) )
@ -104,69 +70,16 @@ class MiniPush
end end
end end
def terminal
@host_console.raw!
Thread.abort_on_exception = true
Thread.report_on_exception = false
# Receive from target and print on host console.
target_to_host = Thread.new do
loop do
char = @target_serial.getc
raise ConnectionError if char.nil?
# onlcr
@host_console.putc("\r") if char == "\n"
@host_console.putc(char)
end
end
# Transmit host console input to target.
loop do
c = @host_console.getc
# CTRL + C in raw mode was pressed
if c == "\u{3}"
target_to_host.kill
break
end
@target_serial.putc(c)
end
end
def connetion_reset
@target_serial&.close
@target_serial = nil
@host_console.cooked!
end
# When the serial lost power or was removed during R/W operation.
def handle_reconnect
connetion_reset
puts
puts "[MP] ⚡ #{'Connection Error: Reinsert the USB serial again'.light_red}"
end
# When the serial is still powered. # When the serial is still powered.
def handle_protocol_error def handle_protocol_error
connetion_reset connetion_reset
puts puts
puts "[MP] ⚡ #{'Protocol Error: Remove and insert the USB serial again'.light_red}" puts "[#{@name_short}] ⚡ " \
"#{'Protocol Error: Remove and insert the USB serial again'.light_red}"
sleep(1) while serial_connected? sleep(1) while serial_connected?
end end
def handle_unexpected(error)
connetion_reset
puts
puts "[MP] ⚡ #{"Unexpected Error: #{error.inspect}".light_red}"
end
public public
def run def run
@ -187,7 +100,7 @@ class MiniPush
ensure ensure
connetion_reset connetion_reset
puts puts
puts '[MP] Bye 👋' puts "[#{@name_short}] Bye 👋"
end end
end end

@ -0,0 +1,138 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
# SPDX-License-Identifier: MIT OR Apache-2.0
#
# Copyright (c) 2020 Andre Richter <andre.o.richter@gmail.com>
require 'rubygems'
require 'bundler/setup'
require 'io/console'
require 'colorize'
require 'serialport'
class ConnectionError < StandardError; end
# The main class
class MiniTerm
def initialize(serial_name)
@name_short = 'MT'
@target_serial_name = serial_name
@target_serial = nil
@host_console = IO.console
end
private
def serial_connected?
File.exist?(@target_serial_name)
end
def wait_for_serial
loop do
break if serial_connected?
print "\r[#{@name_short}] ⏳ Waiting for #{@target_serial_name}"
sleep(1)
end
end
def open_serial
wait_for_serial
@target_serial = SerialPort.new(@target_serial_name, 230_400, 8, 1, SerialPort::NONE)
# Ensure all output is immediately flushed to the device.
@target_serial.sync = true
rescue Errno::EACCES => e
puts
puts "[#{@name_short}] 🚫 #{e.message} - Maybe try with 'sudo'"
exit
else
puts
puts "[#{@name_short}] ✅ Connected"
end
def terminal
@host_console.raw!
Thread.abort_on_exception = true
Thread.report_on_exception = false
# Receive from target and print on host console.
target_to_host = Thread.new do
loop do
char = @target_serial.getc
raise ConnectionError if char.nil?
# Translate incoming newline to newline + carriage return.
@host_console.putc("\r") if char == "\n"
@host_console.putc(char)
end
end
# Transmit host console input to target.
loop do
c = @host_console.getc
# CTRL + C in raw mode was pressed.
if c == "\u{3}"
target_to_host.kill
break
end
@target_serial.putc(c)
end
end
def connetion_reset
@target_serial&.close
@target_serial = nil
@host_console.cooked!
end
# When the serial lost power or was removed during R/W operation.
def handle_reconnect
connetion_reset
puts
puts "[#{@name_short}] ⚡ #{'Connection Error: Reinsert the USB serial again'.light_red}"
end
def handle_unexpected(error)
connetion_reset
puts
puts "[#{@name_short}] ⚡ #{"Unexpected Error: #{error.inspect}".light_red}"
end
public
def run
open_serial
terminal
rescue ConnectionError, EOFError, Errno::EIO
handle_reconnect
retry
rescue StandardError => e
handle_unexpected(e)
ensure
connetion_reset
puts
puts "[#{@name_short}] Bye 👋"
end
end
if __FILE__ == $PROGRAM_NAME
puts 'Miniterm 1.0'.cyan
puts
# CTRL + C handler. Only here to suppress Ruby's default exception print.
trap('INT') do
# The `ensure` block from `MiniTerm::run` will run after exit, restoring console state.
exit
end
MiniTerm.new(ARGV[0]).run
end
Loading…
Cancel
Save