| /* Copyright 2018 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 "2api.h" |
| #include "2common.h" |
| #include "2misc.h" |
| #include "2nvstorage.h" |
| #include "2sysincludes.h" |
| #include "vboot_api.h" |
| #include "vboot_kernel.h" |
| #include "vboot_test.h" |
| #include "vboot_ui_legacy.h" |
| |
| 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 void Uint8ToString(char *buf, uint8_t val) |
| { |
| const char *trans = "0123456789abcdef"; |
| *buf++ = trans[val >> 4]; |
| *buf = trans[val & 0xF]; |
| } |
| |
| static void FillInSha1Sum(char *outbuf, struct vb2_packed_key *key) |
| { |
| uint8_t *buf = ((uint8_t *)key) + key->key_offset; |
| uint64_t buflen = key->key_size; |
| uint8_t digest[VB2_SHA1_DIGEST_SIZE]; |
| int i; |
| |
| vb2_digest_buffer(buf, buflen, VB2_HASH_SHA1, digest, sizeof(digest)); |
| for (i = 0; i < sizeof(digest); i++) { |
| Uint8ToString(outbuf, digest[i]); |
| outbuf += 2; |
| } |
| *outbuf = '\0'; |
| } |
| |
| #define DEBUG_INFO_SIZE 1024 |
| #define DEBUG_INFO_APPEND(format, args...) do { \ |
| if (used < DEBUG_INFO_SIZE) \ |
| used += snprintf(buf + used, DEBUG_INFO_SIZE - used, format, \ |
| ## args); \ |
| } while (0) |
| |
| vb2_error_t VbDisplayDebugInfo(struct vb2_context *ctx) |
| { |
| struct vb2_shared_data *sd = vb2_get_sd(ctx); |
| struct vb2_gbb_header *gbb = vb2_get_gbb(ctx); |
| struct vb2_workbuf wb; |
| char buf[DEBUG_INFO_SIZE] = ""; |
| char sha1sum[VB2_SHA1_DIGEST_SIZE * 2 + 1]; |
| int32_t used = 0; |
| vb2_error_t ret; |
| uint32_t i; |
| |
| vb2_workbuf_from_ctx(ctx, &wb); |
| |
| /* Add hardware ID */ |
| { |
| char hwid[VB2_GBB_HWID_MAX_SIZE]; |
| uint32_t size = sizeof(hwid); |
| ret = vb2api_gbb_read_hwid(ctx, hwid, &size); |
| if (ret) |
| strcpy(hwid, "{INVALID}"); |
| DEBUG_INFO_APPEND("HWID: %s", hwid); |
| } |
| |
| /* Add recovery reason and subcode */ |
| i = vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE); |
| DEBUG_INFO_APPEND("\nrecovery_reason: %#.2x / %#.2x %s", |
| sd->recovery_reason, i, |
| vb2_get_recovery_reason_string(sd->recovery_reason)); |
| |
| /* Add vb2_context and vb2_shared_data flags */ |
| DEBUG_INFO_APPEND("\ncontext.flags: %#.16" PRIx64, ctx->flags); |
| DEBUG_INFO_APPEND("\nshared_data.flags: %#.8x", sd->flags); |
| DEBUG_INFO_APPEND("\nshared_data.status: %#.8x", sd->status); |
| |
| /* Add raw contents of nvdata */ |
| DEBUG_INFO_APPEND("\nnvdata:"); |
| if (vb2_nv_get_size(ctx) > 16) /* Multi-line starts on next line */ |
| DEBUG_INFO_APPEND("\n "); |
| for (i = 0; i < vb2_nv_get_size(ctx); i++) { |
| /* Split into 16-byte blocks */ |
| if (i > 0 && i % 16 == 0) |
| DEBUG_INFO_APPEND("\n "); |
| DEBUG_INFO_APPEND(" %02x", ctx->nvdata[i]); |
| } |
| |
| /* Add dev_boot_usb flag */ |
| i = vb2_nv_get(ctx, VB2_NV_DEV_BOOT_EXTERNAL); |
| DEBUG_INFO_APPEND("\ndev_boot_usb: %d", i); |
| |
| /* Add dev_boot_legacy flag */ |
| i = vb2_nv_get(ctx, VB2_NV_DEV_BOOT_LEGACY); |
| DEBUG_INFO_APPEND("\ndev_boot_legacy: %d", i); |
| |
| /* Add dev_default_boot flag */ |
| i = vb2_nv_get(ctx, VB2_NV_DEV_DEFAULT_BOOT); |
| DEBUG_INFO_APPEND("\ndev_default_boot: %d", i); |
| |
| /* Add dev_boot_signed_only flag */ |
| i = vb2_nv_get(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY); |
| DEBUG_INFO_APPEND("\ndev_boot_signed_only: %d", i); |
| |
| /* Add TPM versions */ |
| DEBUG_INFO_APPEND("\nTPM: fwver=%#.8x kernver=%#.8x", |
| sd->fw_version_secdata, sd->kernel_version_secdata); |
| |
| /* Add GBB flags */ |
| DEBUG_INFO_APPEND("\ngbb.flags: %#.8x", gbb->flags); |
| |
| /* Add sha1sum for Root & Recovery keys */ |
| { |
| struct vb2_packed_key *key; |
| struct vb2_workbuf wblocal = wb; |
| ret = vb2_gbb_read_root_key(ctx, &key, NULL, &wblocal); |
| if (!ret) { |
| FillInSha1Sum(sha1sum, key); |
| DEBUG_INFO_APPEND("\ngbb.rootkey: %s", sha1sum); |
| } |
| } |
| |
| { |
| struct vb2_packed_key *key; |
| struct vb2_workbuf wblocal = wb; |
| ret = vb2_gbb_read_recovery_key(ctx, &key, NULL, &wblocal); |
| if (!ret) { |
| FillInSha1Sum(sha1sum, key); |
| DEBUG_INFO_APPEND("\ngbb.recovery_key: %s", sha1sum); |
| } |
| } |
| |
| /* If we're in dev-mode, show the kernel subkey that we expect, too. */ |
| if (!(ctx->flags & VB2_CONTEXT_RECOVERY_MODE) && |
| sd->kernel_key_offset) { |
| struct vb2_packed_key *key = |
| vb2_member_of(sd, sd->kernel_key_offset); |
| FillInSha1Sum(sha1sum, key); |
| DEBUG_INFO_APPEND("\nkernel_subkey: %s", sha1sum); |
| } |
| |
| /* Make sure we finish with a newline */ |
| DEBUG_INFO_APPEND("\n"); |
| |
| /* TODO: add more interesting data: |
| * - Information on current disks */ |
| |
| buf[DEBUG_INFO_SIZE - 1] = '\0'; |
| VB2_DEBUG("[TAB] Debug Info:\n%s", buf); |
| return VbExDisplayDebugInfo(buf, 1); |
| } |
| |
| vb2_error_t VbCheckDisplayKey(struct vb2_context *ctx, uint32_t key, |
| uint32_t screen, const VbScreenData *data) |
| { |
| uint32_t loc = 0; |
| uint32_t count = 0; |
| |
| switch (key) { |
| case '\t': |
| /* Tab = display debug info */ |
| return VbDisplayDebugInfo(ctx); |
| case VB_KEY_ESC: |
| /* Force redraw current screen (to clear Tab debug output) */ |
| return VbDisplayScreen(ctx, screen, 1, data); |
| case VB_KEY_LEFT: |
| case VB_KEY_RIGHT: |
| case VB_KEY_UP: |
| case VB_KEY_DOWN: |
| /* Arrow keys = change localization */ |
| loc = vb2_nv_get(ctx, VB2_NV_LOCALIZATION_INDEX); |
| count = vb2ex_get_locale_count(); |
| if (count == 0) |
| loc = 0; /* No localization count (bad GBB?) */ |
| else if (VB_KEY_RIGHT == key || VB_KEY_UP == key) |
| loc = (loc < count - 1 ? loc + 1 : 0); |
| else |
| loc = (loc > 0 ? loc - 1 : count - 1); |
| VB2_DEBUG("VbCheckDisplayKey() - change localization to %d\n", |
| (int)loc); |
| vb2_nv_set(ctx, VB2_NV_LOCALIZATION_INDEX, loc); |
| vb2_nv_set(ctx, VB2_NV_BACKUP_NVRAM_REQUEST, 1); |
| |
| /* |
| * Non-manual recovery mode is meant to be left via three-finger |
| * salute (into manual recovery mode). Need to commit nvdata |
| * changes immediately. Ignore commit errors in recovery mode. |
| */ |
| if ((ctx->flags & VB2_CONTEXT_RECOVERY_MODE) && |
| !vb2_allow_recovery(ctx)) |
| vb2ex_commit_data(ctx); |
| |
| /* Force redraw of current screen */ |
| return VbDisplayScreen(ctx, screen, 1, data); |
| } |
| |
| return VB2_SUCCESS; |
| } |
| |
| static enum { |
| POWER_BUTTON_HELD_SINCE_BOOT = 0, |
| POWER_BUTTON_RELEASED, |
| POWER_BUTTON_PRESSED, /* Must have been previously released */ |
| } power_button_state; |
| |
| void vb2_reset_power_button(void) { |
| power_button_state = POWER_BUTTON_HELD_SINCE_BOOT; |
| } |
| |
| void vb2_error_beep(enum vb2_beep_type beep) |
| { |
| switch (beep) { |
| case VB_BEEP_FAILED: |
| vb2ex_beep(250, 200); |
| break; |
| default: |
| case VB_BEEP_NOT_ALLOWED: |
| vb2ex_beep(120, 400); |
| vb2ex_msleep(120); |
| vb2ex_beep(120, 400); |
| break; |
| } |
| } |
| |
| void vb2_error_notify(const char *print_msg, |
| const char *log_msg, |
| enum vb2_beep_type beep) |
| { |
| if (print_msg) |
| VbExDisplayDebugInfo(print_msg, 0); |
| if (!log_msg) |
| log_msg = print_msg; |
| if (log_msg) |
| VB2_DEBUG(log_msg); |
| vb2_error_beep(beep); |
| } |
| |
| void vb2_error_no_altfw(void) |
| { |
| VB2_DEBUG("Legacy boot is disabled\n"); |
| VbExDisplayDebugInfo("WARNING: Booting legacy BIOS has not been " |
| "enabled. Refer to the developer-mode " |
| "documentation for details.\n", 0); |
| vb2_error_beep(VB_BEEP_NOT_ALLOWED); |
| } |
| |
| void vb2_try_altfw(struct vb2_context *ctx, int allowed, |
| enum VbAltFwIndex_t altfw_num) |
| { |
| if (!allowed) { |
| vb2_error_no_altfw(); |
| return; |
| } |
| |
| if (vb2ex_commit_data(ctx)) { |
| vb2_error_notify("Error committing data on legacy boot.\n", |
| NULL, VB_BEEP_FAILED); |
| return; |
| } |
| |
| /* Will not return if successful */ |
| VbExLegacy(altfw_num); |
| |
| vb2_error_notify("Legacy boot failed. Missing BIOS?\n", NULL, |
| VB_BEEP_FAILED); |
| } |
| |
| int vb2_want_shutdown(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; |
| } |