blob: be635825d4a59665abd1c86cab740b921ffafd18 [file] [log] [blame]
/* 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