[Concept,17/18] sysreset: Add SYSRESET_TO_FIRMWARE_UI with reset -u support

Message ID 20250902152158.2285264-18-sjg@u-boot.org
State New
Headers
Series efi: Improve integration of the app with a Shim environment |

Commit Message

Simon Glass Sept. 2, 2025, 3:21 p.m. UTC
  From: Simon Glass <sjg@chromium.org>

Add new SYSRESET_TO_FIRMWARE_UI reset type to allow resetting directly
to firmware UI. This is implemented via the reset command's new -u flag.

For the EFI app, this sets the EFI_OS_INDICATIONS_BOOT_TO_FW_UI bit in
the OsIndications variable before performing a warm reset, causing the
firmware to boot to its setup interface.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
---

 cmd/boot.c                          |  1 +
 doc/usage/cmd/reset.rst             |  9 ++++++++-
 drivers/sysreset/sysreset-uclass.c  |  3 +++
 drivers/sysreset/sysreset_sandbox.c |  1 +
 include/sysreset.h                  |  2 ++
 lib/efi_client/efi_app.c            | 27 +++++++++++++++++++++++++++
 test/dm/sysreset.c                  |  2 ++
 7 files changed, 44 insertions(+), 1 deletion(-)
  

Patch

diff --git a/cmd/boot.c b/cmd/boot.c
index 832ad08c319..01b4bd76826 100644
--- a/cmd/boot.c
+++ b/cmd/boot.c
@@ -61,6 +61,7 @@  U_BOOT_CMD(
 	"Perform RESET of the CPU",
 	"- cold boot without level specifier\n"
 	"reset -h - hotreset if implemented\n"
+	"reset -u - reset to firmware UI if implemented\n"
 	"reset -w - warm reset if implemented"
 );
 
diff --git a/doc/usage/cmd/reset.rst b/doc/usage/cmd/reset.rst
index 3a43db204e5..190f3281d78 100644
--- a/doc/usage/cmd/reset.rst
+++ b/doc/usage/cmd/reset.rst
@@ -11,7 +11,7 @@  Synopsis
 
 ::
 
-    reset [-w]
+    reset [-w] [-u]
 
 Description
 -----------
@@ -26,6 +26,13 @@  DDR and peripherals, on some boards also resets external PMIC.
     Do a hot reset, if supported, which returns back to the program which
     started U-Boot.
 
+-u
+    Reset to firmware UI (EFI app only). Sets the
+    EFI_OS_INDICATIONS_BOOT_TO_FW_UI bit in the OsIndications variable and
+    performs a warm reset, causing the firmware to boot directly to its
+    setup/configuration interface.
+
+
 Return value
 ------------
 
