From 98f84a497da93b9cf9d09e74edfbfb0a1bdebaf9 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sat, 14 Aug 2021 01:41:01 +0200 Subject: [PATCH] qemu: signal correct exit code to QEMU There were a few issues that were causing qemu-system-arm and qemu-system-riscv to give the wrong exit codes. They are in fact capable of exiting with 0 or 1 signalled from the running application, but this functionality wasn't used. This commit changes this in the following ways: * It fixes SemiHosting codes, which were incorrectly written in decimal while they should have been written in hexadecimal (oops!). * It modifies all the baremetal main functions (aka reset handlers) to exit with `exit(0)` instead of `abort()`. * It changes `syscall.Exit` to call `exit(code)` instead of `abort()` on baremetal targets. * It adds these new exit functions where necessary, implemented in a way that signals the correct exit status if running under QEMU. All in all, this means that `tinygo test` doesn't have to look at the output of a test to determine the outcome. It can simply look at the exit code. --- src/device/arm/semihosting.go | 37 +++++++++++++------------ src/runtime/baremetal.go | 2 +- src/runtime/runtime_arm7tdmi.go | 4 +++ src/runtime/runtime_atsamd21.go | 2 +- src/runtime/runtime_atsamd51.go | 2 +- src/runtime/runtime_avr.go | 6 +++- src/runtime/runtime_cortexm_abort.go | 4 +++ src/runtime/runtime_cortexm_qemu.go | 15 +++++++--- src/runtime/runtime_esp32.go | 2 +- src/runtime/runtime_esp32c3.go | 2 +- src/runtime/runtime_esp32xx.go | 4 +++ src/runtime/runtime_esp8266.go | 6 +++- src/runtime/runtime_fe310.go | 2 +- src/runtime/runtime_fe310_baremetal.go | 4 +++ src/runtime/runtime_fe310_qemu.go | 14 ++++++++-- src/runtime/runtime_k210.go | 2 +- src/runtime/runtime_k210_baremetal.go | 4 +++ src/runtime/runtime_mimxrt1062.go | 6 +++- src/runtime/runtime_nrf.go | 2 +- src/runtime/runtime_nxpmk66f18.go | 6 +++- src/runtime/runtime_rp2040.go | 2 +- src/runtime/runtime_stm32.go | 2 +- src/runtime/runtime_tinygoriscv_qemu.go | 15 ++++++++-- 23 files changed, 104 insertions(+), 41 deletions(-) diff --git a/src/device/arm/semihosting.go b/src/device/arm/semihosting.go index cfece50d..19e9f22c 100644 --- a/src/device/arm/semihosting.go +++ b/src/device/arm/semihosting.go @@ -33,28 +33,29 @@ const ( ) // Special codes for the Angel Semihosting interface. +// https://www.keil.com/support/man/docs/armcc/armcc_pge1358787050566.htm const ( // Hardware vector reason codes - SemihostingBranchThroughZero = 20000 - SemihostingUndefinedInstr = 20001 - SemihostingSoftwareInterrupt = 20002 - SemihostingPrefetchAbort = 20003 - SemihostingDataAbort = 20004 - SemihostingAddressException = 20005 - SemihostingIRQ = 20006 - SemihostingFIQ = 20007 + SemihostingBranchThroughZero = 0x20000 + SemihostingUndefinedInstr = 0x20001 + SemihostingSoftwareInterrupt = 0x20002 + SemihostingPrefetchAbort = 0x20003 + SemihostingDataAbort = 0x20004 + SemihostingAddressException = 0x20005 + SemihostingIRQ = 0x20006 + SemihostingFIQ = 0x20007 // Software reason codes - SemihostingBreakPoint = 20020 - SemihostingWatchPoint = 20021 - SemihostingStepComplete = 20022 - SemihostingRunTimeErrorUnknown = 20023 - SemihostingInternalError = 20024 - SemihostingUserInterruption = 20025 - SemihostingApplicationExit = 20026 - SemihostingStackOverflow = 20027 - SemihostingDivisionByZero = 20028 - SemihostingOSSpecific = 20029 + SemihostingBreakPoint = 0x20020 + SemihostingWatchPoint = 0x20021 + SemihostingStepComplete = 0x20022 + SemihostingRunTimeErrorUnknown = 0x20023 + SemihostingInternalError = 0x20024 + SemihostingUserInterruption = 0x20025 + SemihostingApplicationExit = 0x20026 + SemihostingStackOverflow = 0x20027 + SemihostingDivisionByZero = 0x20028 + SemihostingOSSpecific = 0x20029 ) // Call a semihosting function. diff --git a/src/runtime/baremetal.go b/src/runtime/baremetal.go index 5abd1371..5201ae2b 100644 --- a/src/runtime/baremetal.go +++ b/src/runtime/baremetal.go @@ -48,7 +48,7 @@ func libc_free(ptr unsafe.Pointer) { //go:linkname syscall_Exit syscall.Exit func syscall_Exit(code int) { - abort() + exit(code) } const baremetal = true diff --git a/src/runtime/runtime_arm7tdmi.go b/src/runtime/runtime_arm7tdmi.go index 705d5edf..3938506b 100644 --- a/src/runtime/runtime_arm7tdmi.go +++ b/src/runtime/runtime_arm7tdmi.go @@ -75,6 +75,10 @@ func sleepTicks(d timeUnit) { // TODO } +func exit(code int) { + abort() +} + func abort() { // TODO for { diff --git a/src/runtime/runtime_atsamd21.go b/src/runtime/runtime_atsamd21.go index 5ae58121..32ed1cea 100644 --- a/src/runtime/runtime_atsamd21.go +++ b/src/runtime/runtime_atsamd21.go @@ -19,7 +19,7 @@ func postinit() {} func main() { preinit() run() - abort() + exit(0) } func init() { diff --git a/src/runtime/runtime_atsamd51.go b/src/runtime/runtime_atsamd51.go index 54034445..ec704471 100644 --- a/src/runtime/runtime_atsamd51.go +++ b/src/runtime/runtime_atsamd51.go @@ -19,7 +19,7 @@ func main() { arm.SCB.CPACR.Set(0) // disable FPU if it is enabled preinit() run() - abort() + exit(0) } func init() { diff --git a/src/runtime/runtime_avr.go b/src/runtime/runtime_avr.go index 4aedf811..492390c4 100644 --- a/src/runtime/runtime_avr.go +++ b/src/runtime/runtime_avr.go @@ -37,7 +37,7 @@ var _ebss [0]byte func main() { preinit() run() - abort() + exit(0) } func preinit() { @@ -84,6 +84,10 @@ func ticks() timeUnit { return currentTime } +func exit(code int) { + abort() +} + func abort() { for { sleepWDT(WDT_PERIOD_2S) diff --git a/src/runtime/runtime_cortexm_abort.go b/src/runtime/runtime_cortexm_abort.go index cc0d76ad..861d330a 100644 --- a/src/runtime/runtime_cortexm_abort.go +++ b/src/runtime/runtime_cortexm_abort.go @@ -6,6 +6,10 @@ import ( "device/arm" ) +func exit(code int) { + abort() +} + func abort() { // lock up forever for { diff --git a/src/runtime/runtime_cortexm_qemu.go b/src/runtime/runtime_cortexm_qemu.go index 634144fa..2d7e78f3 100644 --- a/src/runtime/runtime_cortexm_qemu.go +++ b/src/runtime/runtime_cortexm_qemu.go @@ -23,8 +23,7 @@ func main() { run() // Signal successful exit. - arm.SemihostingCall(arm.SemihostingReportException, arm.SemihostingApplicationExit) - abort() + exit(0) } func ticksToNanoseconds(ticks timeUnit) int64 { @@ -56,8 +55,16 @@ func waitForEvents() { } func abort() { - // Signal an abnormal exit. - arm.SemihostingCall(arm.SemihostingReportException, arm.SemihostingRunTimeErrorUnknown) + exit(1) +} + +func exit(code int) { + // Exit QEMU. + if code == 0 { + arm.SemihostingCall(arm.SemihostingReportException, arm.SemihostingApplicationExit) + } else { + arm.SemihostingCall(arm.SemihostingReportException, arm.SemihostingRunTimeErrorUnknown) + } // Lock up forever (should be unreachable). for { diff --git a/src/runtime/runtime_esp32.go b/src/runtime/runtime_esp32.go index 79791ff2..7855e2d6 100644 --- a/src/runtime/runtime_esp32.go +++ b/src/runtime/runtime_esp32.go @@ -51,7 +51,7 @@ func main() { run() // Fallback: if main ever returns, hang the CPU. - abort() + exit(0) } //go:extern _sbss diff --git a/src/runtime/runtime_esp32c3.go b/src/runtime/runtime_esp32c3.go index 0f769c0f..a19d5849 100644 --- a/src/runtime/runtime_esp32c3.go +++ b/src/runtime/runtime_esp32c3.go @@ -54,7 +54,7 @@ func main() { run() // Fallback: if main ever returns, hang the CPU. - abort() + exit(0) } func abort() { diff --git a/src/runtime/runtime_esp32xx.go b/src/runtime/runtime_esp32xx.go index c03565d1..54dadab8 100644 --- a/src/runtime/runtime_esp32xx.go +++ b/src/runtime/runtime_esp32xx.go @@ -67,3 +67,7 @@ func sleepTicks(d timeUnit) { // TODO: suspend the CPU to not burn power here unnecessarily. } } + +func exit(code int) { + abort() +} diff --git a/src/runtime/runtime_esp8266.go b/src/runtime/runtime_esp8266.go index 290d51c7..aa698aed 100644 --- a/src/runtime/runtime_esp8266.go +++ b/src/runtime/runtime_esp8266.go @@ -51,7 +51,7 @@ func main() { run() // Fallback: if main ever returns, hang the CPU. - abort() + exit(0) } //go:extern _sbss @@ -106,6 +106,10 @@ func sleepTicks(d timeUnit) { } } +func exit(code int) { + abort() +} + func abort() { for { device.Asm("waiti 0") diff --git a/src/runtime/runtime_fe310.go b/src/runtime/runtime_fe310.go index fbc97199..2163fb0f 100644 --- a/src/runtime/runtime_fe310.go +++ b/src/runtime/runtime_fe310.go @@ -42,7 +42,7 @@ func main() { preinit() initPeripherals() run() - abort() + exit(0) } //go:extern handleInterruptASM diff --git a/src/runtime/runtime_fe310_baremetal.go b/src/runtime/runtime_fe310_baremetal.go index 4fa62e69..7750e7bb 100644 --- a/src/runtime/runtime_fe310_baremetal.go +++ b/src/runtime/runtime_fe310_baremetal.go @@ -22,6 +22,10 @@ func nanosecondsToTicks(ns int64) timeUnit { return timeUnit(ns * 64 / 1953125) } +func exit(code int) { + abort() +} + func abort() { // lock up forever for { diff --git a/src/runtime/runtime_fe310_qemu.go b/src/runtime/runtime_fe310_qemu.go index ce14abfd..98614736 100644 --- a/src/runtime/runtime_fe310_qemu.go +++ b/src/runtime/runtime_fe310_qemu.go @@ -21,6 +21,16 @@ func nanosecondsToTicks(ns int64) timeUnit { } func abort() { - // Signal a successful exit. - testExit.Set(0x5555) + exit(1) +} + +func exit(code int) { + if code == 0 { + // Signal a successful exit. + testExit.Set(0x5555) // FINISHER_PASS + } else { + // Signal a failure. The exit code is stored in the upper 16 bits of the + // 32 bit value. + testExit.Set(uint32(code)<<16 | 0x3333) // FINISHER_FAIL + } } diff --git a/src/runtime/runtime_k210.go b/src/runtime/runtime_k210.go index 8648f11c..c94153ba 100644 --- a/src/runtime/runtime_k210.go +++ b/src/runtime/runtime_k210.go @@ -48,7 +48,7 @@ func main() { preinit() initPeripherals() run() - abort() + exit(0) } func initPLIC() { diff --git a/src/runtime/runtime_k210_baremetal.go b/src/runtime/runtime_k210_baremetal.go index 0f993249..2b4f46cc 100644 --- a/src/runtime/runtime_k210_baremetal.go +++ b/src/runtime/runtime_k210_baremetal.go @@ -20,6 +20,10 @@ func nanosecondsToTicks(ns int64) timeUnit { return timeUnit(ns * 39 / 5000) } +func exit(code int) { + abort() +} + func abort() { // lock up forever for { diff --git a/src/runtime/runtime_mimxrt1062.go b/src/runtime/runtime_mimxrt1062.go index a9cc063d..6c1212cb 100644 --- a/src/runtime/runtime_mimxrt1062.go +++ b/src/runtime/runtime_mimxrt1062.go @@ -43,7 +43,7 @@ func main() { arm.EnableInterrupts(irq) run() - abort() + exit(0) } func getRamSizeConfig(itcmKB, dtcmKB uint32) uint32 { @@ -127,6 +127,10 @@ func putchar(c byte) { machine.UART1.WriteByte(c) } +func exit(code int) { + abort() +} + func abort() { for { arm.Asm("wfe") diff --git a/src/runtime/runtime_nrf.go b/src/runtime/runtime_nrf.go index 60d20bdf..551078b8 100644 --- a/src/runtime/runtime_nrf.go +++ b/src/runtime/runtime_nrf.go @@ -25,7 +25,7 @@ func main() { systemInit() preinit() run() - abort() + exit(0) } func init() { diff --git a/src/runtime/runtime_nxpmk66f18.go b/src/runtime/runtime_nxpmk66f18.go index ab985661..c54ab377 100644 --- a/src/runtime/runtime_nxpmk66f18.go +++ b/src/runtime/runtime_nxpmk66f18.go @@ -58,7 +58,7 @@ func main() { initInternal() run() - abort() + exit(0) } func initSystem() { @@ -232,6 +232,10 @@ func putchar(c byte) { machine.PutcharUART(machine.UART0, c) } +func exit(code int) { + abort() +} + func abort() { println("!!! ABORT !!!") diff --git a/src/runtime/runtime_rp2040.go b/src/runtime/runtime_rp2040.go index 7d3c9310..4d615a14 100644 --- a/src/runtime/runtime_rp2040.go +++ b/src/runtime/runtime_rp2040.go @@ -59,5 +59,5 @@ func postinit() {} func main() { preinit() run() - abort() + exit(0) } diff --git a/src/runtime/runtime_stm32.go b/src/runtime/runtime_stm32.go index 4eaafe20..08dde0dd 100644 --- a/src/runtime/runtime_stm32.go +++ b/src/runtime/runtime_stm32.go @@ -12,7 +12,7 @@ func postinit() {} func main() { preinit() run() - abort() + exit(0) } func waitForEvents() { diff --git a/src/runtime/runtime_tinygoriscv_qemu.go b/src/runtime/runtime_tinygoriscv_qemu.go index 0b443838..04e41b81 100644 --- a/src/runtime/runtime_tinygoriscv_qemu.go +++ b/src/runtime/runtime_tinygoriscv_qemu.go @@ -21,7 +21,7 @@ func postinit() {} func main() { preinit() run() - abort() + exit(0) } func ticksToNanoseconds(ticks timeUnit) int64 { @@ -49,7 +49,7 @@ var ( // UART0 output register. stdoutWrite = (*volatile.Register8)(unsafe.Pointer(uintptr(0x10000000))) // SiFive test finisher - testFinisher = (*volatile.Register16)(unsafe.Pointer(uintptr(0x100000))) + testFinisher = (*volatile.Register32)(unsafe.Pointer(uintptr(0x100000))) ) func putchar(c byte) { @@ -57,8 +57,17 @@ func putchar(c byte) { } func abort() { + exit(1) +} + +func exit(code int) { // Make sure the QEMU process exits. - testFinisher.Set(0x5555) // FINISHER_PASS + if code == 0 { + testFinisher.Set(0x5555) // FINISHER_PASS + } else { + // Exit code is stored in the upper 16 bits of the 32 bit value. + testFinisher.Set(uint32(code)<<16 | 0x3333) // FINISHER_FAIL + } // Lock up forever (as a fallback). for {