summaryrefslogtreecommitdiff
path: root/firmware/2lib/2nvstorage.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/2lib/2nvstorage.c')
-rw-r--r--firmware/2lib/2nvstorage.c323
1 files changed, 323 insertions, 0 deletions
diff --git a/firmware/2lib/2nvstorage.c b/firmware/2lib/2nvstorage.c
new file mode 100644
index 00000000..3bfe151c
--- /dev/null
+++ b/firmware/2lib/2nvstorage.c
@@ -0,0 +1,323 @@
+/* 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_UNKNOWN;
+
+ /* Check CRC */
+ if (vb2_crc8(p, VB2_NV_OFFS_CRC) != p[VB2_NV_OFFS_CRC])
+ return VB2_ERROR_UNKNOWN;
+
+ 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