From: Ahmad Fatoum <a.fatoum@pengutronix.de>
To: barebox@lists.infradead.org
Cc: Ahmad Fatoum <a.fatoum@pengutronix.de>
Subject: [PATCH] semihosting: add semihosting_exit() and an smhexit command
Date: Mon, 21 Sep 2026 12:18:57 +0200 [thread overview]
Message-ID: <20260921101948.1007488-1-a.fatoum@pengutronix.de> (raw)
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
next reply other threads:[~2026-09-21 10:20 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-21 10:18 Ahmad Fatoum [this message]
2026-09-23 6:25 ` Sascha Hauer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260921101948.1007488-1-a.fatoum@pengutronix.de \
--to=a.fatoum@pengutronix.de \
--cc=barebox@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox