Files
OpenCellular/firmware/2lib/2api.c
Randall Spangler c8e48545d5 vboot2: Support reboot requested by secdata
When a TPM goes from the disabled state to the enabled state, it must
reboot after being enabled, before it can be initialized.  In vboot1,
TLCL was part of vboot and this was handled internally.  In vboot2, the
caller must set a context flag, so that vboot can decide whether to
allow the reboot, or whether to go directly to recovery mode.  This
check is necessary to handle the following cases:

1) The device is booting normally, but the TPM needs a reboot.  This
should simply reboot, without going to recovery mode.

2) The device is booting in recovery mode, but the TPM needs a reboot.
If this is the first time it asked us, allow the reboot.

3) The TPM asked for a reboot last time, so we did.  And it's still
asking.  Don't reboot, because that runs the risk that whatever is wrong
won't be fixed next boot either, and we'll get stuck in a reboot loop
that will prevent recovery.  Boot into recovery mode.

Add a new NvStorage bit to track whether the TPM requested a reboot on
the previous boot.  That's better than what we did in vboot1, where we
used a special recovery request.  Vboot1 couldn't track getting stuck in
a reboot loop in normal mode, only in recovery mode.  The new code can
catch both.

BUG=chrome-os-partner:45462
BRANCH=ryu
TEST=make runtests

Change-Id: I2ee54af107275ccf64a6cb41132b7a0fc02bb983
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/300572
Tested-by: Furquan Shaikh <furquan@chromium.org>
Reviewed-by: Furquan Shaikh <furquan@chromium.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
2015-09-17 17:35:11 -07:00

178 lines
4.3 KiB
C

/* 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.
*
* Externally-callable APIs
* (Firmware portion)
*/
#include "2sysincludes.h"
#include "2api.h"
#include "2common.h"
#include "2misc.h"
#include "2nvstorage.h"
#include "2secdata.h"
#include "2sha.h"
#include "2rsa.h"
#include "2tpm_bootmode.h"
int vb2api_secdata_check(const struct vb2_context *ctx)
{
return vb2_secdata_check_crc(ctx);
}
int vb2api_secdata_create(struct vb2_context *ctx)
{
return vb2_secdata_create(ctx);
}
void vb2api_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode)
{
/* Initialize the vboot context if it hasn't been yet */
vb2_init_context(ctx);
vb2_fail(ctx, reason, subcode);
}
int vb2api_fw_phase1(struct vb2_context *ctx)
{
int rv;
/* Initialize the vboot context if it hasn't been yet */
vb2_init_context(ctx);
/* Initialize NV context */
vb2_nv_init(ctx);
/*
* Handle caller-requested reboot due to secdata. Do this before we
* even look at secdata. If we fail because of a reboot loop we'll be
* the first failure so will get to set the recovery reason.
*/
if (!(ctx->flags & VB2_CONTEXT_SECDATA_WANTS_REBOOT)) {
/* No reboot requested */
vb2_nv_set(ctx, VB2_NV_TPM_REQUESTED_REBOOT, 0);
} else if (vb2_nv_get(ctx, VB2_NV_TPM_REQUESTED_REBOOT)) {
/*
* Reboot requested... again. Fool me once, shame on you.
* Fool me twice, shame on me. Fail into recovery to avoid
* a reboot loop.
*/
vb2_fail(ctx, VB2_RECOVERY_RO_TPM_REBOOT, 0);
} else {
/* Reboot requested for the first time */
vb2_nv_set(ctx, VB2_NV_TPM_REQUESTED_REBOOT, 1);
return VB2_ERROR_API_PHASE1_SECDATA_REBOOT;
}
/* Initialize secure data */
rv = vb2_secdata_init(ctx);
if (rv)
vb2_fail(ctx, VB2_RECOVERY_SECDATA_INIT, rv);
/* Load and parse the GBB header */
rv = vb2_fw_parse_gbb(ctx);
if (rv)
vb2_fail(ctx, VB2_RECOVERY_GBB_HEADER, rv);
/* Check for dev switch */
rv = vb2_check_dev_switch(ctx);
if (rv)
vb2_fail(ctx, VB2_RECOVERY_DEV_SWITCH, rv);
/*
* Check for recovery. Note that this function returns void, since
* any errors result in requesting recovery.
*/
vb2_check_recovery(ctx);
/* Return error if recovery is needed */
if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) {
/* Always clear RAM when entering recovery mode */
ctx->flags |= VB2_CONTEXT_CLEAR_RAM;
return VB2_ERROR_API_PHASE1_RECOVERY;
}
return VB2_SUCCESS;
}
int vb2api_fw_phase2(struct vb2_context *ctx)
{
int rv;
/* Always clear RAM when entering developer mode */
if (ctx->flags & VB2_CONTEXT_DEVELOPER_MODE)
ctx->flags |= VB2_CONTEXT_CLEAR_RAM;
/* Check for explicit request to clear TPM */
rv = vb2_check_tpm_clear(ctx);
if (rv) {
vb2_fail(ctx, VB2_RECOVERY_TPM_CLEAR_OWNER, rv);
return rv;
}
/* Decide which firmware slot to try this boot */
rv = vb2_select_fw_slot(ctx);
if (rv) {
vb2_fail(ctx, VB2_RECOVERY_FW_SLOT, rv);
return rv;
}
return VB2_SUCCESS;
}
int vb2api_extend_hash(struct vb2_context *ctx,
const void *buf,
uint32_t size)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
struct vb2_digest_context *dc = (struct vb2_digest_context *)
(ctx->workbuf + sd->workbuf_hash_offset);
/* Must have initialized hash digest work area */
if (!sd->workbuf_hash_size)
return VB2_ERROR_API_EXTEND_HASH_WORKBUF;
/* Don't extend past the data we expect to hash */
if (!size || size > sd->hash_remaining_size)
return VB2_ERROR_API_EXTEND_HASH_SIZE;
sd->hash_remaining_size -= size;
if (dc->using_hwcrypto)
return vb2ex_hwcrypto_digest_extend(buf, size);
else
return vb2_digest_extend(dc, buf, size);
}
int vb2api_get_pcr_digest(struct vb2_context *ctx,
enum vb2_pcr_digest which_digest,
uint8_t *dest,
uint32_t *dest_size)
{
const uint8_t *digest;
uint32_t digest_size;
switch (which_digest) {
case BOOT_MODE_PCR:
digest = vb2_get_boot_state_digest(ctx);
digest_size = VB2_SHA1_DIGEST_SIZE;
break;
case HWID_DIGEST_PCR:
digest = vb2_get_sd(ctx)->gbb_hwid_digest;
digest_size = VB2_GBB_HWID_DIGEST_SIZE;
break;
default:
return VB2_ERROR_API_PCR_DIGEST;
}
if (digest == NULL || *dest_size < digest_size)
return VB2_ERROR_API_PCR_DIGEST_BUF;
memcpy(dest, digest, digest_size);
*dest_size = digest_size;
return VB2_SUCCESS;
}