blob: 027b49ea4c093ae03e8d42d261de698b20209262 [file] [log] [blame] [edit]
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* High-level firmware wrapper API - user interface for RW firmware
*/
#include "2common.h"
#include "2misc.h"
#include "2nvstorage.h"
#include "2rsa.h"
#include "2secdata.h"
#include "2sysincludes.h"
#include "load_kernel_fw.h"
#include "tlcl.h"
#include "vb2_common.h"
#include "vboot_api.h"
#include "vboot_audio.h"
#include "vboot_kernel.h"
#include "vboot_struct.h"
#include "vboot_test.h"
#include "vboot_ui_legacy.h"
#include "vboot_ui_legacy_wilco.h"
static uint32_t disp_current_screen = VB_SCREEN_BLANK;
test_mockable
vb2_error_t VbDisplayScreen(struct vb2_context *ctx, uint32_t screen, int force,
const VbScreenData *data)
{
uint32_t locale;
/* If requested screen is the same as the current one, we're done. */
if (disp_current_screen == screen && !force)
return VB2_SUCCESS;
/* Keep track of the currently displayed screen */
disp_current_screen = screen;
/* Read the locale last saved */
locale = vb2_nv_get(ctx, VB2_NV_LOCALIZATION_INDEX);
return VbExDisplayScreen(screen, locale, data);
}
static vb2_error_t VbTryUsb(struct vb2_context *ctx)
{
int retval = VbTryLoadKernel(ctx, VB_DISK_FLAG_REMOVABLE);
if (VB2_SUCCESS == retval) {
VB2_DEBUG("developer UI - booting USB\n");
} else {
vb2_error_notify("Could not boot from USB\n",
"developer UI - no kernel found on USB\n",
VB_BEEP_FAILED);
}
return retval;
}
int VbUserConfirms(struct vb2_context *ctx, uint32_t confirm_flags)
{
uint32_t key;
uint32_t key_flags;
uint32_t btn;
int button_pressed = 0;
int shutdown_requested = 0;
VB2_DEBUG("Entering(%x)\n", confirm_flags);
/* Await further instructions */
do {
key = VbExKeyboardReadWithFlags(&key_flags);
shutdown_requested = vb2_want_shutdown(ctx, key);
switch (key) {
case VB_KEY_ENTER:
/* If we are using a trusted keyboard or a trusted
* keyboard is not required then return yes, otherwise
* keep waiting (for instance if the user is using a
* USB keyboard).
*/
if (!(confirm_flags & VB_CONFIRM_MUST_TRUST_KEYBOARD) ||
(key_flags & VB_KEY_FLAG_TRUSTED_KEYBOARD)) {
VB2_DEBUG("Yes (1)\n");
return 1;
}
/*
* If physical presence is confirmed using the keyboard,
* beep and notify the user when the ENTER key comes
* from an untrusted keyboard.
*
* If physical presence is confirmed using a physical
* button, the existing message on the screen will
* instruct the user which button to push. Silently
* ignore any ENTER presses.
*/
if (PHYSICAL_PRESENCE_KEYBOARD)
vb2_error_notify("Please use internal keyboard "
"to confirm\n",
"VbUserConfirms() - "
"Trusted keyboard is required\n",
VB_BEEP_NOT_ALLOWED);
break;
case ' ':
VB2_DEBUG("Space (%d)\n",
confirm_flags & VB_CONFIRM_SPACE_MEANS_NO);
if (confirm_flags & VB_CONFIRM_SPACE_MEANS_NO)
return 0;
break;
case VB_KEY_ESC:
VB2_DEBUG("No (0)\n");
return 0;
default:
/*
* If the physical presence button is separate from the
* keyboard, and is pressed, this is also a YES, but
* must wait for release.
*/
if (!PHYSICAL_PRESENCE_KEYBOARD) {
btn = vb2ex_physical_presence_pressed();
if (btn) {
VB2_DEBUG("Presence button pressed, "
"awaiting release\n");
button_pressed = 1;
} else if (button_pressed) {
VB2_DEBUG("Presence button released "
"(1)\n");
return 1;
}
}
VbCheckDisplayKey(ctx, key, disp_current_screen, NULL);
}
vb2ex_msleep(KEY_DELAY_MS);
} while (!shutdown_requested);
return -1;
}
/*
* User interface for selecting alternative firmware
*
* This shows the user a list of bootloaders and allows selection of one of
* them. We loop forever until something is chosen or Escape is pressed.
*/
static vb2_error_t vb2_altfw_ui(struct vb2_context *ctx)
{
int active = 1;
VbDisplayScreen(ctx, VB_SCREEN_ALT_FW_PICK, 0, NULL);
/* We'll loop until the user decides what to do */
do {
uint32_t key = VbExKeyboardRead();
if (vb2_want_shutdown(ctx, key)) {
VB2_DEBUG("developer UI - shutdown requested!\n");
return VB2_REQUEST_SHUTDOWN;
}
switch (key) {
case 0:
/* nothing pressed */
break;
case VB_KEY_ESC:
/* Escape pressed - return to developer screen */
VB2_DEBUG("developer UI - user pressed Esc; "
"exit to Developer screen\n");
active = 0;
break;
/* We allow selection of the default '0' bootloader here */
case '0'...'9':
VB2_DEBUG("developer UI - user pressed key '%c';"
"Boot alternative firmware\n", key);
/*
* This will not return if successful. Drop out to
* developer mode on failure.
*/
vb2_try_altfw(ctx, 1, key - '0');
active = 0;
break;
default:
VB2_DEBUG("developer UI - pressed key %#x\n", key);
VbCheckDisplayKey(ctx, key, disp_current_screen, NULL);
break;
}
vb2ex_msleep(KEY_DELAY_MS);
} while (active);
/* Back to developer screen */
VbDisplayScreen(ctx, VB_SCREEN_DEVELOPER_WARNING, 0, NULL);
return 0;
}
static vb2_error_t vb2_developer_ui(struct vb2_context *ctx)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
struct vb2_gbb_header *gbb = vb2_get_gbb(ctx);
uint32_t disable_dev_boot = 0;
uint32_t use_usb = 0;
uint32_t use_legacy = 0;
uint32_t ctrl_d_pressed = 0;
VB2_DEBUG("Entering\n");
/* Check if booting from external disk is allowed */
uint32_t allow_usb = vb2_nv_get(ctx, VB2_NV_DEV_BOOT_EXTERNAL);
uint32_t allow_legacy = vb2_nv_get(ctx, VB2_NV_DEV_BOOT_LEGACY);
/* Check if the default boot target: internal/external disk or legacy */
uint32_t default_boot = vb2_nv_get(ctx, VB2_NV_DEV_DEFAULT_BOOT);
if (default_boot == VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL)
use_usb = 1;
if (default_boot == VB2_DEV_DEFAULT_BOOT_TARGET_LEGACY)
use_legacy = 1;
/* Handle GBB flag override */
if (gbb->flags & VB2_GBB_FLAG_FORCE_DEV_BOOT_USB)
allow_usb = 1;
if (gbb->flags & VB2_GBB_FLAG_FORCE_DEV_BOOT_LEGACY)
allow_legacy = 1;
if (gbb->flags & VB2_GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY) {
use_legacy = 1;
use_usb = 0;
}
/* Handle FWMP override */
if (vb2_secdata_fwmp_get_flag(ctx, VB2_SECDATA_FWMP_DEV_ENABLE_EXTERNAL))
allow_usb = 1;
if (vb2_secdata_fwmp_get_flag(ctx, VB2_SECDATA_FWMP_DEV_ENABLE_LEGACY))
allow_legacy = 1;
if (vb2_secdata_fwmp_get_flag(ctx, VB2_SECDATA_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 {
disable_dev_boot = 1;
}
}
/* If dev mode is disabled, only allow TONORM */
while (disable_dev_boot) {
VB2_DEBUG("dev_disable_boot is set\n");
VbDisplayScreen(ctx,
VB_SCREEN_DEVELOPER_TO_NORM, 0, NULL);
VbExDisplayDebugInfo(dev_disable_msg, 0);
/* Ignore space in VbUserConfirms()... */
switch (VbUserConfirms(ctx, 0)) {
case 1:
VB2_DEBUG("leaving dev-mode\n");
vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST, 1);
VbDisplayScreen(ctx,
VB_SCREEN_TO_NORM_CONFIRMED, 0, NULL);
vb2ex_msleep(5 * VB2_MSEC_PER_SEC);
return VB2_REQUEST_REBOOT;
case -1:
VB2_DEBUG("shutdown requested\n");
return VB2_REQUEST_SHUTDOWN;
default:
/* Ignore user attempt to cancel */
VB2_DEBUG("ignore cancel TONORM\n");
}
}
if ((ctx->flags & VB2_CONTEXT_VENDOR_DATA_SETTABLE) &&
VENDOR_DATA_LENGTH > 0) {
vb2_error_t ret;
VB2_DEBUG("developer UI - Vendor data not set\n");
ret = vb2_vendor_data_ui(ctx);
if (ret)
return ret;
}
/* Show the dev mode warning screen */
VbDisplayScreen(ctx, VB_SCREEN_DEVELOPER_WARNING, 0, NULL);
/* Initialize audio/delay context */
vb2_audio_start(ctx);
/* We'll loop until we finish the delay or are interrupted */
do {
uint32_t key = VbExKeyboardRead();
if (vb2_want_shutdown(ctx, key)) {
VB2_DEBUG("developer UI - shutdown requested!\n");
return VB2_REQUEST_SHUTDOWN;
}
switch (key) {
case 0:
/* nothing pressed */
break;
case VB_KEY_ENTER:
/* Only disable virtual dev switch if allowed by GBB */
if (!(gbb->flags & VB2_GBB_FLAG_ENTER_TRIGGERS_TONORM))
break;
VBOOT_FALLTHROUGH;
case ' ':
/* See if we should disable virtual dev-mode switch. */
VB2_DEBUG("sd->flags=%#x\n", sd->flags);
/* Validity check, should never fail. */
VB2_ASSERT(sd->flags & VB2_SD_FLAG_DEV_MODE_ENABLED);
/* Stop the countdown while we go ask... */
if (gbb->flags & VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON) {
/*
* TONORM won't work (only for
* non-shipping devices).
*/
vb2_error_notify(
"WARNING: TONORM prohibited by "
"GBB FORCE_DEV_SWITCH_ON.\n",
NULL, VB_BEEP_NOT_ALLOWED);
break;
}
VbDisplayScreen(ctx, VB_SCREEN_DEVELOPER_TO_NORM,
0, NULL);
/* Ignore space in VbUserConfirms()... */
switch (VbUserConfirms(ctx, 0)) {
case 1:
VB2_DEBUG("leaving dev-mode\n");
vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST, 1);
VbDisplayScreen(ctx,
VB_SCREEN_TO_NORM_CONFIRMED, 0, NULL);
vb2ex_msleep(5 * VB2_MSEC_PER_SEC);
return VB2_REQUEST_REBOOT;
case -1:
VB2_DEBUG("shutdown requested\n");
return VB2_REQUEST_SHUTDOWN;
default:
/* Stay in dev-mode */
VB2_DEBUG("stay in dev-mode\n");
VbDisplayScreen(ctx,
VB_SCREEN_DEVELOPER_WARNING, 0, NULL);
/* Start new countdown */
vb2_audio_start(ctx);
}
break;
case VB_KEY_CTRL('D'):
/* Ctrl+D = dismiss warning; advance to timeout */
VB2_DEBUG("developer UI - user pressed Ctrl+D; "
"skip delay\n");
ctrl_d_pressed = 1;
goto fallout;
case VB_KEY_CTRL('L'):
VB2_DEBUG("developer UI - user pressed Ctrl+L; "
"Try alt firmware\n");
if (allow_legacy) {
vb2_error_t ret;
ret = vb2_altfw_ui(ctx);
if (ret)
return ret;
} else {
vb2_error_no_altfw();
}
break;
case VB_KEY_CTRL_ENTER:
/*
* The Ctrl-Enter is special for Lumpy test purpose;
* fall through to Ctrl+U handler.
*/
case VB_KEY_CTRL('U'):
/* Ctrl+U = try USB boot, or beep if failure */
VB2_DEBUG("developer UI - user pressed Ctrl+U; "
"try USB\n");
if (!allow_usb) {
vb2_error_notify(
"WARNING: Booting from external media "
"(USB/SD) has not been enabled. Refer "
"to the developer-mode documentation "
"for details.\n",
"developer UI - "
"USB booting is disabled\n",
VB_BEEP_NOT_ALLOWED);
} else {
/*
* Clear the screen to show we get the Ctrl+U
* key press.
*/
VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL);
if (VB2_SUCCESS == VbTryUsb(ctx)) {
return VB2_SUCCESS;
} else {
/* Show dev mode warning screen again */
VbDisplayScreen(ctx,
VB_SCREEN_DEVELOPER_WARNING,
0, NULL);
}
}
break;
/* We allow selection of the default '0' bootloader here */
case '0'...'9':
VB2_DEBUG("developer UI - user pressed key '%c'; "
"Boot alternative firmware\n", key);
vb2_try_altfw(ctx, allow_legacy, key - '0');
break;
default:
VB2_DEBUG("developer UI - pressed key %#x\n", key);
VbCheckDisplayKey(ctx, key, disp_current_screen, NULL);
break;
}
vb2ex_msleep(KEY_DELAY_MS);
} while(vb2_audio_looping());
fallout:
/* If defaulting to legacy boot, try that unless Ctrl+D was pressed */
if (use_legacy && !ctrl_d_pressed) {
VB2_DEBUG("developer UI - defaulting to legacy\n");
vb2_try_altfw(ctx, allow_legacy, 0);
}
if ((use_usb && !ctrl_d_pressed) && allow_usb) {
if (VB2_SUCCESS == VbTryUsb(ctx)) {
return VB2_SUCCESS;
}
}
/* Timeout or Ctrl+D; attempt loading from fixed disk */
VB2_DEBUG("developer UI - trying fixed disk\n");
return VbTryLoadKernel(ctx, VB_DISK_FLAG_FIXED);
}
vb2_error_t VbBootDeveloperLegacyClamshell(struct vb2_context *ctx)
{
vb2_reset_power_button();
vb2_error_t retval = vb2_developer_ui(ctx);
VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL);
return retval;
}
vb2_error_t VbBootDiagnosticLegacyClamshell(struct vb2_context *ctx)
{
vb2_reset_power_button();
vb2_error_t retval = vb2_diagnostics_ui(ctx);
VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL);
return retval;
}
static vb2_error_t recovery_ui(struct vb2_context *ctx)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
uint32_t retval;
uint32_t key;
const char release_button_msg[] =
"Release the recovery button and try again\n";
const char recovery_pressed_msg[] =
"^D but recovery switch is pressed\n";
VB2_DEBUG("recovery UI - start\n");
if (!vb2_allow_recovery(ctx)) {
VbDisplayScreen(ctx, VB_SCREEN_OS_BROKEN, 0, NULL);
VB2_DEBUG("recovery UI - waiting for manual recovery\n");
while (1) {
key = VbExKeyboardRead();
VbCheckDisplayKey(ctx, key, disp_current_screen, NULL);
if (vb2_want_shutdown(ctx, key))
return VB2_REQUEST_SHUTDOWN;
else if ((retval =
vb2_check_diagnostic_key(ctx, key)) !=
VB2_SUCCESS)
return retval;
vb2ex_msleep(KEY_DELAY_MS);
}
}
/* Loop and wait for a recovery image */
VB2_DEBUG("recovery UI - waiting for a recovery image\n");
while (1) {
retval = VbTryLoadKernel(ctx, VB_DISK_FLAG_REMOVABLE);
if (VB2_SUCCESS == retval)
break; /* Found a recovery kernel */
enum VbScreenType_t next_screen =
retval == VB2_ERROR_LK_NO_DISK_FOUND ?
VB_SCREEN_RECOVERY_INSERT : VB_SCREEN_RECOVERY_NO_GOOD;
VbDisplayScreen(ctx, next_screen, 0, NULL);
key = VbExKeyboardRead();
/*
* We might want to enter dev-mode from the Insert
* screen if all of the following are true:
* - user pressed Ctrl-D
* - we can honor the virtual dev switch
* - not already in dev mode
* - user forced recovery mode
*/
if (key == VB_KEY_CTRL('D') &&
!(sd->flags & VB2_SD_FLAG_DEV_MODE_ENABLED) &&
(sd->flags & VB2_SD_FLAG_MANUAL_RECOVERY)) {
if (!PHYSICAL_PRESENCE_KEYBOARD &&
vb2ex_physical_presence_pressed()) {
/*
* Is the presence button stuck? In any case
* we don't like this. Beep and ignore.
*/
vb2_error_notify(release_button_msg,
recovery_pressed_msg,
VB_BEEP_NOT_ALLOWED);
continue;
}
/* Ask the user to confirm entering dev-mode */
VbDisplayScreen(ctx, VB_SCREEN_RECOVERY_TO_DEV,
0, NULL);
/* SPACE means no... */
uint32_t vbc_flags = VB_CONFIRM_SPACE_MEANS_NO |
VB_CONFIRM_MUST_TRUST_KEYBOARD;
switch (VbUserConfirms(ctx, vbc_flags)) {
case 1:
vb2_enable_developer_mode(ctx);
return VB2_REQUEST_REBOOT_EC_TO_RO;
case -1:
VB2_DEBUG("Shutdown requested\n");
return VB2_REQUEST_SHUTDOWN;
default: /* zero, actually */
VB2_DEBUG("Not enabling dev-mode\n");
break;
}
} else if ((retval = vb2_check_diagnostic_key(ctx, key)) !=
VB2_SUCCESS) {
return retval;
} else {
VbCheckDisplayKey(ctx, key, disp_current_screen, NULL);
}
if (vb2_want_shutdown(ctx, key))
return VB2_REQUEST_SHUTDOWN;
vb2ex_msleep(KEY_DELAY_MS);
}
return VB2_SUCCESS;
}
vb2_error_t VbBootRecoveryLegacyClamshell(struct vb2_context *ctx)
{
vb2_error_t retval = recovery_ui(ctx);
VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL);
return retval;
}