blob: 3946ac2dbb700dc88d9fabf5767808b600eec366 [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
Raymond Mao5187a642025-01-27 06:58:46 -080047u32 tpm2_startup(struct udevice *dev, bool bon, enum tpm2_startup_types mode)
Miquel Raynal65a1a6c2018-05-15 11:57:12 +020048{
Raymond Mao5187a642025-01-27 06:58:46 -080049 int op = bon ? TPM2_CC_STARTUP : TPM2_CC_SHUTDOWN;
Miquel Raynal65a1a6c2018-05-15 11:57:12 +020050 const u8 command_v2[12] = {
51 tpm_u16(TPM2_ST_NO_SESSIONS),
52 tpm_u32(12),
Raymond Mao5187a642025-01-27 06:58:46 -080053 tpm_u32(op),
Miquel Raynal65a1a6c2018-05-15 11:57:12 +020054 tpm_u16(mode),
55 };
56 int ret;
57
58 /*
59 * Note TPM2_Startup command will return RC_SUCCESS the first time,
60 * but will return RC_INITIALIZE otherwise.
61 */
Simon Glass8ceca1d2018-11-18 14:22:27 -070062 ret = tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Raymond Mao5187a642025-01-27 06:58:46 -080063 if ((ret && ret != TPM2_RC_INITIALIZE) || !bon)
Miquel Raynal65a1a6c2018-05-15 11:57:12 +020064 return ret;
65
Ilias Apalodimas2f7ad7e2024-12-24 08:01:09 -080066 return tpm2_update_active_banks(dev);
Miquel Raynal65a1a6c2018-05-15 11:57:12 +020067}
Miquel Raynal39c76082018-05-15 11:57:13 +020068
Simon Glass8ceca1d2018-11-18 14:22:27 -070069u32 tpm2_self_test(struct udevice *dev, enum tpm2_yes_no full_test)
Miquel Raynal39c76082018-05-15 11:57:13 +020070{
71 const u8 command_v2[12] = {
72 tpm_u16(TPM2_ST_NO_SESSIONS),
73 tpm_u32(11),
74 tpm_u32(TPM2_CC_SELF_TEST),
75 full_test,
76 };
77
Simon Glass8ceca1d2018-11-18 14:22:27 -070078 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal39c76082018-05-15 11:57:13 +020079}
Miquel Raynal8df6f8d2018-05-15 11:57:14 +020080
Ilias Apalodimas42d7bdf2023-01-25 12:18:36 +020081u32 tpm2_auto_start(struct udevice *dev)
82{
83 u32 rc;
84
Ilias Apalodimas42d7bdf2023-01-25 12:18:36 +020085 rc = tpm2_self_test(dev, TPMI_YES);
86
87 if (rc == TPM2_RC_INITIALIZE) {
Raymond Mao5187a642025-01-27 06:58:46 -080088 rc = tpm2_startup(dev, true, TPM2_SU_CLEAR);
Ilias Apalodimas42d7bdf2023-01-25 12:18:36 +020089 if (rc)
90 return rc;
91
92 rc = tpm2_self_test(dev, TPMI_YES);
93 }
Ilias Apalodimas2f7ad7e2024-12-24 08:01:09 -080094 if (rc)
95 return rc;
Ilias Apalodimas42d7bdf2023-01-25 12:18:36 +020096
Ilias Apalodimas2f7ad7e2024-12-24 08:01:09 -080097 return tpm2_update_active_banks(dev);
Ilias Apalodimas42d7bdf2023-01-25 12:18:36 +020098}
99
Simon Glass8ceca1d2018-11-18 14:22:27 -0700100u32 tpm2_clear(struct udevice *dev, u32 handle, const char *pw,
101 const ssize_t pw_sz)
Miquel Raynal8df6f8d2018-05-15 11:57:14 +0200102{
Simon Glass3b3ac8b2021-02-06 14:23:38 -0700103 /* Length of the message header, up to start of password */
104 uint offset = 27;
Miquel Raynal8df6f8d2018-05-15 11:57:14 +0200105 u8 command_v2[COMMAND_BUFFER_SIZE] = {
106 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
Simon Glass3b3ac8b2021-02-06 14:23:38 -0700107 tpm_u32(offset + pw_sz), /* Length */
Miquel Raynal8df6f8d2018-05-15 11:57:14 +0200108 tpm_u32(TPM2_CC_CLEAR), /* Command code */
109
110 /* HANDLE */
111 tpm_u32(handle), /* TPM resource handle */
112
113 /* AUTH_SESSION */
114 tpm_u32(9 + pw_sz), /* Authorization size */
115 tpm_u32(TPM2_RS_PW), /* Session handle */
116 tpm_u16(0), /* Size of <nonce> */
117 /* <nonce> (if any) */
118 0, /* Attributes: Cont/Excl/Rst */
119 tpm_u16(pw_sz), /* Size of <hmac/password> */
120 /* STRING(pw) <hmac/password> (if any) */
121 };
Miquel Raynal8df6f8d2018-05-15 11:57:14 +0200122 int ret;
123
124 /*
125 * Fill the command structure starting from the first buffer:
126 * - the password (if any)
127 */
128 ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
129 offset, pw, pw_sz);
130 offset += pw_sz;
131 if (ret)
132 return TPM_LIB_ERROR;
133
Simon Glass8ceca1d2018-11-18 14:22:27 -0700134 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal8df6f8d2018-05-15 11:57:14 +0200135}
Miquel Raynal14d72352018-05-15 11:57:15 +0200136
Simon Glass713c58a2021-02-06 14:23:39 -0700137u32 tpm2_nv_define_space(struct udevice *dev, u32 space_index,
138 size_t space_size, u32 nv_attributes,
139 const u8 *nv_policy, size_t nv_policy_size)
140{
141 /*
142 * Calculate the offset of the nv_policy piece by adding each of the
143 * chunks below.
144 */
Simon Glass5252cac2022-08-30 21:05:34 -0600145 const int platform_len = sizeof(u32);
146 const int session_hdr_len = 13;
147 const int message_len = 14;
148 uint offset = TPM2_HDR_LEN + platform_len + session_hdr_len +
149 message_len;
Simon Glass713c58a2021-02-06 14:23:39 -0700150 u8 command_v2[COMMAND_BUFFER_SIZE] = {
151 /* header 10 bytes */
152 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
Simon Glass5252cac2022-08-30 21:05:34 -0600153 tpm_u32(offset + nv_policy_size + 2),/* Length */
Simon Glass713c58a2021-02-06 14:23:39 -0700154 tpm_u32(TPM2_CC_NV_DEFINE_SPACE),/* Command code */
155
Simon Glass5252cac2022-08-30 21:05:34 -0600156 /* handles 4 bytes */
Simon Glass713c58a2021-02-06 14:23:39 -0700157 tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
158
159 /* session header 13 bytes */
160 tpm_u32(9), /* Header size */
161 tpm_u32(TPM2_RS_PW), /* Password authorisation */
162 tpm_u16(0), /* nonce_size */
163 0, /* session_attrs */
164 tpm_u16(0), /* auth_size */
165
166 /* message 14 bytes + policy */
Simon Glass5252cac2022-08-30 21:05:34 -0600167 tpm_u16(message_len + nv_policy_size), /* size */
Simon Glass713c58a2021-02-06 14:23:39 -0700168 tpm_u32(space_index),
169 tpm_u16(TPM2_ALG_SHA256),
170 tpm_u32(nv_attributes),
171 tpm_u16(nv_policy_size),
Simon Glass5252cac2022-08-30 21:05:34 -0600172 /*
173 * nv_policy
174 * space_size
175 */
Simon Glass713c58a2021-02-06 14:23:39 -0700176 };
177 int ret;
178
179 /*
180 * Fill the command structure starting from the first buffer:
181 * - the password (if any)
182 */
Simon Glass5252cac2022-08-30 21:05:34 -0600183 ret = pack_byte_string(command_v2, sizeof(command_v2), "sw",
184 offset, nv_policy, nv_policy_size,
185 offset + nv_policy_size, space_size);
Simon Glass713c58a2021-02-06 14:23:39 -0700186 if (ret)
187 return TPM_LIB_ERROR;
188
189 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
190}
191
Ilias Apalodimas7f59c712020-11-26 23:07:22 +0200192u32 tpm2_pcr_extend(struct udevice *dev, u32 index, u32 algorithm,
193 const u8 *digest, u32 digest_len)
Miquel Raynal14d72352018-05-15 11:57:15 +0200194{
Simon Glass3b3ac8b2021-02-06 14:23:38 -0700195 /* Length of the message header, up to start of digest */
196 uint offset = 33;
Miquel Raynal14d72352018-05-15 11:57:15 +0200197 u8 command_v2[COMMAND_BUFFER_SIZE] = {
198 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
Simon Glass3b3ac8b2021-02-06 14:23:38 -0700199 tpm_u32(offset + digest_len), /* Length */
Miquel Raynal14d72352018-05-15 11:57:15 +0200200 tpm_u32(TPM2_CC_PCR_EXTEND), /* Command code */
201
202 /* HANDLE */
203 tpm_u32(index), /* Handle (PCR Index) */
204
205 /* AUTH_SESSION */
206 tpm_u32(9), /* Authorization size */
207 tpm_u32(TPM2_RS_PW), /* Session handle */
208 tpm_u16(0), /* Size of <nonce> */
209 /* <nonce> (if any) */
210 0, /* Attributes: Cont/Excl/Rst */
211 tpm_u16(0), /* Size of <hmac/password> */
212 /* <hmac/password> (if any) */
Simon Glass3b3ac8b2021-02-06 14:23:38 -0700213
214 /* hashes */
Miquel Raynal14d72352018-05-15 11:57:15 +0200215 tpm_u32(1), /* Count (number of hashes) */
Ilias Apalodimas7f59c712020-11-26 23:07:22 +0200216 tpm_u16(algorithm), /* Algorithm of the hash */
Miquel Raynal14d72352018-05-15 11:57:15 +0200217 /* STRING(digest) Digest */
218 };
Miquel Raynal14d72352018-05-15 11:57:15 +0200219 int ret;
220
Simon Glass4927f472022-08-30 21:05:32 -0600221 if (!digest)
222 return -EINVAL;
Ilias Apalodimas1e665f92024-06-23 14:48:18 +0300223
Ilias Apalodimasd788b062024-12-24 08:01:05 -0800224 if (!tpm2_check_active_banks(dev)) {
Ilias Apalodimas1e665f92024-06-23 14:48:18 +0300225 log_err("Cannot extend PCRs if all the TPM enabled algorithms are not supported\n");
226 return -EINVAL;
227 }
Miquel Raynal14d72352018-05-15 11:57:15 +0200228 /*
229 * Fill the command structure starting from the first buffer:
230 * - the digest
231 */
232 ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
Ilias Apalodimas7f59c712020-11-26 23:07:22 +0200233 offset, digest, digest_len);
Miquel Raynal14d72352018-05-15 11:57:15 +0200234 if (ret)
235 return TPM_LIB_ERROR;
236
Simon Glass8ceca1d2018-11-18 14:22:27 -0700237 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal14d72352018-05-15 11:57:15 +0200238}
Miquel Raynal4c1a5852018-05-15 11:57:16 +0200239
Simon Glass3d930ed2021-02-06 14:23:40 -0700240u32 tpm2_nv_read_value(struct udevice *dev, u32 index, void *data, u32 count)
241{
242 u8 command_v2[COMMAND_BUFFER_SIZE] = {
243 /* header 10 bytes */
244 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
245 tpm_u32(10 + 8 + 4 + 9 + 4), /* Length */
246 tpm_u32(TPM2_CC_NV_READ), /* Command code */
247
248 /* handles 8 bytes */
249 tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
250 tpm_u32(HR_NV_INDEX + index), /* Password authorisation */
251
252 /* AUTH_SESSION */
253 tpm_u32(9), /* Authorization size */
254 tpm_u32(TPM2_RS_PW), /* Session handle */
255 tpm_u16(0), /* Size of <nonce> */
256 /* <nonce> (if any) */
257 0, /* Attributes: Cont/Excl/Rst */
258 tpm_u16(0), /* Size of <hmac/password> */
259 /* <hmac/password> (if any) */
260
261 tpm_u16(count), /* Number of bytes */
262 tpm_u16(0), /* Offset */
263 };
264 size_t response_len = COMMAND_BUFFER_SIZE;
265 u8 response[COMMAND_BUFFER_SIZE];
266 int ret;
267 u16 tag;
268 u32 size, code;
269
270 ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
271 if (ret)
272 return log_msg_ret("read", ret);
273 if (unpack_byte_string(response, response_len, "wdds",
274 0, &tag, 2, &size, 6, &code,
275 16, data, count))
276 return TPM_LIB_ERROR;
277
278 return 0;
279}
280
281u32 tpm2_nv_write_value(struct udevice *dev, u32 index, const void *data,
282 u32 count)
283{
284 struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
285 uint offset = 10 + 8 + 4 + 9 + 2;
286 uint len = offset + count + 2;
287 /* Use empty password auth if platform hierarchy is disabled */
288 u32 auth = priv->plat_hier_disabled ? HR_NV_INDEX + index :
289 TPM2_RH_PLATFORM;
290 u8 command_v2[COMMAND_BUFFER_SIZE] = {
291 /* header 10 bytes */
292 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
293 tpm_u32(len), /* Length */
294 tpm_u32(TPM2_CC_NV_WRITE), /* Command code */
295
296 /* handles 8 bytes */
297 tpm_u32(auth), /* Primary platform seed */
298 tpm_u32(HR_NV_INDEX + index), /* Password authorisation */
299
300 /* AUTH_SESSION */
301 tpm_u32(9), /* Authorization size */
302 tpm_u32(TPM2_RS_PW), /* Session handle */
303 tpm_u16(0), /* Size of <nonce> */
304 /* <nonce> (if any) */
305 0, /* Attributes: Cont/Excl/Rst */
306 tpm_u16(0), /* Size of <hmac/password> */
307 /* <hmac/password> (if any) */
308
309 tpm_u16(count),
310 };
311 size_t response_len = COMMAND_BUFFER_SIZE;
312 u8 response[COMMAND_BUFFER_SIZE];
313 int ret;
314
315 ret = pack_byte_string(command_v2, sizeof(command_v2), "sw",
316 offset, data, count,
317 offset + count, 0);
318 if (ret)
319 return TPM_LIB_ERROR;
320
321 return tpm_sendrecv_command(dev, command_v2, response, &response_len);
322}
323
Simon Glass8ceca1d2018-11-18 14:22:27 -0700324u32 tpm2_pcr_read(struct udevice *dev, u32 idx, unsigned int idx_min_sz,
Ruchika Gupta686bedb2021-11-29 13:09:45 +0530325 u16 algorithm, void *data, u32 digest_len,
326 unsigned int *updates)
Miquel Raynal4c1a5852018-05-15 11:57:16 +0200327{
328 u8 idx_array_sz = max(idx_min_sz, DIV_ROUND_UP(idx, 8));
329 u8 command_v2[COMMAND_BUFFER_SIZE] = {
330 tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
331 tpm_u32(17 + idx_array_sz), /* Length */
332 tpm_u32(TPM2_CC_PCR_READ), /* Command code */
333
334 /* TPML_PCR_SELECTION */
335 tpm_u32(1), /* Number of selections */
Ruchika Gupta686bedb2021-11-29 13:09:45 +0530336 tpm_u16(algorithm), /* Algorithm of the hash */
Miquel Raynal4c1a5852018-05-15 11:57:16 +0200337 idx_array_sz, /* Array size for selection */
338 /* bitmap(idx) Selected PCR bitmap */
339 };
340 size_t response_len = COMMAND_BUFFER_SIZE;
341 u8 response[COMMAND_BUFFER_SIZE];
342 unsigned int pcr_sel_idx = idx / 8;
343 u8 pcr_sel_bit = BIT(idx % 8);
344 unsigned int counter = 0;
345 int ret;
346
347 if (pack_byte_string(command_v2, COMMAND_BUFFER_SIZE, "b",
348 17 + pcr_sel_idx, pcr_sel_bit))
349 return TPM_LIB_ERROR;
350
Simon Glass8ceca1d2018-11-18 14:22:27 -0700351 ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
Miquel Raynal4c1a5852018-05-15 11:57:16 +0200352 if (ret)
353 return ret;
354
Ruchika Gupta686bedb2021-11-29 13:09:45 +0530355 if (digest_len > response_len)
356 return TPM_LIB_ERROR;
357
Miquel Raynal4c1a5852018-05-15 11:57:16 +0200358 if (unpack_byte_string(response, response_len, "ds",
359 10, &counter,
Ruchika Gupta686bedb2021-11-29 13:09:45 +0530360 response_len - digest_len, data,
361 digest_len))
Miquel Raynal4c1a5852018-05-15 11:57:16 +0200362 return TPM_LIB_ERROR;
363
364 if (updates)
365 *updates = counter;
366
367 return 0;
368}
Miquel Raynal2e52c062018-05-15 11:57:17 +0200369
Simon Glass8ceca1d2018-11-18 14:22:27 -0700370u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property,
371 void *buf, size_t prop_count)
Miquel Raynal2e52c062018-05-15 11:57:17 +0200372{
373 u8 command_v2[COMMAND_BUFFER_SIZE] = {
374 tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
375 tpm_u32(22), /* Length */
376 tpm_u32(TPM2_CC_GET_CAPABILITY), /* Command code */
377
378 tpm_u32(capability), /* Capability */
379 tpm_u32(property), /* Property */
380 tpm_u32(prop_count), /* Property count */
381 };
382 u8 response[COMMAND_BUFFER_SIZE];
383 size_t response_len = COMMAND_BUFFER_SIZE;
384 unsigned int properties_off;
385 int ret;
386
Simon Glass8ceca1d2018-11-18 14:22:27 -0700387 ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
Miquel Raynal2e52c062018-05-15 11:57:17 +0200388 if (ret)
389 return ret;
390
391 /*
392 * In the response buffer, the properties are located after the:
393 * tag (u16), response size (u32), response code (u32),
Ilias Apalodimasa0789152020-11-05 23:58:43 +0200394 * YES/NO flag (u8), TPM_CAP (u32).
Miquel Raynal2e52c062018-05-15 11:57:17 +0200395 */
396 properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
Ilias Apalodimasa0789152020-11-05 23:58:43 +0200397 sizeof(u8) + sizeof(u32);
Miquel Raynal2e52c062018-05-15 11:57:17 +0200398 memcpy(buf, &response[properties_off], response_len - properties_off);
399
400 return 0;
401}
Miquel Raynal228e9902018-05-15 11:57:18 +0200402
Eddie James8ed7bb32023-10-24 10:43:49 -0500403static int tpm2_get_num_pcr(struct udevice *dev, u32 *num_pcr)
404{
405 u8 response[(sizeof(struct tpms_capability_data) -
406 offsetof(struct tpms_capability_data, data))];
407 u32 properties_offset =
408 offsetof(struct tpml_tagged_tpm_property, tpm_property) +
409 offsetof(struct tpms_tagged_property, value);
410 u32 ret;
411
412 memset(response, 0, sizeof(response));
413 ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
414 TPM2_PT_PCR_COUNT, response, 1);
415 if (ret)
416 return ret;
417
418 *num_pcr = get_unaligned_be32(response + properties_offset);
419 if (*num_pcr > TPM2_MAX_PCRS) {
420 printf("%s: too many pcrs: %u\n", __func__, *num_pcr);
421 return -E2BIG;
422 }
423
424 return 0;
425}
426
Ilias Apalodimascb356612024-06-23 14:48:17 +0300427int tpm2_get_pcr_info(struct udevice *dev, struct tpml_pcr_selection *pcrs)
Eddie James8ed7bb32023-10-24 10:43:49 -0500428{
429 u8 response[(sizeof(struct tpms_capability_data) -
430 offsetof(struct tpms_capability_data, data))];
Eddie James8ed7bb32023-10-24 10:43:49 -0500431 u32 num_pcr;
432 size_t i;
433 u32 ret;
434
Eddie James8ed7bb32023-10-24 10:43:49 -0500435 ret = tpm2_get_capability(dev, TPM2_CAP_PCRS, 0, response, 1);
436 if (ret)
437 return ret;
438
Ilias Apalodimascb356612024-06-23 14:48:17 +0300439 pcrs->count = get_unaligned_be32(response);
Eddie James8ed7bb32023-10-24 10:43:49 -0500440 /*
Ilias Apalodimas1e665f92024-06-23 14:48:18 +0300441 * We only support 4 algorithms for now so check against that
Eddie James8ed7bb32023-10-24 10:43:49 -0500442 * instead of TPM2_NUM_PCR_BANKS
443 */
Ilias Apalodimas1e665f92024-06-23 14:48:18 +0300444 if (pcrs->count > 4 || pcrs->count < 1) {
Ilias Apalodimascb356612024-06-23 14:48:17 +0300445 printf("%s: too many pcrs: %u\n", __func__, pcrs->count);
Eddie James8ed7bb32023-10-24 10:43:49 -0500446 return -EMSGSIZE;
447 }
448
449 ret = tpm2_get_num_pcr(dev, &num_pcr);
450 if (ret)
451 return ret;
452
Ilias Apalodimascb356612024-06-23 14:48:17 +0300453 for (i = 0; i < pcrs->count; i++) {
Eddie James8ed7bb32023-10-24 10:43:49 -0500454 /*
455 * Definition of TPMS_PCR_SELECTION Structure
456 * hash: u16
457 * size_of_select: u8
458 * pcr_select: u8 array
459 *
460 * The offsets depend on the number of the device PCRs
461 * so we have to calculate them based on that
462 */
463 u32 hash_offset = offsetof(struct tpml_pcr_selection, selection) +
464 i * offsetof(struct tpms_pcr_selection, pcr_select) +
465 i * ((num_pcr + 7) / 8);
466 u32 size_select_offset =
467 hash_offset + offsetof(struct tpms_pcr_selection,
468 size_of_select);
469 u32 pcr_select_offset =
470 hash_offset + offsetof(struct tpms_pcr_selection,
471 pcr_select);
472
Ilias Apalodimascb356612024-06-23 14:48:17 +0300473 pcrs->selection[i].hash =
Eddie James8ed7bb32023-10-24 10:43:49 -0500474 get_unaligned_be16(response + hash_offset);
Ilias Apalodimascb356612024-06-23 14:48:17 +0300475 pcrs->selection[i].size_of_select =
Eddie James8ed7bb32023-10-24 10:43:49 -0500476 __get_unaligned_be(response + size_select_offset);
Ilias Apalodimascb356612024-06-23 14:48:17 +0300477 if (pcrs->selection[i].size_of_select > TPM2_PCR_SELECT_MAX) {
Eddie James8ed7bb32023-10-24 10:43:49 -0500478 printf("%s: pcrs selection too large: %u\n", __func__,
Ilias Apalodimascb356612024-06-23 14:48:17 +0300479 pcrs->selection[i].size_of_select);
Eddie James8ed7bb32023-10-24 10:43:49 -0500480 return -ENOBUFS;
481 }
482 /* copy the array of pcr_select */
Ilias Apalodimascb356612024-06-23 14:48:17 +0300483 memcpy(pcrs->selection[i].pcr_select, response + pcr_select_offset,
484 pcrs->selection[i].size_of_select);
Eddie James8ed7bb32023-10-24 10:43:49 -0500485 }
486
Eddie James8ed7bb32023-10-24 10:43:49 -0500487 return 0;
488}
489
Simon Glass8ceca1d2018-11-18 14:22:27 -0700490u32 tpm2_dam_reset(struct udevice *dev, const char *pw, const ssize_t pw_sz)
Miquel Raynal228e9902018-05-15 11:57:18 +0200491{
492 u8 command_v2[COMMAND_BUFFER_SIZE] = {
493 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
494 tpm_u32(27 + pw_sz), /* Length */
495 tpm_u32(TPM2_CC_DAM_RESET), /* Command code */
496
497 /* HANDLE */
498 tpm_u32(TPM2_RH_LOCKOUT), /* TPM resource handle */
499
500 /* AUTH_SESSION */
501 tpm_u32(9 + pw_sz), /* Authorization size */
502 tpm_u32(TPM2_RS_PW), /* Session handle */
503 tpm_u16(0), /* Size of <nonce> */
504 /* <nonce> (if any) */
505 0, /* Attributes: Cont/Excl/Rst */
506 tpm_u16(pw_sz), /* Size of <hmac/password> */
507 /* STRING(pw) <hmac/password> (if any) */
508 };
509 unsigned int offset = 27;
510 int ret;
511
512 /*
513 * Fill the command structure starting from the first buffer:
514 * - the password (if any)
515 */
516 ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
517 offset, pw, pw_sz);
518 offset += pw_sz;
519 if (ret)
520 return TPM_LIB_ERROR;
521
Simon Glass8ceca1d2018-11-18 14:22:27 -0700522 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal228e9902018-05-15 11:57:18 +0200523}
524
Simon Glass8ceca1d2018-11-18 14:22:27 -0700525u32 tpm2_dam_parameters(struct udevice *dev, const char *pw,
526 const ssize_t pw_sz, unsigned int max_tries,
527 unsigned int recovery_time,
Miquel Raynal228e9902018-05-15 11:57:18 +0200528 unsigned int lockout_recovery)
529{
530 u8 command_v2[COMMAND_BUFFER_SIZE] = {
531 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
532 tpm_u32(27 + pw_sz + 12), /* Length */
533 tpm_u32(TPM2_CC_DAM_PARAMETERS), /* Command code */
534
535 /* HANDLE */
536 tpm_u32(TPM2_RH_LOCKOUT), /* TPM resource handle */
537
538 /* AUTH_SESSION */
539 tpm_u32(9 + pw_sz), /* Authorization size */
540 tpm_u32(TPM2_RS_PW), /* Session handle */
541 tpm_u16(0), /* Size of <nonce> */
542 /* <nonce> (if any) */
543 0, /* Attributes: Cont/Excl/Rst */
544 tpm_u16(pw_sz), /* Size of <hmac/password> */
545 /* STRING(pw) <hmac/password> (if any) */
546
547 /* LOCKOUT PARAMETERS */
548 /* tpm_u32(max_tries) Max tries (0, always lock) */
549 /* tpm_u32(recovery_time) Recovery time (0, no lock) */
550 /* tpm_u32(lockout_recovery) Lockout recovery */
551 };
552 unsigned int offset = 27;
553 int ret;
554
555 /*
556 * Fill the command structure starting from the first buffer:
557 * - the password (if any)
558 * - max tries
559 * - recovery time
560 * - lockout recovery
561 */
562 ret = pack_byte_string(command_v2, sizeof(command_v2), "sddd",
563 offset, pw, pw_sz,
564 offset + pw_sz, max_tries,
565 offset + pw_sz + 4, recovery_time,
566 offset + pw_sz + 8, lockout_recovery);
567 offset += pw_sz + 12;
568 if (ret)
569 return TPM_LIB_ERROR;
570
Simon Glass8ceca1d2018-11-18 14:22:27 -0700571 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal228e9902018-05-15 11:57:18 +0200572}
Miquel Raynal05d7be32018-05-15 11:57:19 +0200573
Simon Glass8ceca1d2018-11-18 14:22:27 -0700574int tpm2_change_auth(struct udevice *dev, u32 handle, const char *newpw,
575 const ssize_t newpw_sz, const char *oldpw,
576 const ssize_t oldpw_sz)
Miquel Raynal05d7be32018-05-15 11:57:19 +0200577{
578 unsigned int offset = 27;
579 u8 command_v2[COMMAND_BUFFER_SIZE] = {
580 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
581 tpm_u32(offset + oldpw_sz + 2 + newpw_sz), /* Length */
582 tpm_u32(TPM2_CC_HIERCHANGEAUTH), /* Command code */
583
584 /* HANDLE */
585 tpm_u32(handle), /* TPM resource handle */
586
587 /* AUTH_SESSION */
588 tpm_u32(9 + oldpw_sz), /* Authorization size */
589 tpm_u32(TPM2_RS_PW), /* Session handle */
590 tpm_u16(0), /* Size of <nonce> */
591 /* <nonce> (if any) */
592 0, /* Attributes: Cont/Excl/Rst */
593 tpm_u16(oldpw_sz) /* Size of <hmac/password> */
594 /* STRING(oldpw) <hmac/password> (if any) */
595
596 /* TPM2B_AUTH (TPM2B_DIGEST) */
597 /* tpm_u16(newpw_sz) Digest size, new pw length */
598 /* STRING(newpw) Digest buffer, new pw */
599 };
600 int ret;
601
602 /*
603 * Fill the command structure starting from the first buffer:
604 * - the old password (if any)
605 * - size of the new password
606 * - new password
607 */
608 ret = pack_byte_string(command_v2, sizeof(command_v2), "sws",
609 offset, oldpw, oldpw_sz,
610 offset + oldpw_sz, newpw_sz,
611 offset + oldpw_sz + 2, newpw, newpw_sz);
612 offset += oldpw_sz + 2 + newpw_sz;
613 if (ret)
614 return TPM_LIB_ERROR;
615
Simon Glass8ceca1d2018-11-18 14:22:27 -0700616 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal05d7be32018-05-15 11:57:19 +0200617}
Miquel Raynal0b864f62018-05-15 11:57:20 +0200618
Simon Glass8ceca1d2018-11-18 14:22:27 -0700619u32 tpm2_pcr_setauthpolicy(struct udevice *dev, const char *pw,
620 const ssize_t pw_sz, u32 index, const char *key)
Miquel Raynal0b864f62018-05-15 11:57:20 +0200621{
622 u8 command_v2[COMMAND_BUFFER_SIZE] = {
623 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
624 tpm_u32(35 + pw_sz + TPM2_DIGEST_LEN), /* Length */
625 tpm_u32(TPM2_CC_PCR_SETAUTHPOL), /* Command code */
626
627 /* HANDLE */
628 tpm_u32(TPM2_RH_PLATFORM), /* TPM resource handle */
629
630 /* AUTH_SESSION */
631 tpm_u32(9 + pw_sz), /* Authorization size */
632 tpm_u32(TPM2_RS_PW), /* session handle */
633 tpm_u16(0), /* Size of <nonce> */
634 /* <nonce> (if any) */
635 0, /* Attributes: Cont/Excl/Rst */
636 tpm_u16(pw_sz) /* Size of <hmac/password> */
637 /* STRING(pw) <hmac/password> (if any) */
638
639 /* TPM2B_AUTH (TPM2B_DIGEST) */
640 /* tpm_u16(TPM2_DIGEST_LEN) Digest size length */
641 /* STRING(key) Digest buffer (PCR key) */
642
643 /* TPMI_ALG_HASH */
644 /* tpm_u16(TPM2_ALG_SHA256) Algorithm of the hash */
645
646 /* TPMI_DH_PCR */
647 /* tpm_u32(index), PCR Index */
648 };
649 unsigned int offset = 27;
650 int ret;
651
652 /*
653 * Fill the command structure starting from the first buffer:
654 * - the password (if any)
655 * - the PCR key length
656 * - the PCR key
657 * - the hash algorithm
658 * - the PCR index
659 */
660 ret = pack_byte_string(command_v2, sizeof(command_v2), "swswd",
661 offset, pw, pw_sz,
662 offset + pw_sz, TPM2_DIGEST_LEN,
663 offset + pw_sz + 2, key, TPM2_DIGEST_LEN,
664 offset + pw_sz + 2 + TPM2_DIGEST_LEN,
665 TPM2_ALG_SHA256,
666 offset + pw_sz + 4 + TPM2_DIGEST_LEN, index);
667 offset += pw_sz + 2 + TPM2_DIGEST_LEN + 2 + 4;
668 if (ret)
669 return TPM_LIB_ERROR;
670
Simon Glass8ceca1d2018-11-18 14:22:27 -0700671 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal0b864f62018-05-15 11:57:20 +0200672}
673
Simon Glass8ceca1d2018-11-18 14:22:27 -0700674u32 tpm2_pcr_setauthvalue(struct udevice *dev, const char *pw,
675 const ssize_t pw_sz, u32 index, const char *key,
676 const ssize_t key_sz)
Miquel Raynal0b864f62018-05-15 11:57:20 +0200677{
678 u8 command_v2[COMMAND_BUFFER_SIZE] = {
679 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
680 tpm_u32(33 + pw_sz + TPM2_DIGEST_LEN), /* Length */
681 tpm_u32(TPM2_CC_PCR_SETAUTHVAL), /* Command code */
682
683 /* HANDLE */
684 tpm_u32(index), /* Handle (PCR Index) */
685
686 /* AUTH_SESSION */
687 tpm_u32(9 + pw_sz), /* Authorization size */
688 tpm_u32(TPM2_RS_PW), /* session handle */
689 tpm_u16(0), /* Size of <nonce> */
690 /* <nonce> (if any) */
691 0, /* Attributes: Cont/Excl/Rst */
692 tpm_u16(pw_sz), /* Size of <hmac/password> */
693 /* STRING(pw) <hmac/password> (if any) */
694
695 /* TPM2B_DIGEST */
696 /* tpm_u16(key_sz) Key length */
697 /* STRING(key) Key */
698 };
699 unsigned int offset = 27;
700 int ret;
701
702 /*
703 * Fill the command structure starting from the first buffer:
704 * - the password (if any)
705 * - the number of digests, 1 in our case
706 * - the algorithm, sha256 in our case
707 * - the digest (64 bytes)
708 */
709 ret = pack_byte_string(command_v2, sizeof(command_v2), "sws",
710 offset, pw, pw_sz,
711 offset + pw_sz, key_sz,
712 offset + pw_sz + 2, key, key_sz);
713 offset += pw_sz + 2 + key_sz;
714 if (ret)
715 return TPM_LIB_ERROR;
716
Simon Glass8ceca1d2018-11-18 14:22:27 -0700717 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
Miquel Raynal0b864f62018-05-15 11:57:20 +0200718}
Dhananjay Phadke7a2cf2e2020-06-04 16:43:59 -0700719
720u32 tpm2_get_random(struct udevice *dev, void *data, u32 count)
721{
722 const u8 command_v2[10] = {
723 tpm_u16(TPM2_ST_NO_SESSIONS),
724 tpm_u32(12),
725 tpm_u32(TPM2_CC_GET_RANDOM),
726 };
727 u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
728
729 const size_t data_size_offset = 10;
730 const size_t data_offset = 12;
731 size_t response_length = sizeof(response);
732 u32 data_size;
733 u8 *out = data;
734
735 while (count > 0) {
736 u32 this_bytes = min((size_t)count,
737 sizeof(response) - data_offset);
738 u32 err;
739
740 if (pack_byte_string(buf, sizeof(buf), "sw",
741 0, command_v2, sizeof(command_v2),
742 sizeof(command_v2), this_bytes))
743 return TPM_LIB_ERROR;
744 err = tpm_sendrecv_command(dev, buf, response,
745 &response_length);
746 if (err)
747 return err;
748 if (unpack_byte_string(response, response_length, "w",
749 data_size_offset, &data_size))
750 return TPM_LIB_ERROR;
751 if (data_size > this_bytes)
752 return TPM_LIB_ERROR;
753 if (unpack_byte_string(response, response_length, "s",
754 data_offset, out, data_size))
755 return TPM_LIB_ERROR;
756
757 count -= data_size;
758 out += data_size;
759 }
760
761 return 0;
762}
Simon Glasse9d3d592021-02-06 14:23:41 -0700763
764u32 tpm2_write_lock(struct udevice *dev, u32 index)
765{
766 u8 command_v2[COMMAND_BUFFER_SIZE] = {
767 /* header 10 bytes */
768 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
769 tpm_u32(10 + 8 + 13), /* Length */
770 tpm_u32(TPM2_CC_NV_WRITELOCK), /* Command code */
771
772 /* handles 8 bytes */
773 tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
774 tpm_u32(HR_NV_INDEX + index), /* Password authorisation */
775
776 /* session header 9 bytes */
777 tpm_u32(9), /* Header size */
778 tpm_u32(TPM2_RS_PW), /* Password authorisation */
779 tpm_u16(0), /* nonce_size */
780 0, /* session_attrs */
781 tpm_u16(0), /* auth_size */
782 };
783
784 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
785}
Simon Glass77759db2021-02-06 14:23:42 -0700786
787u32 tpm2_disable_platform_hierarchy(struct udevice *dev)
788{
789 struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
790 u8 command_v2[COMMAND_BUFFER_SIZE] = {
791 /* header 10 bytes */
792 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
793 tpm_u32(10 + 4 + 13 + 5), /* Length */
794 tpm_u32(TPM2_CC_HIER_CONTROL), /* Command code */
795
796 /* 4 bytes */
797 tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
798
799 /* session header 9 bytes */
800 tpm_u32(9), /* Header size */
801 tpm_u32(TPM2_RS_PW), /* Password authorisation */
802 tpm_u16(0), /* nonce_size */
803 0, /* session_attrs */
804 tpm_u16(0), /* auth_size */
805
806 /* payload 5 bytes */
807 tpm_u32(TPM2_RH_PLATFORM), /* Hierarchy to disable */
808 0, /* 0=disable */
809 };
810 int ret;
811
812 ret = tpm_sendrecv_command(dev, command_v2, NULL, NULL);
813 log_info("ret=%s, %x\n", dev->name, ret);
814 if (ret)
815 return ret;
816
817 priv->plat_hier_disabled = true;
818
819 return 0;
820}
Masahisa Kojima06ef6b62021-11-04 22:59:16 +0900821
822u32 tpm2_submit_command(struct udevice *dev, const u8 *sendbuf,
823 u8 *recvbuf, size_t *recv_size)
824{
825 return tpm_sendrecv_command(dev, sendbuf, recvbuf, recv_size);
826}
Simon Glass3f7a73a2022-08-30 21:05:37 -0600827
828u32 tpm2_report_state(struct udevice *dev, uint vendor_cmd, uint vendor_subcmd,
829 u8 *recvbuf, size_t *recv_size)
830{
831 u8 command_v2[COMMAND_BUFFER_SIZE] = {
832 /* header 10 bytes */
833 tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
834 tpm_u32(10 + 2), /* Length */
835 tpm_u32(vendor_cmd), /* Command code */
836
837 tpm_u16(vendor_subcmd),
838 };
839 int ret;
840
841 ret = tpm_sendrecv_command(dev, command_v2, recvbuf, recv_size);
842 log_debug("ret=%s, %x\n", dev->name, ret);
843 if (ret)
844 return ret;
845 if (*recv_size < 12)
846 return -ENODATA;
847 *recv_size -= 12;
Heinrich Schuchardt5b8577d2024-11-02 11:27:37 +0100848 memmove(recvbuf, recvbuf + 12, *recv_size);
Simon Glass3f7a73a2022-08-30 21:05:37 -0600849
850 return 0;
851}
Simon Glass3564b8e2022-08-30 21:05:38 -0600852
853u32 tpm2_enable_nvcommits(struct udevice *dev, uint vendor_cmd,
854 uint vendor_subcmd)
855{
856 u8 command_v2[COMMAND_BUFFER_SIZE] = {
857 /* header 10 bytes */
858 tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
859 tpm_u32(10 + 2), /* Length */
860 tpm_u32(vendor_cmd), /* Command code */
861
862 tpm_u16(vendor_subcmd),
863 };
864 int ret;
865
866 ret = tpm_sendrecv_command(dev, command_v2, NULL, NULL);
867 log_debug("ret=%s, %x\n", dev->name, ret);
868 if (ret)
869 return ret;
870
871 return 0;
872}
Tim Harvey6ea1e052024-05-25 13:00:48 -0700873
Ilias Apalodimas9465f7a2024-12-24 08:01:04 -0800874bool tpm2_is_active_bank(struct tpms_pcr_selection *selection)
Ilias Apalodimascb356612024-06-23 14:48:17 +0300875{
876 int i;
877
878 for (i = 0; i < selection->size_of_select; i++) {
879 if (selection->pcr_select[i])
880 return true;
881 }
882
883 return false;
884}
885
Tim Harvey6ea1e052024-05-25 13:00:48 -0700886enum tpm2_algorithms tpm2_name_to_algorithm(const char *name)
887{
888 size_t i;
889
890 for (i = 0; i < ARRAY_SIZE(hash_algo_list); ++i) {
891 if (!strcasecmp(name, hash_algo_list[i].hash_name))
892 return hash_algo_list[i].hash_alg;
893 }
894 printf("%s: unsupported algorithm %s\n", __func__, name);
895
896 return -EINVAL;
897}
898
899const char *tpm2_algorithm_name(enum tpm2_algorithms algo)
900{
901 size_t i;
902
903 for (i = 0; i < ARRAY_SIZE(hash_algo_list); ++i) {
904 if (hash_algo_list[i].hash_alg == algo)
905 return hash_algo_list[i].hash_name;
906 }
907
908 return "";
909}
910
Raymond Mao43158122024-12-24 08:01:07 -0800911bool tpm2_algorithm_supported(enum tpm2_algorithms algo)
912{
913 size_t i;
914
915 for (i = 0; i < ARRAY_SIZE(hash_algo_list); ++i) {
916 if (hash_algo_list[i].hash_alg == algo)
917 return hash_algo_list[i].supported;
918 }
919
920 return false;
921}
922
Ilias Apalodimas1e665f92024-06-23 14:48:18 +0300923u16 tpm2_algorithm_to_len(enum tpm2_algorithms algo)
924{
925 size_t i;
926
927 for (i = 0; i < ARRAY_SIZE(hash_algo_list); ++i) {
928 if (hash_algo_list[i].hash_alg == algo)
929 return hash_algo_list[i].hash_len;
930 }
931
932 return 0;
933}
934
Ilias Apalodimasd788b062024-12-24 08:01:05 -0800935bool tpm2_check_active_banks(struct udevice *dev)
Ilias Apalodimas1e665f92024-06-23 14:48:18 +0300936{
937 struct tpml_pcr_selection pcrs;
938 size_t i;
939 int rc;
940
941 rc = tpm2_get_pcr_info(dev, &pcrs);
942 if (rc)
943 return false;
944
945 for (i = 0; i < pcrs.count; i++) {
Ilias Apalodimas9465f7a2024-12-24 08:01:04 -0800946 if (tpm2_is_active_bank(&pcrs.selection[i]) &&
Raymond Mao43158122024-12-24 08:01:07 -0800947 !tpm2_algorithm_supported(pcrs.selection[i].hash))
Ilias Apalodimas1e665f92024-06-23 14:48:18 +0300948 return false;
949 }
950
951 return true;
952}
Ilias Apalodimas7b1e5222024-12-24 08:01:08 -0800953
954void tpm2_print_active_banks(struct udevice *dev)
955{
956 struct tpml_pcr_selection pcrs;
957 size_t i;
958 int rc;
959
960 rc = tpm2_get_pcr_info(dev, &pcrs);
961 if (rc) {
962 log_err("Can't retrieve active PCRs\n");
963 return;
964 }
965
966 for (i = 0; i < pcrs.count; i++) {
967 if (tpm2_is_active_bank(&pcrs.selection[i])) {
968 const char *str;
969
970 str = tpm2_algorithm_name(pcrs.selection[i].hash);
971 if (str)
972 log_info("%s\n", str);
973 }
974 }
975}