Home | History | Annotate | Download | only in lib
      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