shim/shim-bsc1092000-fallback-menu.patch
2018-05-30 03:26:13 +00:00

195 lines
5.1 KiB
Diff

From 22269728415432718e7757842086785d7daf0cc3 Mon Sep 17 00:00:00 2001
From: Gary Lin <glin@suse.com>
Date: Mon, 28 May 2018 10:57:06 +0800
Subject: [PATCH] fallback: show a countdown menu before reset
Some machines with the faulty firmware may keep booting the default boot
path instead of the boot option we create. To avoid the infinite reset
loop, this commit introduce a countdown screen before fallback resets the
system, so the user can interrupt the system reset and choose to boot
the restored boot option. The "Always continue boot" option creates a
BS+RT+NV variable, FB_NO_REBOOT, to make fallback boot the first boot
option afterward without asking. The user can revert the behavior by
removing the variable.
https://github.com/rhboot/shim/issues/128
https://bugzilla.opensuse.org/show_bug.cgi?id=1092000
Signed-off-by: Gary Lin <glin@suse.com>
---
fallback.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 144 insertions(+)
diff --git a/fallback.c b/fallback.c
index 886e052..1f3eb78 100644
--- a/fallback.c
+++ b/fallback.c
@@ -13,6 +13,9 @@
#include "ucs2.h"
#include "variables.h"
#include "tpm.h"
+#include "console.h"
+
+#define NO_REBOOT L"FB_NO_REBOOT"
EFI_LOADED_IMAGE *this_image = NULL;
@@ -953,6 +956,127 @@ try_start_first_option(EFI_HANDLE parent_image_handle)
return rc;
}
+static UINT32
+get_fallback_no_reboot(void)
+{
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+ EFI_STATUS efi_status;
+ UINT32 no_reboot;
+ UINTN size = sizeof(UINT32);
+
+ efi_status = uefi_call_wrapper(RT->GetVariable, 5,
+ NO_REBOOT, &shim_lock_guid,
+ NULL, &size, &no_reboot);
+ if (!EFI_ERROR(efi_status)) {
+ return no_reboot;
+ }
+ return 0;
+}
+
+static EFI_STATUS
+set_fallback_no_reboot(void)
+{
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+ EFI_STATUS efi_status;
+ UINT32 no_reboot = 1;
+ efi_status = uefi_call_wrapper(RT->SetVariable, 5,
+ NO_REBOOT, &shim_lock_guid,
+ EFI_VARIABLE_NON_VOLATILE
+ | EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | EFI_VARIABLE_RUNTIME_ACCESS,
+ sizeof(UINT32), &no_reboot);
+ return efi_status;
+}
+
+static void console_save_and_set_mode (SIMPLE_TEXT_OUTPUT_MODE *SavedMode)
+{
+ if (!SavedMode) {
+ Print(L"Invalid parameter: SavedMode\n");
+ return;
+ }
+
+ CopyMem(SavedMode, ST->ConOut->Mode, sizeof(SIMPLE_TEXT_OUTPUT_MODE));
+ uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE);
+}
+
+static void console_restore_mode (SIMPLE_TEXT_OUTPUT_MODE *SavedMode)
+{
+ uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut,
+ SavedMode->CursorVisible);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut,
+ SavedMode->CursorColumn, SavedMode->CursorRow);
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
+ SavedMode->Attribute);
+}
+
+static int
+draw_countdown(void)
+{
+ SIMPLE_TEXT_OUTPUT_MODE SavedMode;
+ EFI_INPUT_KEY key;
+ EFI_STATUS status;
+ UINTN cols, rows;
+ CHAR16 *title[2];
+ CHAR16 *message = L"Press any key to stop system reset";
+ int timeout = 5, wait = 10000000;
+
+ console_save_and_set_mode (&SavedMode);
+
+ title[0] = L"Boot Option Restoration";
+ title[1] = NULL;
+
+ console_print_box_at(title, -1, 0, 0, -1, -1, 1, 1);
+
+ uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut,
+ ST->ConOut->Mode->Mode, &cols, &rows);
+
+ PrintAt((cols - StrLen(message))/2, rows/2, message);
+ while (1) {
+ if (timeout > 1)
+ PrintAt(2, rows - 3, L"Booting in %d seconds ", timeout);
+ else if (timeout)
+ PrintAt(2, rows - 3, L"Booting in %d second ", timeout);
+
+ status = WaitForSingleEvent(ST->ConIn->WaitForKey, wait);
+
+ if (status != EFI_TIMEOUT) {
+ /* Clear the key in the queue */
+ uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2,
+ ST->ConIn, &key);
+ break;
+ }
+
+ timeout--;
+ if (!timeout)
+ break;
+ }
+
+ console_restore_mode(&SavedMode);
+
+ return timeout;
+}
+
+static int
+get_user_choice(void)
+{
+ int choice;
+ CHAR16 *title[] = {L"Boot Option Restored", NULL};
+ CHAR16 *menu_strings[] = {
+ L"Reset system",
+ L"Continue boot",
+ L"Always continue boot",
+ NULL
+ };
+
+ do {
+ choice = console_select(title, menu_strings, 0);
+ } while (choice < 0 || choice > 2);
+
+ return choice;
+}
+
extern EFI_STATUS
efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab);
@@ -1014,6 +1138,26 @@ efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
VerbosePrint(L"tpm not present, starting the first image\n");
try_start_first_option(image);
} else {
+ if (get_fallback_no_reboot() == 1) {
+ VerbosePrint(L"NO_REBOOT is set, starting the first image\n");
+ try_start_first_option(image);
+ }
+
+ int timeout = draw_countdown();
+ if (timeout == 0)
+ goto reset;
+
+ int choice = get_user_choice();
+ if (choice == 0) {
+ goto reset;
+ } else if (choice == 2) {
+ rc = set_fallback_no_reboot();
+ if (EFI_ERROR(rc))
+ goto reset;
+ }
+ VerbosePrint(L"tpm present, starting the first image\n");
+ try_start_first_option(image);
+reset:
VerbosePrint(L"tpm present, resetting system\n");
}
--
2.16.3