950 lines
26 KiB
C
950 lines
26 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 "2common.h"
|
|
#include "2misc.h"
|
|
#include "2nvstorage.h"
|
|
#include "2rsa.h"
|
|
#include "2secdata.h"
|
|
#include "2sysincludes.h"
|
|
#include "ec_sync.h"
|
|
#include "load_kernel_fw.h"
|
|
#include "secdata_tpm.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_common.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, force_redraw;
|
|
static uint32_t default_boot;
|
|
static uint32_t disable_dev_boot;
|
|
static uint32_t altfw_allowed;
|
|
static struct vb2_menu menus[];
|
|
static const char no_legacy[] = "Legacy boot failed. Missing BIOS?\n";
|
|
|
|
/**
|
|
* 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_gbb_header *gbb = vb2_get_gbb(ctx);
|
|
uint32_t shutdown_request = VbExIsShutdownRequested();
|
|
|
|
/* If desired, ignore shutdown request due to lid closure. */
|
|
if (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 vb2_error_t vb2_draw_current_screen(struct vb2_context *ctx) {
|
|
vb2_error_t ret = VbDisplayMenu(ctx, menus[current_menu].screen,
|
|
force_redraw, current_menu_idx, disabled_idx_mask);
|
|
force_redraw = 0;
|
|
return ret;
|
|
}
|
|
|
|
/* 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, NULL);
|
|
VbExSleepMs(50);
|
|
vb2_draw_current_screen(ctx);
|
|
}
|
|
|
|
static void vb2_log_menu_change(void)
|
|
{
|
|
if (menus[current_menu].size)
|
|
VB2_DEBUG("================ %s Menu ================ [ %s ]\n",
|
|
menus[current_menu].name,
|
|
menus[current_menu].items[current_menu_idx].text);
|
|
else
|
|
VB2_DEBUG("=============== %s Screen ===============\n",
|
|
menus[current_menu].name);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
/* 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;
|
|
|
|
/* Enable menu items for the selected bootloaders */
|
|
if (current_menu == VB_MENU_ALT_FW) {
|
|
disabled_idx_mask = ~(VbExGetAltFwIdxMask() >> 1);
|
|
|
|
/* Make sure 'cancel' is shown even with an invalid mask */
|
|
disabled_idx_mask &= (1 << VB_ALTFW_COUNT) - 1;
|
|
}
|
|
/* We assume that there is at least one enabled item */
|
|
while ((1 << new_current_menu_idx) & disabled_idx_mask)
|
|
new_current_menu_idx++;
|
|
if (new_current_menu_idx < menus[current_menu].size)
|
|
current_menu_idx = new_current_menu_idx;
|
|
|
|
vb2_log_menu_change();
|
|
}
|
|
|
|
/************************
|
|
* Menu Actions *
|
|
************************/
|
|
|
|
/* Boot from internal disk if allowed. */
|
|
static vb2_error_t boot_disk_action(struct vb2_context *ctx)
|
|
{
|
|
if (disable_dev_boot) {
|
|
vb2_flash_screen(ctx);
|
|
vb2_error_notify("Developer mode disabled\n", NULL,
|
|
VB_BEEP_NOT_ALLOWED);
|
|
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 vb2_error_t boot_legacy_action(struct vb2_context *ctx)
|
|
{
|
|
if (disable_dev_boot) {
|
|
vb2_flash_screen(ctx);
|
|
vb2_error_notify("Developer mode disabled\n", NULL,
|
|
VB_BEEP_NOT_ALLOWED);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
if (!altfw_allowed) {
|
|
vb2_flash_screen(ctx);
|
|
vb2_error_notify("WARNING: Booting legacy BIOS has not "
|
|
"been enabled. Refer to the developer"
|
|
"-mode documentation for details.\n",
|
|
"Legacy boot is disabled\n",
|
|
VB_BEEP_NOT_ALLOWED);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
vb2_run_altfw(ctx, VB_ALTFW_DEFAULT);
|
|
vb2_flash_screen(ctx);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
/* Boot from USB or SD card if allowed and available. */
|
|
static vb2_error_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_notify("Developer mode disabled\n", NULL,
|
|
VB_BEEP_NOT_ALLOWED);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
if (!vb2_nv_get(ctx, VB2_NV_DEV_BOOT_USB) &&
|
|
!(vb2_get_gbb(ctx)->flags & VB2_GBB_FLAG_FORCE_DEV_BOOT_USB) &&
|
|
!(vb2_get_fwmp_flags() & FWMP_DEV_ENABLE_USB)) {
|
|
vb2_flash_screen(ctx);
|
|
vb2_error_notify("WARNING: Booting from external media "
|
|
"(USB/SD) has not been enabled. Refer "
|
|
"to the developer-mode documentation "
|
|
"for details.\n",
|
|
"USB booting is disabled\n",
|
|
VB_BEEP_NOT_ALLOWED);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
if (VB2_SUCCESS == VbTryLoadKernel(ctx, VB_DISK_FLAG_REMOVABLE)) {
|
|
VB2_DEBUG("booting USB\n");
|
|
return VB2_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_error_notify(no_kernel, NULL, VB_BEEP_FAILED);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
static vb2_error_t enter_developer_menu(struct vb2_context *ctx)
|
|
{
|
|
int menu_idx;
|
|
switch(default_boot) {
|
|
default:
|
|
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 vb2_error_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 vb2_error_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 vb2_error_t enter_recovery_base_screen(struct vb2_context *ctx)
|
|
{
|
|
if (!vb2_allow_recovery(ctx))
|
|
vb2_change_menu(VB_MENU_RECOVERY_BROKEN, 0);
|
|
else 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 vb2_error_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 vb2_error_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_error_notify(dev_already_on, NULL, VB_BEEP_NOT_ALLOWED);
|
|
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 vb2_error_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;
|
|
}
|
|
|
|
/* Boot alternative bootloader if allowed and available. */
|
|
static vb2_error_t enter_altfw_menu(struct vb2_context *ctx)
|
|
{
|
|
VB2_DEBUG("enter_altfw_menu()\n");
|
|
if (disable_dev_boot) {
|
|
vb2_flash_screen(ctx);
|
|
vb2_error_beep(VB_BEEP_NOT_ALLOWED);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
if (!altfw_allowed) {
|
|
vb2_flash_screen(ctx);
|
|
vb2_error_no_altfw();
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
vb2_change_menu(VB_MENU_ALT_FW, 0);
|
|
vb2_draw_current_screen(ctx);
|
|
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
static vb2_error_t debug_info_action(struct vb2_context *ctx)
|
|
{
|
|
force_redraw = 1;
|
|
VbDisplayDebugInfo(ctx);
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
/* Action when selecting a language entry in the language menu. */
|
|
static vb2_error_t language_action(struct vb2_context *ctx)
|
|
{
|
|
VbSharedDataHeader *vbsd = vb2_get_sd(ctx)->vbsd;
|
|
|
|
/* Write selected language ID back to NVRAM. */
|
|
vb2_nv_set(ctx, VB2_NV_LOCALIZATION_INDEX, current_menu_idx);
|
|
|
|
/*
|
|
* Non-manual recovery mode is meant to be left via hard reset (into
|
|
* manual recovery mode). Need to commit NVRAM changes immediately.
|
|
*/
|
|
if (vbsd->recovery_reason && !vb2_allow_recovery(ctx))
|
|
vb2_nv_commit(ctx);
|
|
|
|
/* Return to previous menu. */
|
|
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 when selecting a bootloader in the alternative firmware menu. */
|
|
static vb2_error_t altfw_action(struct vb2_context *ctx)
|
|
{
|
|
vb2_run_altfw(ctx, current_menu_idx + 1);
|
|
vb2_flash_screen(ctx);
|
|
VB2_DEBUG(no_legacy);
|
|
VbExDisplayDebugInfo(no_legacy, 0);
|
|
|
|
return VBERROR_KEEP_LOOPING;
|
|
}
|
|
|
|
/* Action that enables developer mode and reboots. */
|
|
static vb2_error_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_BOOT_DEV_SWITCH_ON) ||
|
|
!vb2_allow_recovery(ctx))
|
|
return VBERROR_KEEP_LOOPING;
|
|
|
|
VB2_DEBUG("Enabling dev-mode...\n");
|
|
if (VB2_SUCCESS != SetVirtualDevMode(1))
|
|
return VBERROR_TPM_SET_BOOT_MODE_STATE;
|
|
|
|
/* This was meant for headless devices, shouldn't really matter here. */
|
|
if (VbExGetSwitches(VB_SWITCH_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 vb2_error_t to_norm_action(struct vb2_context *ctx)
|
|
{
|
|
if (vb2_get_gbb(ctx)->flags & VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON) {
|
|
vb2_flash_screen(ctx);
|
|
vb2_error_notify("WARNING: TONORM prohibited by "
|
|
"GBB FORCE_DEV_SWITCH_ON.\n", NULL,
|
|
VB_BEEP_NOT_ALLOWED);
|
|
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 vb2_error_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;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
vb2_log_menu_change();
|
|
}
|
|
|
|
static vb2_error_t vb2_handle_menu_input(struct vb2_context *ctx,
|
|
uint32_t key, uint32_t key_flags)
|
|
{
|
|
switch (key) {
|
|
case 0:
|
|
/* nothing pressed */
|
|
break;
|
|
case '\t':
|
|
/* Tab = display debug info */
|
|
return debug_info_action(ctx);
|
|
case VB_KEY_ESC:
|
|
/* Esc = redraw screen (to clear old debug info) */
|
|
vb2_draw_current_screen(ctx);
|
|
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_notify("Please use the on-device volume "
|
|
"buttons to navigate\n",
|
|
"vb2_handle_menu_input() - Untrusted "
|
|
"(USB keyboard) input disabled\n",
|
|
VB_BEEP_NOT_ALLOWED);
|
|
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 VB_KEY_ENTER:
|
|
/* 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;
|
|
}
|
|
|
|
/* Delay in developer menu */
|
|
#define DEV_KEY_DELAY 20 /* Check keys every 20ms */
|
|
|
|
/* 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] = {
|
|
.name = "Developer 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] = {
|
|
.name = "Developer Boot Options",
|
|
.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 = enter_altfw_menu,
|
|
},
|
|
[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] = {
|
|
.name = "TO_NORM Confirmation",
|
|
.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] = {
|
|
.name = "TO_DEV Confirmation",
|
|
.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] = {
|
|
.name = "Language Selection",
|
|
.screen = VB_SCREEN_LANGUAGES_MENU,
|
|
/* Rest is filled out dynamically by vb2_init_menus() */
|
|
},
|
|
[VB_MENU_OPTIONS] = {
|
|
.name = "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] = {
|
|
.name = "Recovery INSERT",
|
|
.size = 0,
|
|
.screen = VB_SCREEN_RECOVERY_INSERT,
|
|
.items = NULL,
|
|
},
|
|
[VB_MENU_RECOVERY_NO_GOOD] = {
|
|
.name = "Recovery NO_GOOD",
|
|
.size = 0,
|
|
.screen = VB_SCREEN_RECOVERY_NO_GOOD,
|
|
.items = NULL,
|
|
},
|
|
[VB_MENU_RECOVERY_BROKEN] = {
|
|
.name = "Non-manual Recovery (BROKEN)",
|
|
.size = 0,
|
|
.screen = VB_SCREEN_OS_BROKEN,
|
|
.items = NULL,
|
|
},
|
|
[VB_MENU_TO_NORM_CONFIRMED] = {
|
|
.name = "TO_NORM Interstitial",
|
|
.size = 0,
|
|
.screen = VB_SCREEN_TO_NORM_CONFIRMED,
|
|
.items = NULL,
|
|
},
|
|
[VB_MENU_ALT_FW] = {
|
|
.name = "Alternative Firmware Selection",
|
|
.screen = VB_SCREEN_ALT_FW_MENU,
|
|
.size = VB_ALTFW_COUNT + 1,
|
|
.items = (struct vb2_menu_item[]) {{
|
|
.text = "Bootloader 1",
|
|
.action = altfw_action,
|
|
}, {
|
|
.text = "Bootloader 2",
|
|
.action = altfw_action,
|
|
}, {
|
|
.text = "Bootloader 3",
|
|
.action = altfw_action,
|
|
}, {
|
|
.text = "Bootloader 4",
|
|
.action = altfw_action,
|
|
}, {
|
|
.text = "Bootloader 5",
|
|
.action = altfw_action,
|
|
}, {
|
|
.text = "Bootloader 6",
|
|
.action = altfw_action,
|
|
}, {
|
|
.text = "Bootloader 7",
|
|
.action = altfw_action,
|
|
}, {
|
|
.text = "Bootloader 8",
|
|
.action = altfw_action,
|
|
}, {
|
|
.text = "Bootloader 9",
|
|
.action = altfw_action,
|
|
}, {
|
|
.text = "Cancel",
|
|
.action = enter_developer_menu,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
/* Initialize menu state. Must be called once before displaying any menus. */
|
|
static vb2_error_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. */
|
|
if (VB2_SUCCESS != VbExGetLocalizationCount(&count) || count == 0)
|
|
count = 1; /* Fall back to 1 language entry on failure */
|
|
|
|
items = malloc(count * sizeof(struct vb2_menu_item));
|
|
if (!items)
|
|
return VB2_ERROR_UNKNOWN;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
/* The actual language is drawn by the bootloader */
|
|
items[i].text = "Some Language";
|
|
items[i].action = language_action;
|
|
}
|
|
menus[VB_MENU_LANGUAGES].size = count;
|
|
menus[VB_MENU_LANGUAGES].items = items;
|
|
|
|
return VB2_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Main function that handles developer warning menu functionality
|
|
*
|
|
* @param ctx Vboot2 context
|
|
* @return VB2_SUCCESS, or non-zero error code if error.
|
|
*/
|
|
static vb2_error_t vb2_developer_menu(struct vb2_context *ctx)
|
|
{
|
|
struct vb2_gbb_header *gbb = vb2_get_gbb(ctx);
|
|
vb2_error_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 (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 (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");
|
|
}
|
|
}
|
|
altfw_allowed = vb2_nv_get(ctx, VB2_NV_DEV_BOOT_LEGACY) ||
|
|
(gbb->flags & VB2_GBB_FLAG_FORCE_DEV_BOOT_LEGACY) ||
|
|
(vb2_get_fwmp_flags() & FWMP_DEV_ENABLE_LEGACY);
|
|
|
|
/* 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, 0);
|
|
|
|
switch (key) {
|
|
case VB_BUTTON_VOL_DOWN_LONG_PRESS:
|
|
case VB_KEY_CTRL('D'):
|
|
/* Ctrl+D = boot from internal disk */
|
|
ret = boot_disk_action(ctx);
|
|
break;
|
|
case VB_KEY_CTRL('L'):
|
|
/* Ctrl+L = boot alternative bootloader */
|
|
ret = enter_altfw_menu(ctx);
|
|
break;
|
|
case VB_BUTTON_VOL_UP_LONG_PRESS:
|
|
case VB_KEY_CTRL('U'):
|
|
/* Ctrl+U = boot from USB or SD card */
|
|
ret = boot_usb_action(ctx);
|
|
break;
|
|
/* We allow selection of the default '0' bootloader here */
|
|
case '0'...'9':
|
|
VB2_DEBUG("VbBootDeveloper() - "
|
|
"user pressed key '%c': Boot alternative "
|
|
"firmware\n", key);
|
|
vb2_try_alt_fw(ctx, altfw_allowed, key - '0');
|
|
ret = VBERROR_KEEP_LOOPING;
|
|
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);
|
|
|
|
VbExSleepMs(DEV_KEY_DELAY);
|
|
|
|
/* 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 (VB2_SUCCESS == boot_usb_action(ctx))
|
|
return VB2_SUCCESS;
|
|
|
|
return boot_disk_action(ctx);
|
|
}
|
|
|
|
/* Developer mode entry point. */
|
|
vb2_error_t VbBootDeveloperMenu(struct vb2_context *ctx)
|
|
{
|
|
vb2_error_t retval = vb2_init_menus(ctx);
|
|
if (VB2_SUCCESS != retval)
|
|
return retval;
|
|
retval = vb2_developer_menu(ctx);
|
|
VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL);
|
|
return retval;
|
|
}
|
|
|
|
/* Main function that handles non-manual recovery (BROKEN) menu functionality */
|
|
static vb2_error_t broken_ui(struct vb2_context *ctx)
|
|
{
|
|
VbSharedDataHeader *vbsd = vb2_get_sd(ctx)->vbsd;
|
|
|
|
/*
|
|
* Temporarily stash recovery reason in subcode so we'll still know what
|
|
* to display if the user reboots into manual recovery from here. Commit
|
|
* immediately since the user may hard-reset out of here.
|
|
*/
|
|
VB2_DEBUG("saving recovery reason (%#x)\n", vbsd->recovery_reason);
|
|
vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, vbsd->recovery_reason);
|
|
vb2_nv_commit(ctx);
|
|
|
|
enter_recovery_base_screen(ctx);
|
|
|
|
/* Loop and wait for the user to reset or shut down. */
|
|
VB2_DEBUG("waiting for manual recovery\n");
|
|
while (1) {
|
|
uint32_t key = VbExKeyboardRead();
|
|
vb2_error_t ret = vb2_handle_menu_input(ctx, key, 0);
|
|
if (ret != VBERROR_KEEP_LOOPING)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* 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 VB2_SUCCESS, or non-zero error code if error.
|
|
*/
|
|
static vb2_error_t recovery_ui(struct vb2_context *ctx)
|
|
{
|
|
uint32_t key;
|
|
uint32_t key_flags;
|
|
vb2_error_t ret;
|
|
int i;
|
|
|
|
/* 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 (VB2_SUCCESS == ret)
|
|
return ret; /* 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Recovery mode entry point. */
|
|
vb2_error_t VbBootRecoveryMenu(struct vb2_context *ctx)
|
|
{
|
|
vb2_error_t retval = vb2_init_menus(ctx);
|
|
if (VB2_SUCCESS != retval)
|
|
return retval;
|
|
if (vb2_allow_recovery(ctx))
|
|
retval = recovery_ui(ctx);
|
|
else
|
|
retval = broken_ui(ctx);
|
|
VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL);
|
|
return retval;
|
|
}
|