mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-24 02:05:01 +00:00
When the lid is closed and external power is applied the system may boot and shut down faster than required for the OS to determine that things were alright. In timed charging setups this led to systems ending up to consider the current version broken because it "failed" repeatedly. Remain generic about the reason for not counting boots since there may be more situations in which we want to handle the situation optimistically. BRANCH=none BUG=chromium:446945 TEST=none Change-Id: Iea350e3c98d5c00156da682e52c90a882ba017c0 Signed-off-by: Patrick Georgi <pgeorgi@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/249150 Reviewed-by: Randall Spangler <rspangler@chromium.org>
377 lines
12 KiB
C
377 lines
12 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;
|
|
uint32_t backup_requested = 0;
|
|
uint32_t backup_for_safety = 0;
|
|
int lost_nvram;
|
|
|
|
/* 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);
|
|
lost_nvram = vnc.regenerate_crc;
|
|
|
|
/* 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_NOFAIL_BOOT)
|
|
shared->flags |= VBSD_NOFAIL_BOOT;
|
|
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;
|
|
if (iparams->flags & VB_INIT_FLAG_VIRTUAL_REC_SWITCH)
|
|
shared->flags |= VBSD_BOOT_REC_SWITCH_VIRTUAL;
|
|
if (iparams->flags & VB_INIT_FLAG_OPROM_MATTERS)
|
|
shared->flags |= VBSD_OPROM_MATTERS;
|
|
if (iparams->flags & VB_INIT_FLAG_OPROM_LOADED)
|
|
shared->flags |= VBSD_OPROM_LOADED;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the nvram state was lost, try to restore the bits we care about
|
|
* from the backup in the TPM. It's okay if we can't, though.
|
|
* Note: None of the bits that we back up should have been referenced
|
|
* before this point. Otherwise, they'll just be overwritten here.
|
|
* All the other bits will be unchanged from whatever has happened to
|
|
* them since VbNvSetup() reinitialized the VbNvContext.
|
|
*/
|
|
if (lost_nvram)
|
|
RestoreNvFromBackup(&vnc);
|
|
|
|
/* 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);
|
|
/*
|
|
* If VbInit() is run before Option ROMs are run it
|
|
* can still respond to the VbNv flag and does not
|
|
* need to reboot here.
|
|
*/
|
|
if (!(iparams->flags & VB_INIT_FLAG_BEFORE_OPROM_LOAD))
|
|
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);
|
|
VbNvSet(&vnc, VBNV_DEV_BOOT_FASTBOOT_FULL_CAP, 0);
|
|
/*
|
|
* Back up any changes now, so these values can't be forgotten
|
|
* by draining the battery. We really only care about these
|
|
* three fields, but it's uncommon for any others to change so
|
|
* this is an easier test than checking each one.
|
|
*/
|
|
if (vnc.regenerate_crc)
|
|
backup_for_safety = 1;
|
|
|
|
/*
|
|
* 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);
|
|
/*
|
|
* If VbInit() is run before Option ROMs are run it
|
|
* can still respond to the VbNv flag and does not
|
|
* need to reboot here.
|
|
*/
|
|
if (!(iparams->flags & VB_INIT_FLAG_BEFORE_OPROM_LOAD))
|
|
retval = VBERROR_VGA_OPROM_MISMATCH;
|
|
VBDEBUG(("VbInit() has oprom, doesn't need it\n"));
|
|
}
|
|
}
|
|
|
|
VbInit_exit:
|
|
/*
|
|
* If we successfully backup the NV storage, it will clear the
|
|
* VBNV_BACKUP_NVRAM_REQUEST field, so we want to do it before
|
|
* calling VbNvTeardown(). It's okay if we can't backup, though.
|
|
*/
|
|
VbNvGet(&vnc, VBNV_BACKUP_NVRAM_REQUEST, &backup_requested);
|
|
if (backup_requested || backup_for_safety)
|
|
SaveNvToBackup(&vnc);
|
|
|
|
/* 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;
|
|
}
|