xref: /aosp_15_r20/external/coreboot/src/drivers/tpm/cr50.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: BSD-3-Clause */
2 
3 #include <delay.h>
4 #include <drivers/spi/tpm/tpm.h>
5 #include <security/tpm/tis.h>
6 #include <string.h>
7 #include <timer.h>
8 #include <types.h>
9 
10 #define CR50_DID_VID	0x00281ae0L
11 #define TI50_DT_DID_VID	0x504a6666L
12 #define TI50_OT_DID_VID	0x50666666L
13 
14 #define CR50_BOARD_CFG_LOCKBIT_MASK 0x80000000U
15 #define CR50_BOARD_CFG_FEATUREBITS_MASK 0x3FFFFFFFU
16 
17 #define CR50_BOARD_CFG_100US_READY_PULSE 0x00000001U
18 #define CR50_BOARD_CFG_VALUE \
19 		(CONFIG(CR50_USE_LONG_INTERRUPT_PULSES) \
20 		 ? CR50_BOARD_CFG_100US_READY_PULSE : 0)
21 
22 #define CR50_TIMEOUT_NOIRQ_MS	20	/* Timeout for TPM ready without IRQ */
23 
24 enum cr50_register {
25 	CR50_FW_VER_REG,
26 	CR50_BOARD_CFG_REG,
27 };
28 
29 #define CR50_FW_VER_REG_SPI		(TPM_LOCALITY_0_SPI_BASE + 0xf90)
30 #define CR50_BOARD_CFG_REG_SPI		(TPM_LOCALITY_0_SPI_BASE + 0xfe0)
31 
32 #define CR50_FW_VER_REG_I2C		0x0f
33 #define CR50_BOARD_CFG_REG_I2C		0x1c
34 
35 /* Return register address, which depends on the bus type, or -1 for error. */
get_reg_addr(enum cr50_register reg)36 static int get_reg_addr(enum cr50_register reg)
37 {
38 	if (CONFIG(SPI_TPM)) {
39 		switch (reg) {
40 		case CR50_FW_VER_REG:
41 			return CR50_FW_VER_REG_SPI;
42 		case CR50_BOARD_CFG_REG:
43 			return CR50_BOARD_CFG_REG_SPI;
44 		default:
45 			return -1;
46 		}
47 	}
48 
49 	if (CONFIG(I2C_TPM)) {
50 		switch (reg) {
51 		case CR50_FW_VER_REG:
52 			return CR50_FW_VER_REG_I2C;
53 		case CR50_BOARD_CFG_REG:
54 			return CR50_BOARD_CFG_REG_I2C;
55 		default:
56 			return -1;
57 		}
58 	}
59 
60 	return -1;
61 }
62 
cr50_fw_supports_board_cfg(struct cr50_firmware_version * version)63 static bool cr50_fw_supports_board_cfg(struct cr50_firmware_version *version)
64 {
65 	/* Cr50 supports the CR50_BOARD_CFG register from version 0.5.5 / 0.6.5
66 	 * and onwards. */
67 	if (version->epoch > 0 || version->major >= 7
68 	    || (version->major >= 5 && version->minor >= 5))
69 		return true;
70 
71 	printk(BIOS_INFO, "Cr50 firmware does not support CR50_BOARD_CFG, version: %d.%d.%d\n",
72 	       version->epoch, version->major, version->minor);
73 
74 	return false;
75 }
76 
77 /*
78  * Expose method to read the CR50_BOARD_CFG register, will return zero if
79  * register not supported by Cr50 firmware.
80  */
cr50_get_board_cfg(void)81 static uint32_t cr50_get_board_cfg(void)
82 {
83 	struct cr50_firmware_version ver;
84 	uint32_t value;
85 
86 	if (cr50_get_firmware_version(&ver) != CB_SUCCESS)
87 		return 0;
88 
89 	if (!cr50_fw_supports_board_cfg(&ver))
90 		return 0;
91 
92 	const enum cb_err ret = tis_vendor_read(get_reg_addr(CR50_BOARD_CFG_REG), &value,
93 					     sizeof(value));
94 	if (ret != CB_SUCCESS) {
95 		printk(BIOS_ERR, "Error reading from Cr50\n");
96 		return 0;
97 	}
98 
99 	return value & CR50_BOARD_CFG_FEATUREBITS_MASK;
100 }
101 
cr50_plat_irq_status(void)102 __weak int cr50_plat_irq_status(void)
103 {
104 	static int warning_displayed;
105 
106 	if (!warning_displayed) {
107 		printk(BIOS_WARNING, "%s() not implemented, wasting 20ms to wait on Cr50!\n",
108 		       __func__);
109 		warning_displayed = 1;
110 	}
111 	mdelay(CR50_TIMEOUT_NOIRQ_MS);
112 
113 	return 1;
114 }
115 
116 /**
117  * Set the BOARD_CFG register on the TPM chip to a particular compile-time constant value.
118  */
cr50_set_board_cfg(void)119 enum cb_err cr50_set_board_cfg(void)
120 {
121 	/* If we get here and we aren't cr50, then we must be ti50 which does
122 	 * not currently need to support a board_cfg register. */
123 	if (!CONFIG(TPM_GOOGLE_CR50))
124 		return CB_SUCCESS;
125 
126 	struct cr50_firmware_version ver;
127 	enum cb_err ret;
128 	uint32_t value;
129 
130 	if (cr50_get_firmware_version(&ver) != CB_SUCCESS)
131 		return CB_ERR;
132 
133 	if (!cr50_fw_supports_board_cfg(&ver))
134 		return CB_ERR;
135 
136 	/* Set the CR50_BOARD_CFG register, for e.g. asking cr50 to use longer ready pulses. */
137 	ret = tis_vendor_read(get_reg_addr(CR50_BOARD_CFG_REG), &value, sizeof(value));
138 	if (ret != CB_SUCCESS) {
139 		printk(BIOS_ERR, "Error reading from Cr50\n");
140 		return CB_ERR;
141 	}
142 
143 	if ((value & CR50_BOARD_CFG_FEATUREBITS_MASK) == CR50_BOARD_CFG_VALUE) {
144 		printk(BIOS_INFO, "Current CR50_BOARD_CFG = 0x%08x, matches desired = 0x%08x\n",
145 		       value, CR50_BOARD_CFG_VALUE);
146 		return CB_SUCCESS;
147 	}
148 
149 	if (value & CR50_BOARD_CFG_LOCKBIT_MASK) {
150 		/* The high bit is set, meaning that the Cr50 is already locked on a particular
151 		 * value for the register, but not the one we wanted. */
152 		printk(BIOS_ERR, "Current CR50_BOARD_CFG = 0x%08x, does not match"
153 		       "desired = 0x%08x\n", value, CR50_BOARD_CFG_VALUE);
154 		return CB_ERR;
155 	}
156 
157 	printk(BIOS_INFO, "Current CR50_BOARD_CFG = 0x%08x, setting to 0x%08x\n",
158 	       value, CR50_BOARD_CFG_VALUE);
159 	value = CR50_BOARD_CFG_VALUE;
160 
161 	ret = tis_vendor_write(get_reg_addr(CR50_BOARD_CFG_REG), &value, sizeof(value));
162 	if (ret != CB_SUCCESS) {
163 		printk(BIOS_ERR, "Error writing to cr50\n");
164 		return ret;
165 	}
166 
167 	return CB_SUCCESS;
168 }
169 
cr50_is_long_interrupt_pulse_enabled(void)170 bool cr50_is_long_interrupt_pulse_enabled(void)
171 {
172 	if (CONFIG(TPM_GOOGLE_CR50))
173 		return !!(cr50_get_board_cfg() & CR50_BOARD_CFG_100US_READY_PULSE);
174 
175         /* Ti50 and future GSCs will support only long interrupt pulses. */
176         return true;
177 }
178 
cr50_parse_fw_version(const char * version_str,struct cr50_firmware_version * ver)179 static enum cb_err cr50_parse_fw_version(const char *version_str,
180 					 struct cr50_firmware_version *ver)
181 {
182 	int epoch, major, minor;
183 
184 	char *number = strstr(version_str, " RW_A:");
185 	if (!number)
186 		number = strstr(version_str, " RW_B:");
187 	if (!number)
188 		return CB_ERR_ARG;
189 	number += 6; /* Skip past the colon. */
190 
191 	epoch = skip_atoi(&number);
192 	if (*number++ != '.')
193 		return CB_ERR_ARG;
194 	major = skip_atoi(&number);
195 	if (*number++ != '.')
196 		return CB_ERR_ARG;
197 	minor = skip_atoi(&number);
198 
199 	ver->epoch = epoch;
200 	ver->major = major;
201 	ver->minor = minor;
202 	return CB_SUCCESS;
203 }
204 
cr50_get_firmware_version(struct cr50_firmware_version * version)205 enum cb_err cr50_get_firmware_version(struct cr50_firmware_version *version)
206 {
207 	static struct cr50_firmware_version cr50_firmware_version;
208 
209 	if (cr50_firmware_version.epoch || cr50_firmware_version.major ||
210 	    cr50_firmware_version.minor)
211 		goto success;
212 
213 	int chunk_count = 0;
214 	size_t chunk_size = 50;
215 	char version_str[301];
216 	int addr = get_reg_addr(CR50_FW_VER_REG);
217 
218 	/*
219 	 * Does not really matter what's written, this just makes sure
220 	 * the version is reported from the beginning.
221 	 */
222 	tis_vendor_write(addr, &chunk_size, 1);
223 
224 	/*
225 	 * Read chunk_size bytes at a time, last chunk will be zero padded.
226 	 */
227 	do {
228 		uint8_t *buf = (uint8_t *)version_str + chunk_count * chunk_size;
229 		tis_vendor_read(addr, buf, chunk_size);
230 		if (!version_str[++chunk_count * chunk_size - 1])
231 			/* Zero padding detected: end of string. */
232 			break;
233 		/* Check if there is enough room for reading one more chunk. */
234 	} while (chunk_count * chunk_size < sizeof(version_str) - chunk_size);
235 
236 	version_str[chunk_count * chunk_size] = '\0';
237 	printk(BIOS_INFO, "Firmware version: %s\n", version_str);
238 
239 	if (cr50_parse_fw_version(version_str, &cr50_firmware_version) != CB_SUCCESS) {
240 		printk(BIOS_ERR, "Did not recognize Cr50 version format\n");
241 		return CB_ERR;
242 	}
243 
244 success:
245 	if (version)
246 		*version = cr50_firmware_version;
247 	return CB_SUCCESS;
248 }
249 
cr50_wait_tpm_ready(void)250 enum cb_err cr50_wait_tpm_ready(void)
251 {
252 	struct stopwatch sw;
253 
254 	stopwatch_init_msecs_expire(&sw, CONFIG_GOOGLE_TPM_IRQ_TIMEOUT_MS);
255 
256 	while (!cr50_plat_irq_status())
257 		if (stopwatch_expired(&sw)) {
258 			printk(BIOS_ERR, "Cr50 TPM IRQ timeout!\n");
259 			return CB_ERR;
260 		}
261 
262 	return CB_SUCCESS;
263 }
264