mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-24 02:05:01 +00:00
At present reading data from storage in Vboot is a little fragmented. For the firmware image, we expect the boot loader to handle this. For the disk we have a block-level API. For the GBB (which also sits in the firmware image) we expect the entire thing to be read before Vboot is called. Add the concept of a region, and an API to read from a region. At present, and most pressing, is reading from a GBB region. In the future this could be extended to other parts of the firmware or even the disk. Move all access to the GBB into this API so that the boot loader can provide either a GBB region in one large contiguous chunk, or a function to deal with read requests from vboot. The call to VbExRegionRead() is behind a flag since not all boot loaders support it yet. The main change for boot loaders which don't support this new API is that vboot will do more behind the scenes. For example, it will allocate memory for chunks of data that it reads from the GBB, rather than just accessing it directly. This approach is considerably simpler than trying to pass char ** everywhere and have vboot decide whether something needs to be allocated or not. The tests are updated, mainly to include setting up a GBB structure accessible from VbCommonParams, which is now required by the firmware and kernel functions. In normal operation this is set up at the start of VbLoadFIrmware() and VbSelectAndLoadKernel() but for tests which call children of these functions directly, the GBB structure must be set up manually by the test. BUG=chrome-os-partner:21115 BRANCH=none TEST=manual FEATURES=test sudo -E emerge vboot_reference Change-Id: I2c19e9dc2ed602d0642bbf4f7d27f79fe9fad873 Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/63336 Reviewed-by: Randall Spangler <rspangler@chromium.org>
325 lines
10 KiB
C
325 lines
10 KiB
C
/* 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.
|
|
*
|
|
* High-level firmware wrapper API - entry points for init, firmware selection
|
|
*/
|
|
|
|
#include "sysincludes.h"
|
|
|
|
#include "region.h"
|
|
#include "gbb_access.h"
|
|
#include "gbb_header.h"
|
|
#include "load_firmware_fw.h"
|
|
#include "rollback_index.h"
|
|
#include "utility.h"
|
|
#include "vboot_api.h"
|
|
#include "vboot_common.h"
|
|
#include "vboot_nvstorage.h"
|
|
|
|
VbError_t VbInit(VbCommonParams *cparams, VbInitParams *iparams)
|
|
{
|
|
VbSharedDataHeader *shared =
|
|
(VbSharedDataHeader *)cparams->shared_data_blob;
|
|
GoogleBinaryBlockHeader gbb;
|
|
VbNvContext vnc;
|
|
VbError_t retval = VBERROR_SUCCESS;
|
|
uint32_t recovery = VBNV_RECOVERY_NOT_REQUESTED;
|
|
int is_s3_resume = 0;
|
|
uint32_t s3_debug_boot = 0;
|
|
uint32_t require_official_os = 0;
|
|
uint32_t tpm_version = 0;
|
|
uint32_t tpm_status = 0;
|
|
int has_virt_dev_switch = 0;
|
|
int is_hw_dev = 0;
|
|
int is_virt_dev = 0;
|
|
uint32_t disable_dev_request = 0;
|
|
uint32_t clear_tpm_owner_request = 0;
|
|
int is_dev = 0;
|
|
|
|
/* Initialize output flags */
|
|
iparams->out_flags = 0;
|
|
|
|
retval = VbGbbReadHeader_static(cparams, &gbb);
|
|
if (retval)
|
|
return retval;
|
|
|
|
VBDEBUG(("VbInit() input flags 0x%x gbb flags 0x%x\n", iparams->flags,
|
|
gbb.flags));
|
|
|
|
/* Set up NV storage */
|
|
VbExNvStorageRead(vnc.raw);
|
|
VbNvSetup(&vnc);
|
|
|
|
/* Initialize shared data structure */
|
|
if (0 != VbSharedDataInit(shared, cparams->shared_data_size)) {
|
|
VBDEBUG(("Shared data init error\n"));
|
|
return VBERROR_INIT_SHARED_DATA;
|
|
}
|
|
|
|
shared->timer_vb_init_enter = VbExGetTimer();
|
|
|
|
/* Copy some boot switch flags */
|
|
/* TODO: in next refactor, just save in/out flags in VbSharedData */
|
|
shared->flags = 0;
|
|
if (iparams->flags & VB_INIT_FLAG_REC_BUTTON_PRESSED)
|
|
shared->flags |= VBSD_BOOT_REC_SWITCH_ON;
|
|
if (iparams->flags & VB_INIT_FLAG_WP_ENABLED)
|
|
shared->flags |= VBSD_BOOT_FIRMWARE_WP_ENABLED;
|
|
if (iparams->flags & VB_INIT_FLAG_SW_WP_ENABLED)
|
|
shared->flags |= VBSD_BOOT_FIRMWARE_SW_WP_ENABLED;
|
|
if (iparams->flags & VB_INIT_FLAG_S3_RESUME)
|
|
shared->flags |= VBSD_BOOT_S3_RESUME;
|
|
if (iparams->flags & VB_INIT_FLAG_RO_NORMAL_SUPPORT)
|
|
shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT;
|
|
if (iparams->flags & VB_INIT_FLAG_EC_SOFTWARE_SYNC)
|
|
shared->flags |= VBSD_EC_SOFTWARE_SYNC;
|
|
if (iparams->flags & VB_INIT_FLAG_EC_SLOW_UPDATE)
|
|
shared->flags |= VBSD_EC_SLOW_UPDATE;
|
|
|
|
is_s3_resume = (iparams->flags & VB_INIT_FLAG_S3_RESUME ? 1 : 0);
|
|
|
|
/* Check if the OS is requesting a debug S3 reset */
|
|
VbNvGet(&vnc, VBNV_DEBUG_RESET_MODE, &s3_debug_boot);
|
|
if (s3_debug_boot) {
|
|
if (is_s3_resume) {
|
|
VBDEBUG(("VbInit() requesting S3 debug boot\n"));
|
|
iparams->out_flags |= VB_INIT_OUT_S3_DEBUG_BOOT;
|
|
is_s3_resume = 0; /* Proceed as if normal boot */
|
|
}
|
|
|
|
/*
|
|
* Clear the request even if this is a normal boot, since we
|
|
* don't want the NEXT S3 resume to be a debug reset unless the
|
|
* OS asserts the request again.
|
|
*/
|
|
VbNvSet(&vnc, VBNV_DEBUG_RESET_MODE, 0);
|
|
}
|
|
|
|
/*
|
|
* If this isn't a S3 resume, read the current recovery request, then
|
|
* clear it so we don't get stuck in recovery mode.
|
|
*/
|
|
if (!is_s3_resume) {
|
|
VbNvGet(&vnc, VBNV_RECOVERY_REQUEST, &recovery);
|
|
VBDEBUG(("VbInit sees recovery request = %d\n", recovery));
|
|
if (VBNV_RECOVERY_NOT_REQUESTED != recovery)
|
|
VbNvSet(&vnc, VBNV_RECOVERY_REQUEST,
|
|
VBNV_RECOVERY_NOT_REQUESTED);
|
|
}
|
|
|
|
/*
|
|
* If the previous boot failed in the firmware somewhere outside of
|
|
* verified boot, and recovery is not requested for our own reasons,
|
|
* request recovery mode. This gives the calling firmware a way to
|
|
* request recovery if it finds something terribly wrong.
|
|
*/
|
|
if (VBNV_RECOVERY_NOT_REQUESTED == recovery &&
|
|
iparams->flags & VB_INIT_FLAG_PREVIOUS_BOOT_FAIL) {
|
|
recovery = VBNV_RECOVERY_RO_FIRMWARE;
|
|
}
|
|
|
|
/*
|
|
* If recovery button is pressed, override recovery reason. Note that
|
|
* we do this in the S3 resume path also.
|
|
*/
|
|
if (iparams->flags & VB_INIT_FLAG_REC_BUTTON_PRESSED)
|
|
recovery = VBNV_RECOVERY_RO_MANUAL;
|
|
|
|
/*
|
|
* Copy current recovery reason to shared data. If we fail later on, it
|
|
* won't matter, since we'll just reboot.
|
|
*/
|
|
shared->recovery_reason = (uint8_t)recovery;
|
|
VBDEBUG(("VbInit now sets shared->recovery_reason = %d\n", recovery));
|
|
|
|
/*
|
|
* If this is a S3 resume, resume the TPM.
|
|
*
|
|
* FIXME: I think U-Boot won't ever ask us to do this. Can we remove
|
|
* it?
|
|
*/
|
|
if (is_s3_resume) {
|
|
if (TPM_SUCCESS != RollbackS3Resume()) {
|
|
/*
|
|
* If we can't resume, just do a full reboot. No need
|
|
* to go to recovery mode here, since if the TPM is
|
|
* really broken we'll catch it on the next boot.
|
|
*/
|
|
retval = VBERROR_TPM_S3_RESUME;
|
|
}
|
|
} else {
|
|
/* Should we pay attention to the TPM's virtual dev-switch? */
|
|
if (iparams->flags & VB_INIT_FLAG_VIRTUAL_DEV_SWITCH) {
|
|
shared->flags |= VBSD_HONOR_VIRT_DEV_SWITCH;
|
|
has_virt_dev_switch = 1;
|
|
}
|
|
|
|
/*
|
|
* We always believe the HW dev-switch, since there's one
|
|
* attached to servo which may be active even on systems
|
|
* without a physical switch. The EC may also implement a fake
|
|
* dev-switch for testing.
|
|
*/
|
|
if (iparams->flags & VB_INIT_FLAG_DEV_SWITCH_ON)
|
|
is_hw_dev = 1;
|
|
|
|
/* We may be asked to clear the virtual dev-switch at boot. */
|
|
VbNvGet(&vnc, VBNV_DISABLE_DEV_REQUEST, &disable_dev_request);
|
|
|
|
/* Allow GBB flag to override dev switch */
|
|
if (gbb.flags & GBB_FLAG_FORCE_DEV_SWITCH_ON)
|
|
is_hw_dev = 1;
|
|
|
|
/* Have we been explicitly asked to clear the TPM owner? */
|
|
VbNvGet(&vnc, VBNV_CLEAR_TPM_OWNER_REQUEST,
|
|
&clear_tpm_owner_request);
|
|
|
|
/*
|
|
* Initialize the TPM. If the developer mode state has changed
|
|
* since the last boot, we need to clear TPM ownership. If the
|
|
* TPM space is initialized by this call, the virtual
|
|
* dev-switch will be disabled by default)
|
|
*/
|
|
VBDEBUG(("TPM: Call RollbackFirmwareSetup(r%d, d%d)\n",
|
|
recovery, is_hw_dev));
|
|
tpm_status = RollbackFirmwareSetup(is_hw_dev,
|
|
disable_dev_request,
|
|
clear_tpm_owner_request,
|
|
/* two outputs on success */
|
|
&is_virt_dev, &tpm_version);
|
|
|
|
if (0 != tpm_status) {
|
|
VBDEBUG(("Unable to setup TPM and read "
|
|
"firmware version (0x%x)\n", tpm_status));
|
|
|
|
if (TPM_E_MUST_REBOOT == tpm_status) {
|
|
/*
|
|
* TPM wants to reboot into the same mode we're
|
|
* in now
|
|
*/
|
|
VBDEBUG(("TPM requires a reboot.\n"));
|
|
if (!recovery) {
|
|
/*
|
|
* Not recovery mode. Just reboot (not
|
|
* into recovery).
|
|
*/
|
|
retval = VBERROR_TPM_REBOOT_REQUIRED;
|
|
goto VbInit_exit;
|
|
} else if (VBNV_RECOVERY_RO_TPM_REBOOT !=
|
|
shared->recovery_reason) {
|
|
/*
|
|
* In recovery mode now, and we haven't
|
|
* requested a TPM reboot yet, so
|
|
* request one.
|
|
*/
|
|
VbNvSet(&vnc, VBNV_RECOVERY_REQUEST,
|
|
VBNV_RECOVERY_RO_TPM_REBOOT);
|
|
retval = VBERROR_TPM_REBOOT_REQUIRED;
|
|
goto VbInit_exit;
|
|
}
|
|
}
|
|
|
|
if (!recovery) {
|
|
VbNvSet(&vnc, VBNV_RECOVERY_REQUEST,
|
|
VBNV_RECOVERY_RO_TPM_S_ERROR);
|
|
VbNvSet(&vnc, VBNV_RECOVERY_SUBCODE,
|
|
tpm_status);
|
|
retval = VBERROR_TPM_FIRMWARE_SETUP;
|
|
goto VbInit_exit;
|
|
}
|
|
}
|
|
|
|
/* TPM setup succeeded, or we're in recovery mode and ignoring
|
|
* errors. What did we learn? */
|
|
shared->fw_version_tpm_start = tpm_version;
|
|
shared->fw_version_tpm = tpm_version;
|
|
if (is_hw_dev || (has_virt_dev_switch && is_virt_dev)) {
|
|
is_dev = 1;
|
|
shared->flags |= VBSD_BOOT_DEV_SWITCH_ON;
|
|
}
|
|
if (disable_dev_request && !is_virt_dev)
|
|
VbNvSet(&vnc, VBNV_DISABLE_DEV_REQUEST, 0);
|
|
if (clear_tpm_owner_request) {
|
|
VbNvSet(&vnc, VBNV_CLEAR_TPM_OWNER_REQUEST, 0);
|
|
VbNvSet(&vnc, VBNV_CLEAR_TPM_OWNER_DONE, 1);
|
|
}
|
|
}
|
|
|
|
/* Allow BIOS to load arbitrary option ROMs? */
|
|
if (gbb.flags & GBB_FLAG_LOAD_OPTION_ROMS)
|
|
iparams->out_flags |= VB_INIT_OUT_ENABLE_OPROM;
|
|
|
|
/* Factory may need to boot custom OSes when the dev-switch is on */
|
|
if (is_dev && (gbb.flags & GBB_FLAG_ENABLE_ALTERNATE_OS))
|
|
iparams->out_flags |= VB_INIT_OUT_ENABLE_ALTERNATE_OS;
|
|
|
|
/* Set output flags */
|
|
if (VBNV_RECOVERY_NOT_REQUESTED != recovery) {
|
|
/* Requesting recovery mode */
|
|
iparams->out_flags |= (VB_INIT_OUT_ENABLE_RECOVERY |
|
|
VB_INIT_OUT_CLEAR_RAM |
|
|
VB_INIT_OUT_ENABLE_DISPLAY |
|
|
VB_INIT_OUT_ENABLE_USB_STORAGE);
|
|
} else if (is_dev) {
|
|
/* Developer switch is on, so need to support dev mode */
|
|
iparams->out_flags |= (VB_INIT_OUT_ENABLE_DEVELOPER |
|
|
VB_INIT_OUT_CLEAR_RAM |
|
|
VB_INIT_OUT_ENABLE_DISPLAY |
|
|
VB_INIT_OUT_ENABLE_USB_STORAGE);
|
|
/* ... which may or may not include custom OSes */
|
|
VbNvGet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &require_official_os);
|
|
if (!require_official_os)
|
|
iparams->out_flags |= VB_INIT_OUT_ENABLE_ALTERNATE_OS;
|
|
|
|
/*
|
|
* Dev-mode needs the VGA option ROM to be loaded so it can
|
|
* display the scary boot screen. If we don't have it, we need
|
|
* to request it and reboot so it can be loaded.
|
|
*/
|
|
if ((iparams->flags & VB_INIT_FLAG_OPROM_MATTERS) &&
|
|
!(iparams->flags & VB_INIT_FLAG_OPROM_LOADED)) {
|
|
VbNvSet(&vnc, VBNV_OPROM_NEEDED, 1);
|
|
retval = VBERROR_VGA_OPROM_MISMATCH;
|
|
VBDEBUG(("VbInit() needs oprom, doesn't have it\n"));
|
|
}
|
|
|
|
} else {
|
|
/*
|
|
* Normal mode, so disable dev_boot_* flags. This ensures they
|
|
* will be initially disabled if the user later transitions
|
|
* back into developer mode.
|
|
*/
|
|
VbNvSet(&vnc, VBNV_DEV_BOOT_USB, 0);
|
|
VbNvSet(&vnc, VBNV_DEV_BOOT_LEGACY, 0);
|
|
VbNvSet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, 0);
|
|
|
|
/*
|
|
* If we don't need the VGA option ROM but got it anyway, stop
|
|
* asking for it and reboot in case there's some vulnerability
|
|
* in using it.
|
|
*/
|
|
if ((iparams->flags & VB_INIT_FLAG_OPROM_MATTERS) &&
|
|
(iparams->flags & VB_INIT_FLAG_OPROM_LOADED)) {
|
|
VbNvSet(&vnc, VBNV_OPROM_NEEDED, 0);
|
|
retval = VBERROR_VGA_OPROM_MISMATCH;
|
|
VBDEBUG(("VbInit() has oprom, doesn't need it\n"));
|
|
}
|
|
}
|
|
|
|
VbInit_exit:
|
|
|
|
/* Tear down NV storage */
|
|
VbNvTeardown(&vnc);
|
|
if (vnc.raw_changed)
|
|
VbExNvStorageWrite(vnc.raw);
|
|
|
|
VBDEBUG(("VbInit() output flags 0x%x\n", iparams->out_flags));
|
|
|
|
shared->timer_vb_init_exit = VbExGetTimer();
|
|
|
|
VBDEBUG(("VbInit() returning 0x%x\n", retval));
|
|
|
|
return retval;
|
|
}
|