blob: bc750b7ca194d67eed7baa09f0ee52edd394dc8e [file] [log] [blame]
Miquel Raynalf3b43502018-05-15 11:57:08 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
Eddie James8ed7bb32023-10-24 10:43:49 -05003 * Copyright (c) 2023 Linaro Limited
Miquel Raynalf3b43502018-05-15 11:57:08 +02004 * Copyright (c) 2018 Bootlin
5 * Author: Miquel Raynal <miquel.raynal@bootlin.com>
6 */
7
Miquel Raynalf3b43502018-05-15 11:57:08 +02008#include <dm.h>
Eddie James8ed7bb32023-10-24 10:43:49 -05009#include <dm/of_access.h>
10#include <tpm_api.h>
Miquel Raynalf3b43502018-05-15 11:57:08 +020011#include <tpm-common.h>
12#include <tpm-v2.h>
Ilias Apalodimasca615322024-06-23 14:48:14 +030013#include <tpm_tcg2.h>
Eddie James8ed7bb32023-10-24 10:43:49 -050014#include <u-boot/sha1.h>
15#include <u-boot/sha256.h>
16#include <u-boot/sha512.h>
17#include <version_string.h>
18#include <asm/io.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060019#include <linux/bitops.h>
Eddie James8ed7bb32023-10-24 10:43:49 -050020#include <linux/unaligned/be_byteshift.h>
21#include <linux/unaligned/generic.h>
22#include <linux/unaligned/le_byteshift.h>
23
Miquel Raynalf3b43502018-05-15 11:57:08 +020024#include "tpm-utils.h"
Miquel Raynal65a1a6c2018-05-15 11:57:12 +020025
Ilias Apalodimas2f7ad7e2024-12-24 08:01:09 -080026static int tpm2_update_active_banks(struct udevice *dev)
27{
28 struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
29 struct tpml_pcr_selection pcrs;
30 int ret, i;
31
32 ret = tpm2_get_pcr_info(dev, &pcrs);
33 if (ret)
34 return ret;
35
36 priv->active_bank_count = 0;
37 for (i = 0; i < pcrs.count; i++) {
38 if (!tpm2_is_active_bank(&pcrs.selection[i]))
39 continue;
40 priv->active_banks[priv->active_bank_count] = pcrs.selection[i].hash;
41 priv->active_bank_count++;
42 }
43
44 return 0;
45}
46
Simon Glass8ceca1d2018-11-18 14:22:27 -070047u32 tpm2_startup(struct udevice *dev, enum tpm2_startup_types mode)
Miquel Raynal65a1a6c2018-05-15 11:57:12 +020048{
49 const u8 command_v2[12] = {
50 tpm_u16(TPM2_ST_NO_SESSIONS),
51 tpm_u32(12),
52 tpm_u32(TPM2_CC_STARTUP),
53 tpm_u16(mode),
54 };
55 int ret;
56
57 /*
58 * Note TPM2_Startup command will return RC_SUCCESS the first time,
59 * but will return RC_INITIALIZE otherwise.
60 */
Simon Glass8ceca1d2018-11-18 14:22:27 -070061 ret = tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal65a1a6c2018-05-15 11:57:12 +020062 if (ret && ret != TPM2_RC_INITIALIZE)
63 return ret;
64
Ilias Apalodimas2f7ad7e2024-12-24 08:01:09 -080065 return tpm2_update_active_banks(dev);
Miquel Raynal65a1a6c2018-05-15 11:57:12 +020066}
Miquel Raynal39c76082018-05-15 11:57:13 +020067
Simon Glass8ceca1d2018-11-18 14:22:27 -070068u32 tpm2_self_test(struct udevice *dev, enum tpm2_yes_no full_test)
Miquel Raynal39c76082018-05-15 11:57:13 +020069{
70 const u8 command_v2[12] = {
71 tpm_u16(TPM2_ST_NO_SESSIONS),
72 tpm_u32(11),
73 tpm_u32(TPM2_CC_SELF_TEST),
74 full_test,
75 };
76
Simon Glass8ceca1d2018-11-18 14:22:27 -070077 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal39c76082018-05-15 11:57:13 +020078}
Miquel Raynal8df6f8d2018-05-15 11:57:14 +020079
Ilias Apalodimas42d7bdf2023-01-25 12:18:36 +020080u32 tpm2_auto_start(struct udevice *dev)
81{
82 u32 rc;
83
Ilias Apalodimas42d7bdf2023-01-25 12:18:36 +020084 rc = tpm2_self_test(dev, TPMI_YES);
85
86 if (rc == TPM2_RC_INITIALIZE) {
87 rc = tpm2_startup(dev, TPM2_SU_CLEAR);
88 if (rc)
89 return rc;
90
91 rc = tpm2_self_test(dev, TPMI_YES);
92 }
Ilias Apalodimas2f7ad7e2024-12-24 08:01:09 -080093 if (rc)
94 return rc;
Ilias Apalodimas42d7bdf2023-01-25 12:18:36 +020095
Ilias Apalodimas2f7ad7e2024-12-24 08:01:09 -080096 return tpm2_update_active_banks(dev);
Ilias Apalodimas42d7bdf2023-01-25 12:18:36 +020097}
98
Simon Glass8ceca1d2018-11-18 14:22:27 -070099u32 tpm2_clear(struct udevice *dev, u32 handle, const char *pw,
100 const ssize_t pw_sz)
Miquel Raynal8df6f8d2018-05-15 11:57:14 +0200101{
Simon Glass3b3ac8b2021-02-06 14:23:38 -0700102 /* Length of the message header, up to start of password */
103 uint offset = 27;
Miquel Raynal8df6f8d2018-05-15 11:57:14 +0200104 u8 command_v2[COMMAND_BUFFER_SIZE] = {
105 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
Simon Glass3b3ac8b2021-02-06 14:23:38 -0700106 tpm_u32(offset + pw_sz), /* Length */
Miquel Raynal8df6f8d2018-05-15 11:57:14 +0200107 tpm_u32(TPM2_CC_CLEAR), /* Command code */
108
109 /* HANDLE */
110 tpm_u32(handle), /* TPM resource handle */
111
112 /* AUTH_SESSION */
113 tpm_u32(9 + pw_sz), /* Authorization size */
114 tpm_u32(TPM2_RS_PW), /* Session handle */
115 tpm_u16(0), /* Size of <nonce> */
116 /* <nonce> (if any) */
117 0, /* Attributes: Cont/Excl/Rst */
118 tpm_u16(pw_sz), /* Size of <hmac/password> */
119 /* STRING(pw) <hmac/password> (if any) */
120 };
Miquel Raynal8df6f8d2018-05-15 11:57:14 +0200121 int ret;
122
123 /*
124 * Fill the command structure starting from the first buffer:
125 * - the password (if any)
126 */
127 ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
128 offset, pw, pw_sz);
129 offset += pw_sz;
130 if (ret)
131 return TPM_LIB_ERROR;
132
Simon Glass8ceca1d2018-11-18 14:22:27 -0700133 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal8df6f8d2018-05-15 11:57:14 +0200134}
Miquel Raynal14d72352018-05-15 11:57:15 +0200135
Simon Glass713c58a2021-02-06 14:23:39 -0700136u32 tpm2_nv_define_space(struct udevice *dev, u32 space_index,
137 size_t space_size, u32 nv_attributes,
138 const u8 *nv_policy, size_t nv_policy_size)
139{
140 /*
141 * Calculate the offset of the nv_policy piece by adding each of the
142 * chunks below.
143 */
Simon Glass5252cac2022-08-30 21:05:34 -0600144 const int platform_len = sizeof(u32);
145 const int session_hdr_len = 13;
146 const int message_len = 14;
147 uint offset = TPM2_HDR_LEN + platform_len + session_hdr_len +
148 message_len;
Simon Glass713c58a2021-02-06 14:23:39 -0700149 u8 command_v2[COMMAND_BUFFER_SIZE] = {
150 /* header 10 bytes */
151 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
Simon Glass5252cac2022-08-30 21:05:34 -0600152 tpm_u32(offset + nv_policy_size + 2),/* Length */
Simon Glass713c58a2021-02-06 14:23:39 -0700153 tpm_u32(TPM2_CC_NV_DEFINE_SPACE),/* Command code */
154
Simon Glass5252cac2022-08-30 21:05:34 -0600155 /* handles 4 bytes */
Simon Glass713c58a2021-02-06 14:23:39 -0700156 tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
157
158 /* session header 13 bytes */
159 tpm_u32(9), /* Header size */
160 tpm_u32(TPM2_RS_PW), /* Password authorisation */
161 tpm_u16(0), /* nonce_size */
162 0, /* session_attrs */
163 tpm_u16(0), /* auth_size */
164
165 /* message 14 bytes + policy */
Simon Glass5252cac2022-08-30 21:05:34 -0600166 tpm_u16(message_len + nv_policy_size), /* size */
Simon Glass713c58a2021-02-06 14:23:39 -0700167 tpm_u32(space_index),
168 tpm_u16(TPM2_ALG_SHA256),
169 tpm_u32(nv_attributes),
170 tpm_u16(nv_policy_size),
Simon Glass5252cac2022-08-30 21:05:34 -0600171 /*
172 * nv_policy
173 * space_size
174 */
Simon Glass713c58a2021-02-06 14:23:39 -0700175 };
176 int ret;
177
178 /*
179 * Fill the command structure starting from the first buffer:
180 * - the password (if any)
181 */
Simon Glass5252cac2022-08-30 21:05:34 -0600182 ret = pack_byte_string(command_v2, sizeof(command_v2), "sw",
183 offset, nv_policy, nv_policy_size,
184 offset + nv_policy_size, space_size);
Simon Glass713c58a2021-02-06 14:23:39 -0700185 if (ret)
186 return TPM_LIB_ERROR;
187
188 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
189}
190
Ilias Apalodimas7f59c712020-11-26 23:07:22 +0200191u32 tpm2_pcr_extend(struct udevice *dev, u32 index, u32 algorithm,
192 const u8 *digest, u32 digest_len)
Miquel Raynal14d72352018-05-15 11:57:15 +0200193{
Simon Glass3b3ac8b2021-02-06 14:23:38 -0700194 /* Length of the message header, up to start of digest */
195 uint offset = 33;
Miquel Raynal14d72352018-05-15 11:57:15 +0200196 u8 command_v2[COMMAND_BUFFER_SIZE] = {
197 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
Simon Glass3b3ac8b2021-02-06 14:23:38 -0700198 tpm_u32(offset + digest_len), /* Length */
Miquel Raynal14d72352018-05-15 11:57:15 +0200199 tpm_u32(TPM2_CC_PCR_EXTEND), /* Command code */
200
201 /* HANDLE */
202 tpm_u32(index), /* Handle (PCR Index) */
203
204 /* AUTH_SESSION */
205 tpm_u32(9), /* Authorization size */
206 tpm_u32(TPM2_RS_PW), /* Session handle */
207 tpm_u16(0), /* Size of <nonce> */
208 /* <nonce> (if any) */
209 0, /* Attributes: Cont/Excl/Rst */
210 tpm_u16(0), /* Size of <hmac/password> */
211 /* <hmac/password> (if any) */
Simon Glass3b3ac8b2021-02-06 14:23:38 -0700212
213 /* hashes */
Miquel Raynal14d72352018-05-15 11:57:15 +0200214 tpm_u32(1), /* Count (number of hashes) */
Ilias Apalodimas7f59c712020-11-26 23:07:22 +0200215 tpm_u16(algorithm), /* Algorithm of the hash */
Miquel Raynal14d72352018-05-15 11:57:15 +0200216 /* STRING(digest) Digest */
217 };
Miquel Raynal14d72352018-05-15 11:57:15 +0200218 int ret;
219
Simon Glass4927f472022-08-30 21:05:32 -0600220 if (!digest)
221 return -EINVAL;
Ilias Apalodimas1e665f92024-06-23 14:48:18 +0300222
Ilias Apalodimasd788b062024-12-24 08:01:05 -0800223 if (!tpm2_check_active_banks(dev)) {
Ilias Apalodimas1e665f92024-06-23 14:48:18 +0300224 log_err("Cannot extend PCRs if all the TPM enabled algorithms are not supported\n");
225 return -EINVAL;
226 }
Miquel Raynal14d72352018-05-15 11:57:15 +0200227 /*
228 * Fill the command structure starting from the first buffer:
229 * - the digest
230 */
231 ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
Ilias Apalodimas7f59c712020-11-26 23:07:22 +0200232 offset, digest, digest_len);
Miquel Raynal14d72352018-05-15 11:57:15 +0200233 if (ret)
234 return TPM_LIB_ERROR;
235
Simon Glass8ceca1d2018-11-18 14:22:27 -0700236 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal14d72352018-05-15 11:57:15 +0200237}
Miquel Raynal4c1a5852018-05-15 11:57:16 +0200238
Simon Glass3d930ed2021-02-06 14:23:40 -0700239u32 tpm2_nv_read_value(struct udevice *dev, u32 index, void *data, u32 count)
240{
241 u8 command_v2[COMMAND_BUFFER_SIZE] = {
242 /* header 10 bytes */
243 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
244 tpm_u32(10 + 8 + 4 + 9 + 4), /* Length */
245 tpm_u32(TPM2_CC_NV_READ), /* Command code */
246
247 /* handles 8 bytes */
248 tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
249 tpm_u32(HR_NV_INDEX + index), /* Password authorisation */
250
251 /* AUTH_SESSION */
252 tpm_u32(9), /* Authorization size */
253 tpm_u32(TPM2_RS_PW), /* Session handle */
254 tpm_u16(0), /* Size of <nonce> */
255 /* <nonce> (if any) */
256 0, /* Attributes: Cont/Excl/Rst */
257 tpm_u16(0), /* Size of <hmac/password> */
258 /* <hmac/password> (if any) */
259
260 tpm_u16(count), /* Number of bytes */
261 tpm_u16(0), /* Offset */
262 };
263 size_t response_len = COMMAND_BUFFER_SIZE;
264 u8 response[COMMAND_BUFFER_SIZE];
265 int ret;
266 u16 tag;
267 u32 size, code;
268
269 ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
270 if (ret)
271 return log_msg_ret("read", ret);
272 if (unpack_byte_string(response, response_len, "wdds",
273 0, &tag, 2, &size, 6, &code,
274 16, data, count))
275 return TPM_LIB_ERROR;
276
277 return 0;
278}
279
280u32 tpm2_nv_write_value(struct udevice *dev, u32 index, const void *data,
281 u32 count)
282{
283 struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
284 uint offset = 10 + 8 + 4 + 9 + 2;
285 uint len = offset + count + 2;
286 /* Use empty password auth if platform hierarchy is disabled */
287 u32 auth = priv->plat_hier_disabled ? HR_NV_INDEX + index :
288 TPM2_RH_PLATFORM;
289 u8 command_v2[COMMAND_BUFFER_SIZE] = {
290 /* header 10 bytes */
291 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
292 tpm_u32(len), /* Length */
293 tpm_u32(TPM2_CC_NV_WRITE), /* Command code */
294
295 /* handles 8 bytes */
296 tpm_u32(auth), /* Primary platform seed */
297 tpm_u32(HR_NV_INDEX + index), /* Password authorisation */
298
299 /* AUTH_SESSION */
300 tpm_u32(9), /* Authorization size */
301 tpm_u32(TPM2_RS_PW), /* Session handle */
302 tpm_u16(0), /* Size of <nonce> */
303 /* <nonce> (if any) */
304 0, /* Attributes: Cont/Excl/Rst */
305 tpm_u16(0), /* Size of <hmac/password> */
306 /* <hmac/password> (if any) */
307
308 tpm_u16(count),
309 };
310 size_t response_len = COMMAND_BUFFER_SIZE;
311 u8 response[COMMAND_BUFFER_SIZE];
312 int ret;
313
314 ret = pack_byte_string(command_v2, sizeof(command_v2), "sw",
315 offset, data, count,
316 offset + count, 0);
317 if (ret)
318 return TPM_LIB_ERROR;
319
320 return tpm_sendrecv_command(dev, command_v2, response, &response_len);
321}
322
Simon Glass8ceca1d2018-11-18 14:22:27 -0700323u32 tpm2_pcr_read(struct udevice *dev, u32 idx, unsigned int idx_min_sz,
Ruchika Gupta686bedb2021-11-29 13:09:45 +0530324 u16 algorithm, void *data, u32 digest_len,
325 unsigned int *updates)
Miquel Raynal4c1a5852018-05-15 11:57:16 +0200326{
327 u8 idx_array_sz = max(idx_min_sz, DIV_ROUND_UP(idx, 8));
328 u8 command_v2[COMMAND_BUFFER_SIZE] = {
329 tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
330 tpm_u32(17 + idx_array_sz), /* Length */
331 tpm_u32(TPM2_CC_PCR_READ), /* Command code */
332
333 /* TPML_PCR_SELECTION */
334 tpm_u32(1), /* Number of selections */
Ruchika Gupta686bedb2021-11-29 13:09:45 +0530335 tpm_u16(algorithm), /* Algorithm of the hash */
Miquel Raynal4c1a5852018-05-15 11:57:16 +0200336 idx_array_sz, /* Array size for selection */
337 /* bitmap(idx) Selected PCR bitmap */
338 };
339 size_t response_len = COMMAND_BUFFER_SIZE;
340 u8 response[COMMAND_BUFFER_SIZE];
341 unsigned int pcr_sel_idx = idx / 8;
342 u8 pcr_sel_bit = BIT(idx % 8);
343 unsigned int counter = 0;
344 int ret;
345
346 if (pack_byte_string(command_v2, COMMAND_BUFFER_SIZE, "b",
347 17 + pcr_sel_idx, pcr_sel_bit))
348 return TPM_LIB_ERROR;
349
Simon Glass8ceca1d2018-11-18 14:22:27 -0700350 ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
Miquel Raynal4c1a5852018-05-15 11:57:16 +0200351 if (ret)
352 return ret;
353
Ruchika Gupta686bedb2021-11-29 13:09:45 +0530354 if (digest_len > response_len)
355 return TPM_LIB_ERROR;
356
Miquel Raynal4c1a5852018-05-15 11:57:16 +0200357 if (unpack_byte_string(response, response_len, "ds",
358 10, &counter,
Ruchika Gupta686bedb2021-11-29 13:09:45 +0530359 response_len - digest_len, data,
360 digest_len))
Miquel Raynal4c1a5852018-05-15 11:57:16 +0200361 return TPM_LIB_ERROR;
362
363 if (updates)
364 *updates = counter;
365
366 return 0;
367}
Miquel Raynal2e52c062018-05-15 11:57:17 +0200368
Simon Glass8ceca1d2018-11-18 14:22:27 -0700369u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property,
370 void *buf, size_t prop_count)
Miquel Raynal2e52c062018-05-15 11:57:17 +0200371{
372 u8 command_v2[COMMAND_BUFFER_SIZE] = {
373 tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
374 tpm_u32(22), /* Length */
375 tpm_u32(TPM2_CC_GET_CAPABILITY), /* Command code */
376
377 tpm_u32(capability), /* Capability */
378 tpm_u32(property), /* Property */
379 tpm_u32(prop_count), /* Property count */
380 };
381 u8 response[COMMAND_BUFFER_SIZE];
382 size_t response_len = COMMAND_BUFFER_SIZE;
383 unsigned int properties_off;
384 int ret;
385
Simon Glass8ceca1d2018-11-18 14:22:27 -0700386 ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
Miquel Raynal2e52c062018-05-15 11:57:17 +0200387 if (ret)
388 return ret;
389
390 /*
391 * In the response buffer, the properties are located after the:
392 * tag (u16), response size (u32), response code (u32),
Ilias Apalodimasa0789152020-11-05 23:58:43 +0200393 * YES/NO flag (u8), TPM_CAP (u32).
Miquel Raynal2e52c062018-05-15 11:57:17 +0200394 */
395 properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
Ilias Apalodimasa0789152020-11-05 23:58:43 +0200396 sizeof(u8) + sizeof(u32);
Miquel Raynal2e52c062018-05-15 11:57:17 +0200397 memcpy(buf, &response[properties_off], response_len - properties_off);
398
399 return 0;
400}
Miquel Raynal228e9902018-05-15 11:57:18 +0200401
Eddie James8ed7bb32023-10-24 10:43:49 -0500402static int tpm2_get_num_pcr(struct udevice *dev, u32 *num_pcr)
403{
404 u8 response[(sizeof(struct tpms_capability_data) -
405 offsetof(struct tpms_capability_data, data))];
406 u32 properties_offset =
407 offsetof(struct tpml_tagged_tpm_property, tpm_property) +
408 offsetof(struct tpms_tagged_property, value);
409 u32 ret;
410
411 memset(response, 0, sizeof(response));
412 ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
413 TPM2_PT_PCR_COUNT, response, 1);
414 if (ret)
415 return ret;
416
417 *num_pcr = get_unaligned_be32(response + properties_offset);
418 if (*num_pcr > TPM2_MAX_PCRS) {
419 printf("%s: too many pcrs: %u\n", __func__, *num_pcr);
420 return -E2BIG;
421 }
422
423 return 0;
424}
425
Ilias Apalodimascb356612024-06-23 14:48:17 +0300426int tpm2_get_pcr_info(struct udevice *dev, struct tpml_pcr_selection *pcrs)
Eddie James8ed7bb32023-10-24 10:43:49 -0500427{
428 u8 response[(sizeof(struct tpms_capability_data) -
429 offsetof(struct tpms_capability_data, data))];
Eddie James8ed7bb32023-10-24 10:43:49 -0500430 u32 num_pcr;
431 size_t i;
432 u32 ret;
433
Eddie James8ed7bb32023-10-24 10:43:49 -0500434 ret = tpm2_get_capability(dev, TPM2_CAP_PCRS, 0, response, 1);
435 if (ret)
436 return ret;
437
Ilias Apalodimascb356612024-06-23 14:48:17 +0300438 pcrs->count = get_unaligned_be32(response);
Eddie James8ed7bb32023-10-24 10:43:49 -0500439 /*
Ilias Apalodimas1e665f92024-06-23 14:48:18 +0300440 * We only support 4 algorithms for now so check against that
Eddie James8ed7bb32023-10-24 10:43:49 -0500441 * instead of TPM2_NUM_PCR_BANKS
442 */
Ilias Apalodimas1e665f92024-06-23 14:48:18 +0300443 if (pcrs->count > 4 || pcrs->count < 1) {
Ilias Apalodimascb356612024-06-23 14:48:17 +0300444 printf("%s: too many pcrs: %u\n", __func__, pcrs->count);
Eddie James8ed7bb32023-10-24 10:43:49 -0500445 return -EMSGSIZE;
446 }
447
448 ret = tpm2_get_num_pcr(dev, &num_pcr);
449 if (ret)
450 return ret;
451
Ilias Apalodimascb356612024-06-23 14:48:17 +0300452 for (i = 0; i < pcrs->count; i++) {
Eddie James8ed7bb32023-10-24 10:43:49 -0500453 /*
454 * Definition of TPMS_PCR_SELECTION Structure
455 * hash: u16
456 * size_of_select: u8
457 * pcr_select: u8 array
458 *
459 * The offsets depend on the number of the device PCRs
460 * so we have to calculate them based on that
461 */
462 u32 hash_offset = offsetof(struct tpml_pcr_selection, selection) +
463 i * offsetof(struct tpms_pcr_selection, pcr_select) +
464 i * ((num_pcr + 7) / 8);
465 u32 size_select_offset =
466 hash_offset + offsetof(struct tpms_pcr_selection,
467 size_of_select);
468 u32 pcr_select_offset =
469 hash_offset + offsetof(struct tpms_pcr_selection,
470 pcr_select);
471
Ilias Apalodimascb356612024-06-23 14:48:17 +0300472 pcrs->selection[i].hash =
Eddie James8ed7bb32023-10-24 10:43:49 -0500473 get_unaligned_be16(response + hash_offset);
Ilias Apalodimascb356612024-06-23 14:48:17 +0300474 pcrs->selection[i].size_of_select =
Eddie James8ed7bb32023-10-24 10:43:49 -0500475 __get_unaligned_be(response + size_select_offset);
Ilias Apalodimascb356612024-06-23 14:48:17 +0300476 if (pcrs->selection[i].size_of_select > TPM2_PCR_SELECT_MAX) {
Eddie James8ed7bb32023-10-24 10:43:49 -0500477 printf("%s: pcrs selection too large: %u\n", __func__,
Ilias Apalodimascb356612024-06-23 14:48:17 +0300478 pcrs->selection[i].size_of_select);
Eddie James8ed7bb32023-10-24 10:43:49 -0500479 return -ENOBUFS;
480 }
481 /* copy the array of pcr_select */
Ilias Apalodimascb356612024-06-23 14:48:17 +0300482 memcpy(pcrs->selection[i].pcr_select, response + pcr_select_offset,
483 pcrs->selection[i].size_of_select);
Eddie James8ed7bb32023-10-24 10:43:49 -0500484 }
485
Eddie James8ed7bb32023-10-24 10:43:49 -0500486 return 0;
487}
488
Simon Glass8ceca1d2018-11-18 14:22:27 -0700489u32 tpm2_dam_reset(struct udevice *dev, const char *pw, const ssize_t pw_sz)
Miquel Raynal228e9902018-05-15 11:57:18 +0200490{
491 u8 command_v2[COMMAND_BUFFER_SIZE] = {
492 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
493 tpm_u32(27 + pw_sz), /* Length */
494 tpm_u32(TPM2_CC_DAM_RESET), /* Command code */
495
496 /* HANDLE */
497 tpm_u32(TPM2_RH_LOCKOUT), /* TPM resource handle */
498
499 /* AUTH_SESSION */
500 tpm_u32(9 + pw_sz), /* Authorization size */
501 tpm_u32(TPM2_RS_PW), /* Session handle */
502 tpm_u16(0), /* Size of <nonce> */
503 /* <nonce> (if any) */
504 0, /* Attributes: Cont/Excl/Rst */
505 tpm_u16(pw_sz), /* Size of <hmac/password> */
506 /* STRING(pw) <hmac/password> (if any) */
507 };
508 unsigned int offset = 27;
509 int ret;
510
511 /*
512 * Fill the command structure starting from the first buffer:
513 * - the password (if any)
514 */
515 ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
516 offset, pw, pw_sz);
517 offset += pw_sz;
518 if (ret)
519 return TPM_LIB_ERROR;
520
Simon Glass8ceca1d2018-11-18 14:22:27 -0700521 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal228e9902018-05-15 11:57:18 +0200522}
523
Simon Glass8ceca1d2018-11-18 14:22:27 -0700524u32 tpm2_dam_parameters(struct udevice *dev, const char *pw,
525 const ssize_t pw_sz, unsigned int max_tries,
526 unsigned int recovery_time,
Miquel Raynal228e9902018-05-15 11:57:18 +0200527 unsigned int lockout_recovery)
528{
529 u8 command_v2[COMMAND_BUFFER_SIZE] = {
530 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
531 tpm_u32(27 + pw_sz + 12), /* Length */
532 tpm_u32(TPM2_CC_DAM_PARAMETERS), /* Command code */
533
534 /* HANDLE */
535 tpm_u32(TPM2_RH_LOCKOUT), /* TPM resource handle */
536
537 /* AUTH_SESSION */
538 tpm_u32(9 + pw_sz), /* Authorization size */
539 tpm_u32(TPM2_RS_PW), /* Session handle */
540 tpm_u16(0), /* Size of <nonce> */
541 /* <nonce> (if any) */
542 0, /* Attributes: Cont/Excl/Rst */
543 tpm_u16(pw_sz), /* Size of <hmac/password> */
544 /* STRING(pw) <hmac/password> (if any) */
545
546 /* LOCKOUT PARAMETERS */
547 /* tpm_u32(max_tries) Max tries (0, always lock) */
548 /* tpm_u32(recovery_time) Recovery time (0, no lock) */
549 /* tpm_u32(lockout_recovery) Lockout recovery */
550 };
551 unsigned int offset = 27;
552 int ret;
553
554 /*
555 * Fill the command structure starting from the first buffer:
556 * - the password (if any)
557 * - max tries
558 * - recovery time
559 * - lockout recovery
560 */
561 ret = pack_byte_string(command_v2, sizeof(command_v2), "sddd",
562 offset, pw, pw_sz,
563 offset + pw_sz, max_tries,
564 offset + pw_sz + 4, recovery_time,
565 offset + pw_sz + 8, lockout_recovery);
566 offset += pw_sz + 12;
567 if (ret)
568 return TPM_LIB_ERROR;
569
Simon Glass8ceca1d2018-11-18 14:22:27 -0700570 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal228e9902018-05-15 11:57:18 +0200571}
Miquel Raynal05d7be32018-05-15 11:57:19 +0200572
Simon Glass8ceca1d2018-11-18 14:22:27 -0700573int tpm2_change_auth(struct udevice *dev, u32 handle, const char *newpw,
574 const ssize_t newpw_sz, const char *oldpw,
575 const ssize_t oldpw_sz)
Miquel Raynal05d7be32018-05-15 11:57:19 +0200576{
577 unsigned int offset = 27;
578 u8 command_v2[COMMAND_BUFFER_SIZE] = {
579 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
580 tpm_u32(offset + oldpw_sz + 2 + newpw_sz), /* Length */
581 tpm_u32(TPM2_CC_HIERCHANGEAUTH), /* Command code */
582
583 /* HANDLE */
584 tpm_u32(handle), /* TPM resource handle */
585
586 /* AUTH_SESSION */
587 tpm_u32(9 + oldpw_sz), /* Authorization size */
588 tpm_u32(TPM2_RS_PW), /* Session handle */
589 tpm_u16(0), /* Size of <nonce> */
590 /* <nonce> (if any) */
591 0, /* Attributes: Cont/Excl/Rst */
592 tpm_u16(oldpw_sz) /* Size of <hmac/password> */
593 /* STRING(oldpw) <hmac/password> (if any) */
594
595 /* TPM2B_AUTH (TPM2B_DIGEST) */
596 /* tpm_u16(newpw_sz) Digest size, new pw length */
597 /* STRING(newpw) Digest buffer, new pw */
598 };
599 int ret;
600
601 /*
602 * Fill the command structure starting from the first buffer:
603 * - the old password (if any)
604 * - size of the new password
605 * - new password
606 */
607 ret = pack_byte_string(command_v2, sizeof(command_v2), "sws",
608 offset, oldpw, oldpw_sz,
609 offset + oldpw_sz, newpw_sz,
610 offset + oldpw_sz + 2, newpw, newpw_sz);
611 offset += oldpw_sz + 2 + newpw_sz;
612 if (ret)
613 return TPM_LIB_ERROR;
614
Simon Glass8ceca1d2018-11-18 14:22:27 -0700615 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal05d7be32018-05-15 11:57:19 +0200616}
Miquel Raynal0b864f62018-05-15 11:57:20 +0200617
Simon Glass8ceca1d2018-11-18 14:22:27 -0700618u32 tpm2_pcr_setauthpolicy(struct udevice *dev, const char *pw,
619 const ssize_t pw_sz, u32 index, const char *key)
Miquel Raynal0b864f62018-05-15 11:57:20 +0200620{
621 u8 command_v2[COMMAND_BUFFER_SIZE] = {
622 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
623 tpm_u32(35 + pw_sz + TPM2_DIGEST_LEN), /* Length */
624 tpm_u32(TPM2_CC_PCR_SETAUTHPOL), /* Command code */
625
626 /* HANDLE */
627 tpm_u32(TPM2_RH_PLATFORM), /* TPM resource handle */
628
629 /* AUTH_SESSION */
630 tpm_u32(9 + pw_sz), /* Authorization size */
631 tpm_u32(TPM2_RS_PW), /* session handle */
632 tpm_u16(0), /* Size of <nonce> */
633 /* <nonce> (if any) */
634 0, /* Attributes: Cont/Excl/Rst */
635 tpm_u16(pw_sz) /* Size of <hmac/password> */
636 /* STRING(pw) <hmac/password> (if any) */
637
638 /* TPM2B_AUTH (TPM2B_DIGEST) */
639 /* tpm_u16(TPM2_DIGEST_LEN) Digest size length */
640 /* STRING(key) Digest buffer (PCR key) */
641
642 /* TPMI_ALG_HASH */
643 /* tpm_u16(TPM2_ALG_SHA256) Algorithm of the hash */
644
645 /* TPMI_DH_PCR */
646 /* tpm_u32(index), PCR Index */
647 };
648 unsigned int offset = 27;
649 int ret;
650
651 /*
652 * Fill the command structure starting from the first buffer:
653 * - the password (if any)
654 * - the PCR key length
655 * - the PCR key
656 * - the hash algorithm
657 * - the PCR index
658 */
659 ret = pack_byte_string(command_v2, sizeof(command_v2), "swswd",
660 offset, pw, pw_sz,
661 offset + pw_sz, TPM2_DIGEST_LEN,
662 offset + pw_sz + 2, key, TPM2_DIGEST_LEN,
663 offset + pw_sz + 2 + TPM2_DIGEST_LEN,
664 TPM2_ALG_SHA256,
665 offset + pw_sz + 4 + TPM2_DIGEST_LEN, index);
666 offset += pw_sz + 2 + TPM2_DIGEST_LEN + 2 + 4;
667 if (ret)
668 return TPM_LIB_ERROR;
669
Simon Glass8ceca1d2018-11-18 14:22:27 -0700670 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal0b864f62018-05-15 11:57:20 +0200671}
672
Simon Glass8ceca1d2018-11-18 14:22:27 -0700673u32 tpm2_pcr_setauthvalue(struct udevice *dev, const char *pw,
674 const ssize_t pw_sz, u32 index, const char *key,
675 const ssize_t key_sz)
Miquel Raynal0b864f62018-05-15 11:57:20 +0200676{
677 u8 command_v2[COMMAND_BUFFER_SIZE] = {
678 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
679 tpm_u32(33 + pw_sz + TPM2_DIGEST_LEN), /* Length */
680 tpm_u32(TPM2_CC_PCR_SETAUTHVAL), /* Command code */
681
682 /* HANDLE */
683 tpm_u32(index), /* Handle (PCR Index) */
684
685 /* AUTH_SESSION */
686 tpm_u32(9 + pw_sz), /* Authorization size */
687 tpm_u32(TPM2_RS_PW), /* session handle */
688 tpm_u16(0), /* Size of <nonce> */
689 /* <nonce> (if any) */
690 0, /* Attributes: Cont/Excl/Rst */
691 tpm_u16(pw_sz), /* Size of <hmac/password> */
692 /* STRING(pw) <hmac/password> (if any) */
693
694 /* TPM2B_DIGEST */
695 /* tpm_u16(key_sz) Key length */
696 /* STRING(key) Key */
697 };
698 unsigned int offset = 27;
699 int ret;
700
701 /*
702 * Fill the command structure starting from the first buffer:
703 * - the password (if any)
704 * - the number of digests, 1 in our case
705 * - the algorithm, sha256 in our case
706 * - the digest (64 bytes)
707 */
708 ret = pack_byte_string(command_v2, sizeof(command_v2), "sws",
709 offset, pw, pw_sz,
710 offset + pw_sz, key_sz,
711 offset + pw_sz + 2, key, key_sz);
712 offset += pw_sz + 2 + key_sz;
713 if (ret)
714 return TPM_LIB_ERROR;
715
Simon Glass8ceca1d2018-11-18 14:22:27 -0700716 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal0b864f62018-05-15 11:57:20 +0200717}
Dhananjay Phadke7a2cf2e2020-06-04 16:43:59 -0700718
719u32 tpm2_get_random(struct udevice *dev, void *data, u32 count)
720{
721 const u8 command_v2[10] = {
722 tpm_u16(TPM2_ST_NO_SESSIONS),
723 tpm_u32(12),
724 tpm_u32(TPM2_CC_GET_RANDOM),
725 };
726 u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
727
728 const size_t data_size_offset = 10;
729 const size_t data_offset = 12;
730 size_t response_length = sizeof(response);
731 u32 data_size;
732 u8 *out = data;
733
734 while (count > 0) {
735 u32 this_bytes = min((size_t)count,
736 sizeof(response) - data_offset);
737 u32 err;
738
739 if (pack_byte_string(buf, sizeof(buf), "sw",
740 0, command_v2, sizeof(command_v2),
741 sizeof(command_v2), this_bytes))
742 return TPM_LIB_ERROR;
743 err = tpm_sendrecv_command(dev, buf, response,
744 &response_length);
745 if (err)
746 return err;
747 if (unpack_byte_string(response, response_length, "w",
748 data_size_offset, &data_size))
749 return TPM_LIB_ERROR;
750 if (data_size > this_bytes)
751 return TPM_LIB_ERROR;
752 if (unpack_byte_string(response, response_length, "s",
753 data_offset, out, data_size))
754 return TPM_LIB_ERROR;
755
756 count -= data_size;
757 out += data_size;
758 }
759
760 return 0;
761}
Simon Glasse9d3d592021-02-06 14:23:41 -0700762
763u32 tpm2_write_lock(struct udevice *dev, u32 index)
764{
765 u8 command_v2[COMMAND_BUFFER_SIZE] = {
766 /* header 10 bytes */
767 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
768 tpm_u32(10 + 8 + 13), /* Length */
769 tpm_u32(TPM2_CC_NV_WRITELOCK), /* Command code */
770
771 /* handles 8 bytes */
772 tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
773 tpm_u32(HR_NV_INDEX + index), /* Password authorisation */
774
775 /* session header 9 bytes */
776 tpm_u32(9), /* Header size */
777 tpm_u32(TPM2_RS_PW), /* Password authorisation */
778 tpm_u16(0), /* nonce_size */
779 0, /* session_attrs */
780 tpm_u16(0), /* auth_size */
781 };
782
783 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
784}
Simon Glass77759db2021-02-06 14:23:42 -0700785
786u32 tpm2_disable_platform_hierarchy(struct udevice *dev)
787{
788 struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
789 u8 command_v2[COMMAND_BUFFER_SIZE] = {
790 /* header 10 bytes */
791 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
792 tpm_u32(10 + 4 + 13 + 5), /* Length */
793 tpm_u32(TPM2_CC_HIER_CONTROL), /* Command code */
794
795 /* 4 bytes */
796 tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
797
798 /* session header 9 bytes */
799 tpm_u32(9), /* Header size */
800 tpm_u32(TPM2_RS_PW), /* Password authorisation */
801 tpm_u16(0), /* nonce_size */
802 0, /* session_attrs */
803 tpm_u16(0), /* auth_size */
804
805 /* payload 5 bytes */
806 tpm_u32(TPM2_RH_PLATFORM), /* Hierarchy to disable */
807 0, /* 0=disable */
808 };
809 int ret;
810
811 ret = tpm_sendrecv_command(dev, command_v2, NULL, NULL);
812 log_info("ret=%s, %x\n", dev->name, ret);
813 if (ret)
814 return ret;
815
816 priv->plat_hier_disabled = true;
817
818 return 0;
819}
Masahisa Kojima06ef6b62021-11-04 22:59:16 +0900820
821u32 tpm2_submit_command(struct udevice *dev, const u8 *sendbuf,
822 u8 *recvbuf, size_t *recv_size)
823{
824 return tpm_sendrecv_command(dev, sendbuf, recvbuf, recv_size);
825}
Simon Glass3f7a73a2022-08-30 21:05:37 -0600826
827u32 tpm2_report_state(struct udevice *dev, uint vendor_cmd, uint vendor_subcmd,
828 u8 *recvbuf, size_t *recv_size)
829{
830 u8 command_v2[COMMAND_BUFFER_SIZE] = {
831 /* header 10 bytes */
832 tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
833 tpm_u32(10 + 2), /* Length */
834 tpm_u32(vendor_cmd), /* Command code */
835
836 tpm_u16(vendor_subcmd),
837 };
838 int ret;
839
840 ret = tpm_sendrecv_command(dev, command_v2, recvbuf, recv_size);
841 log_debug("ret=%s, %x\n", dev->name, ret);
842 if (ret)
843 return ret;
844 if (*recv_size < 12)
845 return -ENODATA;
846 *recv_size -= 12;
Heinrich Schuchardt5b8577d2024-11-02 11:27:37 +0100847 memmove(recvbuf, recvbuf + 12, *recv_size);
Simon Glass3f7a73a2022-08-30 21:05:37 -0600848
849 return 0;
850}
Simon Glass3564b8e2022-08-30 21:05:38 -0600851
852u32 tpm2_enable_nvcommits(struct udevice *dev, uint vendor_cmd,
853 uint vendor_subcmd)
854{
855 u8 command_v2[COMMAND_BUFFER_SIZE] = {
856 /* header 10 bytes */
857 tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
858 tpm_u32(10 + 2), /* Length */
859 tpm_u32(vendor_cmd), /* Command code */
860
861 tpm_u16(vendor_subcmd),
862 };
863 int ret;
864
865 ret = tpm_sendrecv_command(dev, command_v2, NULL, NULL);
866 log_debug("ret=%s, %x\n", dev->name, ret);
867 if (ret)
868 return ret;
869
870 return 0;
871}
Tim Harvey6ea1e052024-05-25 13:00:48 -0700872
Ilias Apalodimas9465f7a2024-12-24 08:01:04 -0800873bool tpm2_is_active_bank(struct tpms_pcr_selection *selection)
Ilias Apalodimascb356612024-06-23 14:48:17 +0300874{
875 int i;
876
877 for (i = 0; i < selection->size_of_select; i++) {
878 if (selection->pcr_select[i])
879 return true;
880 }
881
882 return false;
883}
884
Tim Harvey6ea1e052024-05-25 13:00:48 -0700885enum tpm2_algorithms tpm2_name_to_algorithm(const char *name)
886{
887 size_t i;
888
889 for (i = 0; i < ARRAY_SIZE(hash_algo_list); ++i) {
890 if (!strcasecmp(name, hash_algo_list[i].hash_name))
891 return hash_algo_list[i].hash_alg;
892 }
893 printf("%s: unsupported algorithm %s\n", __func__, name);
894
895 return -EINVAL;
896}
897
898const char *tpm2_algorithm_name(enum tpm2_algorithms algo)
899{
900 size_t i;
901
902 for (i = 0; i < ARRAY_SIZE(hash_algo_list); ++i) {
903 if (hash_algo_list[i].hash_alg == algo)
904 return hash_algo_list[i].hash_name;
905 }
906
907 return "";
908}
909
Raymond Mao43158122024-12-24 08:01:07 -0800910bool tpm2_algorithm_supported(enum tpm2_algorithms algo)
911{
912 size_t i;
913
914 for (i = 0; i < ARRAY_SIZE(hash_algo_list); ++i) {
915 if (hash_algo_list[i].hash_alg == algo)
916 return hash_algo_list[i].supported;
917 }
918
919 return false;
920}
921
Ilias Apalodimas1e665f92024-06-23 14:48:18 +0300922u16 tpm2_algorithm_to_len(enum tpm2_algorithms algo)
923{
924 size_t i;
925
926 for (i = 0; i < ARRAY_SIZE(hash_algo_list); ++i) {
927 if (hash_algo_list[i].hash_alg == algo)
928 return hash_algo_list[i].hash_len;
929 }
930
931 return 0;
932}
933
Ilias Apalodimasd788b062024-12-24 08:01:05 -0800934bool tpm2_check_active_banks(struct udevice *dev)
Ilias Apalodimas1e665f92024-06-23 14:48:18 +0300935{
936 struct tpml_pcr_selection pcrs;
937 size_t i;
938 int rc;
939
940 rc = tpm2_get_pcr_info(dev, &pcrs);
941 if (rc)
942 return false;
943
944 for (i = 0; i < pcrs.count; i++) {
Ilias Apalodimas9465f7a2024-12-24 08:01:04 -0800945 if (tpm2_is_active_bank(&pcrs.selection[i]) &&
Raymond Mao43158122024-12-24 08:01:07 -0800946 !tpm2_algorithm_supported(pcrs.selection[i].hash))
Ilias Apalodimas1e665f92024-06-23 14:48:18 +0300947 return false;
948 }
949
950 return true;
951}
Ilias Apalodimas7b1e5222024-12-24 08:01:08 -0800952
953void tpm2_print_active_banks(struct udevice *dev)
954{
955 struct tpml_pcr_selection pcrs;
956 size_t i;
957 int rc;
958
959 rc = tpm2_get_pcr_info(dev, &pcrs);
960 if (rc) {
961 log_err("Can't retrieve active PCRs\n");
962 return;
963 }
964
965 for (i = 0; i < pcrs.count; i++) {
966 if (tpm2_is_active_bank(&pcrs.selection[i])) {
967 const char *str;
968
969 str = tpm2_algorithm_name(pcrs.selection[i].hash);
970 if (str)
971 log_info("%s\n", str);
972 }
973 }
974}