diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | firmware/lib/include/vboot_kernel.h | 10 | ||||
-rw-r--r-- | firmware/lib/vboot_ui_menu.c | 814 |
3 files changed, 825 insertions, 0 deletions
@@ -340,6 +340,7 @@ VBSLK_SRCS = \ firmware/lib/vboot_display.c \ firmware/lib/vboot_kernel.c \ firmware/lib/vboot_ui.c \ + firmware/lib/vboot_ui_menu.c \ firmware/lib/region-kernel.c \ # Code common to both vboot 2.0 (old structs) and 2.1 (new structs) diff --git a/firmware/lib/include/vboot_kernel.h b/firmware/lib/include/vboot_kernel.h index f117fa77..2195e0cf 100644 --- a/firmware/lib/include/vboot_kernel.h +++ b/firmware/lib/include/vboot_kernel.h @@ -73,6 +73,16 @@ VbError_t VbBootDeveloper(struct vb2_context *ctx, VbCommonParams *cparams); VbError_t VbBootRecovery(struct vb2_context *ctx, VbCommonParams *cparams); /** + * Handle a developer-mode boot using detachable menu ui + */ +VbError_t VbBootDeveloperMenu(struct vb2_context *ctx, VbCommonParams *cparams); + +/** + * Handle a recovery-mode boot using detachable menu ui + */ +VbError_t VbBootRecoveryMenu(struct vb2_context *ctx, VbCommonParams *cparams); + +/** * Return the current FWMP flags. Valid only inside VbSelectAndLoadKernel(). */ uint32_t vb2_get_fwmp_flags(void); diff --git a/firmware/lib/vboot_ui_menu.c b/firmware/lib/vboot_ui_menu.c new file mode 100644 index 00000000..7fa44edd --- /dev/null +++ b/firmware/lib/vboot_ui_menu.c @@ -0,0 +1,814 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * High-level firmware wrapper API - user interface for RW firmware + */ + +#include "2sysincludes.h" +#include "2common.h" +#include "2misc.h" +#include "2nvstorage.h" +#include "2rsa.h" +#include "ec_sync.h" +#include "gbb_access.h" +#include "gbb_header.h" +#include "load_kernel_fw.h" +#include "region.h" +#include "rollback_index.h" +#include "utility.h" +#include "vb2_common.h" +#include "vboot_api.h" +#include "vboot_audio.h" +#include "vboot_common.h" +#include "vboot_display.h" +#include "vboot_kernel.h" +#include "vboot_nvstorage.h" + +static void VbAllowUsbBootMenu(struct vb2_context *ctx) +{ + VB2_DEBUG("%s\n", __func__); + vb2_nv_set(ctx, VB2_NV_DEV_BOOT_USB, 1); +} + +/** + * Checks GBB flags against VbExIsShutdownRequested() shutdown request to + * determine if a shutdown is required. + * + * Returns true if a shutdown is required and false if no shutdown is required. + */ +static int VbWantShutdownMenu(uint32_t gbb_flags) +{ + uint32_t shutdown_request = VbExIsShutdownRequested(); + + /* If desired, ignore shutdown request due to lid closure. */ + if (gbb_flags & GBB_FLAG_DISABLE_LID_SHUTDOWN) + shutdown_request &= ~VB_SHUTDOWN_REQUEST_LID_CLOSED; + + return !!shutdown_request; +} + +static void VbTryLegacyMenu(int allowed) +{ + if (!allowed) + VB2_DEBUG("VbBootDeveloperMenu() - Legacy boot is disabled\n"); + else if (0 != RollbackKernelLock(0)) + VB2_DEBUG("Error locking kernel versions on legacy boot.\n"); + else + VbExLegacy(); /* will not return if successful */ + + /* If legacy boot fails, beep and return to calling UI loop. */ + VbExBeep(120, 400); + VbExSleepMs(120); + VbExBeep(120, 400); +} + +uint32_t VbTryUsbMenu(struct vb2_context *ctx, VbCommonParams *cparams) +{ + uint32_t retval = VbTryLoadKernel(ctx, cparams, VB_DISK_FLAG_REMOVABLE); + if (VBERROR_SUCCESS == retval) { + VB2_DEBUG("VbBootDeveloperMenu() - booting USB\n"); + } else { + VB2_DEBUG("VbBootDeveloperMenu() - no kernel found on USB\n"); + VbExBeep(250, 200); + VbExSleepMs(120); + /* + * Clear recovery requests from failed + * kernel loading, so that powering off + * at this point doesn't put us into + * recovery mode. + */ + vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, + VBNV_RECOVERY_NOT_REQUESTED); + } + return retval; +} + +#define CONFIRM_KEY_DELAY 20 /* Check confirm screen keys every 20ms */ + +int VbUserConfirmsMenu(struct vb2_context *ctx, VbCommonParams *cparams, + uint32_t confirm_flags) +{ + VbSharedDataHeader *shared = + (VbSharedDataHeader *)cparams->shared_data_blob; + uint32_t key; + uint32_t key_flags; + uint32_t button; + int rec_button_was_pressed = 0; + + VB2_DEBUG("Entering %s(0x%x)\n", __func__, confirm_flags); + + /* Await further instructions */ + while (1) { + if (VbWantShutdownMenu(cparams->gbb->flags)) + return -1; + key = VbExKeyboardReadWithFlags(&key_flags); + button = VbExGetSwitches(VB_INIT_FLAG_REC_BUTTON_PRESSED); + switch (key) { + case '\r': + /* If we require a trusted keyboard for confirmation, + * but the keyboard may be faked (for instance, a USB + * device), beep and keep waiting. + */ + if (confirm_flags & VB_CONFIRM_MUST_TRUST_KEYBOARD && + !(key_flags & VB_KEY_FLAG_TRUSTED_KEYBOARD)) { + VbExBeep(120, 400); + break; + } + + VB2_DEBUG("%s() - Yes (1)\n", __func__); + return 1; + break; + case ' ': + VB2_DEBUG("%s() - Space (%d)\n", __func__, + confirm_flags & VB_CONFIRM_SPACE_MEANS_NO); + if (confirm_flags & VB_CONFIRM_SPACE_MEANS_NO) + return 0; + break; + case 0x1b: + VB2_DEBUG("%s() - No (0)\n", __func__); + return 0; + break; + default: + /* If the recovery button is physical, and is pressed, + * this is also a YES, but must wait for release. + */ + if (!(shared->flags & VBSD_BOOT_REC_SWITCH_VIRTUAL)) { + if (button) { + VB2_DEBUG("%s() - Rec button pressed\n", + __func__); + rec_button_was_pressed = 1; + } else if (rec_button_was_pressed) { + VB2_DEBUG("%s() - Rec button (1)\n", + __func__); + return 1; + } + } + VbCheckDisplayKey(ctx, cparams, key); + } + VbExSleepMs(CONFIRM_KEY_DELAY); + } + + /* Not reached, but compiler will complain without it */ + return -1; +} + +static const char dev_disable_msg[] = + "Developer mode is disabled on this device by system policy.\n" + "For more information, see http://dev.chromium.org/chromium-os/fwmp\n" + "\n"; + +typedef enum _VB_MENU { + VB_MENU_DEV_WARNING, + VB_MENU_DEV, + VB_MENU_TO_NORM, + VB_MENU_COUNT, +} VB_MENU; + +typedef enum _VB_DEV_WARNING_MENU { + VB_WARN_OPTIONS, + VB_WARN_DBG_INFO, + VB_WARN_ENABLE_VER, + VB_WARN_POWER_OFF, + VB_WARN_LANGUAGE, + VB_WARN_COUNT, +} VB_DEV_WARNING_MENU; + +typedef enum _VB_DEV_MENU { + VB_DEV_NETWORK, + VB_DEV_LEGACY, + VB_DEV_USB, + VB_DEV_DISK, + VB_DEV_CANCEL, + VB_DEV_POWER_OFF, + VB_DEV_LANGUAGE, + VB_DEV_COUNT, +} VB_DEV_MENU; + +typedef enum _VB_TO_NORM_MENU { + VB_TO_NORM_CONFIRM, + VB_TO_NORM_CANCEL, + VB_TO_NORM_POWER_OFF, + VB_TO_NORM_LANGUAGE, + VB_TO_NORM_COUNT, +} VB_TO_NORM_MENU; + +static VB_MENU current_menu = VB_MENU_DEV_WARNING; +static int current_menu_idx = 0; +static int selected = 0; + +// TODO: add in consts +static char *dev_warning_menu[] = { + "Developer Options\n", + "Show Debug Info\n", + "Enable Root Verification\n", + "Power Off\n", + "Language\n" +}; + +static char *dev_menu[] = { + "Boot Network Image\n", + "Boot Legacy BIOS\n", + "Boot USB Image\n", + "Boot Developer Image\n", + "Cancel\n", + "Power Off\n", + "Language\n" +}; + +static char *to_normal_menu[] = { + "Confirm Enabling Verified Boot\n", + "Cancel\n", + "Power Off\n", + "Language\n" +}; + +// function that gets the current menu string array and size. +// can set menu_array to NULL and only return string size. +VbError_t vb2_get_current_menu_size(VB_MENU menu, char ***menu_array, int *size) +{ + char **temp_menu; + + switch(menu) { + case VB_MENU_DEV_WARNING: + *size = VB_WARN_COUNT; + temp_menu = dev_warning_menu; + break; + case VB_MENU_DEV: + *size = VB_DEV_COUNT; + temp_menu = dev_menu; + break; + case VB_MENU_TO_NORM: + *size = VB_TO_NORM_COUNT; + temp_menu = to_normal_menu; + break; + default: + *size = 0; + return VBERROR_UNKNOWN; + } + *menu_array = temp_menu; + + return VBERROR_SUCCESS; +} + +// TODO: will probably have to print menu a +// line at a time to center the text at X. +// Otherwise, only the first line will be lined up +// vertically properly. +// which is why x is currently 0. +// also, want to calculate what center is eventually instead of +// hard-coding it. +// at least right now there's no overlapping with the debug +// printouts. +VbError_t vb2_print_current_menu() +{ + // create menu string + char m_str[1024]; + const char *selected = "==> "; + const char *deselected = " "; + int size = 0; + int i = 0; + static char **m = NULL; + + memset(m_str, 0, strlen(m_str)); + // TODO: need to check for error code. + vb2_get_current_menu_size(current_menu, &m, &size); + VB2_DEBUG("vb2_print_current_menu:\n"); + + for (i = 0; i < size; i++) { + if (current_menu_idx == i) { + // add selection to indicate current selection + strncat(m_str, selected, strlen(selected)); + } else { + strncat(m_str, deselected, strlen(deselected)); + } + strncat(m_str, m[i], strlen(m[i])); + } + VB2_DEBUG("%s", m_str); + + return VbExDisplayText(0,0,m_str); +} + +// This updates current_menu and current_menu_idx, +// (as necessary) +// which are used to determine what to do. +VbError_t vb2_update_menu() +{ + VbError_t ret = VBERROR_SUCCESS; + switch(current_menu) { + case VB_MENU_DEV_WARNING: + switch(current_menu_idx) { + case VB_WARN_OPTIONS: + // select dev menu + current_menu = VB_MENU_DEV; + current_menu_idx = 0; + selected = 0; + break; + case VB_WARN_DBG_INFO: + // show debug info + break; + case VB_WARN_ENABLE_VER: + // enable boot verification + current_menu = VB_MENU_TO_NORM; + current_menu_idx = 0; + selected = 0; + break; + case VB_WARN_POWER_OFF: + // power off machine + ret = VBERROR_SHUTDOWN_REQUESTED; + break; + case VB_WARN_LANGUAGE: + // Languages + // we'll have to figure out how to display this + break; + default: + // invalid menu item. Don't update anything. + break; + } + break; + case VB_MENU_DEV: + switch(current_menu_idx) { + case VB_DEV_NETWORK: + // boot network image + break; + case VB_DEV_LEGACY: + // boot legacy BIOS + break; + case VB_DEV_USB: + // book USB image + break; + case VB_DEV_DISK: + // boot developer image + break; + case VB_DEV_CANCEL: + // cancel (go back to developer warning menu) + current_menu = VB_MENU_DEV_WARNING; + current_menu_idx = 0; + selected = 0; + break; + case VB_DEV_POWER_OFF: + // power off + ret = VBERROR_SHUTDOWN_REQUESTED; + break; + case VB_DEV_LANGUAGE: + // Language + break; + default: + // invalid menu item. don't update anything. + break; + } + break; + case VB_MENU_TO_NORM: + switch(current_menu_idx) { + case VB_TO_NORM_CONFIRM: + // confirm enabling verified boot + break; + case VB_TO_NORM_CANCEL: + // cancel (go back to developer warning menu) + current_menu = VB_MENU_DEV_WARNING; + current_menu_idx = 0; + selected = 0; + break; + case VB_TO_NORM_POWER_OFF: + // power off + ret = VBERROR_SHUTDOWN_REQUESTED; + break; + case VB_TO_NORM_LANGUAGE: + // Language + break; + default: + // invalid menu item. don't update anything + break; + } + break; + default: + VB2_DEBUG("Current Menu Invalid!"); + } + return ret; +} + +VbError_t vb2_developer_menu(struct vb2_context *ctx, VbCommonParams *cparams) +{ + GoogleBinaryBlockHeader *gbb = cparams->gbb; +#if defined(VBOOT_DEBUG) + VbSharedDataHeader *shared = + (VbSharedDataHeader *)cparams->shared_data_blob; +#endif + + uint32_t disable_dev_boot = 0; + uint32_t use_usb = 0; + uint32_t use_legacy = 0; + uint32_t ctrl_d_pressed = 0; + + VbAudioContext *audio = 0; + VbError_t ret; + + VB2_DEBUG("Entering %s()\n", __func__); + + /* Check if USB booting is allowed */ + uint32_t allow_usb = vb2_nv_get(ctx, VB2_NV_DEV_BOOT_USB); + uint32_t allow_legacy = vb2_nv_get(ctx, VB2_NV_DEV_BOOT_LEGACY); + + /* Check if the default is to boot using disk, usb, or legacy */ + uint32_t default_boot = vb2_nv_get(ctx, VB2_NV_DEV_DEFAULT_BOOT); + + if(default_boot == VBNV_DEV_DEFAULT_BOOT_USB) + use_usb = 1; + if(default_boot == VBNV_DEV_DEFAULT_BOOT_LEGACY) + use_legacy = 1; + + /* Handle GBB flag override */ + if (gbb->flags & GBB_FLAG_FORCE_DEV_BOOT_USB) + allow_usb = 1; + if (gbb->flags & GBB_FLAG_FORCE_DEV_BOOT_LEGACY) + allow_legacy = 1; + if (gbb->flags & GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY) { + use_legacy = 1; + use_usb = 0; + } + + /* Handle FWMP override */ + uint32_t fwmp_flags = vb2_get_fwmp_flags(); + if (fwmp_flags & FWMP_DEV_ENABLE_USB) + allow_usb = 1; + if (fwmp_flags & FWMP_DEV_ENABLE_LEGACY) + allow_legacy = 1; + if (fwmp_flags & FWMP_DEV_DISABLE_BOOT) { + if (gbb->flags & GBB_FLAG_FORCE_DEV_SWITCH_ON) { + VB2_DEBUG("%s() - FWMP_DEV_DISABLE_BOOT rejected by" + "FORCE_DEV_SWITCH_ON\n", + __func__); + } else { + disable_dev_boot = 1; + } + } + + /* If dev mode is disabled, only allow TONORM */ + while (disable_dev_boot) { + VB2_DEBUG("%s() - dev_disable_boot is set.\n", __func__); + VbDisplayScreen(ctx, cparams, VB_SCREEN_DEVELOPER_TO_NORM, 0); + VbExDisplayDebugInfo(dev_disable_msg); + + /* Ignore space in VbUserConfirmsMenu()... */ + switch (VbUserConfirmsMenu(ctx, cparams, 0)) { + case 1: + VB2_DEBUG("%s() - leaving dev-mode.\n", __func__); + vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST, 1); + VbDisplayScreen(ctx, cparams, + VB_SCREEN_TO_NORM_CONFIRMED, + 0); + VbExSleepMs(5000); + return VBERROR_REBOOT_REQUIRED; + case -1: + VB2_DEBUG("%s() - shutdown requested\n", __func__); + return VBERROR_SHUTDOWN_REQUESTED; + default: + /* Ignore user attempt to cancel */ + VB2_DEBUG("%s() - ignore cancel TONORM\n", __func__); + } + } + + /* Show the dev mode warning screen */ + /* TODO: change this to blank screen? */ + VbDisplayScreen(ctx, cparams, VB_SCREEN_DEVELOPER_WARNING, 0); + vb2_print_current_menu(); + + /* Get audio/delay context */ + audio = VbAudioOpen(cparams); + + /* We'll loop until we finish the delay or are interrupted */ + do { + uint32_t key; + int menu_size; + + if (VbWantShutdownMenu(gbb->flags)) { + VB2_DEBUG("shutdown requested!\n"); + VbAudioClose(audio); + return VBERROR_SHUTDOWN_REQUESTED; + } + + key = VbExKeyboardRead(); + switch (key) { + case 0: + /* nothing pressed */ + break; + case '\r': + /* Only disable virtual dev switch if allowed by GBB */ + if (!(gbb->flags & GBB_FLAG_ENTER_TRIGGERS_TONORM)) + break; + case VB_KEY_UP: + vb2_get_current_menu_size(current_menu, + NULL, &menu_size); + current_menu_idx = (current_menu_idx+menu_size-1) % + menu_size; + vb2_print_current_menu(); + break; + case VB_KEY_DOWN: + vb2_get_current_menu_size(current_menu, + NULL, &menu_size); + current_menu_idx = (current_menu_idx+1) % menu_size; + VbDisplayScreen(ctx, cparams, VB_SCREEN_DEVELOPER_WARNING, 0); + vb2_print_current_menu(); + break; + case VB_KEY_RIGHT: + // temporarily using this as a stand in for power button + // until get power button bypassed + selected = 1; + + ret = vb2_update_menu(); + // unfortunately, we need the blanking to get rid of + // artifacts from previous menu printing. + VbDisplayScreen(ctx, cparams, VB_SCREEN_BLANK, 0); + VbDisplayScreen(ctx, cparams, + VB_SCREEN_DEVELOPER_WARNING, 0); + vb2_print_current_menu(); + + // probably shutting down + if (ret != VBERROR_SUCCESS) { + VB2_DEBUG("VbBootDeveloperMenu() - shutting down!\n"); + return ret; + } + + // nothing selected, skip everything else. + if (selected == 0) + break; + + // below is all the selection actions + // Display debug information = tab on most chromebooks + if (current_menu == VB_MENU_DEV_WARNING && + current_menu_idx == VB_WARN_DBG_INFO) { + VbDisplayDebugInfo(ctx, cparams); + } + + // Ctrl+L = legacy mode + if (current_menu == VB_MENU_DEV && + current_menu_idx == VB_DEV_LEGACY) { + VB2_DEBUG("VbBootDeveloperMenu() - " + "user pressed Ctrl+L; " + "Try legacy boot\n"); + VbTryLegacyMenu(allow_legacy); + } + + // Ctrl+U = try USB boot, or beep if failure + if (current_menu == VB_MENU_DEV && + current_menu_idx == VB_DEV_USB) { + VB2_DEBUG("VbBootDeveloperMenu() - " + "user pressed Ctrl+U; try USB\n"); + if (!allow_usb) { + VB2_DEBUG("VbBootDeveloperMenu() - " + "USB booting is disabled\n"); + VbExDisplayDebugInfo( + "WARNING: Booting from external media " + "(USB/SD) has not been enabled. Refer " + "to the developer-mode documentation " + "for details.\n"); + VbExBeep(120, 400); + VbExSleepMs(120); + VbExBeep(120, 400); + } else { + // Clear the screen to show we get the + // Ctrl+U key press. + VbDisplayScreen(ctx, + cparams, VB_SCREEN_BLANK, 0); + if (VBERROR_SUCCESS == + VbTryUsbMenu(ctx, cparams)) { + VbAudioClose(audio); + return VBERROR_SUCCESS; + } else { + // Show dev mode warning screen + // again + VbDisplayScreen(ctx, + cparams, + VB_SCREEN_DEVELOPER_WARNING, + 0); + } + } + } + + /* Ctrl+D = dismiss warning; advance to timeout */ + if (current_menu == VB_MENU_DEV && + current_menu_idx == VB_DEV_DISK) { + VB2_DEBUG("VbBootDeveloperMenu() - " + "user pressed Ctrl+D; skip delay\n"); + ctrl_d_pressed = 1; + goto fallout; + } + + /* enabling verified boot */ + if (current_menu == VB_MENU_TO_NORM && + current_menu_idx == VB_TO_NORM_CONFIRM) { + // See if we should disable virtual dev-mode + // switch. + VB2_DEBUG("%s shared->flags=0x%x\n", + __func__, shared->flags); + // Ignore space in VbUserConfirmsMenu()... + VB2_DEBUG("%s() - leaving dev-mode.\n", + __func__); + vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST, + 1); + VbDisplayScreen(ctx, + cparams, + VB_SCREEN_TO_NORM_CONFIRMED, + 0); + VbExSleepMs(5000); + return VBERROR_REBOOT_REQUIRED; + } + break; + default: + VB2_DEBUG("VbBootDeveloperMenu() - pressed key %d\n", + key); + VbCheckDisplayKey(ctx, cparams, key); + break; + } + } while(VbAudioLooping(audio)); + +fallout: + + /* If defaulting to legacy boot, try that unless Ctrl+D was pressed */ + if (use_legacy && !ctrl_d_pressed) { + VB2_DEBUG("VbBootDeveloperMenu() - defaulting to legacy\n"); + VbTryLegacyMenu(allow_legacy); + } + + if ((use_usb && !ctrl_d_pressed) && allow_usb) { + if (VBERROR_SUCCESS == VbTryUsbMenu(ctx, cparams)) { + VbAudioClose(audio); + return VBERROR_SUCCESS; + } + } + + /* Timeout or Ctrl+D; attempt loading from fixed disk */ + VB2_DEBUG("VbBootDeveloperMenu() - trying fixed disk\n"); + VbAudioClose(audio); + return VbTryLoadKernel(ctx, cparams, VB_DISK_FLAG_FIXED); +} + +VbError_t VbBootDeveloperMenu(struct vb2_context *ctx, VbCommonParams *cparams) +{ + VbError_t retval = vb2_developer_menu(ctx, cparams); + VbDisplayScreen(ctx, cparams, VB_SCREEN_BLANK, 0); + return retval; +} + +/* Delay in recovery mode */ +#define REC_DISK_DELAY 1000 /* Check disks every 1s */ +#define REC_KEY_DELAY 20 /* Check keys every 20ms */ +#define REC_MEDIA_INIT_DELAY 500 /* Check removable media every 500ms */ + +VbError_t vb2_recovery_menu(struct vb2_context *ctx, VbCommonParams *cparams) +{ + VbSharedDataHeader *shared = + (VbSharedDataHeader *)cparams->shared_data_blob; + uint32_t retval; + uint32_t key; + int i; + + VBDEBUG(("VbBootRecoveryMenu() start\n")); + + /* + * If the dev-mode switch is off and the user didn't press the recovery + * button (recovery was triggerred automatically), show 'broken' screen. + * The user can either only shutdown to abort or hit esc+refresh+power + * to initiate recovery as instructed on the screen. + */ + if (!(shared->flags & VBSD_BOOT_DEV_SWITCH_ON) && + !(shared->flags & VBSD_BOOT_REC_SWITCH_ON)) { + /* + * We have to save the reason here so that it will survive + * coming up three-finger-salute. We're saving it in + * VBNV_RECOVERY_SUBCODE to avoid a recovery loop. + * If we save the reason in VBNV_RECOVERY_REQUEST, we will come + * back here, thus, we won't be able to give a user a chance to + * reboot to workaround boot hicups. + */ + VBDEBUG(("VbBootRecoveryMenu() saving recovery reason (%#x)\n", + shared->recovery_reason)); + vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, + shared->recovery_reason); + /* + * Commit NV now, because it won't get saved if the user forces + * manual recovery via the three-finger salute. + */ + vb2_nv_commit(ctx); + + VbDisplayScreen(ctx, cparams, VB_SCREEN_OS_BROKEN, 0); + VBDEBUG(("VbBootRecoveryMenu() waiting for manual recovery\n")); + while (1) { + VbCheckDisplayKey(ctx, cparams, VbExKeyboardRead()); + if (VbWantShutdownMenu(cparams->gbb->flags)) + return VBERROR_SHUTDOWN_REQUESTED; + VbExSleepMs(REC_KEY_DELAY); + } + } + + /* Loop and wait for a recovery image */ + VBDEBUG(("VbBootRecoveryMenu() waiting for a recovery image\n")); + while (1) { + VBDEBUG(("VbBootRecoveryMenu() attempting to load kernel2\n")); + retval = VbTryLoadKernel(ctx, cparams, VB_DISK_FLAG_REMOVABLE); + + /* + * Clear recovery requests from failed kernel loading, since + * we're already in recovery mode. Do this now, so that + * powering off after inserting an invalid disk doesn't leave + * us stuck in recovery mode. + */ + vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, + VBNV_RECOVERY_NOT_REQUESTED); + + if (VBERROR_SUCCESS == retval) + break; /* Found a recovery kernel */ + + VbDisplayScreen(ctx, cparams, VBERROR_NO_DISK_FOUND == retval ? + VB_SCREEN_RECOVERY_INSERT : + VB_SCREEN_RECOVERY_NO_GOOD, + 0); + + /* + * Scan keyboard more frequently than media, since x86 + * platforms don't like to scan USB too rapidly. + */ + for (i = 0; i < REC_DISK_DELAY; i += REC_KEY_DELAY) { + key = VbExKeyboardRead(); + /* + * We might want to enter dev-mode from the Insert + * screen if all of the following are true: + * - user pressed Ctrl-D + * - we can honor the virtual dev switch + * - not already in dev mode + * - user forced recovery mode + * - EC isn't pwned + */ + if (key == 0x04 && + shared->flags & VBSD_HONOR_VIRT_DEV_SWITCH && + !(shared->flags & VBSD_BOOT_DEV_SWITCH_ON) && + (shared->flags & VBSD_BOOT_REC_SWITCH_ON) && + VbExTrustEC(0)) { + if (!(shared->flags & + VBSD_BOOT_REC_SWITCH_VIRTUAL) && + VbExGetSwitches( + VB_INIT_FLAG_REC_BUTTON_PRESSED)) { + /* + * Is the recovery button stuck? In + * any case we don't like this. Beep + * and ignore. + */ + VBDEBUG(("%s() - ^D but rec switch " + "is pressed\n", __func__)); + VbExBeep(120, 400); + continue; + } + + /* Ask the user to confirm entering dev-mode */ + VbDisplayScreen(ctx, cparams, + VB_SCREEN_RECOVERY_TO_DEV, + 0); + /* SPACE means no... */ + uint32_t vbc_flags = + VB_CONFIRM_SPACE_MEANS_NO | + VB_CONFIRM_MUST_TRUST_KEYBOARD; + switch (VbUserConfirmsMenu(ctx, cparams, + vbc_flags)) { + case 1: + VBDEBUG(("%s() Enabling dev-mode...\n", + __func__)); + if (TPM_SUCCESS != SetVirtualDevMode(1)) + return VBERROR_TPM_SET_BOOT_MODE_STATE; + VBDEBUG(("%s() Reboot so it will take " + "effect\n", __func__)); + if (VbExGetSwitches + (VB_INIT_FLAG_ALLOW_USB_BOOT)) + VbAllowUsbBootMenu(ctx); + return VBERROR_REBOOT_REQUIRED; + case -1: + VBDEBUG(("%s() - Shutdown requested\n", + __func__)); + return VBERROR_SHUTDOWN_REQUESTED; + default: /* zero, actually */ + VBDEBUG(("%s() - Not enabling " + "dev-mode\n", __func__)); + /* + * Jump out of the outer loop to + * refresh the display quickly. + */ + i = 4; + break; + } + } else { + VbCheckDisplayKey(ctx, cparams, key); + } + if (VbWantShutdownMenu(cparams->gbb->flags)) + return VBERROR_SHUTDOWN_REQUESTED; + VbExSleepMs(REC_KEY_DELAY); + } + } + + return VBERROR_SUCCESS; +} + +VbError_t VbBootRecoveryMenu(struct vb2_context *ctx, VbCommonParams *cparams) +{ + VbError_t retval = vb2_recovery_menu(ctx, cparams); + VbDisplayScreen(ctx, cparams, VB_SCREEN_BLANK, 0); + return retval; +} |