| /* Copyright (c) 2014 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. |
| * |
| * Misc functions which need access to vb2_context but are not public APIs |
| */ |
| |
| #include "2api.h" |
| #include "2common.h" |
| #include "2misc.h" |
| #include "2nvstorage.h" |
| #include "2recovery_reasons.h" |
| #include "2rsa.h" |
| #include "2secdata.h" |
| #include "2sha.h" |
| #include "2struct.h" |
| #include "2sysincludes.h" |
| #include "vb2_common.h" |
| #include "vboot_api.h" |
| #include "vboot_struct.h" |
| |
| vb2_error_t vb2_validate_gbb_signature(uint8_t *sig) |
| { |
| const static uint8_t sig_xor[VB2_GBB_SIGNATURE_SIZE] = |
| VB2_GBB_XOR_SIGNATURE; |
| int i; |
| for (i = 0; i < VB2_GBB_SIGNATURE_SIZE; i++) { |
| if (sig[i] != (sig_xor[i] ^ VB2_GBB_XOR_CHARS[i])) |
| return VB2_ERROR_GBB_MAGIC; |
| } |
| return VB2_SUCCESS; |
| } |
| |
| test_mockable |
| struct vb2_gbb_header *vb2_get_gbb(struct vb2_context *ctx) |
| { |
| struct vb2_shared_data *sd = vb2_get_sd(ctx); |
| return (struct vb2_gbb_header *)((void *)sd + sd->gbb_offset); |
| } |
| |
| uint32_t vb2api_get_firmware_size(struct vb2_context *ctx) |
| { |
| struct vb2_shared_data *sd = vb2_get_sd(ctx); |
| if (!sd->preamble_size) |
| return 0; |
| |
| const struct vb2_fw_preamble *pre = (const struct vb2_fw_preamble *) |
| vb2_member_of(sd, sd->preamble_offset); |
| return pre->body_signature.data_size; |
| } |
| |
| test_mockable |
| vb2_error_t vb2_read_gbb_header(struct vb2_context *ctx, |
| struct vb2_gbb_header *gbb) |
| { |
| /* Read the entire header */ |
| VB2_TRY(vb2ex_read_resource(ctx, VB2_RES_GBB, 0, gbb, sizeof(*gbb))); |
| |
| /* Make sure it's really a GBB */ |
| VB2_TRY(vb2_validate_gbb_signature(gbb->signature)); |
| |
| /* Check for compatible version */ |
| if (gbb->major_version != VB2_GBB_MAJOR_VER) |
| return VB2_ERROR_GBB_VERSION; |
| |
| /* Current code is not backwards-compatible to 1.1 headers or older */ |
| if (gbb->minor_version < VB2_GBB_MINOR_VER) |
| return VB2_ERROR_GBB_TOO_OLD; |
| |
| /* |
| * Header size should be at least as big as we expect. It could be |
| * bigger, if the header has grown. |
| */ |
| if (gbb->header_size < sizeof(*gbb)) |
| return VB2_ERROR_GBB_HEADER_SIZE; |
| |
| return VB2_SUCCESS; |
| } |
| |
| test_mockable |
| void vb2api_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode) |
| { |
| struct vb2_shared_data *sd = vb2_get_sd(ctx); |
| |
| /* If NV data hasn't been initialized, initialize it now */ |
| if (!(sd->status & VB2_SD_STATUS_NV_INIT)) |
| vb2_nv_init(ctx); |
| |
| /* See if we were far enough in the boot process to choose a slot */ |
| if (sd->status & VB2_SD_STATUS_CHOSE_SLOT) { |
| |
| /* Boot failed */ |
| vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_FAILURE); |
| |
| /* Use up remaining tries */ |
| vb2_nv_set(ctx, VB2_NV_TRY_COUNT, 0); |
| |
| /* |
| * Try the other slot next time. We'll alternate |
| * between slots, which may help if one or both slots is |
| * flaky. |
| */ |
| vb2_nv_set(ctx, VB2_NV_TRY_NEXT, 1 - sd->fw_slot); |
| |
| /* |
| * If we didn't try the other slot last boot, or we tried it |
| * and it didn't fail, try it next boot. |
| */ |
| if (sd->last_fw_slot != 1 - sd->fw_slot || |
| sd->last_fw_result != VB2_FW_RESULT_FAILURE) |
| return; |
| } |
| |
| /* |
| * If we're still here, we failed before choosing a slot, or both |
| * this slot and the other slot failed in successive boots. So we |
| * need to go to recovery. |
| * |
| * Set a recovery reason and subcode only if they're not already set. |
| * If recovery is already requested, it's a more specific error code |
| * than later code is providing and we shouldn't overwrite it. |
| */ |
| VB2_DEBUG("Need recovery, reason: %#x / %#x\n", reason, subcode); |
| if (!vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST)) { |
| vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, reason); |
| vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, subcode); |
| } |
| } |
| |
| void vb2_check_recovery(struct vb2_context *ctx) |
| { |
| struct vb2_shared_data *sd = vb2_get_sd(ctx); |
| uint32_t reason = vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST); |
| uint32_t subcode = vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE); |
| |
| VB2_DEBUG("Recovery reason from previous boot: %#x / %#x\n", |
| reason, subcode); |
| |
| /* |
| * Sets the current recovery request, unless there's already been a |
| * failure earlier in the boot process. |
| */ |
| if (!sd->recovery_reason) |
| sd->recovery_reason = reason; |
| |
| if (ctx->flags & VB2_CONTEXT_FORCE_RECOVERY_MODE) { |
| VB2_DEBUG("Recovery was requested manually\n"); |
| if (subcode && !sd->recovery_reason && |
| subcode != VB2_RECOVERY_TRAIN_AND_REBOOT) |
| /* |
| * Recovery was requested at 'broken' screen. |
| * Promote subcode to reason. |
| */ |
| sd->recovery_reason = subcode; |
| else |
| /* Recovery was forced. Override recovery reason */ |
| sd->recovery_reason = VB2_RECOVERY_RO_MANUAL; |
| sd->flags |= VB2_SD_FLAG_MANUAL_RECOVERY; |
| } |
| |
| /* If recovery reason is non-zero, tell caller we need recovery mode */ |
| if (sd->recovery_reason) { |
| ctx->flags |= VB2_CONTEXT_RECOVERY_MODE; |
| VB2_DEBUG("We have a recovery request: %#x / %#x\n", |
| sd->recovery_reason, |
| vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE)); |
| } |
| } |
| |
| vb2_error_t vb2_fw_init_gbb(struct vb2_context *ctx) |
| { |
| struct vb2_shared_data *sd = vb2_get_sd(ctx); |
| struct vb2_gbb_header *gbb; |
| struct vb2_workbuf wb; |
| |
| vb2_workbuf_from_ctx(ctx, &wb); |
| |
| /* Read GBB into next chunk of work buffer */ |
| gbb = vb2_workbuf_alloc(&wb, sizeof(*gbb)); |
| if (!gbb) |
| return VB2_ERROR_GBB_WORKBUF; |
| |
| VB2_TRY(vb2_read_gbb_header(ctx, gbb)); |
| |
| /* Keep on the work buffer permanently */ |
| sd->gbb_offset = vb2_offset_of(sd, gbb); |
| vb2_set_workbuf_used(ctx, vb2_offset_of(sd, wb.buf)); |
| |
| /* Set any context flags based on GBB flags */ |
| if (gbb->flags & VB2_GBB_FLAG_DISABLE_FWMP) |
| ctx->flags |= VB2_CONTEXT_NO_SECDATA_FWMP; |
| |
| return VB2_SUCCESS; |
| } |
| |
| vb2_error_t vb2_check_dev_switch(struct vb2_context *ctx) |
| { |
| struct vb2_shared_data *sd = vb2_get_sd(ctx); |
| struct vb2_gbb_header *gbb = vb2_get_gbb(ctx); |
| uint32_t flags = 0; |
| uint32_t old_flags; |
| int is_dev = 0; |
| int valid_secdata = 1; |
| vb2_error_t rv; |
| |
| /* Check whether secdata_firmware is initialized */ |
| if (!(sd->status & VB2_SD_STATUS_SECDATA_FIRMWARE_INIT)) |
| valid_secdata = 0; |
| |
| /* Read secure flags */ |
| flags = vb2_secdata_firmware_get(ctx, VB2_SECDATA_FIRMWARE_FLAGS); |
| old_flags = flags; |
| |
| /* Handle dev disable request */ |
| if (valid_secdata && vb2_nv_get(ctx, VB2_NV_DISABLE_DEV_REQUEST)) { |
| flags &= ~VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE; |
| |
| /* Clear the request */ |
| vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST, 0); |
| } |
| |
| /* |
| * Check if we've been asked by the caller to disable dev mode. Note |
| * that hardware switch and GBB flag will take precedence over this. |
| */ |
| if (ctx->flags & VB2_CONTEXT_DISABLE_DEVELOPER_MODE) |
| flags &= ~VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE; |
| |
| /* Check virtual dev switch */ |
| if (flags & VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE) |
| is_dev = 1; |
| |
| /* Check if GBB is forcing dev mode */ |
| if (gbb->flags & VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON) |
| is_dev = 1; |
| |
| /* Handle whichever mode we end up in */ |
| if (is_dev) { |
| /* Developer mode */ |
| sd->flags |= VB2_SD_FLAG_DEV_MODE_ENABLED; |
| ctx->flags |= VB2_CONTEXT_DEVELOPER_MODE; |
| |
| flags |= VB2_SECDATA_FIRMWARE_FLAG_LAST_BOOT_DEVELOPER; |
| } else { |
| /* Normal mode */ |
| flags &= ~VB2_SECDATA_FIRMWARE_FLAG_LAST_BOOT_DEVELOPER; |
| |
| /* |
| * Disable dev_boot_* flags. This ensures they will be |
| * initially disabled if the user later transitions back into |
| * developer mode. |
| */ |
| vb2_nv_set(ctx, VB2_NV_DEV_BOOT_EXTERNAL, 0); |
| vb2_nv_set(ctx, VB2_NV_DEV_BOOT_LEGACY, 0); |
| vb2_nv_set(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY, 0); |
| vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT, 0); |
| } |
| |
| if (ctx->flags & VB2_CONTEXT_FORCE_WIPEOUT_MODE) |
| vb2_nv_set(ctx, VB2_NV_REQ_WIPEOUT, 1); |
| |
| if (flags != old_flags) { |
| /* |
| * Just changed dev mode state. Clear TPM owner. This must be |
| * done here instead of simply passing a flag to |
| * vb2_check_tpm_clear(), because we don't want to update |
| * last_boot_developer and then fail to clear the TPM owner. |
| * |
| * Note that we do this even if secdata_firmware is having |
| * issues, since the TPM owner and secdata_firmware may be |
| * independent, and we want the owner to be cleared if *this |
| * boot* is different than the last one (perhaps due to GBB or |
| * hardware override). |
| */ |
| rv = vb2ex_tpm_clear_owner(ctx); |
| /* Check for failure to clear owner */ |
| if (valid_secdata && rv) { |
| /* |
| * Note that this truncates rv to 8 bit. Which |
| * is not as useful as the full error code, but |
| * we don't have NVRAM space to store the full |
| * 32-bit code. |
| */ |
| vb2api_fail(ctx, VB2_RECOVERY_TPM_CLEAR_OWNER, rv); |
| return rv; |
| } |
| |
| /* Save new flags */ |
| vb2_secdata_firmware_set(ctx, VB2_SECDATA_FIRMWARE_FLAGS, |
| flags); |
| } |
| |
| return VB2_SUCCESS; |
| } |
| |
| vb2_error_t vb2_check_tpm_clear(struct vb2_context *ctx) |
| { |
| vb2_error_t rv; |
| |
| /* Check if we've been asked to clear the owner */ |
| if (!vb2_nv_get(ctx, VB2_NV_CLEAR_TPM_OWNER_REQUEST)) |
| return VB2_SUCCESS; /* No need to clear */ |
| |
| /* Request applies one time only */ |
| vb2_nv_set(ctx, VB2_NV_CLEAR_TPM_OWNER_REQUEST, 0); |
| |
| /* Try clearing */ |
| rv = vb2ex_tpm_clear_owner(ctx); |
| if (rv) { |
| /* |
| * Note that this truncates rv to 8 bit. Which is not as |
| * useful as the full error code, but we don't have NVRAM space |
| * to store the full 32-bit code. |
| */ |
| vb2api_fail(ctx, VB2_RECOVERY_TPM_CLEAR_OWNER, rv); |
| return rv; |
| } |
| |
| /* Clear successful */ |
| vb2_nv_set(ctx, VB2_NV_CLEAR_TPM_OWNER_DONE, 1); |
| return VB2_SUCCESS; |
| } |
| |
| vb2_error_t vb2_select_fw_slot(struct vb2_context *ctx) |
| { |
| struct vb2_shared_data *sd = vb2_get_sd(ctx); |
| uint32_t tries; |
| |
| /* Get result of last boot */ |
| sd->last_fw_slot = vb2_nv_get(ctx, VB2_NV_FW_TRIED); |
| sd->last_fw_result = vb2_nv_get(ctx, VB2_NV_FW_RESULT); |
| |
| /* Save to the previous result fields in NV storage */ |
| vb2_nv_set(ctx, VB2_NV_FW_PREV_TRIED, sd->last_fw_slot); |
| vb2_nv_set(ctx, VB2_NV_FW_PREV_RESULT, sd->last_fw_result); |
| |
| /* Clear result, since we don't know what will happen this boot */ |
| vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN); |
| |
| /* Get slot to try */ |
| sd->fw_slot = vb2_nv_get(ctx, VB2_NV_TRY_NEXT); |
| |
| /* Check try count */ |
| tries = vb2_nv_get(ctx, VB2_NV_TRY_COUNT); |
| |
| if (sd->last_fw_result == VB2_FW_RESULT_TRYING && |
| sd->last_fw_slot == sd->fw_slot && |
| tries == 0) { |
| /* |
| * We used up our last try on the previous boot, so fall back |
| * to the other slot this boot. |
| */ |
| sd->fw_slot = 1 - sd->fw_slot; |
| vb2_nv_set(ctx, VB2_NV_TRY_NEXT, sd->fw_slot); |
| } |
| |
| if (tries > 0) { |
| /* Still trying this firmware */ |
| vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_TRYING); |
| |
| /* Decrement non-zero try count, unless told not to */ |
| if (!(ctx->flags & VB2_CONTEXT_NOFAIL_BOOT)) |
| vb2_nv_set(ctx, VB2_NV_TRY_COUNT, tries - 1); |
| } |
| |
| /* Store the slot we're trying */ |
| vb2_nv_set(ctx, VB2_NV_FW_TRIED, sd->fw_slot); |
| |
| /* Set context flag if we're using slot B */ |
| if (sd->fw_slot) |
| ctx->flags |= VB2_CONTEXT_FW_SLOT_B; |
| |
| /* Set status flag */ |
| sd->status |= VB2_SD_STATUS_CHOSE_SLOT; |
| |
| return VB2_SUCCESS; |
| } |
| |
| void vb2_enable_developer_mode(struct vb2_context *ctx) |
| { |
| uint32_t flags; |
| |
| VB2_DEBUG("Enabling developer mode...\n"); |
| |
| flags = vb2_secdata_firmware_get(ctx, VB2_SECDATA_FIRMWARE_FLAGS); |
| flags |= VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE; |
| vb2_secdata_firmware_set(ctx, VB2_SECDATA_FIRMWARE_FLAGS, flags); |
| |
| if (BOOT_EXTERNAL_ON_DEV) |
| vb2_nv_set(ctx, VB2_NV_DEV_BOOT_EXTERNAL, 1); |
| |
| VB2_DEBUG("Mode change will take effect on next reboot\n"); |
| } |
| |
| test_mockable |
| int vb2_allow_recovery(struct vb2_context *ctx) |
| { |
| if (ctx->flags & VB2_CONTEXT_NO_BOOT) |
| return 0; |
| |
| /* VB2_GBB_FLAG_FORCE_MANUAL_RECOVERY forces this to always return |
| true. */ |
| if (vb2_get_gbb(ctx)->flags & VB2_GBB_FLAG_FORCE_MANUAL_RECOVERY) |
| return 1; |
| |
| /* |
| * If EC is in RW, it implies recovery wasn't manually requested. |
| * On some platforms, EC_IN_RW can't be reset by the EC, thus, this may |
| * return false (=RW). That's ok because if recovery is manual, we will |
| * get the right signal and that's the case we care about. |
| */ |
| if (!vb2ex_ec_trusted()) |
| return 0; |
| |
| /* Now we confidently check the recovery switch state at boot */ |
| return !!(vb2_get_sd(ctx)->flags & VB2_SD_FLAG_MANUAL_RECOVERY); |
| } |
| |
| void vb2_clear_recovery(struct vb2_context *ctx) |
| { |
| struct vb2_shared_data *sd = vb2_get_sd(ctx); |
| uint32_t reason = vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST); |
| uint32_t subcode = vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE); |
| |
| if (reason || subcode) |
| VB2_DEBUG("Clearing recovery request: %#x / %#x %s\n", |
| reason, subcode, |
| vb2_get_recovery_reason_string(reason)); |
| |
| /* Clear recovery request for both manual and non-manual. */ |
| vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, VB2_RECOVERY_NOT_REQUESTED); |
| vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, 0); |
| |
| /* But stow recovery reason as subcode for non-manual recovery. */ |
| if ((ctx->flags & VB2_CONTEXT_RECOVERY_MODE) && |
| !vb2_allow_recovery(ctx)) { |
| VB2_DEBUG("Stow recovery reason as subcode (%#x)\n", |
| sd->recovery_reason); |
| vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, sd->recovery_reason); |
| } |
| } |
| |
| int vb2api_need_reboot_for_display(struct vb2_context *ctx) |
| { |
| if (!(vb2_get_sd(ctx)->flags & VB2_SD_FLAG_DISPLAY_AVAILABLE)) { |
| VB2_DEBUG("Need reboot to initialize display\n"); |
| vb2_nv_set(ctx, VB2_NV_DISPLAY_REQUEST, 1); |
| return 1; |
| } |
| return 0; |
| } |
| |
| uint32_t vb2api_get_recovery_reason(struct vb2_context *ctx) |
| { |
| return vb2_get_sd(ctx)->recovery_reason; |
| } |
| |
| uint32_t vb2api_get_locale_id(struct vb2_context *ctx) |
| { |
| return vb2_nv_get(ctx, VB2_NV_LOCALIZATION_INDEX); |
| } |
| |
| void vb2api_export_vbsd(struct vb2_context *ctx, void *dest) |
| { |
| struct vb2_shared_data *sd = vb2_get_sd(ctx); |
| VbSharedDataHeader *vbsd = (void *)dest; |
| |
| /* Initialize with boilerplate fields. */ |
| memset(vbsd, 0, VB2_VBSD_SIZE); |
| vbsd->magic = VB_SHARED_DATA_MAGIC; |
| vbsd->struct_version = VB_SHARED_DATA_VERSION; |
| vbsd->struct_size = VB2_VBSD_SIZE; |
| vbsd->data_size = VB2_VBSD_SIZE; |
| vbsd->data_used = VB2_VBSD_SIZE; |
| vbsd->flags |= VBSD_BOOT_FIRMWARE_VBOOT2; |
| |
| /* Translate vboot2 flags and fields into vboot1. */ |
| if (ctx->flags & VB2_CONTEXT_EC_SYNC_SUPPORTED) |
| vbsd->flags |= VBSD_EC_SOFTWARE_SYNC; |
| if (ctx->flags & VB2_CONTEXT_NVDATA_V2) |
| vbsd->flags |= VBSD_NVDATA_V2; |
| if (ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) |
| vbsd->flags |= VBSD_BOOT_DEV_SWITCH_ON; |
| if (ctx->flags & VB2_CONTEXT_FORCE_RECOVERY_MODE) |
| vbsd->flags |= VBSD_BOOT_REC_SWITCH_ON; |
| if (sd->flags & VB2_SD_FLAG_KERNEL_SIGNED) |
| vbsd->flags |= VBSD_KERNEL_KEY_VERIFIED; |
| |
| vbsd->fw_version_tpm_start = sd->fw_version_secdata; |
| vbsd->fw_version_tpm = sd->fw_version; |
| vbsd->kernel_version_tpm_start = sd->kernel_version_secdata; |
| vbsd->kernel_version_tpm = sd->kernel_version; |
| |
| vbsd->recovery_reason = sd->recovery_reason; |
| if (sd->recovery_reason) |
| vbsd->firmware_index = 0xff; |
| else |
| vbsd->firmware_index = sd->fw_slot; |
| } |
| _Static_assert(VB2_VBSD_SIZE == sizeof(VbSharedDataHeader), |
| "VB2_VBSD_SIZE incorrect"); |
| |
| int vb2api_phone_recovery_enabled(struct vb2_context *ctx) |
| { |
| return !(vb2_secdata_kernel_get(ctx, VB2_SECDATA_KERNEL_FLAGS) & |
| VB2_SECDATA_KERNEL_FLAG_PHONE_RECOVERY_DISABLED); |
| } |
| |
| int vb2api_phone_recovery_ui_enabled(struct vb2_context *ctx) |
| { |
| /* |
| * When phone recovery functionality is disabled, return 0 even if |
| * PHONE_RECOVERY_UI_DISABLED is not set. |
| */ |
| return vb2api_phone_recovery_enabled(ctx) && |
| !(vb2_secdata_kernel_get(ctx, VB2_SECDATA_KERNEL_FLAGS) & |
| VB2_SECDATA_KERNEL_FLAG_PHONE_RECOVERY_UI_DISABLED); |
| } |
| |
| int vb2api_diagnostic_ui_enabled(struct vb2_context *ctx) |
| { |
| return !(vb2_secdata_kernel_get(ctx, VB2_SECDATA_KERNEL_FLAGS) & |
| VB2_SECDATA_KERNEL_FLAG_DIAGNOSTIC_UI_DISABLED); |
| } |
| |
| enum vb2_dev_default_boot_target vb2api_get_dev_default_boot_target( |
| struct vb2_context *ctx) |
| { |
| struct vb2_gbb_header *gbb = vb2_get_gbb(ctx); |
| |
| if (gbb->flags & VB2_GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY) |
| return VB2_DEV_DEFAULT_BOOT_TARGET_LEGACY; |
| |
| switch (vb2_nv_get(ctx, VB2_NV_DEV_DEFAULT_BOOT)) { |
| case VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL: |
| if (vb2_dev_boot_external_allowed(ctx)) |
| return VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL; |
| break; |
| |
| case VB2_DEV_DEFAULT_BOOT_TARGET_LEGACY: |
| if (vb2_dev_boot_legacy_allowed(ctx)) |
| return VB2_DEV_DEFAULT_BOOT_TARGET_LEGACY; |
| break; |
| } |
| |
| return VB2_DEV_DEFAULT_BOOT_TARGET_INTERNAL; |
| } |
| |
| int vb2_dev_boot_allowed(struct vb2_context *ctx) |
| { |
| struct vb2_gbb_header *gbb = vb2_get_gbb(ctx); |
| |
| if (vb2_secdata_fwmp_get_flag(ctx, VB2_SECDATA_FWMP_DEV_DISABLE_BOOT)) |
| return !!(gbb->flags & VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON); |
| |
| return 1; |
| } |
| |
| int vb2_dev_boot_legacy_allowed(struct vb2_context *ctx) |
| { |
| struct vb2_gbb_header *gbb = vb2_get_gbb(ctx); |
| |
| return vb2_nv_get(ctx, VB2_NV_DEV_BOOT_LEGACY) || |
| (gbb->flags & VB2_GBB_FLAG_FORCE_DEV_BOOT_LEGACY) || |
| vb2_secdata_fwmp_get_flag(ctx, |
| VB2_SECDATA_FWMP_DEV_ENABLE_LEGACY); |
| } |
| |
| int vb2_dev_boot_external_allowed(struct vb2_context *ctx) |
| { |
| struct vb2_gbb_header *gbb = vb2_get_gbb(ctx); |
| |
| return vb2_nv_get(ctx, VB2_NV_DEV_BOOT_EXTERNAL) || |
| (gbb->flags & VB2_GBB_FLAG_FORCE_DEV_BOOT_USB) || |
| vb2_secdata_fwmp_get_flag(ctx, VB2_SECDATA_FWMP_DEV_ENABLE_EXTERNAL); |
| } |
| |
| int vb2api_use_short_dev_screen_delay(struct vb2_context *ctx) |
| { |
| struct vb2_gbb_header *gbb = vb2_get_gbb(ctx); |
| return gbb->flags & VB2_GBB_FLAG_DEV_SCREEN_SHORT_DELAY; |
| } |
| |
| static void snprint_sha1_sum(struct vb2_packed_key *key, |
| char *dest, size_t dest_size) |
| { |
| uint8_t *buf = ((uint8_t *)key) + key->key_offset; |
| uint64_t buflen = key->key_size; |
| uint8_t digest[VB2_SHA1_DIGEST_SIZE]; |
| int32_t used = 0; |
| int i; |
| |
| vb2_digest_buffer(buf, buflen, VB2_HASH_SHA1, digest, sizeof(digest)); |
| for (i = 0; i < sizeof(digest); i++) |
| if (used < dest_size) |
| used += snprintf(dest + used, dest_size - used, |
| "%02x", digest[i]); |
| dest[dest_size - 1] = '\0'; |
| } |
| |
| #define DEBUG_INFO_MAX_LENGTH 1024 |
| |
| #define DEBUG_INFO_APPEND(format, args...) do { \ |
| if (used < DEBUG_INFO_MAX_LENGTH) \ |
| used += snprintf(buf + used, DEBUG_INFO_MAX_LENGTH - used, \ |
| format, ## args); \ |
| } while (0) |
| |
| char *vb2api_get_debug_info(struct vb2_context *ctx) |
| { |
| char *buf; |
| uint32_t used = 0; |
| |
| struct vb2_shared_data *sd = vb2_get_sd(ctx); |
| struct vb2_gbb_header *gbb = vb2_get_gbb(ctx); |
| struct vb2_workbuf wb; |
| char sha1sum[VB2_SHA1_DIGEST_SIZE * 2 + 1]; |
| |
| vb2_error_t rv; |
| uint32_t i; |
| |
| buf = malloc(DEBUG_INFO_MAX_LENGTH + 1); |
| if (buf == NULL) |
| return NULL; |
| |
| vb2_workbuf_from_ctx(ctx, &wb); |
| |
| /* Add hardware ID */ |
| { |
| char hwid[VB2_GBB_HWID_MAX_SIZE]; |
| uint32_t size = sizeof(hwid); |
| rv = vb2api_gbb_read_hwid(ctx, hwid, &size); |
| if (rv) |
| 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; |
| rv = vb2_gbb_read_root_key(ctx, &key, NULL, &wblocal); |
| if (rv == VB2_SUCCESS) { |
| snprint_sha1_sum(key, sha1sum, sizeof(sha1sum)); |
| DEBUG_INFO_APPEND("\ngbb.rootkey: %s", sha1sum); |
| } |
| } |
| |
| { |
| struct vb2_packed_key *key; |
| struct vb2_workbuf wblocal = wb; |
| rv = vb2_gbb_read_recovery_key(ctx, &key, NULL, &wblocal); |
| if (rv == VB2_SUCCESS) { |
| snprint_sha1_sum(key, sha1sum, sizeof(sha1sum)); |
| 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); |
| snprint_sha1_sum(key, sha1sum, sizeof(sha1sum)); |
| DEBUG_INFO_APPEND("\nkernel_subkey: %s", sha1sum); |
| } |
| |
| buf[DEBUG_INFO_MAX_LENGTH] = '\0'; |
| return buf; |
| } |