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 
VbSelectFirmware(VbCommonParams * cparams,VbSelectFirmwareParams * fparams)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