/* 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 "2sysincludes.h" #include "2common.h" #include "2misc.h" #include "2nvstorage.h" #include "2rsa.h" #include "ec_sync.h" #include "load_kernel_fw.h" #include "rollback_index.h" #include "tlcl.h" #include "utility.h" #include "vb2_common.h" #include "vboot_api.h" #include "vboot_audio.h" #include "vboot_common.h" #include "vboot_display.h" #include "vboot_kernel.h" #include "vboot_ui_common.h" /* Global variables */ enum { POWER_BUTTON_HELD_SINCE_BOOT = 0, POWER_BUTTON_RELEASED, POWER_BUTTON_PRESSED, /* must have been previously released */ } power_button_state; void vb2_init_ui(void) { power_button_state = POWER_BUTTON_HELD_SINCE_BOOT; } static void VbAllowUsbBoot(struct vb2_context *ctx) { VB2_DEBUG("."); vb2_nv_set(ctx, VB2_NV_DEV_BOOT_USB, 1); } /** * Checks GBB flags against VbExIsShutdownRequested() shutdown request to * determine if a shutdown is required. * * Returns zero or more of the following flags (if any are set then typically * shutdown is required): * VB_SHUTDOWN_REQUEST_LID_CLOSED * VB_SHUTDOWN_REQUEST_POWER_BUTTON */ static int VbWantShutdown(struct vb2_context *ctx, uint32_t key) { struct vb2_gbb_header *gbb = vb2_get_gbb(ctx); uint32_t shutdown_request = VbExIsShutdownRequested(); /* * Ignore power button push until after we have seen it released. * This avoids shutting down immediately if the power button is still * being held on startup. After we've recognized a valid power button * push then don't report the event until after the button is released. */ if (shutdown_request & VB_SHUTDOWN_REQUEST_POWER_BUTTON) { shutdown_request &= ~VB_SHUTDOWN_REQUEST_POWER_BUTTON; if (power_button_state == POWER_BUTTON_RELEASED) power_button_state = POWER_BUTTON_PRESSED; } else { if (power_button_state == POWER_BUTTON_PRESSED) shutdown_request |= VB_SHUTDOWN_REQUEST_POWER_BUTTON; power_button_state = POWER_BUTTON_RELEASED; } if (key == VB_BUTTON_POWER_SHORT_PRESS) shutdown_request |= VB_SHUTDOWN_REQUEST_POWER_BUTTON; /* If desired, ignore shutdown request due to lid closure. */ if (gbb->flags & VB2_GBB_FLAG_DISABLE_LID_SHUTDOWN) shutdown_request &= ~VB_SHUTDOWN_REQUEST_LID_CLOSED; return shutdown_request; } static vb2_error_t VbTryUsb(struct vb2_context *ctx) { int retval = VbTryLoadKernel(ctx, VB_DISK_FLAG_REMOVABLE); if (VB2_SUCCESS == retval) { VB2_DEBUG("VbBootDeveloper() - booting USB\n"); } else { vb2_error_notify("Could not boot from USB\n", "VbBootDeveloper() - no kernel found on USB\n", VB_BEEP_FAILED); /* * Clear recovery requests from failed * kernel loading, so that powering off * at this point doesn't put us into * recovery mode. */ vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, VB2_RECOVERY_NOT_REQUESTED); } return retval; } #define CONFIRM_KEY_DELAY 20 /* Check confirm screen keys every 20ms */ int VbUserConfirms(struct vb2_context *ctx, uint32_t confirm_flags) { struct vb2_shared_data *sd = vb2_get_sd(ctx); VbSharedDataHeader *shared = sd->vbsd; uint32_t key; uint32_t key_flags; uint32_t btn; int phys_presence_button_was_pressed = 0; int shutdown_requested = 0; VB2_DEBUG("Entering(%x)\n", confirm_flags); /* Await further instructions */ do { key = VbExKeyboardReadWithFlags(&key_flags); shutdown_requested = VbWantShutdown(ctx, key); switch (key) { case VB_KEY_ENTER: /* If we require a trusted keyboard for confirmation, * but the keyboard may be faked (for instance, a USB * device), beep and keep waiting. */ if (confirm_flags & VB_CONFIRM_MUST_TRUST_KEYBOARD && !(key_flags & VB_KEY_FLAG_TRUSTED_KEYBOARD)) { vb2_error_notify("Please use internal keyboard " "to confirm\n", "VbUserConfirms() - " "Trusted keyboard is requierd\n", VB_BEEP_NOT_ALLOWED); break; } VB2_DEBUG("Yes (1)\n"); return 1; 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; break; default: /* If the physical presence button is physical, and is * pressed, this is also a YES, but must wait for * release. */ btn = VbExGetSwitches( VB_SWITCH_FLAG_PHYS_PRESENCE_PRESSED); if (!(shared->flags & VBSD_BOOT_REC_SWITCH_VIRTUAL)) { if (btn) { VB2_DEBUG("Presence button pressed, " "awaiting release\n"); phys_presence_button_was_pressed = 1; } else if (phys_presence_button_was_pressed) { VB2_DEBUG("Presence button released " "(1)\n"); return 1; } } VbCheckDisplayKey(ctx, key, NULL); } VbExSleepMs(CONFIRM_KEY_DELAY); } while (!shutdown_requested); return -1; } /* Delay in developer ui */ #define DEV_KEY_DELAY 20 /* Check keys every 20ms */ /* * 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 (VbWantShutdown(ctx, key)) { VB2_DEBUG("VbBootDeveloper() - shutdown requested!\n"); return VBERROR_SHUTDOWN_REQUESTED; } switch (key) { case 0: /* nothing pressed */ break; case VB_KEY_ESC: /* Escape pressed - return to developer screen */ VB2_DEBUG("VbBootDeveloper() - 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("VbBootDeveloper() - " "user pressed key '%c': Boot alternative " "firmware\n", key); /* * This will not return if successful. Drop out to * developer mode on failure. */ vb2_run_altfw(ctx, key - '0'); active = 0; break; default: VB2_DEBUG("VbBootDeveloper() - pressed key %d\n", key); VbCheckDisplayKey(ctx, key, NULL); break; } VbExSleepMs(DEV_KEY_DELAY); } while (active); /* Back to developer screen */ VbDisplayScreen(ctx, VB_SCREEN_DEVELOPER_WARNING, 0, NULL); return 0; } static inline int is_vowel(uint32_t key) { return key == 'A' || key == 'E' || key == 'I' || key == 'O' || key == 'U'; } /* * Prompt the user to enter the vendor data */ static vb2_error_t vb2_enter_vendor_data_ui(struct vb2_context *ctx, char *data_value) { int len = 0; VbScreenData data = { .vendor_data = { data_value } }; data_value[0] = '\0'; VbDisplayScreen(ctx, VB_SCREEN_SET_VENDOR_DATA, 1, &data); /* We'll loop until the user decides what to do */ do { uint32_t key = VbExKeyboardRead(); if (VbWantShutdown(ctx, key)) { VB2_DEBUG("Vendor Data UI - shutdown requested!\n"); return VBERROR_SHUTDOWN_REQUESTED; } switch (key) { case 0: /* nothing pressed */ break; case VB_KEY_ESC: /* Escape pressed - return to developer screen */ VB2_DEBUG("Vendor Data UI - user pressed Esc: " "exit to Developer screen\n"); data_value[0] = '\0'; return VB2_SUCCESS; case 'a'...'z': key = toupper(key); /* fall through */ case '0'...'9': case 'A'...'Z': if ((len > 0 && is_vowel(key)) || len >= VENDOR_DATA_LENGTH) { vb2_error_beep(VB_BEEP_NOT_ALLOWED); } else { data_value[len++] = key; data_value[len] = '\0'; VbDisplayScreen(ctx, VB_SCREEN_SET_VENDOR_DATA, 1, &data); } VB2_DEBUG("Vendor Data UI - vendor_data: %s\n", data_value); break; case VB_KEY_BACKSPACE: if (len > 0) { data_value[--len] = '\0'; VbDisplayScreen(ctx, VB_SCREEN_SET_VENDOR_DATA, 1, &data); } VB2_DEBUG("Vendor Data UI - vendor_data: %s\n", data_value); break; case VB_KEY_ENTER: if (len == VENDOR_DATA_LENGTH) { /* Enter pressed - confirm input */ VB2_DEBUG("Vendor Data UI - user pressed " "Enter: confirm vendor data\n"); return VB2_SUCCESS; } else { vb2_error_beep(VB_BEEP_NOT_ALLOWED); } break; default: VB2_DEBUG("Vendor Data UI - pressed key %d\n", key); VbCheckDisplayKey(ctx, key, &data); break; } VbExSleepMs(DEV_KEY_DELAY); } while (1); return VB2_SUCCESS; } /* * User interface for setting the vendor data in VPD */ static vb2_error_t vb2_vendor_data_ui(struct vb2_context *ctx) { char data_value[VENDOR_DATA_LENGTH + 1]; VbScreenData data = { .vendor_data = { data_value } }; vb2_error_t ret = vb2_enter_vendor_data_ui(ctx, data_value); if (ret) return ret; /* Vendor data was not entered just return */ if (data_value[0] == '\0') return VB2_SUCCESS; VbDisplayScreen(ctx, VB_SCREEN_CONFIRM_VENDOR_DATA, 1, &data); /* We'll loop until the user decides what to do */ do { uint32_t key = VbExKeyboardRead(); if (VbWantShutdown(ctx, key)) { VB2_DEBUG("Vendor Data UI - shutdown requested!\n"); return VBERROR_SHUTDOWN_REQUESTED; } switch (key) { case 0: /* nothing pressed */ break; case VB_KEY_ESC: /* Escape pressed - return to developer screen */ VB2_DEBUG("Vendor Data UI - user pressed Esc: " "exit to Developer screen\n"); return VB2_SUCCESS; case VB_KEY_ENTER: /* Enter pressed - write vendor data */ VB2_DEBUG("Vendor Data UI - user pressed Enter: " "write vendor data (%s) to VPD\n", data_value); ret = VbExSetVendorData(data_value); if (ret == VB2_SUCCESS) { vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST, 1); return VBERROR_REBOOT_REQUIRED; } else { vb2_error_notify( "ERROR: Vendor data was not set.\n" "System will now shutdown\n", NULL, VB_BEEP_FAILED); VbExSleepMs(5000); return VBERROR_SHUTDOWN_REQUESTED; } default: VB2_DEBUG("Vendor Data UI - pressed key %d\n", key); VbCheckDisplayKey(ctx, key, &data); break; } VbExSleepMs(DEV_KEY_DELAY); } while (1); return VB2_SUCCESS; } static vb2_error_t vb2_check_diagnostic_key(struct vb2_context *ctx, uint32_t key) { if (DIAGNOSTIC_UI && (key == VB_KEY_CTRL('C') || key == VB_KEY_F(12))) { VB2_DEBUG("Diagnostic mode requested, rebooting\n"); vb2_nv_set(ctx, VB2_NV_DIAG_REQUEST, 1); return VBERROR_REBOOT_REQUIRED; } return VB2_SUCCESS; } /* * User interface for confirming launch of diagnostics rom * * This asks the user to confirm the launch of the diagnostics rom. The user * can press the power button to confirm or press escape. There is a 30-second * timeout which acts the same as escape. */ static vb2_error_t vb2_diagnostics_ui(struct vb2_context *ctx) { int active = 1; int power_button_was_released = 0; int power_button_was_pressed = 0; vb2_error_t result = VBERROR_REBOOT_REQUIRED; int action_confirmed = 0; uint64_t start_time_us; VbDisplayScreen(ctx, VB_SCREEN_CONFIRM_DIAG, 0, NULL); start_time_us = VbExGetTimer(); /* We'll loop until the user decides what to do */ do { uint32_t key = VbExKeyboardRead(); /* * VbExIsShutdownRequested() is almost an adequate substitute * for adding a new flag to VbExGetSwitches(). The main * issue is that the former doesn't consult the power button * on detachables, and this function wants to see for itself * that the power button isn't currently pressed. */ if (VbExGetSwitches(VB_SWITCH_FLAG_PHYS_PRESENCE_PRESSED)) { /* Wait for a release before registering a press. */ if (power_button_was_released) power_button_was_pressed = 1; } else { power_button_was_released = 1; if (power_button_was_pressed) { VB2_DEBUG("vb2_diagnostics_ui() - power released\n"); action_confirmed = 1; active = 0; break; } } /* Check the lid and ignore the power button. */ if (VbWantShutdown(ctx, 0) & ~VB_SHUTDOWN_REQUEST_POWER_BUTTON) { VB2_DEBUG("vb2_diagnostics_ui() - shutdown request\n"); result = VBERROR_SHUTDOWN_REQUESTED; active = 0; break; } switch (key) { case 0: /* nothing pressed */ break; case VB_KEY_ESC: /* Escape pressed - reboot */ VB2_DEBUG("vb2_diagnostics_ui() - user pressed Esc\n"); active = 0; break; default: VB2_DEBUG("vb2_diagnostics_ui() - pressed key %d\n", key); VbCheckDisplayKey(ctx, key, NULL); break; } if (VbExGetTimer() - start_time_us >= 30 * VB_USEC_PER_SEC) { VB2_DEBUG("vb2_diagnostics_ui() - timeout\n"); break; } if (active) { VbExSleepMs(DEV_KEY_DELAY); } } while (active); VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL); if (action_confirmed) { VB2_DEBUG("Diagnostic requested, running\n"); /* * The following helps avoid use of the TPM after * it's disabled (e.g., when vb2_run_altfw() calls * RollbackKernelLock() ). */ if (RollbackKernelLock(0)) { VB2_DEBUG("Failed to lock TPM PP\n"); vb2_fail(ctx, VB2_RECOVERY_TPM_DISABLE_FAILED, 0); } else if (vb2ex_tpm_set_mode(VB2_TPM_MODE_DISABLED) != VB2_SUCCESS) { VB2_DEBUG("Failed to disable TPM\n"); vb2_fail(ctx, VB2_RECOVERY_TPM_DISABLE_FAILED, 0); } else { vb2_run_altfw(ctx, VB_ALTFW_DIAGNOSTIC); VB2_DEBUG("Diagnostic failed to run\n"); /* * Assuming failure was due to bad hash, though * the rom could just be missing or invalid. */ vb2_fail(ctx, VB2_RECOVERY_ALTFW_HASH_FAILED, 0); } } return result; } static const char dev_disable_msg[] = "Developer mode is disabled on this device by system policy.\n" "For more information, see http://dev.chromium.org/chromium-os/fwmp\n" "\n"; 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); VbSharedDataHeader *shared = sd->vbsd; 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 USB booting is allowed */ uint32_t allow_usb = vb2_nv_get(ctx, VB2_NV_DEV_BOOT_USB); uint32_t allow_legacy = vb2_nv_get(ctx, VB2_NV_DEV_BOOT_LEGACY); /* Check if the default is to boot using disk, usb, or legacy */ uint32_t default_boot = vb2_nv_get(ctx, VB2_NV_DEV_DEFAULT_BOOT); if(default_boot == VB2_DEV_DEFAULT_BOOT_USB) use_usb = 1; if(default_boot == VB2_DEV_DEFAULT_BOOT_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 */ uint32_t fwmp_flags = vb2_get_fwmp_flags(); if (fwmp_flags & FWMP_DEV_ENABLE_USB) allow_usb = 1; if (fwmp_flags & FWMP_DEV_ENABLE_LEGACY) allow_legacy = 1; if (fwmp_flags & 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); VbExSleepMs(5000); return VBERROR_REBOOT_REQUIRED; case -1: VB2_DEBUG("shutdown requested\n"); return VBERROR_SHUTDOWN_REQUESTED; default: /* Ignore user attempt to cancel */ VB2_DEBUG("ignore cancel TONORM\n"); } } /* 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 (VbWantShutdown(ctx, key)) { VB2_DEBUG("VbBootDeveloper() - shutdown requested!\n"); return VBERROR_SHUTDOWN_REQUESTED; } 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; /* fall through */ case ' ': /* See if we should disable virtual dev-mode switch. */ VB2_DEBUG("shared->flags=0x%x\n", shared->flags); if (shared->flags & VBSD_BOOT_DEV_SWITCH_ON) { /* 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); VbExSleepMs(5000); return VBERROR_REBOOT_REQUIRED; case -1: VB2_DEBUG("shutdown requested\n"); return VBERROR_SHUTDOWN_REQUESTED; 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); } } else { /* * No virtual dev-mode switch, so go directly * to recovery mode. */ VB2_DEBUG("going to recovery\n"); vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, VB2_RECOVERY_RW_DEV_SCREEN); return VBERROR_LOAD_KERNEL_RECOVERY; } break; case VB_KEY_CTRL('D'): /* Ctrl+D = dismiss warning; advance to timeout */ VB2_DEBUG("VbBootDeveloper() - " "user pressed Ctrl+D; skip delay\n"); ctrl_d_pressed = 1; goto fallout; break; case VB_KEY_CTRL('L'): VB2_DEBUG("VbBootDeveloper() - " "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('S'): if (VENDOR_DATA_LENGTH == 0) break; /* * Only show the vendor data ui if it is tag is settable */ if (ctx->flags & VB2_CONTEXT_VENDOR_DATA_SETTABLE) { vb2_error_t ret; VB2_DEBUG("VbBootDeveloper() - user pressed " "Ctrl+S; Try set vendor data\n"); ret = vb2_vendor_data_ui(ctx); if (ret) { return ret; } else { /* Show dev mode warning screen again */ VbDisplayScreen(ctx, VB_SCREEN_DEVELOPER_WARNING, 0, NULL); } } else { vb2_error_notify( "WARNING: Vendor data cannot be " "changed because it is already set.\n", NULL, VB_BEEP_NOT_ALLOWED); } 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("VbBootDeveloper() - " "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", "VbBootDeveloper() - " "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("VbBootDeveloper() - " "user pressed key '%c': Boot alternative " "firmware\n", key); vb2_try_alt_fw(ctx, allow_legacy, key - '0'); break; default: VB2_DEBUG("VbBootDeveloper() - pressed key %d\n", key); VbCheckDisplayKey(ctx, key, NULL); break; } VbExSleepMs(DEV_KEY_DELAY); } 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("VbBootDeveloper() - defaulting to legacy\n"); vb2_try_alt_fw(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("VbBootDeveloper() - trying fixed disk\n"); return VbTryLoadKernel(ctx, VB_DISK_FLAG_FIXED); } vb2_error_t VbBootDeveloper(struct vb2_context *ctx) { vb2_init_ui(); vb2_error_t retval = vb2_developer_ui(ctx); VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL); return retval; } vb2_error_t VbBootDiagnostic(struct vb2_context *ctx) { vb2_init_ui(); vb2_error_t retval = vb2_diagnostics_ui(ctx); VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL); return retval; } /* Delay in recovery mode */ #define REC_DISK_DELAY 1000 /* Check disks every 1s */ #define REC_KEY_DELAY 20 /* Check keys every 20ms */ #define REC_MEDIA_INIT_DELAY 500 /* Check removable media every 500ms */ static vb2_error_t recovery_ui(struct vb2_context *ctx) { struct vb2_shared_data *sd = vb2_get_sd(ctx); VbSharedDataHeader *shared = sd->vbsd; uint32_t retval; uint32_t key; int i; 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("VbBootRecovery() start\n"); if (!vb2_allow_recovery(ctx)) { /* * We have to save the reason here so that it will survive * coming up three-finger-salute. We're saving it in * VB2_RECOVERY_SUBCODE to avoid a recovery loop. * If we save the reason in VB2_RECOVERY_REQUEST, we will come * back here, thus, we won't be able to give a user a chance to * reboot to workaround a boot hiccup. */ VB2_DEBUG("VbBootRecovery() saving recovery reason (%#x)\n", shared->recovery_reason); vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, shared->recovery_reason); /* * Commit NV now, because it won't get saved if the user forces * manual recovery via the three-finger salute. */ vb2_nv_commit(ctx); VbDisplayScreen(ctx, VB_SCREEN_OS_BROKEN, 0, NULL); VB2_DEBUG("VbBootRecovery() waiting for manual recovery\n"); while (1) { key = VbExKeyboardRead(); VbCheckDisplayKey(ctx, key, NULL); if (VbWantShutdown(ctx, key)) return VBERROR_SHUTDOWN_REQUESTED; else if ((retval = vb2_check_diagnostic_key(ctx, key)) != VB2_SUCCESS) return retval; VbExSleepMs(REC_KEY_DELAY); } } /* Loop and wait for a recovery image */ VB2_DEBUG("VbBootRecovery() waiting for a recovery image\n"); while (1) { VB2_DEBUG("VbBootRecovery() attempting to load kernel2\n"); retval = VbTryLoadKernel(ctx, VB_DISK_FLAG_REMOVABLE); /* * Clear recovery requests from failed kernel loading, since * we're already in recovery mode. Do this now, so that * powering off after inserting an invalid disk doesn't leave * us stuck in recovery mode. */ vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, VB2_RECOVERY_NOT_REQUESTED); if (VB2_SUCCESS == retval) break; /* Found a recovery kernel */ VbDisplayScreen(ctx, VBERROR_NO_DISK_FOUND == retval ? VB_SCREEN_RECOVERY_INSERT : VB_SCREEN_RECOVERY_NO_GOOD, 0, NULL); /* * Scan keyboard more frequently than media, since x86 * platforms don't like to scan USB too rapidly. */ for (i = 0; i < REC_DISK_DELAY; i += REC_KEY_DELAY) { 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') && !(shared->flags & VBSD_BOOT_DEV_SWITCH_ON) && (shared->flags & VBSD_BOOT_REC_SWITCH_ON)) { if (!(shared->flags & VBSD_BOOT_REC_SWITCH_VIRTUAL) && VbExGetSwitches( VB_SWITCH_FLAG_PHYS_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_DEBUG("Enabling dev-mode...\n"); if (VB2_SUCCESS != SetVirtualDevMode(1)) return VBERROR_TPM_SET_BOOT_MODE_STATE; VB2_DEBUG("Reboot so it will take " "effect\n"); if (VbExGetSwitches (VB_SWITCH_FLAG_ALLOW_USB_BOOT)) VbAllowUsbBoot(ctx); return VBERROR_EC_REBOOT_TO_RO_REQUIRED; case -1: VB2_DEBUG("Shutdown requested\n"); return VBERROR_SHUTDOWN_REQUESTED; default: /* zero, actually */ VB2_DEBUG("Not enabling dev-mode\n"); /* * Jump out of the outer loop to * refresh the display quickly. */ i = 4; break; } } else if ((retval = vb2_check_diagnostic_key(ctx, key)) != VB2_SUCCESS) { return retval; } else { VbCheckDisplayKey(ctx, key, NULL); } if (VbWantShutdown(ctx, key)) return VBERROR_SHUTDOWN_REQUESTED; VbExSleepMs(REC_KEY_DELAY); } } return VB2_SUCCESS; } vb2_error_t VbBootRecovery(struct vb2_context *ctx) { vb2_error_t retval = recovery_ui(ctx); VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL); return retval; }