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