/* 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. */ /* Non-volatile storage routines */ #include "2sysincludes.h" #include "2common.h" #include "2crc8.h" #include "2misc.h" #include "2nvstorage.h" /* * Constants for NV storage. We use this rather than structs and bitfields so * the data format is consistent across platforms and compilers. Total NV * storage size is VB2_NVDATA_SIZE = 16 bytes. */ enum vb2_nv_offset { VB2_NV_OFFS_HEADER = 0, VB2_NV_OFFS_BOOT = 1, VB2_NV_OFFS_RECOVERY = 2, VB2_NV_OFFS_LOCALIZATION = 3, VB2_NV_OFFS_DEV = 4, VB2_NV_OFFS_TPM = 5, VB2_NV_OFFS_RECOVERY_SUBCODE = 6, VB2_NV_OFFS_BOOT2 = 7, /* Offsets 7-10 are currently unused */ VB2_NV_OFFS_KERNEL = 11, /* 11-14; field is 32 bits */ /* CRC must be last field */ VB2_NV_OFFS_CRC = 15 }; /* Fields in VB2_NV_OFFS_HEADER (unused = 0x0f) */ #define VB2_NV_HEADER_KERNEL_SETTINGS_RESET 0x10 #define VB2_NV_HEADER_FW_SETTINGS_RESET 0x20 #define VB2_NV_HEADER_SIGNATURE 0x40 #define VB2_NV_HEADER_MASK 0xc0 /* Fields in VB2_NV_OFFS_BOOT */ #define VB2_NV_BOOT_TRY_COUNT_MASK 0x0f #define VB2_NV_BOOT_TRY_NEXT 0x10 #define VB2_NV_BOOT_OPROM_NEEDED 0x20 #define VB2_NV_BOOT_DISABLE_DEV 0x40 #define VB2_NV_BOOT_DEBUG_RESET 0x80 /* Fields in VB2_NV_OFFS_BOOT2 (unused = 0xf8) */ #define VB2_NV_BOOT2_RESULT_MASK 0x03 #define VB2_NV_BOOT2_TRIED 0x04 /* Fields in VB2_NV_OFFS_DEV (unused = 0xf8) */ #define VB2_NV_DEV_FLAG_USB 0x01 #define VB2_NV_DEV_FLAG_SIGNED_ONLY 0x02 #define VB2_NV_DEV_FLAG_LEGACY 0x04 /* Fields in VB2_NV_OFFS_TPM (unused = 0xfc) */ #define VB2_NV_TPM_CLEAR_OWNER_REQUEST 0x01 #define VB2_NV_TPM_CLEAR_OWNER_DONE 0x02 static void vb2_nv_regen_crc(struct vb2_context *ctx) { ctx->nvdata[VB2_NV_OFFS_CRC] = vb2_crc8(ctx->nvdata, VB2_NV_OFFS_CRC); ctx->flags |= VB2_CONTEXT_NVDATA_CHANGED; } /** * Check the CRC of the non-volatile storage context. * * Use this if reading from non-volatile storage may be flaky, and you want to * retry reading it several times. * * This may be called before vb2_context_init(). * * @param ctx Context pointer * @return VB2_SUCCESS, or non-zero error code if error. */ int vb2_nv_check_crc(const struct vb2_context *ctx) { const uint8_t *p = ctx->nvdata; /* Check header */ if (VB2_NV_HEADER_SIGNATURE != (p[VB2_NV_OFFS_HEADER] & VB2_NV_HEADER_MASK)) return VB2_ERROR_NV_HEADER; /* Check CRC */ if (vb2_crc8(p, VB2_NV_OFFS_CRC) != p[VB2_NV_OFFS_CRC]) return VB2_ERROR_NV_CRC; return VB2_SUCCESS; } void vb2_nv_init(struct vb2_context *ctx) { struct vb2_shared_data *sd = vb2_get_sd(ctx); uint8_t *p = ctx->nvdata; /* Check data for consistency */ if (vb2_nv_check_crc(ctx) != VB2_SUCCESS) { /* Data is inconsistent (bad CRC or header); reset defaults */ memset(p, 0, VB2_NVDATA_SIZE); p[VB2_NV_OFFS_HEADER] = (VB2_NV_HEADER_SIGNATURE | VB2_NV_HEADER_FW_SETTINGS_RESET | VB2_NV_HEADER_KERNEL_SETTINGS_RESET); /* Regenerate CRC */ vb2_nv_regen_crc(ctx); /* Set status flag */ sd->status |= VB2_SD_STATUS_NV_REINIT; // TODO: unit test for status flag being set } sd->status |= VB2_SD_STATUS_NV_INIT; } /* Macro for vb2_nv_get() single-bit settings to reduce duplicate code. */ #define GETBIT(offs, mask) (p[offs] & mask ? 1 : 0) uint32_t vb2_nv_get(struct vb2_context *ctx, enum vb2_nv_param param) { const uint8_t *p = ctx->nvdata; /* * TODO: We could reduce the binary size for this code by #ifdef'ing * out the params not used by firmware verification. */ switch (param) { case VB2_NV_FIRMWARE_SETTINGS_RESET: return GETBIT(VB2_NV_OFFS_HEADER, VB2_NV_HEADER_FW_SETTINGS_RESET); case VB2_NV_KERNEL_SETTINGS_RESET: return GETBIT(VB2_NV_OFFS_HEADER, VB2_NV_HEADER_KERNEL_SETTINGS_RESET); case VB2_NV_DEBUG_RESET_MODE: return GETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_DEBUG_RESET); case VB2_NV_TRY_NEXT: return GETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_TRY_NEXT); case VB2_NV_TRY_COUNT: return p[VB2_NV_OFFS_BOOT] & VB2_NV_BOOT_TRY_COUNT_MASK; case VB2_NV_FW_TRIED: return GETBIT(VB2_NV_OFFS_BOOT2, VB2_NV_BOOT2_TRIED); case VB2_NV_FW_RESULT: return p[VB2_NV_OFFS_BOOT2] & VB2_NV_BOOT2_RESULT_MASK; case VB2_NV_RECOVERY_REQUEST: return p[VB2_NV_OFFS_RECOVERY]; case VB2_NV_RECOVERY_SUBCODE: return p[VB2_NV_OFFS_RECOVERY_SUBCODE]; case VB2_NV_LOCALIZATION_INDEX: return p[VB2_NV_OFFS_LOCALIZATION]; case VB2_NV_KERNEL_FIELD: return (p[VB2_NV_OFFS_KERNEL] | (p[VB2_NV_OFFS_KERNEL + 1] << 8) | (p[VB2_NV_OFFS_KERNEL + 2] << 16) | (p[VB2_NV_OFFS_KERNEL + 3] << 24)); case VB2_NV_DEV_BOOT_USB: return GETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_USB); case VB2_NV_DEV_BOOT_LEGACY: return GETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_LEGACY); case VB2_NV_DEV_BOOT_SIGNED_ONLY: return GETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_SIGNED_ONLY); case VB2_NV_DISABLE_DEV_REQUEST: return GETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_DISABLE_DEV); case VB2_NV_OPROM_NEEDED: return GETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_OPROM_NEEDED); case VB2_NV_CLEAR_TPM_OWNER_REQUEST: return GETBIT(VB2_NV_OFFS_TPM, VB2_NV_TPM_CLEAR_OWNER_REQUEST); case VB2_NV_CLEAR_TPM_OWNER_DONE: return GETBIT(VB2_NV_OFFS_TPM, VB2_NV_TPM_CLEAR_OWNER_DONE); } /* * Put default return outside the switch() instead of in default:, so * that adding a new param will cause a compiler warning. */ return 0; } #undef GETBIT /* Macro for vb2_nv_set() single-bit settings to reduce duplicate code. */ #define SETBIT(offs, mask) \ { if (value) p[offs] |= mask; else p[offs] &= ~mask; } void vb2_nv_set(struct vb2_context *ctx, enum vb2_nv_param param, uint32_t value) { uint8_t *p = ctx->nvdata; /* If not changing the value, don't regenerate the CRC. */ if (vb2_nv_get(ctx, param) == value) return; /* * TODO: We could reduce the binary size for this code by #ifdef'ing * out the params not used by firmware verification. */ switch (param) { case VB2_NV_FIRMWARE_SETTINGS_RESET: SETBIT(VB2_NV_OFFS_HEADER, VB2_NV_HEADER_FW_SETTINGS_RESET); break; case VB2_NV_KERNEL_SETTINGS_RESET: SETBIT(VB2_NV_OFFS_HEADER, VB2_NV_HEADER_KERNEL_SETTINGS_RESET); break; case VB2_NV_DEBUG_RESET_MODE: SETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_DEBUG_RESET); break; case VB2_NV_TRY_NEXT: SETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_TRY_NEXT); break; case VB2_NV_TRY_COUNT: /* Clip to valid range. */ if (value > VB2_NV_BOOT_TRY_COUNT_MASK) value = VB2_NV_BOOT_TRY_COUNT_MASK; p[VB2_NV_OFFS_BOOT] &= ~VB2_NV_BOOT_TRY_COUNT_MASK; p[VB2_NV_OFFS_BOOT] |= (uint8_t)value; break; case VB2_NV_FW_TRIED: SETBIT(VB2_NV_OFFS_BOOT2, VB2_NV_BOOT2_TRIED); break; case VB2_NV_FW_RESULT: /* Map out of range values to unknown */ if (value > VB2_NV_BOOT2_RESULT_MASK) value = VB2_FW_RESULT_UNKNOWN; p[VB2_NV_OFFS_BOOT2] &= ~VB2_NV_BOOT2_RESULT_MASK; p[VB2_NV_OFFS_BOOT2] |= (uint8_t)value; break; case VB2_NV_RECOVERY_REQUEST: /* * Map values outside the valid range to the legacy reason, * since we can't determine if we're called from kernel or user * mode. */ if (value > 0xff) value = VB2_RECOVERY_LEGACY; p[VB2_NV_OFFS_RECOVERY] = (uint8_t)value; break; case VB2_NV_RECOVERY_SUBCODE: p[VB2_NV_OFFS_RECOVERY_SUBCODE] = (uint8_t)value; break; case VB2_NV_LOCALIZATION_INDEX: /* Map values outside the valid range to the default index. */ if (value > 0xFF) value = 0; p[VB2_NV_OFFS_LOCALIZATION] = (uint8_t)value; break; case VB2_NV_KERNEL_FIELD: p[VB2_NV_OFFS_KERNEL] = (uint8_t)(value); p[VB2_NV_OFFS_KERNEL + 1] = (uint8_t)(value >> 8); p[VB2_NV_OFFS_KERNEL + 2] = (uint8_t)(value >> 16); p[VB2_NV_OFFS_KERNEL + 3] = (uint8_t)(value >> 24); break; case VB2_NV_DEV_BOOT_USB: SETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_USB); break; case VB2_NV_DEV_BOOT_LEGACY: SETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_LEGACY); break; case VB2_NV_DEV_BOOT_SIGNED_ONLY: SETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_SIGNED_ONLY); break; case VB2_NV_DISABLE_DEV_REQUEST: SETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_DISABLE_DEV); break; case VB2_NV_OPROM_NEEDED: SETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_OPROM_NEEDED); break; case VB2_NV_CLEAR_TPM_OWNER_REQUEST: SETBIT(VB2_NV_OFFS_TPM, VB2_NV_TPM_CLEAR_OWNER_REQUEST); break; case VB2_NV_CLEAR_TPM_OWNER_DONE: SETBIT(VB2_NV_OFFS_TPM, VB2_NV_TPM_CLEAR_OWNER_DONE); break; } /* * Note there is no default case. This causes a compiler warning if * a new param is added to the enum without adding support here. */ /* Need to regenerate CRC, since the value changed. */ vb2_nv_regen_crc(ctx); } #undef SETBIT