mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-03 21:49:32 +00:00
Most of the UI loop input handling is essentially the same between the developer and recovery mode interfaces. This patch factors it out into a separate function, which reduces duplication and will make it easier to add a third UI for non-manual recovery mode. BRANCH=None BUG=None TEST=make runtests, played around with menus on Scarlet Change-Id: I75751770a81eb373f8bc7ed0e5f5dfcb6d2bd464 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/892279 Reviewed-by: Randall Spangler <rspangler@chromium.org> Reviewed-by: Furquan Shaikh <furquan@chromium.org> Reviewed-by: Shelley Chen <shchen@chromium.org>
831 lines
22 KiB
C
831 lines
22 KiB
C
/* 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 "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_ui_menu_private.h"
|
|
|
|
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";
|
|
|
|
static VB_MENU current_menu, prev_menu;
|
|
static int current_menu_idx, disabled_idx_mask, usb_nogood;
|
|
static uint32_t default_boot;
|
|
static uint32_t disable_dev_boot;
|
|
static struct vb2_menu menus[];
|
|
|
|
/**
|
|
* 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(struct vb2_context *ctx)
|
|
{
|
|
struct vb2_shared_data *sd = vb2_get_sd(ctx);
|
|
uint32_t shutdown_request = VbExIsShutdownRequested();
|
|
|
|
/* If desired, ignore shutdown request due to lid closure. */
|
|
if (sd->gbb_flags & VB2_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;
|
|
}
|
|
|
|
/* (Re-)Draw the menu identified by current_menu[_idx] to the screen. */
|
|
static VbError_t vb2_draw_current_screen(struct vb2_context *ctx) {
|
|
return VbDisplayMenu(ctx, menus[current_menu].screen, 0,
|
|
current_menu_idx, disabled_idx_mask);
|
|
}
|
|
|
|
/* Flash the screen to black to catch user awareness, then redraw menu. */
|
|
static void vb2_flash_screen(struct vb2_context *ctx)
|
|
{
|
|
VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0);
|
|
VbExSleepMs(50);
|
|
vb2_draw_current_screen(ctx);
|
|
}
|
|
|
|
/* Two short beeps to notify the user that attempted action was disallowed. */
|
|
static void vb2_error_beep(void)
|
|
{
|
|
VbExBeep(120, 400);
|
|
VbExSleepMs(120);
|
|
VbExBeep(120, 400);
|
|
}
|
|
|
|
/**
|
|
* Switch to a new menu (but don't draw it yet).
|
|
*
|
|
* @param new_current_menu: new menu to set current_menu to
|
|
* @param new_current_menu_idx: new idx to set current_menu_idx to
|
|
*/
|
|
static void vb2_change_menu(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;
|
|
|
|
/* Reconfigure disabled_idx_mask for the new menu */
|
|
disabled_idx_mask = 0;
|
|
/* 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;
|
|
}
|
|
|
|
/************************
|
|
* Menu Actions *
|
|
************************/
|
|
|
|
/* Boot from internal disk if allowed. */
|
|
static VbError_t boot_disk_action(struct vb2_context *ctx)
|
|
{
|
|
if (disable_dev_boot) {
|
|
vb2_flash_screen(ctx);
|
|
vb2_error_beep();
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
VB2_DEBUG("trying fixed disk\n");
|
|
return VbTryLoadKernel(ctx, VB_DISK_FLAG_FIXED);
|
|
}
|
|
|
|
/* Boot legacy BIOS if allowed and available. */
|
|
static VbError_t boot_legacy_action(struct vb2_context *ctx)
|
|
{
|
|
const char no_legacy[] = "Legacy boot failed. Missing BIOS?\n";
|
|
|
|
if (disable_dev_boot) {
|
|
vb2_flash_screen(ctx);
|
|
vb2_error_beep();
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
if (!vb2_nv_get(ctx, VB2_NV_DEV_BOOT_LEGACY) &&
|
|
!(vb2_get_sd(ctx)->gbb_flags & VB2_GBB_FLAG_FORCE_DEV_BOOT_LEGACY)
|
|
&& !(vb2_get_fwmp_flags() & FWMP_DEV_ENABLE_LEGACY)) {
|
|
vb2_flash_screen(ctx);
|
|
VB2_DEBUG("Legacy boot is disabled\n");
|
|
VbExDisplayDebugInfo("WARNING: Booting legacy BIOS has not "
|
|
"been enabled. Refer to the developer"
|
|
"-mode documentation for details.\n");
|
|
vb2_error_beep();
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
if (0 == RollbackKernelLock(0))
|
|
VbExLegacy(); /* Will not return if successful */
|
|
else
|
|
VB2_DEBUG("Error locking kernel versions on legacy boot.\n");
|
|
|
|
vb2_flash_screen(ctx);
|
|
VB2_DEBUG(no_legacy);
|
|
VbExDisplayDebugInfo(no_legacy);
|
|
VbExBeep(250, 200);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
/* Boot from USB or SD card if allowed and available. */
|
|
static VbError_t boot_usb_action(struct vb2_context *ctx)
|
|
{
|
|
const char no_kernel[] = "No bootable kernel found on USB/SD.\n";
|
|
|
|
if (disable_dev_boot) {
|
|
vb2_flash_screen(ctx);
|
|
vb2_error_beep();
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
if (!vb2_nv_get(ctx, VB2_NV_DEV_BOOT_USB) &&
|
|
!(vb2_get_sd(ctx)->gbb_flags & VB2_GBB_FLAG_FORCE_DEV_BOOT_USB) &&
|
|
!(vb2_get_fwmp_flags() & FWMP_DEV_ENABLE_USB)) {
|
|
vb2_flash_screen(ctx);
|
|
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");
|
|
vb2_error_beep();
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
if (VBERROR_SUCCESS == VbTryLoadKernel(ctx, VB_DISK_FLAG_REMOVABLE)) {
|
|
VB2_DEBUG("booting USB\n");
|
|
return VBERROR_SUCCESS;
|
|
}
|
|
|
|
/* Loading kernel failed. Clear recovery request from that. */
|
|
vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, VB2_RECOVERY_NOT_REQUESTED);
|
|
vb2_flash_screen(ctx);
|
|
VB2_DEBUG(no_kernel);
|
|
VbExDisplayDebugInfo(no_kernel);
|
|
VbExBeep(250, 200);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
static VbError_t enter_developer_menu(struct vb2_context *ctx)
|
|
{
|
|
int menu_idx;
|
|
switch(default_boot) {
|
|
case VB2_DEV_DEFAULT_BOOT_DISK:
|
|
menu_idx = VB_DEV_DISK;
|
|
break;
|
|
case VB2_DEV_DEFAULT_BOOT_USB:
|
|
menu_idx = VB_DEV_USB;
|
|
break;
|
|
case VB2_DEV_DEFAULT_BOOT_LEGACY:
|
|
menu_idx = VB_DEV_LEGACY;
|
|
break;
|
|
}
|
|
vb2_change_menu(VB_MENU_DEV, menu_idx);
|
|
vb2_draw_current_screen(ctx);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
static VbError_t enter_dev_warning_menu(struct vb2_context *ctx)
|
|
{
|
|
vb2_change_menu(VB_MENU_DEV_WARNING, VB_WARN_POWER_OFF);
|
|
vb2_draw_current_screen(ctx);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
static VbError_t enter_language_menu(struct vb2_context *ctx)
|
|
{
|
|
vb2_change_menu(VB_MENU_LANGUAGES,
|
|
vb2_nv_get(ctx, VB2_NV_LOCALIZATION_INDEX));
|
|
vb2_draw_current_screen(ctx);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
static VbError_t enter_recovery_base_screen(struct vb2_context *ctx)
|
|
{
|
|
if (usb_nogood)
|
|
vb2_change_menu(VB_MENU_RECOVERY_NO_GOOD, 0);
|
|
else
|
|
vb2_change_menu(VB_MENU_RECOVERY_INSERT, 0);
|
|
vb2_draw_current_screen(ctx);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
static VbError_t enter_options_menu(struct vb2_context *ctx)
|
|
{
|
|
vb2_change_menu(VB_MENU_OPTIONS, VB_OPTIONS_CANCEL);
|
|
vb2_draw_current_screen(ctx);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
static VbError_t enter_to_dev_menu(struct vb2_context *ctx)
|
|
{
|
|
const char dev_already_on[] =
|
|
"WARNING: TODEV rejected, developer mode is already on.\n";
|
|
if (vb2_get_sd(ctx)->vbsd->flags & VBSD_BOOT_DEV_SWITCH_ON) {
|
|
vb2_flash_screen(ctx);
|
|
VB2_DEBUG(dev_already_on);
|
|
VbExDisplayDebugInfo(dev_already_on);
|
|
vb2_error_beep();
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
vb2_change_menu(VB_MENU_TO_DEV, VB_TO_DEV_CANCEL);
|
|
vb2_draw_current_screen(ctx);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
static VbError_t enter_to_norm_menu(struct vb2_context *ctx)
|
|
{
|
|
vb2_change_menu(VB_MENU_TO_NORM, VB_TO_NORM_CONFIRM);
|
|
vb2_draw_current_screen(ctx);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
static VbError_t debug_info_action(struct vb2_context *ctx)
|
|
{
|
|
VbDisplayDebugInfo(ctx);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
/* Action when selecting a language entry in the language menu. */
|
|
static VbError_t language_action(struct vb2_context *ctx)
|
|
{
|
|
vb2_nv_set(ctx, VB2_NV_LOCALIZATION_INDEX, current_menu_idx);
|
|
|
|
switch (prev_menu) {
|
|
case VB_MENU_DEV_WARNING:
|
|
return enter_dev_warning_menu(ctx);
|
|
case VB_MENU_DEV:
|
|
return enter_developer_menu(ctx);
|
|
case VB_MENU_TO_NORM:
|
|
return enter_to_norm_menu(ctx);
|
|
case VB_MENU_TO_DEV:
|
|
return enter_to_dev_menu(ctx);
|
|
case VB_MENU_OPTIONS:
|
|
return enter_options_menu(ctx);
|
|
default:
|
|
/* This should never happen. */
|
|
VB2_DEBUG("ERROR: prev_menu state corrupted, force shutdown\n");
|
|
return VBERROR_SHUTDOWN_REQUESTED;
|
|
}
|
|
}
|
|
|
|
/* Action that enables developer mode and reboots. */
|
|
static VbError_t to_dev_action(struct vb2_context *ctx)
|
|
{
|
|
uint32_t vbsd_flags = vb2_get_sd(ctx)->vbsd->flags;
|
|
|
|
/* Sanity check, should never happen. */
|
|
if (!(vbsd_flags & VBSD_HONOR_VIRT_DEV_SWITCH) ||
|
|
(vbsd_flags & VBSD_BOOT_DEV_SWITCH_ON) ||
|
|
!vb2_allow_recovery(vbsd_flags))
|
|
return VBERROR_KEEP_LOOPING;
|
|
|
|
VB2_DEBUG("Enabling dev-mode...\n");
|
|
if (TPM_SUCCESS != SetVirtualDevMode(1))
|
|
return VBERROR_TPM_SET_BOOT_MODE_STATE;
|
|
|
|
/* This was meant for headless devices, shouldn't really matter here. */
|
|
if (VbExGetSwitches(VB_INIT_FLAG_ALLOW_USB_BOOT))
|
|
vb2_nv_set(ctx, VB2_NV_DEV_BOOT_USB, 1);
|
|
|
|
VB2_DEBUG("Reboot so it will take effect\n");
|
|
return VBERROR_REBOOT_REQUIRED;
|
|
}
|
|
|
|
/* Action that disables developer mode, shows TO_NORM_CONFIRMED and reboots. */
|
|
static VbError_t to_norm_action(struct vb2_context *ctx)
|
|
{
|
|
if (vb2_get_sd(ctx)->gbb_flags & VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON) {
|
|
vb2_flash_screen(ctx);
|
|
VB2_DEBUG("TONORM rejected by FORCE_DEV_SWITCH_ON\n");
|
|
VbExDisplayDebugInfo("WARNING: TONORM prohibited by "
|
|
"GBB FORCE_DEV_SWITCH_ON.\n\n");
|
|
vb2_error_beep();
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
VB2_DEBUG("leaving dev-mode.\n");
|
|
vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST, 1);
|
|
vb2_change_menu(VB_MENU_TO_NORM_CONFIRMED, 0);
|
|
vb2_draw_current_screen(ctx);
|
|
VbExSleepMs(5000);
|
|
return VBERROR_REBOOT_REQUIRED;
|
|
}
|
|
|
|
/* Action that will power off the system. */
|
|
static VbError_t power_off_action(struct vb2_context *ctx)
|
|
{
|
|
VB2_DEBUG("Power off requested from screen 0x%x\n",
|
|
menus[current_menu].screen);
|
|
return VBERROR_SHUTDOWN_REQUESTED;
|
|
}
|
|
|
|
/* Master table of all menus. Menus with size == 0 count as menuless screens. */
|
|
static struct vb2_menu menus[VB_MENU_COUNT] = {
|
|
[VB_MENU_DEV_WARNING] = {
|
|
.size = VB_WARN_COUNT,
|
|
.screen = VB_SCREEN_DEVELOPER_WARNING_MENU,
|
|
.items = (struct vb2_menu_item[]){
|
|
[VB_WARN_OPTIONS] = {
|
|
.text = "Developer Options",
|
|
.action = enter_developer_menu,
|
|
},
|
|
[VB_WARN_DBG_INFO] = {
|
|
.text = "Show Debug Info",
|
|
.action = debug_info_action,
|
|
},
|
|
[VB_WARN_ENABLE_VER] = {
|
|
.text = "Enable OS Verification",
|
|
.action = enter_to_norm_menu,
|
|
},
|
|
[VB_WARN_POWER_OFF] = {
|
|
.text = "Power Off",
|
|
.action = power_off_action,
|
|
},
|
|
[VB_WARN_LANGUAGE] = {
|
|
.text = "Language",
|
|
.action = enter_language_menu,
|
|
},
|
|
},
|
|
},
|
|
[VB_MENU_DEV] = {
|
|
.size = VB_DEV_COUNT,
|
|
.screen = VB_SCREEN_DEVELOPER_MENU,
|
|
.items = (struct vb2_menu_item[]){
|
|
[VB_DEV_NETWORK] = {
|
|
.text = "Boot From Network",
|
|
.action = NULL, /* unimplemented */
|
|
},
|
|
[VB_DEV_LEGACY] = {
|
|
.text = "Boot Legacy BIOS",
|
|
.action = boot_legacy_action,
|
|
},
|
|
[VB_DEV_USB] = {
|
|
.text = "Boot From USB or SD Card",
|
|
.action = boot_usb_action,
|
|
},
|
|
[VB_DEV_DISK] = {
|
|
.text = "Boot From Internal Disk",
|
|
.action = boot_disk_action,
|
|
},
|
|
[VB_DEV_CANCEL] = {
|
|
.text = "Cancel",
|
|
.action = enter_dev_warning_menu,
|
|
},
|
|
[VB_DEV_POWER_OFF] = {
|
|
.text = "Power Off",
|
|
.action = power_off_action,
|
|
},
|
|
[VB_DEV_LANGUAGE] = {
|
|
.text = "Language",
|
|
.action = enter_language_menu,
|
|
},
|
|
},
|
|
},
|
|
[VB_MENU_TO_NORM] = {
|
|
.size = VB_TO_NORM_COUNT,
|
|
.screen = VB_SCREEN_DEVELOPER_TO_NORM_MENU,
|
|
.items = (struct vb2_menu_item[]){
|
|
[VB_TO_NORM_CONFIRM] = {
|
|
.text = "Confirm Enabling OS Verification",
|
|
.action = to_norm_action,
|
|
},
|
|
[VB_TO_NORM_CANCEL] = {
|
|
.text = "Cancel",
|
|
.action = enter_dev_warning_menu,
|
|
},
|
|
[VB_TO_NORM_POWER_OFF] = {
|
|
.text = "Power Off",
|
|
.action = power_off_action,
|
|
},
|
|
[VB_TO_NORM_LANGUAGE] = {
|
|
.text = "Language",
|
|
.action = enter_language_menu,
|
|
},
|
|
},
|
|
},
|
|
[VB_MENU_TO_DEV] = {
|
|
.size = VB_TO_DEV_COUNT,
|
|
.screen = VB_SCREEN_RECOVERY_TO_DEV_MENU,
|
|
.items = (struct vb2_menu_item[]){
|
|
[VB_TO_DEV_CONFIRM] = {
|
|
.text = "Confirm Disabling OS Verification",
|
|
.action = to_dev_action,
|
|
},
|
|
[VB_TO_DEV_CANCEL] = {
|
|
.text = "Cancel",
|
|
.action = enter_recovery_base_screen,
|
|
},
|
|
[VB_TO_DEV_POWER_OFF] = {
|
|
.text = "Power Off",
|
|
.action = power_off_action,
|
|
},
|
|
[VB_TO_DEV_LANGUAGE] = {
|
|
.text = "Language",
|
|
.action = enter_language_menu,
|
|
},
|
|
},
|
|
},
|
|
[VB_MENU_LANGUAGES] = {
|
|
.screen = VB_SCREEN_LANGUAGES_MENU,
|
|
/* Rest is filled out dynamically by vb2_init_menus() */
|
|
},
|
|
[VB_MENU_OPTIONS] = {
|
|
.size = VB_OPTIONS_COUNT,
|
|
.screen = VB_SCREEN_OPTIONS_MENU,
|
|
.items = (struct vb2_menu_item[]){
|
|
[VB_OPTIONS_DBG_INFO] = {
|
|
.text = "Show Debug Info",
|
|
.action = debug_info_action,
|
|
},
|
|
[VB_OPTIONS_CANCEL] = {
|
|
.text = "Cancel",
|
|
.action = enter_recovery_base_screen,
|
|
},
|
|
[VB_OPTIONS_POWER_OFF] = {
|
|
.text = "Power Off",
|
|
.action = power_off_action,
|
|
},
|
|
[VB_OPTIONS_LANGUAGE] = {
|
|
.text = "Language",
|
|
.action = enter_language_menu,
|
|
},
|
|
},
|
|
},
|
|
[VB_MENU_RECOVERY_INSERT] = {
|
|
.size = 0,
|
|
.screen = VB_SCREEN_RECOVERY_INSERT,
|
|
.items = NULL,
|
|
},
|
|
[VB_MENU_RECOVERY_NO_GOOD] = {
|
|
.size = 0,
|
|
.screen = VB_SCREEN_RECOVERY_NO_GOOD,
|
|
.items = NULL,
|
|
},
|
|
[VB_MENU_RECOVERY_BROKEN] = {
|
|
.size = 0,
|
|
.screen = VB_SCREEN_OS_BROKEN,
|
|
.items = NULL,
|
|
},
|
|
[VB_MENU_TO_NORM_CONFIRMED] = {
|
|
.size = 0,
|
|
.screen = VB_SCREEN_TO_NORM_CONFIRMED,
|
|
.items = NULL,
|
|
},
|
|
};
|
|
|
|
/* Initialize menu state. Must be called once before displaying any menus. */
|
|
static VbError_t vb2_init_menus(struct vb2_context *ctx)
|
|
{
|
|
struct vb2_menu_item *items;
|
|
uint32_t count;
|
|
int i;
|
|
|
|
/* Initialize language menu with the correct amount of entries. */
|
|
VbExGetLocalizationCount(&count);
|
|
if (!count)
|
|
count = 1; /* Always need at least one language entry. */
|
|
|
|
items = malloc(count * sizeof(struct vb2_menu_item));
|
|
if (!items)
|
|
return VBERROR_UNKNOWN;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
items[i].text = "Some Language";
|
|
items[i].action = language_action;
|
|
}
|
|
menus[VB_MENU_LANGUAGES].size = count;
|
|
menus[VB_MENU_LANGUAGES].items = items;
|
|
|
|
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 key VOL_KEY_UP = increase index selection
|
|
* VOL_KEY_DOWN = decrease index selection.
|
|
* Every other key has no effect now.
|
|
*/
|
|
static void vb2_update_selection(uint32_t key) {
|
|
int idx;
|
|
|
|
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 < menus[current_menu].size &&
|
|
((1 << idx) & disabled_idx_mask))
|
|
idx++;
|
|
/* Only update if idx is valid */
|
|
if (idx < menus[current_menu].size)
|
|
current_menu_idx = idx;
|
|
break;
|
|
default:
|
|
VB2_DEBUG("ERROR: %s called with key 0x%x!\n", __func__, key);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static VbError_t vb2_handle_menu_input(struct vb2_context *ctx,
|
|
uint32_t key, uint32_t 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:
|
|
/* Untrusted (USB keyboard) input disabled for TO_DEV menu. */
|
|
if (current_menu == VB_MENU_TO_DEV &&
|
|
!(key_flags & VB_KEY_FLAG_TRUSTED_KEYBOARD)) {
|
|
vb2_flash_screen(ctx);
|
|
vb2_error_beep();
|
|
break;
|
|
}
|
|
|
|
/* Menuless screens enter OPTIONS on volume button press. */
|
|
if (!menus[current_menu].size) {
|
|
enter_options_menu(ctx);
|
|
break;
|
|
}
|
|
|
|
vb2_update_selection(key);
|
|
vb2_draw_current_screen(ctx);
|
|
break;
|
|
case VB_BUTTON_POWER_SHORT_PRESS:
|
|
case '\r':
|
|
/* Menuless screens shut down on power button press. */
|
|
if (!menus[current_menu].size)
|
|
return VBERROR_SHUTDOWN_REQUESTED;
|
|
|
|
return menus[current_menu].items[current_menu_idx].action(ctx);
|
|
default:
|
|
VB2_DEBUG("pressed key 0x%x\n", key);
|
|
break;
|
|
}
|
|
|
|
if (VbWantShutdownMenu(ctx)) {
|
|
VB2_DEBUG("shutdown requested!\n");
|
|
return VBERROR_SHUTDOWN_REQUESTED;
|
|
}
|
|
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
/**
|
|
* Main function that handles developer warning menu functionality
|
|
*
|
|
* @param ctx Vboot2 context
|
|
* @return VBERROR_SUCCESS, or non-zero error code if error.
|
|
*/
|
|
static VbError_t vb2_developer_menu(struct vb2_context *ctx)
|
|
{
|
|
struct vb2_shared_data *sd = vb2_get_sd(ctx);
|
|
VbError_t ret;
|
|
|
|
/* Check if the default is to boot using disk, usb, or legacy */
|
|
default_boot = vb2_nv_get(ctx, VB2_NV_DEV_DEFAULT_BOOT);
|
|
if (sd->gbb_flags & VB2_GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY)
|
|
default_boot = VB2_DEV_DEFAULT_BOOT_LEGACY;
|
|
|
|
/* Check if developer mode is disabled by FWMP */
|
|
disable_dev_boot = 0;
|
|
if (vb2_get_fwmp_flags() & FWMP_DEV_DISABLE_BOOT) {
|
|
if (sd->gbb_flags & VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON) {
|
|
VB2_DEBUG("FWMP_DEV_DISABLE_BOOT rejected by"
|
|
"FORCE_DEV_SWITCH_ON\n");
|
|
} else {
|
|
/* If dev mode is disabled, only allow TONORM */
|
|
disable_dev_boot = 1;
|
|
VB2_DEBUG("dev_disable_boot is set.\n");
|
|
}
|
|
}
|
|
|
|
/* Show appropriate initial menu */
|
|
if (disable_dev_boot)
|
|
enter_to_norm_menu(ctx);
|
|
else
|
|
enter_dev_warning_menu(ctx);
|
|
|
|
/* Get audio/delay context */
|
|
vb2_audio_start(ctx);
|
|
|
|
/* We'll loop until we finish the delay or are interrupted */
|
|
do {
|
|
uint32_t key = VbExKeyboardRead();
|
|
|
|
/* Make sure user knows dev mode disabled */
|
|
if (disable_dev_boot)
|
|
VbExDisplayDebugInfo(dev_disable_msg);
|
|
|
|
switch (key) {
|
|
case VB_BUTTON_VOL_DOWN_LONG_PRESS:
|
|
case 'D' & 0x1f:
|
|
/* Ctrl+D = boot from internal disk */
|
|
ret = boot_disk_action(ctx);
|
|
break;
|
|
case 'L' & 0x1f:
|
|
/* Ctrl+L = boot legacy BIOS */
|
|
ret = boot_legacy_action(ctx);
|
|
break;
|
|
case VB_BUTTON_VOL_UP_LONG_PRESS:
|
|
case 'U' & 0x1f:
|
|
/* Ctrl+U = boot from USB or SD card */
|
|
ret = boot_usb_action(ctx);
|
|
break;
|
|
default:
|
|
ret = vb2_handle_menu_input(ctx, key, 0);
|
|
break;
|
|
}
|
|
|
|
/* We may have loaded a kernel or decided to shut down now. */
|
|
if (ret != VBERROR_KEEP_LOOPING)
|
|
return ret;
|
|
|
|
/* Reset 30 second timer whenever we see a new key. */
|
|
if (key != 0)
|
|
vb2_audio_start(ctx);
|
|
|
|
/* If dev mode was disabled, loop forever (never timeout) */
|
|
} while(disable_dev_boot ? 1 : vb2_audio_looping());
|
|
|
|
if (default_boot == VB2_DEV_DEFAULT_BOOT_LEGACY)
|
|
boot_legacy_action(ctx); /* Doesn't return on success. */
|
|
|
|
if (default_boot == VB2_DEV_DEFAULT_BOOT_USB)
|
|
if (VBERROR_SUCCESS == boot_usb_action(ctx))
|
|
return VBERROR_SUCCESS;
|
|
|
|
return boot_disk_action(ctx);
|
|
}
|
|
|
|
/* Developer mode entry point. */
|
|
VbError_t VbBootDeveloperMenu(struct vb2_context *ctx)
|
|
{
|
|
VbError_t retval = vb2_init_menus(ctx);
|
|
if (VBERROR_SUCCESS != retval)
|
|
return retval;
|
|
retval = vb2_developer_menu(ctx);
|
|
VbDisplayScreen(ctx, 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
|
|
* @return VBERROR_SUCCESS, or non-zero error code if error.
|
|
*/
|
|
static VbError_t recovery_ui(struct vb2_context *ctx)
|
|
{
|
|
VbSharedDataHeader *vbsd = vb2_get_sd(ctx)->vbsd;
|
|
uint32_t key;
|
|
uint32_t key_flags;
|
|
VbError_t ret;
|
|
int i;
|
|
|
|
if (!vb2_allow_recovery(vbsd->flags)) {
|
|
/*
|
|
* We have to save the reason here so that it will survive
|
|
* coming up three-finger-salute. We're saving it in
|
|
* VB2_RECOVERY_SUBCODE to avoid a recovery loop.
|
|
* If we save the reason in VB2_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",
|
|
vbsd->recovery_reason);
|
|
vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE,
|
|
vbsd->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, VB_SCREEN_OS_BROKEN, 0);
|
|
vb2_change_menu(VB_MENU_RECOVERY_BROKEN, 0);
|
|
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, key);
|
|
if (VbWantShutdownMenu(ctx))
|
|
return VBERROR_SHUTDOWN_REQUESTED;
|
|
}
|
|
VbExSleepMs(REC_KEY_DELAY);
|
|
}
|
|
}
|
|
|
|
/* Loop and wait for a recovery image */
|
|
VB2_DEBUG("waiting for a recovery image\n");
|
|
|
|
usb_nogood = -1;
|
|
while (1) {
|
|
VB2_DEBUG("attempting to load kernel2\n");
|
|
ret = VbTryLoadKernel(ctx, 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,
|
|
VB2_RECOVERY_NOT_REQUESTED);
|
|
|
|
if (VBERROR_SUCCESS == ret)
|
|
break; /* Found a recovery kernel */
|
|
|
|
if (usb_nogood != (ret != VBERROR_NO_DISK_FOUND)) {
|
|
/* USB state changed, force back to base screen */
|
|
usb_nogood = ret != VBERROR_NO_DISK_FOUND;
|
|
enter_recovery_base_screen(ctx);
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
if (key == VB_BUTTON_VOL_UP_DOWN_COMBO_PRESS) {
|
|
if (key_flags & VB_KEY_FLAG_TRUSTED_KEYBOARD)
|
|
enter_to_dev_menu(ctx);
|
|
else
|
|
VB2_DEBUG("ERROR: untrusted combo?!\n");
|
|
} else {
|
|
ret = vb2_handle_menu_input(ctx, key,
|
|
key_flags);
|
|
if (ret != VBERROR_KEEP_LOOPING)
|
|
return ret;
|
|
}
|
|
VbExSleepMs(REC_KEY_DELAY);
|
|
}
|
|
}
|
|
|
|
return VBERROR_SUCCESS;
|
|
}
|
|
|
|
/* Recovery mode entry point. */
|
|
VbError_t VbBootRecoveryMenu(struct vb2_context *ctx)
|
|
{
|
|
VbError_t retval = vb2_init_menus(ctx);
|
|
if (VBERROR_SUCCESS != retval)
|
|
return retval;
|
|
retval = recovery_ui(ctx);
|
|
VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0);
|
|
return retval;
|
|
}
|