blob: d15a28d9fc82d943a4b7b34f039c190471904dd7 [file] [log] [blame]
Miquel Raynalcf4bfea2018-05-15 11:57:25 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2018, Bootlin
4 * Author: Miquel Raynal <miquel.raynal@bootlin.com>
5 */
6
7#include <common.h>
8#include <dm.h>
9#include <tpm-v2.h>
10#include <asm/state.h>
11#include <asm/unaligned.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060012#include <linux/bitops.h>
Simon Glassca9b0af2019-11-14 12:57:14 -070013#include <u-boot/crc.h>
Simon Glass00211022021-07-18 14:18:06 -060014#include <u-boot/sha256.h>
Simon Glassca31f072021-07-18 14:18:03 -060015#include "sandbox_common.h"
Miquel Raynalcf4bfea2018-05-15 11:57:25 +020016
17/* Hierarchies */
18enum tpm2_hierarchy {
19 TPM2_HIERARCHY_LOCKOUT = 0,
20 TPM2_HIERARCHY_ENDORSEMENT,
21 TPM2_HIERARCHY_PLATFORM,
22 TPM2_HIERARCHY_NB,
23};
24
Miquel Raynalcf4bfea2018-05-15 11:57:25 +020025/* Subset of supported properties */
26#define TPM2_PROPERTIES_OFFSET 0x0000020E
27
28enum tpm2_cap_tpm_property {
29 TPM2_FAIL_COUNTER = 0,
30 TPM2_PROP_MAX_TRIES,
31 TPM2_RECOVERY_TIME,
32 TPM2_LOCKOUT_RECOVERY,
33 TPM2_PROPERTY_NB,
34};
35
Eddie Jamesd64202a2023-10-24 10:43:48 -050036#define SANDBOX_TPM_PCR_NB TPM2_MAX_PCRS
37#define SANDBOX_TPM_PCR_SELECT_MAX ((SANDBOX_TPM_PCR_NB + 7) / 8)
Miquel Raynalcf4bfea2018-05-15 11:57:25 +020038
Simon Glass92594f12021-07-18 14:18:01 -060039/*
40 * Information about our TPM emulation. This is preserved in the sandbox
41 * state file if enabled.
42 *
Simon Glasse39503c2021-07-18 14:18:02 -060043 * @valid: true if this is valid (only used in s_state)
Simon Glass92594f12021-07-18 14:18:01 -060044 * @init_done: true if open() has been called
45 * @startup_done: true if TPM2_CC_STARTUP has been processed
46 * @tests_done: true if TPM2_CC_SELF_TEST has be processed
47 * @pw: TPM password per hierarchy
48 * @pw_sz: Size of each password in bytes
49 * @properties: TPM properties
50 * @pcr: TPM Platform Configuration Registers. Each of these holds a hash and
51 * can be 'extended' a number of times, meaning another hash is added into
52 * its value (initial value all zeroes)
53 * @pcr_extensions: Number of times each PCR has been extended (starts at 0)
54 * @nvdata: non-volatile data, used to store important things for the platform
55 */
Miquel Raynalcf4bfea2018-05-15 11:57:25 +020056struct sandbox_tpm2 {
Simon Glasse39503c2021-07-18 14:18:02 -060057 bool valid;
Miquel Raynalcf4bfea2018-05-15 11:57:25 +020058 /* TPM internal states */
59 bool init_done;
60 bool startup_done;
61 bool tests_done;
Miquel Raynalcf4bfea2018-05-15 11:57:25 +020062 char pw[TPM2_HIERARCHY_NB][TPM2_DIGEST_LEN + 1];
63 int pw_sz[TPM2_HIERARCHY_NB];
Miquel Raynalcf4bfea2018-05-15 11:57:25 +020064 u32 properties[TPM2_PROPERTY_NB];
Miquel Raynalcf4bfea2018-05-15 11:57:25 +020065 u8 pcr[SANDBOX_TPM_PCR_NB][TPM2_DIGEST_LEN];
Miquel Raynalcf4bfea2018-05-15 11:57:25 +020066 u32 pcr_extensions[SANDBOX_TPM_PCR_NB];
Simon Glassca31f072021-07-18 14:18:03 -060067 struct nvdata_state nvdata[NV_SEQ_COUNT];
Miquel Raynalcf4bfea2018-05-15 11:57:25 +020068};
69
Simon Glasse39503c2021-07-18 14:18:02 -060070static struct sandbox_tpm2 s_state, *g_state;
71
Simon Glass3220cb02021-07-18 14:18:04 -060072/**
73 * sandbox_tpm2_read_state() - read the sandbox EC state from the state file
74 *
75 * If data is available, then blob and node will provide access to it. If
76 * not this function sets up an empty TPM.
77 *
78 * @blob: Pointer to device tree blob, or NULL if no data to read
79 * @node: Node offset to read from
80 */
81static int sandbox_tpm2_read_state(const void *blob, int node)
82{
83 struct sandbox_tpm2 *state = &s_state;
84 char prop_name[20];
85 const char *prop;
86 int len;
87 int i;
88
89 if (!blob)
90 return 0;
91 state->tests_done = fdtdec_get_int(blob, node, "tests-done", 0);
92
93 for (i = 0; i < TPM2_HIERARCHY_NB; i++) {
94 snprintf(prop_name, sizeof(prop_name), "pw%d", i);
95
96 prop = fdt_getprop(blob, node, prop_name, &len);
97 if (len > TPM2_DIGEST_LEN)
98 return log_msg_ret("pw", -E2BIG);
99 if (prop) {
100 memcpy(state->pw[i], prop, len);
101 state->pw_sz[i] = len;
102 }
103 }
104
105 for (i = 0; i < TPM2_PROPERTY_NB; i++) {
106 snprintf(prop_name, sizeof(prop_name), "properties%d", i);
107 state->properties[i] = fdtdec_get_uint(blob, node, prop_name,
108 0);
109 }
110
111 for (i = 0; i < SANDBOX_TPM_PCR_NB; i++) {
112 int subnode;
113
114 snprintf(prop_name, sizeof(prop_name), "pcr%d", i);
115 subnode = fdt_subnode_offset(blob, node, prop_name);
116 if (subnode < 0)
117 continue;
118 prop = fdt_getprop(blob, subnode, "value", &len);
119 if (len != TPM2_DIGEST_LEN)
120 return log_msg_ret("pcr", -E2BIG);
121 memcpy(state->pcr[i], prop, TPM2_DIGEST_LEN);
122 state->pcr_extensions[i] = fdtdec_get_uint(blob, subnode,
123 "extensions", 0);
124 }
125
126 for (i = 0; i < NV_SEQ_COUNT; i++) {
127 struct nvdata_state *nvd = &state->nvdata[i];
128
129 sprintf(prop_name, "nvdata%d", i);
130 prop = fdt_getprop(blob, node, prop_name, &len);
131 if (len > NV_DATA_SIZE)
132 return log_msg_ret("nvd", -E2BIG);
133 if (prop) {
134 memcpy(nvd->data, prop, len);
135 nvd->length = len;
136 nvd->present = true;
137 }
138 }
139 s_state.valid = true;
140
141 return 0;
142}
143
144/**
145 * sandbox_tpm2_write_state() - Write out our state to the state file
146 *
147 * The caller will ensure that there is a node ready for the state. The node
148 * may already contain the old state, in which case it is overridden.
149 *
150 * @blob: Device tree blob holding state
151 * @node: Node to write our state into
152 */
153static int sandbox_tpm2_write_state(void *blob, int node)
154{
155 const struct sandbox_tpm2 *state = g_state;
156 char prop_name[20];
157 int i;
158
159 if (!state)
160 return 0;
161
162 /*
163 * We are guaranteed enough space to write basic properties. This is
164 * SANDBOX_STATE_MIN_SPACE.
165 *
166 * We could use fdt_add_subnode() to put each set of data in its
167 * own node - perhaps useful if we add access information to each.
168 */
169 fdt_setprop_u32(blob, node, "tests-done", state->tests_done);
170
171 for (i = 0; i < TPM2_HIERARCHY_NB; i++) {
172 if (state->pw_sz[i]) {
173 snprintf(prop_name, sizeof(prop_name), "pw%d", i);
174 fdt_setprop(blob, node, prop_name, state->pw[i],
175 state->pw_sz[i]);
176 }
177 }
178
179 for (i = 0; i < TPM2_PROPERTY_NB; i++) {
180 snprintf(prop_name, sizeof(prop_name), "properties%d", i);
181 fdt_setprop_u32(blob, node, prop_name, state->properties[i]);
182 }
183
184 for (i = 0; i < SANDBOX_TPM_PCR_NB; i++) {
185 int subnode;
186
187 snprintf(prop_name, sizeof(prop_name), "pcr%d", i);
188 subnode = fdt_add_subnode(blob, node, prop_name);
189 fdt_setprop(blob, subnode, "value", state->pcr[i],
190 TPM2_DIGEST_LEN);
191 fdt_setprop_u32(blob, subnode, "extensions",
192 state->pcr_extensions[i]);
193 }
194
195 for (i = 0; i < NV_SEQ_COUNT; i++) {
196 const struct nvdata_state *nvd = &state->nvdata[i];
197
198 if (nvd->present) {
199 snprintf(prop_name, sizeof(prop_name), "nvdata%d", i);
200 fdt_setprop(blob, node, prop_name, nvd->data,
201 nvd->length);
202 }
203 }
204
205 return 0;
206}
207
208SANDBOX_STATE_IO(sandbox_tpm2, "sandbox,tpm2", sandbox_tpm2_read_state,
209 sandbox_tpm2_write_state);
210
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200211/*
212 * Check the tag validity depending on the command (authentication required or
213 * not). If authentication is required, check it is valid. Update the auth
214 * pointer to point to the next chunk of data to process if needed.
215 */
216static int sandbox_tpm2_check_session(struct udevice *dev, u32 command, u16 tag,
217 const u8 **auth,
218 enum tpm2_hierarchy *hierarchy)
219{
220 struct sandbox_tpm2 *tpm = dev_get_priv(dev);
221 u32 handle, auth_sz, session_handle;
222 u16 nonce_sz, pw_sz;
223 const char *pw;
224
225 switch (command) {
226 case TPM2_CC_STARTUP:
227 case TPM2_CC_SELF_TEST:
228 case TPM2_CC_GET_CAPABILITY:
229 case TPM2_CC_PCR_READ:
230 if (tag != TPM2_ST_NO_SESSIONS) {
231 printf("No session required for command 0x%x\n",
232 command);
233 return TPM2_RC_BAD_TAG;
234 }
235
236 return 0;
237
238 case TPM2_CC_CLEAR:
239 case TPM2_CC_HIERCHANGEAUTH:
240 case TPM2_CC_DAM_RESET:
241 case TPM2_CC_DAM_PARAMETERS:
242 case TPM2_CC_PCR_EXTEND:
Simon Glassca31f072021-07-18 14:18:03 -0600243 case TPM2_CC_NV_READ:
244 case TPM2_CC_NV_WRITE:
245 case TPM2_CC_NV_WRITELOCK:
246 case TPM2_CC_NV_DEFINE_SPACE:
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200247 if (tag != TPM2_ST_SESSIONS) {
248 printf("Session required for command 0x%x\n", command);
249 return TPM2_RC_AUTH_CONTEXT;
250 }
251
252 handle = get_unaligned_be32(*auth);
253 *auth += sizeof(handle);
254
255 /*
256 * PCR_Extend had a different protection mechanism and does not
257 * use the same standards as other commands.
258 */
259 if (command == TPM2_CC_PCR_EXTEND)
260 break;
261
262 switch (handle) {
263 case TPM2_RH_LOCKOUT:
264 *hierarchy = TPM2_HIERARCHY_LOCKOUT;
265 break;
266 case TPM2_RH_ENDORSEMENT:
267 if (command == TPM2_CC_CLEAR) {
268 printf("Endorsement hierarchy unsupported\n");
269 return TPM2_RC_AUTH_MISSING;
270 }
271 *hierarchy = TPM2_HIERARCHY_ENDORSEMENT;
272 break;
273 case TPM2_RH_PLATFORM:
274 *hierarchy = TPM2_HIERARCHY_PLATFORM;
Simon Glassca31f072021-07-18 14:18:03 -0600275 if (command == TPM2_CC_NV_READ ||
276 command == TPM2_CC_NV_WRITE ||
277 command == TPM2_CC_NV_WRITELOCK)
278 *auth += sizeof(u32);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200279 break;
280 default:
281 printf("Wrong handle 0x%x\n", handle);
282 return TPM2_RC_VALUE;
283 }
284
285 break;
286
287 default:
288 printf("Command code not recognized: 0x%x\n", command);
289 return TPM2_RC_COMMAND_CODE;
290 }
291
292 auth_sz = get_unaligned_be32(*auth);
293 *auth += sizeof(auth_sz);
294
295 session_handle = get_unaligned_be32(*auth);
296 *auth += sizeof(session_handle);
297 if (session_handle != TPM2_RS_PW) {
298 printf("Wrong session handle 0x%x\n", session_handle);
299 return TPM2_RC_VALUE;
300 }
301
302 nonce_sz = get_unaligned_be16(*auth);
303 *auth += sizeof(nonce_sz);
304 if (nonce_sz) {
305 printf("Nonces not supported in Sandbox, aborting\n");
306 return TPM2_RC_HANDLE;
307 }
308
309 /* Ignore attributes */
310 *auth += sizeof(u8);
311
312 pw_sz = get_unaligned_be16(*auth);
313 *auth += sizeof(pw_sz);
314 if (auth_sz != (9 + nonce_sz + pw_sz)) {
315 printf("Authentication size (%d) do not match %d\n",
316 auth_sz, 9 + nonce_sz + pw_sz);
317 return TPM2_RC_SIZE;
318 }
319
320 /* No passwork is acceptable */
321 if (!pw_sz && !tpm->pw_sz[*hierarchy])
322 return TPM2_RC_SUCCESS;
323
324 /* Password is too long */
325 if (pw_sz > TPM2_DIGEST_LEN) {
326 printf("Password should not be more than %dB\n",
327 TPM2_DIGEST_LEN);
328 return TPM2_RC_AUTHSIZE;
329 }
330
331 pw = (const char *)*auth;
332 *auth += pw_sz;
333
334 /* Password is wrong */
335 if (pw_sz != tpm->pw_sz[*hierarchy] ||
336 strncmp(pw, tpm->pw[*hierarchy], tpm->pw_sz[*hierarchy])) {
337 printf("Authentication failed: wrong password.\n");
338 return TPM2_RC_BAD_AUTH;
339 }
340
341 return TPM2_RC_SUCCESS;
342}
343
344static int sandbox_tpm2_check_readyness(struct udevice *dev, int command)
345{
346 struct sandbox_tpm2 *tpm = dev_get_priv(dev);
347
348 switch (command) {
349 case TPM2_CC_STARTUP:
350 if (!tpm->init_done || tpm->startup_done)
351 return TPM2_RC_INITIALIZE;
352
353 break;
354 case TPM2_CC_GET_CAPABILITY:
355 if (!tpm->init_done || !tpm->startup_done)
356 return TPM2_RC_INITIALIZE;
357
358 break;
359 case TPM2_CC_SELF_TEST:
360 if (!tpm->startup_done)
361 return TPM2_RC_INITIALIZE;
362
363 break;
364 default:
Simon Glassbddfcea2022-08-30 21:05:35 -0600365 /* Skip this, since the startup may have happened in SPL
366 * if (!tpm->tests_done)
367 * return TPM2_RC_NEEDS_TEST;
368 */
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200369
370 break;
371 }
372
373 return 0;
374}
375
Miquel Raynal3ca89672018-08-05 18:53:07 +0200376static int sandbox_tpm2_fill_buf(u8 *recv, size_t *recv_len, u16 tag, u32 rc)
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200377{
378 *recv_len = sizeof(tag) + sizeof(u32) + sizeof(rc);
379
380 /* Write tag */
Miquel Raynal3ca89672018-08-05 18:53:07 +0200381 put_unaligned_be16(tag, recv);
382 recv += sizeof(tag);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200383
384 /* Write length */
Miquel Raynal3ca89672018-08-05 18:53:07 +0200385 put_unaligned_be32(*recv_len, recv);
386 recv += sizeof(u32);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200387
388 /* Write return code */
Miquel Raynal3ca89672018-08-05 18:53:07 +0200389 put_unaligned_be32(rc, recv);
390 recv += sizeof(rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200391
392 /* Add trailing \0 */
Miquel Raynal3ca89672018-08-05 18:53:07 +0200393 *recv = '\0';
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200394
395 return 0;
396}
397
398static int sandbox_tpm2_extend(struct udevice *dev, int pcr_index,
399 const u8 *extension)
400{
401 struct sandbox_tpm2 *tpm = dev_get_priv(dev);
Simon Glass00211022021-07-18 14:18:06 -0600402 sha256_context ctx;
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200403
Simon Glass00211022021-07-18 14:18:06 -0600404 /* Zero the PCR if this is the first use */
405 if (!tpm->pcr_extensions[pcr_index])
406 memset(tpm->pcr[pcr_index], '\0', TPM2_DIGEST_LEN);
407
408 sha256_starts(&ctx);
409 sha256_update(&ctx, tpm->pcr[pcr_index], TPM2_DIGEST_LEN);
410 sha256_update(&ctx, extension, TPM2_DIGEST_LEN);
411 sha256_finish(&ctx, tpm->pcr[pcr_index]);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200412
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200413 tpm->pcr_extensions[pcr_index]++;
414
415 return 0;
416};
417
418static int sandbox_tpm2_xfer(struct udevice *dev, const u8 *sendbuf,
419 size_t send_size, u8 *recvbuf,
420 size_t *recv_len)
421{
422 struct sandbox_tpm2 *tpm = dev_get_priv(dev);
423 enum tpm2_hierarchy hierarchy = 0;
424 const u8 *sent = sendbuf;
425 u8 *recv = recvbuf;
426 u32 length, command, rc = 0;
427 u16 tag, mode, new_pw_sz;
428 u8 yes_no;
429 int i, j;
430
431 /* TPM2_GetProperty */
Eddie Jamesd64202a2023-10-24 10:43:48 -0500432 u32 capability, property, property_count, val;
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200433
434 /* TPM2_PCR_Read/Extend variables */
Miquel Raynal9b1c5502018-08-05 18:53:06 +0200435 int pcr_index = 0;
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200436 u64 pcr_map = 0;
437 u32 selections, pcr_nb;
438 u16 alg;
439 u8 pcr_array_sz;
440
441 tag = get_unaligned_be16(sent);
442 sent += sizeof(tag);
443
444 length = get_unaligned_be32(sent);
445 sent += sizeof(length);
446 if (length != send_size) {
Simon Glass50fbfba2021-02-07 14:27:04 -0700447 printf("TPM2: Unmatching length, received: %zd, expected: %d\n",
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200448 send_size, length);
449 rc = TPM2_RC_SIZE;
Miquel Raynal3ca89672018-08-05 18:53:07 +0200450 sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200451 return 0;
452 }
453
454 command = get_unaligned_be32(sent);
455 sent += sizeof(command);
456 rc = sandbox_tpm2_check_readyness(dev, command);
457 if (rc) {
Miquel Raynal3ca89672018-08-05 18:53:07 +0200458 sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200459 return 0;
460 }
461
462 rc = sandbox_tpm2_check_session(dev, command, tag, &sent, &hierarchy);
463 if (rc) {
Miquel Raynal3ca89672018-08-05 18:53:07 +0200464 sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200465 return 0;
466 }
467
468 switch (command) {
469 case TPM2_CC_STARTUP:
470 mode = get_unaligned_be16(sent);
471 sent += sizeof(mode);
472 switch (mode) {
473 case TPM2_SU_CLEAR:
474 case TPM2_SU_STATE:
475 break;
476 default:
477 rc = TPM2_RC_VALUE;
478 }
479
480 tpm->startup_done = true;
481
Miquel Raynal3ca89672018-08-05 18:53:07 +0200482 sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200483 break;
484
485 case TPM2_CC_SELF_TEST:
486 yes_no = *sent;
487 sent += sizeof(yes_no);
488 switch (yes_no) {
489 case TPMI_YES:
490 case TPMI_NO:
491 break;
492 default:
493 rc = TPM2_RC_VALUE;
494 }
495
496 tpm->tests_done = true;
497
Miquel Raynal3ca89672018-08-05 18:53:07 +0200498 sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200499 break;
500
501 case TPM2_CC_CLEAR:
502 /* Reset this hierarchy password */
503 tpm->pw_sz[hierarchy] = 0;
504
505 /* Reset all password if thisis the PLATFORM hierarchy */
506 if (hierarchy == TPM2_HIERARCHY_PLATFORM)
507 for (i = 0; i < TPM2_HIERARCHY_NB; i++)
508 tpm->pw_sz[i] = 0;
509
510 /* Reset the properties */
511 for (i = 0; i < TPM2_PROPERTY_NB; i++)
512 tpm->properties[i] = 0;
513
514 /* Reset the PCRs and their number of extensions */
515 for (i = 0; i < SANDBOX_TPM_PCR_NB; i++) {
516 tpm->pcr_extensions[i] = 0;
517 for (j = 0; j < TPM2_DIGEST_LEN; j++)
518 tpm->pcr[i][j] = 0;
519 }
520
Miquel Raynal3ca89672018-08-05 18:53:07 +0200521 sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200522 break;
523
524 case TPM2_CC_HIERCHANGEAUTH:
525 new_pw_sz = get_unaligned_be16(sent);
526 sent += sizeof(new_pw_sz);
527 if (new_pw_sz > TPM2_DIGEST_LEN) {
528 rc = TPM2_RC_SIZE;
529 } else if (new_pw_sz) {
530 tpm->pw_sz[hierarchy] = new_pw_sz;
531 memcpy(tpm->pw[hierarchy], sent, new_pw_sz);
532 sent += new_pw_sz;
533 }
534
Miquel Raynal3ca89672018-08-05 18:53:07 +0200535 sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200536 break;
537
538 case TPM2_CC_GET_CAPABILITY:
539 capability = get_unaligned_be32(sent);
540 sent += sizeof(capability);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200541 property = get_unaligned_be32(sent);
542 sent += sizeof(property);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200543 property_count = get_unaligned_be32(sent);
544 sent += sizeof(property_count);
Eddie Jamesd64202a2023-10-24 10:43:48 -0500545
546 switch (capability) {
547 case TPM2_CAP_PCRS:
548 break;
549 case TPM2_CAP_TPM_PROPERTIES:
550 if (!property_count) {
551 rc = TPM2_RC_HANDLE;
552 return sandbox_tpm2_fill_buf(recv, recv_len,
553 tag, rc);
554 }
555
556 if (property >= TPM2_PROPERTIES_OFFSET &&
557 ((property - TPM2_PROPERTIES_OFFSET) +
558 property_count > TPM2_PROPERTY_NB)) {
559 rc = TPM2_RC_HANDLE;
560 return sandbox_tpm2_fill_buf(recv, recv_len,
561 tag, rc);
562 }
563 break;
564 default:
565 printf("Sandbox TPM2 only supports TPM2_CAP_PCRS or "
566 "TPM2_CAP_TPM_PROPERTIES\n");
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200567 rc = TPM2_RC_HANDLE;
Miquel Raynal3ca89672018-08-05 18:53:07 +0200568 return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200569 }
570
571 /* Write tag */
572 put_unaligned_be16(tag, recv);
573 recv += sizeof(tag);
574
575 /* Ignore length for now */
576 recv += sizeof(u32);
577
578 /* Write return code */
579 put_unaligned_be32(rc, recv);
580 recv += sizeof(rc);
581
582 /* Tell there is more data to read */
583 *recv = TPMI_YES;
584 recv += sizeof(yes_no);
585
586 /* Repeat the capability */
587 put_unaligned_be32(capability, recv);
588 recv += sizeof(capability);
589
Eddie Jamesd64202a2023-10-24 10:43:48 -0500590 switch (capability) {
591 case TPM2_CAP_PCRS:
592 /* Give the number of algorithms supported - just SHA256 */
593 put_unaligned_be32(1, recv);
594 recv += sizeof(u32);
595
596 /* Give SHA256 algorithm */
597 put_unaligned_be16(TPM2_ALG_SHA256, recv);
598 recv += sizeof(u16);
599
600 /* Select the PCRs supported */
601 *recv = SANDBOX_TPM_PCR_SELECT_MAX;
602 recv++;
603
604 /* Activate all the PCR bits */
605 for (i = 0; i < SANDBOX_TPM_PCR_SELECT_MAX; ++i) {
606 *recv = 0xff;
607 recv++;
608 }
609 break;
610 case TPM2_CAP_TPM_PROPERTIES:
611 /* Give the number of properties that follow */
612 put_unaligned_be32(property_count, recv);
613 recv += sizeof(property_count);
614
615 /* Fill with the properties */
616 for (i = 0; i < property_count; i++) {
617 put_unaligned_be32(property + i, recv);
618 recv += sizeof(property);
619 if (property >= TPM2_PROPERTIES_OFFSET) {
620 val = tpm->properties[(property -
621 TPM2_PROPERTIES_OFFSET) + i];
622 } else {
623 switch (property) {
624 case TPM2_PT_PCR_COUNT:
625 val = SANDBOX_TPM_PCR_NB;
626 break;
627 default:
628 val = 0xffffffff;
629 break;
630 }
631 }
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200632
Eddie Jamesd64202a2023-10-24 10:43:48 -0500633 put_unaligned_be32(val, recv);
634 recv += sizeof(property);
635 }
636 break;
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200637 }
638
639 /* Add trailing \0 */
640 *recv = '\0';
641
642 /* Write response length */
643 *recv_len = recv - recvbuf;
644 put_unaligned_be32(*recv_len, recvbuf + sizeof(tag));
645
646 break;
647
648 case TPM2_CC_DAM_PARAMETERS:
649 tpm->properties[TPM2_PROP_MAX_TRIES] = get_unaligned_be32(sent);
650 sent += sizeof(*tpm->properties);
651 tpm->properties[TPM2_RECOVERY_TIME] = get_unaligned_be32(sent);
652 sent += sizeof(*tpm->properties);
653 tpm->properties[TPM2_LOCKOUT_RECOVERY] = get_unaligned_be32(sent);
654 sent += sizeof(*tpm->properties);
655
Miquel Raynal3ca89672018-08-05 18:53:07 +0200656 sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200657 break;
658
659 case TPM2_CC_PCR_READ:
660 selections = get_unaligned_be32(sent);
661 sent += sizeof(selections);
662 if (selections != 1) {
663 printf("Sandbox cannot handle more than one PCR\n");
664 rc = TPM2_RC_VALUE;
Miquel Raynal3ca89672018-08-05 18:53:07 +0200665 return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200666 }
667
668 alg = get_unaligned_be16(sent);
669 sent += sizeof(alg);
670 if (alg != TPM2_ALG_SHA256) {
671 printf("Sandbox TPM only handle SHA256 algorithm\n");
672 rc = TPM2_RC_VALUE;
Miquel Raynal3ca89672018-08-05 18:53:07 +0200673 return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200674 }
675
676 pcr_array_sz = *sent;
677 sent += sizeof(pcr_array_sz);
678 if (!pcr_array_sz || pcr_array_sz > 8) {
679 printf("Sandbox TPM cannot handle so much PCRs\n");
680 rc = TPM2_RC_VALUE;
Miquel Raynal3ca89672018-08-05 18:53:07 +0200681 return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200682 }
683
684 for (i = 0; i < pcr_array_sz; i++)
685 pcr_map += (u64)sent[i] << (i * 8);
686
Miquel Raynal9b1c5502018-08-05 18:53:06 +0200687 if (!pcr_map) {
Simon Glass05917ac2021-07-18 14:18:05 -0600688 printf("Empty PCR map\n");
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200689 rc = TPM2_RC_VALUE;
Miquel Raynal3ca89672018-08-05 18:53:07 +0200690 return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200691 }
692
693 for (i = 0; i < SANDBOX_TPM_PCR_NB; i++)
694 if (pcr_map & BIT(i))
695 pcr_index = i;
696
Simon Glass05917ac2021-07-18 14:18:05 -0600697 if (pcr_index >= SANDBOX_TPM_PCR_NB) {
698 printf("Invalid index %d, sandbox TPM handles up to %d PCR(s)\n",
699 pcr_index, SANDBOX_TPM_PCR_NB);
700 rc = TPM2_RC_VALUE;
701 return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
702 }
703
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200704 /* Write tag */
705 put_unaligned_be16(tag, recv);
706 recv += sizeof(tag);
707
708 /* Ignore length for now */
709 recv += sizeof(u32);
710
711 /* Write return code */
712 put_unaligned_be32(rc, recv);
713 recv += sizeof(rc);
714
715 /* Number of extensions */
716 put_unaligned_be32(tpm->pcr_extensions[pcr_index], recv);
717 recv += sizeof(u32);
718
719 /* Copy the PCR */
720 memcpy(recv, tpm->pcr[pcr_index], TPM2_DIGEST_LEN);
721 recv += TPM2_DIGEST_LEN;
722
723 /* Add trailing \0 */
724 *recv = '\0';
725
726 /* Write response length */
727 *recv_len = recv - recvbuf;
728 put_unaligned_be32(*recv_len, recvbuf + sizeof(tag));
729
730 break;
731
732 case TPM2_CC_PCR_EXTEND:
733 /* Get the PCR index */
734 pcr_index = get_unaligned_be32(sendbuf + sizeof(tag) +
735 sizeof(length) +
736 sizeof(command));
Simon Glass05917ac2021-07-18 14:18:05 -0600737 if (pcr_index >= SANDBOX_TPM_PCR_NB) {
738 printf("Invalid index %d, sandbox TPM handles up to %d PCR(s)\n",
739 pcr_index, SANDBOX_TPM_PCR_NB);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200740 rc = TPM2_RC_VALUE;
741 }
742
743 /* Check the number of hashes */
744 pcr_nb = get_unaligned_be32(sent);
745 sent += sizeof(pcr_nb);
746 if (pcr_nb != 1) {
747 printf("Sandbox cannot handle more than one PCR\n");
748 rc = TPM2_RC_VALUE;
Miquel Raynal3ca89672018-08-05 18:53:07 +0200749 return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200750 }
751
752 /* Check the hash algorithm */
753 alg = get_unaligned_be16(sent);
754 sent += sizeof(alg);
755 if (alg != TPM2_ALG_SHA256) {
756 printf("Sandbox TPM only handle SHA256 algorithm\n");
757 rc = TPM2_RC_VALUE;
Miquel Raynal3ca89672018-08-05 18:53:07 +0200758 return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200759 }
760
761 /* Extend the PCR */
762 rc = sandbox_tpm2_extend(dev, pcr_index, sent);
763
Miquel Raynal3ca89672018-08-05 18:53:07 +0200764 sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200765 break;
766
Simon Glassca31f072021-07-18 14:18:03 -0600767 case TPM2_CC_NV_READ: {
768 int index, seq;
769
770 index = get_unaligned_be32(sendbuf + TPM2_HDR_LEN + 4);
771 length = get_unaligned_be16(sent);
772 /* ignore offset */
773 seq = sb_tpm_index_to_seq(index);
774 if (seq < 0)
775 return log_msg_ret("index", -EINVAL);
776 printf("tpm: nvread index=%#02x, len=%#02x, seq=%#02x\n", index,
777 length, seq);
778 *recv_len = TPM2_HDR_LEN + 6 + length;
779 memset(recvbuf, '\0', *recv_len);
780 put_unaligned_be32(length, recvbuf + 2);
781 sb_tpm_read_data(tpm->nvdata, seq, recvbuf,
782 TPM2_HDR_LEN + 4 + 2, length);
783 break;
784 }
785 case TPM2_CC_NV_WRITE: {
786 int index, seq;
787
788 index = get_unaligned_be32(sendbuf + TPM2_HDR_LEN + 4);
789 length = get_unaligned_be16(sent);
790 sent += sizeof(u16);
791
792 /* ignore offset */
793 seq = sb_tpm_index_to_seq(index);
794 if (seq < 0)
795 return log_msg_ret("index", -EINVAL);
796 printf("tpm: nvwrite index=%#02x, len=%#02x, seq=%#02x\n", index,
797 length, seq);
798 memcpy(&tpm->nvdata[seq].data, sent, length);
799 tpm->nvdata[seq].present = true;
800 *recv_len = TPM2_HDR_LEN + 2;
801 memset(recvbuf, '\0', *recv_len);
802 break;
803 }
804 case TPM2_CC_NV_DEFINE_SPACE: {
805 int policy_size, index, seq;
806
807 policy_size = get_unaligned_be16(sent + 12);
808 index = get_unaligned_be32(sent + 2);
809 sent += 14 + policy_size;
810 length = get_unaligned_be16(sent);
811 seq = sb_tpm_index_to_seq(index);
812 if (seq < 0)
813 return -EINVAL;
814 printf("tpm: define_space index=%x, len=%x, seq=%x, policy_size=%x\n",
815 index, length, seq, policy_size);
816 sb_tpm_define_data(tpm->nvdata, seq, length);
817 *recv_len = 12;
818 memset(recvbuf, '\0', *recv_len);
819 break;
820 }
821 case TPM2_CC_NV_WRITELOCK:
822 *recv_len = 12;
823 memset(recvbuf, '\0', *recv_len);
824 break;
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200825 default:
826 printf("TPM2 command %02x unknown in Sandbox\n", command);
827 rc = TPM2_RC_COMMAND_CODE;
Miquel Raynal3ca89672018-08-05 18:53:07 +0200828 sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200829 }
830
831 return 0;
832}
833
834static int sandbox_tpm2_get_desc(struct udevice *dev, char *buf, int size)
835{
836 if (size < 15)
837 return -ENOSPC;
838
839 return snprintf(buf, size, "Sandbox TPM2.x");
840}
841
Simon Glass11a075c2022-08-30 21:05:36 -0600842static int sandbox_tpm2_report_state(struct udevice *dev, char *buf, int size)
843{
844 struct sandbox_tpm2 *priv = dev_get_priv(dev);
845
846 if (size < 40)
847 return -ENOSPC;
848
849 return snprintf(buf, size, "init_done=%d", priv->init_done);
850}
851
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200852static int sandbox_tpm2_open(struct udevice *dev)
853{
854 struct sandbox_tpm2 *tpm = dev_get_priv(dev);
855
856 if (tpm->init_done)
Ilias Apalodimas3c9d75f2023-02-18 17:18:49 +0200857 return -EBUSY;
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200858
859 tpm->init_done = true;
860
861 return 0;
862}
863
864static int sandbox_tpm2_probe(struct udevice *dev)
865{
866 struct sandbox_tpm2 *tpm = dev_get_priv(dev);
867 struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
868
Miquel Raynal79920d12018-07-19 22:35:09 +0200869 /* Use the TPM v2 stack */
870 priv->version = TPM_V2;
871
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200872 priv->pcr_count = 32;
873 priv->pcr_select_min = 2;
874
Simon Glasse39503c2021-07-18 14:18:02 -0600875 if (s_state.valid)
876 memcpy(tpm, &s_state, sizeof(*tpm));
877 g_state = tpm;
878
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200879 return 0;
880}
881
882static int sandbox_tpm2_close(struct udevice *dev)
883{
884 return 0;
885}
886
887static const struct tpm_ops sandbox_tpm2_ops = {
888 .open = sandbox_tpm2_open,
889 .close = sandbox_tpm2_close,
890 .get_desc = sandbox_tpm2_get_desc,
Simon Glass11a075c2022-08-30 21:05:36 -0600891 .report_state = sandbox_tpm2_report_state,
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200892 .xfer = sandbox_tpm2_xfer,
893};
894
895static const struct udevice_id sandbox_tpm2_ids[] = {
896 { .compatible = "sandbox,tpm2" },
897 { }
898};
899
900U_BOOT_DRIVER(sandbox_tpm2) = {
901 .name = "sandbox_tpm2",
902 .id = UCLASS_TPM,
903 .of_match = sandbox_tpm2_ids,
904 .ops = &sandbox_tpm2_ops,
905 .probe = sandbox_tpm2_probe,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700906 .priv_auto = sizeof(struct sandbox_tpm2),
Miquel Raynalcf4bfea2018-05-15 11:57:25 +0200907};