mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-10 09:31:51 +00:00
bdb: Add NVM library
This patch adds NVM library, which verifies, updates, and syncs NVM-RW of vboot SoC. BUG=chrome-os-partner:51907 BRANCH=tot TEST=make runtests Change-Id: I5adc399f9e582bd9ea7d9ee73482ed9a924837e0 Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/343121 Reviewed-by: Daisuke Nojiri <dnojiri@google.com>
This commit is contained in:
committed by
chrome-bot
parent
a6ec557609
commit
6b5c4e961f
5
Makefile
5
Makefile
@@ -377,7 +377,8 @@ BDBLIB_SRCS = \
|
||||
firmware/bdb/bdb.c \
|
||||
firmware/bdb/misc.c \
|
||||
firmware/bdb/rsa.c \
|
||||
firmware/bdb/stub.c
|
||||
firmware/bdb/stub.c \
|
||||
firmware/bdb/nvm.c
|
||||
|
||||
# Support real TPM unless BIOS sets MOCK_TPM
|
||||
ifeq (${MOCK_TPM},)
|
||||
@@ -1195,7 +1196,7 @@ ${TEST21_BINS}: LIBS += ${UTILLIB21}
|
||||
|
||||
${TESTBDB_BINS}: ${FWLIB2X} ${UTILBDB}
|
||||
${TESTBDB_BINS}: INCLUDES += -Ifirmware/bdb
|
||||
${TESTBDB_BINS}: LIBS += ${FWLIB2X} ${UTILBDB_OBJS} ${BDBLIB_OBJS}
|
||||
${TESTBDB_BINS}: LIBS += ${UTILBDB_OBJS} ${BDBLIB_OBJS} ${FWLIB2X}
|
||||
|
||||
${TESTLIB}: ${TESTLIB_OBJS}
|
||||
@${PRINTF} " RM $(subst ${BUILD}/,,$@)\n"
|
||||
|
||||
@@ -37,6 +37,9 @@ enum bdb_return_code {
|
||||
* fully verified. */
|
||||
BDB_GOOD_OTHER_THAN_KEY = 1,
|
||||
|
||||
/* Function is not implemented, thus supposed to be not called */
|
||||
BDB_ERROR_NOT_IMPLEMENTED,
|
||||
|
||||
/* Other errors */
|
||||
BDB_ERROR_UNKNOWN = 100,
|
||||
|
||||
@@ -72,6 +75,19 @@ enum bdb_return_code {
|
||||
/* Errors in vba_bdb_init */
|
||||
BDB_ERROR_TRY_OTHER_SLOT,
|
||||
BDB_ERROR_RECOVERY_REQUEST,
|
||||
|
||||
BDB_ERROR_NVM_INIT,
|
||||
BDB_ERROR_NVM_WRITE,
|
||||
BDB_ERROR_NVM_RW_HMAC,
|
||||
BDB_ERROR_NVM_RW_INVALID_HMAC,
|
||||
BDB_ERROR_NVM_INVALID_PARAMETER,
|
||||
BDB_ERROR_NVM_INVALID_SECRET,
|
||||
BDB_ERROR_NVM_RW_MAGIC,
|
||||
BDB_ERROR_NVM_STRUCT_SIZE,
|
||||
BDB_ERROR_NVM_WRITE_VERIFY,
|
||||
BDB_ERROR_NVM_STRUCT_VERSION,
|
||||
BDB_ERROR_NVM_VBE_READ,
|
||||
BDB_ERROR_NVM_RW_BUFFER_SMALL,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
@@ -8,10 +8,22 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include "vboot_register.h"
|
||||
#include "nvm.h"
|
||||
#include "secrets.h"
|
||||
|
||||
struct vba_context {
|
||||
/* Indicate which slot is being tried: 0 - primary, 1 - secondary */
|
||||
uint8_t slot;
|
||||
|
||||
/* BDB */
|
||||
uint8_t *bdb;
|
||||
|
||||
/* Secrets */
|
||||
struct bdb_ro_secrets *ro_secrets;
|
||||
struct bdb_rw_secrets *rw_secrets;
|
||||
|
||||
/* NVM-RW buffer */
|
||||
struct nvmrw nvmrw;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -37,6 +49,24 @@ int vba_bdb_finalize(struct vba_context *ctx);
|
||||
*/
|
||||
void vba_bdb_fail(struct vba_context *ctx);
|
||||
|
||||
/**
|
||||
* Update kernel and its data key version in NVM
|
||||
*
|
||||
* This is the function called from SP-RW, which receives a kernel version
|
||||
* from an AP-RW after successful verification of a kernel.
|
||||
*
|
||||
* It checks whether the version in NVM-RW is older than the reported version
|
||||
* or not. If so, it updates the version in NVM-RW.
|
||||
*
|
||||
* @param ctx
|
||||
* @param kernel_data_key_version
|
||||
* @param kernel_version
|
||||
* @return BDB_SUCCESS or BDB_ERROR_*
|
||||
*/
|
||||
int vba_update_kernel_version(struct vba_context *ctx,
|
||||
uint32_t kernel_data_key_version,
|
||||
uint32_t kernel_version);
|
||||
|
||||
/**
|
||||
* Get vboot register value
|
||||
*
|
||||
@@ -65,4 +95,28 @@ void vbe_set_vboot_register(enum vboot_register type, uint32_t val);
|
||||
*/
|
||||
void vbe_reset(void);
|
||||
|
||||
/**
|
||||
* Read contents from Non-Volatile Memory
|
||||
*
|
||||
* Implemented by each chip.
|
||||
*
|
||||
* @param type Type of NVM
|
||||
* @param buf Buffer where the data will be read to
|
||||
* @param size Size of data to read
|
||||
* @return Zero if success or non-zero otherwise
|
||||
*/
|
||||
int vbe_read_nvm(enum nvm_type type, uint8_t *buf, uint32_t size);
|
||||
|
||||
/**
|
||||
* Write contents to Non-Volatile Memory
|
||||
*
|
||||
* Implemented by each chip.
|
||||
*
|
||||
* @param type Type of NVM
|
||||
* @param buf Buffer where the data will be written from
|
||||
* @param size Size of data to write
|
||||
* @return Zero if success or non-zero otherwise
|
||||
*/
|
||||
int vbe_write_nvm(enum nvm_type type, void *buf, uint32_t size);
|
||||
|
||||
#endif
|
||||
|
||||
231
firmware/bdb/nvm.c
Normal file
231
firmware/bdb/nvm.c
Normal file
@@ -0,0 +1,231 @@
|
||||
/* Copyright 2016 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 "2hmac.h"
|
||||
#include "2sha.h"
|
||||
#include "bdb_api.h"
|
||||
#include "bdb_struct.h"
|
||||
#include "bdb.h"
|
||||
#include "nvm.h"
|
||||
#include "secrets.h"
|
||||
|
||||
static int nvmrw_validate(const void *buf, uint32_t size)
|
||||
{
|
||||
const struct nvmrw *nvm = buf;
|
||||
|
||||
if (nvm->struct_magic != NVM_RW_MAGIC)
|
||||
return BDB_ERROR_NVM_RW_MAGIC;
|
||||
|
||||
if (nvm->struct_major_version != NVM_HEADER_VERSION_MAJOR)
|
||||
return BDB_ERROR_NVM_STRUCT_VERSION;
|
||||
|
||||
if (size < nvm->struct_size)
|
||||
return BDB_ERROR_NVM_STRUCT_SIZE;
|
||||
|
||||
/*
|
||||
* We allow any sizes between min and max so that we can handle minor
|
||||
* version mismatches. Reader can be older than data or the other way
|
||||
* around. FW in slot B can upgrade NVM-RW but fails to qualify as a
|
||||
* stable boot path. Then, FW in slot A is invoked which is older than
|
||||
* the NVM-RW written by FW in slot B.
|
||||
*/
|
||||
if (nvm->struct_size < NVM_RW_MIN_STRUCT_SIZE ||
|
||||
NVM_RW_MAX_STRUCT_SIZE < nvm->struct_size)
|
||||
return BDB_ERROR_NVM_STRUCT_SIZE;
|
||||
|
||||
return BDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int nvmrw_verify(const struct bdb_ro_secrets *secrets,
|
||||
const struct nvmrw *nvm, uint32_t size)
|
||||
{
|
||||
uint8_t mac[NVM_HMAC_SIZE];
|
||||
int rv;
|
||||
|
||||
if (!secrets || !nvm)
|
||||
return BDB_ERROR_NVM_INVALID_PARAMETER;
|
||||
|
||||
rv = nvmrw_validate(nvm, size);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
/* Compute and verify HMAC */
|
||||
if (hmac(VB2_HASH_SHA256, secrets->nvm_rw, BDB_SECRET_SIZE,
|
||||
nvm, nvm->struct_size - sizeof(mac), mac, sizeof(mac)))
|
||||
return BDB_ERROR_NVM_RW_HMAC;
|
||||
/* TODO: Use safe_memcmp */
|
||||
if (memcmp(mac, nvm->hmac, sizeof(mac)))
|
||||
return BDB_ERROR_NVM_RW_INVALID_HMAC;
|
||||
|
||||
return BDB_SUCCESS;
|
||||
}
|
||||
|
||||
int nvmrw_write(struct vba_context *ctx, enum nvm_type type)
|
||||
{
|
||||
struct nvmrw *nvm = &ctx->nvmrw;
|
||||
int retry = NVM_MAX_WRITE_RETRY;
|
||||
int rv;
|
||||
|
||||
if (!ctx)
|
||||
return BDB_ERROR_NVM_INVALID_PARAMETER;
|
||||
|
||||
if (!ctx->ro_secrets)
|
||||
return BDB_ERROR_NVM_INVALID_SECRET;
|
||||
|
||||
rv = nvmrw_validate(nvm, sizeof(*nvm));
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
/* Update HMAC */
|
||||
hmac(VB2_HASH_SHA256, ctx->ro_secrets->nvm_rw, BDB_SECRET_SIZE,
|
||||
nvm, nvm->struct_size - sizeof(nvm->hmac),
|
||||
nvm->hmac, sizeof(nvm->hmac));
|
||||
|
||||
while (retry--) {
|
||||
uint8_t buf[sizeof(struct nvmrw)];
|
||||
if (vbe_write_nvm(type, nvm, nvm->struct_size))
|
||||
continue;
|
||||
if (vbe_read_nvm(type, buf, sizeof(buf)))
|
||||
continue;
|
||||
if (memcmp(buf, nvm, sizeof(buf)))
|
||||
continue;
|
||||
/* Write success */
|
||||
return BDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* NVM seems corrupted. Go to chip recovery mode */
|
||||
return BDB_ERROR_NVM_WRITE;
|
||||
}
|
||||
|
||||
static int read_verify_nvmrw(enum nvm_type type,
|
||||
const struct bdb_ro_secrets *secrets,
|
||||
uint8_t *buf, uint32_t buf_size)
|
||||
{
|
||||
struct nvmrw *nvm = (struct nvmrw *)buf;
|
||||
int rv;
|
||||
|
||||
/* Read minimum amount */
|
||||
if (vbe_read_nvm(type, buf, NVM_MIN_STRUCT_SIZE))
|
||||
return BDB_ERROR_NVM_VBE_READ;
|
||||
|
||||
/* Validate the content */
|
||||
rv = nvmrw_validate(buf, buf_size);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
/* Read full body */
|
||||
if (vbe_read_nvm(type, buf, nvm->struct_size))
|
||||
return BDB_ERROR_NVM_VBE_READ;
|
||||
|
||||
/* Verify the content */
|
||||
rv = nvmrw_verify(secrets, nvm, sizeof(*nvm));
|
||||
return rv;
|
||||
|
||||
return BDB_SUCCESS;
|
||||
}
|
||||
|
||||
int nvmrw_read(struct vba_context *ctx)
|
||||
{
|
||||
uint8_t buf1[NVM_RW_MAX_STRUCT_SIZE];
|
||||
uint8_t buf2[NVM_RW_MAX_STRUCT_SIZE];
|
||||
struct nvmrw *nvm1 = (struct nvmrw *)buf1;
|
||||
struct nvmrw *nvm2 = (struct nvmrw *)buf2;
|
||||
int rv1, rv2;
|
||||
|
||||
/* Read and verify the 1st copy */
|
||||
rv1 = read_verify_nvmrw(NVM_TYPE_RW_PRIMARY, ctx->ro_secrets,
|
||||
buf1, sizeof(buf1));
|
||||
|
||||
/* Read and verify the 2nd copy */
|
||||
rv2 = read_verify_nvmrw(NVM_TYPE_RW_SECONDARY, ctx->ro_secrets,
|
||||
buf2, sizeof(buf2));
|
||||
|
||||
if (rv1 == BDB_SUCCESS && rv2 == BDB_SUCCESS) {
|
||||
/* Sync primary and secondary based on update_count. */
|
||||
if (nvm1->update_count > nvm2->update_count)
|
||||
rv2 = !BDB_SUCCESS;
|
||||
else if (nvm1->update_count < nvm2->update_count)
|
||||
rv1 = !BDB_SUCCESS;
|
||||
} else if (rv1 != BDB_SUCCESS && rv2 != BDB_SUCCESS){
|
||||
/* Abort. Neither was successful. */
|
||||
return rv1;
|
||||
}
|
||||
|
||||
if (rv1 == BDB_SUCCESS)
|
||||
/* both copies are good. use primary copy */
|
||||
memcpy(&ctx->nvmrw, buf1, sizeof(ctx->nvmrw));
|
||||
else
|
||||
/* primary is bad but secondary is good. */
|
||||
memcpy(&ctx->nvmrw, buf2, sizeof(ctx->nvmrw));
|
||||
|
||||
if (ctx->nvmrw.struct_minor_version != NVM_HEADER_VERSION_MINOR) {
|
||||
/*
|
||||
* Upgrade or downgrade is required. So, we need to write both.
|
||||
* When upgrading, this is the place where new fields should be
|
||||
* initialized. We don't increment update_count.
|
||||
*/
|
||||
ctx->nvmrw.struct_minor_version = NVM_HEADER_VERSION_MINOR;
|
||||
ctx->nvmrw.struct_size = sizeof(ctx->nvmrw);
|
||||
/* We don't worry about calculating hmac twice because
|
||||
* this is a corner case. */
|
||||
rv1 = nvmrw_write(ctx, NVM_TYPE_RW_PRIMARY);
|
||||
rv2 = nvmrw_write(ctx, NVM_TYPE_RW_SECONDARY);
|
||||
} else if (rv1 != BDB_SUCCESS) {
|
||||
/* primary copy is bad. sync it with secondary copy */
|
||||
rv1 = nvmrw_write(ctx, NVM_TYPE_RW_PRIMARY);
|
||||
} else if (rv2 != BDB_SUCCESS){
|
||||
/* secondary copy is bad. sync it with primary copy */
|
||||
rv2 = nvmrw_write(ctx, NVM_TYPE_RW_SECONDARY);
|
||||
} else {
|
||||
/* Both copies are good and versions are same as the reader.
|
||||
* Skip writing. This should be the common case. */
|
||||
}
|
||||
|
||||
if (rv1 || rv2)
|
||||
return rv1 ? rv1 : rv2;
|
||||
|
||||
return BDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int nvmrw_init(struct vba_context *ctx)
|
||||
{
|
||||
if (nvmrw_read(ctx))
|
||||
return BDB_ERROR_NVM_INIT;
|
||||
|
||||
return BDB_SUCCESS;
|
||||
}
|
||||
|
||||
int vba_update_kernel_version(struct vba_context *ctx,
|
||||
uint32_t kernel_data_key_version,
|
||||
uint32_t kernel_version)
|
||||
{
|
||||
struct nvmrw *nvm = &ctx->nvmrw;
|
||||
|
||||
if (nvmrw_verify(ctx->ro_secrets, nvm, sizeof(*nvm))) {
|
||||
if (nvmrw_init(ctx))
|
||||
return BDB_ERROR_NVM_INIT;
|
||||
}
|
||||
|
||||
if (nvm->min_kernel_data_key_version < kernel_data_key_version ||
|
||||
nvm->min_kernel_version < kernel_version) {
|
||||
int rv1, rv2;
|
||||
|
||||
/* Roll forward versions */
|
||||
nvm->min_kernel_data_key_version = kernel_data_key_version;
|
||||
nvm->min_kernel_version = kernel_version;
|
||||
|
||||
/* Increment update counter */
|
||||
nvm->update_count++;
|
||||
|
||||
/* Update both copies */
|
||||
rv1 = nvmrw_write(ctx, NVM_TYPE_RW_PRIMARY);
|
||||
rv2 = nvmrw_write(ctx, NVM_TYPE_RW_SECONDARY);
|
||||
if (rv1 || rv2)
|
||||
return BDB_ERROR_RECOVERY_REQUEST;
|
||||
}
|
||||
|
||||
return BDB_SUCCESS;
|
||||
}
|
||||
100
firmware/bdb/nvm.h
Normal file
100
firmware/bdb/nvm.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
#ifndef VBOOT_REFERENCE_BDB_NVM_H_
|
||||
#define VBOOT_REFERENCE_BDB_NVM_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "bdb_struct.h"
|
||||
#include "bdb_api.h"
|
||||
|
||||
enum nvm_type {
|
||||
NVM_TYPE_WP_PRIMARY,
|
||||
NVM_TYPE_WP_SECONDARY,
|
||||
NVM_TYPE_RW_PRIMARY,
|
||||
NVM_TYPE_RW_SECONDARY,
|
||||
};
|
||||
|
||||
#define NVM_RW_MAGIC 0x3052766e
|
||||
|
||||
/* Size in bytes of encrypted BUC (Boot Unlock Code) */
|
||||
#define BUC_ENC_DIGEST_SIZE 32
|
||||
/* Size in bytes of HMAC of struct NVM-RW */
|
||||
#define NVM_HMAC_SIZE BDB_SHA256_DIGEST_SIZE
|
||||
|
||||
#define NVM_RW_FLAG_BUC_PRESENT (1 << 0)
|
||||
#define NVM_RW_FLAG_DFM_DISABLE (1 << 1)
|
||||
#define NVM_RW_FLAG_DOSM (1 << 2)
|
||||
|
||||
/* This is the minimum size of the data needed to learn the actual size */
|
||||
#define NVM_MIN_STRUCT_SIZE 8
|
||||
|
||||
#define NVM_HEADER_VERSION_MAJOR 1
|
||||
#define NVM_HEADER_VERSION_MINOR 1
|
||||
|
||||
/* Maximum number of retries for writing NVM */
|
||||
#define NVM_MAX_WRITE_RETRY 2
|
||||
|
||||
struct nvmrw {
|
||||
/* Magic number to identify struct */
|
||||
uint32_t struct_magic;
|
||||
|
||||
/* Structure version */
|
||||
uint8_t struct_major_version;
|
||||
uint8_t struct_minor_version;
|
||||
|
||||
/* Size of struct in bytes. 96 for version 1.0 */
|
||||
uint16_t struct_size;
|
||||
|
||||
/* Number of updates to structure contents */
|
||||
uint32_t update_count;
|
||||
|
||||
/* Flags: NVM_RW_FLAG_* */
|
||||
uint32_t flags;
|
||||
|
||||
/* Minimum valid kernel data key version */
|
||||
uint32_t min_kernel_data_key_version;
|
||||
|
||||
/* Minimum valid kernel version */
|
||||
uint32_t min_kernel_version;
|
||||
|
||||
/* Type of BUC */
|
||||
uint8_t buc_type;
|
||||
|
||||
uint8_t reserved0[7];
|
||||
|
||||
/* Encrypted BUC */
|
||||
uint8_t buc_enc_digest[BUC_ENC_DIGEST_SIZE];
|
||||
|
||||
/* SHA-256 HMAC of the struct contents. Add new fields before this. */
|
||||
uint8_t hmac[NVM_HMAC_SIZE];
|
||||
} __attribute__((packed));
|
||||
|
||||
/* Size of the version 1.0 */
|
||||
#define NVM_RW_MIN_STRUCT_SIZE 96
|
||||
/* 4 Kbit EEPROM divided by 4 regions (RO,RW) x (1st,2nd) = 128 KB */
|
||||
#define NVM_RW_MAX_STRUCT_SIZE 128
|
||||
|
||||
/* For nvm_rw_read and nvm_write */
|
||||
struct vba_context;
|
||||
|
||||
/**
|
||||
* Read NVM-RW contents into the context
|
||||
*
|
||||
* @param ctx struct vba_context
|
||||
* @return BDB_SUCCESS or BDB_ERROR_NVM_*
|
||||
*/
|
||||
int nvmrw_read(struct vba_context *ctx);
|
||||
|
||||
/**
|
||||
* Write to NVM-RW from the context
|
||||
*
|
||||
* @param ctx struct vba_context
|
||||
* @param type NVM_TYPE_RW_*
|
||||
* @return BDB_SUCCESS or BDB_ERROR_NVM_*
|
||||
*/
|
||||
int nvmrw_write(struct vba_context *ctx, enum nvm_type type);
|
||||
|
||||
#endif
|
||||
31
firmware/bdb/secrets.h
Normal file
31
firmware/bdb/secrets.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
#ifndef VBOOT_REFERENCE_FIRMWARE_BDB_SECRETS_H_
|
||||
#define VBOOT_REFERENCE_FIRMWARE_BDB_SECRETS_H_
|
||||
|
||||
#define BDB_SECRET_SIZE 32
|
||||
|
||||
/*
|
||||
* Secrets passed to SP-RW by SP-RO. How it's passed depends on chips.
|
||||
* These are hash-extended by SP-RW.
|
||||
*/
|
||||
struct bdb_ro_secrets {
|
||||
uint8_t nvm_wp[BDB_SECRET_SIZE];
|
||||
uint8_t nvm_rw[BDB_SECRET_SIZE];
|
||||
uint8_t bdb[BDB_SECRET_SIZE];
|
||||
uint8_t boot_verified[BDB_SECRET_SIZE];
|
||||
uint8_t boot_path[BDB_SECRET_SIZE];
|
||||
};
|
||||
|
||||
/*
|
||||
* Additional secrets SP-RW derives from RO secrets. This can be independently
|
||||
* updated as more secrets are needed.
|
||||
*/
|
||||
struct bdb_rw_secrets {
|
||||
uint8_t buc[BDB_SECRET_SIZE];
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
#include "bdb_api.h"
|
||||
#include "bdb.h"
|
||||
|
||||
__attribute__((weak))
|
||||
uint32_t vbe_get_vboot_register(enum vboot_register type)
|
||||
@@ -22,3 +23,15 @@ void vbe_reset(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
int vbe_read_nvm(enum nvm_type type, uint8_t *buf, uint32_t size)
|
||||
{
|
||||
return BDB_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
int vbe_write_nvm(enum nvm_type type, void *buf, uint32_t size)
|
||||
{
|
||||
return BDB_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
@@ -10,12 +10,14 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "2sha.h"
|
||||
#include "2hmac.h"
|
||||
#include "bdb.h"
|
||||
#include "bdb_api.h"
|
||||
#include "bdb_struct.h"
|
||||
#include "host.h"
|
||||
#include "test_common.h"
|
||||
#include "vboot_register.h"
|
||||
#include "secrets.h"
|
||||
|
||||
static struct bdb_header *bdb, *bdb0, *bdb1;
|
||||
static uint32_t vboot_register;
|
||||
@@ -24,6 +26,16 @@ static char slot_selected;
|
||||
static uint8_t aprw_digest[BDB_SHA256_DIGEST_SIZE];
|
||||
static uint8_t reset_count;
|
||||
|
||||
/* NVM-RW image in storage (e.g. EEPROM) */
|
||||
static uint8_t nvmrw1[NVM_RW_MAX_STRUCT_SIZE];
|
||||
static uint8_t nvmrw2[NVM_RW_MAX_STRUCT_SIZE];
|
||||
|
||||
struct bdb_ro_secrets secrets = {
|
||||
.nvm_rw = {0x00, },
|
||||
};
|
||||
|
||||
static int vbe_write_nvm_failure = 0;
|
||||
|
||||
static struct bdb_header *create_bdb(const char *key_dir,
|
||||
struct bdb_hash *hash, int num_hashes)
|
||||
{
|
||||
@@ -215,12 +227,12 @@ static void test_verify_aprw(const char *key_dir)
|
||||
slot_selected = 'X';
|
||||
memcpy(aprw_digest, hash0.digest, 4);
|
||||
vbe_reset();
|
||||
TEST_EQ_S(reset_count, 1);
|
||||
TEST_EQ_S(slot_selected, 'A');
|
||||
TEST_EQ(reset_count, 1, NULL);
|
||||
TEST_EQ(slot_selected, 'A', NULL);
|
||||
TEST_FALSE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_PRIMARY,
|
||||
"VBOOT_REGISTER_FAILED_RW_PRIMARY==false");
|
||||
NULL);
|
||||
TEST_FALSE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_SECONDARY,
|
||||
"VBOOT_REGISTER_FAILED_RW_SECONDARY==false");
|
||||
NULL);
|
||||
|
||||
/* (slotA, slotB) = (bad, good) */
|
||||
reset_count = 0;
|
||||
@@ -228,12 +240,12 @@ static void test_verify_aprw(const char *key_dir)
|
||||
slot_selected = 'X';
|
||||
memcpy(aprw_digest, hash1.digest, 4);
|
||||
vbe_reset();
|
||||
TEST_EQ_S(reset_count, 3);
|
||||
TEST_EQ_S(slot_selected, 'B');
|
||||
TEST_EQ(reset_count, 3, NULL);
|
||||
TEST_EQ(slot_selected, 'B', NULL);
|
||||
TEST_TRUE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_PRIMARY,
|
||||
"VBOOT_REGISTER_FAILED_RW_PRIMARY==true");
|
||||
NULL);
|
||||
TEST_FALSE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_SECONDARY,
|
||||
"VBOOT_REGISTER_FAILED_RW_SECONDARY==false");
|
||||
NULL);
|
||||
|
||||
/* (slotA, slotB) = (bad, bad) */
|
||||
reset_count = 0;
|
||||
@@ -241,21 +253,306 @@ static void test_verify_aprw(const char *key_dir)
|
||||
slot_selected = 'X';
|
||||
memset(aprw_digest, 0, BDB_SHA256_DIGEST_SIZE);
|
||||
vbe_reset();
|
||||
TEST_EQ_S(reset_count, 5);
|
||||
TEST_EQ_S(slot_selected, 'X');
|
||||
TEST_EQ(reset_count, 5, NULL);
|
||||
TEST_EQ(slot_selected, 'X', NULL);
|
||||
TEST_TRUE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_PRIMARY,
|
||||
"VBOOT_REGISTER_FAILED_RW_PRIMARY==true");
|
||||
NULL);
|
||||
TEST_TRUE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_SECONDARY,
|
||||
"VBOOT_REGISTER_FAILED_RW_SECONDARY==true");
|
||||
NULL);
|
||||
TEST_TRUE(vboot_register_persist & VBOOT_REGISTER_RECOVERY_REQUEST,
|
||||
"Recovery request");
|
||||
NULL);
|
||||
|
||||
/* Clean up */
|
||||
free(bdb0);
|
||||
free(bdb1);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
int vbe_read_nvm(enum nvm_type type, uint8_t *buf, uint32_t size)
|
||||
{
|
||||
/* Read NVM-RW contents (from EEPROM for example) */
|
||||
switch (type) {
|
||||
case NVM_TYPE_RW_PRIMARY:
|
||||
if (sizeof(nvmrw1) < size)
|
||||
return -1;
|
||||
memcpy(buf, nvmrw1, size);
|
||||
break;
|
||||
case NVM_TYPE_RW_SECONDARY:
|
||||
if (sizeof(nvmrw2) < size)
|
||||
return -1;
|
||||
memcpy(buf, nvmrw2, size);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vbe_write_nvm(enum nvm_type type, void *buf, uint32_t size)
|
||||
{
|
||||
if (vbe_write_nvm_failure > 0) {
|
||||
fprintf(stderr, "Failed to write NVM (type=%d failure=%d)\n",
|
||||
type, vbe_write_nvm_failure);
|
||||
vbe_write_nvm_failure--;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Write NVM-RW contents (to EEPROM for example) */
|
||||
switch (type) {
|
||||
case NVM_TYPE_RW_PRIMARY:
|
||||
memcpy(nvmrw1, buf, size);
|
||||
break;
|
||||
case NVM_TYPE_RW_SECONDARY:
|
||||
memcpy(nvmrw2, buf, size);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void install_nvm(enum nvm_type type,
|
||||
uint32_t min_kernel_data_key_version,
|
||||
uint32_t min_kernel_version,
|
||||
uint32_t update_count)
|
||||
{
|
||||
struct nvmrw nvm = {
|
||||
.struct_magic = NVM_RW_MAGIC,
|
||||
.struct_major_version = NVM_HEADER_VERSION_MAJOR,
|
||||
.struct_minor_version = NVM_HEADER_VERSION_MINOR,
|
||||
.struct_size = sizeof(struct nvmrw),
|
||||
.min_kernel_data_key_version = min_kernel_data_key_version,
|
||||
.min_kernel_version = min_kernel_version,
|
||||
.update_count = update_count,
|
||||
};
|
||||
|
||||
/* Compute HMAC */
|
||||
hmac(VB2_HASH_SHA256, secrets.nvm_rw, BDB_SECRET_SIZE,
|
||||
&nvm, nvm.struct_size - sizeof(nvm.hmac),
|
||||
nvm.hmac, sizeof(nvm.hmac));
|
||||
|
||||
/* Install NVM-RWs (in EEPROM for example) */
|
||||
switch (type) {
|
||||
case NVM_TYPE_RW_PRIMARY:
|
||||
memset(nvmrw1, 0, sizeof(nvmrw1));
|
||||
memcpy(nvmrw1, &nvm, sizeof(nvm));
|
||||
break;
|
||||
case NVM_TYPE_RW_SECONDARY:
|
||||
memset(nvmrw2, 0, sizeof(nvmrw2));
|
||||
memcpy(nvmrw2, &nvm, sizeof(nvm));
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unsupported NVM type (%d)\n", type);
|
||||
exit(2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_nvm_read(void)
|
||||
{
|
||||
struct vba_context ctx = {
|
||||
.bdb = NULL,
|
||||
.ro_secrets = &secrets,
|
||||
};
|
||||
struct nvmrw *nvm;
|
||||
uint8_t nvmrw1_copy[NVM_RW_MAX_STRUCT_SIZE];
|
||||
uint8_t nvmrw2_copy[NVM_RW_MAX_STRUCT_SIZE];
|
||||
|
||||
install_nvm(NVM_TYPE_RW_PRIMARY, 0, 1, 0);
|
||||
install_nvm(NVM_TYPE_RW_SECONDARY, 1, 0, 0);
|
||||
memcpy(nvmrw1_copy, nvmrw1, sizeof(nvmrw1));
|
||||
memcpy(nvmrw2_copy, nvmrw2, sizeof(nvmrw2));
|
||||
|
||||
/* Test nvm_read: both good -> pick primary, no sync */
|
||||
memset(&ctx.nvmrw, 0, sizeof(ctx.nvmrw));
|
||||
TEST_SUCC(nvmrw_read(&ctx), NULL);
|
||||
TEST_SUCC(memcmp(&ctx.nvmrw, nvmrw1, sizeof(*nvm)), NULL);
|
||||
TEST_SUCC(memcmp(nvmrw1, nvmrw1_copy, sizeof(nvmrw1)), NULL);
|
||||
TEST_SUCC(memcmp(nvmrw2, nvmrw2_copy, sizeof(nvmrw2)), NULL);
|
||||
|
||||
/* Test nvm_read: primary bad -> pick secondary */
|
||||
install_nvm(NVM_TYPE_RW_PRIMARY, 0, 1, 0);
|
||||
install_nvm(NVM_TYPE_RW_SECONDARY, 1, 0, 0);
|
||||
memcpy(nvmrw2_copy, nvmrw2, sizeof(*nvm));
|
||||
nvm = (struct nvmrw *)nvmrw1;
|
||||
nvm->hmac[0] ^= 0xff;
|
||||
memset(&ctx.nvmrw, 0, sizeof(ctx.nvmrw));
|
||||
TEST_SUCC(nvmrw_read(&ctx), NULL);
|
||||
TEST_SUCC(memcmp(&ctx.nvmrw, nvmrw2, sizeof(*nvm)), NULL);
|
||||
TEST_SUCC(memcmp(nvmrw1, nvmrw2_copy, sizeof(nvmrw2)), NULL);
|
||||
TEST_SUCC(memcmp(nvmrw2, nvmrw2_copy, sizeof(nvmrw2)), NULL);
|
||||
|
||||
/* Test nvm_read: secondary bad -> pick primary */
|
||||
install_nvm(NVM_TYPE_RW_PRIMARY, 0, 1, 0);
|
||||
install_nvm(NVM_TYPE_RW_SECONDARY, 1, 0, 0);
|
||||
memcpy(nvmrw1_copy, nvmrw1, sizeof(*nvm));
|
||||
nvm = (struct nvmrw *)nvmrw2;
|
||||
nvm->hmac[0] ^= 0xff;
|
||||
memset(&ctx.nvmrw, 0, sizeof(ctx.nvmrw));
|
||||
TEST_SUCC(nvmrw_read(&ctx), NULL);
|
||||
TEST_SUCC(memcmp(&ctx.nvmrw, nvmrw1, sizeof(*nvm)), NULL);
|
||||
TEST_SUCC(memcmp(nvmrw1, nvmrw1_copy, sizeof(nvmrw1)), NULL);
|
||||
TEST_SUCC(memcmp(nvmrw2, nvmrw1_copy, sizeof(nvmrw1)), NULL);
|
||||
|
||||
/* Test nvm_read: both bad */
|
||||
nvm = (struct nvmrw *)nvmrw1;
|
||||
nvm->hmac[0] ^= 0xff;
|
||||
nvm = (struct nvmrw *)nvmrw2;
|
||||
nvm->hmac[0] ^= 0xff;
|
||||
memset(&ctx.nvmrw, 0, sizeof(ctx.nvmrw));
|
||||
TEST_EQ(nvmrw_read(&ctx), BDB_ERROR_NVM_RW_INVALID_HMAC, NULL);
|
||||
|
||||
/* Test update count: secondary new -> pick secondary */
|
||||
install_nvm(NVM_TYPE_RW_PRIMARY, 0, 1, 0);
|
||||
install_nvm(NVM_TYPE_RW_SECONDARY, 1, 0, 1);
|
||||
memcpy(nvmrw2_copy, nvmrw2, sizeof(*nvm));
|
||||
memset(&ctx.nvmrw, 0, sizeof(ctx.nvmrw));
|
||||
TEST_SUCC(nvmrw_read(&ctx), NULL);
|
||||
TEST_SUCC(memcmp(&ctx.nvmrw, nvmrw2, sizeof(*nvm)), NULL);
|
||||
TEST_SUCC(memcmp(nvmrw1, nvmrw2_copy, sizeof(nvmrw1)), NULL);
|
||||
TEST_SUCC(memcmp(nvmrw2, nvmrw2_copy, sizeof(nvmrw2)), NULL);
|
||||
|
||||
/* Test old reader -> minor version downgrade */
|
||||
install_nvm(NVM_TYPE_RW_PRIMARY, 0, 1, 0);
|
||||
install_nvm(NVM_TYPE_RW_SECONDARY, 1, 0, 1);
|
||||
memset(&ctx.nvmrw, 0, sizeof(ctx.nvmrw));
|
||||
nvm = (struct nvmrw *)nvmrw1;
|
||||
nvm->struct_minor_version++;
|
||||
nvm->struct_size++;
|
||||
TEST_SUCC(nvmrw_read(&ctx), NULL);
|
||||
TEST_EQ(ctx.nvmrw.struct_minor_version, NVM_HEADER_VERSION_MINOR, NULL);
|
||||
TEST_EQ(ctx.nvmrw.struct_size, sizeof(*nvm), NULL);
|
||||
}
|
||||
|
||||
static void verify_nvm_write(struct vba_context *ctx,
|
||||
int expected_result)
|
||||
{
|
||||
struct nvmrw *nvmrw;
|
||||
struct nvmrw *nvm = &ctx->nvmrw;
|
||||
|
||||
TEST_EQ(nvmrw_write(ctx, NVM_TYPE_RW_PRIMARY), expected_result, NULL);
|
||||
|
||||
if (expected_result != BDB_SUCCESS)
|
||||
return;
|
||||
|
||||
nvmrw = (struct nvmrw *)nvmrw1;
|
||||
TEST_EQ(nvmrw->min_kernel_data_key_version,
|
||||
nvm->min_kernel_data_key_version, NULL);
|
||||
TEST_EQ(nvmrw->min_kernel_version, nvm->min_kernel_version, NULL);
|
||||
TEST_EQ(nvmrw->update_count, nvm->update_count, NULL);
|
||||
}
|
||||
|
||||
static void test_nvm_write(void)
|
||||
{
|
||||
struct vba_context ctx = {
|
||||
.bdb = NULL,
|
||||
.ro_secrets = &secrets,
|
||||
};
|
||||
struct nvmrw nvm = {
|
||||
.struct_magic = NVM_RW_MAGIC,
|
||||
.struct_major_version = NVM_HEADER_VERSION_MAJOR,
|
||||
.struct_minor_version = NVM_HEADER_VERSION_MINOR,
|
||||
.struct_size = sizeof(struct nvmrw),
|
||||
.min_kernel_data_key_version = 1,
|
||||
.min_kernel_version = 2,
|
||||
.update_count = 3,
|
||||
};
|
||||
|
||||
/* Test normal case */
|
||||
memcpy(&ctx.nvmrw, &nvm, sizeof(nvm));
|
||||
vbe_write_nvm_failure = 0;
|
||||
verify_nvm_write(&ctx, BDB_SUCCESS);
|
||||
|
||||
/* Test write failure: once */
|
||||
memcpy(&ctx.nvmrw, &nvm, sizeof(nvm));
|
||||
vbe_write_nvm_failure = 1;
|
||||
verify_nvm_write(&ctx, BDB_SUCCESS);
|
||||
|
||||
/* Test write failure: twice */
|
||||
memcpy(&ctx.nvmrw, &nvm, sizeof(nvm));
|
||||
vbe_write_nvm_failure = 2;
|
||||
verify_nvm_write(&ctx, BDB_ERROR_NVM_WRITE);
|
||||
|
||||
/* Test invalid struct magic */
|
||||
memcpy(&ctx.nvmrw, &nvm, sizeof(nvm));
|
||||
ctx.nvmrw.struct_magic ^= 0xff;
|
||||
verify_nvm_write(&ctx, BDB_ERROR_NVM_RW_MAGIC);
|
||||
|
||||
/* Test struct size too small */
|
||||
memcpy(&ctx.nvmrw, &nvm, sizeof(nvm));
|
||||
ctx.nvmrw.struct_size = NVM_RW_MIN_STRUCT_SIZE - 1;
|
||||
verify_nvm_write(&ctx, BDB_ERROR_NVM_STRUCT_SIZE);
|
||||
|
||||
/* Test struct size too large */
|
||||
memcpy(&ctx.nvmrw, &nvm, sizeof(nvm));
|
||||
ctx.nvmrw.struct_size = NVM_RW_MAX_STRUCT_SIZE + 1;
|
||||
verify_nvm_write(&ctx, BDB_ERROR_NVM_STRUCT_SIZE);
|
||||
|
||||
/* Test invalid struct version */
|
||||
memcpy(&ctx.nvmrw, &nvm, sizeof(nvm));
|
||||
ctx.nvmrw.struct_major_version = NVM_HEADER_VERSION_MAJOR - 1;
|
||||
verify_nvm_write(&ctx, BDB_ERROR_NVM_STRUCT_VERSION);
|
||||
|
||||
vbe_write_nvm_failure = 0;
|
||||
}
|
||||
|
||||
static void verify_kernel_version(uint32_t min_kernel_data_key_version,
|
||||
uint32_t new_kernel_data_key_version,
|
||||
uint32_t min_kernel_version,
|
||||
uint32_t new_kernel_version,
|
||||
int expected_result)
|
||||
{
|
||||
struct vba_context ctx = {
|
||||
.bdb = NULL,
|
||||
.ro_secrets = &secrets,
|
||||
};
|
||||
struct nvmrw *nvm = (struct nvmrw *)nvmrw1;
|
||||
uint32_t expected_kernel_data_key_version = min_kernel_data_key_version;
|
||||
uint32_t expected_kernel_version = min_kernel_version;
|
||||
int should_update = 0;
|
||||
|
||||
if (min_kernel_data_key_version < new_kernel_data_key_version) {
|
||||
expected_kernel_data_key_version = new_kernel_data_key_version;
|
||||
should_update = 1;
|
||||
}
|
||||
if (min_kernel_version < new_kernel_version) {
|
||||
expected_kernel_version = new_kernel_version;
|
||||
should_update = 1;
|
||||
}
|
||||
|
||||
install_nvm(NVM_TYPE_RW_PRIMARY, min_kernel_data_key_version,
|
||||
min_kernel_version, 0);
|
||||
install_nvm(NVM_TYPE_RW_SECONDARY, 0, 0, 0);
|
||||
|
||||
TEST_EQ(vba_update_kernel_version(&ctx, new_kernel_data_key_version,
|
||||
new_kernel_version),
|
||||
expected_result, NULL);
|
||||
|
||||
if (expected_result != BDB_SUCCESS)
|
||||
return;
|
||||
|
||||
/* Check data key version */
|
||||
TEST_EQ(nvm->min_kernel_data_key_version,
|
||||
expected_kernel_data_key_version, NULL);
|
||||
/* Check kernel version */
|
||||
TEST_EQ(nvm->min_kernel_version, expected_kernel_version, NULL);
|
||||
/* Check update_count */
|
||||
TEST_EQ(nvm->update_count, 0 + should_update, NULL);
|
||||
/* Check sync if update is expected */
|
||||
if (should_update)
|
||||
TEST_SUCC(memcmp(nvmrw2, nvmrw1, sizeof(nvmrw1)), NULL);
|
||||
}
|
||||
|
||||
static void test_update_kernel_version(void)
|
||||
{
|
||||
/* Test update: data key version */
|
||||
verify_kernel_version(0, 1, 0, 0, BDB_SUCCESS);
|
||||
/* Test update: kernel version */
|
||||
verify_kernel_version(0, 0, 0, 1, BDB_SUCCESS);
|
||||
/* Test no update: data key version */
|
||||
verify_kernel_version(1, 0, 0, 0, BDB_SUCCESS);
|
||||
/* Test no update: kernel version */
|
||||
verify_kernel_version(0, 0, 1, 0, BDB_SUCCESS);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@@ -266,6 +563,9 @@ int main(int argc, char *argv[])
|
||||
printf("Running BDB SP-RW tests...\n");
|
||||
|
||||
test_verify_aprw(argv[1]);
|
||||
test_nvm_read();
|
||||
test_nvm_write();
|
||||
test_update_kernel_version();
|
||||
|
||||
return gTestSuccess ? 0 : 255;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user