* [PATCH] semihosting: add semihosting_exit() and an smhexit command
@ 2026-09-21 10:18 Ahmad Fatoum
2026-09-23 6:25 ` Sascha Hauer
0 siblings, 1 reply; 2+ messages in thread
From: Ahmad Fatoum @ 2026-09-21 10:18 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Give barebox running under an emulator or a debugger an easy way to
report status to whatever started it via the Semihosting SYS_EXIT
command.
Only on 64-bit AArch64/RISC-V does SYS_EXIT take a parameter block it
can carry the status in. On AArch32 that is SYS_EXIT_EXTENDED, so let's
use the latter everywhere. On the off-chance the host doesn't support
it, smhexit -p (plain) can be used instead.
Assisted-by: Claude:opus-5
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
I want to make use of this for integration tests: barebox will assert
invariants at runtime and exit with this if they fail.
---
commands/Kconfig | 9 ++++
commands/Makefile | 1 +
commands/smhexit.c | 69 +++++++++++++++++++++++++++++++
drivers/firmware/semihosting.c | 30 +++++++++++++-
include/asm-generic/semihosting.h | 2 +
5 files changed, 110 insertions(+), 1 deletion(-)
create mode 100644 commands/smhexit.c
diff --git a/commands/Kconfig b/commands/Kconfig
index c8804c8e17fe..29e574c72952 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -587,6 +587,15 @@ config CMD_RESET
Options:
-f force RESET, don't call shutdown
+config CMD_SMHEXIT
+ tristate
+ depends on SEMIHOSTING
+ prompt "smhexit"
+ help
+ End the semihosting session and hand the host an exit status.
+
+ Usage: smhexit [-pf] [STATUS]
+
config CMD_SAVES
tristate
depends on CMD_LOADS
diff --git a/commands/Makefile b/commands/Makefile
index 7f0c68a5e58e..17389465950d 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_CMD_SMC) += smc.o
obj-$(CONFIG_CMD_MSLEEP) += msleep.o
obj-$(CONFIG_CMD_RESET) += reset.o
obj-$(CONFIG_CMD_POWEROFF) += poweroff.o
+obj-$(CONFIG_CMD_SMHEXIT) += smhexit.o
obj-$(CONFIG_CMD_GO) += go.o
obj-$(CONFIG_CMD_PARTITION) += partition.o
obj-$(CONFIG_CMD_LS) += ls.o
diff --git a/commands/smhexit.c b/commands/smhexit.c
new file mode 100644
index 000000000000..b519e9399ae0
--- /dev/null
+++ b/commands/smhexit.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <barebox.h>
+#include <command.h>
+#include <complete.h>
+#include <console.h>
+#include <getopt.h>
+#include <linux/kstrtox.h>
+#include <asm/semihosting.h>
+
+static int do_smhexit(int argc, char *argv[])
+{
+ bool extended_flag = true, shutdown_flag = true;
+ int opt, status = 0;
+
+ while ((opt = getopt(argc, argv, "pf")) > 0) {
+ switch (opt) {
+ case 'p':
+ extended_flag = false;
+ break;
+ case 'f':
+ shutdown_flag = false;
+ break;
+ default:
+ return COMMAND_ERROR_USAGE;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 1)
+ return COMMAND_ERROR_USAGE;
+
+ if (argc == 1 && kstrtoint(argv[0], 0, &status))
+ return COMMAND_ERROR_USAGE;
+
+ if (shutdown_flag)
+ shutdown_barebox();
+
+ console_flush();
+ semihosting_exit(status, extended_flag);
+
+ /* Unreachable */
+ return 1;
+}
+
+BAREBOX_CMD_HELP_START(smhexit)
+BAREBOX_CMD_HELP_TEXT("Tell the debugger or emulator barebox runs under that the session")
+BAREBOX_CMD_HELP_TEXT("is over and hand it STATUS (0 if none is given) as the exit status.")
+BAREBOX_CMD_HELP_TEXT("QEMU exits with it, so a boot driven from the outside can report")
+BAREBOX_CMD_HELP_TEXT("its outcome without anything having to read the console.")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT("-p", "use the plain SYS_EXIT call for a host that has no")
+BAREBOX_CMD_HELP_OPT("", "SYS_EXIT_EXTENDED. On 32-bit, this loses STATUS.")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Without a host listening the trap instruction is undefined and the")
+BAREBOX_CMD_HELP_TEXT("machine stops instead.")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(smhexit)
+ .cmd = do_smhexit,
+ BAREBOX_CMD_DESC("end the semihosting session")
+ BAREBOX_CMD_OPTS("[-pf] [STATUS]")
+ BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+ BAREBOX_CMD_HELP(cmd_smhexit_help)
+ BAREBOX_CMD_COMPLETE(empty_complete)
+BAREBOX_CMD_END
diff --git a/drivers/firmware/semihosting.c b/drivers/firmware/semihosting.c
index 9663959aa49f..9b665dfce07b 100644
--- a/drivers/firmware/semihosting.c
+++ b/drivers/firmware/semihosting.c
@@ -37,12 +37,40 @@ enum {
SEMIHOSTING_SYS_ERRNO = 0x13,
/* SYS_GET_CMDLINE is not implemented */
/* SYS_HEAPINFO is not implemented */
- /* angel_SWIreason_ReportException is not implemented */
SEMIHOSTING_SYS_SYSTEM = 0x12,
+ SEMIHOSTING_SYS_EXIT = 0x18,
+ SEMIHOSTING_SYS_EXIT_EXTENDED = 0x20,
};
+/* the reason code of angel_SWIreason_ReportException we report */
+#define ADP_STOPPED_APPLICATION_EXIT 0x20026
+
long semihosting_trap(ulong sysnum, void *addr);
+/**
+ * semihosting_exit - end the semihosting session
+ * @status: exit status to report to the host
+ * @extended: use SYS_EXIT_EXTENDED instead of the plain SYS_EXIT
+ *
+ * Tell the debugger or the emulator on the other end that barebox is done
+ * and hand it @status. Does not return.
+ */
+void __noreturn semihosting_exit(int status, bool extended)
+{
+ ulong block[2] = { ADP_STOPPED_APPLICATION_EXIT, status };
+ void *arg = block;
+
+ if (!extended && !IS_ENABLED(CONFIG_64BIT))
+ arg = (void *)ADP_STOPPED_APPLICATION_EXIT;
+
+ semihosting_trap(extended ? SEMIHOSTING_SYS_EXIT_EXTENDED
+ : SEMIHOSTING_SYS_EXIT, arg);
+
+ /* Unreachable, above command would hang if unsupported */
+ BUG();
+}
+EXPORT_SYMBOL(semihosting_exit);
+
static long semihosting_call(ulong sysnum, void *addr)
{
long ret = semihosting_trap(sysnum, addr);
diff --git a/include/asm-generic/semihosting.h b/include/asm-generic/semihosting.h
index 67031c331b97..2f745ac8042c 100644
--- a/include/asm-generic/semihosting.h
+++ b/include/asm-generic/semihosting.h
@@ -3,6 +3,7 @@
#ifndef __ASM_GENERIC_SEMIHOSTING_H
#define __ASM_GENERIC_SEMIHOSTING_H
+#include <linux/compiler.h>
#include <linux/types.h>
int semihosting_open(const char *fname, int flags);
@@ -19,5 +20,6 @@ int semihosting_remove(const char *fname);
int semihosting_rename(const char *fname1, const char *fname2);
int semihosting_errno(void);
int semihosting_system(const char *command);
+void __noreturn semihosting_exit(int status, bool extended);
#endif
--
2.47.3
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH] semihosting: add semihosting_exit() and an smhexit command
2026-09-21 10:18 [PATCH] semihosting: add semihosting_exit() and an smhexit command Ahmad Fatoum
@ 2026-09-23 6:25 ` Sascha Hauer
0 siblings, 0 replies; 2+ messages in thread
From: Sascha Hauer @ 2026-09-23 6:25 UTC (permalink / raw)
To: Ahmad Fatoum; +Cc: barebox, Ahmad Fatoum
On 2026-09-21 12:18, Ahmad Fatoum wrote:
> +
> +static int do_smhexit(int argc, char *argv[])
> +{
> + bool extended_flag = true, shutdown_flag = true;
> + int opt, status = 0;
> +
> + while ((opt = getopt(argc, argv, "pf")) > 0) {
> + switch (opt) {
> + case 'p':
> + extended_flag = false;
> + break;
> + case 'f':
> + shutdown_flag = false;
> + break;
> + default:
> + return COMMAND_ERROR_USAGE;
> + }
> + }
> +
> + argc -= optind;
> + argv += optind;
> +
> + if (argc > 1)
> + return COMMAND_ERROR_USAGE;
> +
> + if (argc == 1 && kstrtoint(argv[0], 0, &status))
> + return COMMAND_ERROR_USAGE;
> +
> + if (shutdown_flag)
> + shutdown_barebox();
> +
> + console_flush();
> + semihosting_exit(status, extended_flag);
> +
> + /* Unreachable */
unreachable();
> + return 1;
> +}
> +
> +BAREBOX_CMD_HELP_START(smhexit)
> +BAREBOX_CMD_HELP_TEXT("Tell the debugger or emulator barebox runs under that the session")
> +BAREBOX_CMD_HELP_TEXT("is over and hand it STATUS (0 if none is given) as the exit status.")
> +BAREBOX_CMD_HELP_TEXT("QEMU exits with it, so a boot driven from the outside can report")
> +BAREBOX_CMD_HELP_TEXT("its outcome without anything having to read the console.")
> +BAREBOX_CMD_HELP_TEXT("")
> +BAREBOX_CMD_HELP_TEXT("Options:")
> +BAREBOX_CMD_HELP_OPT("-p", "use the plain SYS_EXIT call for a host that has no")
> +BAREBOX_CMD_HELP_OPT("", "SYS_EXIT_EXTENDED. On 32-bit, this loses STATUS.")
-f option is not documented.
> +BAREBOX_CMD_HELP_TEXT("")
> +BAREBOX_CMD_HELP_TEXT("Without a host listening the trap instruction is undefined and the")
> +BAREBOX_CMD_HELP_TEXT("machine stops instead.")
> +BAREBOX_CMD_HELP_END
> +
Sascha
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-23 6:27 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-21 10:18 [PATCH] semihosting: add semihosting_exit() and an smhexit command Ahmad Fatoum
2026-09-23 6:25 ` Sascha Hauer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox