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