mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [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

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