From 3333e578497aafc4eb8c6e1e359f6e2b1dee633a Mon Sep 17 00:00:00 2001 From: Randall Spangler Date: Wed, 14 May 2014 11:37:52 -0700 Subject: 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 Reviewed-on: https://chromium-review.googlesource.com/199841 Reviewed-by: Bill Richardson --- Makefile | 10 + firmware/2lib/2crc8.c | 29 +++ firmware/2lib/2misc.c | 39 +++ firmware/2lib/2nvstorage.c | 323 +++++++++++++++++++++++++ firmware/2lib/2secdata.c | 115 +++++++++ firmware/2lib/include/2api.h | 146 ++++++++++++ firmware/2lib/include/2common.h | 3 +- firmware/2lib/include/2crc8.h | 20 ++ firmware/2lib/include/2misc.h | 35 +++ firmware/2lib/include/2nvstorage.h | 135 +++++++++++ firmware/2lib/include/2recovery_reasons.h | 204 ++++++++++++++++ firmware/2lib/include/2secdata.h | 117 +++++++++ firmware/2lib/include/2struct.h | 383 ++++++++++++++++++++++++++++++ tests/vb2_misc_tests.c | 54 +++++ tests/vb2_nvstorage_tests.c | 192 +++++++++++++++ tests/vb2_secdata_tests.c | 103 ++++++++ 16 files changed, 1907 insertions(+), 1 deletion(-) create mode 100644 firmware/2lib/2crc8.c create mode 100644 firmware/2lib/2misc.c create mode 100644 firmware/2lib/2nvstorage.c create mode 100644 firmware/2lib/2secdata.c create mode 100644 firmware/2lib/include/2api.h create mode 100644 firmware/2lib/include/2crc8.h create mode 100644 firmware/2lib/include/2misc.h create mode 100644 firmware/2lib/include/2nvstorage.h create mode 100644 firmware/2lib/include/2recovery_reasons.h create mode 100644 firmware/2lib/include/2secdata.h create mode 100644 firmware/2lib/include/2struct.h create mode 100644 tests/vb2_misc_tests.c create mode 100644 tests/vb2_nvstorage_tests.c create mode 100644 tests/vb2_secdata_tests.c 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 + +#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 + +/* + * 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 +#include +#include +#include + +#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 +#include +#include +#include + +#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 +#include +#include +#include + +#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; +} -- cgit v1.2.1