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.
Этот коммит содержится в:
Ayke van Laethem 2021-08-14 01:41:01 +02:00 коммит произвёл Ron Evans
родитель 00c73d62ad
коммит 98f84a497d
23 изменённых файлов: 104 добавлений и 41 удалений

Просмотреть файл

@ -33,28 +33,29 @@ const (
) )
// Special codes for the Angel Semihosting interface. // Special codes for the Angel Semihosting interface.
// https://www.keil.com/support/man/docs/armcc/armcc_pge1358787050566.htm
const ( const (
// Hardware vector reason codes // Hardware vector reason codes
SemihostingBranchThroughZero = 20000 SemihostingBranchThroughZero = 0x20000
SemihostingUndefinedInstr = 20001 SemihostingUndefinedInstr = 0x20001
SemihostingSoftwareInterrupt = 20002 SemihostingSoftwareInterrupt = 0x20002
SemihostingPrefetchAbort = 20003 SemihostingPrefetchAbort = 0x20003
SemihostingDataAbort = 20004 SemihostingDataAbort = 0x20004
SemihostingAddressException = 20005 SemihostingAddressException = 0x20005
SemihostingIRQ = 20006 SemihostingIRQ = 0x20006
SemihostingFIQ = 20007 SemihostingFIQ = 0x20007
// Software reason codes // Software reason codes
SemihostingBreakPoint = 20020 SemihostingBreakPoint = 0x20020
SemihostingWatchPoint = 20021 SemihostingWatchPoint = 0x20021
SemihostingStepComplete = 20022 SemihostingStepComplete = 0x20022
SemihostingRunTimeErrorUnknown = 20023 SemihostingRunTimeErrorUnknown = 0x20023
SemihostingInternalError = 20024 SemihostingInternalError = 0x20024
SemihostingUserInterruption = 20025 SemihostingUserInterruption = 0x20025
SemihostingApplicationExit = 20026 SemihostingApplicationExit = 0x20026
SemihostingStackOverflow = 20027 SemihostingStackOverflow = 0x20027
SemihostingDivisionByZero = 20028 SemihostingDivisionByZero = 0x20028
SemihostingOSSpecific = 20029 SemihostingOSSpecific = 0x20029
) )
// Call a semihosting function. // Call a semihosting function.

Просмотреть файл

