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
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 @@ KERNEL_ELF = target/$(TARGET)/release/kernel
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 @@ qemu: $(KERNEL_BIN)
@$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN)
endif
miniterm:
@$(DOCKER_MINITERM) $(EXEC_MINITERM) $(DEV_SERIAL)
clippy:
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.
- 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
$ sudo screen /dev/ttyUSB0 230400
$ make miniterm
```
> ❗ **NOTE**: Depending on your host operating system, the serial device name might differ.
> For example, on `macOS`, it might be something like `/dev/tty.usbserial-0001`.
> ❗ **NOTE**: `Miniterm` 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:
7. Hit <kbd>Enter</kbd> to kick off the kernel boot process. Observe the output:
```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
[1] Drivers loaded:
1. BCM GPIO
@ -86,7 +97,7 @@ $ sudo screen /dev/ttyUSB0 230400
[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
@ -116,6 +127,59 @@ diff -uNr 05_safe_globals/Cargo.toml 06_drivers_gpio_uart/Cargo.toml
[target.'cfg(target_arch = "aarch64")'.dependencies]
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
--- 05_safe_globals/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
--- 06_drivers_gpio_uart/Makefile
+++ 07_uart_chainloader/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
@@ -15,7 +21,8 @@
@@ -21,7 +21,8 @@
OBJDUMP_BINARY = aarch64-none-elf-objdump
NM_BINARY = aarch64-none-elf-nm
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)
TARGET = aarch64-unknown-none-softfloat
KERNEL_BIN = kernel8.img
@@ -25,7 +32,8 @@
@@ -31,7 +32,8 @@
OBJDUMP_BINARY = aarch64-none-elf-objdump
NM_BINARY = aarch64-none-elf-nm
LINKER_FILE = src/bsp/raspberrypi/link.ld
@ -136,34 +123,25 @@ diff -uNr 06_drivers_gpio_uart/Makefile 07_uart_chainloader/Makefile
endif
# Export for build.rs
@@ -51,13 +59,24 @@
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)
+
@@ -67,13 +69,14 @@
ifeq ($(UNAME_S),Linux)
DOCKER_CMD_DEV = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_DEV)
- DOCKER_MINITERM = $(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
+
-.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 \
+ check
all: $(KERNEL_BIN)
@@ -71,13 +90,19 @@
@@ -87,15 +90,18 @@
$(DOC_CMD) --document-private-items --open
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
endif
-miniterm:
- @$(DOCKER_MINITERM) $(EXEC_MINITERM) $(DEV_SERIAL)
+chainboot:
+ @$(DOCKER_CHAINBOOT) $(EXEC_MINIPUSH) $(DEV_SERIAL) $(CHAINBOOT_DEMO_PAYLOAD)
+
clippy:
RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(CLIPPY_CMD)
@@ -88,7 +113,10 @@
@@ -107,7 +113,10 @@
readelf --headers $(KERNEL_ELF)
objdump: $(KERNEL_ELF)

@ -5,60 +5,26 @@
#
# Copyright (c) 2020 Andre Richter <andre.o.richter@gmail.com>
require 'rubygems'
require 'bundler/setup'
require 'io/console'
require 'colorize'
require_relative 'miniterm'
require 'ruby-progressbar'
require 'serialport'
require 'timeout'
require_relative 'minipush/progressbar_patch'
require 'timeout'
class ConnectionError < StandardError; end
class ProtocolError < StandardError; end
# The main class
class MiniPush
class MiniPush < MiniTerm
def initialize(serial_name, binary_image_path)
@target_serial_name = serial_name
@target_serial = nil
super(serial_name)
@name_short = 'MP'
@binary_image_path = binary_image_path
@binary_size = nil
@binary_image = 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[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
# characters _at the end_ of a character stream (e.g. after a header print from Miniload).
def wait_for_binary_request
@ -92,7 +58,7 @@ class MiniPush
def send_binary
pb = ProgressBar.create(
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 },
length: 92
)
@ -104,69 +70,16 @@ class MiniPush
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.
def handle_protocol_error
connetion_reset
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?
end
def handle_unexpected(error)
connetion_reset
puts
puts "[MP] ⚡ #{"Unexpected Error: #{error.inspect}".light_red}"
end
public
def run
@ -187,7 +100,7 @@ class MiniPush
ensure
connetion_reset
puts
puts '[MP] Bye 👋'
puts "[#{@name_short}] Bye 👋"
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