summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2014-05-14 11:37:52 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-06-05 23:14:27 +0000
commit3333e578497aafc4eb8c6e1e359f6e2b1dee633a (patch)
treef4c62ed293d9605559c09cb1b0607b4210a3d839
parente166d04e797b605dd2f6784bc863a262c418c0c4 (diff)
downloadvboot-3333e578497aafc4eb8c6e1e359f6e2b1dee633a.tar.gz
vboot2: Add nvstorage and secdata functions
This is the second of several CLs adding a more memory- and code-efficient firmware verification library. BUG=chromium:370082 BRANCH=none TEST=make clean && COV=1 make Change-Id: I1dd571e7511bff18469707d5a2e90068e68e0d6f Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/199841 Reviewed-by: Bill Richardson <wfrichar@chromium.org>
-rw-r--r--Makefile10
-rw-r--r--firmware/2lib/2crc8.c29
-rw-r--r--firmware/2lib/2misc.c39
-rw-r--r--firmware/2lib/2nvstorage.c323
-rw-r--r--firmware/2lib/2secdata.c115
-rw-r--r--firmware/2lib/include/2api.h146
-rw-r--r--firmware/2lib/include/2common.h3
-rw-r--r--firmware/2lib/include/2crc8.h20
-rw-r--r--firmware/2lib/include/2misc.h35
-rw-r--r--firmware/2lib/include/2nvstorage.h135
-rw-r--r--firmware/2lib/include/2recovery_reasons.h204
-rw-r--r--firmware/2lib/include/2secdata.h117
-rw-r--r--firmware/2lib/include/2struct.h383
-rw-r--r--tests/vb2_misc_tests.c54
-rw-r--r--tests/vb2_nvstorage_tests.c192
-rw-r--r--tests/vb2_secdata_tests.c103
16 files changed, 1907 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 33c74179..e2bd891d 100644
--- a/Makefile
+++ b/Makefile
@@ -280,7 +280,11 @@ VBSLK_SRCS = \
# Firmware library source needed for smaller library 2
FWLIB2_SRCS = \
firmware/2lib/2common.c \
+ firmware/2lib/2crc8.c \
+ firmware/2lib/2misc.c \
+ firmware/2lib/2nvstorage.c \
firmware/2lib/2rsa.c \
+ firmware/2lib/2secdata.c \
firmware/2lib/2sha1.c \
firmware/2lib/2sha256.c \
firmware/2lib/2sha512.c \
@@ -564,8 +568,11 @@ endif
ifneq (${VBOOT2},)
TEST_NAMES += \
tests/vb2_common_tests \
+ tests/vb2_misc_tests \
+ tests/vb2_nvstorage_tests \
tests/vb2_rsa_padding_tests \
tests/vb2_rsa_utility_tests \
+ tests/vb2_secdata_tests \
tests/vb2_sha_tests \
endif
@@ -1088,7 +1095,10 @@ runmisctests: test_setup
.PHONY: run2tests
run2tests: test_setup
${RUNTEST} ${BUILD_RUN}/tests/vb2_common_tests
+ ${RUNTEST} ${BUILD_RUN}/tests/vb2_misc_tests
+ ${RUNTEST} ${BUILD_RUN}/tests/vb2_nvstorage_tests
${RUNTEST} ${BUILD_RUN}/tests/vb2_rsa_utility_tests
+ ${RUNTEST} ${BUILD_RUN}/tests/vb2_secdata_tests
${RUNTEST} ${BUILD_RUN}/tests/vb2_sha_tests
.PHONY: runfutiltests
diff --git a/firmware/2lib/2crc8.c b/firmware/2lib/2crc8.c
new file mode 100644
index 00000000..9df0a00f
--- /dev/null
+++ b/firmware/2lib/2crc8.c
@@ -0,0 +1,29 @@
+/* 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.
+ */
+
+#include "2sysincludes.h"
+#include "2crc8.h"
+
+uint8_t vb2_crc8(const void *vptr, uint32_t size)
+{
+ const uint8_t *data = vptr;
+ unsigned crc = 0;
+ uint32_t i, j;
+
+ /*
+ * Calculate CRC-8 directly. A table-based algorithm would be faster,
+ * but for only a few bytes it isn't worth the code size.
+ */
+ for (j = size; j; j--, data++) {
+ crc ^= (*data << 8);
+ for(i = 8; i; i--) {
+ if (crc & 0x8000)
+ crc ^= (0x1070 << 3);
+ crc <<= 1;
+ }
+ }
+
+ return (uint8_t)(crc >> 8);
+}
diff --git a/firmware/2lib/2misc.c b/firmware/2lib/2misc.c
new file mode 100644
index 00000000..760d234d
--- /dev/null
+++ b/firmware/2lib/2misc.c
@@ -0,0 +1,39 @@
+/* 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 "2sysincludes.h"
+#include "2api.h"
+#include "2common.h"
+#include "2misc.h"
+#include "2nvstorage.h"
+#include "2secdata.h"
+#include "2sha.h"
+#include "2rsa.h"
+
+int vb2_init_context(struct vb2_context *ctx)
+{
+ struct vb2_shared_data *sd = vb2_get_sd(ctx);
+
+ /* Don't do anything if the context has already been initialized */
+ if (ctx->workbuf_used)
+ return VB2_SUCCESS;
+
+ /*
+ * Workbuf had better be big enough for our shared data struct and
+ * aligned. Not much we can do if it isn't; we'll die before we can
+ * store a recovery reason.
+ */
+ if (ctx->workbuf_size < sizeof(*sd))
+ return VB2_ERROR_WORKBUF_TOO_SMALL;
+ if (!vb_aligned(ctx->workbuf, sizeof(uint32_t)))
+ return VB2_ERROR_BUFFER_UNALIGNED;
+
+ /* Initialize the shared data at the start of the work buffer */
+ memset(sd, 0, sizeof(*sd));
+ ctx->workbuf_used = sizeof(*sd);
+ return VB2_SUCCESS;
+}
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
diff --git a/firmware/2lib/2secdata.c b/firmware/2lib/2secdata.c
new file mode 100644
index 00000000..668bc507
--- /dev/null
+++ b/firmware/2lib/2secdata.c
@@ -0,0 +1,115 @@
+/* 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.
+ *
+ * Secure storage APIs
+ */
+
+#include "2sysincludes.h"
+#include "2common.h"
+#include "2crc8.h"
+#include "2misc.h"
+#include "2secdata.h"
+
+int vb2_secdata_check_crc(const struct vb2_context *ctx)
+{
+ const struct vb2_secdata *sec =
+ (const struct vb2_secdata *)ctx->secdata;
+
+ /* Verify CRC */
+ if (sec->crc8 != vb2_crc8(sec, offsetof(struct vb2_secdata, crc8)))
+ return VB2_ERROR_BAD_SECDATA;
+
+ return VB2_SUCCESS;
+}
+
+int vb2_secdata_create(struct vb2_context *ctx)
+{
+ struct vb2_secdata *sec = (struct vb2_secdata *)ctx->secdata;
+
+ /* Clear the entire struct */
+ memset(sec, 0, sizeof(*sec));
+
+ /* Set to current version */
+ sec->struct_version = VB2_SECDATA_VERSION;
+
+ /* Calculate initial CRC */
+ sec->crc8 = vb2_crc8(sec, offsetof(struct vb2_secdata, crc8));
+ ctx->flags |= VB2_CONTEXT_SECDATA_CHANGED;
+ return VB2_SUCCESS;
+}
+
+int vb2_secdata_init(struct vb2_context *ctx)
+{
+ struct vb2_shared_data *sd = vb2_get_sd(ctx);
+ struct vb2_secdata *sec = (struct vb2_secdata *)ctx->secdata;
+ int rv;
+
+ /* Data must be new enough to have a CRC */
+ if (sec->struct_version < 2)
+ return VB2_ERROR_BAD_SECDATA;
+
+ rv = vb2_secdata_check_crc(ctx);
+ if (rv)
+ return rv;
+
+ /* Set status flag */
+ sd->status |= VB2_SD_STATUS_SECDATA_INIT;
+ // TODO: unit test for that
+
+ return VB2_SUCCESS;
+}
+
+int vb2_secdata_get(struct vb2_context *ctx,
+ enum vb2_secdata_param param,
+ uint32_t *dest)
+{
+ struct vb2_secdata *sec = (struct vb2_secdata *)ctx->secdata;
+
+ switch(param) {
+ case VB2_SECDATA_FLAGS:
+ *dest = sec->flags;
+ return VB2_SUCCESS;
+
+ case VB2_SECDATA_VERSIONS:
+ *dest = sec->fw_versions;
+ return VB2_SUCCESS;
+
+ default:
+ return VB2_ERROR_UNKNOWN;
+ }
+}
+
+int vb2_secdata_set(struct vb2_context *ctx,
+ enum vb2_secdata_param param,
+ uint32_t value)
+{
+ struct vb2_secdata *sec = (struct vb2_secdata *)ctx->secdata;
+ uint32_t now;
+
+ /* If not changing the value, don't regenerate the CRC. */
+ if (vb2_secdata_get(ctx, param, &now) == VB2_SUCCESS && now == value)
+ return VB2_SUCCESS;
+
+ switch(param) {
+ case VB2_SECDATA_FLAGS:
+ /* Make sure flags is in valid range */
+ if (value > 0xff)
+ return VB2_ERROR_UNKNOWN;
+
+ sec->flags = value;
+ break;
+
+ case VB2_SECDATA_VERSIONS:
+ sec->fw_versions = value;
+ break;
+
+ default:
+ return VB2_ERROR_UNKNOWN;
+ }
+
+ /* Regenerate CRC */
+ sec->crc8 = vb2_crc8(sec, offsetof(struct vb2_secdata, crc8));
+ ctx->flags |= VB2_CONTEXT_SECDATA_CHANGED;
+ return VB2_SUCCESS;
+}
diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h
new file mode 100644
index 00000000..3fb75895
--- /dev/null
+++ b/firmware/2lib/include/2api.h
@@ -0,0 +1,146 @@
+/* Copyright (c) 2013 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.
+ */
+
+/* APIs between calling firmware and vboot_reference
+ *
+ * General notes:
+ *
+ * TODO: split this file into a vboot_entry_points.h file which contains the
+ * entry points for the firmware to call vboot_reference, and a
+ * vboot_firmware_exports.h which contains the APIs to be implemented by the
+ * calling firmware and exported to vboot_reference.
+ *
+ * Notes:
+ * * Assumes this code is never called in the S3 resume path. TPM resume
+ * must be done elsewhere, and VB2_NV_DEBUG_RESET_MODE is ignored.
+ */
+
+#ifndef VBOOT_2_API_H_
+#define VBOOT_2_API_H_
+#include <stdint.h>
+
+#include "2recovery_reasons.h"
+#include "2return_codes.h"
+
+/* Size of non-volatile data used by vboot */
+#define VB2_NVDATA_SIZE 16
+
+/* Size of secure data used by vboot */
+#define VB2_SECDATA_SIZE 10
+
+/*
+ * Recommended size of work buffer.
+ *
+ * TODO: The recommended size really depends on which key algorithms are
+ * used. Should have a better / more accurate recommendation than this.
+ */
+#define VB2_WORKBUF_RECOMMENDED_SIZE (12 * 1024)
+
+/* Flags for vb2_context.
+ *
+ * Unless otherwise noted, flags are set by verified boot and may be read (but
+ * not set or cleared) by the caller.
+ */
+enum vb2_context_flags {
+
+ /*
+ * Verified boot has changed nvdata[]. Caller must save nvdata[] back
+ * to its underlying storage, then may clear this flag.
+ */
+ VB2_CONTEXT_NVDATA_CHANGED = (1 << 0),
+
+ /*
+ * Verified boot has changed secdata[]. Caller must save secdata[]
+ * back to its underlying storage, then may clear this flag.
+ */
+ VB2_CONTEXT_SECDATA_CHANGED = (1 << 1),
+
+ /* Recovery mode is requested this boot */
+ VB2_CONTEXT_RECOVERY_MODE = (1 << 2),
+
+ /* Developer mode is requested this boot */
+ VB2_CONTEXT_DEVELOPER_MODE = (1 << 3),
+
+ /*
+ * Force recovery mode due to physical user request. Caller may set
+ * this flag when initializing the context.
+ */
+ VB2_CONTEXT_FORCE_RECOVERY_MODE = (1 << 4),
+
+ /*
+ * Force developer mode enabled. Caller may set this flag when
+ * initializing the context.
+ */
+ VB2_CONTEXT_FORCE_DEVELOPER_MODE = (1 << 5),
+
+ /* Using firmware slot B. If this flag is clear, using slot A. */
+ VB2_CONTEXT_FW_SLOT_B = (1 << 6),
+
+ /* RAM should be cleared by caller this boot */
+ VB2_CONTEXT_CLEAR_RAM = (1 << 7),
+};
+
+/*
+ * Context for firmware verification. Pass this to all vboot APIs.
+ *
+ * Caller may relocate this between calls to vboot APIs.
+ */
+struct vb2_context {
+ /**********************************************************************
+ * Fields which must be initialized by caller.
+ */
+
+ /*
+ * Flags; see vb2_context_flags. Some flags may only be set by caller
+ * prior to calling vboot functions.
+ */
+ uint32_t flags;
+
+ /*
+ * Work buffer, and length in bytes. Caller may relocate this between
+ * calls to vboot APIs; it contains no internal pointers. Caller must
+ * not examine the contents of this work buffer directly.
+ */
+ uint8_t *workbuf;
+ uint32_t workbuf_size;
+
+ /*
+ * Non-volatile data. Caller must fill this from some non-volatile
+ * location. If the VB2_CONTEXT_NVDATA_CHANGED flag is set when a
+ * vb2api function returns, caller must save the data back to the
+ * non-volatile location and then clear the flag.
+ */
+ uint8_t nvdata[VB2_NVDATA_SIZE];
+
+ /*
+ * Secure data. Caller must fill this from some secure non-volatile
+ * location. If the VB2_CONTEXT_SECDATA_CHANGED flag is set when a
+ * function returns, caller must save the data back to the secure
+ * non-volatile location and then clear the flag.
+ */
+ uint8_t secdata[VB2_SECDATA_SIZE];
+
+ /*
+ * Context pointer for use by caller. Verified boot never looks at
+ * this. Put context here if you need it for APIs that verified boot
+ * may call (vb2ex_...() functions).
+ */
+ void *non_vboot_context;
+
+ /**********************************************************************
+ * Fields caller may examine after calling vb2api_fw_phase1(). Caller
+ * must set these fields to 0 before calling any vboot functions.
+ */
+
+ /*
+ * Amount of work buffer used so far. Verified boot sub-calls use
+ * this to know where the unused work area starts. Caller may use
+ * this between calls to vboot APIs to know how much data must be
+ * copied when relocating the work buffer.
+ */
+ uint32_t workbuf_used;
+};
+
+#endif /* VBOOT_2_API_H_ */
diff --git a/firmware/2lib/include/2common.h b/firmware/2lib/include/2common.h
index 3724354b..3e70e800 100644
--- a/firmware/2lib/include/2common.h
+++ b/firmware/2lib/include/2common.h
@@ -9,6 +9,7 @@
#define VBOOT_REFERENCE_VBOOT_2COMMON_H_
#include "2return_codes.h"
+#include "2struct.h"
struct vb2_public_key;
@@ -92,7 +93,7 @@ void *vb2_workbuf_realloc(struct vb2_workbuf *wb,
void vb2_workbuf_free(struct vb2_workbuf *wb, uint32_t size);
/* Check if a pointer is aligned on an align-byte boundary */
-#define vb_aligned(ptr, align) (!(((size_t)(ptr)) & ((align) - 1)))
+#define vb_aligned(ptr, align) (!(((uintptr_t)(ptr)) & ((align) - 1)))
/**
* Align a buffer and check its size.
diff --git a/firmware/2lib/include/2crc8.h b/firmware/2lib/include/2crc8.h
new file mode 100644
index 00000000..f01eabd9
--- /dev/null
+++ b/firmware/2lib/include/2crc8.h
@@ -0,0 +1,20 @@
+/* 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.
+ *
+ * Very simple 8-bit CRC function.
+ */
+
+#ifndef VBOOT_REFERENCE_2_CRC8_H_
+#define VBOOT_REFERENCE_2_CRC8_H_
+
+/**
+ * Calculate CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial.
+ *
+ * @param data Data to CRC
+ * @param size Size of data in bytes
+ * @return CRC-8 of the data.
+ */
+uint8_t vb2_crc8(const void *data, uint32_t size);
+
+#endif /* VBOOT_REFERENCE_2_CRC8_H_ */
diff --git a/firmware/2lib/include/2misc.h b/firmware/2lib/include/2misc.h
new file mode 100644
index 00000000..bf707ed9
--- /dev/null
+++ b/firmware/2lib/include/2misc.h
@@ -0,0 +1,35 @@
+/* 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
+ */
+
+#ifndef VBOOT_REFERENCE_VBOOT_2MISC_H_
+#define VBOOT_REFERENCE_VBOOT_2MISC_H_
+
+#include "2api.h"
+
+/**
+ * Get the shared data pointer from the vboot context
+ *
+ * @param ctx Vboot context
+ * @return The shared data pointer.
+ */
+static __inline struct vb2_shared_data *vb2_get_sd(struct vb2_context *ctx) {
+ return (struct vb2_shared_data *)ctx->workbuf;
+}
+
+/**
+ * Set up the verified boot context data, if not already set up.
+ *
+ * This uses ctx->workbuf_used=0 as a flag to indicate that the data has not
+ * yet been set up. Caller must set that before calling any voot functions;
+ * see 2api.h.
+ *
+ * @param ctx Vboot context to initialize
+ * @return VB2_SUCCESS, or error code on error.
+ */
+int vb2_init_context(struct vb2_context *ctx);
+
+#endif /* VBOOT_REFERENCE_VBOOT_2MISC_H_ */
diff --git a/firmware/2lib/include/2nvstorage.h b/firmware/2lib/include/2nvstorage.h
new file mode 100644
index 00000000..ec775699
--- /dev/null
+++ b/firmware/2lib/include/2nvstorage.h
@@ -0,0 +1,135 @@
+/* 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
+ */
+
+#ifndef VBOOT_REFERENCE_VBOOT_2NVSTORAGE_H_
+#define VBOOT_REFERENCE_VBOOT_2NVSTORAGE_H_
+
+enum vb2_nv_param {
+ /*
+ * Parameter values have been reset to defaults (flag for firmware).
+ * 0=clear; 1=set.
+ */
+ VB2_NV_FIRMWARE_SETTINGS_RESET = 0,
+ /*
+ * Parameter values have been reset to defaults (flag for kernel).
+ * 0=clear; 1=set.
+ */
+ VB2_NV_KERNEL_SETTINGS_RESET,
+ /* Request debug reset on next S3->S0 transition. 0=clear; 1=set. */
+ VB2_NV_DEBUG_RESET_MODE,
+ /* Firmware slot to try next. 0=A, 1=B */
+ VB2_NV_TRY_NEXT,
+ /*
+ * Number of times to try booting RW firmware slot B before slot A.
+ * Valid range: 0-15.
+ *
+ * For VB2, number of times to try booting the slot indicated by
+ * VB2_NV_TRY_NEXT. On a 1->0 transition of try count, VB2_NV_TRY_NEXT
+ * will be set to the other slot.
+ */
+ VB2_NV_TRY_COUNT,
+ /*
+ * Request recovery mode on next boot; see 2recovery_reason.h for
+ * currently defined reason codes. 8-bit value.
+ */
+ VB2_NV_RECOVERY_REQUEST,
+ /*
+ * Localization index for screen bitmaps displayed by firmware.
+ * 8-bit value.
+ */
+ VB2_NV_LOCALIZATION_INDEX,
+ /* Field reserved for kernel/user-mode use; 32-bit value. */
+ VB2_NV_KERNEL_FIELD,
+ /* Allow booting from USB in developer mode. 0=no, 1=yes. */
+ VB2_NV_DEV_BOOT_USB,
+ /* Allow booting of legacy OSes in developer mode. 0=no, 1=yes. */
+ VB2_NV_DEV_BOOT_LEGACY,
+ /* Only boot Google-signed images in developer mode. 0=no, 1=yes. */
+ VB2_NV_DEV_BOOT_SIGNED_ONLY,
+ /*
+ * Set by userspace to request that RO firmware disable dev-mode on the
+ * next boot. This is likely only possible if the dev-switch is
+ * virtual.
+ */
+ VB2_NV_DISABLE_DEV_REQUEST,
+ /*
+ * Set and cleared by vboot to request that the video Option ROM be
+ * loaded at boot time, so that BIOS screens can be displayed. 0=no,
+ * 1=yes.
+ */
+ VB2_NV_OPROM_NEEDED,
+ /* Request that the firmware clear the TPM owner on the next boot. */
+ VB2_NV_CLEAR_TPM_OWNER_REQUEST,
+ /* Flag that TPM owner was cleared on request. */
+ VB2_NV_CLEAR_TPM_OWNER_DONE,
+ /* More details on recovery reason */
+ VB2_NV_RECOVERY_SUBCODE,
+ /* Firmware slot tried this boot (0=A, 1=B) */
+ VB2_NV_FW_TRIED,
+ /* Result of trying that firmware (see vb2_fw_result) */
+ VB2_NV_FW_RESULT,
+};
+
+/* Result of trying the firmware in VB2_NV_FW_TRIED */
+enum vb2_fw_result {
+ /* Unknown */
+ VB2_FW_RESULT_UNKNOWN = 0,
+
+ /* Trying a new slot, but haven't reached success/failure */
+ VB2_FW_RESULT_TRYING = 1,
+
+ /* Successfully booted to the OS */
+ VB2_FW_RESULT_SUCCESS = 2,
+
+ /* Known failure */
+ VB2_FW_RESULT_FAILURE = 3,
+};
+
+/**
+ * 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);
+
+/**
+ * Initialize the non-volatile storage context and verify its CRC.
+ *
+ * @param ctx Context pointer
+ */
+void vb2_nv_init(struct vb2_context *ctx);
+
+/**
+ * Read a non-volatile value.
+ *
+ * @param ctx Context pointer
+ * @param param Parameter to read
+ * @return The value of the parameter. If you somehow force an invalid
+ * parameter number, returns 0.
+ */
+uint32_t vb2_nv_get(struct vb2_context *ctx, enum vb2_nv_param param);
+
+/**
+ * Write a non-volatile value.
+ *
+ * Ignores writes to unknown params.
+ *
+ * @param ctx Context pointer
+ * @param param Parameter to write
+ * @param value New value
+ */
+void vb2_nv_set(struct vb2_context *ctx,
+ enum vb2_nv_param param,
+ uint32_t value);
+
+#endif /* VBOOT_REFERENCE_VBOOT_2NVSTORAGE_H_ */
diff --git a/firmware/2lib/include/2recovery_reasons.h b/firmware/2lib/include/2recovery_reasons.h
new file mode 100644
index 00000000..c305dd62
--- /dev/null
+++ b/firmware/2lib/include/2recovery_reasons.h
@@ -0,0 +1,204 @@
+/* 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.
+ *
+ * Recovery reasons
+ */
+
+#ifndef VBOOT_REFERENCE_VBOOT_2RECOVERY_REASONS_H_
+#define VBOOT_REFERENCE_VBOOT_2RECOVERY_REASONS_H_
+
+/* Recovery reason codes */
+enum vb2_nv_recovery {
+ /* Recovery not requested. */
+ VB2_RECOVERY_NOT_REQUESTED = 0x00,
+
+ /*
+ * Recovery requested from legacy utility. (Prior to the NV storage
+ * spec, recovery mode was a single bitfield; this value is reserved so
+ * that scripts which wrote 1 to the recovery field are distinguishable
+ * from scripts whch use the recovery reasons listed here.
+ */
+ VB2_RECOVERY_LEGACY = 0x01,
+
+ /* User manually requested recovery via recovery button */
+ VB2_RECOVERY_RO_MANUAL = 0x02,
+
+ /*
+ * RW firmware failed signature check (neither RW firmware slot was
+ * valid)
+ */
+ VB2_RECOVERY_RO_INVALID_RW = 0x03,
+
+ /* S3 resume failed */
+ VB2_RECOVERY_RO_S3_RESUME = 0x04,
+
+ /* TPM error in read-only firmware (deprecated) */
+ VB2_RECOVERY_DEP_RO_TPM_ERROR = 0x05,
+
+ /* Shared data error in read-only firmware */
+ VB2_RECOVERY_RO_SHARED_DATA = 0x06,
+
+ /* Test error from S3Resume() */
+ VB2_RECOVERY_RO_TEST_S3 = 0x07,
+
+ /* Test error from LoadFirmwareSetup() (deprecated) */
+ VB2_RECOVERY_RO_TEST_LFS = 0x08,
+
+ /* Test error from LoadFirmware() (deprecated) */
+ VB2_RECOVERY_RO_TEST_LF = 0x09,
+
+ /*
+ * RW firmware failed signature check (neither RW firmware slot was
+ * valid). Recovery reason is VB2_RECOVERY_RO_INVALID_RW_CHECK_MIN +
+ * the check value for the slot which came closest to validating; see
+ * VBSD_LF_CHECK_* in vboot_struct.h.
+ */
+ // TODO: pass back those codes from vboot2?
+ VB2_RECOVERY_RO_INVALID_RW_CHECK_MIN = 0x10,
+ VB2_RECOVERY_RO_INVALID_RW_CHECK_MAX = 0x1F,
+
+ /*
+ * Firmware boot failure outside of verified boot (RAM init, missing
+ * SSD, etc.).
+ */
+ VB2_RECOVERY_RO_FIRMWARE = 0x20,
+
+ /*
+ * Recovery mode TPM initialization requires a system reboot. The
+ * system was already in recovery mode for some other reason when this
+ * happened.
+ */
+ VB2_RECOVERY_RO_TPM_REBOOT = 0x21,
+
+ /* EC software sync - other error */
+ VB2_RECOVERY_EC_SOFTWARE_SYNC = 0x22,
+
+ /* EC software sync - unable to determine active EC image */
+ VB2_RECOVERY_EC_UNKNOWN_IMAGE = 0x23,
+
+ /* EC software sync - error obtaining EC image hash (deprecated) */
+ VB2_RECOVERY_DEP_EC_HASH = 0x24,
+
+ /* EC software sync - error obtaining expected EC image */
+ VB2_RECOVERY_EC_EXPECTED_IMAGE = 0x25,
+
+ /* EC software sync - error updating EC */
+ VB2_RECOVERY_EC_UPDATE = 0x26,
+
+ /* EC software sync - unable to jump to EC-RW */
+ VB2_RECOVERY_EC_JUMP_RW = 0x27,
+
+ /* EC software sync - unable to protect / unprotect EC-RW */
+ VB2_RECOVERY_EC_PROTECT = 0x28,
+
+ /* EC software sync - error obtaining expected EC hash */
+ VB2_RECOVERY_EC_EXPECTED_HASH = 0x29,
+
+ /* EC software sync - expected EC image doesn't match hash */
+ VB2_RECOVERY_EC_HASH_MISMATCH = 0x2a,
+
+ /* New error codes from VB2 */
+ // TODO: may need to add strings for these in the original fwlib
+
+ /* Secure data inititalization error */
+ VB2_RECOVERY_SECDATA_INIT = 0x2b,
+
+ /* GBB header is bad */
+ VB2_RECOVERY_GBB_HEADER = 0x2c,
+
+ /* Unable to clear TPM owner */
+ VB2_RECOVERY_TPM_CLEAR_OWNER = 0x2d,
+
+ /* Error determining/updating virtual dev switch */
+ VB2_RECOVERY_DEV_SWITCH = 0x2e,
+
+ /* Error determining firmware slot */
+ VB2_RECOVERY_FW_SLOT = 0x2f,
+
+ /* Unspecified/unknown error in read-only firmware */
+ VB2_RECOVERY_RO_UNSPECIFIED = 0x3f,
+
+ /*
+ * User manually requested recovery by pressing a key at developer
+ * warning screen
+ */
+ VB2_RECOVERY_RW_DEV_SCREEN = 0x41,
+
+ /* No OS kernel detected */
+ VB2_RECOVERY_RW_NO_OS = 0x42,
+
+ /* OS kernel failed signature check */
+ VB2_RECOVERY_RW_INVALID_OS = 0x43,
+
+ /* TPM error in rewritable firmware (deprecated) */
+ VB2_RECOVERY_DEP_RW_TPM_ERROR = 0x44,
+
+ /* RW firmware in dev mode, but dev switch is off */
+ VB2_RECOVERY_RW_DEV_MISMATCH = 0x45,
+
+ /* Shared data error in rewritable firmware */
+ VB2_RECOVERY_RW_SHARED_DATA = 0x46,
+
+ /* Test error from LoadKernel() */
+ VB2_RECOVERY_RW_TEST_LK = 0x47,
+
+ /* No bootable disk found (deprecated)*/
+ VB2_RECOVERY_DEP_RW_NO_DISK = 0x48,
+
+ /* Rebooting did not correct TPM_E_FAIL or TPM_E_FAILEDSELFTEST */
+ VB2_RECOVERY_TPM_E_FAIL = 0x49,
+
+ /* TPM setup error in read-only firmware */
+ VB2_RECOVERY_RO_TPM_S_ERROR = 0x50,
+
+ /* TPM write error in read-only firmware */
+ VB2_RECOVERY_RO_TPM_W_ERROR = 0x51,
+
+ /* TPM lock error in read-only firmware */
+ VB2_RECOVERY_RO_TPM_L_ERROR = 0x52,
+
+ /* TPM update error in read-only firmware */
+ VB2_RECOVERY_RO_TPM_U_ERROR = 0x53,
+
+ /* TPM read error in rewritable firmware */
+ VB2_RECOVERY_RW_TPM_R_ERROR = 0x54,
+
+ /* TPM write error in rewritable firmware */
+ VB2_RECOVERY_RW_TPM_W_ERROR = 0x55,
+
+ /* TPM lock error in rewritable firmware */
+ VB2_RECOVERY_RW_TPM_L_ERROR = 0x56,
+
+ /* EC software sync unable to get EC image hash */
+ VB2_RECOVERY_EC_HASH_FAILED = 0x57,
+
+ /* EC software sync invalid image hash size */
+ VB2_RECOVERY_EC_HASH_SIZE = 0x58,
+
+ /* Unspecified error while trying to load kernel */
+ VB2_RECOVERY_LK_UNSPECIFIED = 0x59,
+
+ /* No bootable storage device in system */
+ VB2_RECOVERY_RW_NO_DISK = 0x5a,
+
+ /* No bootable kernel found on disk */
+ VB2_RECOVERY_RW_NO_KERNEL = 0x5b,
+
+ /* Unspecified/unknown error in rewritable firmware */
+ VB2_RECOVERY_RW_UNSPECIFIED = 0x7f,
+
+ /* DM-verity error */
+ VB2_RECOVERY_KE_DM_VERITY = 0x81,
+
+ /* Unspecified/unknown error in kernel */
+ VB2_RECOVERY_KE_UNSPECIFIED = 0xbf,
+
+ /* Recovery mode test from user-mode */
+ VB2_RECOVERY_US_TEST = 0xc1,
+
+ /* Unspecified/unknown error in user-mode */
+ VB2_RECOVERY_US_UNSPECIFIED = 0xff,
+};
+
+#endif /* VBOOT_REFERENCE_VBOOT_2RECOVERY_REASONS_H_ */
diff --git a/firmware/2lib/include/2secdata.h b/firmware/2lib/include/2secdata.h
new file mode 100644
index 00000000..25a051d9
--- /dev/null
+++ b/firmware/2lib/include/2secdata.h
@@ -0,0 +1,117 @@
+/* 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.
+ *
+ * Secure non-volatile storage routines
+ */
+
+#ifndef VBOOT_REFERENCE_VBOOT_SECDATA_H_
+#define VBOOT_REFERENCE_VBOOT_SECDATA_H_
+
+/* Expected value of vb2_secdata.version */
+#define VB2_SECDATA_VERSION 2
+
+/* Flags for firmware space */
+enum vb2_secdata_flags {
+ /*
+ * Last boot was developer mode. TPM ownership is cleared when
+ * transitioning to/from developer mode. Set/cleared by
+ * vb2_check_dev_switch().
+ */
+ VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER = (1 << 0),
+
+ /*
+ * Virtual developer mode switch is on. Set/cleared by the
+ * keyboard-controlled dev screens in recovery mode. Cleared by
+ * vb2_check_dev_switch().
+ */
+ VB2_SECDATA_FLAG_DEV_MODE = (1 << 1),
+};
+
+/* Secure data area */
+struct vb2_secdata {
+ /* Struct version, for backwards compatibility */
+ uint8_t struct_version;
+
+ /* Flags; see vb2_secdata_flags */
+ uint8_t flags;
+
+ /* Firmware versions */
+ uint32_t fw_versions;
+
+ /* Reserved for future expansion */
+ uint8_t reserved[3];
+
+ /* CRC; must be last field in struct */
+ uint8_t crc8;
+} __attribute__((packed));
+
+/* Which param to get/set for vb2_secdata_get() / vb2_secdata_set() */
+enum vb2_secdata_param {
+ /* Flags; see vb2_secdata_flags */
+ VB2_SECDATA_FLAGS = 0,
+
+ /* Firmware versions */
+ VB2_SECDATA_VERSIONS,
+};
+
+/**
+ * Check the CRC of the secure storage context.
+ *
+ * Use this if reading from secure 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_secdata_check_crc(const struct vb2_context *ctx);
+
+/**
+ * Create fresh data in the secure storage context.
+ *
+ * Use this only when initializing the secure storage context on a new machine
+ * the first time it boots. Do NOT simply use this if vb2_secdata_check_crc()
+ * (or any other API in this library) fails; that could allow the secure data
+ * to be rolled back to an insecure state.
+ *
+ * This may be called before vb2_context_init().
+ */
+int vb2_secdata_create(struct vb2_context *ctx);
+
+/**
+ * Initialize the secure storage context and verify its CRC.
+ *
+ * This must be called before vb2_secdata_get() or vb2_secdata_set().
+ *
+ * @param ctx Context pointer
+ * @return VB2_SUCCESS, or non-zero error code if error.
+ */
+int vb2_secdata_init(struct vb2_context *ctx);
+
+/**
+ * Read a secure storage value.
+ *
+ * @param ctx Context pointer
+ * @param param Parameter to read
+ * @param dest Destination for value
+ * @return VB2_SUCCESS, or non-zero error code if error.
+ */
+int vb2_secdata_get(struct vb2_context *ctx,
+ enum vb2_secdata_param param,
+ uint32_t *dest);
+
+/**
+ * Write a secure storage value.
+ *
+ * @param ctx Context pointer
+ * @param param Parameter to write
+ * @param value New value
+ * @return VB2_SUCCESS, or non-zero error code if error.
+ */
+int vb2_secdata_set(struct vb2_context *ctx,
+ enum vb2_secdata_param param,
+ uint32_t value);
+
+#endif /* VBOOT_REFERENCE_VBOOT_2SECDATA_H_ */
diff --git a/firmware/2lib/include/2struct.h b/firmware/2lib/include/2struct.h
new file mode 100644
index 00000000..7a6d0ce7
--- /dev/null
+++ b/firmware/2lib/include/2struct.h
@@ -0,0 +1,383 @@
+/* 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.
+ *
+ * Data structure definitions for verified boot, for on-disk / in-eeprom
+ * data.
+ */
+
+#ifndef VBOOT_REFERENCE_VBOOT_2STRUCT_H_
+#define VBOOT_REFERENCE_VBOOT_2STRUCT_H_
+#include <stdint.h>
+
+/*
+ * Note: Many of the structs have pairs of 32-bit fields and reserved fields.
+ * This is to be backwards-compatible with older verified boot data which used
+ * 64-bit fields (when we thought that hey, UEFI is 64-bit so all our fields
+ * should be too).
+ */
+
+/* Packed public key data */
+struct vb2_packed_key {
+ /* Offset of key data from start of this struct */
+ uint32_t key_offset;
+ uint32_t reserved0;
+
+ /* Size of key data in bytes (NOT strength of key in bits) */
+ uint32_t key_size;
+ uint32_t reserved1;
+
+ /* Signature algorithm used by the key */
+ uint32_t algorithm;
+ uint32_t reserved2;
+
+ /* Key version */
+ uint32_t key_version;
+ uint32_t reserved3;
+
+ /* TODO: when redoing this struct, add a text description of the key */
+} __attribute__((packed));
+
+#define EXPECTED_VBPUBLICKEY_SIZE 32
+
+/* Signature data (a secure hash, possibly signed) */
+struct vb2_signature {
+ /* Offset of signature data from start of this struct */
+ uint32_t sig_offset;
+ uint32_t reserved0;
+
+ /* Size of signature data in bytes */
+ uint32_t sig_size;
+ uint32_t reserved1;
+
+ /* Size of the data block which was signed in bytes */
+ uint32_t data_size;
+ uint32_t reserved2;
+
+ /*
+ * TODO: when redoing this struct, add a text description of the
+ * signature (including what key was used) and an algorithm type field.
+ */
+} __attribute__((packed));
+
+#define EXPECTED_VBSIGNATURE_SIZE 24
+
+#define KEY_BLOCK_MAGIC "CHROMEOS"
+#define KEY_BLOCK_MAGIC_SIZE 8
+
+#define KEY_BLOCK_HEADER_VERSION_MAJOR 2
+#define KEY_BLOCK_HEADER_VERSION_MINOR 1
+
+/*
+ * The following flags set where the key is valid. Not used by firmware
+ * verification; only kernel verification.
+ */
+#define VB2_KEY_BLOCK_FLAG_DEVELOPER_0 0x01 /* Developer switch off */
+#define VB2_KEY_BLOCK_FLAG_DEVELOPER_1 0x02 /* Developer switch on */
+#define VB2_KEY_BLOCK_FLAG_RECOVERY_0 0x04 /* Not recovery mode */
+#define VB2_KEY_BLOCK_FLAG_RECOVERY_1 0x08 /* Recovery mode */
+
+/*
+ * Key block, containing the public key used to sign some other chunk of data.
+ *
+ * This should be followed by:
+ * 1) The data_key key data, pointed to by data_key.key_offset.
+ * 2) The checksum data for (vb2_keyblock + data_key data), pointed to
+ * by keyblock_checksum.sig_offset.
+ * 3) The signature data for (vb2_keyblock + data_key data), pointed to
+ * by keyblock_signature.sig_offset.
+ */
+struct vb2_keyblock {
+ /* Magic number */
+ uint8_t magic[KEY_BLOCK_MAGIC_SIZE];
+
+ /* Version of this header format */
+ uint32_t header_version_major;
+
+ /* Version of this header format */
+ uint32_t header_version_minor;
+
+ /*
+ * Length of this entire key block, including keys, signatures, and
+ * padding, in bytes
+ */
+ uint32_t keyblock_size;
+ uint32_t reserved0;
+
+ /*
+ * Signature for this key block (header + data pointed to by data_key)
+ * For use with signed data keys
+ */
+ struct vb2_signature keyblock_signature;
+
+ /*
+ * SHA-512 checksum for this key block (header + data pointed to by
+ * data_key) For use with unsigned data keys.
+ *
+ * Note that the vb2 lib currently only supports signed blocks.
+ */
+ struct vb2_signature keyblock_checksum_unused;
+
+ /* Flags for key (VB2_KEY_BLOCK_FLAG_*) */
+ uint32_t keyblock_flags;
+ uint32_t reserved1;
+
+ /* Key to verify the chunk of data */
+ struct vb2_packed_key data_key;
+} __attribute__((packed));
+
+#define EXPECTED_VB2KEYBLOCKHEADER_SIZE 112
+
+/****************************************************************************/
+
+/* Firmware preamble header */
+#define FIRMWARE_PREAMBLE_HEADER_VERSION_MAJOR 2
+#define FIRMWARE_PREAMBLE_HEADER_VERSION_MINOR 1
+
+/* Flags for VbFirmwarePreambleHeader.flags */
+/* Reserved; do not use */
+#define VB2_FIRMWARE_PREAMBLE_RESERVED0 0x00000001
+
+/* Premable block for rewritable firmware, version 2.1.
+ *
+ * The firmware preamble header should be followed by:
+ * 1) The kernel_subkey key data, pointed to by kernel_subkey.key_offset.
+ * 2) The signature data for the firmware body, pointed to by
+ * body_signature.sig_offset.
+ * 3) The signature data for (header + kernel_subkey data + body signature
+ * data), pointed to by preamble_signature.sig_offset.
+ */
+struct vb2_fw_preamble {
+ /*
+ * Size of this preamble, including keys, signatures, and padding, in
+ * bytes
+ */
+ uint32_t preamble_size;
+ uint32_t reserved0;
+
+ /*
+ * Signature for this preamble (header + kernel subkey + body
+ * signature)
+ */
+ struct vb2_signature preamble_signature;
+
+ /* Version of this header format */
+ uint32_t header_version_major;
+ uint32_t header_version_minor;
+
+ /* Firmware version */
+ uint32_t firmware_version;
+ uint32_t reserved1;
+
+ /* Key to verify kernel key block */
+ struct vb2_packed_key kernel_subkey;
+
+ /* Signature for the firmware body */
+ struct vb2_signature body_signature;
+
+ /*
+ * Fields added in header version 2.1. You must verify the header
+ * version before reading these fields!
+ */
+
+ /*
+ * Flags; see VB2_FIRMWARE_PREAMBLE_*. Readers should return 0 for
+ * header version < 2.1.
+ */
+ uint32_t flags;
+} __attribute__((packed));
+
+#define EXPECTED_VB2FIRMWAREPREAMBLEHEADER2_1_SIZE 108
+
+/****************************************************************************/
+
+/* Flags for vb2_shared_data.flags */
+enum vb2_shared_data_flags {
+ /* User has explicitly and physically requested recovery */
+ VB2_SD_FLAG_MANUAL_RECOVERY = (1 << 0),
+
+ /* Developer mode is enabled */
+ VB2_SD_DEV_MODE_ENABLED = (1 << 1),
+
+ /*
+ * TODO: might be nice to add flags for why dev mode is enabled - via
+ * gbb, virtual dev switch, or forced on for testing.
+ */
+};
+
+/* Flags for vb2_shared_data.status */
+enum vb2_shared_data_status {
+ /* Reinitialized NV data due to invalid checksum */
+ VB2_SD_STATUS_NV_REINIT = (1 << 0),
+
+ /* NV data has been initialized */
+ VB2_SD_STATUS_NV_INIT = (1 << 1),
+
+ /* Secure data initialized */
+ VB2_SD_STATUS_SECDATA_INIT = (1 << 2),
+
+ /* Chose a firmware slot */
+ VB2_SD_STATUS_CHOSE_SLOT = (1 << 3),
+};
+
+/*
+ * Data shared between vboot API calls. Stored at the start of the work
+ * buffer.
+ */
+struct vb2_shared_data {
+ /* Flags; see enum vb2_shared_data_flags */
+ uint32_t flags;
+
+ /* Flags from GBB header */
+ uint32_t gbb_flags;
+
+ /* Reason we are in recovery mode this boot, or 0 if we aren't */
+ uint32_t recovery_reason;
+
+ /* Firmware slot used last boot (0=A, 1=B) */
+ uint32_t last_fw_slot;
+
+ /* Result of last boot (enum vb2_fw_result) */
+ uint32_t last_fw_result;
+
+ /* Firmware slot used this boot */
+ uint32_t fw_slot;
+
+ /*
+ * Version for this slot (top 16 bits = key, lower 16 bits = firmware).
+ *
+ * TODO: Make this a union to allow getting/setting those versions
+ * separately?
+ */
+ uint32_t fw_version;
+
+ /*
+ * Status flags for this boot; see enum vb2_shared_data_status. Status
+ * is "what we've done"; flags above are "decisions we've made".
+ */
+ uint32_t status;
+
+ /**********************************************************************
+ * Temporary variables used during firmware verification. These don't
+ * really need to persist through to the OS, but there's nowhere else
+ * we can put them.
+ */
+
+ /* Root key offset and size from GBB header */
+ uint32_t gbb_rootkey_offset;
+ uint32_t gbb_rootkey_size;
+
+ /* Offset of preamble from start of vblock */
+ uint32_t vblock_preamble_offset;
+
+ /*
+ * Offset and size of packed data key in work buffer. Size is 0 if
+ * data key is not stored in the work buffer.
+ */
+ uint32_t workbuf_data_key_offset;
+ uint32_t workbuf_data_key_size;
+
+ /*
+ * Offset and size of firmware preamble in work buffer. Size if 0 if
+ * preamble is not stored in the work buffer.
+ */
+ uint32_t workbuf_preamble_offset;
+ uint32_t workbuf_preamble_size;
+
+ /*
+ * Offset and size of hash context in work buffer. Size if 0 if
+ * hash context is not stored in the work buffer.
+ */
+ uint32_t workbuf_hash_offset;
+ uint32_t workbuf_hash_size;
+
+ /* Current tag we're hashing */
+ uint32_t hash_tag;
+
+ /* Amount of data we still expect to hash */
+ uint32_t hash_remaining_size;
+
+} __attribute__((packed));
+
+/****************************************************************************/
+
+/* Signature at start of the GBB */
+#define VB2_GBB_SIGNATURE "$GBB"
+#define VB2_GBB_SIGNATURE_SIZE 4
+
+/* VB2 GBB struct version */
+#define VB2_GBB_MAJOR_VER 1
+#define VB2_GBB_MINOR_VER 1
+
+/* Flags for vb2_gbb_header.flags */
+enum vb2_gbb_flag {
+ /*
+ * Reduce the dev screen delay to 2 sec from 30 sec to speed up
+ * factory.
+ */
+ VB2_GBB_FLAG_DEV_SCREEN_SHORT_DELAY = (1 << 0),
+
+ /*
+ * BIOS should load option ROMs from arbitrary PCI devices. We'll never
+ * enable this ourselves because it executes non-verified code, but if
+ * a customer wants to void their warranty and set this flag in the
+ * read-only flash, they should be able to do so.
+ */
+ VB2_GBB_FLAG_LOAD_OPTION_ROMS = (1 << 1),
+
+ /*
+ * The factory flow may need the BIOS to boot a non-ChromeOS kernel if
+ * the dev-switch is on. This flag allows that.
+ */
+ VB2_GBB_FLAG_ENABLE_ALTERNATE_OS = (1 << 2),
+
+ /*
+ * Force dev switch on, regardless of physical/keyboard dev switch
+ * position.
+ */
+ VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON = (1 << 3),
+
+ /* Allow booting from USB in dev mode even if dev_boot_usb=0. */
+ VB2_GBB_FLAG_FORCE_DEV_BOOT_USB = (1 << 4),
+
+ /* Disable firmware rollback protection. */
+ VB2_GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK = (1 << 5),
+
+ /* Allow Enter key to trigger dev->tonorm screen transition */
+ VB2_GBB_FLAG_ENTER_TRIGGERS_TONORM = (1 << 6),
+
+ /* Allow booting Legacy OSes in dev mode even if dev_boot_legacy=0. */
+ VB2_GBB_FLAG_FORCE_DEV_BOOT_LEGACY = (1 << 7),
+
+ /* Allow booting using alternate keys for FAFT servo testing */
+ VB2_GBB_FLAG_FAFT_KEY_OVERIDE = (1 << 8),
+
+ /* Disable EC software sync */
+ VB2_GBB_FLAG_DISABLE_EC_SOFTWARE_SYNC = (1 << 9),
+
+ /* Default to booting legacy OS when dev screen times out */
+ VB2_GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY = (1 << 10),
+};
+
+struct vb2_gbb_header {
+ /* Fields present in version 1.1 */
+ uint8_t signature[VB2_GBB_SIGNATURE_SIZE]; /* VB2_GBB_SIGNATURE */
+ uint16_t major_version; /* See VB2_GBB_MAJOR_VER */
+ uint16_t minor_version; /* See VB2_GBB_MINOR_VER */
+ uint32_t header_size; /* Size of GBB header in bytes */
+ uint32_t flags; /* Flags (see enum vb2_gbb_flag) */
+
+ /* Offsets (from start of header) and sizes (in bytes) of components */
+ uint32_t hwid_offset; /* HWID */
+ uint32_t hwid_size;
+ uint32_t rootkey_offset; /* Root key */
+ uint32_t rootkey_size;
+ uint32_t bmpfv_offset; /* BMP FV */
+ uint32_t bmpfv_size;
+ uint32_t recovery_key_offset; /* Recovery key */
+ uint32_t recovery_key_size;
+
+ uint8_t pad[80]; /* To match GBB_HEADER_SIZE. Initialize to 0. */
+} __attribute__((packed));
+
+#endif /* VBOOT_REFERENCE_VBOOT_2STRUCT_H_ */
diff --git a/tests/vb2_misc_tests.c b/tests/vb2_misc_tests.c
new file mode 100644
index 00000000..64d00cac
--- /dev/null
+++ b/tests/vb2_misc_tests.c
@@ -0,0 +1,54 @@
+/* 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.
+ *
+ * Tests for misc library
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "test_common.h"
+#include "vboot_common.h"
+
+#include "2api.h"
+#include "2common.h"
+#include "2misc.h"
+
+static void misc_test(void)
+{
+ uint8_t workbuf[VB2_WORKBUF_RECOMMENDED_SIZE];
+
+ struct vb2_context c = {
+ .workbuf = workbuf,
+ .workbuf_size = sizeof(workbuf),
+ };
+
+ TEST_EQ(vb2_init_context(&c), 0, "Init context good");
+ TEST_EQ(c.workbuf_used, sizeof(struct vb2_shared_data),
+ "Init vbsd");
+
+ /* Don't re-init if used is non-zero */
+ c.workbuf_used = 200;
+ TEST_EQ(vb2_init_context(&c), 0, "Re-init context good");
+ TEST_EQ(c.workbuf_used, 200, "Didn't re-init");
+
+ /* Handle workbuf errors */
+ c.workbuf_used = 0;
+ c.workbuf_size = sizeof(struct vb2_shared_data) - 1;
+ TEST_NEQ(vb2_init_context(&c), 0, "Init too small");
+ c.workbuf_size = sizeof(workbuf);
+
+ /* Handle workbuf unaligned */
+ c.workbuf++;
+ TEST_NEQ(vb2_init_context(&c), 0, "Init unaligned");
+}
+
+int main(int argc, char* argv[])
+{
+ misc_test();
+
+ return gTestSuccess ? 0 : 255;
+}
diff --git a/tests/vb2_nvstorage_tests.c b/tests/vb2_nvstorage_tests.c
new file mode 100644
index 00000000..061f8691
--- /dev/null
+++ b/tests/vb2_nvstorage_tests.c
@@ -0,0 +1,192 @@
+/* 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.
+ *
+ * Tests for firmware NV storage library.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "test_common.h"
+#include "vboot_common.h"
+
+#include "2api.h"
+#include "2common.h"
+#include "2misc.h"
+#include "2nvstorage.h"
+
+/* Single NV storage field to test */
+struct nv_field {
+ enum vb2_nv_param param; /* Parameter index */
+ uint32_t default_value; /* Expected default value */
+ uint32_t test_value; /* Value to test writing */
+ uint32_t test_value2; /* Second value to test writing */
+ char *desc; /* Field description */
+};
+
+/* Array of fields to test, terminated with a field with desc==NULL. */
+static struct nv_field nvfields[] = {
+ {VB2_NV_DEBUG_RESET_MODE, 0, 1, 0, "debug reset mode"},
+ {VB2_NV_TRY_NEXT, 0, 1, 0, "try next"},
+ {VB2_NV_TRY_COUNT, 0, 6, 15, "try B count"},
+ {VB2_NV_FW_TRIED, 0, 1, 0, "firmware tried"},
+ {VB2_NV_FW_RESULT, 0, 1, 2, "firmware result"},
+ {VB2_NV_RECOVERY_REQUEST, 0, 0x42, 0xED, "recovery request"},
+ {VB2_NV_RECOVERY_SUBCODE, 0, 0x56, 0xAC, "recovery subcode"},
+ {VB2_NV_LOCALIZATION_INDEX, 0, 0x69, 0xB0, "localization index"},
+ {VB2_NV_KERNEL_FIELD, 0, 0x12345678, 0xFEDCBA98, "kernel field"},
+ {VB2_NV_DEV_BOOT_USB, 0, 1, 0, "dev boot usb"},
+ {VB2_NV_DEV_BOOT_LEGACY, 0, 1, 0, "dev boot legacy"},
+ {VB2_NV_DEV_BOOT_SIGNED_ONLY, 0, 1, 0, "dev boot custom"},
+ {VB2_NV_DISABLE_DEV_REQUEST, 0, 1, 0, "disable dev request"},
+ {VB2_NV_CLEAR_TPM_OWNER_REQUEST, 0, 1, 0, "clear tpm owner request"},
+ {VB2_NV_CLEAR_TPM_OWNER_DONE, 0, 1, 0, "clear tpm owner done"},
+ {VB2_NV_OPROM_NEEDED, 0, 1, 0, "oprom needed"},
+ {0, 0, 0, 0, NULL}
+};
+
+static void test_changed(struct vb2_context *ctx, int changed, const char *why)
+{
+ if (changed)
+ TEST_NEQ(ctx->flags & VB2_CONTEXT_NVDATA_CHANGED, 0, why);
+ else
+ TEST_EQ(ctx->flags & VB2_CONTEXT_NVDATA_CHANGED, 0, why);
+};
+
+static void nv_storage_test(void)
+{
+ struct nv_field *vnf;
+ uint8_t goodcrc;
+ uint8_t workbuf[VB2_WORKBUF_RECOMMENDED_SIZE];
+ struct vb2_context c = {
+ .flags = 0,
+ .workbuf = workbuf,
+ .workbuf_size = sizeof(workbuf),
+ };
+ struct vb2_shared_data *sd = vb2_get_sd(&c);
+
+ memset(c.nvdata, 0xA6, sizeof(c.nvdata));
+ vb2_init_context(&c);
+
+ /* Init with invalid data should set defaults and regenerate CRC */
+ vb2_nv_init(&c);
+ TEST_EQ(c.nvdata[0], 0x70, "vb2_nv_init() reset header byte");
+ TEST_NEQ(c.nvdata[15], 0, "vb2_nv_init() CRC");
+ TEST_EQ(sd->status, VB2_SD_STATUS_NV_INIT | VB2_SD_STATUS_NV_REINIT,
+ "vb2_nv_init() status changed");
+ test_changed(&c, 1, "vb2_nv_init() reset changed");
+ goodcrc = c.nvdata[15];
+
+ /* Another init should not cause further changes */
+ c.flags = 0;
+ sd->status = 0;
+ vb2_nv_init(&c);
+ test_changed(&c, 0, "vb2_nv_init() didn't re-reset");
+ TEST_EQ(c.nvdata[15], goodcrc, "vb2_nv_init() CRC same");
+ TEST_EQ(sd->status, VB2_SD_STATUS_NV_INIT, "vb2_nv_init() status same");
+
+ /* Perturbing the header should force defaults */
+ c.nvdata[0] ^= 0x40;
+ vb2_nv_init(&c);
+ TEST_EQ(c.nvdata[0], 0x70, "vb2_nv_init() reset header byte again");
+ test_changed(&c, 1, "vb2_nv_init() corrupt changed");
+ TEST_EQ(c.nvdata[15], goodcrc, "vb2_nv_init() CRC same again");
+
+ /* So should perturbing some other byte */
+ TEST_EQ(c.nvdata[11], 0, "Kernel byte starts at 0");
+ c.nvdata[11] = 12;
+ vb2_nv_init(&c);
+ TEST_EQ(c.nvdata[11], 0, "vb2_nv_init() reset kernel byte");
+ test_changed(&c, 1, "vb2_nv_init() corrupt elsewhere changed");
+ TEST_EQ(c.nvdata[15], goodcrc, "vb2_nv_init() CRC same again");
+
+ /* Clear the kernel and firmware flags */
+ vb2_nv_init(&c);
+ TEST_EQ(vb2_nv_get(&c, VB2_NV_FIRMWARE_SETTINGS_RESET),
+ 1, "Firmware settings are reset");
+ vb2_nv_set(&c, VB2_NV_FIRMWARE_SETTINGS_RESET, 0);
+ TEST_EQ(vb2_nv_get(&c, VB2_NV_FIRMWARE_SETTINGS_RESET),
+ 0, "Firmware settings are clear");
+
+ TEST_EQ(vb2_nv_get(&c, VB2_NV_KERNEL_SETTINGS_RESET),
+ 1, "Kernel settings are reset");
+ vb2_nv_set(&c, VB2_NV_KERNEL_SETTINGS_RESET, 0);
+ TEST_EQ(vb2_nv_get(&c, VB2_NV_KERNEL_SETTINGS_RESET),
+ 0, "Kernel settings are clear");
+
+ TEST_EQ(c.nvdata[0], 0x40, "Header byte now just has the header bit");
+ /* That should have changed the CRC */
+ TEST_NEQ(c.nvdata[15], goodcrc,
+ "vb2_nv_init() CRC changed due to flags clear");
+
+ /* Test explicitly setting the reset flags again */
+ vb2_nv_init(&c);
+ vb2_nv_set(&c, VB2_NV_FIRMWARE_SETTINGS_RESET, 1);
+ TEST_EQ(vb2_nv_get(&c, VB2_NV_FIRMWARE_SETTINGS_RESET),
+ 1, "Firmware settings forced reset");
+ vb2_nv_set(&c, VB2_NV_FIRMWARE_SETTINGS_RESET, 0);
+
+ vb2_nv_set(&c, VB2_NV_KERNEL_SETTINGS_RESET, 1);
+ TEST_EQ(vb2_nv_get(&c, VB2_NV_KERNEL_SETTINGS_RESET),
+ 1, "Kernel settings forced reset");
+ vb2_nv_set(&c, VB2_NV_KERNEL_SETTINGS_RESET, 0);
+
+ /* Get/set an invalid field */
+ vb2_nv_init(&c);
+ vb2_nv_set(&c, -1, 1);
+ TEST_EQ(vb2_nv_get(&c, -1), 0, "Get invalid setting");
+
+ /* Test other fields */
+ vb2_nv_init(&c);
+ for (vnf = nvfields; vnf->desc; vnf++) {
+ TEST_EQ(vb2_nv_get(&c, vnf->param), vnf->default_value,
+ vnf->desc);
+ vb2_nv_set(&c, vnf->param, vnf->test_value);
+ TEST_EQ(vb2_nv_get(&c, vnf->param), vnf->test_value, vnf->desc);
+ vb2_nv_set(&c, vnf->param, vnf->test_value2);
+ TEST_EQ(vb2_nv_get(&c, vnf->param), vnf->test_value2,
+ vnf->desc);
+ }
+
+ /* None of those changes should have caused a reset to defaults */
+ vb2_nv_init(&c);
+ TEST_EQ(vb2_nv_get(&c, VB2_NV_FIRMWARE_SETTINGS_RESET),
+ 0, "Firmware settings are still clear");
+ TEST_EQ(vb2_nv_get(&c, VB2_NV_KERNEL_SETTINGS_RESET),
+ 0, "Kernel settings are still clear");
+
+ /* Writing identical settings doesn't cause the CRC to regenerate */
+ c.flags = 0;
+ vb2_nv_init(&c);
+ test_changed(&c, 0, "No regen CRC on open");
+ for (vnf = nvfields; vnf->desc; vnf++)
+ vb2_nv_set(&c, vnf->param, vnf->test_value2);
+ test_changed(&c, 0, "No regen CRC if data not changed");
+
+ /* Test out-of-range fields mapping to defaults or failing */
+ vb2_nv_init(&c);
+ vb2_nv_set(&c, VB2_NV_TRY_COUNT, 16);
+ TEST_EQ(vb2_nv_get(&c, VB2_NV_TRY_COUNT),
+ 15, "Try b count out of range");
+ vb2_nv_set(&c, VB2_NV_RECOVERY_REQUEST, 0x101);
+ TEST_EQ(vb2_nv_get(&c, VB2_NV_RECOVERY_REQUEST),
+ VB2_RECOVERY_LEGACY, "Recovery request out of range");
+ vb2_nv_set(&c, VB2_NV_LOCALIZATION_INDEX, 0x102);
+ TEST_EQ(vb2_nv_get(&c, VB2_NV_LOCALIZATION_INDEX),
+ 0, "Localization index out of range");
+
+ vb2_nv_set(&c, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN + 1);
+ vb2_nv_set(&c, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN + 100);
+ TEST_EQ(vb2_nv_get(&c, VB2_NV_FW_RESULT),
+ VB2_FW_RESULT_UNKNOWN, "Firmware result out of range");
+}
+
+int main(int argc, char* argv[])
+{
+ nv_storage_test();
+
+ return gTestSuccess ? 0 : 255;
+}
diff --git a/tests/vb2_secdata_tests.c b/tests/vb2_secdata_tests.c
new file mode 100644
index 00000000..3451b324
--- /dev/null
+++ b/tests/vb2_secdata_tests.c
@@ -0,0 +1,103 @@
+/* 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.
+ *
+ * Tests for firmware secure storage library.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "test_common.h"
+#include "vboot_common.h"
+
+#include "2common.h"
+#include "2api.h"
+#include "2secdata.h"
+
+static void test_changed(struct vb2_context *ctx, int changed, const char *why)
+{
+ if (changed)
+ TEST_NEQ(ctx->flags & VB2_CONTEXT_SECDATA_CHANGED, 0, why);
+ else
+ TEST_EQ(ctx->flags & VB2_CONTEXT_SECDATA_CHANGED, 0, why);
+
+ ctx->flags &= ~VB2_CONTEXT_SECDATA_CHANGED;
+};
+
+static void secdata_test(void)
+{
+ uint8_t workbuf[VB2_WORKBUF_RECOMMENDED_SIZE];
+ struct vb2_context c = {
+ .flags = 0,
+ .workbuf = workbuf,
+ .workbuf_size = sizeof(workbuf),
+ };
+ struct vb2_secdata *s = (struct vb2_secdata *)c.secdata;
+ uint32_t v = 1;
+
+ /* Blank data is invalid */
+ memset(c.secdata, 0xa6, sizeof(c.secdata));
+ TEST_NEQ(vb2_secdata_check_crc(&c), 0, "Check blank CRC");
+ TEST_NEQ(vb2_secdata_init(&c), 0, "Init blank CRC");
+
+ /* Create good data */
+ TEST_EQ(vb2_secdata_create(&c), 0, "Create");
+ TEST_EQ(vb2_secdata_check_crc(&c), 0, "Check created CRC");
+ TEST_EQ(vb2_secdata_init(&c), 0, "Init created CRC");
+ test_changed(&c, 1, "Create changes data");
+
+ /* Now corrupt it */
+ c.secdata[2]++;
+ TEST_NEQ(vb2_secdata_check_crc(&c), 0, "Check invalid CRC");
+ TEST_NEQ(vb2_secdata_init(&c), 0, "Init invalid CRC");
+
+ /* Version 1 didn't have a CRC, so init should reject it */
+ vb2_secdata_create(&c);
+ s->struct_version = 1;
+ TEST_NEQ(vb2_secdata_init(&c), 0, "Init old version");
+
+ vb2_secdata_create(&c);
+ c.flags = 0;
+
+ /* Read/write flags */
+ TEST_EQ(vb2_secdata_get(&c, VB2_SECDATA_FLAGS, &v), 0, "Get flags");
+ TEST_EQ(v, 0, "Flags created 0");
+ test_changed(&c, 0, "Get doesn't change data");
+ TEST_EQ(vb2_secdata_set(&c, VB2_SECDATA_FLAGS, 0x12), 0, "Set flags");
+ test_changed(&c, 1, "Set changes data");
+ TEST_EQ(vb2_secdata_set(&c, VB2_SECDATA_FLAGS, 0x12), 0, "Set flags 2");
+ test_changed(&c, 0, "Set again doesn't change data");
+ TEST_EQ(vb2_secdata_get(&c, VB2_SECDATA_FLAGS, &v), 0, "Get flags 2");
+ TEST_EQ(v, 0x12, "Flags changed");
+ TEST_NEQ(vb2_secdata_set(&c, VB2_SECDATA_FLAGS, 0x100), 0, "Bad flags");
+
+ /* Read/write versions */
+ TEST_EQ(vb2_secdata_get(&c, VB2_SECDATA_VERSIONS, &v),
+ 0, "Get versions");
+ TEST_EQ(v, 0, "Versions created 0");
+ test_changed(&c, 0, "Get doesn't change data");
+ TEST_EQ(vb2_secdata_set(&c, VB2_SECDATA_VERSIONS, 0x123456ff),
+ 0, "Set versions");
+ test_changed(&c, 1, "Set changes data");
+ TEST_EQ(vb2_secdata_set(&c, VB2_SECDATA_VERSIONS, 0x123456ff),
+ 0, "Set versions 2");
+ test_changed(&c, 0, "Set again doesn't change data");
+ TEST_EQ(vb2_secdata_get(&c, VB2_SECDATA_VERSIONS, &v), 0,
+ "Get versions 2");
+ TEST_EQ(v, 0x123456ff, "Versions changed");
+
+ /* Invalid field fails */
+ TEST_NEQ(vb2_secdata_get(&c, -1, &v), 0, "Get invalid");
+ TEST_NEQ(vb2_secdata_set(&c, -1, 456), 0, "Set invalid");
+ test_changed(&c, 0, "Set invalid field doesn't change data");
+}
+
+int main(int argc, char* argv[])
+{
+ secdata_test();
+
+ return gTestSuccess ? 0 : 255;
+}