blob: 9827c1ea1f4443824b3a991d14b210a70ba41f50 [file] [log] [blame]
/* 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 "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_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. */
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++) {
/* 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;
}