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