xref: /aosp_15_r20/external/coreboot/src/security/tpm/tss/tcg-2.0/tss.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: BSD-3-Clause */
2 
3 #include <console/console.h>
4 #include <endian.h>
5 #include <string.h>
6 #include <vb2_api.h>
7 #include <security/tpm/tis.h>
8 #include <security/tpm/tss.h>
9 
10 #include "tss_structures.h"
11 #include "tss_marshaling.h"
12 
13 /*
14  * This file provides interface between firmware and TPM2 device. The TPM1.2
15  * API was copied as is and relevant functions modified to comply with the
16  * TPM2 specification.
17  */
18 
tlcl2_process_command(TPM_CC command,void * command_body)19 void *tlcl2_process_command(TPM_CC command, void *command_body)
20 {
21 	struct obuf ob;
22 	struct ibuf ib;
23 	size_t out_size;
24 	size_t in_size;
25 	const uint8_t *sendb;
26 	/* Command/response buffer. */
27 	static uint8_t cr_buffer[TPM_BUFFER_SIZE];
28 
29 	if (tlcl_tis_sendrecv == NULL) {
30 		printk(BIOS_ERR, "Attempted use of uninitialized TSS 2.0 stack\n");
31 		return NULL;
32 	}
33 
34 	obuf_init(&ob, cr_buffer, sizeof(cr_buffer));
35 
36 	if (tpm_marshal_command(command, command_body, &ob) < 0) {
37 		printk(BIOS_ERR, "command %#x\n", command);
38 		return NULL;
39 	}
40 
41 	sendb = obuf_contents(&ob, &out_size);
42 
43 	in_size = sizeof(cr_buffer);
44 	if (tlcl_tis_sendrecv(sendb, out_size, cr_buffer, &in_size)) {
45 		printk(BIOS_ERR, "tpm transaction failed\n");
46 		return NULL;
47 	}
48 
49 	ibuf_init(&ib, cr_buffer, in_size);
50 
51 	return tpm_unmarshal_response(command, &ib);
52 }
53 
tlcl2_send_startup(TPM_SU type)54 static tpm_result_t tlcl2_send_startup(TPM_SU type)
55 {
56 	struct tpm2_startup startup;
57 	struct tpm2_response *response;
58 
59 	startup.startup_type = type;
60 	response = tlcl2_process_command(TPM2_Startup, &startup);
61 
62 	/* IO error, tpm2_response pointer is empty. */
63 	if (!response) {
64 		printk(BIOS_ERR, "%s: TPM communication error\n", __func__);
65 		return TPM_IOERROR;
66 	}
67 
68 	printk(BIOS_INFO, "%s: Startup return code is %#x\n",
69 	       __func__, response->hdr.tpm_code);
70 
71 	switch (response->hdr.tpm_code) {
72 	case TPM_RC_INITIALIZE:
73 		/* TPM already initialized. */
74 		return TPM_INVALID_POSTINIT;
75 	case TPM2_RC_SUCCESS:
76 		return TPM_SUCCESS;
77 	}
78 
79 	/* Collapse any other errors into TPM_IOERROR. */
80 	return TPM_IOERROR;
81 }
82 
tlcl2_resume(void)83 tpm_result_t tlcl2_resume(void)
84 {
85 	return tlcl2_send_startup(TPM_SU_STATE);
86 }
87 
tlcl2_send_shutdown(TPM_SU type)88 static tpm_result_t tlcl2_send_shutdown(TPM_SU type)
89 {
90 	struct tpm2_shutdown shutdown;
91 	struct tpm2_response *response;
92 
93 	shutdown.shutdown_type = type;
94 	response = tlcl2_process_command(TPM2_Shutdown, &shutdown);
95 
96 	/* IO error, tpm2_response pointer is empty. */
97 	if (!response) {
98 		printk(BIOS_ERR, "%s: TPM communication error\n", __func__);
99 		return TPM_IOERROR;
100 	}
101 
102 	printk(BIOS_INFO, "%s: Shutdown return code is %#x\n",
103 	       __func__, response->hdr.tpm_code);
104 
105 	if (response->hdr.tpm_code == TPM2_RC_SUCCESS)
106 		return TPM_SUCCESS;
107 
108 	/* Collapse any other errors into TPM_IOERROR. */
109 	return TPM_IOERROR;
110 }
111 
tlcl2_save_state(void)112 tpm_result_t tlcl2_save_state(void)
113 {
114 	return tlcl2_send_shutdown(TPM_SU_STATE);
115 }
116 
tlcl2_assert_physical_presence(void)117 tpm_result_t tlcl2_assert_physical_presence(void)
118 {
119 	/*
120 	 * Nothing to do on TPM2 for this, use platform hierarchy availability
121 	 * instead.
122 	 */
123 	return TPM_SUCCESS;
124 }
125 
tpmalg_from_vb2_hash(enum vb2_hash_algorithm hash_type)126 static TPM_ALG_ID tpmalg_from_vb2_hash(enum vb2_hash_algorithm hash_type)
127 {
128 	switch (hash_type) {
129 	case VB2_HASH_SHA1:
130 		return TPM_ALG_SHA1;
131 	case VB2_HASH_SHA256:
132 		return TPM_ALG_SHA256;
133 	case VB2_HASH_SHA384:
134 		return TPM_ALG_SHA384;
135 	case VB2_HASH_SHA512:
136 		return TPM_ALG_SHA512;
137 
138 	default:
139 		return TPM_ALG_ERROR;
140 	}
141 }
142 
143 /*
144  * The caller will provide the digest in a 32 byte buffer, let's consider it a
145  * sha256 digest.
146  */
tlcl2_extend(int pcr_num,const uint8_t * digest_data,enum vb2_hash_algorithm digest_type)147 tpm_result_t tlcl2_extend(int pcr_num, const uint8_t *digest_data,
148 			  enum vb2_hash_algorithm digest_type)
149 {
150 	struct tpm2_pcr_extend_cmd pcr_ext_cmd;
151 	struct tpm2_response *response;
152 	TPM_ALG_ID alg;
153 
154 	alg = tpmalg_from_vb2_hash(digest_type);
155 	if (alg == TPM_ALG_ERROR)
156 		return TPM_CB_HASH_ERROR;
157 
158 	pcr_ext_cmd.pcrHandle = HR_PCR + pcr_num;
159 	pcr_ext_cmd.digests.count = 1;
160 	pcr_ext_cmd.digests.digests[0].hashAlg = alg;
161 	/* Always copying to sha512 as it's the largest one */
162 	memcpy(pcr_ext_cmd.digests.digests[0].digest.sha512, digest_data,
163 	       vb2_digest_size(digest_type));
164 
165 	response = tlcl2_process_command(TPM2_PCR_Extend, &pcr_ext_cmd);
166 
167 	printk(BIOS_INFO, "%s: response is %#x\n",
168 	       __func__, response ? response->hdr.tpm_code : -1);
169 	if (!response || response->hdr.tpm_code)
170 		return TPM_IOERROR;
171 
172 	return TPM_SUCCESS;
173 }
174 
tlcl2_finalize_physical_presence(void)175 tpm_result_t tlcl2_finalize_physical_presence(void)
176 {
177 	/* Nothing needs to be done with tpm2. */
178 	printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
179 	return TPM_SUCCESS;
180 }
181 
tlcl2_force_clear(void)182 tpm_result_t tlcl2_force_clear(void)
183 {
184 	struct tpm2_response *response;
185 
186 	response = tlcl2_process_command(TPM2_Clear, NULL);
187 	printk(BIOS_INFO, "%s: response is %#x\n",
188 	       __func__, response ? response->hdr.tpm_code : -1);
189 
190 	if (!response || response->hdr.tpm_code)
191 		return TPM_IOERROR;
192 
193 	return TPM_SUCCESS;
194 }
195 
tlcl2_clear_control(bool disable)196 tpm_result_t tlcl2_clear_control(bool disable)
197 {
198 	struct tpm2_response *response;
199 	struct tpm2_clear_control_cmd cc = {
200 		.disable = 0,
201 	};
202 
203 	response = tlcl2_process_command(TPM2_ClearControl, &cc);
204 	printk(BIOS_INFO, "%s: response is %#x\n",
205 	       __func__, response ? response->hdr.tpm_code : -1);
206 
207 	if (!response || response->hdr.tpm_code)
208 		return TPM_IOERROR;
209 
210 	return TPM_SUCCESS;
211 }
212 
tlcl2_physical_presence_cmd_enable(void)213 tpm_result_t tlcl2_physical_presence_cmd_enable(void)
214 {
215 	printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
216 	return TPM_SUCCESS;
217 }
218 
tlcl2_read(uint32_t index,void * data,uint32_t length)219 tpm_result_t tlcl2_read(uint32_t index, void *data, uint32_t length)
220 {
221 	struct tpm2_nv_read_cmd nv_readc;
222 	struct tpm2_response *response;
223 
224 	memset(&nv_readc, 0, sizeof(nv_readc));
225 
226 	nv_readc.nvIndex = HR_NV_INDEX + index;
227 	nv_readc.size = length;
228 
229 	response = tlcl2_process_command(TPM2_NV_Read, &nv_readc);
230 
231 	/* Need to map tpm error codes into internal values. */
232 	if (!response)
233 		return TPM_CB_READ_FAILURE;
234 
235 	printk(BIOS_INFO, "%s:%d index %#x return code %#x\n",
236 	       __FILE__, __LINE__, index, response->hdr.tpm_code);
237 	switch (response->hdr.tpm_code) {
238 	case 0:
239 		break;
240 
241 		/* Uninitialized, returned if the space hasn't been written. */
242 	case TPM_RC_NV_UNINITIALIZED:
243 		/*
244 		 * Bad index, cr50 specific value, returned if the space
245 		 * hasn't been defined.
246 		 */
247 	case TPM_RC_CR50_NV_UNDEFINED:
248 		return TPM_BADINDEX;
249 
250 	case TPM_RC_NV_RANGE:
251 		return TPM_CB_RANGE;
252 
253 	default:
254 		return TPM_CB_READ_FAILURE;
255 	}
256 
257 	if (length > response->nvr.buffer.t.size)
258 		return TPM_CB_RESPONSE_TOO_LARGE;
259 
260 	if (length < response->nvr.buffer.t.size)
261 		return TPM_CB_READ_EMPTY;
262 
263 	memcpy(data, response->nvr.buffer.t.buffer, length);
264 
265 	return TPM_SUCCESS;
266 }
267 
tlcl2_self_test_full(void)268 tpm_result_t tlcl2_self_test_full(void)
269 {
270 	struct tpm2_self_test st;
271 	struct tpm2_response *response;
272 
273 	st.yes_no = 1;
274 
275 	response = tlcl2_process_command(TPM2_SelfTest, &st);
276 	printk(BIOS_INFO, "%s: response is %#x\n",
277 	       __func__, response ? response->hdr.tpm_code : -1);
278 	return TPM_SUCCESS;
279 }
280 
tlcl2_lock_nv_write(uint32_t index)281 tpm_result_t tlcl2_lock_nv_write(uint32_t index)
282 {
283 	struct tpm2_response *response;
284 	/* TPM Will reject attempts to write at non-defined index. */
285 	struct tpm2_nv_write_lock_cmd nv_wl = {
286 		.nvIndex = HR_NV_INDEX + index,
287 	};
288 
289 	response = tlcl2_process_command(TPM2_NV_WriteLock, &nv_wl);
290 
291 	printk(BIOS_INFO, "%s: response is %#x\n",
292 	       __func__, response ? response->hdr.tpm_code : -1);
293 
294 	if (!response || response->hdr.tpm_code)
295 		return TPM_IOERROR;
296 
297 	return TPM_SUCCESS;
298 }
299 
tlcl2_startup(void)300 tpm_result_t tlcl2_startup(void)
301 {
302 	return tlcl2_send_startup(TPM_SU_CLEAR);
303 }
304 
tlcl2_write(uint32_t index,const void * data,uint32_t length)305 tpm_result_t tlcl2_write(uint32_t index, const void *data, uint32_t length)
306 {
307 	struct tpm2_nv_write_cmd nv_writec;
308 	struct tpm2_response *response;
309 
310 	memset(&nv_writec, 0, sizeof(nv_writec));
311 
312 	nv_writec.nvIndex = HR_NV_INDEX + index;
313 	nv_writec.data.t.size = length;
314 	nv_writec.data.t.buffer = data;
315 
316 	response = tlcl2_process_command(TPM2_NV_Write, &nv_writec);
317 
318 	printk(BIOS_INFO, "%s: response is %#x\n",
319 	       __func__, response ? response->hdr.tpm_code : -1);
320 
321 	/* Need to map tpm error codes into internal values. */
322 	if (!response || response->hdr.tpm_code)
323 		return TPM_CB_WRITE_FAILURE;
324 
325 	return TPM_SUCCESS;
326 }
327 
tlcl2_set_bits(uint32_t index,uint64_t bits)328 tpm_result_t tlcl2_set_bits(uint32_t index, uint64_t bits)
329 {
330 	struct tpm2_nv_setbits_cmd nvsb_cmd;
331 	struct tpm2_response *response;
332 
333 	/* Prepare the command structure */
334 	memset(&nvsb_cmd, 0, sizeof(nvsb_cmd));
335 
336 	nvsb_cmd.nvIndex = HR_NV_INDEX + index;
337 	nvsb_cmd.bits = bits;
338 
339 	response = tlcl2_process_command(TPM2_NV_SetBits, &nvsb_cmd);
340 
341 	printk(BIOS_INFO, "%s: response is %#x\n",
342 	       __func__, response ? response->hdr.tpm_code : -1);
343 
344 	/* Need to map tpm error codes into internal values. */
345 	if (!response || response->hdr.tpm_code)
346 		return TPM_CB_WRITE_FAILURE;
347 
348 	return TPM_SUCCESS;
349 }
350 
tlcl2_define_space(uint32_t space_index,size_t space_size,const TPMA_NV nv_attributes,const uint8_t * nv_policy,size_t nv_policy_size)351 tpm_result_t tlcl2_define_space(uint32_t space_index, size_t space_size,
352 				const TPMA_NV nv_attributes,
353 				const uint8_t *nv_policy, size_t nv_policy_size)
354 {
355 	struct tpm2_nv_define_space_cmd nvds_cmd;
356 	struct tpm2_response *response;
357 
358 	/* Prepare the define space command structure. */
359 	memset(&nvds_cmd, 0, sizeof(nvds_cmd));
360 
361 	nvds_cmd.publicInfo.dataSize = space_size;
362 	nvds_cmd.publicInfo.nvIndex = HR_NV_INDEX + space_index;
363 	nvds_cmd.publicInfo.nameAlg = TPM_ALG_SHA256;
364 	nvds_cmd.publicInfo.attributes = nv_attributes;
365 
366 	/*
367 	 * Use policy digest based on default pcr0 value. This makes
368 	 * sure that the space can not be deleted as soon as PCR0
369 	 * value has been extended from default.
370 	 */
371 	if (nv_policy && nv_policy_size) {
372 		nvds_cmd.publicInfo.authPolicy.t.buffer = nv_policy;
373 		nvds_cmd.publicInfo.authPolicy.t.size = nv_policy_size;
374 	}
375 
376 	response = tlcl2_process_command(TPM2_NV_DefineSpace, &nvds_cmd);
377 	printk(BIOS_INFO, "%s: response is %#x\n", __func__,
378 	       response ? response->hdr.tpm_code : -1);
379 
380 	if (!response)
381 		return TPM_CB_NO_DEVICE;
382 
383 	/* Map TPM2 return codes into common vboot representation. */
384 	switch (response->hdr.tpm_code) {
385 	case TPM2_RC_SUCCESS:
386 		return TPM_SUCCESS;
387 	case TPM2_RC_NV_DEFINED:
388 		return TPM_CB_NV_DEFINED;
389 	default:
390 		return TPM_CB_INTERNAL_INCONSISTENCY;
391 	}
392 }
393 
tlcl2_get_hash_size_from_algo(TPMI_ALG_HASH hash_algo)394 uint16_t tlcl2_get_hash_size_from_algo(TPMI_ALG_HASH hash_algo)
395 {
396 	uint16_t value;
397 
398 	switch (hash_algo) {
399 	case TPM_ALG_ERROR:
400 		value = 1;
401 		break;
402 	case TPM_ALG_SHA1:
403 		value = SHA1_DIGEST_SIZE;
404 		break;
405 	case TPM_ALG_SHA256:
406 		value = SHA256_DIGEST_SIZE;
407 		break;
408 	case TPM_ALG_SHA384:
409 		value = SHA384_DIGEST_SIZE;
410 		break;
411 	case TPM_ALG_SHA512:
412 		value = SHA512_DIGEST_SIZE;
413 		break;
414 	case TPM_ALG_SM3_256:
415 		value = SM3_256_DIGEST_SIZE;
416 		break;
417 	default:
418 		printk(BIOS_SPEW, "%s: unknown hash algorithm %d\n", __func__,
419 		hash_algo);
420 		value = 0;
421 	};
422 
423 	return value;
424 }
425 
tlcl2_disable_platform_hierarchy(void)426 tpm_result_t tlcl2_disable_platform_hierarchy(void)
427 {
428 	struct tpm2_response *response;
429 	struct tpm2_hierarchy_control_cmd hc = {
430 		.enable = TPM_RH_PLATFORM,
431 		.state = 0,
432 	};
433 
434 	response = tlcl2_process_command(TPM2_Hierarchy_Control, &hc);
435 
436 	if (!response || response->hdr.tpm_code)
437 		return TPM_CB_INTERNAL_INCONSISTENCY;
438 
439 	return TPM_SUCCESS;
440 }
441 
tlcl2_get_capability(TPM_CAP capability,uint32_t property,uint32_t property_count,TPMS_CAPABILITY_DATA * capability_data)442 tpm_result_t tlcl2_get_capability(TPM_CAP capability, uint32_t property,
443 				  uint32_t property_count,
444 				  TPMS_CAPABILITY_DATA *capability_data)
445 {
446 	struct tpm2_get_capability cmd;
447 	struct tpm2_response *response;
448 
449 	cmd.capability = capability;
450 	cmd.property = property;
451 	cmd.propertyCount = property_count;
452 
453 	if (property_count > 1) {
454 		printk(BIOS_ERR, "%s: property_count more than one not "
455 		       "supported yet\n", __func__);
456 		return TPM_IOERROR;
457 	}
458 
459 	response = tlcl2_process_command(TPM2_GetCapability, &cmd);
460 
461 	if (!response) {
462 		printk(BIOS_ERR, "%s: Command Failed\n", __func__);
463 		return TPM_IOERROR;
464 	}
465 
466 	memcpy(capability_data, &response->gc.cd, sizeof(TPMS_CAPABILITY_DATA));
467 	return TPM_SUCCESS;
468 }
469