From 8982682d47c5ec2bad1edac74b3ad34e220f1809 Mon Sep 17 00:00:00 2001 From: Andre Richter Date: Thu, 29 Apr 2021 22:54:57 +0200 Subject: [PATCH] Add chainloader test closes #101 --- 06_uart_chainloader/Makefile | 14 ++- 06_uart_chainloader/README.md | 117 +++++++++++++++++++- 06_uart_chainloader/tests/qemu_minipush.rb | 82 ++++++++++++++ 07_timestamps/README.md | 122 ++++++++++++++++++++- devtool_completion.bash | 2 +- utils/devtool.rb | 17 ++- utils/minipush.rb | 29 +++-- utils/miniterm.rb | 9 +- 8 files changed, 359 insertions(+), 33 deletions(-) create mode 100644 06_uart_chainloader/tests/qemu_minipush.rb diff --git a/06_uart_chainloader/Makefile b/06_uart_chainloader/Makefile index ce9d5a64..6805ed1c 100644 --- a/06_uart_chainloader/Makefile +++ b/06_uart_chainloader/Makefile @@ -70,6 +70,7 @@ 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_TEST = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_DIR_UTILS) $(DOCKER_IMAGE) DOCKER_TOOLS = $(DOCKER_CMD) $(DOCKER_IMAGE) # Dockerize commands that require USB device passthrough only on Linux @@ -79,8 +80,9 @@ ifeq ($(UNAME_S),Linux) DOCKER_CHAINBOOT = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_UTILS) $(DOCKER_IMAGE) endif -EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -EXEC_MINIPUSH = ruby ../utils/minipush.rb +EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) +EXEC_MINIPUSH = ruby ../utils/minipush.rb +EXEC_QEMU_MINIPUSH = ruby tests/qemu_minipush.rb .PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu qemuasm chainboot clippy clean readelf objdump nm \ check @@ -99,7 +101,7 @@ doc: @$(DOC_CMD) --document-private-items --open ifeq ($(QEMU_MACHINE_TYPE),) -qemu: +qemu test: $(call colorecho, "\n$(QEMU_MISSING_STRING)") else qemu: $(KERNEL_BIN) @@ -109,6 +111,12 @@ qemu: $(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 + +test: $(KERNEL_BIN) + $(call colorecho, "\nTesting chainloading - $(BSP)") + @$(DOCKER_TEST) $(EXEC_QEMU_MINIPUSH) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) \ + -kernel $(KERNEL_BIN) $(CHAINBOOT_DEMO_PAYLOAD) + endif chainboot: diff --git a/06_uart_chainloader/README.md b/06_uart_chainloader/README.md index 80aa2133..2674bf63 100644 --- a/06_uart_chainloader/README.md +++ b/06_uart_chainloader/README.md @@ -153,7 +153,14 @@ diff -uNr 05_drivers_gpio_uart/Makefile 06_uart_chainloader/Makefile endif # Export for build.rs -@@ -74,13 +76,14 @@ +@@ -68,19 +70,22 @@ + DOCKER_ARG_DEV = --privileged -v /dev:/dev + + DOCKER_QEMU = $(DOCKER_CMD_INTERACT) $(DOCKER_IMAGE) ++DOCKER_TEST = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_DIR_UTILS) $(DOCKER_IMAGE) + DOCKER_TOOLS = $(DOCKER_CMD) $(DOCKER_IMAGE) + + # Dockerize commands that require USB device passthrough only on Linux ifeq ($(UNAME_S),Linux) DOCKER_CMD_DEV = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_DEV) @@ -161,9 +168,11 @@ diff -uNr 05_drivers_gpio_uart/Makefile 06_uart_chainloader/Makefile + DOCKER_CHAINBOOT = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_UTILS) $(DOCKER_IMAGE) endif - 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_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) ++EXEC_MINIPUSH = ruby ../utils/minipush.rb ++EXEC_QEMU_MINIPUSH = ruby tests/qemu_minipush.rb -.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu miniterm clippy clean readelf objdump nm check +.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu qemuasm chainboot clippy clean readelf objdump nm \ @@ -171,7 +180,14 @@ diff -uNr 05_drivers_gpio_uart/Makefile 06_uart_chainloader/Makefile all: $(KERNEL_BIN) -@@ -102,10 +105,14 @@ +@@ -96,16 +101,26 @@ + @$(DOC_CMD) --document-private-items --open + + ifeq ($(QEMU_MACHINE_TYPE),) +-qemu: ++qemu test: + $(call colorecho, "\n$(QEMU_MISSING_STRING)") + else qemu: $(KERNEL_BIN) $(call colorecho, "\nLaunching QEMU") @$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN) @@ -179,6 +195,12 @@ diff -uNr 05_drivers_gpio_uart/Makefile 06_uart_chainloader/Makefile +qemuasm: $(KERNEL_BIN) + $(call colorecho, "\nLaunching QEMU with ASM output") + @$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN) -d in_asm ++ ++test: $(KERNEL_BIN) ++ $(call colorecho, "\nTesting chainloading - $(BSP)") ++ @$(DOCKER_TEST) $(EXEC_QEMU_MINIPUSH) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) \ ++ -kernel $(KERNEL_BIN) $(CHAINBOOT_DEMO_PAYLOAD) ++ endif -miniterm: @@ -470,6 +492,93 @@ diff -uNr 05_drivers_gpio_uart/src/main.rs 06_uart_chainloader/src/main.rs + kernel() } +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 +@@ -0,0 +1,82 @@ ++# frozen_string_literal: true ++ ++# SPDX-License-Identifier: MIT OR Apache-2.0 ++# ++# Copyright (c) 2020-2021 Andre Richter ++ ++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 ++ ++ public ++ ++ # 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 + diff -uNr 05_drivers_gpio_uart/update.sh 06_uart_chainloader/update.sh --- 05_drivers_gpio_uart/update.sh +++ 06_uart_chainloader/update.sh diff --git a/06_uart_chainloader/tests/qemu_minipush.rb b/06_uart_chainloader/tests/qemu_minipush.rb new file mode 100644 index 00000000..36a8abda --- /dev/null +++ b/06_uart_chainloader/tests/qemu_minipush.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2020-2021 Andre Richter + +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 + + public + + # 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 diff --git a/07_timestamps/README.md b/07_timestamps/README.md index a5063cb6..1701ebf6 100644 --- a/07_timestamps/README.md +++ b/07_timestamps/README.md @@ -78,9 +78,23 @@ diff -uNr 06_uart_chainloader/Makefile 07_timestamps/Makefile endif # Export for build.rs -@@ -82,8 +80,7 @@ - EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) - EXEC_MINIPUSH = ruby ../utils/minipush.rb +@@ -70,7 +68,6 @@ + DOCKER_ARG_DEV = --privileged -v /dev:/dev + + DOCKER_QEMU = $(DOCKER_CMD_INTERACT) $(DOCKER_IMAGE) +-DOCKER_TEST = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_DIR_UTILS) $(DOCKER_IMAGE) + DOCKER_TOOLS = $(DOCKER_CMD) $(DOCKER_IMAGE) + + # Dockerize commands that require USB device passthrough only on Linux +@@ -80,12 +77,10 @@ + DOCKER_CHAINBOOT = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_UTILS) $(DOCKER_IMAGE) + endif + +-EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) +-EXEC_MINIPUSH = ruby ../utils/minipush.rb +-EXEC_QEMU_MINIPUSH = ruby tests/qemu_minipush.rb ++EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) ++EXEC_MINIPUSH = ruby ../utils/minipush.rb -.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu qemuasm chainboot clippy clean readelf objdump nm \ - check @@ -88,7 +102,14 @@ diff -uNr 06_uart_chainloader/Makefile 07_timestamps/Makefile all: $(KERNEL_BIN) -@@ -105,14 +102,10 @@ +@@ -101,26 +96,16 @@ + @$(DOC_CMD) --document-private-items --open + + ifeq ($(QEMU_MACHINE_TYPE),) +-qemu test: ++qemu: + $(call colorecho, "\n$(QEMU_MISSING_STRING)") + else qemu: $(KERNEL_BIN) $(call colorecho, "\nLaunching QEMU") @$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN) @@ -96,6 +117,12 @@ diff -uNr 06_uart_chainloader/Makefile 07_timestamps/Makefile -qemuasm: $(KERNEL_BIN) - $(call colorecho, "\nLaunching QEMU with ASM output") - @$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN) -d in_asm +- +-test: $(KERNEL_BIN) +- $(call colorecho, "\nTesting chainloading - $(BSP)") +- @$(DOCKER_TEST) $(EXEC_QEMU_MINIPUSH) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) \ +- -kernel $(KERNEL_BIN) $(CHAINBOOT_DEMO_PAYLOAD) +- endif -chainboot: @@ -692,6 +719,93 @@ diff -uNr 06_uart_chainloader/src/time.rs 07_timestamps/src/time.rs + } +} +diff -uNr 06_uart_chainloader/tests/qemu_minipush.rb 07_timestamps/tests/qemu_minipush.rb +--- 06_uart_chainloader/tests/qemu_minipush.rb ++++ 07_timestamps/tests/qemu_minipush.rb +@@ -1,82 +0,0 @@ +-# frozen_string_literal: true +- +-# SPDX-License-Identifier: MIT OR Apache-2.0 +-# +-# Copyright (c) 2020-2021 Andre Richter +- +-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 +- +- public +- +- # 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 + diff -uNr 06_uart_chainloader/update.sh 07_timestamps/update.sh --- 06_uart_chainloader/update.sh +++ 07_timestamps/update.sh diff --git a/devtool_completion.bash b/devtool_completion.bash index e351aa2e..3831425c 100755 --- a/devtool_completion.bash +++ b/devtool_completion.bash @@ -1,3 +1,3 @@ #!/usr/bin/env bash -complete -W "clean clippy copyright diff fmt fmt_check make make_xtra misspell ready_for_publish ready_for_publish_no_rust rubocop test_integration test_unit update" devtool +complete -W "clean clippy copyright diff fmt fmt_check make make_xtra misspell ready_for_publish ready_for_publish_no_rust rubocop test_integration test_unit test_xtra update" devtool diff --git a/utils/devtool.rb b/utils/devtool.rb index a62af380..494de032 100755 --- a/utils/devtool.rb +++ b/utils/devtool.rb @@ -52,7 +52,7 @@ class TutorialCrate end def test_unit - return unless testable? + return unless kernel_tests? puts "Unit Tests #{@folder}".light_blue @@ -60,7 +60,7 @@ class TutorialCrate end def test_integration - return unless testable? + return unless kernel_tests? puts "Integration Tests #{@folder}".light_blue @@ -74,8 +74,8 @@ class TutorialCrate private - def testable? - Dir.exist?("#{@folder}/tests") + def kernel_tests? + File.exist?("#{@folder}/tests/runner.rb") end end @@ -147,6 +147,13 @@ class DevTool system('cd X1_JTAG_boot && bash update.sh') end + def test_xtra + return if @user_has_supplied_crates + + puts 'Test Xtra stuff'.light_blue + exit(1) unless system('cd *_uart_chainloader && make test') + end + def test_unit @crates.each(&:test_unit) end @@ -166,8 +173,6 @@ class DevTool def rubocop puts 'Rubocop'.light_blue - system('which bundle') - system('bundle --version') exit(1) unless system('bundle exec rubocop') end diff --git a/utils/minipush.rb b/utils/minipush.rb index 14c1e6a5..a9ee9bb2 100755 --- a/utils/minipush.rb +++ b/utils/minipush.rb @@ -80,7 +80,7 @@ class MiniPush < MiniTerm end # override - def handle_reconnect + def handle_reconnect(_error) connetion_reset puts @@ -100,8 +100,8 @@ class MiniPush < MiniTerm send_size send_binary terminal - rescue ConnectionError, EOFError, Errno::EIO, ProtocolError, Timeout::Error - handle_reconnect + rescue ConnectionError, EOFError, Errno::EIO, ProtocolError, Timeout::Error => e + handle_reconnect(e) retry rescue StandardError => e handle_unexpected(e) @@ -112,14 +112,19 @@ class MiniPush < MiniTerm end end -puts -puts 'Minipush 1.0'.cyan -puts +##-------------------------------------------------------------------------------------------------- +## Execution starts here +##-------------------------------------------------------------------------------------------------- +if __FILE__ == $PROGRAM_NAME + puts + puts 'Minipush 1.0'.cyan + puts + + # CTRL + C handler. Only here to suppress Ruby's default exception print. + trap('INT') do + # The `ensure` block from `MiniPush::run` will run after exit, restoring console state. + exit + end -# CTRL + C handler. Only here to suppress Ruby's default exception print. -trap('INT') do - # The `ensure` block from `MiniPush::run` will run after exit, restoring console state. - exit + MiniPush.new(ARGV[0], ARGV[1]).run end - -MiniPush.new(ARGV[0], ARGV[1]).run diff --git a/utils/miniterm.rb b/utils/miniterm.rb index ff0c64fb..10cc4bc5 100755 --- a/utils/miniterm.rb +++ b/utils/miniterm.rb @@ -94,7 +94,7 @@ class MiniTerm end # When the serial lost power or was removed during R/W operation. - def handle_reconnect + def handle_reconnect(_error) connetion_reset puts @@ -113,8 +113,8 @@ class MiniTerm def run open_serial terminal - rescue ConnectionError, EOFError, Errno::EIO - handle_reconnect + rescue ConnectionError, EOFError, Errno::EIO => e + handle_reconnect(e) retry rescue StandardError => e handle_unexpected(e) @@ -125,6 +125,9 @@ class MiniTerm end end +##-------------------------------------------------------------------------------------------------- +## Execution starts here +##-------------------------------------------------------------------------------------------------- if __FILE__ == $PROGRAM_NAME puts puts 'Miniterm 1.0'.cyan