blob: 916bcf2391db0c10a9c5750e86752939956a7707 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2020 Google Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <libpayload.h>
#include <vb2_api.h>
#include "base/list.h"
#include "boot/payload.h"
#include "diag/storage_test.h"
#include "drivers/ec/cros/ec.h"
#include "drivers/tpm/tpm.h"
#include "vboot/firmware_id.h"
#include "vboot/ui.h"
#include "vboot/util/commonparams.h"
#define UI_DESC(a) ((struct ui_desc){ \
.count = ARRAY_SIZE(a), \
.files = a, \
})
#define UI_MENU(a) ((struct ui_menu){ \
.num_items = ARRAY_SIZE(a), \
.items = a, \
})
#define LANGUAGE_SELECT_ITEM ((struct ui_menu_item){ \
.name = "Language selection", \
.file = NULL, \
.type = UI_MENU_ITEM_TYPE_LANGUAGE, \
.target = UI_SCREEN_LANGUAGE_SELECT, \
})
#define PAGE_UP_ITEM ((struct ui_menu_item){ \
.name = "Page up", \
.file = "btn_page_up.bmp", \
.disabled_help_text_file = "page_up_disabled_help.bmp", \
.action = log_page_prev_action, \
})
#define PAGE_DOWN_ITEM ((struct ui_menu_item){ \
.name = "Page down", \
.file = "btn_page_down.bmp", \
.disabled_help_text_file = "page_down_disabled_help.bmp", \
.action = log_page_next_action, \
})
#define BACK_ITEM ((struct ui_menu_item){ \
.name = "Back", \
.file = "btn_back.bmp", \
.action = ui_screen_back, \
})
#define NEXT_ITEM(target_screen) ((struct ui_menu_item){ \
.name = "Next", \
.file = "btn_next.bmp", \
.target = (target_screen), \
})
#define ADVANCED_OPTIONS_ITEM ((struct ui_menu_item){ \
.name = "Advanced options", \
.file = "btn_adv_options.bmp", \
.type = UI_MENU_ITEM_TYPE_SECONDARY, \
.icon_file = "ic_settings.bmp", \
.target = UI_SCREEN_ADVANCED_OPTIONS, \
})
/* Action that will power off the device. */
static vb2_error_t power_off_action(struct ui_context *ui)
{
return VB2_REQUEST_SHUTDOWN;
}
#define POWER_OFF_ITEM ((struct ui_menu_item){ \
.name = "Power off", \
.file = "btn_power_off.bmp", \
.type = UI_MENU_ITEM_TYPE_SECONDARY, \
.icon_file = "ic_power.bmp", \
.flags = UI_MENU_ITEM_FLAG_NO_ARROW, \
.action = power_off_action, \
})
/******************************************************************************/
/* Helper functions */
static int is_battery_low(void)
{
static uint32_t batt_pct;
static int batt_pct_initialized = 0;
if (!batt_pct_initialized) {
if (!CONFIG(DRIVER_EC_CROS)) {
UI_WARN("No EC support to get battery level; "
"assuming low battery\n");
} else if (cros_ec_read_batt_state_of_charge(&batt_pct)) {
UI_WARN("Failed to get battery level; "
"assuming low battery\n");
batt_pct = 0;
}
batt_pct_initialized = 1;
}
return batt_pct < 10;
}
static vb2_error_t draw_log_desc(struct ui_context *ui,
const struct ui_state *prev_state,
int32_t *y)
{
static char *prev_buf;
static size_t prev_buf_len;
static int32_t prev_y;
const struct ui_state *state = ui->state;
char *buf;
size_t buf_len;
vb2_error_t rv = VB2_SUCCESS;
buf = ui_log_get_page_content(&state->log, state->current_page);
if (!buf)
return VB2_ERROR_UI_LOG_INIT;
buf_len = strlen(buf);
/* Redraw only if screen or text changed. */
if (!prev_state || state->screen->id != prev_state->screen->id ||
state->error_code != prev_state->error_code ||
!prev_buf || buf_len != prev_buf_len ||
strncmp(buf, prev_buf, buf_len) ||
state->current_page != prev_state->current_page)
rv = ui_draw_log_textbox(buf, state, y);
else
*y = prev_y;
if (prev_buf)
free(prev_buf);
prev_buf = buf;
prev_buf_len = buf_len;
prev_y = *y;
return rv;
}
/******************************************************************************/
/* Functions for ui error handling */
static vb2_error_t set_ui_error(struct ui_context *ui,
enum ui_error error_code)
{
/* Keep the first occurring error. */
if (ui->state->error_code)
UI_INFO("When handling ui error %#x, another ui error "
"occurred: %#x",
ui->state->error_code, error_code);
else
ui->state->error_code = error_code;
/* Return to the ui loop to show the error code. */
return VB2_REQUEST_UI_CONTINUE;
}
static vb2_error_t set_ui_error_and_go_back(struct ui_context *ui,
enum ui_error error_code)
{
set_ui_error(ui, error_code);
return ui_screen_back(ui);
}
/******************************************************************************/
/*
* Functions used for log screens
*
* Expects that the page_count is valid and page_up_item and page_down_item are
* assigned to correct menu item indices in all three functions, the
* current_page is valid in prev and next actions, and the back_item is assigned
* to a correct menu item index.
*/
static vb2_error_t log_page_update(struct ui_context *ui,
const char *new_log_string)
{
const struct ui_screen_info *screen = ui->state->screen;
struct ui_log_info *log = &ui->state->log;
if (CONFIG(HEADLESS)) {
/* TODO(b/151200757): Support headless devices */
log->page_count = 0;
return VB2_SUCCESS;
}
if (new_log_string) {
VB2_TRY(ui_log_init(screen->id, ui->state->locale->code,
new_log_string, log));
if (log->page_count == 0) {
UI_ERROR("page_count is 0\n");
return VB2_ERROR_UI_LOG_INIT;
}
if (ui->state->current_page >= log->page_count)
ui->state->current_page = log->page_count - 1;
ui->force_display = 1;
}
VB2_CLR_BIT(ui->state->disabled_item_mask, screen->page_up_item);
VB2_CLR_BIT(ui->state->disabled_item_mask, screen->page_down_item);
if (ui->state->current_page == 0)
VB2_SET_BIT(ui->state->disabled_item_mask,
screen->page_up_item);
if (ui->state->current_page == log->page_count - 1)
VB2_SET_BIT(ui->state->disabled_item_mask,
screen->page_down_item);
return VB2_SUCCESS;
}
static vb2_error_t log_page_reset_to_top(struct ui_context *ui)
{
const struct ui_screen_info *screen = ui->state->screen;
ui->state->current_page = 0;
ui->state->selected_item = ui->state->log.page_count > 1
? screen->page_down_item
: screen->back_item;
return log_page_update(ui, NULL);
}
static vb2_error_t log_page_show_back_or_cancel(struct ui_context *ui,
int is_show_cancel)
{
int back_item = ui->state->screen->back_item;
int cancel_item = ui->state->screen->cancel_item;
VB2_CLR_BIT(ui->state->hidden_item_mask, back_item);
VB2_CLR_BIT(ui->state->hidden_item_mask, cancel_item);
if (is_show_cancel) {
VB2_SET_BIT(ui->state->hidden_item_mask, back_item);
if (ui->state->selected_item == back_item)
ui->state->selected_item = cancel_item;
} else {
VB2_SET_BIT(ui->state->hidden_item_mask, cancel_item);
if (ui->state->selected_item == cancel_item)
ui->state->selected_item = back_item;
}
return VB2_SUCCESS;
}
static vb2_error_t log_page_prev_action(struct ui_context *ui)
{
/* Validity check. */
if (ui->state->current_page == 0)
return VB2_SUCCESS;
ui->state->current_page--;
return log_page_update(ui, NULL);
}
static vb2_error_t log_page_next_action(struct ui_context *ui)
{
/* Validity check. */
if (ui->state->current_page == ui->state->log.page_count - 1)
return VB2_SUCCESS;
ui->state->current_page++;
return log_page_update(ui, NULL);
}
/******************************************************************************/
/* UI_SCREEN_FIRMWARE_SYNC */
static const char *const firmware_sync_desc[] = {
"firmware_sync_desc.bmp",
};
static const struct ui_screen_info firmware_sync_screen = {
.id = UI_SCREEN_FIRMWARE_SYNC,
.icon = UI_ICON_TYPE_NONE,
.title = "firmware_sync_title.bmp",
.desc = UI_DESC(firmware_sync_desc),
.no_footer = 1,
.mesg = "Please do not power off your device.\n"
"Your system is applying a critical update.",
};
/******************************************************************************/
/* UI_SCREEN_LANGUAGE_SELECT */
static vb2_error_t language_select_init(struct ui_context *ui)
{
const struct ui_menu *menu = ui_get_menu(ui);
if (menu->num_items == 0) {
UI_ERROR("ERROR: No menu items found; "
"rejecting entering language selection screen\n");
return ui_screen_back(ui);
}
if (ui->state->locale->id < menu->num_items) {
ui->state->selected_item = ui->state->locale->id;
} else {
UI_WARN("WARNING: Current locale not found in menu items; "
"initializing selected_item to 0\n");
ui->state->selected_item = 0;
}
return VB2_SUCCESS;
}
static vb2_error_t draw_language_select(struct ui_context *ui,
const struct ui_state *prev_state)
{
int id;
const struct ui_state *state = ui->state;
const int reverse = state->locale->rtl;
uint32_t num_lang;
uint32_t locale_id;
int32_t x, x_begin, x_end, y, y_begin, y_end, y_center, menu_height;
int num_lang_per_page, target_pos, id_begin, id_end;
int32_t box_width, box_height;
const int32_t border_thickness = UI_LANG_MENU_BORDER_THICKNESS;
const uint32_t flags = PIVOT_H_LEFT | PIVOT_V_CENTER;
int focused;
const struct ui_locale *locale;
const struct rgb_color *bg_color, *fg_color;
struct ui_bitmap bitmap;
/*
* Call default drawing function to clear the screen if necessary, and
* draw the footer.
*/
VB2_TRY(ui_draw_default(ui, prev_state));
num_lang = ui_get_locale_count();
if (num_lang == 0) {
UI_ERROR("Locale count is 0\n");
return VB2_ERROR_UI_INVALID_ARCHIVE;
}
x_begin = UI_MARGIN_H;
x_end = UI_SCALE - UI_MARGIN_H;
box_width = x_end - x_begin;
box_height = UI_LANG_MENU_BOX_HEIGHT;
y_begin = UI_MARGIN_TOP + UI_LANG_BOX_HEIGHT + UI_LANG_MENU_MARGIN_TOP;
y_end = UI_SCALE - UI_MARGIN_BOTTOM - UI_FOOTER_HEIGHT -
UI_FOOTER_MARGIN_TOP;
num_lang_per_page = (y_end - y_begin) / box_height;
menu_height = box_height * MIN(num_lang_per_page, num_lang);
y_end = y_begin + menu_height; /* Correct for integer division error */
/* Get current locale_id */
locale_id = state->selected_item;
if (locale_id >= num_lang) {
UI_WARN("selected_item (%u) exceeds number of locales (%u); "
"falling back to locale 0\n",
locale_id, num_lang);
locale_id = 0;
}
/* Draw language dropdown */
VB2_TRY(ui_get_locale_info(locale_id, &locale));
VB2_TRY(ui_draw_language_header(locale, state, 1));
/*
* Calculate the list of languages to display, from id_begin
* (inclusive) to id_end (exclusive). The focused one is placed at the
* center of the list if possible.
*/
target_pos = (num_lang_per_page - 1) / 2;
if (locale_id < target_pos || num_lang < num_lang_per_page) {
/* locale_id is too small to put at the center, or
all languages fit in the screen */
id_begin = 0;
id_end = MIN(num_lang_per_page, num_lang);
} else if (locale_id > num_lang - num_lang_per_page + target_pos) {
/* locale_id is too large to put at the center */
id_begin = num_lang - num_lang_per_page;
id_end = num_lang;
} else {
/* Place locale_id at the center. It's guaranteed that
(id_begin >= 0) and (id_end <= num_lang). */
id_begin = locale_id - target_pos;
id_end = locale_id + num_lang_per_page - target_pos;
}
/* Draw dropdown menu */
x = x_begin + UI_LANG_ICON_GLOBE_SIZE + UI_LANG_ICON_MARGIN_H * 2;
y = y_begin;
for (id = id_begin; id < id_end; id++) {
focused = id == locale_id;
bg_color = focused ? &ui_color_button : &ui_color_lang_menu_bg;
fg_color = focused ? &ui_color_lang_menu_bg : &ui_color_fg;
/* Solid box */
VB2_TRY(ui_draw_rounded_box(x_begin, y, box_width, box_height,
bg_color, 0, 0, reverse));
/* Separator between languages */
if (id > id_begin)
VB2_TRY(ui_draw_h_line(x_begin, y, box_width,
border_thickness,
&ui_color_lang_menu_border));
/* Text */
y_center = y + box_height / 2;
VB2_TRY(ui_get_locale_info(id, &locale));
VB2_TRY(ui_get_language_name_bitmap(locale->code, &bitmap));
VB2_TRY(ui_draw_mapped_bitmap(&bitmap, x, y_center,
UI_SIZE_AUTO,
UI_LANG_MENU_TEXT_HEIGHT,
bg_color, fg_color,
flags, reverse));
y += box_height;
}
/* Draw outer borders */
VB2_TRY(ui_draw_rounded_box(x_begin, y_begin, box_width, menu_height,
&ui_color_lang_menu_border,
border_thickness, 0, reverse));
if (num_lang <= num_lang_per_page)
return VB2_SUCCESS;
/* Draw scrollbar */
x = x_end - UI_LANG_MENU_SCROLLBAR_MARGIN_RIGHT - UI_SCROLLBAR_WIDTH;
if (reverse)
x = UI_SCALE - x - UI_SCROLLBAR_WIDTH +
UI_LANG_MENU_SCROLLBAR_MARGIN_RIGHT;
VB2_TRY(ui_draw_scrollbar(x, y_begin, menu_height, id_begin, num_lang,
num_lang_per_page));
return VB2_SUCCESS;
}
static vb2_error_t language_select_action(struct ui_context *ui)
{
vb2_error_t rv;
uint32_t locale_id = ui->state->selected_item;
VB2_TRY(ui_get_locale_info(locale_id, &ui->state->locale));
UI_INFO("Locale changed to %u\n", locale_id);
/* Write locale id back to nvdata. */
vb2api_set_locale_id(ui->ctx, 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 ui_screen_back(ui);
}
const struct ui_menu *get_language_menu(struct ui_context *ui)
{
int i;
uint32_t num_locales;
struct ui_menu_item *items;
if (ui->language_menu.num_items > 0)
return &ui->language_menu;
num_locales = ui_get_locale_count();
if (num_locales == 0) {
UI_WARN("WARNING: No locales available; assuming 1 locale\n");
num_locales = 1;
}
items = malloc(num_locales * sizeof(struct ui_menu_item));
if (!items) {
UI_ERROR("ERROR: malloc failed for language items\n");
return NULL;
}
for (i = 0; i < num_locales; i++) {
items[i].name = "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 const struct ui_screen_info language_select_screen = {
.id = UI_SCREEN_LANGUAGE_SELECT,
.name = "Language selection screen",
.init = language_select_init,
.draw = draw_language_select,
.mesg = "Language selection",
.get_menu = get_language_menu,
};
/******************************************************************************/
/* UI_SCREEN_RECOVERY_BROKEN */
static const char *const broken_desc[] = {
"broken_desc.bmp",
};
static const struct ui_menu_item broken_items[] = {
LANGUAGE_SELECT_ITEM,
ADVANCED_OPTIONS_ITEM,
POWER_OFF_ITEM,
};
static const struct ui_screen_info broken_screen = {
.id = UI_SCREEN_RECOVERY_BROKEN,
.name = "Recover broken device",
.icon = UI_ICON_TYPE_INFO,
.title = "broken_title.bmp",
.desc = UI_DESC(broken_desc),
.menu = UI_MENU(broken_items),
.mesg = "Something is wrong. Please remove all connected devices.\n"
"To initiate recovery on a:\n"
"* Chromebook: Hold down Escape, Refresh, and Power buttons\n"
"* Chromebox/Chromebit: Hold down the Recovery button, press "
"Power, release\n"
" the Recovery button\n"
"* Tablet: Hold down Power, Volume Up, Volume Down buttons for "
"10 secs",
};
/******************************************************************************/
/* UI_SCREEN_ADVANCED_OPTIONS */
#define ADVANCED_OPTIONS_ITEM_DEVELOPER_MODE 1
#define ADVANCED_OPTIONS_ITEM_DEBUG_INFO 2
#define ADVANCED_OPTIONS_ITEM_INTERNET_RECOVERY 4
static vb2_error_t boot_minios_impl(struct ui_context *ui, int non_active_only)
{
/* Validity check, should never happen. */
if (ui->ctx->boot_mode != VB2_BOOT_MODE_MANUAL_RECOVERY) {
UI_ERROR("ERROR: Not in manual recovery mode; ignoring\n");
return VB2_REQUEST_UI_CONTINUE;
}
vb2_error_t rv = VbTryLoadMiniOsKernel(ui->ctx, !!non_active_only);
if (rv) {
UI_ERROR("ERROR: Failed to boot from MiniOS: %#x\n", rv);
return set_ui_error(ui, UI_ERROR_MINIOS_BOOT_FAILED);
}
return VB2_REQUEST_UI_EXIT;
}
static vb2_error_t boot_old_minios_action(struct ui_context *ui)
{
return boot_minios_impl(ui, 1);
}
vb2_error_t advanced_options_init(struct ui_context *ui)
{
ui->state->selected_item = ADVANCED_OPTIONS_ITEM_DEVELOPER_MODE;
if ((ui->ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) ||
ui->ctx->boot_mode != VB2_BOOT_MODE_MANUAL_RECOVERY) {
VB2_SET_BIT(ui->state->hidden_item_mask,
ADVANCED_OPTIONS_ITEM_DEVELOPER_MODE);
ui->state->selected_item = ADVANCED_OPTIONS_ITEM_DEBUG_INFO;
}
if (ui->ctx->boot_mode != VB2_BOOT_MODE_MANUAL_RECOVERY)
VB2_SET_BIT(ui->state->hidden_item_mask,
ADVANCED_OPTIONS_ITEM_INTERNET_RECOVERY);
return VB2_SUCCESS;
}
static const struct ui_menu_item advanced_options_items[] = {
LANGUAGE_SELECT_ITEM,
[ADVANCED_OPTIONS_ITEM_DEVELOPER_MODE] = {
.name = "Enable developer mode",
.file = "btn_dev_mode.bmp",
.target = UI_SCREEN_RECOVERY_TO_DEV,
},
[ADVANCED_OPTIONS_ITEM_DEBUG_INFO] = {
.name = "Debug info",
.file = "btn_debug_info.bmp",
.target = UI_SCREEN_DEBUG_INFO,
},
{
.name = "Firmware log",
.file = "btn_firmware_log.bmp",
.target = UI_SCREEN_FIRMWARE_LOG,
},
[ADVANCED_OPTIONS_ITEM_INTERNET_RECOVERY] = {
.name = "Internet recovery (older version)",
.file = "btn_rec_by_internet_old.bmp",
.action = boot_old_minios_action,
},
BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct ui_screen_info advanced_options_screen = {
.id = UI_SCREEN_ADVANCED_OPTIONS,
.name = "Advanced options",
.icon = UI_ICON_TYPE_NONE,
.title = "adv_options_title.bmp",
.init = advanced_options_init,
.mesg = "Advanced options",
.menu = UI_MENU(advanced_options_items),
};
/******************************************************************************/
/* UI_SCREEN_DEBUG_INFO */
#define DEBUG_INFO_ITEM_PAGE_UP 1
#define DEBUG_INFO_ITEM_PAGE_DOWN 2
#define DEBUG_INFO_ITEM_BACK 3
#define DEBUG_INFO_EXTRA_LENGTH 256
static const char *debug_info_get_string(struct ui_context *ui)
{
char *buf;
size_t buf_size;
char *vboot_buf;
char *tpm_str = NULL;
char batt_pct_str[16];
/* Check if cache exists. */
if (ui->debug_info_str)
return ui->debug_info_str;
/* Debug info from the vboot context. */
vboot_buf = vb2api_get_debug_info(ui->ctx);
buf_size = strlen(vboot_buf) + DEBUG_INFO_EXTRA_LENGTH + 1;
buf = malloc(buf_size);
if (!buf) {
UI_ERROR("ERROR: Failed to malloc string buffer\n");
free(vboot_buf);
return NULL;
}
/* States owned by firmware. */
if (CONFIG(MOCK_TPM))
tpm_str = "MOCK TPM";
else if (CONFIG(DRIVER_TPM))
tpm_str = tpm_report_state();
if (!tpm_str)
tpm_str = "(unsupported)";
if (!CONFIG(DRIVER_EC_CROS)) {
strncpy(batt_pct_str, "(unsupported)", sizeof(batt_pct_str));
} else {
uint32_t batt_pct;
if (cros_ec_read_batt_state_of_charge(&batt_pct))
strncpy(batt_pct_str, "(read failure)",
sizeof(batt_pct_str));
else
snprintf(batt_pct_str, sizeof(batt_pct_str),
"%u%%", batt_pct);
}
snprintf(buf, buf_size,
"%s\n" /* vboot output does not include newline. */
"read-only firmware id: %s\n"
"active firmware id: %s\n"
"battery level: %s\n"
"TPM state: %s",
vboot_buf,
get_ro_fw_id(), get_active_fw_id(),
batt_pct_str, tpm_str);
free(vboot_buf);
buf[buf_size - 1] = '\0';
UI_INFO("debug info: %s\n", buf);
ui->debug_info_str = buf;
return buf;
}
static vb2_error_t debug_info_set_content(struct ui_context *ui)
{
const char *log_string = debug_info_get_string(ui);
if (!log_string)
return set_ui_error_and_go_back(ui, UI_ERROR_DEBUG_LOG);
if (vb2_is_error(log_page_update(ui, log_string)))
return set_ui_error_and_go_back(ui, UI_ERROR_DEBUG_LOG);
return VB2_SUCCESS;
}
static vb2_error_t debug_info_init(struct ui_context *ui)
{
VB2_TRY(debug_info_set_content(ui));
if (vb2_is_error(log_page_reset_to_top(ui)))
return set_ui_error_and_go_back(ui, UI_ERROR_DEBUG_LOG);
return VB2_SUCCESS;
}
static const struct ui_menu_item debug_info_items[] = {
LANGUAGE_SELECT_ITEM,
[DEBUG_INFO_ITEM_PAGE_UP] = PAGE_UP_ITEM,
[DEBUG_INFO_ITEM_PAGE_DOWN] = PAGE_DOWN_ITEM,
[DEBUG_INFO_ITEM_BACK] = BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct ui_screen_info debug_info_screen = {
.id = UI_SCREEN_DEBUG_INFO,
.name = "Debug info",
.icon = UI_ICON_TYPE_NONE,
.title = "debug_info_title.bmp",
.menu = UI_MENU(debug_info_items),
.init = debug_info_init,
.draw_desc = draw_log_desc,
.mesg = "Debug info",
.page_up_item = DEBUG_INFO_ITEM_PAGE_UP,
.page_down_item = DEBUG_INFO_ITEM_PAGE_DOWN,
.back_item = DEBUG_INFO_ITEM_BACK,
};
/******************************************************************************/
/* UI_SCREEN_FIRMWARE_LOG */
#define FIRMWARE_LOG_ITEM_PAGE_UP 1
#define FIRMWARE_LOG_ITEM_PAGE_DOWN 2
#define FIRMWARE_LOG_ITEM_BACK 3
static vb2_error_t firmware_log_set_content(struct ui_context *ui,
int reset)
{
char *str = ui->firmware_log_str;
if (!str || reset) {
ui->firmware_log_str = NULL;
free(str);
str = cbmem_console_snapshot();
if (!str) {
UI_ERROR("ERROR: Failed to read cbmem console\n");
return set_ui_error_and_go_back(
ui, UI_ERROR_FIRMWARE_LOG);
}
UI_INFO("Read cbmem console: size=%zu\n", strlen(str));
}
ui->firmware_log_str = str;
if (vb2_is_error(log_page_update(ui, str)))
return set_ui_error_and_go_back(ui, UI_ERROR_FIRMWARE_LOG);
return VB2_SUCCESS;
}
static vb2_error_t firmware_log_init(struct ui_context *ui)
{
VB2_TRY(firmware_log_set_content(ui, 1));
if (vb2_is_error(log_page_reset_to_top(ui)))
return set_ui_error_and_go_back(ui, UI_ERROR_FIRMWARE_LOG);
return VB2_SUCCESS;
}
static const struct ui_menu_item firmware_log_items[] = {
LANGUAGE_SELECT_ITEM,
[FIRMWARE_LOG_ITEM_PAGE_UP] = PAGE_UP_ITEM,
[FIRMWARE_LOG_ITEM_PAGE_DOWN] = PAGE_DOWN_ITEM,
[FIRMWARE_LOG_ITEM_BACK] = BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct ui_screen_info firmware_log_screen = {
.id = UI_SCREEN_FIRMWARE_LOG,
.name = "Firmware log",
.icon = UI_ICON_TYPE_NONE,
.title = "firmware_log_title.bmp",
.menu = UI_MENU(firmware_log_items),
.init = firmware_log_init,
.draw_desc = draw_log_desc,
.mesg = "Firmware log",
.page_up_item = FIRMWARE_LOG_ITEM_PAGE_UP,
.page_down_item = FIRMWARE_LOG_ITEM_PAGE_DOWN,
.back_item = FIRMWARE_LOG_ITEM_BACK,
};
/******************************************************************************/
/* UI_SCREEN_RECOVERY_TO_DEV */
#define RECOVERY_TO_DEV_ITEM_CONFIRM 1
#define RECOVERY_TO_DEV_ITEM_CANCEL 2
static const char *const recovery_to_dev_desc[] = {
"rec_to_dev_desc0.bmp",
"rec_to_dev_desc1.bmp",
};
static vb2_error_t recovery_to_dev_init(struct ui_context *ui)
{
if (ui->ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) {
/* Notify the user that they are already in dev mode */
UI_WARN("Already in dev mode\n");
return set_ui_error_and_go_back(
ui, UI_ERROR_DEV_MODE_ALREADY_ENABLED);
}
if (!CONFIG(PHYSICAL_PRESENCE_KEYBOARD) &&
ui_is_physical_presence_pressed()) {
UI_INFO("Physical presence button stuck?\n");
return ui_screen_back(ui);
}
if (CONFIG(PHYSICAL_PRESENCE_KEYBOARD)) {
ui->state->selected_item = RECOVERY_TO_DEV_ITEM_CONFIRM;
} else {
/*
* Disable "Confirm" button for other physical presence types.
*/
VB2_SET_BIT(ui->state->hidden_item_mask,
RECOVERY_TO_DEV_ITEM_CONFIRM);
ui->state->selected_item = RECOVERY_TO_DEV_ITEM_CANCEL;
}
ui->physical_presence_button_pressed = 0;
return VB2_SUCCESS;
}
static vb2_error_t recovery_to_dev_finalize(struct ui_context *ui)
{
UI_INFO("Physical presence confirmed!\n");
/* Validity check, should never happen. */
if (ui->state->screen->id != UI_SCREEN_RECOVERY_TO_DEV ||
(ui->ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) ||
ui->ctx->boot_mode != VB2_BOOT_MODE_MANUAL_RECOVERY) {
UI_ERROR("ERROR: Dev transition validity check failed\n");
return VB2_SUCCESS;
}
UI_INFO("Enabling dev mode and rebooting...\n");
if (vb2api_enable_developer_mode(ui->ctx) != VB2_SUCCESS) {
UI_WARN("Failed to enable developer mode\n");
return VB2_SUCCESS;
}
return VB2_REQUEST_REBOOT_EC_TO_RO;
}
static vb2_error_t recovery_to_dev_confirm_action(struct ui_context *ui)
{
if (!ui->key_trusted) {
UI_INFO("Reject untrusted %s confirmation\n",
ui->key == UI_KEY_ENTER ? "ENTER" : "POWER");
/*
* If physical presence is confirmed using the keyboard,
* beep and notify the user when the ENTER key comes
* from an untrusted keyboard.
*/
if (CONFIG(PHYSICAL_PRESENCE_KEYBOARD) &&
ui->key == UI_KEY_ENTER)
return set_ui_error(
ui, UI_ERROR_UNTRUSTED_CONFIRMATION);
return VB2_SUCCESS;
}
return recovery_to_dev_finalize(ui);
}
static vb2_error_t recovery_to_dev_action(struct ui_context *ui)
{
int pressed;
if (ui->key == ' ') {
UI_INFO("SPACE means cancel dev mode transition\n");
return ui_screen_back(ui);
}
/* Keyboard physical presence case covered by "Confirm" action. */
if (CONFIG(PHYSICAL_PRESENCE_KEYBOARD))
return VB2_SUCCESS;
pressed = ui_is_physical_presence_pressed();
if (pressed) {
UI_INFO("Physical presence button pressed, "
"awaiting release\n");
ui->physical_presence_button_pressed = 1;
return VB2_SUCCESS;
}
if (!ui->physical_presence_button_pressed)
return VB2_SUCCESS;
UI_INFO("Physical presence button released\n");
return recovery_to_dev_finalize(ui);
}
static const struct ui_menu_item recovery_to_dev_items[] = {
LANGUAGE_SELECT_ITEM,
[RECOVERY_TO_DEV_ITEM_CONFIRM] = {
.name = "Confirm",
.file = "btn_confirm.bmp",
.action = recovery_to_dev_confirm_action,
},
[RECOVERY_TO_DEV_ITEM_CANCEL] = {
.name = "Cancel",
.file = "btn_cancel.bmp",
.action = ui_screen_back,
},
POWER_OFF_ITEM,
};
static const struct ui_screen_info recovery_to_dev_screen = {
.id = UI_SCREEN_RECOVERY_TO_DEV,
.name = "Transition to developer mode",
.icon = UI_ICON_TYPE_INFO,
.title = "rec_to_dev_title.bmp",
.desc = UI_DESC(recovery_to_dev_desc),
.menu = UI_MENU(recovery_to_dev_items),
.init = recovery_to_dev_init,
.action = recovery_to_dev_action,
.mesg = "You are attempting to enable developer mode\n"
"This involves erasing all data from your device,\n"
"and will make your device insecure.\n"
"Select \"Confirm\" or press the RECOVERY/POWER button to\n"
"enable developer mode,\n"
"or select \"Cancel\" to remain protected",
};
/******************************************************************************/
/* UI_SCREEN_RECOVERY_SELECT */
#define RECOVERY_SELECT_ITEM_PHONE 1
#define RECOVERY_SELECT_ITEM_EXTERNAL_DISK 2
#define RECOVERY_SELECT_ITEM_INTERNET 3
#define RECOVERY_SELECT_ITEM_DIAGNOSTICS 4
static vb2_error_t draw_recovery_select_desc(
struct ui_context *ui,
const struct ui_state *prev_state,
int32_t *y)
{
static const char *const desc_files[] = {
"rec_sel_desc0.bmp",
"rec_sel_desc1.bmp",
};
static const char *const desc_no_phone_files[] = {
"rec_sel_desc0.bmp",
"rec_sel_desc1_no_phone.bmp",
};
const struct ui_desc desc = vb2api_phone_recovery_ui_enabled(ui->ctx) ?
UI_DESC(desc_files) : UI_DESC(desc_no_phone_files);
return ui_draw_desc(&desc, ui->state, y);
}
/* Set VB2_NV_DIAG_REQUEST and reboot. */
static vb2_error_t launch_diagnostics_action(struct ui_context *ui)
{
vb2api_request_diagnostics(ui->ctx);
return VB2_REQUEST_REBOOT;
}
vb2_error_t ui_recovery_mode_boot_minios_action(struct ui_context *ui)
{
return boot_minios_impl(ui, 0);
}
vb2_error_t recovery_select_init(struct ui_context *ui)
{
ui->state->selected_item = RECOVERY_SELECT_ITEM_PHONE;
if (!vb2api_phone_recovery_ui_enabled(ui->ctx)) {
UI_WARN("WARNING: Phone recovery not available\n");
VB2_SET_BIT(ui->state->hidden_item_mask,
RECOVERY_SELECT_ITEM_PHONE);
ui->state->selected_item = RECOVERY_SELECT_ITEM_EXTERNAL_DISK;
}
if (!vb2api_diagnostic_ui_enabled(ui->ctx))
VB2_SET_BIT(ui->state->hidden_item_mask,
RECOVERY_SELECT_ITEM_DIAGNOSTICS);
return VB2_SUCCESS;
}
static const struct ui_menu_item recovery_select_items[] = {
LANGUAGE_SELECT_ITEM,
[RECOVERY_SELECT_ITEM_PHONE] = {
.name = "Recovery using phone",
.file = "btn_rec_by_phone.bmp",
.target = UI_SCREEN_RECOVERY_PHONE_STEP1,
},
[RECOVERY_SELECT_ITEM_EXTERNAL_DISK] = {
.name = "Recovery using external disk",
.file = "btn_rec_by_disk.bmp",
.target = UI_SCREEN_RECOVERY_DISK_STEP1,
},
[RECOVERY_SELECT_ITEM_INTERNET] = {
.name = "Recovery using internet connection",
.file = "btn_rec_by_internet.bmp",
.action = ui_recovery_mode_boot_minios_action,
},
[RECOVERY_SELECT_ITEM_DIAGNOSTICS] = {
.name = "Launch diagnostics",
.file = "btn_launch_diag.bmp",
.type = UI_MENU_ITEM_TYPE_SECONDARY,
.icon_file = "ic_search.bmp",
.flags = UI_MENU_ITEM_FLAG_NO_ARROW,
.action = launch_diagnostics_action,
},
ADVANCED_OPTIONS_ITEM,
POWER_OFF_ITEM,
};
static const struct ui_screen_info recovery_select_screen = {
.id = UI_SCREEN_RECOVERY_SELECT,
.name = "Recovery method selection",
.icon = UI_ICON_TYPE_INFO,
.title = "rec_sel_title.bmp",
.draw_desc = draw_recovery_select_desc,
.menu = UI_MENU(recovery_select_items),
.init = recovery_select_init,
.mesg = "Select how you'd like to recover.\n"
"You can recover using a USB drive or an SD card.",
};
/******************************************************************************/
/* UI_SCREEN_RECOVERY_PHONE_STEP1 */
static vb2_error_t draw_recovery_phone_step1_desc(
struct ui_context *ui,
const struct ui_state *prev_state,
int32_t *y)
{
static const char *const desc_files[] = {
"rec_phone_step1_desc0.bmp",
"rec_phone_step1_desc1.bmp",
"rec_step1_desc2.bmp",
};
static const char *const desc_low_battery_files[] = {
"rec_phone_step1_desc0.bmp",
"rec_phone_step1_desc1.bmp",
"rec_step1_desc2_low_bat.bmp",
};
const struct ui_desc desc = is_battery_low() ?
UI_DESC(desc_low_battery_files) : UI_DESC(desc_files);
return ui_draw_desc(&desc, ui->state, y);
}
static const struct ui_menu_item recovery_phone_step1_items[] = {
LANGUAGE_SELECT_ITEM,
NEXT_ITEM(UI_SCREEN_RECOVERY_PHONE_STEP2),
BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct ui_screen_info recovery_phone_step1_screen = {
.id = UI_SCREEN_RECOVERY_PHONE_STEP1,
.name = "Phone recovery step 1",
.icon = UI_ICON_TYPE_STEP,
.step = 1,
.num_steps = 3,
.title = "rec_step1_title.bmp",
.menu = UI_MENU(recovery_phone_step1_items),
.draw_desc = draw_recovery_phone_step1_desc,
.mesg = "To proceed with the recovery process, you’ll need\n"
"1. An Android phone with internet access\n"
"2. A USB cable which connects your phone and this device\n"
"We also recommend that you connect to a power source during\n"
"the recovery process.",
};
/******************************************************************************/
/* UI_SCREEN_RECOVERY_PHONE_STEP2 */
static vb2_error_t draw_recovery_phone_step2_desc(
struct ui_context *ui,
const struct ui_state *prev_state,
int32_t *y)
{
static const char *const desc_files[] = {
"rec_phone_step2_desc.bmp",
};
static const struct ui_desc desc = UI_DESC(desc_files);
const int32_t x = UI_SCALE - UI_MARGIN_H - UI_REC_QR_MARGIN_H;
const int32_t y_begin = *y;
const int32_t size = UI_REC_QR_SIZE;
const uint32_t flags = PIVOT_H_RIGHT | PIVOT_V_TOP;
const int reverse = ui->state->locale->rtl;
struct ui_bitmap bitmap;
VB2_TRY(ui_draw_desc(&desc, ui->state, y));
VB2_TRY(ui_get_bitmap("qr_rec_phone.bmp", NULL, 0, &bitmap));
VB2_TRY(ui_draw_bitmap(&bitmap, x, y_begin, size, size,
flags, reverse));
return VB2_SUCCESS;
}
static const struct ui_menu_item recovery_phone_step2_items[] = {
LANGUAGE_SELECT_ITEM,
BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct ui_screen_info recovery_phone_step2_screen = {
.id = UI_SCREEN_RECOVERY_PHONE_STEP2,
.name = "Phone recovery step 2",
.icon = UI_ICON_TYPE_STEP,
.step = 2,
.num_steps = 3,
.title = "rec_phone_step2_title.bmp",
.menu = UI_MENU(recovery_phone_step2_items),
.draw_desc = draw_recovery_phone_step2_desc,
.mesg = "Download the Chrome OS recovery app on your Android phone\n"
"by plugging in your phone or by scanning the QR code on the\n"
"right. Once you launch the app, connect your phone to your\n"
"device and recovery will start automatically.",
};
/******************************************************************************/
/* UI_SCREEN_RECOVERY_DISK_STEP1 */
static vb2_error_t draw_recovery_disk_step1_desc(
struct ui_context *ui,
const struct ui_state *prev_state,
int32_t *y)
{
static const char *const desc_files[] = {
"rec_disk_step1_desc0.bmp",
"rec_disk_step1_desc1.bmp",
"rec_step1_desc2.bmp",
};
static const char *const desc_low_battery_files[] = {
"rec_disk_step1_desc0.bmp",
"rec_disk_step1_desc1.bmp",
"rec_step1_desc2_low_bat.bmp",
};
const struct ui_desc desc = is_battery_low() ?
UI_DESC(desc_low_battery_files) : UI_DESC(desc_files);
return ui_draw_desc(&desc, ui->state, y);
}
static const struct ui_menu_item recovery_disk_step1_items[] = {
LANGUAGE_SELECT_ITEM,
NEXT_ITEM(UI_SCREEN_RECOVERY_DISK_STEP2),
BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct ui_screen_info recovery_disk_step1_screen = {
.id = UI_SCREEN_RECOVERY_DISK_STEP1,
.name = "Disk recovery step 1",
.icon = UI_ICON_TYPE_STEP,
.step = 1,
.num_steps = 3,
.title = "rec_step1_title.bmp",
.menu = UI_MENU(recovery_disk_step1_items),
.draw_desc = draw_recovery_disk_step1_desc,
.mesg = "To proceed with the recovery process, you'll need\n"
"1. An external storage disk such as a USB drive or an SD card"
"\n2. An additional device with internet access\n"
"We also recommend that you connect to a power source during\n"
"the recovery process.",
};
/******************************************************************************/
/* UI_SCREEN_RECOVERY_DISK_STEP2 */
static const char *const recovery_disk_step2_desc[] = {
"rec_disk_step2_desc0.bmp",
"rec_disk_step2_desc1.bmp",
"rec_disk_step2_desc2.bmp",
};
static const struct ui_menu_item recovery_disk_step2_items[] = {
LANGUAGE_SELECT_ITEM,
NEXT_ITEM(UI_SCREEN_RECOVERY_DISK_STEP3),
BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct ui_screen_info recovery_disk_step2_screen = {
.id = UI_SCREEN_RECOVERY_DISK_STEP2,
.name = "Disk recovery step 2",
.icon = UI_ICON_TYPE_STEP,
.step = 2,
.num_steps = 3,
.title = "rec_disk_step2_title.bmp",
.desc = UI_DESC(recovery_disk_step2_desc),
.menu = UI_MENU(recovery_disk_step2_items),
.mesg = "External disk setup.\n"
"Go to google.com/chromeos/recovery on another computer and\n"
"install the Chrome extension. Follow instructions on the\n"
"Chrome extension, and download the recovery image onto an\n"
"external disk.",
};
/******************************************************************************/
/* UI_SCREEN_RECOVERY_DISK_STEP3 */
static const char *const recovery_disk_step3_desc[] = {
"rec_disk_step3_desc0.bmp",
};
static const struct ui_menu_item recovery_disk_step3_items[] = {
LANGUAGE_SELECT_ITEM,
BACK_ITEM,
POWER_OFF_ITEM,
};
static const struct ui_screen_info recovery_disk_step3_screen = {
.id = UI_SCREEN_RECOVERY_DISK_STEP3,
.name = "Disk recovery step 3",
.icon = UI_ICON_TYPE_STEP,
.step = 3,
.num_steps = 3,
.title = "rec_disk_step3_title.bmp",
.desc = UI_DESC(recovery_disk_step3_desc),
.menu = UI_MENU(recovery_disk_step3_items),
.mesg = "Do you have your external disk ready?\n"
"If your external disk is ready with a recovery image, plug\n"
"it into the device to start the recovery process.",
};
/******************************************************************************/
/* UI_SCREEN_RECOVERY_INVALID */
static vb2_error_t draw_recovery_invalid_desc(
struct ui_context *ui,
const struct ui_state *prev_state,
int32_t *y)
{
static const char *const desc_files[] = {
"rec_invalid_desc.bmp",
};
static const char *const desc_no_phone_files[] = {
"rec_invalid_disk_desc.bmp",
};
const struct ui_desc desc = vb2api_phone_recovery_ui_enabled(ui->ctx) ?
UI_DESC(desc_files) : UI_DESC(desc_no_phone_files);
return ui_draw_desc(&desc, ui->state, y);
}
static const struct ui_menu_item recovery_invalid_items[] = {
LANGUAGE_SELECT_ITEM,
POWER_OFF_ITEM,
};
static const struct ui_screen_info recovery_invalid_screen = {
.id = UI_SCREEN_RECOVERY_INVALID,
.icon = UI_ICON_TYPE_STEP,
.step = -3,
.num_steps = 3,
.title = "rec_invalid_title.bmp",
.menu = UI_MENU(recovery_invalid_items),
.draw_desc = draw_recovery_invalid_desc,
.mesg = "No valid image detected.\n"
"Make sure your external disk has a valid recovery image,\n"
"and re-insert the disk when ready.",
};
/******************************************************************************/
/* UI_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
#define DEVELOPER_MODE_ITEM_SELECT_ALTFW 4
static vb2_error_t developer_mode_init(struct ui_context *ui)
{
enum vb2_dev_default_boot_target default_boot =
vb2api_get_dev_default_boot_target(ui->ctx);
/* Hide "Select alternate bootloader" button if not allowed. */
if (!(ui->ctx->flags & VB2_CONTEXT_DEV_BOOT_ALTFW_ALLOWED))
VB2_SET_BIT(ui->state->hidden_item_mask,
DEVELOPER_MODE_ITEM_SELECT_ALTFW);
/* Choose the default selection. */
switch (default_boot) {
case VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL:
ui->state->selected_item = DEVELOPER_MODE_ITEM_BOOT_EXTERNAL;
break;
case VB2_DEV_DEFAULT_BOOT_TARGET_ALTFW:
ui->state->selected_item =
DEVELOPER_MODE_ITEM_SELECT_ALTFW;
break;
default:
ui->state->selected_item = DEVELOPER_MODE_ITEM_BOOT_INTERNAL;
break;
}
ui->start_time_ms = vb2ex_mtime();
return VB2_SUCCESS;
}
vb2_error_t ui_developer_mode_boot_internal_action(struct ui_context *ui)
{
vb2_error_t rv;
/* Validity check, should never happen. */
if (!(ui->ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) ||
!(ui->ctx->flags & VB2_CONTEXT_DEV_BOOT_ALLOWED)) {
UI_ERROR("ERROR: Dev mode internal boot not allowed\n");
return VB2_SUCCESS;
}
rv = VbTryLoadKernel(ui->ctx, VB_DISK_FLAG_FIXED);
if (rv == VB2_SUCCESS)
return VB2_REQUEST_UI_EXIT;
UI_ERROR("ERROR: Failed to boot from internal disk: %#x\n", rv);
ui->error_beep = 1;
return set_ui_error(ui, UI_ERROR_INTERNAL_BOOT_FAILED);
}
vb2_error_t ui_developer_mode_boot_external_action(struct ui_context *ui)
{
vb2_error_t rv;
if (!(ui->ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) ||
!(ui->ctx->flags & VB2_CONTEXT_DEV_BOOT_ALLOWED) ||
!(ui->ctx->flags & VB2_CONTEXT_DEV_BOOT_EXTERNAL_ALLOWED)) {
UI_ERROR("ERROR: Dev mode external boot not allowed\n");
ui->error_beep = 1;
return set_ui_error(ui, UI_ERROR_EXTERNAL_BOOT_DISABLED);
}
rv = VbTryLoadKernel(ui->ctx, VB_DISK_FLAG_REMOVABLE);
if (rv == VB2_SUCCESS) {
return VB2_REQUEST_UI_EXIT;
} else if (rv == VB2_ERROR_LK_NO_DISK_FOUND) {
if (ui->state->screen->id !=
UI_SCREEN_DEVELOPER_BOOT_EXTERNAL) {
UI_WARN("No external disk found\n");
ui->error_beep = 1;
}
return ui_screen_change(
ui, UI_SCREEN_DEVELOPER_BOOT_EXTERNAL);
} else {
if (ui->state->screen->id !=
UI_SCREEN_DEVELOPER_INVALID_DISK) {
UI_WARN("Invalid external disk: %#x\n", rv);
ui->error_beep = 1;
}
return ui_screen_change(
ui, UI_SCREEN_DEVELOPER_INVALID_DISK);
}
}
static vb2_error_t developer_mode_action(struct ui_context *ui)
{
const int dev_delay_ms = (vb2api_gbb_get_flags(ui->ctx) &
VB2_GBB_FLAG_DEV_SCREEN_SHORT_DELAY) ?
DEV_DELAY_SHORT_MS : DEV_DELAY_NORMAL_MS;
uint64_t elapsed_ms;
enum vb2_dev_default_boot_target default_boot;
/* Once any user interaction occurs, stop the timer. */
if (ui->key)
ui->state->timer_disabled = 1;
if (ui->state->timer_disabled)
return VB2_SUCCESS;
elapsed_ms = vb2ex_mtime() - ui->start_time_ms;
/* Boot from default target after timeout. */
if (elapsed_ms > dev_delay_ms) {
UI_INFO("Booting default target after %ds\n",
dev_delay_ms / MSECS_PER_SEC);
ui->state->timer_disabled = 1;
default_boot = vb2api_get_dev_default_boot_target(ui->ctx);
switch (default_boot) {
case VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL:
return ui_developer_mode_boot_external_action(ui);
case VB2_DEV_DEFAULT_BOOT_TARGET_ALTFW:
return ui_developer_mode_boot_altfw_action(ui);
default:
return ui_developer_mode_boot_internal_action(ui);
}
}
/* Beep at 20 and 20.5 seconds. */
if ((ui->beep_count == 0 && elapsed_ms > DEV_DELAY_BEEP1_MS) ||
(ui->beep_count == 1 && elapsed_ms > DEV_DELAY_BEEP2_MS)) {
ui_beep(250, 400);
ui->beep_count++;
}
return VB2_SUCCESS;
}
static vb2_error_t draw_developer_mode_desc(
struct ui_context *ui,
const struct ui_state *prev_state,
int32_t *y)
{
struct ui_bitmap bitmap;
const struct ui_state *state = ui->state;
const char *locale_code = state->locale->code;
const int reverse = state->locale->rtl;
int32_t x;
const int32_t w = UI_SIZE_AUTO;
int32_t h;
uint32_t flags = PIVOT_H_LEFT | PIVOT_V_TOP;
x = UI_MARGIN_H;
/*
* Description about returning to secure mode. When developer mode is
* forced by GBB flags, hide this description line.
*/
if (!(vb2api_gbb_get_flags(ui->ctx) &
VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON)) {
VB2_TRY(ui_get_bitmap("dev_desc0.bmp", locale_code, 0,
&bitmap));
h = UI_DESC_TEXT_HEIGHT * ui_get_bitmap_num_lines(&bitmap);
VB2_TRY(ui_draw_bitmap(&bitmap, x, *y, w, h, flags, reverse));
*y += h + UI_DESC_TEXT_LINE_SPACING;
}
/*
* Description about automatically booting from the default boot target.
* After the timer in developer mode is disabled, this description no
* longer makes sense, so hide it.
*/
VB2_TRY(ui_get_bitmap("dev_desc1.bmp", locale_code, 0,
&bitmap));
h = UI_DESC_TEXT_HEIGHT * ui_get_bitmap_num_lines(&bitmap);
/* Either clear the desc line, or draw it again. */
if (state->timer_disabled)
VB2_TRY(ui_draw_box(x, *y, UI_SCALE - x, h, &ui_color_bg,
reverse));
else
VB2_TRY(ui_draw_bitmap(&bitmap, x, *y, w, h, flags, reverse));
*y += h;
return VB2_SUCCESS;
}
static const struct ui_menu_item developer_mode_items[] = {
LANGUAGE_SELECT_ITEM,
[DEVELOPER_MODE_ITEM_RETURN_TO_SECURE] = {
.name = "Return to secure mode",
.file = "btn_secure_mode.bmp",
.target = UI_SCREEN_DEVELOPER_TO_NORM,
},
[DEVELOPER_MODE_ITEM_BOOT_INTERNAL] = {
.name = "Boot from internal disk",
.file = "btn_int_disk.bmp",
.action = ui_developer_mode_boot_internal_action,
},
[DEVELOPER_MODE_ITEM_BOOT_EXTERNAL] = {
.name = "Boot from external disk",
.file = "btn_ext_disk.bmp",
.action = ui_developer_mode_boot_external_action,
},
[DEVELOPER_MODE_ITEM_SELECT_ALTFW] = {
.name = "Select alternate bootloader",
.file = "btn_alt_bootloader.bmp",
.target = UI_SCREEN_DEVELOPER_SELECT_ALTFW,
},
ADVANCED_OPTIONS_ITEM,
POWER_OFF_ITEM,
};
static const struct ui_screen_info developer_mode_screen = {
.id = UI_SCREEN_DEVELOPER_MODE,
.name = "Developer mode",
.icon = UI_ICON_TYPE_DEV_MODE,
.title = "dev_title.bmp",
.menu = UI_MENU(developer_mode_items),
.init = developer_mode_init,
.action = developer_mode_action,
.draw_desc = draw_developer_mode_desc,
.mesg = "You are in developer mode\n"
"To return to the recommended secure mode,\n"
"select \"Return to secure mode\" below.\n"
"After timeout, the device will automatically boot from\n"
"the default boot target.",
};
/******************************************************************************/
/* UI_SCREEN_DEVELOPER_TO_NORM */
#define DEVELOPER_TO_NORM_ITEM_CONFIRM 1
#define DEVELOPER_TO_NORM_ITEM_CANCEL 2
static vb2_error_t developer_to_norm_init(struct ui_context *ui)
{
/* Don't allow to-norm if GBB forces dev mode */
if (vb2api_gbb_get_flags(ui->ctx) & VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON) {
UI_WARN("WARNING: to-norm not allowed by gbb flag\n");
return set_ui_error_and_go_back(
ui, UI_ERROR_TO_NORM_NOT_ALLOWED);
}
ui->state->selected_item = DEVELOPER_TO_NORM_ITEM_CONFIRM;
/* Hide "Cancel" button if dev boot is not allowed */
if (!(ui->ctx->flags & VB2_CONTEXT_DEV_BOOT_ALLOWED))
VB2_SET_BIT(ui->state->hidden_item_mask,
DEVELOPER_TO_NORM_ITEM_CANCEL);
return VB2_SUCCESS;
}
vb2_error_t developer_to_norm_action(struct ui_context *ui)
{
if (vb2api_disable_developer_mode(ui->ctx) == VB2_SUCCESS)
return VB2_REQUEST_REBOOT;
else
return VB2_SUCCESS;
}
static const char *const developer_to_norm_desc[] = {
"dev_to_norm_desc0.bmp",
"dev_to_norm_desc1.bmp",
};
static const struct ui_menu_item developer_to_norm_items[] = {
LANGUAGE_SELECT_ITEM,
[DEVELOPER_TO_NORM_ITEM_CONFIRM] = {
.name = "Confirm",
.file = "btn_confirm.bmp",
.action = developer_to_norm_action,
},
[DEVELOPER_TO_NORM_ITEM_CANCEL] = {
.name = "Cancel",
.file = "btn_cancel.bmp",
.action = ui_screen_back,
},
POWER_OFF_ITEM,
};
static const struct ui_screen_info developer_to_norm_screen = {
.id = UI_SCREEN_DEVELOPER_TO_NORM,
.name = "Transition to normal mode",
.icon = UI_ICON_TYPE_RESTART,
.title = "dev_to_norm_title.bmp",
.desc = UI_DESC(developer_to_norm_desc),
.menu = UI_MENU(developer_to_norm_items),
.init = developer_to_norm_init,
.mesg = "Confirm returning to secure mode.\n"
"This option will disable developer mode and restore your\n"
"device to its original state.\n"
"Your user data will be wiped in the process.",
};
/******************************************************************************/
/* UI_SCREEN_DEVELOPER_BOOT_EXTERNAL */
#define DEVELOPER_BOOT_EXTERNAL_ITEM_BACK 1
static const char *const developer_boot_external_desc[] = {
"dev_boot_ext_desc0.bmp",
};
static const struct ui_menu_item developer_boot_external_items[] = {
LANGUAGE_SELECT_ITEM,
[DEVELOPER_BOOT_EXTERNAL_ITEM_BACK] = BACK_ITEM,
POWER_OFF_ITEM,
};
static vb2_error_t developer_boot_external_check(struct ui_context *ui)
{
if (!(ui->ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) ||
!(ui->ctx->flags & VB2_CONTEXT_DEV_BOOT_ALLOWED) ||
!(ui->ctx->flags & VB2_CONTEXT_DEV_BOOT_EXTERNAL_ALLOWED)) {
UI_ERROR("ERROR: Dev mode external boot not allowed\n");
ui->error_beep = 1;
return set_ui_error_and_go_back(
ui, UI_ERROR_EXTERNAL_BOOT_DISABLED);
}
return VB2_SUCCESS;
}
static vb2_error_t developer_boot_external_init(struct ui_context *ui)
{
vb2_error_t rv;
ui->state->selected_item = DEVELOPER_BOOT_EXTERNAL_ITEM_BACK;
VB2_TRY(developer_boot_external_check(ui));
rv = VbTryLoadKernel(ui->ctx, VB_DISK_FLAG_REMOVABLE);
/* If the status of the external disk doesn't match, skip the screen. */
if (rv != VB2_ERROR_LK_NO_DISK_FOUND)
return ui_screen_back(ui);
return VB2_SUCCESS;
}
static const struct ui_screen_info developer_boot_external_screen = {
.id = UI_SCREEN_DEVELOPER_BOOT_EXTERNAL,
.name = "Developer boot from external disk",
.icon = UI_ICON_TYPE_NONE,
.title = "dev_boot_ext_title.bmp",
.desc = UI_DESC(developer_boot_external_desc),
.menu = UI_MENU(developer_boot_external_items),
.init = developer_boot_external_init,
.reinit = developer_boot_external_init,
.action = ui_developer_mode_boot_external_action,
.mesg = "Plug in your external disk\n"
"If your external disk is ready with a Chrome OS image,\n"
"plug it into the device to boot.",
};
/******************************************************************************/
/* UI_SCREEN_DEVELOPER_INVALID_DISK */
#define DEVELOPER_INVALID_DISK_ITEM_BACK 1
static const char *const developer_invalid_disk_desc[] = {
"dev_invalid_disk_desc0.bmp",
};
static const struct ui_menu_item developer_invalid_disk_items[] = {
LANGUAGE_SELECT_ITEM,
[DEVELOPER_INVALID_DISK_ITEM_BACK] = BACK_ITEM,
POWER_OFF_ITEM,
};
static vb2_error_t developer_invalid_disk_init(struct ui_context *ui)
{
vb2_error_t rv;
ui->state->selected_item = DEVELOPER_INVALID_DISK_ITEM_BACK;
VB2_TRY(developer_boot_external_check(ui));
rv = VbTryLoadKernel(ui->ctx, VB_DISK_FLAG_REMOVABLE);
/* If the status of the external disk doesn't match, skip the screen. */
if (rv == VB2_SUCCESS || rv == VB2_ERROR_LK_NO_DISK_FOUND)
return ui_screen_back(ui);
return VB2_SUCCESS;
}
static const struct ui_screen_info developer_invalid_disk_screen = {
.id = UI_SCREEN_DEVELOPER_INVALID_DISK,
.name = "Invalid external disk in dev mode",
.icon = UI_ICON_TYPE_ERROR,
.title = "dev_invalid_disk_title.bmp",
.desc = UI_DESC(developer_invalid_disk_desc),
.menu = UI_MENU(developer_invalid_disk_items),
.init = developer_invalid_disk_init,
.reinit = developer_invalid_disk_init,
.action = ui_developer_mode_boot_external_action,
.mesg = "No valid image detected\n"
"Make sure your external disk has a valid Chrome OS image,\n"
"and re-insert the disk when ready.",
};
/******************************************************************************/
/* UI_SCREEN_DEVELOPER_SELECT_ALTFW */
static const struct ui_menu_item developer_select_altfw_items_before[] = {
LANGUAGE_SELECT_ITEM,
};
static const struct ui_menu_item developer_select_altfw_items_after[] = {
BACK_ITEM,
POWER_OFF_ITEM,
};
static vb2_error_t developer_select_bootloader_init(struct ui_context *ui)
{
if (ui_get_menu(ui)->num_items == 0)
return set_ui_error_and_go_back(ui, UI_ERROR_ALTFW_EMPTY);
/* Select the first bootloader. */
ui->state->selected_item =
ARRAY_SIZE(developer_select_altfw_items_before);
return VB2_SUCCESS;
}
static size_t get_altfw_count(void)
{
struct altfw_info *node;
size_t count = 0;
ListNode *head;
head = payload_get_altfw_list();
if (head) {
list_for_each(node, *head, list_node) {
/* Discount default seqnum=0, since it is duplicated. */
if (node->seqnum)
count += 1;
}
}
return count;
}
static vb2_error_t developer_boot_altfw_impl(struct ui_context *ui,
uint32_t altfw_id)
{
if (!(ui->ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) ||
!(ui->ctx->flags & VB2_CONTEXT_DEV_BOOT_ALLOWED) ||
!(ui->ctx->flags & VB2_CONTEXT_DEV_BOOT_ALTFW_ALLOWED)) {
UI_ERROR("ERROR: Dev mode alternate bootloader not allowed\n");
return set_ui_error(ui, UI_ERROR_ALTFW_DISABLED);
}
if (get_altfw_count() == 0) {
UI_ERROR("ERROR: No alternate bootloader was found\n");
return set_ui_error(ui, UI_ERROR_ALTFW_EMPTY);
}
UI_INFO("Try booting from bootloader #%u\n", altfw_id);
/* payload_run_altfw will not return if successful. */
payload_run_altfw(altfw_id);
UI_ERROR("ERROR: Alternate bootloader failed\n");
return set_ui_error(ui, UI_ERROR_ALTFW_FAILED);
}
static vb2_error_t developer_boot_altfw_id_action(struct ui_context *ui)
{
/* Validity check, should never happen. */
if (ui->state->screen->id != UI_SCREEN_DEVELOPER_SELECT_ALTFW) {
UI_ERROR("ERROR: Boot altfw id from wrong screen: %#x\n",
ui->state->screen->id);
return VB2_SUCCESS;
}
const size_t menu_before_len =
ARRAY_SIZE(developer_select_altfw_items_before);
uint32_t altfw_id = ui->state->selected_item - menu_before_len + 1;
return developer_boot_altfw_impl(ui, altfw_id);
}
vb2_error_t ui_developer_mode_boot_altfw_action(struct ui_context *ui)
{
/* Bootloader #0 is the default. */
return developer_boot_altfw_impl(ui, 0);
}
static const struct ui_menu *get_bootloader_menu(struct ui_context *ui)
{
struct altfw_info *node;
ListNode *head;
uint32_t num_bootloaders = 0;
struct ui_menu_item *items;
size_t num_items;
uint32_t i;
/* Cached */
if (ui->bootloader_menu.num_items > 0) {
UI_INFO("Cached with %zu item(s)\n",
ui->bootloader_menu.num_items);
return &ui->bootloader_menu;
}
head = payload_get_altfw_list();
if (!head) {
UI_ERROR("ERROR: Failed to get altfw list\n");
return NULL;
}
list_for_each(node, *head, list_node) {
/* Discount default seqnum=0, since it is duplicated. */
if (node->seqnum)
num_bootloaders++;
}
if (num_bootloaders == 0) {
UI_WARN("No bootloader was found\n");
return NULL;
}
UI_INFO("num_bootloaders: %u\n", num_bootloaders);
num_items = num_bootloaders +
ARRAY_SIZE(developer_select_altfw_items_before) +
ARRAY_SIZE(developer_select_altfw_items_after);
items = malloc(num_items * sizeof(struct ui_menu_item));
if (!items) {
UI_ERROR("Failed to malloc menu items for bootloaders\n");
return NULL;
}
/* Copy prefix items to the begin. */
memcpy(&items[0], developer_select_altfw_items_before,
sizeof(developer_select_altfw_items_before));
/* Copy bootloaders. */
i = ARRAY_SIZE(developer_select_altfw_items_before);
list_for_each(node, *head, list_node) {
/* Discount default seqnum=0, since it is duplicated. */
if (!node->seqnum)
continue;
UI_INFO("Bootloader: filename=%s, name=%s, "
"desc=%s, seqnum=%d\n",
node->filename, node->name,
node->desc, node->seqnum);
const char *name = node->name;
if (!name || strlen(name) == 0) {
UI_WARN("Failed to get bootloader name with "
"seqnum=%d, use filename instead\n",
node->seqnum);
name = node->filename;
}
items[i] = (struct ui_menu_item){
.name = name,
.action = developer_boot_altfw_id_action,
};
i++;
}
/* Copy postfix items to the end. */
memcpy(&items[i], developer_select_altfw_items_after,
sizeof(developer_select_altfw_items_after));
ui->bootloader_menu.num_items = num_items;
ui->bootloader_menu.items = items;
return &ui->bootloader_menu;
}
static const struct ui_screen_info developer_select_bootloader_screen = {
.id = UI_SCREEN_DEVELOPER_SELECT_ALTFW,
.name = "Select alternate bootloader",
.icon = UI_ICON_TYPE_NONE,
.title = "dev_select_bootloader_title.bmp",
.init = developer_select_bootloader_init,
.mesg = "Select an alternate bootloader.",
.get_menu = get_bootloader_menu,
};
/******************************************************************************/
/* UI_SCREEN_DIAGNOSTICS */
#define DIAGNOSTICS_ITEM_STORAGE_HEALTH 1
#define DIAGNOSTICS_ITEM_STORAGE_TEST_SHORT 2
#define DIAGNOSTICS_ITEM_STORAGE_TEST_EXTENDED 3
static vb2_error_t diagnostics_init(struct ui_context *ui)
{
const char *unused_log_string;
vb2_error_t rv = vb2ex_diag_get_storage_test_log(&unused_log_string);
if (rv == VB2_ERROR_EX_UNIMPLEMENTED) {
VB2_SET_BIT(ui->state->disabled_item_mask,
DIAGNOSTICS_ITEM_STORAGE_TEST_SHORT);
VB2_SET_BIT(ui->state->disabled_item_mask,
DIAGNOSTICS_ITEM_STORAGE_TEST_EXTENDED);
}
ui->state->selected_item = DIAGNOSTICS_ITEM_STORAGE_HEALTH;
return VB2_SUCCESS;
}
static const char *const diagnostics_desc[] = {
"diag_menu_desc0.bmp",
};
static const struct ui_menu_item diagnostics_items[] = {
LANGUAGE_SELECT_ITEM,
[DIAGNOSTICS_ITEM_STORAGE_HEALTH] = {
.name = "Storage health info",
.file = "btn_diag_storage_health.bmp",
.target = UI_SCREEN_DIAGNOSTICS_STORAGE_HEALTH,
},
[DIAGNOSTICS_ITEM_STORAGE_TEST_SHORT] = {
.name = "Storage self-test (short)",
.file = "btn_diag_storage_short_test.bmp",
.target = UI_SCREEN_DIAGNOSTICS_STORAGE_TEST_SHORT,
},
[DIAGNOSTICS_ITEM_STORAGE_TEST_EXTENDED] = {
.name = "Storage self-test (Extended)",
.file = "btn_diag_storage_ext_test.bmp",
.target = UI_SCREEN_DIAGNOSTICS_STORAGE_TEST_EXTENDED,
},
{
.name = "Memory check (quick)",
.file = "btn_diag_memory_quick.bmp",
.target = UI_SCREEN_DIAGNOSTICS_MEMORY_QUICK,
},
{
.name = "Memory check (full)",
.file = "btn_diag_memory_full.bmp",
.target = UI_SCREEN_DIAGNOSTICS_MEMORY_FULL,
},
POWER_OFF_ITEM,
};
static const struct ui_screen_info diagnostics_screen = {
.id = UI_SCREEN_DIAGNOSTICS,
.name = "Diagnostic tools",
.icon = UI_ICON_TYPE_INFO,
.title = "diag_menu_title.bmp",
.desc = UI_DESC(diagnostics_desc),
.menu = UI_MENU(diagnostics_items),
.init = diagnostics_init,
.mesg = "Select the component you'd like to check",
};
/******************************************************************************/
/* UI_SCREEN_DIAGNOSTICS_STORAGE_HEALTH */
#define DIAGNOSTICS_STORAGE_HEALTH_ITEM_PAGE_UP 0
#define DIAGNOSTICS_STORAGE_HEALTH_ITEM_PAGE_DOWN 1
#define DIAGNOSTICS_STORAGE_HEALTH_ITEM_BACK 2
static const struct ui_menu_item diagnostics_storage_health_items[] = {
[DIAGNOSTICS_STORAGE_HEALTH_ITEM_PAGE_UP] = PAGE_UP_ITEM,
[DIAGNOSTICS_STORAGE_HEALTH_ITEM_PAGE_DOWN] = PAGE_DOWN_ITEM,
[DIAGNOSTICS_STORAGE_HEALTH_ITEM_BACK] = BACK_ITEM,
POWER_OFF_ITEM,
};
static vb2_error_t diagnostics_storage_health_init_impl(
struct ui_context *ui)
{
const char *log_string;
VB2_TRY(vb2ex_diag_get_storage_health(&log_string));
VB2_TRY(log_page_update(ui, log_string));
return log_page_reset_to_top(ui);
}
static vb2_error_t diagnostics_storage_health_init(struct ui_context *ui)
{
if (vb2_is_error(diagnostics_storage_health_init_impl(ui)))
return set_ui_error_and_go_back(ui, UI_ERROR_DIAGNOSTICS);
return VB2_SUCCESS;
}
static const struct ui_screen_info diagnostics_storage_health_screen = {
.id = UI_SCREEN_DIAGNOSTICS_STORAGE_HEALTH,
.name = "Storage health info",
.icon = UI_ICON_TYPE_NONE,
.title = "diag_storage_health_title.bmp",
.menu = UI_MENU(diagnostics_storage_health_items),
.init = diagnostics_storage_health_init,
.draw_desc = draw_log_desc,
.mesg = "Storage health info",
.page_up_item = DIAGNOSTICS_STORAGE_HEALTH_ITEM_PAGE_UP,
.page_down_item = DIAGNOSTICS_STORAGE_HEALTH_ITEM_PAGE_DOWN,
.back_item = DIAGNOSTICS_STORAGE_HEALTH_ITEM_BACK,
};
/******************************************************************************/
/* UI_SCREEN_DIAGNOSTICS_STORAGE_TEST_SHORT */
/* UI_SCREEN_DIAGNOSTICS_STORAGE_TEST_EXTENDED */
#define DIAGNOSTICS_STORAGE_TEST_ITEM_PAGE_UP 0
#define DIAGNOSTICS_STORAGE_TEST_ITEM_PAGE_DOWN 1
#define DIAGNOSTICS_STORAGE_TEST_ITEM_BACK 2
#define DIAGNOSTICS_STORAGE_TEST_ITEM_CANCEL 3
static vb2_error_t diagnostics_storage_test_update_impl(
struct ui_context *ui)
{
const char *log_string;
int is_test_running = 0;
/* Early return if the test is done. */
if (ui->state->test_finished)
return VB2_SUCCESS;
vb2_error_t rv = vb2ex_diag_get_storage_test_log(&log_string);
switch (rv) {
case VB2_ERROR_EX_DIAG_TEST_RUNNING:
is_test_running = 1;
break;
case VB2_SUCCESS:
ui->state->test_finished = 1;
break;
default:
UI_INFO("vb2ex_diag_get_storage_test_log returned %#x\n", rv);
return rv;
}
VB2_TRY(log_page_show_back_or_cancel(ui, is_test_running));
return log_page_update(ui, log_string);
}
static vb2_error_t diagnostics_storage_test_update(struct ui_context *ui)
{
if (vb2_is_error(diagnostics_storage_test_update_impl(ui)))
return set_ui_error_and_go_back(ui, UI_ERROR_DIAGNOSTICS);
return VB2_SUCCESS;
}
static vb2_error_t diagnostics_storage_test_control(
struct ui_context *ui, enum BlockDevTestOpsType op)
{
if (vb2_is_error(diag_storage_test_control(op)))
return set_ui_error_and_go_back(ui, UI_ERROR_DIAGNOSTICS);
return VB2_SUCCESS;
}
static vb2_error_t diagnostics_storage_test_init(struct ui_context *ui)
{
VB2_TRY(diagnostics_storage_test_update(ui));
if (vb2_is_error(log_page_reset_to_top(ui)))
return set_ui_error_and_go_back(ui, UI_ERROR_DIAGNOSTICS);
return VB2_SUCCESS;
}
static vb2_error_t diagnostics_storage_test_short_init(
struct ui_context *ui)
{
VB2_TRY(diagnostics_storage_test_control(ui,
BLOCKDEV_TEST_OPS_TYPE_STOP));
VB2_TRY(diagnostics_storage_test_control(ui,
BLOCKDEV_TEST_OPS_TYPE_SHORT));
return diagnostics_storage_test_init(ui);
}
static vb2_error_t diagnostics_storage_test_extended_init(
struct ui_context *ui)
{
VB2_TRY(diagnostics_storage_test_control(ui,
BLOCKDEV_TEST_OPS_TYPE_STOP));
VB2_TRY(diagnostics_storage_test_control(
ui, BLOCKDEV_TEST_OPS_TYPE_EXTENDED));
return diagnostics_storage_test_init(ui);
}
static vb2_error_t diagnostics_storage_test_cancel(struct ui_context *ui)
{
VB2_TRY(diagnostics_storage_test_control(ui,
BLOCKDEV_TEST_OPS_TYPE_STOP));
return ui_screen_back(ui);
}
static const struct ui_menu_item diagnostics_storage_test_items[] = {
[DIAGNOSTICS_STORAGE_TEST_ITEM_PAGE_UP] = PAGE_UP_ITEM,
[DIAGNOSTICS_STORAGE_TEST_ITEM_PAGE_DOWN] = PAGE_DOWN_ITEM,
[DIAGNOSTICS_STORAGE_TEST_ITEM_BACK] = {
.name = "Back",
.file = "btn_back.bmp",
.flags = UI_MENU_ITEM_FLAG_TRANSIENT,
.action = ui_screen_back,
},
[DIAGNOSTICS_STORAGE_TEST_ITEM_CANCEL] = {
.name = "Cancel",
.file = "btn_cancel.bmp",
.flags = UI_MENU_ITEM_FLAG_TRANSIENT,
.action = diagnostics_storage_test_cancel,
},
POWER_OFF_ITEM,
};
static const struct ui_screen_info diagnostics_storage_test_short_screen = {
.id = UI_SCREEN_DIAGNOSTICS_STORAGE_TEST_SHORT,
.name = "Storage self-test (short)",
.icon = UI_ICON_TYPE_NONE,
.title = "diag_storage_srt_test_title.bmp",
.menu = UI_MENU(diagnostics_storage_test_items),
.init = diagnostics_storage_test_short_init,
.action = diagnostics_storage_test_update,
.draw_desc = draw_log_desc,
.mesg = "Storage self test (short)",
.page_up_item = DIAGNOSTICS_STORAGE_TEST_ITEM_PAGE_UP,
.page_down_item = DIAGNOSTICS_STORAGE_TEST_ITEM_PAGE_DOWN,
.back_item = DIAGNOSTICS_STORAGE_TEST_ITEM_BACK,
.cancel_item = DIAGNOSTICS_STORAGE_TEST_ITEM_CANCEL,
};
static const struct ui_screen_info diagnostics_storage_test_extended_screen = {
.id = UI_SCREEN_DIAGNOSTICS_STORAGE_TEST_EXTENDED,
.name = "Storage self-test (extended)",
.icon = UI_ICON_TYPE_NONE,
.title = "diag_storage_ext_test_title.bmp",
.menu = UI_MENU(diagnostics_storage_test_items),
.init = diagnostics_storage_test_extended_init,
.action = diagnostics_storage_test_update,
.draw_desc = draw_log_desc,
.mesg = "Storage self test (extended)",
.page_up_item = DIAGNOSTICS_STORAGE_TEST_ITEM_PAGE_UP,
.page_down_item = DIAGNOSTICS_STORAGE_TEST_ITEM_PAGE_DOWN,
.back_item = DIAGNOSTICS_STORAGE_TEST_ITEM_BACK,
.cancel_item = DIAGNOSTICS_STORAGE_TEST_ITEM_CANCEL,
};
/******************************************************************************/
/* UI_SCREEN_DIAGNOSTICS_MEMORY_QUICK */
/* UI_SCREEN_DIAGNOSTICS_MEMORY_FULL */
#define DIAGNOSTICS_MEMORY_ITEM_PAGE_UP 0
#define DIAGNOSTICS_MEMORY_ITEM_PAGE_DOWN 1
#define DIAGNOSTICS_MEMORY_ITEM_BACK 2
#define DIAGNOSTICS_MEMORY_ITEM_CANCEL 3
typedef vb2_error_t (*memory_test_op_t)(int reset, const char **out);
static vb2_error_t diagnostics_memory_update_screen_impl(
struct ui_context *ui, memory_test_op_t op, int reset)
{
const char *log_string = NULL;
vb2_error_t rv;
int is_test_running = 0;
/* Early return if the memory test is done. */
if (ui->state->test_finished)
return VB2_SUCCESS;
rv = op(reset, &log_string);
switch (rv) {
/* The test is still running but the output buffer was unchanged. */
case VB2_ERROR_EX_DIAG_TEST_RUNNING:
return VB2_SUCCESS;
case VB2_ERROR_EX_DIAG_TEST_UPDATED:
is_test_running = 1;
break;
case VB2_SUCCESS:
ui->state->test_finished = 1;
break;
default:
UI_INFO("memory_test_op returned %#x\n", rv);
return rv;
}
VB2_TRY(log_page_show_back_or_cancel(ui, is_test_running));
return log_page_update(ui, log_string);
}
static vb2_error_t diagnostics_memory_update_screen(struct ui_context *ui,
memory_test_op_t op,
int reset)
{
if (vb2_is_error(diagnostics_memory_update_screen_impl(ui, op, reset)))
return set_ui_error_and_go_back(ui, UI_ERROR_DIAGNOSTICS);
return VB2_SUCCESS;
}
static vb2_error_t diagnostics_memory_init_quick(struct ui_context *ui)
{
VB2_TRY(diagnostics_memory_update_screen(
ui, &vb2ex_diag_memory_quick_test, 1));
if (vb2_is_error(log_page_reset_to_top(ui)))
return set_ui_error_and_go_back(ui, UI_ERROR_DIAGNOSTICS);
return VB2_SUCCESS;
}
static vb2_error_t diagnostics_memory_init_full(struct ui_context *ui)
{
VB2_TRY(diagnostics_memory_update_screen(
ui, &vb2ex_diag_memory_full_test, 1));
if (vb2_is_error(log_page_reset_to_top(ui)))
return set_ui_error_and_go_back(ui, UI_ERROR_DIAGNOSTICS);
return VB2_SUCCESS;
}
static vb2_error_t diagnostics_memory_update_quick(struct ui_context *ui)
{
return diagnostics_memory_update_screen(
ui, &vb2ex_diag_memory_quick_test, 0);
}
static vb2_error_t diagnostics_memory_update_full(struct ui_context *ui)
{
return diagnostics_memory_update_screen(
ui, &vb2ex_diag_memory_full_test, 0);
}
static const struct ui_menu_item diagnostics_memory_items[] = {
[DIAGNOSTICS_MEMORY_ITEM_PAGE_UP] = PAGE_UP_ITEM,
[DIAGNOSTICS_MEMORY_ITEM_PAGE_DOWN] = PAGE_DOWN_ITEM,
[DIAGNOSTICS_MEMORY_ITEM_BACK] = {
.name = "Back",
.file = "btn_back.bmp",
.flags = UI_MENU_ITEM_FLAG_TRANSIENT,
.action = ui_screen_back,
},
[DIAGNOSTICS_MEMORY_ITEM_CANCEL] = {
.name = "Cancel",
.file = "btn_cancel.bmp",
.flags = UI_MENU_ITEM_FLAG_TRANSIENT,
.action = ui_screen_back,
},
POWER_OFF_ITEM,
};
static const struct ui_screen_info diagnostics_memory_quick_screen = {
.id = UI_SCREEN_DIAGNOSTICS_MEMORY_QUICK,
.name = "Memory check (quick)",
.icon = UI_ICON_TYPE_NONE,
.title = "diag_memory_quick_title.bmp",
.menu = UI_MENU(diagnostics_memory_items),
.init = diagnostics_memory_init_quick,
.action = diagnostics_memory_update_quick,
.draw_desc = draw_log_desc,
.mesg = "Memory check (quick)",
.page_up_item = DIAGNOSTICS_MEMORY_ITEM_PAGE_UP,
.page_down_item = DIAGNOSTICS_MEMORY_ITEM_PAGE_DOWN,
.back_item = DIAGNOSTICS_MEMORY_ITEM_BACK,
.cancel_item = DIAGNOSTICS_MEMORY_ITEM_CANCEL,
};
static const struct ui_screen_info diagnostics_memory_full_screen = {
.id = UI_SCREEN_DIAGNOSTICS_MEMORY_FULL,
.name = "Memory check (full)",
.icon = UI_ICON_TYPE_NONE,
.title = "diag_memory_full_title.bmp",
.menu = UI_MENU(diagnostics_memory_items),
.init = diagnostics_memory_init_full,
.action = diagnostics_memory_update_full,
.draw_desc = draw_log_desc,
.mesg = "Memory check (full)",
.page_up_item = DIAGNOSTICS_MEMORY_ITEM_PAGE_UP,
.page_down_item = DIAGNOSTICS_MEMORY_ITEM_PAGE_DOWN,
.back_item = DIAGNOSTICS_MEMORY_ITEM_BACK,
.cancel_item = DIAGNOSTICS_MEMORY_ITEM_CANCEL,
};
/******************************************************************************/
static const struct ui_screen_info *const screens[] = {
&firmware_sync_screen,
&language_select_screen,
&broken_screen,
&advanced_options_screen,
&debug_info_screen,
&firmware_log_screen,
&recovery_to_dev_screen,
&recovery_select_screen,
&recovery_phone_step1_screen,
&recovery_phone_step2_screen,
&recovery_disk_step1_screen,
&recovery_disk_step2_screen,
&recovery_disk_step3_screen,
&recovery_invalid_screen,
&developer_mode_screen,
&developer_to_norm_screen,
&developer_boot_external_screen,
&developer_invalid_disk_screen,
&developer_select_bootloader_screen,
&diagnostics_screen,
&diagnostics_storage_health_screen,
&diagnostics_storage_test_short_screen,
&diagnostics_storage_test_extended_screen,
&diagnostics_memory_quick_screen,
&diagnostics_memory_full_screen,
};
const struct ui_screen_info *ui_get_screen_info(enum ui_screen screen_id)
{
static const struct ui_screen_info *screen_info;
int i;
if (screen_info && screen_info->id == screen_id)
return screen_info;
for (i = 0; i < ARRAY_SIZE(screens); i++) {
if (screens[i]->id == screen_id) {
screen_info = screens[i];
return screen_info;
}
}
return NULL;
}