/* 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 "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 = (GoogleBinaryBlockHeader *)cparams->gbb_data; 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; VBDEBUG(("VbInit() input flags 0x%x gbb flags 0x%x\n", iparams->flags, gbb->flags)); /* Initialize output flags */ iparams->out_flags = 0; /* 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; }