diff --git a/drivers/sysreset/sysreset-uclass.c b/drivers/sysreset/sysreset-uclass.c
index db81c9b5779..5c9b7c6a4f2 100644
--- a/drivers/sysreset/sysreset-uclass.c
+++ b/drivers/sysreset/sysreset-uclass.c
@@ -132,6 +132,9 @@  int do_reset(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 		case 'h':
 			reset_type = SYSRESET_HOT;
 			break;
+		case 'u':
+			reset_type = SYSRESET_TO_FIRMWARE_UI;
+			break;
 		case 'w':
 			reset_type = SYSRESET_WARM;
 			break;
diff --git a/drivers/sysreset/sysreset_sandbox.c b/drivers/sysreset/sysreset_sandbox.c
index 522050eb4bf..d126fad0372 100644
--- a/drivers/sysreset/sysreset_sandbox.c
+++ b/drivers/sysreset/sysreset_sandbox.c
@@ -66,6 +66,7 @@  static int sandbox_sysreset_request(struct udevice *dev, enum sysreset_t type)
 		sandbox_exit();
 	case SYSRESET_POWER:
 	case SYSRESET_HOT:
+	case SYSRESET_TO_FIRMWARE_UI:
 		if (!state->sysreset_allowed[type])
 			return -EACCES;
 		sandbox_exit();
diff --git a/include/sysreset.h b/include/sysreset.h
index f231d4e8bc4..16a16952026 100644
--- a/include/sysreset.h
+++ b/include/sysreset.h
@@ -23,6 +23,8 @@  enum sysreset_t {
 	SYSRESET_POWER_OFF,
 	/** @SYSRESET_HOT: exit out of U-Boot (e.g. from EFI app) */
 	SYSRESET_HOT,
+	/** @SYSRESET_TO_FIRMWARE_UI: reset to firmware UI */
+	SYSRESET_TO_FIRMWARE_UI,
 
 	/** @SYSRESET_COUNT: number of available reset types */
 	SYSRESET_COUNT,
diff --git a/lib/efi_client/efi_app.c b/lib/efi_client/efi_app.c
index 88e04f1084b..1afccc8c8c7 100644
--- a/lib/efi_client/efi_app.c
+++ b/lib/efi_client/efi_app.c
@@ -16,6 +16,7 @@ 
 #include <efi.h>
 #include <efi_api.h>
 #include <efi_stub.h>
+#include <efi_variable.h>
 #include <errno.h>
 #include <fdt_simplefb.h>
 #include <image.h>
@@ -253,6 +254,32 @@  static int efi_sysreset_request(struct udevice *dev, enum sysreset_t type)
 	struct efi_priv *priv = efi_get_priv();
 
 	switch (type) {
+	case SYSRESET_TO_FIRMWARE_UI: {
+		efi_status_t ret;
+		u64 osind;
+
+		/* Read current OsIndications value */
+		osind = 0;
+		ret = efi_get_variable_int(u"OsIndications",
+					   &efi_global_variable_guid,
+					   NULL, NULL, &osind, NULL);
+		if (ret && ret != EFI_NOT_FOUND)
+			log_warning("Failed to read OsIndications: %lx\n", ret);
+
+		/* Set the boot-to-firmware-UI bit */
+		osind |= EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
+		ret = efi_set_variable_int(u"OsIndications",
+					   &efi_global_variable_guid,
+					   EFI_VARIABLE_NON_VOLATILE |
+					   EFI_VARIABLE_BOOTSERVICE_ACCESS |
+					   EFI_VARIABLE_RUNTIME_ACCESS,
+					   sizeof(osind), &osind, false);
+		if (ret) {
+			log_err("Failed to set OsIndications: %lx\n", ret);
+			return -EIO;
+		}
+		fallthrough;
+	}
 	case SYSRESET_WARM:
 		priv->run->reset_system(EFI_RESET_WARM, EFI_SUCCESS, 0, NULL);
 		break;
diff --git a/test/dm/sysreset.c b/test/dm/sysreset.c
index a30d035b9a6..0afd19ff52f 100644
--- a/test/dm/sysreset.c
+++ b/test/dm/sysreset.c
@@ -77,11 +77,13 @@  static int dm_test_sysreset_walk(struct unit_test_state *uts)
 	state->sysreset_allowed[SYSRESET_POWER] = false;
 	state->sysreset_allowed[SYSRESET_POWER_OFF] = false;
 	state->sysreset_allowed[SYSRESET_HOT] = false;
+	state->sysreset_allowed[SYSRESET_TO_FIRMWARE_UI] = false;
 	ut_asserteq(-EACCES, sysreset_walk(SYSRESET_WARM));
 	ut_asserteq(-EACCES, sysreset_walk(SYSRESET_COLD));
 	ut_asserteq(-EACCES, sysreset_walk(SYSRESET_POWER));
 	ut_asserteq(-EACCES, sysreset_walk(SYSRESET_POWER_OFF));
 	ut_asserteq(-EACCES, sysreset_walk(SYSRESET_HOT));
+	ut_asserteq(-EACCES, sysreset_walk(SYSRESET_TO_FIRMWARE_UI));
 
 	/*
 	 * Enable cold system reset - this should make cold system reset work,