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>
This commit is contained in:
Randall Spangler
2014-05-14 11:37:52 -07:00
committed by chrome-internal-fetch
parent e166d04e79
commit 3333e57849
16 changed files with 1907 additions and 1 deletions

View File

@@ -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

29
firmware/2lib/2crc8.c Normal file
View File

@@ -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);
}

39
firmware/2lib/2misc.c Normal file
View File

@@ -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;
}

323
firmware/2lib/2nvstorage.c Normal file
View File

@@ -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

115
firmware/2lib/2secdata.c Normal file
View File

@@ -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;
}

View File

@@ -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_ */

View File

@@ -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.

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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_ */

54
tests/vb2_misc_tests.c Normal file
View File

@@ -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;
}

192
tests/vb2_nvstorage_tests.c Normal file
View File

@@ -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;
}

103
tests/vb2_secdata_tests.c Normal file
View File

@@ -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;
}