diff options
-rw-r--r-- | Makefile | 10 | ||||
-rw-r--r-- | firmware/2lib/2ui.c | 414 | ||||
-rw-r--r-- | firmware/2lib/2ui_screens.c | 523 | ||||
-rw-r--r-- | firmware/2lib/include/2ui.h | 231 | ||||
-rw-r--r-- | firmware/2lib/include/2ui_private.h | 43 | ||||
-rw-r--r-- | tests/vb2_ui_action_tests.c | 936 | ||||
-rw-r--r-- | tests/vb2_ui_utility_tests.c | 376 |
7 files changed, 0 insertions, 2533 deletions
@@ -415,12 +415,6 @@ FWLIB_SRCS = \ firmware/lib20/api_kernel.c \ firmware/lib20/kernel.c -# Only add these to firmware and test builds, -# as regular host builds don't need them -$(if ${FIRMWARE_ARCH},FWLIB_SRCS,TESTLIB_SRCS) += \ - firmware/2lib/2ui.c \ - firmware/2lib/2ui_screens.c \ - # TPM lightweight command library ifeq (${TPM2_MODE},) TLCL_SRCS = \ @@ -755,8 +749,6 @@ TEST2X_NAMES = \ tests/vb2_secdata_kernel_tests \ tests/vb2_sha_api_tests \ tests/vb2_sha_tests \ - tests/vb2_ui_action_tests \ - tests/vb2_ui_utility_tests \ tests/hmac_test TEST20_NAMES = \ @@ -1297,8 +1289,6 @@ run2tests: install_for_test ${RUNTEST} ${BUILD_RUN}/tests/vb2_secdata_kernel_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_sha_api_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_sha_tests - ${RUNTEST} ${BUILD_RUN}/tests/vb2_ui_action_tests - ${RUNTEST} ${BUILD_RUN}/tests/vb2_ui_utility_tests ${RUNTEST} ${BUILD_RUN}/tests/vb20_api_kernel_tests ${RUNTEST} ${BUILD_RUN}/tests/vb20_kernel_tests ${RUNTEST} ${BUILD_RUN}/tests/vb21_host_common_tests diff --git a/firmware/2lib/2ui.c b/firmware/2lib/2ui.c deleted file mode 100644 index 64680220..00000000 --- a/firmware/2lib/2ui.c +++ /dev/null @@ -1,414 +0,0 @@ -/* 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. - * - * User interfaces for developer and recovery mode menus. - */ - -#include "2api.h" -#include "2common.h" -#include "2misc.h" -#include "2nvstorage.h" -#include "2return_codes.h" -#include "2ui.h" -#include "2ui_private.h" -#include "vboot_api.h" /* For VB_SHUTDOWN_REQUEST_POWER_BUTTON */ - -/*****************************************************************************/ -/* Utility functions */ - -/** - * Check GBB flags against VbExIsShutdownRequested() shutdown request, - * and check for VB_BUTTON_POWER_SHORT_PRESS key, to determine if a - * shutdown is required. - * - * @param ui UI context pointer - * @return VB2_REQUEST_SHUTDOWN if shutdown needed, or VB2_SUCCESS - */ -vb2_error_t vb2_check_shutdown_request(struct vb2_ui_context *ui) -{ - 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 (ui->power_button == VB2_POWER_BUTTON_RELEASED) - ui->power_button = VB2_POWER_BUTTON_PRESSED; - } else { - if (ui->power_button == VB2_POWER_BUTTON_PRESSED) - shutdown_request |= VB_SHUTDOWN_REQUEST_POWER_BUTTON; - ui->power_button = VB2_POWER_BUTTON_RELEASED; - } - - if (ui->key == VB_BUTTON_POWER_SHORT_PRESS) - shutdown_request |= VB_SHUTDOWN_REQUEST_POWER_BUTTON; - - /* If desired, ignore shutdown request due to lid closure. */ - if (vb2api_gbb_get_flags(ui->ctx) & 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; - - if (shutdown_request) - return VB2_REQUEST_SHUTDOWN; - - return VB2_SUCCESS; -} - -/*****************************************************************************/ -/* Error action functions */ - -vb2_error_t vb2_error_exit_action(struct vb2_ui_context *ui) -{ - /* - * If an error message is currently shown on the screen, any - * key press clears that error. Unset the key so that it is - * not processed by other action functions. - */ - if (ui->key && ui->error_code) { - ui->error_code = VB2_UI_ERROR_NONE; - ui->key = 0; - } - return VB2_SUCCESS; -} - -/*****************************************************************************/ -/* Menu navigation functions */ - -const struct vb2_menu *vb2_get_menu(struct vb2_ui_context *ui) -{ - const struct vb2_menu *menu; - static const struct vb2_menu empty_menu = { - .num_items = 0, - .items = NULL, - }; - if (ui->state->screen->get_menu) { - menu = ui->state->screen->get_menu(ui); - return menu ? menu : &empty_menu; - } else { - return &ui->state->screen->menu; - } -} - -vb2_error_t vb2_menu_navigation_action(struct vb2_ui_context *ui) -{ - uint32_t key = ui->key; - - /* Map detachable button presses for simplicity. */ - if (DETACHABLE) { - if (key == VB_BUTTON_VOL_UP_SHORT_PRESS) - key = VB_KEY_UP; - else if (key == VB_BUTTON_VOL_DOWN_SHORT_PRESS) - key = VB_KEY_DOWN; - else if (key == VB_BUTTON_POWER_SHORT_PRESS) - key = VB_KEY_ENTER; - } - - switch (key) { - case VB_KEY_UP: - return vb2_ui_menu_prev(ui); - case VB_KEY_DOWN: - return vb2_ui_menu_next(ui); - case VB_KEY_ENTER: - return vb2_ui_menu_select(ui); - case VB_KEY_ESC: - return vb2_ui_screen_back(ui); - default: - if (key != 0) - VB2_DEBUG("Pressed key %#x, trusted? %d\n", - ui->key, ui->key_trusted); - } - - return VB2_SUCCESS; -} - -vb2_error_t vb2_ui_menu_prev(struct vb2_ui_context *ui) -{ - int item; - - if (!DETACHABLE && ui->key == VB_BUTTON_VOL_UP_SHORT_PRESS) - return VB2_SUCCESS; - - item = ui->state->selected_item - 1; - while (item >= 0 && VB2_GET_BIT(ui->state->hidden_item_mask, item)) - item--; - /* Only update if item is valid */ - if (item >= 0) - ui->state->selected_item = item; - - return VB2_SUCCESS; -} - -vb2_error_t vb2_ui_menu_next(struct vb2_ui_context *ui) -{ - int item; - const struct vb2_menu *menu; - - if (!DETACHABLE && ui->key == VB_BUTTON_VOL_DOWN_SHORT_PRESS) - return VB2_SUCCESS; - - menu = vb2_get_menu(ui); - item = ui->state->selected_item + 1; - while (item < menu->num_items && - VB2_GET_BIT(ui->state->hidden_item_mask, item)) - item++; - /* Only update if item is valid */ - if (item < menu->num_items) - ui->state->selected_item = item; - - return VB2_SUCCESS; -} - -vb2_error_t vb2_ui_menu_select(struct vb2_ui_context *ui) -{ - const struct vb2_menu *menu; - const struct vb2_menu_item *menu_item; - - if (!DETACHABLE && ui->key == VB_BUTTON_POWER_SHORT_PRESS) - return VB2_SUCCESS; - - menu = vb2_get_menu(ui); - if (menu->num_items == 0) - return VB2_SUCCESS; - - menu_item = &menu->items[ui->state->selected_item]; - - /* Cannot select a disabled menu item */ - if (VB2_GET_BIT(ui->state->disabled_item_mask, - ui->state->selected_item)) { - VB2_DEBUG("Menu item <%s> disabled; ignoring\n", - menu_item->text); - return VB2_SUCCESS; - } - - if (menu_item->action) { - VB2_DEBUG("Menu item <%s> run action\n", menu_item->text); - return menu_item->action(ui); - } else if (menu_item->target) { - VB2_DEBUG("Menu item <%s> to target screen %#x\n", - menu_item->text, menu_item->target); - return vb2_ui_screen_change(ui, menu_item->target); - } - - VB2_DEBUG("Menu item <%s> no action or target screen\n", - menu_item->text); - return VB2_SUCCESS; -} - -/*****************************************************************************/ -/* Screen navigation functions */ - -vb2_error_t vb2_ui_screen_back(struct vb2_ui_context *ui) -{ - struct vb2_screen_state *tmp; - - if (ui->state && ui->state->prev) { - tmp = ui->state->prev; - free(ui->state); - ui->state = tmp; - if (ui->state->screen->reinit) - VB2_TRY(ui->state->screen->reinit(ui)); - } else { - VB2_DEBUG("ERROR: No previous screen; ignoring\n"); - } - - return VB2_REQUEST_UI_CONTINUE; -} - -static vb2_error_t default_screen_init(struct vb2_ui_context *ui) -{ - const struct vb2_menu *menu = vb2_get_menu(ui); - ui->state->selected_item = 0; - if (menu->num_items > 1 && menu->items[0].is_language_select) - ui->state->selected_item = 1; - return VB2_SUCCESS; -} - -vb2_error_t vb2_ui_screen_change(struct vb2_ui_context *ui, enum vb2_screen id) -{ - const struct vb2_screen_info *new_screen_info; - struct vb2_screen_state *cur_state; - int state_exists = 0; - - new_screen_info = vb2_get_screen_info(id); - if (new_screen_info == NULL) { - VB2_DEBUG("ERROR: Screen entry %#x not found; ignoring\n", id); - return VB2_REQUEST_UI_CONTINUE; - } - - /* Check to see if the screen state already exists in our stack. */ - cur_state = ui->state; - while (cur_state != NULL) { - if (cur_state->screen->id == id) { - state_exists = 1; - break; - } - cur_state = cur_state->prev; - } - - if (state_exists) { - /* Pop until the requested screen is at the top of stack. */ - while (ui->state->screen->id != id) { - cur_state = ui->state; - ui->state = cur_state->prev; - free(cur_state); - } - if (ui->state->screen->reinit) - VB2_TRY(ui->state->screen->reinit(ui)); - } else { - /* Allocate the requested screen on top of the stack. */ - cur_state = malloc(sizeof(*ui->state)); - if (cur_state == NULL) { - VB2_DEBUG("WARNING: malloc failed; ignoring\n"); - return VB2_REQUEST_UI_CONTINUE; - } - memset(cur_state, 0, sizeof(*ui->state)); - cur_state->prev = ui->state; - cur_state->screen = new_screen_info; - ui->state = cur_state; - if (ui->state->screen->init) - VB2_TRY(ui->state->screen->init(ui)); - else - VB2_TRY(default_screen_init(ui)); - } - - return VB2_REQUEST_UI_CONTINUE; -} - -/*****************************************************************************/ -/* Core UI loop */ - -static vb2_error_t ui_loop_impl( - 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; - int prev_disable_timer; - enum vb2_ui_error prev_error_code; - const struct vb2_menu *menu; - const struct vb2_screen_info *root_info; - uint32_t key_flags; - uint32_t start_time_ms, elapsed_ms; - vb2_error_t rv; - - memset(&ui, 0, sizeof(ui)); - ui.ctx = ctx; - root_info = vb2_get_screen_info(root_screen_id); - if (root_info == NULL) - VB2_DIE("Root screen not found.\n"); - ui.locale_id = vb2api_get_locale_id(ctx); - - rv = vb2_ui_screen_change(&ui, root_screen_id); - if (rv && rv != VB2_REQUEST_UI_CONTINUE) - return rv; - - memset(&prev_state, 0, sizeof(prev_state)); - prev_disable_timer = 0; - prev_error_code = VB2_UI_ERROR_NONE; - - while (1) { - start_time_ms = vb2ex_mtime(); - - /* Draw if there are state changes. */ - if (memcmp(&prev_state, ui.state, sizeof(*ui.state)) || - /* Redraw when timer is disabled. */ - prev_disable_timer != ui.disable_timer || - /* Redraw/beep on a transition. */ - prev_error_code != ui.error_code || - /* Beep. */ - ui.error_beep != 0 || - /* Redraw on a screen request to refresh. */ - ui.force_display) { - - menu = vb2_get_menu(&ui); - VB2_DEBUG("<%s> menu item <%s>\n", - ui.state->screen->name, - menu->num_items ? - menu->items[ui.state->selected_item].text : - "null"); - vb2ex_display_ui(ui.state->screen->id, ui.locale_id, - ui.state->selected_item, - ui.state->disabled_item_mask, - ui.state->hidden_item_mask, - ui.disable_timer, - ui.state->current_page, - ui.error_code); - if (ui.error_beep || - (ui.error_code && - prev_error_code != ui.error_code)) { - vb2ex_beep(250, 400); - ui.error_beep = 0; - } - - /* Reset refresh flag. */ - ui.force_display = 0; - - /* Update prev variables. */ - memcpy(&prev_state, ui.state, sizeof(*ui.state)); - prev_disable_timer = ui.disable_timer; - prev_error_code = ui.error_code; - } - - /* Grab new keyboard input. */ - ui.key = VbExKeyboardReadWithFlags(&key_flags); - ui.key_trusted = !!(key_flags & VB_KEY_FLAG_TRUSTED_KEYBOARD); - - /* Check for shutdown request. */ - rv = vb2_check_shutdown_request(&ui); - if (rv && rv != VB2_REQUEST_UI_CONTINUE) { - VB2_DEBUG("Shutdown requested!\n"); - return rv; - } - - /* Check if we need to exit an error box. */ - rv = vb2_error_exit_action(&ui); - if (rv && rv != VB2_REQUEST_UI_CONTINUE) - return rv; - - /* Run screen action. */ - if (ui.state->screen->action) { - rv = ui.state->screen->action(&ui); - if (rv && rv != VB2_REQUEST_UI_CONTINUE) - return rv; - } - - /* Run menu navigation action. */ - rv = vb2_menu_navigation_action(&ui); - if (rv && rv != VB2_REQUEST_UI_CONTINUE) - return rv; - - /* Run global action function if available. */ - if (global_action) { - rv = global_action(&ui); - if (rv && rv != VB2_REQUEST_UI_CONTINUE) - return rv; - } - - /* Delay. */ - elapsed_ms = vb2ex_mtime() - start_time_ms; - if (elapsed_ms < KEY_DELAY_MS) - vb2ex_msleep(KEY_DELAY_MS - elapsed_ms); - } - - return VB2_SUCCESS; -} - -vb2_error_t vb2_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 rv = ui_loop_impl(ctx, root_screen_id, global_action); - if (rv == VB2_REQUEST_UI_EXIT) - return VB2_SUCCESS; - return rv; -} diff --git a/firmware/2lib/2ui_screens.c b/firmware/2lib/2ui_screens.c deleted file mode 100644 index 3a8e0b60..00000000 --- a/firmware/2lib/2ui_screens.c +++ /dev/null @@ -1,523 +0,0 @@ -/* 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" - -#define MENU_ITEMS(a) ((struct vb2_menu){ \ - .num_items = ARRAY_SIZE(a), \ - .items = a, \ -}) - -#define LANGUAGE_SELECT_ITEM ((struct vb2_menu_item){ \ - .text = "Language selection", \ - .target = VB2_SCREEN_LANGUAGE_SELECT, \ - .is_language_select = 1, \ -}) - -#define NEXT_ITEM(target_screen) ((struct vb2_menu_item){ \ - .text = "Next", \ - .target = (target_screen), \ -}) - -#define BACK_ITEM ((struct vb2_menu_item){ \ - .text = "Back", \ - .action = vb2_ui_screen_back, \ -}) - -#define ADVANCED_OPTIONS_ITEM ((struct vb2_menu_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 ((struct vb2_menu_item){ \ - .text = "Power off", \ - .action = power_off_action, \ -}) - -/******************************************************************************/ -/* - * Functions for ui error handling - */ - -static vb2_error_t set_ui_error(struct vb2_ui_context *ui, - enum vb2_ui_error error_code) -{ - /* Keep the first occurring error. */ - if (ui->error_code) - VB2_DEBUG("When handling ui error %#x, another ui error " - "occurred: %#x", - ui->error_code, error_code); - else - ui->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 vb2_ui_context *ui, - enum vb2_ui_error error_code) -{ - set_ui_error(ui, error_code); - return vb2_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 vb2_ui_context *ui, - const char *new_log_string) -{ - const struct vb2_screen_info *screen = ui->state->screen; - - if (new_log_string) { - ui->state->page_count = vb2ex_prepare_log_screen( - screen->id, ui->locale_id, new_log_string); - if (ui->state->page_count == 0) { - VB2_DEBUG("vb2ex_prepare_log_screen failed\n"); - return VB2_ERROR_UI_LOG_INIT; - } - if (ui->state->current_page >= ui->state->page_count) - ui->state->current_page = ui->state->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 == ui->state->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 vb2_ui_context *ui) -{ - const struct vb2_screen_info *screen = ui->state->screen; - - ui->state->current_page = 0; - ui->state->selected_item = ui->state->page_count > 1 - ? screen->page_down_item - : screen->back_item; - return log_page_update(ui, NULL); -} - -static vb2_error_t log_page_prev_action(struct vb2_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 vb2_ui_context *ui) -{ - /* Validity check. */ - if (ui->state->current_page == ui->state->page_count - 1) - return VB2_SUCCESS; - - ui->state->current_page++; - return log_page_update(ui, NULL); -} - -#define PAGE_UP_ITEM ((struct vb2_menu_item){ \ - .text = "Page up", \ - .action = log_page_prev_action, \ -}) - -#define PAGE_DOWN_ITEM ((struct vb2_menu_item){ \ - .text = "Page down", \ - .action = log_page_next_action, \ -}) - -/******************************************************************************/ -/* 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. */ - vb2api_set_locale_id(ui->ctx, 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_screen_back(ui); -} - -const struct vb2_menu *vb2_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 = vb2_get_menu(ui); - if (menu->num_items == 0) { - VB2_DEBUG("ERROR: No menu items found; " - "rejecting entering language selection screen\n"); - return vb2_ui_screen_back(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_SUCCESS; -} - -static const struct vb2_screen_info language_select_screen = { - .id = VB2_SCREEN_LANGUAGE_SELECT, - .name = "Language selection screen", - .init = language_select_init, - .get_menu = vb2_get_language_menu, -}; - -/******************************************************************************/ -/* VB2_SCREEN_ADVANCED_OPTIONS */ - -#define ADVANCED_OPTIONS_ITEM_DEVELOPER_MODE 1 -#define ADVANCED_OPTIONS_ITEM_DEBUG_INFO 2 - -vb2_error_t vb2_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 || - !vb2api_allow_recovery(ui->ctx)) { - VB2_SET_BIT(ui->state->hidden_item_mask, - ADVANCED_OPTIONS_ITEM_DEVELOPER_MODE); - ui->state->selected_item = ADVANCED_OPTIONS_ITEM_DEBUG_INFO; - } - - return VB2_SUCCESS; -} - -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_DEBUG_INFO] = { - .text = "Debug info", - .target = VB2_SCREEN_DEBUG_INFO, - }, - { - .text = "Firmware log", - .target = VB2_SCREEN_FIRMWARE_LOG, - }, - BACK_ITEM, - POWER_OFF_ITEM, -}; - -static const struct vb2_screen_info advanced_options_screen = { - .id = VB2_SCREEN_ADVANCED_OPTIONS, - .name = "Advanced options", - .init = vb2_advanced_options_init, - .menu = MENU_ITEMS(advanced_options_items), -}; - -/******************************************************************************/ -/* VB2_SCREEN_DEBUG_INFO */ - -#define DEBUG_INFO_ITEM_PAGE_UP 1 -#define DEBUG_INFO_ITEM_PAGE_DOWN 2 -#define DEBUG_INFO_ITEM_BACK 3 - -static vb2_error_t debug_info_set_content(struct vb2_ui_context *ui) -{ - const char *log_string = vb2ex_get_debug_info(ui->ctx); - if (!log_string) - return set_ui_error_and_go_back(ui, VB2_UI_ERROR_DEBUG_LOG); - if (vb2_is_error(log_page_update(ui, log_string))) - return set_ui_error_and_go_back(ui, VB2_UI_ERROR_DEBUG_LOG); - return VB2_SUCCESS; -} - -static vb2_error_t debug_info_init(struct vb2_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, VB2_UI_ERROR_DEBUG_LOG); - return VB2_SUCCESS; -} - -static vb2_error_t debug_info_reinit(struct vb2_ui_context *ui) -{ - return debug_info_set_content(ui); -} - -static const struct vb2_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 vb2_screen_info debug_info_screen = { - .id = VB2_SCREEN_DEBUG_INFO, - .name = "Debug info", - .init = debug_info_init, - .reinit = debug_info_reinit, - .menu = MENU_ITEMS(debug_info_items), - .page_up_item = DEBUG_INFO_ITEM_PAGE_UP, - .page_down_item = DEBUG_INFO_ITEM_PAGE_DOWN, - .back_item = DEBUG_INFO_ITEM_BACK, -}; - -/******************************************************************************/ -/* VB2_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 vb2_ui_context *ui, - int reset) -{ - const char *log_string = vb2ex_get_firmware_log(reset); - if (!log_string) - return set_ui_error_and_go_back(ui, VB2_UI_ERROR_FIRMWARE_LOG); - if (vb2_is_error(log_page_update(ui, log_string))) - return set_ui_error_and_go_back(ui, VB2_UI_ERROR_FIRMWARE_LOG); - return VB2_SUCCESS; -} - -static vb2_error_t firmware_log_init(struct vb2_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, VB2_UI_ERROR_FIRMWARE_LOG); - return VB2_SUCCESS; -} - -static vb2_error_t firmware_log_reinit(struct vb2_ui_context *ui) -{ - return firmware_log_set_content(ui, 0); -} - -static const struct vb2_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 vb2_screen_info firmware_log_screen = { - .id = VB2_SCREEN_FIRMWARE_LOG, - .name = "Firmware log", - .init = firmware_log_init, - .reinit = firmware_log_reinit, - .menu = MENU_ITEMS(firmware_log_items), - .page_up_item = FIRMWARE_LOG_ITEM_PAGE_UP, - .page_down_item = FIRMWARE_LOG_ITEM_PAGE_DOWN, - .back_item = FIRMWARE_LOG_ITEM_BACK, -}; - -/******************************************************************************/ -/* 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) - /* We're in dev mode, so let user know they can't transition */ - return set_ui_error_and_go_back( - ui, VB2_UI_ERROR_DEV_MODE_ALREADY_ENABLED); - - if (!PHYSICAL_PRESENCE_KEYBOARD && vb2ex_physical_presence_pressed()) { - VB2_DEBUG("Presence button stuck?\n"); - return vb2_ui_screen_back(ui); - } - - ui->state->selected_item = RECOVERY_TO_DEV_ITEM_CONFIRM; - - /* Disable "Confirm" button for other physical presence types. */ - if (!PHYSICAL_PRESENCE_KEYBOARD) { - 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 vb2_ui_context *ui) -{ - VB2_DEBUG("Physical presence confirmed!\n"); - - /* Validity 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) || - !vb2api_allow_recovery(ui->ctx)) { - VB2_DEBUG("ERROR: Dev transition validity check failed\n"); - return VB2_SUCCESS; - } - - VB2_DEBUG("Enabling dev mode and rebooting...\n"); - - if (vb2api_enable_developer_mode(ui->ctx) != VB2_SUCCESS) { - VB2_DEBUG("Enable developer mode failed\n"); - return VB2_SUCCESS; - } - - 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"); - /* - * If physical presence is confirmed using the keyboard, - * beep and notify the user when the ENTER key comes - * from an untrusted keyboard. - */ - if (PHYSICAL_PRESENCE_KEYBOARD && ui->key == VB_KEY_ENTER) - return set_ui_error( - ui, VB2_UI_ERROR_UNTRUSTED_CONFIRMATION); - return VB2_SUCCESS; - } - 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_screen_back(ui); - } - - /* Keyboard physical presence case covered by "Confirm" action. */ - if (PHYSICAL_PRESENCE_KEYBOARD) - return VB2_SUCCESS; - - pressed = vb2ex_physical_presence_pressed(); - if (pressed) { - VB2_DEBUG("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; - 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_screen_back, - }, - 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), -}; - - -/******************************************************************************/ -/* - * 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[] = { - &language_select_screen, - &advanced_options_screen, - &debug_info_screen, - &firmware_log_screen, - &recovery_to_dev_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/2ui.h b/firmware/2lib/include/2ui.h deleted file mode 100644 index c01c1f97..00000000 --- a/firmware/2lib/include/2ui.h +++ /dev/null @@ -1,231 +0,0 @@ -/* 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. - * - * User interfaces for developer and recovery mode menus. - */ - -#ifndef VBOOT_REFERENCE_2UI_H_ -#define VBOOT_REFERENCE_2UI_H_ - -#include <2api.h> -#include <2sysincludes.h> - -/*****************************************************************************/ -/* Data structures */ - -struct vb2_ui_context; /* Forward declaration */ - -struct vb2_menu_item { - /* Text description */ - const char *text; - /* Target screen */ - enum vb2_screen target; - /* Action function takes precedence over target screen if non-NULL. */ - vb2_error_t (*action)(struct vb2_ui_context *ui); - /* Whether the item is language selection */ - int is_language_select; -}; - -struct vb2_menu { - /* Number of menu items */ - uint16_t num_items; - /* List of menu items */ - const struct vb2_menu_item *items; -}; - -struct vb2_screen_info { - /* Screen id */ - enum vb2_screen id; - /* Screen name for printing to console only */ - const char *name; - /* - * Init function runs once when changing to the screen which is not in - * the history stack. - */ - vb2_error_t (*init)(struct vb2_ui_context *ui); - /* - * Re-init function runs once when changing to the screen which is - * already in the history stack, for example, when going back to the - * screen. Exactly one of init() and reinit() will be called. - */ - vb2_error_t (*reinit)(struct vb2_ui_context *ui); - /* Action function runs repeatedly while on the screen. */ - vb2_error_t (*action)(struct vb2_ui_context *ui); - /* Menu items. */ - struct vb2_menu menu; - /* - * Custom function for getting menu items. If non-null, field 'menu' - * will be ignored. - */ - const struct vb2_menu *(*get_menu)(struct vb2_ui_context *ui); - /* - * Indices of menu items; - * used by log_page_* functions in 2ui_screens.c. - */ - uint32_t page_up_item; - uint32_t page_down_item; - uint32_t back_item; - uint32_t cancel_item; -}; - -struct vb2_screen_state { - const struct vb2_screen_info *screen; - uint32_t selected_item; - uint32_t disabled_item_mask; - uint32_t hidden_item_mask; - - /* For log screen. */ - uint32_t page_count; - uint32_t current_page; - - /* For minidiag test screens. */ - int test_finished; /* Do not update screen if the content is done */ - - struct vb2_screen_state *prev; -}; - -enum vb2_power_button { - VB2_POWER_BUTTON_HELD_SINCE_BOOT = 0, - VB2_POWER_BUTTON_RELEASED, - VB2_POWER_BUTTON_PRESSED, /* Must have been previously released */ -}; - -struct vb2_ui_context { - struct vb2_context *ctx; - struct vb2_screen_state *state; - uint32_t locale_id; - uint32_t key; - int key_trusted; - - /* For vb2_check_shutdown_request. */ - enum vb2_power_button power_button; - - /* For developer mode. */ - int disable_timer; - uint32_t start_time_ms; - int beep_count; - - /* For manual recovery. */ - vb2_error_t recovery_rv; - - /* For to_dev transition flow. */ - int physical_presence_button_pressed; - - /* For language selection screen. */ - struct vb2_menu language_menu; - - /* For bootloader selection screen. */ - struct vb2_menu bootloader_menu; - - /* For error beep sound. */ - int error_beep; - - /* For displaying error messages. */ - enum vb2_ui_error error_code; - - /* Force calling vb2ex_display_ui for refreshing the screen. This flag - will be reset after done. */ - int force_display; -}; - -vb2_error_t vb2_ui_developer_mode_boot_internal_action( - struct vb2_ui_context *ui); -vb2_error_t vb2_ui_developer_mode_boot_external_action( - struct vb2_ui_context *ui); -vb2_error_t vb2_ui_developer_mode_boot_altfw_action( - struct vb2_ui_context *ui); - -/** - * Get info struct of a screen. - * - * @param id 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); - -/*****************************************************************************/ -/* Menu navigation functions */ - -/** - * Move selection to the previous menu item. - * - * Update selected_item, taking into account hidden indices (from - * hidden_item_mask). The selection does not wrap, meaning that we block - * on 0 when we hit the start of the menu. - * - * @param ui UI context pointer - * @return VB2_SUCCESS, or error code on error. - */ -vb2_error_t vb2_ui_menu_prev(struct vb2_ui_context *ui); - -/** - * Move selection to the next menu item. - * - * Update selected_item, taking into account hidden indices (from - * hidden_item_mask). The selection does not wrap, meaning that we block - * on the max index when we hit the end of the menu. - * - * @param ui UI context pointer - * @return VB2_SUCCESS, or error code on error. - */ -vb2_error_t vb2_ui_menu_next(struct vb2_ui_context *ui); - -/** - * Select the current menu item. - * - * The caller should take care of returning after this function, and should not - * continue executing under the assumption that the screen has *not* changed. - * - * If the current menu item has an action associated with it, run the action. - * Otherwise, navigate to the target screen. If neither of these are set, then - * selecting the menu item is a no-op. - * - * @param ui UI context pointer - * @return VB2_SUCCESS, or error code on error. - */ -vb2_error_t vb2_ui_menu_select(struct vb2_ui_context *ui); - -/*****************************************************************************/ -/* Screen navigation functions */ -/** - * After these functions are called, no assumptions may be made about which - * screen is currently displayed, and thus execution should return to ui_loop. - * VB2_REQUEST_UI_CONTINUE is returned rather than VB2_SUCCESS, so VB2_TRY can - * be used to wrapped to these functions and the callers of these functions. - */ -/** - * Return back to the previous screen. - * - * The caller should take care of returning after this function, and should not - * continue executing under the assumption that the screen has *not* changed. - * - * If the current screen is already the root screen, the request is ignored. - * - * TODO(b/157625765): Consider falling into recovery mode (BROKEN screen) when - * the current screen is already the root screen. - * - * @param ui UI context pointer - * @return VB2_REQUEST_UI_CONTINUE, or error code on error. - */ -vb2_error_t vb2_ui_screen_back(struct vb2_ui_context *ui); - -/** - * Change to the given screen. - * - * The caller should take care of returning after this function, and should not - * continue executing under the assumption that the screen has *not* changed. - * - * If the screen is not found, the request is ignored. - * - * TODO(b/157625765): Consider falling into recovery mode (BROKEN screen) when - * the target screen is not found. - * - * @param ui UI context pointer - * @param id Screen from enum vb2_screen - * @return VB2_REQUEST_UI_CONTINUE, or error code on error. - */ -vb2_error_t vb2_ui_screen_change(struct vb2_ui_context *ui, enum vb2_screen id); - -#endif /* VBOOT_REFERENCE_2UI_H_ */ diff --git a/firmware/2lib/include/2ui_private.h b/firmware/2lib/include/2ui_private.h deleted file mode 100644 index 9f08c3eb..00000000 --- a/firmware/2lib/include/2ui_private.h +++ /dev/null @@ -1,43 +0,0 @@ -/* 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" -#include "2common.h" - -#ifndef VBOOT_REFERENCE_2UI_PRIVATE_H_ -#define VBOOT_REFERENCE_2UI_PRIVATE_H_ - -/* Time-related constants */ -#define KEY_DELAY_MS 20 /* Delay between key scans in UI loops */ -#define DEV_DELAY_SHORT_MS (2 * VB2_MSEC_PER_SEC) /* 2 seconds */ -#define DEV_DELAY_NORMAL_MS (30 * VB2_MSEC_PER_SEC) /* 30 seconds */ -#define DEV_DELAY_BEEP1_MS (20 * VB2_MSEC_PER_SEC) /* 20 seconds */ -#define DEV_DELAY_BEEP2_MS (20 * VB2_MSEC_PER_SEC + 500) /* 20.5 seconds */ - -/* From 2ui.c */ -vb2_error_t vb2_check_shutdown_request(struct vb2_ui_context *ui); -const struct vb2_menu *vb2_get_menu(struct vb2_ui_context *ui); -vb2_error_t vb2_error_exit_action(struct vb2_ui_context *ui); -vb2_error_t vb2_menu_navigation_action(struct vb2_ui_context *ui); -vb2_error_t -vb2_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 developer_action(struct vb2_ui_context *ui); -vb2_error_t manual_recovery_action(struct vb2_ui_context *ui); - -/* From 2ui_screens.c */ -const struct vb2_menu *vb2_get_language_menu(struct vb2_ui_context *ui); -vb2_error_t vb2_advanced_options_init(struct vb2_ui_context *ui); -vb2_error_t recovery_select_init(struct vb2_ui_context *ui); -vb2_error_t recovery_to_dev_init(struct vb2_ui_context *ui); -vb2_error_t recovery_to_dev_confirm_action(struct vb2_ui_context *ui); -vb2_error_t recovery_to_dev_action(struct vb2_ui_context *ui); -vb2_error_t developer_mode_init(struct vb2_ui_context *ui); -vb2_error_t developer_mode_action(struct vb2_ui_context *ui); -vb2_error_t developer_to_norm_action(struct vb2_ui_context *ui); - -#endif /* VBOOT_REFERENCE_2UI_PRIVATE_H_ */ diff --git a/tests/vb2_ui_action_tests.c b/tests/vb2_ui_action_tests.c deleted file mode 100644 index ba378f43..00000000 --- a/tests/vb2_ui_action_tests.c +++ /dev/null @@ -1,936 +0,0 @@ -/* 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. - * - * Tests for UI related actions. - */ - -#include "2api.h" -#include "2common.h" -#include "2misc.h" -#include "2nvstorage.h" -#include "2ui.h" -#include "2ui_private.h" -#include "test_common.h" -#include "vboot_api.h" - -/* Fixed value for ignoring some checks. */ -#define MOCK_IGNORE 0xffffu - -/* Mock screen index for testing screen utility functions. */ -#define MOCK_NO_SCREEN 0xef00 -#define MOCK_SCREEN_BLANK 0xef10 -#define MOCK_SCREEN_BASE 0xef11 -#define MOCK_SCREEN_MENU 0xef12 -#define MOCK_SCREEN_TARGET0 0xef20 -#define MOCK_SCREEN_TARGET1 0xef21 -#define MOCK_SCREEN_TARGET2 0xef22 -#define MOCK_SCREEN_ACTION 0xef30 -#define MOCK_SCREEN_ALL_ACTION 0xef32 - -/* Mock data */ -/* TODO(b/156448738): Add tests for timer_disabled and error_code */ -struct display_call { - const struct vb2_screen_info *screen; - uint32_t locale_id; - uint32_t selected_item; - uint32_t disabled_item_mask; - uint32_t hidden_item_mask; - int timer_disabled; - uint32_t current_page; - enum vb2_ui_error error_code; -} __attribute__((packed)); - -static uint8_t workbuf[VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE] - __attribute__((aligned(VB2_WORKBUF_ALIGN))); -static struct vb2_context *ctx; -static struct vb2_shared_data *sd; -static struct vb2_gbb_header gbb; - -static int mock_calls_until_shutdown; - -static struct vb2_ui_context mock_ui_context; - -static struct display_call mock_displayed[64]; -static int mock_displayed_count; -static int mock_displayed_i; - -static uint32_t mock_key[64]; -static int mock_key_trusted[64]; -static int mock_key_count; -static int mock_key_total; - -static int mock_get_screen_info_called; - -static vb2_error_t mock_vbtlk_retval; -static uint32_t mock_vbtlk_expected_flag; - -static int mock_run_altfw_called; -static uint32_t mock_altfw_last; -static uint32_t mock_altfw_count; - -static uint32_t mock_time_ms; -static const uint32_t mock_time_start_ms = 31ULL * VB2_MSEC_PER_SEC; - -/* Mock actions */ -static uint32_t mock_action_called; -static uint32_t mock_action_countdown_limit; -static vb2_error_t mock_action_countdown(struct vb2_ui_context *ui) -{ - if (++mock_action_called >= mock_action_countdown_limit) - return VB2_REQUEST_UI_EXIT; - return VB2_SUCCESS; -} - -static vb2_error_t mock_action_screen_change(struct vb2_ui_context *ui) -{ - return vb2_ui_screen_change(ui, MOCK_SCREEN_BASE); -} - -static vb2_error_t mock_action_base(struct vb2_ui_context *ui) -{ - mock_action_called++; - return VB2_SUCCESS; -} - -static int mock_action_flags; -static vb2_error_t mock_action_flag0(struct vb2_ui_context *ui) -{ - if ((1 << 0) & mock_action_flags) - return VB2_REQUEST_UI_EXIT; - return VB2_SUCCESS; -} - -static vb2_error_t mock_action_flag1(struct vb2_ui_context *ui) -{ - if ((1 << 1) & mock_action_flags) - return VB2_REQUEST_UI_EXIT; - return VB2_SUCCESS; -} - -static vb2_error_t mock_action_flag2(struct vb2_ui_context *ui) -{ - if ((1 << 2) & mock_action_flags) - return VB2_REQUEST_UI_EXIT; - return VB2_SUCCESS; -} - -static uint32_t mock_action_delay_ms; -static vb2_error_t mock_action_msleep(struct vb2_ui_context *ui) -{ - vb2ex_msleep(mock_action_delay_ms); - return VB2_SUCCESS; -} - -/* Mock screens */ -struct vb2_screen_info mock_screen_temp; -const struct vb2_screen_info mock_screen_blank = { - .id = MOCK_SCREEN_BLANK, - .name = "mock_screen_blank", -}; -const struct vb2_screen_info mock_screen_base = { - .id = MOCK_SCREEN_BASE, - .name = "mock_screen_base: menuless screen", -}; -const struct vb2_menu_item mock_screen_menu_items[] = { - { - .text = "item 0", - .target = MOCK_SCREEN_TARGET0, - }, - { - .text = "item 1", - .target = MOCK_SCREEN_TARGET1, - }, - { - .text = "item 2", - .target = MOCK_SCREEN_TARGET2, - }, - { - .text = "item 3", - .action = mock_action_base, - }, - { - .text = "item 4 (no target)", - }, -}; -const struct vb2_screen_info mock_screen_menu = { - .id = MOCK_SCREEN_MENU, - .name = "mock_screen_menu: screen with 5 items", - .menu = { - .num_items = ARRAY_SIZE(mock_screen_menu_items), - .items = mock_screen_menu_items, - }, -}; -const struct vb2_screen_info mock_screen_target0 = { - .id = MOCK_SCREEN_TARGET0, - .name = "mock_screen_target0", -}; -const struct vb2_screen_info mock_screen_target1 = { - .id = MOCK_SCREEN_TARGET1, - .name = "mock_screen_target1", -}; -const struct vb2_screen_info mock_screen_target2 = { - .id = MOCK_SCREEN_TARGET2, - .name = "mock_screen_target2", -}; -const struct vb2_screen_info mock_screen_action = { - .id = MOCK_SCREEN_ACTION, - .name = "mock_screen_action", - .action = mock_action_countdown, -}; -const struct vb2_menu_item mock_screen_all_action_items[] = { - { - .text = "all_action_screen_item", - .action = mock_action_flag1, - }, -}; -const struct vb2_screen_info mock_screen_all_action = { - .id = MOCK_SCREEN_ALL_ACTION, - .name = "mock_screen_all_action", - .action = mock_action_flag0, - .menu = { - .num_items = ARRAY_SIZE(mock_screen_all_action_items), - .items = mock_screen_all_action_items, - }, -}; - -static void screen_state_eq(const struct vb2_screen_state *state, - enum vb2_screen screen, - uint32_t selected_item, - uint32_t hidden_item_mask) -{ - if (screen != MOCK_IGNORE) { - if (state->screen == NULL) - TEST_TRUE(0, " state.screen does not exist"); - else - TEST_EQ(state->screen->id, screen, " state.screen"); - } - if (selected_item != MOCK_IGNORE) - TEST_EQ(state->selected_item, - selected_item, " state.selected_item"); - if (hidden_item_mask != MOCK_IGNORE) - TEST_EQ(state->hidden_item_mask, - hidden_item_mask, " state.hidden_item_mask"); -} - -static void add_mock_key(uint32_t press, int trusted) -{ - if (mock_key_total >= ARRAY_SIZE(mock_key) || - mock_key_total >= ARRAY_SIZE(mock_key_trusted)) { - TEST_TRUE(0, " mock_key ran out of entries!"); - return; - } - - mock_key[mock_key_total] = press; - mock_key_trusted[mock_key_total] = trusted; - mock_key_total++; -} - -static void add_mock_keypress(uint32_t press) -{ - add_mock_key(press, 0); -} - -static void displayed_eq(const char *text, - enum vb2_screen screen, - uint32_t locale_id, - uint32_t selected_item, - uint32_t hidden_item_mask, - int line) -{ - char text_info[32], text_buf[128]; - - sprintf(text_info, "(line #%d, displayed #%d)", line, mock_displayed_i); - - if (mock_displayed_i >= mock_displayed_count) { - sprintf(text_buf, " %s missing screen %s", - text_info, text); - TEST_TRUE(0, text_buf); - return; - } - - if (screen != MOCK_IGNORE) { - sprintf(text_buf, " %s screen of %s", text_info, text); - TEST_EQ(mock_displayed[mock_displayed_i].screen->id, screen, - text_buf); - } - if (locale_id != MOCK_IGNORE) { - sprintf(text_buf, " %s locale_id of %s", text_info, text); - TEST_EQ(mock_displayed[mock_displayed_i].locale_id, locale_id, - text_buf); - } - if (selected_item != MOCK_IGNORE) { - sprintf(text_buf, " %s selected_item of %s", - text_info, text); - TEST_EQ(mock_displayed[mock_displayed_i].selected_item, - selected_item, text_buf); - } - if (hidden_item_mask != MOCK_IGNORE) { - sprintf(text_buf, " %s hidden_item_mask of %s", - text_info, text); - TEST_EQ(mock_displayed[mock_displayed_i].hidden_item_mask, - hidden_item_mask, text_buf); - } - mock_displayed_i++; -} - -static void displayed_no_extra(int line) -{ - char text_info[32], text_buf[128]; - - sprintf(text_info, "(line #%d)", line); - - if (mock_displayed_i == 0) - sprintf(text_buf, " %s no screen", text_info); - else - sprintf(text_buf, " %s no extra screens", text_info); - TEST_EQ(mock_displayed_count, mock_displayed_i, text_buf); -} - -#define DISPLAYED_EQ(...) displayed_eq(__VA_ARGS__, __LINE__) - -#define DISPLAYED_PASS() \ - displayed_eq("", MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, \ - __LINE__) - -#define DISPLAYED_NO_EXTRA() displayed_no_extra(__LINE__) - -/* Reset mock data (for use before each test) */ -static void reset_common_data(void) -{ - TEST_SUCC(vb2api_init(workbuf, sizeof(workbuf), &ctx), - "vb2api_init failed"); - - memset(&gbb, 0, sizeof(gbb)); - - vb2_nv_init(ctx); - - sd = vb2_get_sd(ctx); - - ctx->flags |= VB2_CONTEXT_DEV_BOOT_ALLOWED; - - /* For check_shutdown_request */ - mock_calls_until_shutdown = 10; - - /* Reset mock_screen_temp for test by test temporary screen_info */ - mock_screen_temp = (struct vb2_screen_info){ - .id = MOCK_NO_SCREEN, - .name = "mock_screen_temp", - }; - - /* Mock ui_context based on mock screens */ - memset(&mock_ui_context, 0, sizeof(mock_ui_context)); - mock_ui_context.ctx = ctx; - - if (!mock_ui_context.state) - mock_ui_context.state = malloc(sizeof(*mock_ui_context.state)); - memset(mock_ui_context.state, 0, sizeof(*mock_ui_context.state)); - mock_ui_context.state->screen = &mock_screen_temp; - - /* For vb2ex_display_ui */ - memset(mock_displayed, 0, sizeof(mock_displayed)); - mock_displayed_count = 0; - mock_displayed_i = 0; - - /* For VbExKeyboardRead */ - memset(mock_key, 0, sizeof(mock_key)); - memset(mock_key_trusted, 0, sizeof(mock_key_trusted)); - mock_key_count = 0; - mock_key_total = 0; - - /* For mock actions */ - mock_action_called = 0; - mock_action_countdown_limit = 1; - mock_action_flags = 0; - mock_action_delay_ms = 0; - - /* For chagen_screen and vb2_get_screen_info */ - mock_get_screen_info_called = 0; - - /* For VbTryLoadKernel */ - mock_vbtlk_retval = VB2_ERROR_MOCK; - mock_vbtlk_expected_flag = MOCK_IGNORE; - - /* For vb2ex_run_altfw */ - mock_run_altfw_called = 0; - mock_altfw_last = -100; - mock_altfw_count = 2; - - /* For vb2ex_mtime and vb2ex_msleep */ - mock_time_ms = mock_time_start_ms; -} - -/* Mock functions */ -struct vb2_gbb_header *vb2_get_gbb(struct vb2_context *c) -{ - return &gbb; -} - -uint32_t VbExIsShutdownRequested(void) -{ - if (mock_calls_until_shutdown < 0) /* Never request shutdown */ - return 0; - if (mock_calls_until_shutdown == 0) - return 1; - mock_calls_until_shutdown--; - - return 0; -} - -const struct vb2_screen_info *vb2_get_screen_info(enum vb2_screen screen) -{ - mock_get_screen_info_called++; - - switch ((int)screen) { - case MOCK_SCREEN_BLANK: - return &mock_screen_blank; - case MOCK_SCREEN_BASE: - return &mock_screen_base; - case MOCK_SCREEN_MENU: - return &mock_screen_menu; - case MOCK_SCREEN_TARGET0: - return &mock_screen_target0; - case MOCK_SCREEN_TARGET1: - return &mock_screen_target1; - case MOCK_SCREEN_TARGET2: - return &mock_screen_target2; - case MOCK_SCREEN_ACTION: - return &mock_screen_action; - case MOCK_SCREEN_ALL_ACTION: - return &mock_screen_all_action; - case MOCK_NO_SCREEN: - return NULL; - default: - mock_screen_temp.id = screen; - return &mock_screen_temp; - } -} - -vb2_error_t vb2ex_display_ui(enum vb2_screen screen, - uint32_t locale_id, - uint32_t selected_item, - uint32_t disabled_item_mask, - uint32_t hidden_item_mask, - int timer_disabled, - uint32_t current_page, - enum vb2_ui_error error_code) -{ - struct display_call displayed = (struct display_call){ - .screen = vb2_get_screen_info(screen), - .locale_id = locale_id, - .selected_item = selected_item, - .disabled_item_mask = disabled_item_mask, - .hidden_item_mask = hidden_item_mask, - .timer_disabled = timer_disabled, - .current_page = current_page, - .error_code = error_code, - }; - - /* Ignore repeated calls with same arguments */ - if (mock_displayed_count > 0 && - !memcmp(&mock_displayed[mock_displayed_count - 1], &displayed, - sizeof(struct display_call))) - return VB2_SUCCESS; - - VB2_DEBUG("displayed %d: screen=%#x, locale_id=%u, selected_item=%u, " - "disabled_item_mask=%#x, hidden_item_mask=%#x, " - "timer_disabled=%d, current_page=%u, error=%#x\n", - mock_displayed_count, screen, locale_id, selected_item, - disabled_item_mask, hidden_item_mask, - timer_disabled, current_page, error_code); - - if (mock_displayed_count >= ARRAY_SIZE(mock_displayed)) { - TEST_TRUE(0, " mock vb2ex_display_ui ran out of entries!"); - return VB2_ERROR_MOCK; - } - - mock_displayed[mock_displayed_count++] = displayed; - - return VB2_SUCCESS; -} - -uint32_t VbExKeyboardRead(void) -{ - return VbExKeyboardReadWithFlags(NULL); -} - -uint32_t VbExKeyboardReadWithFlags(uint32_t *key_flags) -{ - if (mock_key_count < mock_key_total) { - if (key_flags != NULL) { - if (mock_key_trusted[mock_key_count]) - *key_flags = VB_KEY_FLAG_TRUSTED_KEYBOARD; - else - *key_flags = 0; - } - return mock_key[mock_key_count++]; - } - - return 0; -} - -vb2_error_t VbTryLoadKernel(struct vb2_context *c, uint32_t disk_flags) -{ - TEST_EQ(mock_vbtlk_expected_flag, disk_flags, - " unexpected disk_flags"); - return mock_vbtlk_retval; -} - -vb2_error_t vb2ex_run_altfw(uint32_t altfw_id) -{ - mock_run_altfw_called++; - mock_altfw_last = altfw_id; - - if (altfw_id <= mock_altfw_count) - return VB2_SUCCESS; - else - return VB2_ERROR_UNKNOWN; -} - -uint32_t vb2ex_get_altfw_count(void) -{ - return mock_altfw_count; -} - -uint32_t vb2ex_mtime(void) -{ - return mock_time_ms; -} - -void vb2ex_msleep(uint32_t msec) -{ - mock_time_ms += msec; -} - -/* Tests */ -static void menu_prev_tests(void) -{ - VB2_DEBUG("Testing menu_prev...\n"); - - /* Valid action */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 2; - mock_ui_context.key = VB_KEY_UP; - TEST_EQ(vb2_ui_menu_prev(&mock_ui_context), VB2_SUCCESS, - "valid action"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_MENU, 1, - MOCK_IGNORE); - - /* Valid action with hidden mask */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 2; - mock_ui_context.state->hidden_item_mask = 0x0a; /* 0b01010 */ - mock_ui_context.key = VB_KEY_UP; - TEST_EQ(vb2_ui_menu_prev(&mock_ui_context), VB2_SUCCESS, - "valid action with hidden mask"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_MENU, 0, - MOCK_IGNORE); - - /* Disabled mask does not affect menu_prev */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 2; - mock_ui_context.state->disabled_item_mask = 0x0a; /* 0b01010 */ - mock_ui_context.key = VB_KEY_UP; - TEST_EQ(vb2_ui_menu_prev(&mock_ui_context), VB2_SUCCESS, - "valid action with disabled mask"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_MENU, 1, - MOCK_IGNORE); - - /* Invalid action (blocked) */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 0; - mock_ui_context.key = VB_KEY_UP; - TEST_EQ(vb2_ui_menu_prev(&mock_ui_context), VB2_SUCCESS, - "invalid action (blocked)"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_MENU, 0, - MOCK_IGNORE); - - /* Invalid action (blocked by mask) */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 2; - mock_ui_context.state->hidden_item_mask = 0x0b; /* 0b01011 */ - mock_ui_context.key = VB_KEY_UP; - TEST_EQ(vb2_ui_menu_prev(&mock_ui_context), VB2_SUCCESS, - "invalid action (blocked by mask)"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_MENU, 2, - MOCK_IGNORE); - - /* Ignore volume-up when not DETACHABLE */ - if (!DETACHABLE) { - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 2; - mock_ui_context.key = VB_BUTTON_VOL_UP_SHORT_PRESS; - TEST_EQ(vb2_ui_menu_prev(&mock_ui_context), VB2_SUCCESS, - "ignore volume-up when not DETACHABLE"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_MENU, 2, - MOCK_IGNORE); - } - - VB2_DEBUG("...done.\n"); -} - -static void menu_next_tests(void) -{ - VB2_DEBUG("Testing menu_next...\n"); - - /* Valid action */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 2; - mock_ui_context.key = VB_KEY_DOWN; - TEST_EQ(vb2_ui_menu_next(&mock_ui_context), VB2_SUCCESS, - "valid action"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_MENU, 3, - MOCK_IGNORE); - - /* Valid action with hidden mask */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 2; - mock_ui_context.state->hidden_item_mask = 0x0a; /* 0b01010 */ - mock_ui_context.key = VB_KEY_DOWN; - TEST_EQ(vb2_ui_menu_next(&mock_ui_context), VB2_SUCCESS, - "valid action with hidden mask"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_MENU, 4, - MOCK_IGNORE); - - /* Disabled mask does not affect menu_next */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 2; - mock_ui_context.state->disabled_item_mask = 0x0a; /* 0b01010 */ - mock_ui_context.key = VB_KEY_DOWN; - TEST_EQ(vb2_ui_menu_next(&mock_ui_context), VB2_SUCCESS, - "valid action with disabled mask"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_MENU, 3, - MOCK_IGNORE); - - /* Invalid action (blocked) */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 4; - mock_ui_context.key = VB_KEY_DOWN; - TEST_EQ(vb2_ui_menu_next(&mock_ui_context), VB2_SUCCESS, - "invalid action (blocked)"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_MENU, 4, - MOCK_IGNORE); - - /* Invalid action (blocked by mask) */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 2; - mock_ui_context.state->hidden_item_mask = 0x1a; /* 0b11010 */ - mock_ui_context.key = VB_KEY_DOWN; - TEST_EQ(vb2_ui_menu_next(&mock_ui_context), VB2_SUCCESS, - "invalid action (blocked by mask)"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_MENU, 2, - MOCK_IGNORE); - - /* Ignore volume-down when not DETACHABLE */ - if (!DETACHABLE) { - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 2; - mock_ui_context.key = VB_BUTTON_VOL_DOWN_SHORT_PRESS; - TEST_EQ(vb2_ui_menu_next(&mock_ui_context), VB2_SUCCESS, - "ignore volume-down when not DETACHABLE"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_MENU, 2, - MOCK_IGNORE); - } - - VB2_DEBUG("...done.\n"); -} - -static vb2_error_t try_menu_select_helper(void) -{ - VB2_TRY(vb2_ui_menu_select(&mock_ui_context)); - return VB2_ERROR_MOCK; -} - -static void menu_select_tests(void) -{ - VB2_DEBUG("Testing menu_select...\n"); - - /* select action with no item screen */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_base; - mock_ui_context.key = VB_KEY_ENTER; - TEST_EQ(vb2_ui_menu_select(&mock_ui_context), VB2_SUCCESS, - "vb2_ui_menu_select with no item screen"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_BASE, 0, - MOCK_IGNORE); - - /* VB2_TRY around item selection should return right away */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.key = VB_KEY_ENTER; - TEST_NEQ(try_menu_select_helper(), VB2_ERROR_MOCK, - "continued executing after VB2_TRY(menu_select)"); - - /* Try to select an item with a target (item 2) */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 2; - mock_ui_context.key = VB_KEY_ENTER; - TEST_EQ(vb2_ui_menu_select(&mock_ui_context), VB2_REQUEST_UI_CONTINUE, - "select an item with a target"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_TARGET2, 0, - MOCK_IGNORE); - - /* Try to select an item with an action (item 3) */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 3; - mock_ui_context.key = VB_KEY_ENTER; - TEST_EQ(vb2_ui_menu_select(&mock_ui_context), - VB2_SUCCESS, "select an item with an action"); - TEST_EQ(mock_action_called, 1, " action called once"); - - /* Try to select an item with neither targets nor actions (item 4) */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 4; - mock_ui_context.key = VB_KEY_ENTER; - TEST_EQ(vb2_ui_menu_select(&mock_ui_context), VB2_SUCCESS, - "select an item with neither targets nor actions"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_MENU, 4, - MOCK_IGNORE); - - /* Cannot select a disabled item (item 3) */ - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 3; - mock_ui_context.state->disabled_item_mask = 0x08; /* 0b01000 */ - mock_ui_context.key = VB_KEY_ENTER; - TEST_EQ(vb2_ui_menu_select(&mock_ui_context), VB2_SUCCESS, - "cannot select a disabled item"); - TEST_EQ(mock_action_called, 0, " no action called"); - - /* Ignore power button short press when not DETACHABLE */ - if (!DETACHABLE) { - reset_common_data(); - mock_ui_context.state->screen = &mock_screen_menu; - mock_ui_context.state->selected_item = 1; - mock_ui_context.key = VB_BUTTON_POWER_SHORT_PRESS; - TEST_EQ(vb2_ui_menu_select(&mock_ui_context), VB2_SUCCESS, - "ignore power button short press when not DETACHABLE"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_MENU, 1, - MOCK_IGNORE); - } - - VB2_DEBUG("...done.\n"); -} - -static void ui_loop_tests(void) -{ - int i; - const char *action_interfere_test_names[] = { - "hook all actions: screen action return SUCCESS", - "hook all actions: target action hooked return SUCCESS", - "hook all actions: global action return SUCCESS", - }; - - VB2_DEBUG("Testing ui_loop...\n"); - - /* Die if no root screen */ - reset_common_data(); - TEST_ABORT(vb2_ui_loop(ctx, MOCK_NO_SCREEN, NULL), - "die if no root screen"); - DISPLAYED_NO_EXTRA(); - - /* Shutdown if requested */ - reset_common_data(); - TEST_EQ(vb2_ui_loop(ctx, MOCK_SCREEN_BASE, NULL), - VB2_REQUEST_SHUTDOWN, "shutdown if requested"); - TEST_EQ(mock_calls_until_shutdown, 0, " used up shutdown request"); - DISPLAYED_EQ("mock_screen_base", MOCK_SCREEN_BASE, MOCK_IGNORE, - MOCK_IGNORE, MOCK_IGNORE); - DISPLAYED_NO_EXTRA(); - - /* Screen action */ - reset_common_data(); - mock_calls_until_shutdown = -1; - mock_action_countdown_limit = 10; - TEST_EQ(vb2_ui_loop(ctx, MOCK_SCREEN_ACTION, NULL), - VB2_SUCCESS, "screen action"); - TEST_EQ(mock_action_called, 10, " action called"); - - /* Global action */ - reset_common_data(); - mock_calls_until_shutdown = -1; - mock_action_countdown_limit = 10; - TEST_EQ(vb2_ui_loop(ctx, MOCK_SCREEN_BLANK, mock_action_countdown), - VB2_SUCCESS, "global action"); - TEST_EQ(mock_action_called, 10, " action called"); - - /* Global action can change screen */ - reset_common_data(); - TEST_EQ(vb2_ui_loop(ctx, MOCK_SCREEN_BLANK, mock_action_screen_change), - VB2_REQUEST_SHUTDOWN, "global action can change screen"); - DISPLAYED_PASS(); - DISPLAYED_EQ("change to mock_screen_base", MOCK_SCREEN_BASE, - MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE); - DISPLAYED_NO_EXTRA(); - - /* - * Hook all actions, and receive SUCCESS from actions one by one - * Action #0: screen action - * Action #1: item target action - * Action #2: global action - */ - for (i = 0; i <= 2; i++) { - reset_common_data(); - add_mock_keypress(VB_KEY_ENTER); - mock_calls_until_shutdown = -1; - mock_action_flags |= (1 << i); - TEST_EQ(vb2_ui_loop(ctx, MOCK_SCREEN_ALL_ACTION, - mock_action_flag2), - VB2_SUCCESS, action_interfere_test_names[i]); - } - - /* KEY_UP, KEY_DOWN, and KEY_ENTER navigation */ - reset_common_data(); - add_mock_keypress(VB_KEY_UP); /* (blocked) */ - add_mock_keypress(VB_KEY_DOWN); - add_mock_keypress(VB_KEY_DOWN); - add_mock_keypress(VB_KEY_DOWN); - add_mock_keypress(VB_KEY_DOWN); - add_mock_keypress(VB_KEY_DOWN); /* (blocked) */ - add_mock_keypress(VB_KEY_UP); - add_mock_keypress(VB_KEY_UP); - add_mock_keypress(VB_KEY_ENTER); - TEST_EQ(vb2_ui_loop(ctx, MOCK_SCREEN_MENU, NULL), - VB2_REQUEST_SHUTDOWN, "KEY_UP, KEY_DOWN, and KEY_ENTER"); - DISPLAYED_EQ("mock_screen_menu", MOCK_SCREEN_MENU, MOCK_IGNORE, 0, - MOCK_IGNORE); - DISPLAYED_EQ("mock_screen_menu", MOCK_SCREEN_MENU, MOCK_IGNORE, 1, - MOCK_IGNORE); - DISPLAYED_EQ("mock_screen_menu", MOCK_SCREEN_MENU, MOCK_IGNORE, 2, - MOCK_IGNORE); - DISPLAYED_EQ("mock_screen_menu", MOCK_SCREEN_MENU, MOCK_IGNORE, 3, - MOCK_IGNORE); - DISPLAYED_EQ("mock_screen_menu", MOCK_SCREEN_MENU, MOCK_IGNORE, 4, - MOCK_IGNORE); - DISPLAYED_EQ("mock_screen_menu", MOCK_SCREEN_MENU, MOCK_IGNORE, 3, - MOCK_IGNORE); - DISPLAYED_EQ("mock_screen_menu", MOCK_SCREEN_MENU, MOCK_IGNORE, 2, - MOCK_IGNORE); - DISPLAYED_EQ("mock_screen_target_2", MOCK_SCREEN_TARGET2, MOCK_IGNORE, - MOCK_IGNORE, MOCK_IGNORE); - DISPLAYED_NO_EXTRA(); - - /* For DETACHABLE */ - if (DETACHABLE) { - reset_common_data(); - add_mock_keypress(VB_BUTTON_VOL_UP_SHORT_PRESS); - add_mock_keypress(VB_BUTTON_VOL_DOWN_SHORT_PRESS); - add_mock_keypress(VB_BUTTON_VOL_DOWN_SHORT_PRESS); - add_mock_keypress(VB_BUTTON_VOL_DOWN_SHORT_PRESS); - add_mock_keypress(VB_BUTTON_VOL_DOWN_SHORT_PRESS); - add_mock_keypress(VB_BUTTON_VOL_DOWN_SHORT_PRESS); - add_mock_keypress(VB_BUTTON_VOL_UP_SHORT_PRESS); - add_mock_keypress(VB_BUTTON_VOL_UP_SHORT_PRESS); - add_mock_keypress(VB_BUTTON_POWER_SHORT_PRESS); - TEST_EQ(vb2_ui_loop(ctx, MOCK_SCREEN_MENU, NULL), - VB2_REQUEST_SHUTDOWN, "DETACHABLE"); - DISPLAYED_EQ("mock_screen_menu", MOCK_SCREEN_MENU, MOCK_IGNORE, - 0, MOCK_IGNORE); - DISPLAYED_EQ("mock_screen_menu", MOCK_SCREEN_MENU, MOCK_IGNORE, - 1, MOCK_IGNORE); - DISPLAYED_EQ("mock_screen_menu", MOCK_SCREEN_MENU, MOCK_IGNORE, - 2, MOCK_IGNORE); - DISPLAYED_EQ("mock_screen_menu", MOCK_SCREEN_MENU, MOCK_IGNORE, - 3, MOCK_IGNORE); - DISPLAYED_EQ("mock_screen_menu", MOCK_SCREEN_MENU, MOCK_IGNORE, - 4, MOCK_IGNORE); - DISPLAYED_EQ("mock_screen_menu", MOCK_SCREEN_MENU, MOCK_IGNORE, - 3, MOCK_IGNORE); - DISPLAYED_EQ("mock_screen_menu", MOCK_SCREEN_MENU, MOCK_IGNORE, - 2, MOCK_IGNORE); - DISPLAYED_EQ("mock_screen_target_2", MOCK_SCREEN_TARGET2, - MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE); - DISPLAYED_NO_EXTRA(); - } - - VB2_DEBUG("...done.\n"); -} - -static void ui_loop_delay_tests(void) -{ - VB2_DEBUG("Testing ui_loop delay...\n"); - - /* Sleep for 20 ms each iteration */ - reset_common_data(); - mock_calls_until_shutdown = 1; - TEST_EQ(vb2_ui_loop(ctx, MOCK_SCREEN_BASE, mock_action_msleep), - VB2_REQUEST_SHUTDOWN, " sleep for 20 ms in each iteration"); - TEST_EQ(mock_time_ms - mock_time_start_ms, KEY_DELAY_MS, - " delay 20 ms in total"); - - /* Complement to 20 ms */ - reset_common_data(); - mock_calls_until_shutdown = 1; - mock_action_delay_ms = KEY_DELAY_MS / 2; - TEST_EQ(vb2_ui_loop(ctx, MOCK_SCREEN_BASE, mock_action_msleep), - VB2_REQUEST_SHUTDOWN, " complement to 20 ms"); - TEST_EQ(mock_time_ms - mock_time_start_ms, KEY_DELAY_MS, - " delay 10 ms in total"); - - /* No extra sleep if an iteration takes longer than KEY_DELAY_MS */ - reset_common_data(); - mock_calls_until_shutdown = 1; - mock_action_delay_ms = 1234; - TEST_EQ(vb2_ui_loop(ctx, MOCK_SCREEN_BASE, mock_action_msleep), - VB2_REQUEST_SHUTDOWN, " no extra sleep time"); - TEST_EQ(mock_time_ms - mock_time_start_ms, mock_action_delay_ms, - " no extra delay"); - - /* Integer overflow */ - reset_common_data(); - mock_calls_until_shutdown = 1; - mock_time_ms = UINT32_MAX; - TEST_EQ(vb2_ui_loop(ctx, MOCK_SCREEN_BASE, mock_action_msleep), - VB2_REQUEST_SHUTDOWN, " integer overflow #1"); - TEST_EQ(mock_time_ms - UINT32_MAX, KEY_DELAY_MS, - " delay 20 ms in total"); - - reset_common_data(); - mock_calls_until_shutdown = 1; - mock_time_ms = UINT32_MAX; - mock_action_delay_ms = KEY_DELAY_MS / 2; - TEST_EQ(vb2_ui_loop(ctx, MOCK_SCREEN_BASE, mock_action_msleep), - VB2_REQUEST_SHUTDOWN, " integer overflow #2"); - TEST_EQ(mock_time_ms - UINT32_MAX, KEY_DELAY_MS, - " delay 10 ms in total"); - - reset_common_data(); - mock_calls_until_shutdown = 1; - mock_time_ms = UINT32_MAX; - mock_action_delay_ms = 1234; - TEST_EQ(vb2_ui_loop(ctx, MOCK_SCREEN_BASE, mock_action_msleep), - VB2_REQUEST_SHUTDOWN, " integer overflow #3"); - TEST_EQ(mock_time_ms - UINT32_MAX, mock_action_delay_ms, - " no extra delay"); - - VB2_DEBUG("...done.\n"); -} - -int main(void) -{ - /* Input actions */ - menu_prev_tests(); - menu_next_tests(); - menu_select_tests(); - - /* Core UI loop */ - ui_loop_tests(); - ui_loop_delay_tests(); - - return gTestSuccess ? 0 : 255; -} diff --git a/tests/vb2_ui_utility_tests.c b/tests/vb2_ui_utility_tests.c deleted file mode 100644 index b37b8e0f..00000000 --- a/tests/vb2_ui_utility_tests.c +++ /dev/null @@ -1,376 +0,0 @@ -/* 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. - * - * Tests for UI utility functions. - */ - -#include "2api.h" -#include "2common.h" -#include "2misc.h" -#include "2nvstorage.h" -#include "2ui.h" -#include "2ui_private.h" -#include "test_common.h" -#include "vboot_api.h" - -/* Fixed value for ignoring some checks. */ -#define MOCK_IGNORE 0xffffu - -/* Mock screen index for testing screen utility functions. */ -#define MOCK_NO_SCREEN 0xef00 -#define MOCK_SCREEN_BASE 0xef10 -#define MOCK_SCREEN_MENU 0xef11 -#define MOCK_SCREEN_ROOT 0xefff - -/* Mock data */ -static uint8_t workbuf[VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE] - __attribute__((aligned(VB2_WORKBUF_ALIGN))); -static struct vb2_context *ctx; -static struct vb2_gbb_header gbb; - -static uint32_t mock_locale_count; -static int mock_shutdown_request; - -static struct vb2_ui_context mock_ui_context; - -/* Mock actions */ -static uint32_t mock_action_called; -static vb2_error_t mock_action_base(struct vb2_ui_context *ui) -{ - mock_action_called++; - return VB2_SUCCESS; -} - -/* Mock screens */ -struct vb2_screen_info mock_screen_base = { - .id = MOCK_SCREEN_BASE, - .name = "mock_screen_base: menuless screen", -}; -struct vb2_menu_item mock_screen_menu_items[] = { - { - .text = "option 0: language selection", - .is_language_select = 1, - }, - { - .text = "option 1", - }, - { - .text = "option 2", - }, - { - .text = "option 3", - }, - { - .text = "option 4", - }, -}; -struct vb2_screen_info mock_screen_menu = { - .id = MOCK_SCREEN_MENU, - .name = "mock_screen_menu: screen with 5 options", - .menu = { - .num_items = ARRAY_SIZE(mock_screen_menu_items), - .items = mock_screen_menu_items, - }, -}; -struct vb2_screen_info mock_screen_root = { - .id = MOCK_SCREEN_ROOT, - .name = "mock_screen_root", -}; - -static void screen_state_eq(const struct vb2_screen_state *state, - enum vb2_screen screen, - uint32_t selected_item, - uint32_t hidden_item_mask) -{ - if (screen != MOCK_IGNORE) { - if (state->screen == NULL) - TEST_TRUE(0, " state.screen does not exist"); - else - TEST_EQ(state->screen->id, screen, " state.screen"); - } - if (selected_item != MOCK_IGNORE) - TEST_EQ(state->selected_item, - selected_item, " state.selected_item"); - if (hidden_item_mask != MOCK_IGNORE) - TEST_EQ(state->hidden_item_mask, - hidden_item_mask, " state.hidden_item_mask"); -} - -/* Reset mock data (for use before each test) */ -static void reset_common_data(void) -{ - TEST_SUCC(vb2api_init(workbuf, sizeof(workbuf), &ctx), - "vb2api_init failed"); - - memset(&gbb, 0, sizeof(gbb)); - - vb2_nv_init(ctx); - - /* For vb2ex_get_locale_count */ - mock_locale_count = 1; - - /* For check_shutdown_request */ - mock_shutdown_request = MOCK_IGNORE; - - /* Mock ui_context based on mock screens */ - memset(&mock_ui_context, 0, sizeof(mock_ui_context)); - mock_ui_context.power_button = VB2_POWER_BUTTON_HELD_SINCE_BOOT; - - /* For mock actions */ - mock_action_called = 0; - - /* Reset init and action functions */ - mock_screen_base.init = NULL; - mock_screen_base.action = NULL; - mock_screen_menu.init = NULL; - mock_screen_menu.action = NULL; - mock_screen_root.init = NULL; - mock_screen_root.action = NULL; -} - -/* Mock functions */ -struct vb2_gbb_header *vb2_get_gbb(struct vb2_context *c) -{ - return &gbb; -} - -uint32_t vb2ex_get_locale_count(void) -{ - return mock_locale_count; -} - -uint32_t VbExIsShutdownRequested(void) -{ - if (mock_shutdown_request != MOCK_IGNORE) - return mock_shutdown_request; - - return 0; -} - -const struct vb2_screen_info *vb2_get_screen_info(enum vb2_screen screen) -{ - switch ((int)screen) { - case MOCK_SCREEN_BASE: - return &mock_screen_base; - case MOCK_SCREEN_MENU: - return &mock_screen_menu; - case MOCK_SCREEN_ROOT: - return &mock_screen_root; - default: - return NULL; - } -} - -/* Tests */ -static void check_shutdown_request_tests(void) -{ - VB2_DEBUG("Testing vb2_check_shutdown_request...\n"); - - /* Release, press, hold, and release */ - if (!DETACHABLE) { - reset_common_data(); - mock_shutdown_request = 0; - TEST_EQ(vb2_check_shutdown_request(&mock_ui_context), - VB2_SUCCESS, "release, press, hold, and release"); - mock_shutdown_request = VB_SHUTDOWN_REQUEST_POWER_BUTTON; - TEST_EQ(vb2_check_shutdown_request(&mock_ui_context), - VB2_SUCCESS, " press"); - TEST_EQ(vb2_check_shutdown_request(&mock_ui_context), - VB2_SUCCESS, " hold"); - mock_shutdown_request = 0; - TEST_EQ(vb2_check_shutdown_request(&mock_ui_context), - VB2_REQUEST_SHUTDOWN, " release"); - } - - /* Press is ignored because we may held since boot */ - if (!DETACHABLE) { - reset_common_data(); - mock_shutdown_request = VB_SHUTDOWN_REQUEST_POWER_BUTTON; - TEST_EQ(vb2_check_shutdown_request(&mock_ui_context), - VB2_SUCCESS, "press is ignored"); - } - - /* Power button short press from key */ - if (!DETACHABLE) { - reset_common_data(); - mock_shutdown_request = 0; - mock_ui_context.key = VB_BUTTON_POWER_SHORT_PRESS; - TEST_EQ(vb2_check_shutdown_request(&mock_ui_context), - VB2_REQUEST_SHUTDOWN, "power button short press"); - } - - /* Lid closure = shutdown request anyway */ - reset_common_data(); - mock_shutdown_request = VB_SHUTDOWN_REQUEST_LID_CLOSED; - TEST_EQ(vb2_check_shutdown_request(&mock_ui_context), - VB2_REQUEST_SHUTDOWN, "lid closure"); - mock_ui_context.key = 'A'; - TEST_EQ(vb2_check_shutdown_request(&mock_ui_context), - VB2_REQUEST_SHUTDOWN, " lidsw + random key"); - - /* Lid ignored by GBB flags */ - reset_common_data(); - gbb.flags |= VB2_GBB_FLAG_DISABLE_LID_SHUTDOWN; - mock_shutdown_request = VB_SHUTDOWN_REQUEST_LID_CLOSED; - TEST_EQ(vb2_check_shutdown_request(&mock_ui_context), VB2_SUCCESS, - "lid ignored"); - if (!DETACHABLE) { /* Power button works for non DETACHABLE */ - mock_shutdown_request = VB_SHUTDOWN_REQUEST_LID_CLOSED | - VB_SHUTDOWN_REQUEST_POWER_BUTTON; - TEST_EQ(vb2_check_shutdown_request(&mock_ui_context), - VB2_SUCCESS, " lidsw + pwdsw"); - mock_shutdown_request = 0; - TEST_EQ(vb2_check_shutdown_request(&mock_ui_context), - VB2_REQUEST_SHUTDOWN, " pwdsw release"); - } - - /* Lid ignored; power button short pressed */ - if (!DETACHABLE) { - reset_common_data(); - gbb.flags |= VB2_GBB_FLAG_DISABLE_LID_SHUTDOWN; - mock_shutdown_request = VB_SHUTDOWN_REQUEST_LID_CLOSED; - mock_ui_context.key = VB_BUTTON_POWER_SHORT_PRESS; - TEST_EQ(vb2_check_shutdown_request(&mock_ui_context), - VB2_REQUEST_SHUTDOWN, - "lid ignored; power button short pressed"); - } - - /* DETACHABLE ignore power button */ - if (DETACHABLE) { - /* Flag pwdsw */ - reset_common_data(); - mock_shutdown_request = VB_SHUTDOWN_REQUEST_POWER_BUTTON; - TEST_EQ(vb2_check_shutdown_request(&mock_ui_context), - VB2_SUCCESS, "DETACHABLE: ignore pwdsw"); - mock_shutdown_request = 0; - TEST_EQ(vb2_check_shutdown_request(&mock_ui_context), - VB2_SUCCESS, " ignore on release"); - - /* Power button short press */ - reset_common_data(); - mock_shutdown_request = 0; - mock_ui_context.key = VB_BUTTON_POWER_SHORT_PRESS; - TEST_EQ(vb2_check_shutdown_request(&mock_ui_context), - VB2_SUCCESS, - "DETACHABLE: ignore power button short press"); - } - - VB2_DEBUG("...done.\n"); -} - -static vb2_error_t try_back_helper(void) -{ - VB2_TRY(vb2_ui_screen_back(&mock_ui_context)); - return VB2_ERROR_MOCK; -} - -static vb2_error_t try_screen_change_helper(enum vb2_screen screen_id) -{ - VB2_TRY(vb2_ui_screen_change(&mock_ui_context, screen_id)); - return VB2_ERROR_MOCK; -} - -static void screen_stack_tests(void) -{ - VB2_DEBUG("Testing screen stack functionality...\n"); - - /* Change to screen which does not exist */ - reset_common_data(); - TEST_EQ(vb2_ui_screen_change(&mock_ui_context, MOCK_NO_SCREEN), - VB2_REQUEST_UI_CONTINUE, - "change to screen which does not exist"); - TEST_PTR_EQ(mock_ui_context.state, NULL, " stack is empty"); - - /* Screen back with empty stack */ - reset_common_data(); - TEST_EQ(vb2_ui_screen_back(&mock_ui_context), VB2_REQUEST_UI_CONTINUE, - "screen back with empty stack"); - TEST_PTR_EQ(mock_ui_context.state, NULL, " stack is empty"); - - /* Back to previous screen, restoring the state */ - reset_common_data(); - mock_screen_base.init = mock_action_base; - vb2_ui_screen_change(&mock_ui_context, MOCK_SCREEN_ROOT); - vb2_ui_screen_change(&mock_ui_context, MOCK_SCREEN_BASE); - mock_ui_context.state->selected_item = 2; - mock_ui_context.state->hidden_item_mask = 0x10; - vb2_ui_screen_change(&mock_ui_context, MOCK_SCREEN_MENU); - TEST_EQ(vb2_ui_screen_back(&mock_ui_context), VB2_REQUEST_UI_CONTINUE, - "back to previous screen"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_BASE, 2, 0x10); - TEST_EQ(mock_action_called, 1, " action called once"); - - /* VB2_TRY around back should return right away */ - reset_common_data(); - TEST_NEQ(try_back_helper(), VB2_ERROR_MOCK, - "continued executing after VB2_TRY(back)"); - - /* VB2_TRY around screen_change should return right away */ - reset_common_data(); - TEST_NEQ(try_screen_change_helper(MOCK_SCREEN_ROOT), VB2_ERROR_MOCK, - "continued executing after VB2_TRY(screen_change)"); - - /* Change to target screen already in stack, restoring the state */ - reset_common_data(); - mock_screen_base.init = mock_action_base; - vb2_ui_screen_change(&mock_ui_context, MOCK_SCREEN_ROOT); - vb2_ui_screen_change(&mock_ui_context, MOCK_SCREEN_BASE); - mock_ui_context.state->selected_item = 2; - mock_ui_context.state->hidden_item_mask = 0x10; - vb2_ui_screen_change(&mock_ui_context, MOCK_SCREEN_MENU); - TEST_EQ(vb2_ui_screen_change(&mock_ui_context, MOCK_SCREEN_BASE), - VB2_REQUEST_UI_CONTINUE, "change to target in stack"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_BASE, 2, 0x10); - TEST_EQ(mock_action_called, 1, " action called once"); - - /* Change to screen without init; using default init() */ - reset_common_data(); - TEST_EQ(vb2_ui_screen_change(&mock_ui_context, MOCK_SCREEN_MENU), - VB2_REQUEST_UI_CONTINUE, - "change to screen with language selection"); - screen_state_eq(mock_ui_context.state, MOCK_SCREEN_MENU, - 1, /* Since index 0 is the language selection */ - 0); - - VB2_DEBUG("...done.\n"); -} - -static void get_language_menu_tests(void) -{ - const struct vb2_menu *menu; - const struct vb2_menu_item *items; - VB2_DEBUG("Testing get_language_menu...\n"); - - /* Only allocate menu items once */ - reset_common_data(); - mock_locale_count = 7; - menu = vb2_get_language_menu(&mock_ui_context); - TEST_PTR_NEQ(menu, NULL, "get language menu"); - TEST_EQ(menu->num_items, 7, " correct locale count"); - TEST_PTR_NEQ(menu->items, NULL, " items not null"); - items = menu->items; - - menu = vb2_get_language_menu(&mock_ui_context); - TEST_PTR_NEQ(menu, NULL, "get language menu again"); - TEST_EQ(menu->num_items, 7, " correct locale count again"); - TEST_PTR_EQ(menu->items, items, " same pointer of items"); - - /* Locale count = 0 */ - reset_common_data(); - mock_locale_count = 0; - menu = vb2_get_language_menu(&mock_ui_context); - TEST_PTR_NEQ(menu, NULL, "menu not null"); - TEST_EQ(menu->num_items, 1, " locale count 1"); - - VB2_DEBUG("...done.\n"); -} - -int main(void) -{ - check_shutdown_request_tests(); - screen_stack_tests(); - get_language_menu_tests(); - - return gTestSuccess ? 0 : 255; -} |