summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
authorHsuan Ting Chen <roccochen@chromium.org>2020-03-24 16:51:42 +0800
committerCommit Bot <commit-bot@chromium.org>2020-05-04 07:03:46 +0000
commitb4dd58141ba7d003029bec0ad3391327f32a8a39 (patch)
tree74b9cb4fda72c764b98a516207b19a17c0f1561f /firmware
parent173ac74263e1979e4b05911ba8b04ddc3bd27029 (diff)
downloadvboot-b4dd58141ba7d003029bec0ad3391327f32a8a39.tar.gz
vboot: Implement common UI loop
Add config DETACHABLE to control the navigation in menu UI. Implement 4 screens: - VB2_SCREEN_RECOVERY_SELECT - VB2_SCREEN_RECOVERY_INVALID - VB2_SCREEN_RECOVERY_PHONE_STEP1 - VB2_SCREEN_RECOVERY_DISK_STEP1 Handling user inputs. - Shutdown request through VbExIsShutdownRequested. - Navigate with up, down, and enter key. - Navigate with volume up, volume down, and power button in DETACHABLE. Implement common UI loop, currently used for manual and non-manual recovery (developer forthcoming). BRANCH=none BUG=b:146399181 TEST=USE="menu_ui" emerge-nami depthcharge TEST=USE="menu_ui detachable" emerge-nami depthcharge TEST=make clean && make runtests TEST=DETACHABLE=1; make clean && make runtests Cq-Depend: chromium:2152212 Signed-off-by: Hsuan Ting Chen <roccochen@chromium.org> Change-Id: I4e0f2cdf053f75935529826df215b06c8a9af4cc Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/2117810 Reviewed-by: Yu-Ping Wu <yupingso@chromium.org>
Diffstat (limited to 'firmware')
-rw-r--r--firmware/2lib/2ui.c309
-rw-r--r--firmware/2lib/2ui_screens.c107
-rw-r--r--firmware/2lib/include/2api.h4
-rw-r--r--firmware/2lib/include/2ui.h50
-rw-r--r--firmware/2lib/include/2ui_private.h41
5 files changed, 498 insertions, 13 deletions
diff --git a/firmware/2lib/2ui.c b/firmware/2lib/2ui.c
index 5b38e08a..eab7c58d 100644
--- a/firmware/2lib/2ui.c
+++ b/firmware/2lib/2ui.c
@@ -12,18 +12,284 @@
#include "2return_codes.h"
#include "2secdata.h"
#include "2ui.h"
+#include "2ui_private.h"
+#include "vboot_api.h" /* For VB_SHUTDOWN_REQUEST_POWER_BUTTON */
#include "vboot_kernel.h"
+#define KEY_DELAY_MS 20 /* Delay between key scans in UI loops */
+
+/*****************************************************************************/
+/* Global variables */
+
+enum power_button_state power_button;
+int invalid_disk_last = -1;
+
+/*****************************************************************************/
+/* Utility functions */
+
+/**
+ * Checks GBB flags against VbExIsShutdownRequested() shutdown request to
+ * determine if a shutdown is required.
+ *
+ * @param ctx Context pointer
+ * @param key Pressed key (VB_BUTTON_POWER_SHORT_PRESS)
+ * @return true if a shutdown is required, or false otherwise.
+ */
+int shutdown_required(struct vb2_context *ctx, uint32_t key)
+{
+ struct vb2_gbb_header *gbb = vb2_get_gbb(ctx);
+ uint32_t shutdown_request = VbExIsShutdownRequested();
+
+ /*
+ * Ignore power button push until after we have seen it released.
+ * This avoids shutting down immediately if the power button is still
+ * being held on startup. After we've recognized a valid power button
+ * push then don't report the event until after the button is released.
+ */
+ if (shutdown_request & VB_SHUTDOWN_REQUEST_POWER_BUTTON) {
+ shutdown_request &= ~VB_SHUTDOWN_REQUEST_POWER_BUTTON;
+ if (power_button == POWER_BUTTON_RELEASED)
+ power_button = POWER_BUTTON_PRESSED;
+ } else {
+ if (power_button == POWER_BUTTON_PRESSED)
+ shutdown_request |= VB_SHUTDOWN_REQUEST_POWER_BUTTON;
+ power_button = POWER_BUTTON_RELEASED;
+ }
+
+ if (key == VB_BUTTON_POWER_SHORT_PRESS)
+ shutdown_request |= VB_SHUTDOWN_REQUEST_POWER_BUTTON;
+
+ /* If desired, ignore shutdown request due to lid closure. */
+ if (gbb->flags & VB2_GBB_FLAG_DISABLE_LID_SHUTDOWN)
+ shutdown_request &= ~VB_SHUTDOWN_REQUEST_LID_CLOSED;
+
+ /*
+ * In detachables, disable shutdown due to power button.
+ * It is used for menu selection instead.
+ */
+ if (DETACHABLE)
+ shutdown_request &= ~VB_SHUTDOWN_REQUEST_POWER_BUTTON;
+
+ return !!shutdown_request;
+}
+
/*****************************************************************************/
-/* Entry points */
+/* Menu navigation actions */
+
+/**
+ * Update selected_item, taking into account disabled indices (from
+ * disabled_item_mask). The selection does not wrap, meaning that we block
+ * on the 0 or max index when we hit the top or bottom of the menu.
+ */
+vb2_error_t menu_up_action(struct vb2_ui_context *ui)
+{
+ int item;
+
+ if (!DETACHABLE && ui->key == VB_BUTTON_VOL_UP_SHORT_PRESS)
+ return VB2_REQUEST_UI_CONTINUE;
+
+ item = ui->state.selected_item - 1;
+ while (item >= 0 &&
+ ((1 << item) & ui->state.disabled_item_mask))
+ item--;
+ /* Only update if item is valid */
+ if (item >= 0)
+ ui->state.selected_item = item;
+
+ return VB2_REQUEST_UI_CONTINUE;
+}
+
+vb2_error_t menu_down_action(struct vb2_ui_context *ui)
+{
+ int item;
+
+ if (!DETACHABLE && ui->key == VB_BUTTON_VOL_DOWN_SHORT_PRESS)
+ return VB2_REQUEST_UI_CONTINUE;
+
+ item = ui->state.selected_item + 1;
+ while (item < ui->state.screen->num_items &&
+ ((1 << item) & ui->state.disabled_item_mask))
+ item++;
+ /* Only update if item is valid */
+ if (item < ui->state.screen->num_items)
+ ui->state.selected_item = item;
+
+ return VB2_REQUEST_UI_CONTINUE;
+}
+
+/**
+ * Navigate to the target screen of the current menu item selection.
+ */
+vb2_error_t menu_select_action(struct vb2_ui_context *ui)
+{
+ const struct vb2_menu_item *menu_item;
+
+ if (!DETACHABLE && ui->key == VB_BUTTON_POWER_SHORT_PRESS)
+ return VB2_REQUEST_UI_CONTINUE;
+
+ if (ui->state.screen->num_items == 0)
+ return VB2_REQUEST_UI_CONTINUE;
+
+ menu_item = &ui->state.screen->items[ui->state.selected_item];
+
+ VB2_DEBUG("Select <%s> menu item <%s>\n",
+ ui->state.screen->name, menu_item->text);
+
+ if (menu_item->target) {
+ VB2_DEBUG("Changing to target screen %#x for menu item <%s>\n",
+ menu_item->target, menu_item->text);
+ change_screen(ui, menu_item->target);
+ } else {
+ VB2_DEBUG("No target set for menu item <%s>\n",
+ menu_item->text);
+ }
+
+ return VB2_REQUEST_UI_CONTINUE;
+}
+
+/**
+ * Return back to the previous screen.
+ */
+vb2_error_t menu_back_action(struct vb2_ui_context *ui)
+{
+ change_screen(ui, ui->root_screen->id);
+ return VB2_REQUEST_UI_CONTINUE;
+}
+
+/*****************************************************************************/
+/* Action lookup tables */
+
+static struct input_action action_table[] = {
+ { VB_KEY_UP, menu_up_action },
+ { VB_KEY_DOWN, menu_down_action },
+ { VB_KEY_ENTER, menu_select_action },
+ { VB_BUTTON_VOL_UP_SHORT_PRESS, menu_up_action },
+ { VB_BUTTON_VOL_DOWN_SHORT_PRESS, menu_down_action },
+ { VB_BUTTON_POWER_SHORT_PRESS, menu_select_action },
+ { VB_KEY_ESC, menu_back_action },
+};
+
+vb2_error_t (*input_action_lookup(int key))(struct vb2_ui_context *ui)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(action_table); i++)
+ if (action_table[i].key == key)
+ return action_table[i].action;
+ return NULL;
+}
+
+/*****************************************************************************/
+/* Core UI functions */
+
+void change_screen(struct vb2_ui_context *ui, enum vb2_screen id)
+{
+ const struct vb2_screen_info *new_screen_info = vb2_get_screen_info(id);
+ int locale_id;
+ if (new_screen_info == NULL) {
+ VB2_DEBUG("ERROR: Screen entry %#x not found; ignoring\n", id);
+ } else {
+ locale_id = ui->state.locale_id;
+ memset(&ui->state, 0, sizeof(ui->state));
+ ui->state.screen = new_screen_info;
+ ui->state.locale_id = locale_id;
+ }
+}
+
+void validate_selection(struct vb2_screen_state *state)
+{
+ if ((state->selected_item == 0 && state->screen->num_items == 0) ||
+ (state->selected_item < state->screen->num_items &&
+ !((1 << state->selected_item) & state->disabled_item_mask)))
+ return;
+
+ /* Selection invalid; select the first available non-disabled item. */
+ state->selected_item = 0;
+ while (((1 << state->selected_item) & state->disabled_item_mask) &&
+ state->selected_item < state->screen->num_items)
+ state->selected_item++;
+
+ /* No non-disabled items available; just choose 0. */
+ if (state->selected_item >= state->screen->num_items)
+ state->selected_item = 0;
+}
+
+vb2_error_t ui_loop(struct vb2_context *ctx, enum vb2_screen root_screen_id,
+ vb2_error_t (*global_action)(struct vb2_ui_context *ui))
+{
+ struct vb2_ui_context ui;
+ struct vb2_screen_state prev_state;
+ uint32_t key;
+ uint32_t key_flags;
+ vb2_error_t (*action)(struct vb2_ui_context *ui);
+ vb2_error_t rv;
+
+ ui.ctx = ctx;
+ ui.root_screen = vb2_get_screen_info(root_screen_id);
+ if (ui.root_screen == NULL)
+ VB2_DIE("Root screen not found.\n");
+ change_screen(&ui, ui.root_screen->id);
+ memset(&prev_state, 0, sizeof(prev_state));
+
+ while (1) {
+ /* Draw if there are state changes. */
+ if (memcmp(&prev_state, &ui.state, sizeof(ui.state))) {
+ memcpy(&prev_state, &ui.state, sizeof(ui.state));
+
+ VB2_DEBUG("<%s> menu item <%s>\n",
+ ui.state.screen->name,
+ ui.state.screen->num_items ?
+ ui.state.screen->items[
+ ui.state.selected_item].text : "null");
+
+ /* TODO: Stop hard-coding the locale. */
+ vb2ex_display_ui(ui.state.screen->id, 0,
+ ui.state.selected_item,
+ ui.state.disabled_item_mask);
+ }
+
+ /* Check for shutdown request. */
+ key = VbExKeyboardReadWithFlags(&key_flags);
+ if (shutdown_required(ctx, key)) {
+ VB2_DEBUG("Shutdown required!\n");
+ return VB2_REQUEST_SHUTDOWN;
+ }
+
+ /* Run input action function if found. */
+ action = input_action_lookup(key);
+ if (action) {
+ ui.key = key;
+ rv = action(&ui);
+ ui.key = 0;
+ if (rv != VB2_REQUEST_UI_CONTINUE)
+ return rv;
+ validate_selection(&ui.state);
+ } else if (key) {
+ VB2_DEBUG("Pressed key %#x, trusted? %d\n", key,
+ !!(key_flags & VB_KEY_FLAG_TRUSTED_KEYBOARD));
+ }
+
+ /* Run global action function if available. */
+ if (global_action) {
+ rv = global_action(&ui);
+ validate_selection(&ui.state);
+ if (rv != VB2_REQUEST_UI_CONTINUE)
+ return rv;
+ }
+
+ /* Delay. */
+ VbExSleepMs(KEY_DELAY_MS);
+ }
+
+ return VB2_SUCCESS;
+}
+
+/*****************************************************************************/
+/* Developer mode */
vb2_error_t vb2_developer_menu(struct vb2_context *ctx)
{
enum vb2_dev_default_boot default_boot;
- /* TODO(roccochen): Init, wait for user, and boot. */
- vb2ex_display_ui(VB2_SCREEN_BLANK, 0, 0, 0);
-
/* If dev mode was disabled, loop forever. */
if (!vb2_dev_boot_allowed(ctx))
while (1);
@@ -45,22 +311,39 @@ vb2_error_t vb2_developer_menu(struct vb2_context *ctx)
return VbTryLoadKernel(ctx, VB_DISK_FLAG_FIXED);
}
+/*****************************************************************************/
+/* Broken recovery */
+
vb2_error_t vb2_broken_recovery_menu(struct vb2_context *ctx)
{
- /* TODO(roccochen): Init and wait for user to reset or shutdown. */
- vb2ex_display_ui(VB2_SCREEN_BLANK, 0, 0, 0);
+ return ui_loop(ctx, VB2_SCREEN_RECOVERY_BROKEN, NULL);
+}
- while (1);
+/*****************************************************************************/
+/* Manual recovery */
- return VB2_SUCCESS;
+vb2_error_t vb2_manual_recovery_menu(struct vb2_context *ctx)
+{
+ return ui_loop(ctx, VB2_SCREEN_RECOVERY_SELECT, try_recovery_action);
}
-vb2_error_t vb2_manual_recovery_menu(struct vb2_context *ctx)
+vb2_error_t try_recovery_action(struct vb2_ui_context *ui)
{
- /* TODO(roccochen): Init and wait for user. */
- vb2ex_display_ui(VB2_SCREEN_BLANK, 0 ,0, 0);
+ int invalid_disk;
+ vb2_error_t rv = VbTryLoadKernel(ui->ctx, VB_DISK_FLAG_REMOVABLE);
- while (1);
+ if (rv == VB2_SUCCESS)
+ return rv;
- return VB2_SUCCESS;
+ /* If disk validity state changed, switch to appropriate screen. */
+ invalid_disk = rv != VB2_ERROR_LK_NO_DISK_FOUND;
+ if (invalid_disk_last != invalid_disk) {
+ invalid_disk_last = invalid_disk;
+ if (invalid_disk)
+ change_screen(ui, VB2_SCREEN_RECOVERY_INVALID);
+ else
+ change_screen(ui, VB2_SCREEN_RECOVERY_SELECT);
+ }
+
+ return VB2_REQUEST_UI_CONTINUE;
}
diff --git a/firmware/2lib/2ui_screens.c b/firmware/2lib/2ui_screens.c
new file mode 100644
index 00000000..5b235c24
--- /dev/null
+++ b/firmware/2lib/2ui_screens.c
@@ -0,0 +1,107 @@
+/* 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 "2common.h"
+#include "2ui.h"
+
+#define MENU_ITEMS(a) \
+ .num_items = ARRAY_SIZE(a), \
+ .items = a
+
+static const struct vb2_menu_item empty_menu[] = { };
+
+/******************************************************************************/
+/* VB2_SCREEN_BLANK */
+
+static const struct vb2_screen_info blank_screen = {
+ .id = VB2_SCREEN_BLANK,
+ .name = "Blank",
+ MENU_ITEMS(empty_menu),
+};
+
+/******************************************************************************/
+/* VB2_SCREEN_RECOVERY_BROKEN */
+
+static const struct vb2_screen_info recovery_broken_screen = {
+ .id = VB2_SCREEN_RECOVERY_BROKEN,
+ .name = "Recover broken device",
+ MENU_ITEMS(empty_menu),
+};
+
+/******************************************************************************/
+/* VB2_SCREEN_RECOVERY_SELECT */
+
+static const struct vb2_menu_item recovery_select_items[] = {
+ {
+ .text = "Recovery using phone",
+ .target = VB2_SCREEN_RECOVERY_PHONE_STEP1,
+ },
+ {
+ .text = "Recovery using external disk",
+ .target = VB2_SCREEN_RECOVERY_DISK_STEP1,
+ },
+};
+
+static const struct vb2_screen_info recovery_select_screen = {
+ .id = VB2_SCREEN_RECOVERY_SELECT,
+ .name = "Recovery method selection",
+ MENU_ITEMS(recovery_select_items),
+};
+
+/******************************************************************************/
+/* VB2_SCREEN_RECOVERY_INVALID */
+
+static const struct vb2_screen_info recovery_invalid_screen = {
+ .id = VB2_SCREEN_RECOVERY_INVALID,
+ .name = "Invalid recovery inserted",
+ MENU_ITEMS(empty_menu),
+};
+
+/******************************************************************************/
+/* VB2_SCREEN_RECOVERY_PHONE_STEP1 */
+
+static const struct vb2_screen_info recovery_phone_step1_screen = {
+ .id = VB2_SCREEN_RECOVERY_PHONE_STEP1,
+ .name = "Phone recovery step 1",
+ MENU_ITEMS(empty_menu),
+};
+
+/******************************************************************************/
+/* VB2_SCREEN_RECOVERY_DISK_STEP1 */
+
+static const struct vb2_screen_info recovery_disk_step1_screen = {
+ .id = VB2_SCREEN_RECOVERY_DISK_STEP1,
+ .name = "Disk recovery step 1",
+ MENU_ITEMS(empty_menu),
+};
+
+/******************************************************************************/
+/*
+ * 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,
+ &recovery_broken_screen,
+ &recovery_select_screen,
+ &recovery_invalid_screen,
+ &recovery_phone_step1_screen,
+ &recovery_disk_step1_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;
+}
diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h
index 13d24ca9..dc6f19ab 100644
--- a/firmware/2lib/include/2api.h
+++ b/firmware/2lib/include/2api.h
@@ -1177,10 +1177,14 @@ enum vb2_screen {
VB2_SCREEN_RECOVERY_BROKEN = 0x110,
/* First recovery screen to select recovering from disk or phone */
VB2_SCREEN_RECOVERY_SELECT = 0x200,
+ /* Invalid recovery media inserted */
+ VB2_SCREEN_RECOVERY_INVALID = 0x201,
/* Recovery using disk */
VB2_SCREEN_RECOVERY_DISK_STEP1 = 0x210,
VB2_SCREEN_RECOVERY_DISK_STEP2 = 0x211,
VB2_SCREEN_RECOVERY_DISK_STEP3 = 0x212,
+ /* Recovery using phone */
+ VB2_SCREEN_RECOVERY_PHONE_STEP1 = 0x220,
};
/**
diff --git a/firmware/2lib/include/2ui.h b/firmware/2lib/include/2ui.h
index c5fdc1c6..f179024f 100644
--- a/firmware/2lib/include/2ui.h
+++ b/firmware/2lib/include/2ui.h
@@ -8,6 +8,56 @@
#ifndef VBOOT_REFERENCE_2UI_H_
#define VBOOT_REFERENCE_2UI_H_
+#include <2api.h>
+#include <2sysincludes.h>
+
+/*****************************************************************************/
+/* Data structures */
+
+struct vb2_screen_info {
+ /* Screen id */
+ enum vb2_screen id;
+ /* Screen name for printing to console only */
+ const char *name;
+ /* Number of menu items */
+ uint16_t num_items;
+ /* List of menu items */
+ const struct vb2_menu_item *items;
+};
+
+struct vb2_menu_item {
+ /* Text description */
+ const char *text;
+ /* Target screen */
+ enum vb2_screen target;
+};
+
+struct vb2_screen_state {
+ const struct vb2_screen_info *screen;
+ uint32_t locale_id;
+ uint32_t selected_item;
+ uint32_t disabled_item_mask;
+};
+
+struct vb2_ui_context {
+ struct vb2_context *ctx;
+ const struct vb2_screen_info *root_screen;
+ struct vb2_screen_state state;
+ uint32_t key;
+};
+
+/**
+ * Get info struct of a screen.
+ *
+ * @param screen Screen from enum vb2_screen
+ *
+ * @return screen info struct on success, NULL on error.
+ */
+const struct vb2_screen_info *vb2_get_screen_info(enum vb2_screen id);
+
+/*****************************************************************************/
+/* UI loops */
+
/**
* UI for a developer-mode boot.
*
diff --git a/firmware/2lib/include/2ui_private.h b/firmware/2lib/include/2ui_private.h
new file mode 100644
index 00000000..40ee6b51
--- /dev/null
+++ b/firmware/2lib/include/2ui_private.h
@@ -0,0 +1,41 @@
+/* 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.
+ *
+ * Private declarations for 2ui.c. Defined here for testing purposes.
+ */
+
+#include "2api.h"
+
+#ifndef VBOOT_REFERENCE_2UI_PRIVATE_H_
+#define VBOOT_REFERENCE_2UI_PRIVATE_H_
+
+enum power_button_state {
+ POWER_BUTTON_HELD_SINCE_BOOT = 0,
+ POWER_BUTTON_RELEASED,
+ POWER_BUTTON_PRESSED, /* Must have been previously released */
+};
+extern enum power_button_state power_button;
+int shutdown_required(struct vb2_context *ctx, uint32_t key);
+
+extern int invalid_disk_last;
+
+struct input_action {
+ int key;
+ vb2_error_t (*action)(struct vb2_ui_context *ui);
+};
+
+vb2_error_t menu_up_action(struct vb2_ui_context *ui);
+vb2_error_t menu_down_action(struct vb2_ui_context *ui);
+vb2_error_t menu_select_action(struct vb2_ui_context *ui);
+vb2_error_t menu_back_action(struct vb2_ui_context *ui);
+vb2_error_t (*input_action_lookup(int key))(struct vb2_ui_context *ui);
+
+void change_screen(struct vb2_ui_context *ui, enum vb2_screen id);
+void validate_selection(struct vb2_screen_state *state);
+vb2_error_t ui_loop(struct vb2_context *ctx, enum vb2_screen root_screen_id,
+ vb2_error_t (*global_action)(struct vb2_ui_context *ui));
+
+vb2_error_t try_recovery_action(struct vb2_ui_context *ui);
+
+#endif /* VBOOT_REFERENCE_2UI_PRIVATE_H_ */