1 /* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 2 * Use of this source code is governed by a BSD-style license that can be 3 * found in the LICENSE file. 4 * 5 * High-level firmware wrapper API - entry points for init, firmware selection 6 */ 7 8 #include "sysincludes.h" 9 10 #include "gbb_access.h" 11 #include "gbb_header.h" 12 #include "load_firmware_fw.h" 13 #include "rollback_index.h" 14 #include "tpm_bootmode.h" 15 #include "utility.h" 16 #include "vboot_api.h" 17 #include "vboot_common.h" 18 #include "vboot_nvstorage.h" 19 20 VbError_t VbSelectFirmware(VbCommonParams *cparams, 21 VbSelectFirmwareParams *fparams) 22 { 23 VbSharedDataHeader *shared = 24 (VbSharedDataHeader *)cparams->shared_data_blob; 25 VbNvContext vnc; 26 VbError_t retval = VBERROR_UNKNOWN; /* Default to error */ 27 int is_rec = (shared->recovery_reason ? 1 : 0); 28 int is_dev = (shared->flags & VBSD_BOOT_DEV_SWITCH_ON ? 1 : 0); 29 uint32_t tpm_status = 0; 30 31 cparams->gbb = NULL; 32 cparams->bmp = NULL; 33 34 /* Start timer */ 35 shared->timer_vb_select_firmware_enter = VbExGetTimer(); 36 37 /* Load NV storage */ 38 VbExNvStorageRead(vnc.raw); 39 VbNvSetup(&vnc); 40 41 if (is_rec) { 42 /* 43 * Recovery is requested; go straight to recovery without 44 * checking the RW firmware. 45 */ 46 VBDEBUG(("VbSelectFirmware() detected recovery request\n")); 47 48 /* Best effort to read the GBB */ 49 cparams->gbb = VbExMalloc(sizeof(*cparams->gbb)); 50 retval = VbGbbReadHeader_static(cparams, cparams->gbb); 51 if (VBERROR_SUCCESS != retval) { 52 VBDEBUG(("Can't read GBB. Continuing anyway...\n")); 53 VbExFree(cparams->gbb); 54 cparams->gbb = NULL; 55 } 56 57 /* Go directly to recovery mode */ 58 fparams->selected_firmware = VB_SELECT_FIRMWARE_RECOVERY; 59 } else { 60 cparams->gbb = VbExMalloc(sizeof(*cparams->gbb)); 61 retval = VbGbbReadHeader_static(cparams, cparams->gbb); 62 if (VBERROR_SUCCESS != retval) 63 goto VbSelectFirmware_exit; 64 65 /* Chain to LoadFirmware() */ 66 retval = LoadFirmware(cparams, fparams, &vnc); 67 68 /* Exit if we failed to find an acceptable firmware */ 69 if (VBERROR_SUCCESS != retval) 70 goto VbSelectFirmware_exit; 71 72 /* Translate the selected firmware path */ 73 if (shared->flags & VBSD_LF_USE_RO_NORMAL) { 74 /* Request the read-only normal/dev code path */ 75 fparams->selected_firmware = 76 VB_SELECT_FIRMWARE_READONLY; 77 } else if (0 == shared->firmware_index) 78 fparams->selected_firmware = VB_SELECT_FIRMWARE_A; 79 else { 80 fparams->selected_firmware = VB_SELECT_FIRMWARE_B; 81 } 82 83 /* Update TPM if necessary */ 84 if (shared->fw_version_tpm_start < shared->fw_version_tpm) { 85 tpm_status = 86 RollbackFirmwareWrite(shared->fw_version_tpm); 87 if (0 != tpm_status) { 88 VBDEBUG(("Can't write FW version to TPM.\n")); 89 VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 90 VBNV_RECOVERY_RO_TPM_W_ERROR); 91 retval = VBERROR_TPM_WRITE_FIRMWARE; 92 goto VbSelectFirmware_exit; 93 } 94 } 95 96 /* Lock firmware versions in TPM */ 97 tpm_status = RollbackFirmwareLock(); 98 if (0 != tpm_status) { 99 VBDEBUG(("Unable to lock firmware version in TPM.\n")); 100 VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 101 VBNV_RECOVERY_RO_TPM_L_ERROR); 102 retval = VBERROR_TPM_LOCK_FIRMWARE; 103 goto VbSelectFirmware_exit; 104 } 105 } 106 107 /* 108 * At this point, we have a good idea of how we are going to 109 * boot. Update the TPM with this state information. 110 */ 111 tpm_status = SetTPMBootModeState(is_dev, is_rec, 112 shared->fw_keyblock_flags, 113 cparams->gbb); 114 if (0 != tpm_status) { 115 VBDEBUG(("Can't update the TPM with boot mode information.\n")); 116 if (!is_rec) { 117 VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 118 VBNV_RECOVERY_RO_TPM_U_ERROR); 119 retval = VBERROR_TPM_SET_BOOT_MODE_STATE; 120 goto VbSelectFirmware_exit; 121 } 122 } 123 124 /* Success! */ 125 retval = VBERROR_SUCCESS; 126 127 VbSelectFirmware_exit: 128 129 if (cparams->gbb) { 130 VbExFree(cparams->gbb); 131 cparams->gbb = NULL; 132 } 133 134 /* Save NV storage */ 135 VbNvTeardown(&vnc); 136 if (vnc.raw_changed) 137 VbExNvStorageWrite(vnc.raw); 138 139 /* Stop timer */ 140 shared->timer_vb_select_firmware_exit = VbExGetTimer(); 141 142 /* Should always have a known error code */ 143 VbAssert(VBERROR_UNKNOWN != retval); 144 145 return retval; 146 } 147