:: commit 25686280bc3bc64cf243546ec6d95f64dfc8d429

48cf <32851089+48cf@users.noreply.github.com> — 2023-11-24 00:47

parents: 6374a0027b

menu: Support rebooting to UEFI firmware setup. Closes #299

diff --git a/common/console.c b/common/console.c
index ed74252e..d028a645 100644
--- a/common/console.c
+++ b/common/console.c
@@ -21,6 +21,7 @@ static void console_help(void) {
         "lsvol     -- List volumes.\n"
         "firmware  -- Show firmware type.\n"
 #if defined (UEFI)
+        "setup     -- Reboot to firmware setup.\n"
         "slide     -- Print load slide offset.\n"
 #endif
         "version   -- Print version.\n"
@@ -66,6 +67,12 @@ void console(void) {
             print("unknown\n");
 #endif
 #if defined (UEFI)
+        } else if (strcmp(prompt, "setup") == 0) {
+            if (reboot_to_fw_ui_supported()) {
+                reboot_to_fw_ui();
+            } else {
+                print("Your firmware does not support rebooting to the firmware UI.\n");
+            }
         } else if (strcmp(prompt, "slide") == 0) {
             print("%p\n", __slide);
 #endif
diff --git a/common/menu.c b/common/menu.c
index 810ad5e8..f9d03da0 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -628,6 +628,50 @@ static void menu_init_term(void) {
     }
 }
 
+#if defined(UEFI)
+bool reboot_to_fw_ui_supported(void) {
+    uint64_t os_indications_supported;
+    UINTN size = sizeof(os_indications_supported);
+    EFI_GUID global_variable = EFI_GLOBAL_VARIABLE;
+    EFI_STATUS status = gRT->GetVariable(L"OsIndicationsSupported", &global_variable, NULL, &size, &os_indications_supported);
+    if (status == EFI_SUCCESS && size == sizeof(os_indications_supported)) {
+        return (os_indications_supported & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) != 0;
+    }
+    return false;
+}
+
+noreturn void reboot_to_fw_ui(void) {
+    reset_term();
+    print("Rebooting to the firmware setup...\n");
+
+    uint64_t os_indications;
+    UINTN size = sizeof(os_indications);
+    EFI_GUID global_variable = EFI_GLOBAL_VARIABLE;
+    EFI_STATUS status = gRT->GetVariable(L"OsIndications", &global_variable, NULL, &size, &os_indications);
+    if (status != EFI_SUCCESS || size != sizeof(os_indications)) {
+        if (status == EFI_NOT_FOUND) {
+            os_indications = 0;
+            goto not_found;
+        }
+
+        panic(true, "Failed to get OsIndications variable, status=%X", status);
+    }
+
+not_found:;
+    uint64_t new_os_indications = os_indications | EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
+    status = gRT->SetVariable(L"OsIndications", &global_variable,
+        EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+        sizeof(new_os_indications), &new_os_indications);
+
+    if (status != EFI_SUCCESS) {
+        panic(true, "Failed to set OsIndications variable, status=%X", status);
+    }
+
+    gRT->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, NULL);
+    panic(true, "Failed to reboot to firmware UI");
+}
+#endif
+
 noreturn void _menu(bool first_run) {
     size_t data_size = (uintptr_t)data_end - (uintptr_t)data_begin;
 #if defined (BIOS)
@@ -739,6 +783,10 @@ noreturn void _menu(bool first_run) {
             timeout = strtoui(timeout_config, NULL, 10);
     }
 
+#if defined(UEFI)
+    bool reboot_to_firmware_supported = reboot_to_fw_ui_supported();
+#endif
+
     if (!first_run) {
         skip_timeout = true;
     }
@@ -831,6 +879,12 @@ refresh:
                 print("    \e[32mARROWS\e[0m Select    \e[32mENTER\e[0m %s",
                       selected_menu_entry->expanded ? "Collapse" : "Expand");
             }
+#if defined(UEFI)
+            if (reboot_to_firmware_supported) {
+                set_cursor_pos_helper(terms[0]->cols - 33, 3);
+                print("\e[32mS\e[0m Firmware Setup");
+            }
+#endif
             set_cursor_pos_helper(terms[0]->cols - 13, 3);
             print("\e[32mC\e[0m Console");
         }
@@ -941,6 +995,15 @@ timeout_aborted:
                 }
                 break;
             }
+#if defined(UEFI)
+            case 's':
+            case 'S': {
+                if (reboot_to_firmware_supported) {
+                    reboot_to_fw_ui();
+                }
+                break;
+            }
+#endif
             case 'c':
             case 'C': {
                 reset_term();
diff --git a/common/menu.h b/common/menu.h
index 35a3ff73..e23094b3 100644
--- a/common/menu.h
+++ b/common/menu.h
@@ -4,6 +4,11 @@
 #include <stdbool.h>
 #include <stdnoreturn.h>
 
+#if defined(UEFI)
+bool reboot_to_fw_ui_supported(void);
+noreturn void reboot_to_fw_ui(void);
+#endif
+
 noreturn void menu(bool first_run);
 
 noreturn void boot(char *config);
tab: 248 wrap: offon