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

* 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