xref: /aosp_15_r20/external/coreboot/src/vendorcode/google/chromeos/cr50_enable_update.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <bootstate.h>
4 #include <commonlib/console/post_codes.h>
5 #include <console/console.h>
6 #include <ec/google/chromeec/ec.h>
7 #include <elog.h>
8 #include <halt.h>
9 #include <security/tpm/tss.h>
10 #include <vb2_api.h>
11 #include <security/vboot/misc.h>
12 #include <security/vboot/vboot_common.h>
13 #include <vendorcode/google/chromeos/chromeos.h>
14 #include <timestamp.h>
15 
16 #define CR50_RESET_DELAY_MS 1000
17 
mainboard_prepare_cr50_reset(void)18 void __weak mainboard_prepare_cr50_reset(void) {}
19 
20 /**
21  * Check if the Cr50 TPM state requires a chip reset of the Cr50 device.
22  *
23  * Returns 0 if the Cr50 TPM state is good or if the TPM_MODE command is
24  * unsupported.  Returns 1 if the Cr50 requires a reset.
25  */
cr50_is_reset_needed(void)26 static int cr50_is_reset_needed(void)
27 {
28 	tpm_result_t rc;
29 	uint8_t tpm_mode;
30 
31 	rc = tlcl_cr50_get_tpm_mode(&tpm_mode);
32 
33 	if (rc == TPM_CB_NO_SUCH_COMMAND) {
34 		printk(BIOS_INFO,
35 		       "Cr50 does not support TPM mode command\n");
36 		/* Older Cr50 firmware, assume no Cr50 reset is required */
37 		return 0;
38 	}
39 
40 	if (rc == TPM_CB_MUST_REBOOT) {
41 		/*
42 		 * Cr50 indicated a reboot is required to restore TPM
43 		 * functionality.
44 		 */
45 		return 1;
46 	} else if (rc != TPM_SUCCESS)	{
47 		/* TPM command failed, continue booting. */
48 		printk(BIOS_ERR, "Attempt to get CR50 TPM mode failed: %#x\n", rc);
49 		return 0;
50 	}
51 
52 	/*
53 	 * If the TPM mode is not enabled-tentative, then the TPM mode is locked
54 	 * and cannot be changed.  Perform a Cr50 reset because vboot may need
55 	 * to disable TPM as part of booting an untrusted OS.
56 	 *
57 	 * This is not an expected state, as the Cr50 always sets the TPM mode
58 	 * to TPM_MODE_ENABLED_TENTATIVE during any TPM reset action.
59 	 */
60 	if (tpm_mode != TPM_MODE_ENABLED_TENTATIVE) {
61 		printk(BIOS_NOTICE,
62 		       "NOTICE: Unexpected Cr50 TPM mode (%d). "
63 		       "A Cr50 reset is required.\n", tpm_mode);
64 		return 1;
65 	}
66 
67 	/* If TPM state is okay, no reset needed. */
68 	return 0;
69 }
70 
enable_update(void * unused)71 static void enable_update(void *unused)
72 {
73 	tpm_result_t rc;
74 	int cr50_reset_reqd = 0;
75 	uint8_t num_restored_headers;
76 
77 	/**
78 	 * Never update during manually-triggered recovery to ensure update
79 	 * cannot interfere. Non-manual VB2_RECOVERY_TRAIN_AND_REBOOT
80 	 * sometimes used to update in factory.
81 	 */
82 	if (vboot_get_context()->flags & VB2_CONTEXT_FORCE_RECOVERY_MODE)
83 		return;
84 
85 	rc = tlcl_lib_init();
86 
87 	if (rc != TPM_SUCCESS) {
88 		printk(BIOS_ERR, "tlcl_lib_init() failed for CR50 update: %#x\n",
89 		       rc);
90 		return;
91 	}
92 
93 	timestamp_add_now(TS_TPM_ENABLE_UPDATE_START);
94 
95 	/* Reboot in 1000 ms if necessary. */
96 	rc = tlcl_cr50_enable_update(CR50_RESET_DELAY_MS,
97 				      &num_restored_headers);
98 
99 	if (rc != TPM_SUCCESS) {
100 		printk(BIOS_ERR, "Attempt to enable CR50 update failed: %#x\n",
101 		       rc);
102 		return;
103 	}
104 
105 	if (!num_restored_headers) {
106 		/* If no headers were restored there is no reset forthcoming due
107 		 * to a Cr50 firmware update.  Also check if the Cr50 TPM mode
108 		 * requires a reset.
109 		 *
110 		 * TODO: to eliminate a TPM command during every boot, the
111 		 * TURN_UPDATE_ON command could be enhanced/replaced in the Cr50
112 		 * firmware to perform the TPM mode/key-ladder check in addition
113 		 * to the FW version check.
114 		 */
115 
116 		/*
117 		 * If the Cr50 doesn't requires a reset, continue booting.
118 		 */
119 		cr50_reset_reqd = cr50_is_reset_needed();
120 		if (!cr50_reset_reqd) {
121 			timestamp_add_now(TS_TPM_ENABLE_UPDATE_END);
122 			return;
123 		}
124 
125 		printk(BIOS_INFO, "Waiting for CR50 reset to enable TPM.\n");
126 		elog_add_event(ELOG_TYPE_CR50_NEED_RESET);
127 	} else {
128 		printk(BIOS_INFO,
129 		       "Waiting for CR50 reset to pick up update.\n");
130 		elog_add_event(ELOG_TYPE_CR50_UPDATE);
131 	}
132 
133 	/* Give mainboard a chance to take action */
134 	mainboard_prepare_cr50_reset();
135 
136 	/* clear current post code avoid chatty eventlog on subsequent boot*/
137 	post_code(POSTCODE_CODE_CLEAR);
138 
139 	/*
140 	 * Older Cr50 firmware doesn't support the timeout parameter for the
141 	 * immediate reset request, so the reset request must be sent after
142 	 * the mainboard specific code runs.
143 	 */
144 	if (cr50_reset_reqd) {
145 		rc = tlcl_cr50_immediate_reset(CR50_RESET_DELAY_MS);
146 
147 		if (rc != TPM_SUCCESS) {
148 			/*
149 			 * Reset request failed due to TPM error, continue
150 			 * booting but the current boot will likely end up at
151 			 * the recovery screen.
152 			 */
153 			printk(BIOS_ERR, "Attempt to reset CR50 failed: %#x\n",
154 			       rc);
155 			return;
156 		}
157 	}
158 
159 	if (CONFIG(POWER_OFF_ON_CR50_UPDATE)) {
160 		if (CONFIG(CR50_RESET_CLEAR_EC_AP_IDLE_FLAG))
161 			google_chromeec_clear_ec_ap_idle();
162 		poweroff();
163 	}
164 	halt();
165 }
166 BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_ENTRY, enable_update, NULL);
167