@ -48,7 +48,7 @@ func libc_free(ptr unsafe.Pointer) {
//go:linkname syscall_Exit syscall.Exit //go:linkname syscall_Exit syscall.Exit
func syscall_Exit(code int) { func syscall_Exit(code int) {
abort() exit(code)
} }
const baremetal = true const baremetal = true

Просмотреть файл

@ -75,6 +75,10 @@ func sleepTicks(d timeUnit) {
// TODO // TODO
} }
func exit(code int) {
abort()
}
func abort() { func abort() {
// TODO // TODO
for { for {

Просмотреть файл

@ -19,7 +19,7 @@ func postinit() {}
func main() { func main() {
preinit() preinit()
run() run()
abort() exit(0)
} }
func init() { func init() {

Просмотреть файл

@ -19,7 +19,7 @@ func main() {
arm.SCB.CPACR.Set(0) // disable FPU if it is enabled arm.SCB.CPACR.Set(0) // disable FPU if it is enabled
preinit() preinit()
run() run()
abort() exit(0)
} }
func init() { func init() {

Просмотреть файл

@ -37,7 +37,7 @@ var _ebss [0]byte
func main() { func main() {
preinit() preinit()
run() run()
abort() exit(0)
} }
func preinit() { func preinit() {
@ -84,6 +84,10 @@ func ticks() timeUnit {
return currentTime return currentTime
} }
func exit(code int) {
abort()
}
func abort() { func abort() {
for { for {
sleepWDT(WDT_PERIOD_2S) sleepWDT(WDT_PERIOD_2S)

Просмотреть файл

@ -6,6 +6,10 @@ import (
"device/arm" "device/arm"
) )
func exit(code int) {
abort()
}
func abort() { func abort() {
// lock up forever // lock up forever
for { for {

Просмотреть файл

@ -23,8 +23,7 @@ func main() {
run() run()
// Signal successful exit. // Signal successful exit.
arm.SemihostingCall(arm.SemihostingReportException, arm.SemihostingApplicationExit) exit(0)
abort()
} }
func ticksToNanoseconds(ticks timeUnit) int64 { func ticksToNanoseconds(ticks timeUnit) int64 {
@ -56,8 +55,16 @@ func waitForEvents() {
} }
func abort() { func abort() {
// Signal an abnormal exit. exit(1)
arm.SemihostingCall(arm.SemihostingReportException, arm.SemihostingRunTimeErrorUnknown) }
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). // Lock up forever (should be unreachable).
for { for {

Просмотреть файл

@ -51,7 +51,7 @@ func main() {
run() run()
// Fallback: if main ever returns, hang the CPU. // Fallback: if main ever returns, hang the CPU.
abort() exit(0)
} }
//go:extern _sbss //go:extern _sbss

Просмотреть файл

@ -54,7 +54,7 @@ func main() {
run() run()
// Fallback: if main ever returns, hang the CPU. // Fallback: if main ever returns, hang the CPU.
abort() exit(0)
} }
func abort() { func abort() {

Просмотреть файл

@ -67,3 +67,7 @@ func sleepTicks(d timeUnit) {
// TODO: suspend the CPU to not burn power here unnecessarily. // TODO: suspend the CPU to not burn power here unnecessarily.
} }
} }
func exit(code int) {
abort()
}

Просмотреть файл

@ -51,7 +51,7 @@ func main() {
run() run()
// Fallback: if main ever returns, hang the CPU. // Fallback: if main ever returns, hang the CPU.
abort() exit(0)
} }
//go:extern _sbss //go:extern _sbss
@ -106,6 +106,10 @@ func sleepTicks(d timeUnit) {
} }
} }
func exit(code int) {
abort()
}
func abort() { func abort() {
for { for {
device.Asm("waiti 0") device.Asm("waiti 0")

Просмотреть файл

@ -42,7 +42,7 @@ func main() {
preinit() preinit()
initPeripherals() initPeripherals()
run() run()
abort() exit(0)
} }
//go:extern handleInterruptASM //go:extern handleInterruptASM

Просмотреть файл

@ -22,6 +22,10 @@ func nanosecondsToTicks(ns int64) timeUnit {
return timeUnit(ns * 64 / 1953125) return timeUnit(ns * 64 / 1953125)
} }
func exit(code int) {
abort()
}
func abort() { func abort() {
// lock up forever // lock up forever
for { for {

Просмотреть файл

@ -21,6 +21,16 @@ func nanosecondsToTicks(ns int64) timeUnit {
} }
func abort() { func abort() {
// Signal a successful exit. exit(1)
testExit.Set(0x5555) }
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
}
} }

Просмотреть файл

@ -48,7 +48,7 @@ func main() {
preinit() preinit()
initPeripherals() initPeripherals()
run() run()
abort() exit(0)
} }
func initPLIC() { func initPLIC() {

Просмотреть файл

@ -20,6 +20,10 @@ func nanosecondsToTicks(ns int64) timeUnit {
return timeUnit(ns * 39 / 5000) return timeUnit(ns * 39 / 5000)
} }
func exit(code int) {
abort()
}
func abort() { func abort() {
// lock up forever // lock up forever
for { for {

Просмотреть файл

@ -43,7 +43,7 @@ func main() {
arm.EnableInterrupts(irq) arm.EnableInterrupts(irq)
run() run()
abort() exit(0)
} }
func getRamSizeConfig(itcmKB, dtcmKB uint32) uint32 { func getRamSizeConfig(itcmKB, dtcmKB uint32) uint32 {
@ -127,6 +127,10 @@ func putchar(c byte) {
machine.UART1.WriteByte(c) machine.UART1.WriteByte(c)
} }
func exit(code int) {
abort()
}
func abort() { func abort() {
for { for {
arm.Asm("wfe") arm.Asm("wfe")

Просмотреть файл

@ -25,7 +25,7 @@ func main() {
systemInit() systemInit()
preinit() preinit()
run() run()
abort() exit(0)
} }
func init() { func init() {

Просмотреть файл

@ -58,7 +58,7 @@ func main() {
initInternal() initInternal()
run() run()
abort() exit(0)
} }
func initSystem() { func initSystem() {
@ -232,6 +232,10 @@ func putchar(c byte) {
machine.PutcharUART(machine.UART0, c) machine.PutcharUART(machine.UART0, c)
} }
func exit(code int) {
abort()
}
func abort() { func abort() {
println("!!! ABORT !!!") println("!!! ABORT !!!")

Просмотреть файл

@ -59,5 +59,5 @@ func postinit() {}
func main() { func main() {
preinit() preinit()
run() run()
abort() exit(0)
} }

Просмотреть файл

@ -12,7 +12,7 @@ func postinit() {}
func main() { func main() {
preinit() preinit()
run() run()
abort() exit(0)
} }
func waitForEvents() { func waitForEvents() {

Просмотреть файл

@ -21,7 +21,7 @@ func postinit() {}
func main() { func main() {
preinit() preinit()
run() run()
abort() exit(0)
} }
func ticksToNanoseconds(ticks timeUnit) int64 { func ticksToNanoseconds(ticks timeUnit) int64 {
@ -49,7 +49,7 @@ var (
// UART0 output register. // UART0 output register.
stdoutWrite = (*volatile.Register8)(unsafe.Pointer(uintptr(0x10000000))) stdoutWrite = (*volatile.Register8)(unsafe.Pointer(uintptr(0x10000000)))
// SiFive test finisher // SiFive test finisher
testFinisher = (*volatile.Register16)(unsafe.Pointer(uintptr(0x100000))) testFinisher = (*volatile.Register32)(unsafe.Pointer(uintptr(0x100000)))
) )
func putchar(c byte) { func putchar(c byte) {
@ -57,8 +57,17 @@ func putchar(c byte) {
} }
func abort() { func abort() {
exit(1)
}
func exit(code int) {
// Make sure the QEMU process exits. // 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). // Lock up forever (as a fallback).
for { for {