summaryrefslogtreecommitdiff
path: root/firmware/bdb/nvm.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/bdb/nvm.c')
-rw-r--r--firmware/bdb/nvm.c346
1 files changed, 0 insertions, 346 deletions
diff --git a/firmware/bdb/nvm.c b/firmware/bdb/nvm.c
deleted file mode 100644
index 85c301a2..00000000
--- a/firmware/bdb/nvm.c
+++ /dev/null
@@ -1,346 +0,0 @@
-/* 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_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->secrets)
- return BDB_ERROR_NVM_INVALID_SECRET;
-
- rv = nvmrw_validate(nvm, sizeof(*nvm));
- if (rv)
- return rv;
-
- /* Update HMAC */
- hmac(VB2_HASH_SHA256, ctx->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_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->secrets,
- buf1, sizeof(buf1));
-
- /* Read and verify the 2nd copy */
- rv2 = read_verify_nvmrw(NVM_TYPE_RW_SECONDARY, ctx->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->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;
-}
-
-int vba_update_buc(struct vba_context *ctx, uint8_t *new_buc)
-{
- struct nvmrw *nvm = &ctx->nvmrw;
- uint8_t buc[BUC_ENC_DIGEST_SIZE];
- int rv1, rv2;
-
- if (nvmrw_verify(ctx->secrets, nvm, sizeof(*nvm))) {
- if (nvmrw_init(ctx))
- return BDB_ERROR_NVM_INIT;
- }
-
- /* Encrypt new BUC
- * Note that we do not need to decide whether we should use hardware
- * crypto or not because this is supposed to be running in RW code. */
- if (vbe_aes256_encrypt(new_buc, BUC_ENC_DIGEST_SIZE,
- ctx->secrets->buc, buc))
- return BDB_ERROR_ENCRYPT_BUC;
-
- /* Return if new BUC is same as current one. */
- if (!memcmp(buc, nvm->buc_enc_digest, sizeof(buc)))
- return BDB_SUCCESS;
-
- memcpy(nvm->buc_enc_digest, buc, sizeof(buc));
-
- /* Increment update counter */
- nvm->update_count++;
-
- /* Write new BUC */
- rv1 = nvmrw_write(ctx, NVM_TYPE_RW_PRIMARY);
- rv2 = nvmrw_write(ctx, NVM_TYPE_RW_SECONDARY);
- if (rv1 || rv2)
- return BDB_ERROR_WRITE_BUC;
-
- return BDB_SUCCESS;
-}
-
-int nvmrw_get(struct vba_context *ctx, enum nvmrw_var var, uint32_t *val)
-{
- struct nvmrw *nvm = &ctx->nvmrw;
-
- /* No init or verify so that this can be called from futility.
- * Callers are responsible for init and verify. */
-
- switch (var) {
- case NVMRW_VAR_UPDATE_COUNT:
- *val = nvm->update_count;
- break;
- case NVMRW_VAR_MIN_KERNEL_DATA_KEY_VERSION:
- *val = nvm->min_kernel_data_key_version;
- break;
- case NVMRW_VAR_MIN_KERNEL_VERSION:
- *val = nvm->min_kernel_version;
- break;
- case NVMRW_VAR_BUC_TYPE:
- *val = nvm->buc_type;
- break;
- case NVMRW_VAR_FLAG_BUC_PRESENT:
- *val = nvm->flags & NVM_RW_FLAG_BUC_PRESENT;
- break;
- case NVMRW_VAR_FLAG_DFM_DISABLE:
- *val = nvm->flags & NVM_RW_FLAG_DFM_DISABLE;
- break;
- case NVMRW_VAR_FLAG_DOSM:
- *val = nvm->flags & NVM_RW_FLAG_DOSM;
- break;
- default:
- return BDB_ERROR_NVM_INVALID_PARAMETER;
- }
-
- return BDB_SUCCESS;
-}
-
-#define MAX_8BIT_UINT ((((uint64_t)1) << 8) - 1)
-
-int nvmrw_set(struct vba_context *ctx, enum nvmrw_var var, uint32_t val)
-{
- struct nvmrw *nvm = &ctx->nvmrw;
-
- /* No init or verify so that this can be called from futility.
- * Callers are responsible for init and verify. */
-
- switch (var) {
- case NVMRW_VAR_UPDATE_COUNT:
- nvm->update_count = val;
- break;
- case NVMRW_VAR_MIN_KERNEL_DATA_KEY_VERSION:
- nvm->min_kernel_data_key_version = val;
- break;
- case NVMRW_VAR_MIN_KERNEL_VERSION:
- nvm->min_kernel_version = val;
- break;
- case NVMRW_VAR_BUC_TYPE:
- if (val > MAX_8BIT_UINT)
- return BDB_ERROR_NVM_INVALID_PARAMETER;
- nvm->buc_type = val;
- break;
- case NVMRW_VAR_FLAG_BUC_PRESENT:
- nvm->flags &= ~NVM_RW_FLAG_BUC_PRESENT;
- nvm->flags |= val ? NVM_RW_FLAG_BUC_PRESENT : 0;
- break;
- case NVMRW_VAR_FLAG_DFM_DISABLE:
- nvm->flags &= ~NVM_RW_FLAG_DFM_DISABLE;
- nvm->flags |= val ? NVM_RW_FLAG_DFM_DISABLE : 0;
- break;
- case NVMRW_VAR_FLAG_DOSM:
- nvm->flags &= ~NVM_RW_FLAG_DOSM;
- nvm->flags |= val ? NVM_RW_FLAG_DOSM : 0;
- break;
- default:
- return BDB_ERROR_NVM_INVALID_PARAMETER;
- }
-
- return BDB_SUCCESS;
-}