/* Copyright 2017 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_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; /* * In detachables, disabling shutdown due to power button. * We are using it for selection instead. */ shutdown_request &= ~VB_SHUTDOWN_REQUEST_POWER_BUTTON; return !!shutdown_request; } static void VbTryLegacyMenu(int allowed) { if (!allowed) VB2_DEBUG("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("booting USB\n"); } else { VB2_DEBUG("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; } 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_RECOVERY, VB_MENU_TO_DEV, VB_MENU_LANGUAGES, VB_MENU_RECOVERY_INSERT, VB_MENU_RECOVERY_NO_GOOD, VB_MENU_RECOVERY_BROKEN, VB_MENU_TO_NORM_CONFIRMED, 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; typedef enum _VB_RECOVERY_MENU { VB_RECOVERY_TO_DEV, VB_RECOVERY_DBG_INFO, VB_RECOVERY_POWER_OFF, VB_RECOVERY_LANGUAGE, VB_RECOVERY_COUNT, } VB_RECOVERY_MENU; typedef enum _VB_TO_DEV_MENU { VB_TO_DEV_CONFIRM, VB_TO_DEV_CANCEL, VB_TO_DEV_POWER_OFF, VB_TO_DEV_LANGUAGE, VB_TO_DEV_COUNT, } VB_TO_DEV_MENU; // TODO: currently we're only supporting // english. Will need to somehow find mapping // from language to localization index. typedef enum _VB_LANGUAGES_MENU { VB_LANGUAGES_EN_US, VB_LANGUAGES_COUNT, } VB_LANGUAGES_MENU; static VB_MENU current_menu = VB_MENU_DEV_WARNING; static VB_MENU prev_menu = VB_MENU_DEV_WARNING; static int current_menu_idx = VB_WARN_POWER_OFF; static int selected = 0; static int disabled_idx_mask = 0; static uint32_t default_boot = VB2_DEV_DEFAULT_BOOT_DISK; static uint32_t disable_dev_boot = 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 (not working yet)\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" }; static char *recovery_menu[] = { "Enable developer mode\n", "Show Debug Info\n", "Power Off\n", "Language\n" }; static char *to_dev_menu[] = { "Confirm enabling developer mode\n", "Cancel\n", "Power Off\n", "Language\n" }; static char *languages_menu[] = { "US English\n", }; /** * Get the string array and size of current_menu. * * @param menu: The current_menu * @param menu_array: Pointer to the menu's string array. If menu_array==NULL * will not return string array. * @param size: Size of menu's string array. * @return VBERROR_SUCCESS, or non-zero error code if error. */ void vb2_get_current_menu_size(VB_MENU menu, char ***menu_array, uint32_t *size) { char **temp_menu = NULL; 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; case VB_MENU_RECOVERY: *size = VB_RECOVERY_COUNT; temp_menu = recovery_menu; break; case VB_MENU_TO_DEV: *size = VB_TO_DEV_COUNT; temp_menu = to_dev_menu; break; case VB_MENU_LANGUAGES: *size = VB_LANGUAGES_COUNT; temp_menu = languages_menu; break; default: *size = 0; } if (menu_array) *menu_array = temp_menu; } /** * Print current_menu state, including selected entry. * * @return VBERROR_SUCCESS, or non-zero error code if error. */ VbError_t vb2_print_current_menu() { uint32_t size = 0; int i = 0; static char **m = NULL; int highlight = 0; // TODO: We probably want to center this text. uint32_t xindex, yindex; // TODO: need to check for error code. vb2_get_current_menu_size(current_menu, &m, &size); /* Center block of text */ VbExDisplayGetDimension(&xindex, &yindex); xindex = xindex/2 - strlen(m[0])/2; yindex = yindex/2 - size/2; // TODO: do clear screen here. /* Create menu string */ for (i = 0; i < size; i++) { highlight = !!(current_menu_idx == i); VbExDisplayText(xindex, yindex, m[i], highlight); VB2_DEBUG("[%d,%d]: %s", xindex, yindex, m[i]); yindex++; } return VBERROR_SUCCESS; } /* * Static array for mapping current_menu to matching screen item in depthcharge. * Note that order here is important and needs to match that of items in * VB_MENU. */ static const uint32_t VB_MENU_TO_SCREEN_MAP[] = { VB_SCREEN_DEVELOPER_WARNING_MENU, VB_SCREEN_DEVELOPER_MENU, VB_SCREEN_DEVELOPER_TO_NORM_MENU, VB_SCREEN_RECOVERY_MENU, VB_SCREEN_RECOVERY_TO_DEV_MENU, VB_SCREEN_LANGUAGES_MENU, VB_SCREEN_RECOVERY_INSERT, VB_SCREEN_RECOVERY_NO_GOOD, VB_SCREEN_OS_BROKEN, VB_SCREEN_TO_NORM_CONFIRMED, }; VbError_t vb2_draw_current_screen(struct vb2_context *ctx, VbCommonParams *cparams) { uint32_t screen; if (current_menu < VB_MENU_COUNT) screen = VB_MENU_TO_SCREEN_MAP[current_menu]; else return VBERROR_UNKNOWN; return VbDisplayMenu(ctx, cparams, screen, 0, current_menu_idx, disabled_idx_mask); } /** * Set current_menu to new_current_menu * * @param new_current_menu: new menu to set current_menu to * @param new_current_menu_idx: new idx to set current_menu_idx to * @return VBERROR_SUCCESS, or non-zero error code if error. */ VbError_t vb2_set_menu_items(VB_MENU new_current_menu, int new_current_menu_idx) { prev_menu = current_menu; current_menu = new_current_menu; current_menu_idx = new_current_menu_idx; /* Changing menus, so reset selected */ selected = 0; return VBERROR_SUCCESS; } /** * This updates current_menu and current_menu_idx, as necessary * * @return VBERROR_SUCCESS, or non-zero error code if error. */ VbError_t vb2_update_menu(struct vb2_context *ctx) { VbError_t ret = VBERROR_SUCCESS; VB_MENU next_menu_idx = current_menu_idx; uint32_t loc = vb2_nv_get(ctx, VB2_NV_LOCALIZATION_INDEX); switch(current_menu) { case VB_MENU_DEV_WARNING: switch(current_menu_idx) { case VB_WARN_OPTIONS: switch(default_boot) { case VBNV_DEV_DEFAULT_BOOT_DISK: next_menu_idx = VB_DEV_DISK; break; case VBNV_DEV_DEFAULT_BOOT_USB: next_menu_idx = VB_DEV_USB; break; case VBNV_DEV_DEFAULT_BOOT_LEGACY: next_menu_idx = VB_DEV_LEGACY; break; } /* * 1. Select dev menu * 2. Default to dev boot device */ vb2_set_menu_items(VB_MENU_DEV, next_menu_idx); break; case VB_WARN_DBG_INFO: /* Show debug info */ break; case VB_WARN_ENABLE_VER: /* * 1. Enable boot verification * 2. Default to the confirm option */ vb2_set_menu_items(VB_MENU_TO_NORM, VB_TO_NORM_CONFIRM); break; case VB_WARN_POWER_OFF: /* Power off machine */ ret = VBERROR_SHUTDOWN_REQUESTED; break; case VB_WARN_LANGUAGE: /* Languages */ vb2_set_menu_items(VB_MENU_LANGUAGES, loc); 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: /* Boot USB image */ break; case VB_DEV_DISK: /* Boot developer image */ break; case VB_DEV_CANCEL: /* * 1. Cancel (go back to developer warning menu) * 2. Default to power off option. */ vb2_set_menu_items(VB_MENU_DEV_WARNING, VB_WARN_POWER_OFF); break; case VB_DEV_POWER_OFF: /* Power off */ ret = VBERROR_SHUTDOWN_REQUESTED; break; case VB_DEV_LANGUAGE: /* Language */ vb2_set_menu_items(VB_MENU_LANGUAGES, loc); 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: /* * 1. Cancel (go back to developer warning menu) * 2. Default to power off */ vb2_set_menu_items(VB_MENU_DEV_WARNING, VB_WARN_POWER_OFF); break; case VB_TO_NORM_POWER_OFF: /* Power off */ ret = VBERROR_SHUTDOWN_REQUESTED; break; case VB_TO_NORM_LANGUAGE: /* Language */ vb2_set_menu_items(VB_MENU_LANGUAGES, loc); break; default: /* Invalid menu item. Don't update anything */ break; } break; case VB_MENU_RECOVERY_INSERT: vb2_set_menu_items(VB_MENU_RECOVERY, VB_RECOVERY_POWER_OFF); break; case VB_MENU_RECOVERY_NO_GOOD: case VB_MENU_RECOVERY_BROKEN: case VB_MENU_TO_NORM_CONFIRMED: break; case VB_MENU_RECOVERY: switch(current_menu_idx) { case VB_RECOVERY_TO_DEV: /* * 1. Switch to TO_DEV menu * 2. Default to cancel option */ vb2_set_menu_items(VB_MENU_TO_DEV, VB_TO_DEV_CANCEL); break; case VB_RECOVERY_DBG_INFO: break; case VB_RECOVERY_POWER_OFF: ret = VBERROR_SHUTDOWN_REQUESTED; break; case VB_RECOVERY_LANGUAGE: vb2_set_menu_items(VB_MENU_LANGUAGES, loc); break; default: /* Invalid menu item. Don't update anything */ break; } break; case VB_MENU_TO_DEV: switch(current_menu_idx) { case VB_TO_DEV_CONFIRM: /* Confirm enabling dev mode */ break; case VB_TO_DEV_CANCEL: vb2_set_menu_items(VB_MENU_RECOVERY, VB_RECOVERY_POWER_OFF); break; case VB_TO_DEV_POWER_OFF: ret = VBERROR_SHUTDOWN_REQUESTED; break; case VB_TO_DEV_LANGUAGE: vb2_set_menu_items(VB_MENU_LANGUAGES, loc); break; default: /* Invalid menu item. Don't update anything. */ break; } break; case VB_MENU_LANGUAGES: /* * Assume that we selected a language. Go to previous * menu. Purposely bypassing vb2_set_menu_items() here * because need to do in different order. */ current_menu = prev_menu; prev_menu = VB_MENU_LANGUAGES; /* * default to power off index with the exception of * TO_DEV and TO_NORM menus */ switch (current_menu) { case VB_MENU_DEV_WARNING: current_menu_idx = VB_WARN_POWER_OFF; break; case VB_MENU_DEV: current_menu_idx = VB_DEV_POWER_OFF; break; case VB_MENU_TO_NORM: current_menu_idx = VB_TO_NORM_CONFIRM; break; case VB_MENU_RECOVERY: current_menu_idx = VB_RECOVERY_POWER_OFF; break; case VB_MENU_TO_DEV: current_menu_idx = VB_TO_DEV_CANCEL; break; default: current_menu_idx = 0; break; } selected = current_menu_idx; break; default: VB2_DEBUG("Current Menu Invalid! 0x%x\n", current_menu_idx); } return ret; } /** * This updates the current locale to the current_menu_index * This function only does something in the VB_MENU_LANGUAGES menu * Otherwise it's a noop. * * @return VBERROR_SUCCESS */ VbError_t vb2_update_locale(struct vb2_context *ctx) { if (current_menu == VB_MENU_LANGUAGES) { vb2_nv_set(ctx, VB2_NV_LOCALIZATION_INDEX, current_menu_idx); vb2_nv_set(ctx, VB2_NV_BACKUP_NVRAM_REQUEST, 1); #ifdef SAVE_LOCALE_IMMEDIATELY if (ctx->flags & VB2_CONTEXT_NVDATA_CHANGED) { VbExNvStorageWrite(ctx.nvdata); ctx.flags &= ~VB2_CONTEXT_NVDATA_CHANGED; } #endif } return VBERROR_SUCCESS; } /** * Adjust the disabled_idx_mask based on current menu and settings. * * @param flags flag to check for dev/normal mode. * @return VBERROR_SUCCESS */ VbError_t vb2_set_disabled_idx_mask(uint32_t flags) { /* Disable "Enable Developer Mode" menu item */ disabled_idx_mask = 0; if (current_menu == VB_MENU_RECOVERY && (flags & VBSD_BOOT_DEV_SWITCH_ON)) { disabled_idx_mask |= 1 << VB_RECOVERY_TO_DEV; } /* Disable Network Boot Option */ if (current_menu == VB_MENU_DEV) disabled_idx_mask |= 1 << VB_DEV_NETWORK; /* Disable cancel option if enterprise disabled dev mode */ if (current_menu == VB_MENU_TO_NORM && disable_dev_boot == 1) disabled_idx_mask |= 1 << VB_TO_NORM_CANCEL; return VBERROR_SUCCESS; } /** * Updates current_menu_idx upon an up/down key press, taking into * account disabled indices (from disabled_idx_mask). The cursor * will not wrap, meaning that we block on the 0 or max index when * we hit the ends of the menu. * * @param cparams common params * @param key VOL_KEY_UP = increase index selection * VOL_KEY_DOWN = decrease index selection. * Every other key has no effect now. */ void vb2_update_selection(VbCommonParams *cparams, uint32_t key) { int idx; uint32_t menu_size; if (current_menu == VB_MENU_LANGUAGES) { VbGetLocalizationCount(cparams, &menu_size); } else { vb2_get_current_menu_size(current_menu, NULL, &menu_size); } switch (key) { case VB_BUTTON_VOL_UP_SHORT_PRESS: case VB_KEY_UP: idx = current_menu_idx - 1; while (idx >= 0 && ((1 << idx) & disabled_idx_mask)) idx--; /* Only update if idx is valid */ if (idx >= 0) current_menu_idx = idx; break; case VB_BUTTON_VOL_DOWN_SHORT_PRESS: case VB_KEY_DOWN: idx = current_menu_idx + 1; while (idx < menu_size && ((1 << idx) & disabled_idx_mask)) idx++; /* Only update if idx is valid */ if (idx < menu_size) current_menu_idx = idx; break; default: /* Do not update anything */ break; } } /** * Main function that handles developer warning menu functionality * * @param ctx Vboot2 context * @param cparams Vboot1 common params * @return VBERROR_SUCCESS, or non-zero error code if error. */ VbError_t vb2_developer_menu(struct vb2_context *ctx, VbCommonParams *cparams) { GoogleBinaryBlockHeader *gbb = cparams->gbb; VbSharedDataHeader *shared = (VbSharedDataHeader *)cparams->shared_data_blob; uint32_t use_usb = 0; uint32_t use_legacy = 0; uint32_t ctrl_d_pressed = 0; VbAudioContext *audio = NULL; VbError_t ret; VB2_DEBUG("Entering\n"); /* 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 */ 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("FWMP_DEV_DISABLE_BOOT rejected by" "FORCE_DEV_SWITCH_ON\n"); } else { disable_dev_boot = 1; VB2_DEBUG("dev_disable_boot is set.\n"); /* If dev mode is disabled, only allow TONORM */ current_menu = VB_MENU_TO_NORM; prev_menu = VB_MENU_TO_NORM; current_menu_idx = VB_TO_NORM_CONFIRM; } } vb2_set_disabled_idx_mask(shared->flags); /* Show the dev mode warning screen */ vb2_draw_current_screen(ctx, cparams); /* Get audio/delay context */ if (!disable_dev_boot) audio = VbAudioOpen(cparams); /* We'll loop until we finish the delay or are interrupted */ do { uint32_t key; if (VbWantShutdownMenu(gbb->flags)) { VB2_DEBUG("shutdown requested!\n"); if (audio) VbAudioClose(audio); return VBERROR_SHUTDOWN_REQUESTED; } /* Make sure user knows dev mode disabled */ if (disable_dev_boot) VbExDisplayDebugInfo(dev_disable_msg); key = VbExKeyboardRead(); switch (key) { case 0: /* Nothing pressed */ break; case VB_BUTTON_VOL_DOWN_LONG_PRESS: case 0x04: /* Ctrl+D = dismiss warning; advance to timeout */ if (disable_dev_boot) break; VB2_DEBUG("user pressed Ctrl+D; skip delay\n"); ctrl_d_pressed = 1; goto fallout; break; case 0x0c: if (disable_dev_boot) break; VB2_DEBUG("user pressed Ctrl+L; Try legacy boot\n"); VbTryLegacyMenu(allow_legacy); break; case VB_BUTTON_VOL_UP_LONG_PRESS: case 0x15: if (disable_dev_boot) break; /* Ctrl+U = try USB boot, or beep if failure */ VB2_DEBUG("user pressed Ctrl+U; try USB\n"); if (!allow_usb) { VB2_DEBUG("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)) { if (audio) VbAudioClose(audio); return VBERROR_SUCCESS; } else { /* Show dev mode warning screen again */ vb2_draw_current_screen(ctx, cparams); } } break; case VB_BUTTON_VOL_UP_SHORT_PRESS: case VB_KEY_UP: vb2_update_selection(cparams, key); vb2_draw_current_screen(ctx, cparams); /* reset 30 second timer */ if (audio) audio = VbAudioOpen(cparams); break; case VB_BUTTON_VOL_DOWN_SHORT_PRESS: case VB_KEY_DOWN: vb2_update_selection(cparams, key); vb2_draw_current_screen(ctx, cparams); /* reset 30 second timer */ if (audio) audio = VbAudioOpen(cparams); break; case VB_BUTTON_POWER_SHORT_PRESS: case '\r': selected = 1; /* * Need to update locale before updating the menu or * we'll lose the previous state */ vb2_update_locale(ctx); ret = vb2_update_menu(ctx); vb2_set_disabled_idx_mask(shared->flags); vb2_draw_current_screen(ctx, cparams); /* Probably shutting down */ if (ret != VBERROR_SUCCESS) { VB2_DEBUG("shutting down!\n"); return ret; } /* Nothing selected, skip everything else */ if (selected == 0) break; /* All the actions associated with selection */ /* Display debug information */ if (current_menu == VB_MENU_DEV_WARNING && current_menu_idx == VB_WARN_DBG_INFO) { VbDisplayDebugInfo(ctx, cparams); } /* Boot Legacy mode */ if (current_menu == VB_MENU_DEV && current_menu_idx == VB_DEV_LEGACY) { VB2_DEBUG("user pressed Ctrl+L; " "Try legacy boot\n"); VbTryLegacyMenu(allow_legacy); } /* USB boot, or beep if failure */ if (current_menu == VB_MENU_DEV && current_menu_idx == VB_DEV_USB) { VB2_DEBUG("user pressed Ctrl+U; try USB\n"); if (!allow_usb) { VB2_DEBUG("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)) { if (audio) VbAudioClose(audio); return VBERROR_SUCCESS; } else /* * Show dev mode warning screen * again */ vb2_draw_current_screen(ctx, cparams); } } /* Boot developer mode: advance to timeout */ if (current_menu == VB_MENU_DEV && current_menu_idx == VB_DEV_DISK) { VB2_DEBUG("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) { if (gbb->flags & GBB_FLAG_FORCE_DEV_SWITCH_ON) { /* * Throw error when user tries to * confirm transition to normal * mode if FORCE_DEV_SWITCH_ON * is enabled. */ VB2_DEBUG("TONORM rejected by " "FORCE_DEV_SWITCH_ON\n"); VbExDisplayDebugInfo( "WARNING: TONORM prohibited by " "GBB FORCE_DEV_SWITCH_ON.\n\n"); VbExBeep(120, 400); } else { /* * See if we should disable * virtual dev-mode switch. */ VB2_DEBUG("%s shared->flags=0x%x\n", __func__, shared->flags); VB2_DEBUG("leaving dev-mode.\n"); vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST, 1); VbDisplayScreen(ctx, cparams, VB_SCREEN_TO_NORM_CONFIRMED, 0); current_menu = VB_MENU_TO_NORM_CONFIRMED; VbExSleepMs(5000); return VBERROR_REBOOT_REQUIRED; } } /* reset 30 second timer */ if (audio) audio = VbAudioOpen(cparams); break; default: VB2_DEBUG("pressed key %d\n", 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("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("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 */ /** * Main function that handles recovery menu functionality * * @param ctx Vboot2 context * @param cparams Vboot1 common params * @return VBERROR_SUCCESS, or non-zero error code if error. */ static VbError_t recovery_ui(struct vb2_context *ctx, VbCommonParams *cparams) { VbSharedDataHeader *shared = (VbSharedDataHeader *)cparams->shared_data_blob; uint32_t retval; uint32_t key; uint32_t key_flags; int i; VbError_t ret; VB2_DEBUG("start\n"); if (!vb2_allow_recovery(shared->flags)) { /* * 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 a boot hiccup. */ VB2_DEBUG("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); current_menu = VB_MENU_RECOVERY_BROKEN; VB2_DEBUG("waiting for manual recovery\n"); while (1) { key = VbExKeyboardRead(); if (key == VB_BUTTON_POWER_SHORT_PRESS) return VBERROR_SHUTDOWN_REQUESTED; else { VbCheckDisplayKey(ctx, cparams, key); if (VbWantShutdownMenu(cparams->gbb->flags)) return VBERROR_SHUTDOWN_REQUESTED; } VbExSleepMs(REC_KEY_DELAY); } } /* Loop and wait for a recovery image */ VB2_DEBUG("waiting for a recovery image\n"); /* Initialize menu to recovery menu. */ current_menu = VB_MENU_RECOVERY_INSERT; prev_menu = VB_MENU_RECOVERY_INSERT; current_menu_idx = 0; while (1) { VB2_DEBUG("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 */ vb2_set_disabled_idx_mask(shared->flags); if (current_menu != VB_MENU_RECOVERY || current_menu_idx != VB_RECOVERY_DBG_INFO) { if (retval == VBERROR_NO_DISK_FOUND) vb2_draw_current_screen(ctx, cparams); else { VbDisplayScreen(ctx, cparams, VB_SCREEN_RECOVERY_NO_GOOD, 0); current_menu = VB_MENU_RECOVERY_NO_GOOD; } } /* * 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 = VbExKeyboardReadWithFlags(&key_flags); switch (key) { case 0: /* nothing pressed */ break; case VB_KEY_UP: case VB_KEY_DOWN: case VB_BUTTON_VOL_UP_SHORT_PRESS: case VB_BUTTON_VOL_DOWN_SHORT_PRESS: /* User cannot use keyboard to enable dev mode. * They need to use the volume buttons from a * trusted source to navigate to the disable os * verification menu item. Beep so user knows * that they're doing something wrong. */ if (current_menu == VB_MENU_TO_DEV && !(key_flags & VB_KEY_FLAG_TRUSTED_KEYBOARD)) { VbExBeep(120, 400); break; } vb2_update_selection(cparams, key); vb2_draw_current_screen(ctx, cparams); break; case VB_BUTTON_VOL_UP_DOWN_COMBO_PRESS: /* * This is currently only being used to * transition out of the initial insert * graphic */ if (current_menu == VB_MENU_RECOVERY_INSERT) { ret = vb2_update_menu(ctx); if (ret != VBERROR_SUCCESS) return ret; vb2_set_disabled_idx_mask(shared->flags); vb2_draw_current_screen(ctx, cparams); } break; case VB_BUTTON_POWER_SHORT_PRESS: case '\r': selected = 1; /* * If user hits power button in * initial recovery screen (ie: * because didn't really want to go * there), power button will turn off * device. */ if (current_menu == VB_MENU_RECOVERY_INSERT) { ret = VBERROR_SHUTDOWN_REQUESTED; } else { /* * Need to update locale * before updating the menu or * we'll lose the previous state */ vb2_update_locale(ctx); ret = vb2_update_menu(ctx); vb2_set_disabled_idx_mask(shared-> flags); } if (current_menu != VB_MENU_RECOVERY || current_menu_idx != VB_RECOVERY_DBG_INFO) { /* * Unfortunately we need this screen * blanking to clear previous menus * printed. */ if (retval == VBERROR_NO_DISK_FOUND) vb2_draw_current_screen(ctx, cparams); else { VbDisplayScreen(ctx, cparams, VB_SCREEN_RECOVERY_NO_GOOD, 0); current_menu = VB_MENU_RECOVERY_NO_GOOD; } } /* Probably shutting down */ if (ret != VBERROR_SUCCESS) { VB2_DEBUG("update_menu - shutting down!\n"); return ret; } /* Nothing selected, skip everything else. */ if (selected == 0) break; /* Display debug information */ if (current_menu == VB_MENU_RECOVERY && current_menu_idx == VB_RECOVERY_DBG_INFO) { VbDisplayDebugInfo(ctx, cparams); } /* Confirm going into developer mode */ /* * 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 */ if (current_menu == VB_MENU_TO_DEV && current_menu_idx == 0 && shared->flags & VBSD_HONOR_VIRT_DEV_SWITCH && !(shared->flags & VBSD_BOOT_DEV_SWITCH_ON) && (shared->flags & VBSD_BOOT_REC_SWITCH_ON)) { 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. */ VB2_DEBUG("^D but rec switch " "is pressed\n"); VbExBeep(120, 400); continue; } VB2_DEBUG("Enabling dev-mode...\n"); if (TPM_SUCCESS != SetVirtualDevMode(1)) return VBERROR_TPM_SET_BOOT_MODE_STATE; VB2_DEBUG("Reboot so it will take " "effect\n"); if (VbExGetSwitches (VB_INIT_FLAG_ALLOW_USB_BOOT)) VbAllowUsbBootMenu(ctx); return VBERROR_REBOOT_REQUIRED; } } 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 = recovery_ui(ctx, cparams); VbDisplayScreen(ctx, cparams, VB_SCREEN_BLANK, 0); return retval; }