blob: 042d6d4c202e4fa127eb85d24556215e886dd338 [file] [log] [blame]
/* Copyright 2020 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.
*
* Firmware screen definitions.
*/
#include "2api.h"
#include "2common.h"
#include "2misc.h"
#include "2nvstorage.h"
#include "2ui.h"
#include "2ui_private.h"
#include "vboot_api.h"
#include "vboot_kernel.h"
#define MENU_ITEMS(a) ((struct vb2_menu){ \
.num_items = ARRAY_SIZE(a), \
.items = a, \
})
#define LANGUAGE_SELECT_ITEM { \
.text = "Language selection", \
.target = VB2_SCREEN_LANGUAGE_SELECT, \
.is_language_select = 1, \
}
#define NEXT_ITEM(target_screen) { \
.text = "Next", \
.target = (target_screen), \
}
#define BACK_ITEM { \
.text = "Back", \
.action = vb2_ui_change_root, \
}
#define ADVANCED_OPTIONS_ITEM { \
.text = "Advanced options", \
.target = VB2_SCREEN_ADVANCED_OPTIONS, \
}
/* Action that will power off the device. */
static vb2_error_t power_off_action(struct vb2_ui_context *ui)
{
return VB2_REQUEST_SHUTDOWN;
}
#define POWER_OFF_ITEM { \
.text = "Power off", \
.action = power_off_action, \
}
/******************************************************************************/
/* VB2_SCREEN_BLANK */
static const struct vb2_screen_info blank_screen = {
.id = VB2_SCREEN_BLANK,
.name = "Blank",
};
/******************************************************************************/
/* VB2_SCREEN_LANGUAGE_SELECT */
static vb2_error_t language_select_action(struct vb2_ui_context *ui)
{
vb2_error_t rv;
ui->locale_id = ui->state.selected_item;
VB2_DEBUG("Locale changed to %u\n", ui->locale_id);
/* Write locale id back to nvdata. */
vb2_nv_set(ui->ctx, VB2_NV_LOCALIZATION_INDEX, ui->locale_id);
/* Commit nvdata changes immediately, in case of three-finger salute
reboot. Ignore commit errors in recovery mode. */
rv = vb2ex_commit_data(ui->ctx);
if (rv && !(ui->ctx->flags & VB2_CONTEXT_RECOVERY_MODE))
return rv;
return vb2_ui_change_root(ui);
}
const struct vb2_menu *get_language_menu(struct vb2_ui_context *ui)
{
int i;
uint32_t num_locales;
struct vb2_menu_item *items;
if (ui->language_menu.num_items > 0)
return &ui->language_menu;
num_locales = vb2ex_get_locale_count();
if (num_locales == 0) {
VB2_DEBUG("WARNING: No locales available; assuming 1 locale\n");
num_locales = 1;
}
items = malloc(num_locales * sizeof(struct vb2_menu_item));
if (!items) {
VB2_DEBUG("ERROR: malloc failed for language items\n");
return NULL;
}
for (i = 0; i < num_locales; i++) {
items[i].text = "Some language";
items[i].action = language_select_action;
}
ui->language_menu.num_items = num_locales;
ui->language_menu.items = items;
return &ui->language_menu;
}
static vb2_error_t language_select_init(struct vb2_ui_context *ui)
{
const struct vb2_menu *menu = get_menu(ui);
if (menu->num_items == 0) {
VB2_DEBUG("ERROR: No menu items found; "
"rejecting entering language selection screen\n");
return vb2_ui_change_root(ui);
}
if (ui->locale_id < menu->num_items) {
ui->state.selected_item = ui->locale_id;
} else {
VB2_DEBUG("WARNING: Current locale not found in menu items; "
"initializing selected_item to 0\n");
ui->state.selected_item = 0;
}
return VB2_REQUEST_UI_CONTINUE;
}
static const struct vb2_screen_info language_select_screen = {
.id = VB2_SCREEN_LANGUAGE_SELECT,
.name = "Language selection screen",
.init = language_select_init,
.get_menu = get_language_menu,
};
/******************************************************************************/
/* VB2_SCREEN_RECOVERY_BROKEN */
static const struct vb2_menu_item recovery_broken_items[] = {
LANGUAGE_SELECT_ITEM,
ADVANCED_OPTIONS_ITEM,
POWER_OFF_ITEM,
};
static const struct vb2_screen_info recovery_broken_screen = {
.id = VB2_SCREEN_RECOVERY_BROKEN,
.name = "Recover broken device",
.menu = MENU_ITEMS(recovery_broken_items),
};
/******************************************************************************/
/* VB2_SCREEN_ADVANCED_OPTIONS */
#define ADVANCED_OPTIONS_ITEM_DEVELOPER_MODE 1
#define ADVANCED_OPTIONS_ITEM_BACK 2
vb2_error_t advanced_options_init(struct vb2_ui_context *ui)
{
ui->state.selected_item = ADVANCED_OPTIONS_ITEM_DEVELOPER_MODE;
if (vb2_get_sd(ui->ctx)->flags & VB2_SD_FLAG_DEV_MODE_ENABLED ||
!vb2_allow_recovery(ui->ctx)) {
ui->state.disabled_item_mask |=
1 << ADVANCED_OPTIONS_ITEM_DEVELOPER_MODE;
ui->state.selected_item = ADVANCED_OPTIONS_ITEM_BACK;
}
return VB2_REQUEST_UI_CONTINUE;
}
static const struct vb2_menu_item advanced_options_items[] = {
LANGUAGE_SELECT_ITEM,
[ADVANCED_OPTIONS_ITEM_DEVELOPER_MODE] = {
.text = "Enable developer mode",
.target = VB2_SCREEN_RECOVERY_TO_DEV,
},
[ADVANCED_OPTIONS_ITEM_BACK] = BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct vb2_screen_info advanced_options_screen = {
.id = VB2_SCREEN_ADVANCED_OPTIONS,
.name = "Advanced options",
.init = advanced_options_init,
.menu = MENU_ITEMS(advanced_options_items),
};
/******************************************************************************/
/* VB2_SCREEN_RECOVERY_SELECT */
#define RECOVERY_SELECT_ITEM_PHONE 1
#define RECOVERY_SELECT_ITEM_EXTERNAL_DISK 2
vb2_error_t recovery_select_init(struct vb2_ui_context *ui)
{
ui->state.selected_item = RECOVERY_SELECT_ITEM_PHONE;
if (!vb2api_phone_recovery_ui_enabled(ui->ctx)) {
VB2_DEBUG("WARNING: Phone recovery not available\n");
ui->state.disabled_item_mask |=
1 << RECOVERY_SELECT_ITEM_PHONE;
ui->state.selected_item = RECOVERY_SELECT_ITEM_EXTERNAL_DISK;
}
return VB2_REQUEST_UI_CONTINUE;
}
static const struct vb2_menu_item recovery_select_items[] = {
LANGUAGE_SELECT_ITEM,
[RECOVERY_SELECT_ITEM_PHONE] = {
.text = "Recovery using phone",
.target = VB2_SCREEN_RECOVERY_PHONE_STEP1,
},
[RECOVERY_SELECT_ITEM_EXTERNAL_DISK] = {
.text = "Recovery using external disk",
.target = VB2_SCREEN_RECOVERY_DISK_STEP1,
},
ADVANCED_OPTIONS_ITEM,
POWER_OFF_ITEM,
};
static const struct vb2_screen_info recovery_select_screen = {
.id = VB2_SCREEN_RECOVERY_SELECT,
.name = "Recovery method selection",
.init = recovery_select_init,
.menu = MENU_ITEMS(recovery_select_items),
};
/******************************************************************************/
/* VB2_SCREEN_RECOVERY_INVALID */
static const struct vb2_menu_item recovery_invalid_items[] = {
LANGUAGE_SELECT_ITEM,
POWER_OFF_ITEM,
};
static const struct vb2_screen_info recovery_invalid_screen = {
.id = VB2_SCREEN_RECOVERY_INVALID,
.name = "Invalid recovery inserted",
.menu = MENU_ITEMS(recovery_invalid_items),
};
/******************************************************************************/
/* VB2_SCREEN_RECOVERY_TO_DEV */
#define RECOVERY_TO_DEV_ITEM_CONFIRM 1
#define RECOVERY_TO_DEV_ITEM_CANCEL 2
vb2_error_t recovery_to_dev_init(struct vb2_ui_context *ui)
{
if (vb2_get_sd(ui->ctx)->flags & VB2_SD_FLAG_DEV_MODE_ENABLED) {
VB2_DEBUG("Dev mode already enabled?\n");
return vb2_ui_change_root(ui);
}
if (!PHYSICAL_PRESENCE_KEYBOARD && vb2ex_physical_presence_pressed()) {
VB2_DEBUG("Presence button stuck?\n");
return vb2_ui_change_root(ui);
}
ui->state.selected_item = RECOVERY_TO_DEV_ITEM_CONFIRM;
/* Disable "Confirm" button for other physical presence types. */
if (!PHYSICAL_PRESENCE_KEYBOARD) {
ui->state.disabled_item_mask |=
1 << RECOVERY_TO_DEV_ITEM_CONFIRM;
ui->state.selected_item = RECOVERY_TO_DEV_ITEM_CANCEL;
}
ui->physical_presence_button_pressed = 0;
return VB2_REQUEST_UI_CONTINUE;
}
static vb2_error_t recovery_to_dev_finalize(struct vb2_ui_context *ui)
{
VB2_DEBUG("Physical presence confirmed!\n");
/* Sanity check, should never happen. */
if (ui->state.screen->id != VB2_SCREEN_RECOVERY_TO_DEV ||
(vb2_get_sd(ui->ctx)->flags & VB2_SD_FLAG_DEV_MODE_ENABLED) ||
!vb2_allow_recovery(ui->ctx)) {
VB2_DEBUG("ERROR: Dev transition sanity check failed\n");
return VB2_REQUEST_UI_CONTINUE;
}
VB2_DEBUG("Enabling dev mode and rebooting...\n");
vb2_enable_developer_mode(ui->ctx);
return VB2_REQUEST_REBOOT_EC_TO_RO;
}
vb2_error_t recovery_to_dev_confirm_action(struct vb2_ui_context *ui)
{
if (!ui->key_trusted) {
VB2_DEBUG("Reject untrusted %s confirmation\n",
ui->key == VB_KEY_ENTER ? "ENTER" : "POWER");
return VB2_REQUEST_UI_CONTINUE;
}
return recovery_to_dev_finalize(ui);
}
vb2_error_t recovery_to_dev_action(struct vb2_ui_context *ui)
{
int pressed;
if (ui->key == ' ') {
VB2_DEBUG("SPACE means cancel dev mode transition\n");
return vb2_ui_change_root(ui);
}
/* Keyboard physical presence case covered by "Confirm" action. */
if (PHYSICAL_PRESENCE_KEYBOARD)
return VB2_REQUEST_UI_CONTINUE;
pressed = vb2ex_physical_presence_pressed();
if (pressed) {
VB2_DEBUG("Physical presence button pressed, "
"awaiting release\n");
ui->physical_presence_button_pressed = 1;
return VB2_REQUEST_UI_CONTINUE;
}
if (!ui->physical_presence_button_pressed)
return VB2_REQUEST_UI_CONTINUE;
VB2_DEBUG("Physical presence button released\n");
return recovery_to_dev_finalize(ui);
}
static const struct vb2_menu_item recovery_to_dev_items[] = {
LANGUAGE_SELECT_ITEM,
[RECOVERY_TO_DEV_ITEM_CONFIRM] = {
.text = "Confirm",
.action = recovery_to_dev_confirm_action,
},
[RECOVERY_TO_DEV_ITEM_CANCEL] = {
.text = "Cancel",
.action = vb2_ui_change_root,
},
POWER_OFF_ITEM,
};
static const struct vb2_screen_info recovery_to_dev_screen = {
.id = VB2_SCREEN_RECOVERY_TO_DEV,
.name = "Transition to developer mode",
.init = recovery_to_dev_init,
.action = recovery_to_dev_action,
.menu = MENU_ITEMS(recovery_to_dev_items),
};
/******************************************************************************/
/* VB2_SCREEN_RECOVERY_PHONE_STEP1 */
static const struct vb2_menu_item recovery_phone_step1_items[] = {
LANGUAGE_SELECT_ITEM,
NEXT_ITEM(VB2_SCREEN_RECOVERY_PHONE_STEP2),
BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct vb2_screen_info recovery_phone_step1_screen = {
.id = VB2_SCREEN_RECOVERY_PHONE_STEP1,
.name = "Phone recovery step 1",
.menu = MENU_ITEMS(recovery_phone_step1_items),
};
/******************************************************************************/
/* VB2_SCREEN_RECOVERY_PHONE_STEP2 */
static const struct vb2_menu_item recovery_phone_step2_items[] = {
LANGUAGE_SELECT_ITEM,
BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct vb2_screen_info recovery_phone_step2_screen = {
.id = VB2_SCREEN_RECOVERY_PHONE_STEP2,
.name = "Phone recovery step 2",
.menu = MENU_ITEMS(recovery_phone_step2_items),
};
/******************************************************************************/
/* VB2_SCREEN_RECOVERY_DISK_STEP1 */
static const struct vb2_menu_item recovery_disk_step1_items[] = {
LANGUAGE_SELECT_ITEM,
NEXT_ITEM(VB2_SCREEN_RECOVERY_DISK_STEP2),
BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct vb2_screen_info recovery_disk_step1_screen = {
.id = VB2_SCREEN_RECOVERY_DISK_STEP1,
.name = "Disk recovery step 1",
.menu = MENU_ITEMS(recovery_disk_step1_items),
};
/******************************************************************************/
/* VB2_SCREEN_RECOVERY_DISK_STEP2 */
static const struct vb2_menu_item recovery_disk_step2_items[] = {
LANGUAGE_SELECT_ITEM,
NEXT_ITEM(VB2_SCREEN_RECOVERY_DISK_STEP3),
BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct vb2_screen_info recovery_disk_step2_screen = {
.id = VB2_SCREEN_RECOVERY_DISK_STEP2,
.name = "Disk recovery step 2",
.menu = MENU_ITEMS(recovery_disk_step2_items),
};
/******************************************************************************/
/* VB2_SCREEN_RECOVERY_DISK_STEP3 */
static const struct vb2_menu_item recovery_disk_step3_items[] = {
LANGUAGE_SELECT_ITEM,
BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct vb2_screen_info recovery_disk_step3_screen = {
.id = VB2_SCREEN_RECOVERY_DISK_STEP3,
.name = "Disk recovery step 3",
.menu = MENU_ITEMS(recovery_disk_step3_items),
};
/******************************************************************************/
/* VB2_SCREEN_DEVELOPER_MODE */
#define DEVELOPER_MODE_ITEM_RETURN_TO_SECURE 1
#define DEVELOPER_MODE_ITEM_BOOT_INTERNAL 2
#define DEVELOPER_MODE_ITEM_BOOT_EXTERNAL 3
vb2_error_t developer_mode_init(struct vb2_ui_context *ui)
{
enum vb2_dev_default_boot_target default_boot =
vb2api_get_dev_default_boot_target(ui->ctx);
/* Get me outta here! */
if (!vb2_dev_boot_allowed(ui->ctx))
vb2_ui_change_screen(ui, VB2_SCREEN_DEVELOPER_TO_NORM);
/* Don't show "Return to secure mode" button if GBB forces dev mode. */
if (vb2_get_gbb(ui->ctx)->flags & VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON)
ui->state.disabled_item_mask |=
1 << DEVELOPER_MODE_ITEM_RETURN_TO_SECURE;
/* Don't show "Boot from external disk" button if not allowed. */
if (!vb2_dev_boot_external_allowed(ui->ctx))
ui->state.disabled_item_mask |=
1 << DEVELOPER_MODE_ITEM_BOOT_EXTERNAL;
/* Choose the default selection. */
switch (default_boot) {
case VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL:
ui->state.selected_item = DEVELOPER_MODE_ITEM_BOOT_EXTERNAL;
break;
default:
ui->state.selected_item = DEVELOPER_MODE_ITEM_BOOT_INTERNAL;
break;
}
ui->start_time = vb2ex_mtime();
return VB2_REQUEST_UI_CONTINUE;
}
vb2_error_t vb2_ui_developer_mode_boot_internal_action(
struct vb2_ui_context *ui)
{
if (!(ui->ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) ||
!vb2_dev_boot_allowed(ui->ctx)) {
VB2_DEBUG("ERROR: Dev mode internal boot not allowed\n");
return VB2_REQUEST_UI_CONTINUE;
}
VB2_TRY(VbTryLoadKernel(ui->ctx, VB_DISK_FLAG_FIXED));
return VB2_SUCCESS;
}
vb2_error_t vb2_ui_developer_mode_boot_external_action(
struct vb2_ui_context *ui)
{
vb2_error_t rv;
/* Sanity check, should never happen. */
if (!(ui->ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) ||
!vb2_dev_boot_allowed(ui->ctx) ||
!vb2_dev_boot_external_allowed(ui->ctx)) {
VB2_DEBUG("ERROR: Dev mode external boot not allowed\n");
return VB2_REQUEST_UI_CONTINUE;
}
rv = VbTryLoadKernel(ui->ctx, VB_DISK_FLAG_REMOVABLE);
if (rv == VB2_SUCCESS) {
return VB2_SUCCESS;
} else if (rv == VB2_ERROR_LK_NO_DISK_FOUND) {
if (ui->state.screen->id != VB2_SCREEN_DEVELOPER_BOOT_EXTERNAL)
VB2_DEBUG("No external disk found\n");
return vb2_ui_change_screen(
ui, VB2_SCREEN_DEVELOPER_BOOT_EXTERNAL);
} else {
if (ui->state.screen->id != VB2_SCREEN_DEVELOPER_INVALID_DISK)
VB2_DEBUG("Invalid external disk: %#x\n", rv);
return vb2_ui_change_screen(
ui, VB2_SCREEN_DEVELOPER_INVALID_DISK);
}
}
vb2_error_t developer_mode_action(struct vb2_ui_context *ui)
{
const int use_short = vb2api_use_short_dev_screen_delay(ui->ctx);
uint64_t elapsed;
/* Once any user interaction occurs, stop the timer. */
if (ui->key)
ui->disable_timer = 1;
if (ui->disable_timer)
return VB2_REQUEST_UI_CONTINUE;
elapsed = vb2ex_mtime() - ui->start_time;
/* If we're using short delay, wait 2 seconds and don't beep. */
if (use_short && elapsed > 2 * VB2_MSEC_PER_SEC) {
VB2_DEBUG("Booting default target after 2s\n");
ui->disable_timer = 1;
return vb2_ui_menu_select(ui);
}
/* Otherwise, beep at 20 and 20.5 seconds. */
if ((ui->beep_count == 0 && elapsed > 20 * VB2_MSEC_PER_SEC) ||
(ui->beep_count == 1 && elapsed > 20 * VB2_MSEC_PER_SEC + 500)) {
vb2ex_beep(250, 400);
ui->beep_count++;
}
/* Stop after 30 seconds. */
if (elapsed > 30 * VB2_MSEC_PER_SEC) {
VB2_DEBUG("Booting default target after 30s\n");
ui->disable_timer = 1;
return vb2_ui_menu_select(ui);
}
return VB2_REQUEST_UI_CONTINUE;
}
static const struct vb2_menu_item developer_mode_items[] = {
LANGUAGE_SELECT_ITEM,
[DEVELOPER_MODE_ITEM_RETURN_TO_SECURE] = {
.text = "Return to secure mode",
.target = VB2_SCREEN_DEVELOPER_TO_NORM,
},
[DEVELOPER_MODE_ITEM_BOOT_INTERNAL] = {
.text = "Boot from internal disk",
.action = vb2_ui_developer_mode_boot_internal_action,
},
[DEVELOPER_MODE_ITEM_BOOT_EXTERNAL] = {
.text = "Boot from external disk",
.action = vb2_ui_developer_mode_boot_external_action,
},
ADVANCED_OPTIONS_ITEM,
POWER_OFF_ITEM,
};
static const struct vb2_screen_info developer_mode_screen = {
.id = VB2_SCREEN_DEVELOPER_MODE,
.name = "Developer mode",
.init = developer_mode_init,
.action = developer_mode_action,
.menu = MENU_ITEMS(developer_mode_items),
};
/******************************************************************************/
/* VB2_SCREEN_DEVELOPER_TO_NORM */
vb2_error_t developer_to_norm_action(struct vb2_ui_context *ui)
{
if (vb2_get_gbb(ui->ctx)->flags & VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON) {
VB2_DEBUG("ERROR: dev mode forced by GBB flag\n");
return VB2_REQUEST_UI_CONTINUE;
}
VB2_DEBUG("Leaving dev mode\n");
vb2_nv_set(ui->ctx, VB2_NV_DISABLE_DEV_REQUEST, 1);
return VB2_REQUEST_REBOOT;
}
static const struct vb2_menu_item developer_to_norm_items[] = {
LANGUAGE_SELECT_ITEM,
{
.text = "Confirm",
.action = developer_to_norm_action,
},
{
.text = "Cancel",
.action = vb2_ui_change_root,
},
POWER_OFF_ITEM,
};
static const struct vb2_screen_info developer_to_norm_screen = {
.id = VB2_SCREEN_DEVELOPER_TO_NORM,
.name = "Transition to normal mode",
.menu = MENU_ITEMS(developer_to_norm_items),
};
/******************************************************************************/
/* VB2_SCREEN_DEVELOPER_BOOT_EXTERNAL */
static const struct vb2_menu_item developer_boot_external_items[] = {
LANGUAGE_SELECT_ITEM,
BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct vb2_screen_info developer_boot_external_screen = {
.id = VB2_SCREEN_DEVELOPER_BOOT_EXTERNAL,
.name = "Developer boot from external disk",
.action = vb2_ui_developer_mode_boot_external_action,
.menu = MENU_ITEMS(developer_boot_external_items),
};
/******************************************************************************/
/* VB2_SCREEN_DEVELOPER_INVALID_DISK */
static const struct vb2_menu_item developer_invalid_disk_items[] = {
LANGUAGE_SELECT_ITEM,
BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct vb2_screen_info developer_invalid_disk_screen = {
.id = VB2_SCREEN_DEVELOPER_INVALID_DISK,
.name = "Invalid external disk in dev mode",
.action = vb2_ui_developer_mode_boot_external_action,
.menu = MENU_ITEMS(developer_invalid_disk_items),
};
/******************************************************************************/
/*
* TODO(chromium:1035800): Refactor UI code across vboot and depthcharge.
* Currently vboot and depthcharge maintain their own copies of menus/screens.
* vboot detects keyboard input and controls the navigation among different menu
* items and screens, while depthcharge performs the actual rendering of each
* screen, based on the menu information passed from vboot.
*/
static const struct vb2_screen_info *screens[] = {
&blank_screen,
&language_select_screen,
&recovery_broken_screen,
&advanced_options_screen,
&recovery_select_screen,
&recovery_invalid_screen,
&recovery_to_dev_screen,
&recovery_phone_step1_screen,
&recovery_phone_step2_screen,
&recovery_disk_step1_screen,
&recovery_disk_step2_screen,
&recovery_disk_step3_screen,
&developer_mode_screen,
&developer_to_norm_screen,
&developer_boot_external_screen,
&developer_invalid_disk_screen,
};
const struct vb2_screen_info *vb2_get_screen_info(enum vb2_screen id)
{
int i;
for (i = 0; i < ARRAY_SIZE(screens); i++) {
if (screens[i]->id == id)
return screens[i];
}
return NULL;
}