1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2016 Intel Corporation
6 * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
8 * Maintained by: <tpmdd-devel@lists.sourceforge.net>
10 * This file contains TPM2 protocol implementations of the commands
11 * used by the kernel internally.
14 #include <linux/gfp.h>
15 #include <asm/unaligned.h>
18 enum tpm2_handle_types
{
19 TPM2_HT_HMAC_SESSION
= 0x02000000,
20 TPM2_HT_POLICY_SESSION
= 0x03000000,
21 TPM2_HT_TRANSIENT
= 0x80000000,
31 static void tpm2_flush_sessions(struct tpm_chip
*chip
, struct tpm_space
*space
)
35 for (i
= 0; i
< ARRAY_SIZE(space
->session_tbl
); i
++) {
36 if (space
->session_tbl
[i
])
37 tpm2_flush_context(chip
, space
->session_tbl
[i
]);
41 int tpm2_init_space(struct tpm_space
*space
, unsigned int buf_size
)
43 space
->context_buf
= kzalloc(buf_size
, GFP_KERNEL
);
44 if (!space
->context_buf
)
47 space
->session_buf
= kzalloc(buf_size
, GFP_KERNEL
);
48 if (space
->session_buf
== NULL
) {
49 kfree(space
->context_buf
);
50 /* Prevent caller getting a dangling pointer. */
51 space
->context_buf
= NULL
;
55 space
->buf_size
= buf_size
;
59 void tpm2_del_space(struct tpm_chip
*chip
, struct tpm_space
*space
)
61 mutex_lock(&chip
->tpm_mutex
);
62 if (!tpm_chip_start(chip
)) {
63 tpm2_flush_sessions(chip
, space
);
66 mutex_unlock(&chip
->tpm_mutex
);
67 kfree(space
->context_buf
);
68 kfree(space
->session_buf
);
71 static int tpm2_load_context(struct tpm_chip
*chip
, u8
*buf
,
72 unsigned int *offset
, u32
*handle
)
75 struct tpm2_context
*ctx
;
76 unsigned int body_size
;
79 rc
= tpm_buf_init(&tbuf
, TPM2_ST_NO_SESSIONS
, TPM2_CC_CONTEXT_LOAD
);
83 ctx
= (struct tpm2_context
*)&buf
[*offset
];
84 body_size
= sizeof(*ctx
) + be16_to_cpu(ctx
->blob_size
);
85 tpm_buf_append(&tbuf
, &buf
[*offset
], body_size
);
87 rc
= tpm_transmit_cmd(chip
, &tbuf
, 4, NULL
);
89 dev_warn(&chip
->dev
, "%s: failed with a system error %d\n",
91 tpm_buf_destroy(&tbuf
);
93 } else if (tpm2_rc_value(rc
) == TPM2_RC_HANDLE
||
94 rc
== TPM2_RC_REFERENCE_H0
) {
96 * TPM_RC_HANDLE means that the session context can't
97 * be loaded because of an internal counter mismatch
98 * that makes the TPM think there might have been a
99 * replay. This might happen if the context was saved
100 * and loaded outside the space.
102 * TPM_RC_REFERENCE_H0 means the session has been
103 * flushed outside the space
106 tpm_buf_destroy(&tbuf
);
109 dev_warn(&chip
->dev
, "%s: failed with a TPM error 0x%04X\n",
111 tpm_buf_destroy(&tbuf
);
115 *handle
= be32_to_cpup((__be32
*)&tbuf
.data
[TPM_HEADER_SIZE
]);
116 *offset
+= body_size
;
118 tpm_buf_destroy(&tbuf
);
122 static int tpm2_save_context(struct tpm_chip
*chip
, u32 handle
, u8
*buf
,
123 unsigned int buf_size
, unsigned int *offset
)
126 unsigned int body_size
;
129 rc
= tpm_buf_init(&tbuf
, TPM2_ST_NO_SESSIONS
, TPM2_CC_CONTEXT_SAVE
);
133 tpm_buf_append_u32(&tbuf
, handle
);
135 rc
= tpm_transmit_cmd(chip
, &tbuf
, 0, NULL
);
137 dev_warn(&chip
->dev
, "%s: failed with a system error %d\n",
139 tpm_buf_destroy(&tbuf
);
141 } else if (tpm2_rc_value(rc
) == TPM2_RC_REFERENCE_H0
) {
142 tpm_buf_destroy(&tbuf
);
145 dev_warn(&chip
->dev
, "%s: failed with a TPM error 0x%04X\n",
147 tpm_buf_destroy(&tbuf
);
151 body_size
= tpm_buf_length(&tbuf
) - TPM_HEADER_SIZE
;
152 if ((*offset
+ body_size
) > buf_size
) {
153 dev_warn(&chip
->dev
, "%s: out of backing storage\n", __func__
);
154 tpm_buf_destroy(&tbuf
);
158 memcpy(&buf
[*offset
], &tbuf
.data
[TPM_HEADER_SIZE
], body_size
);
159 *offset
+= body_size
;
160 tpm_buf_destroy(&tbuf
);
164 void tpm2_flush_space(struct tpm_chip
*chip
)
166 struct tpm_space
*space
= &chip
->work_space
;
169 for (i
= 0; i
< ARRAY_SIZE(space
->context_tbl
); i
++)
170 if (space
->context_tbl
[i
] && ~space
->context_tbl
[i
])
171 tpm2_flush_context(chip
, space
->context_tbl
[i
]);
173 tpm2_flush_sessions(chip
, space
);
176 static int tpm2_load_space(struct tpm_chip
*chip
)
178 struct tpm_space
*space
= &chip
->work_space
;
183 for (i
= 0, offset
= 0; i
< ARRAY_SIZE(space
->context_tbl
); i
++) {
184 if (!space
->context_tbl
[i
])
187 /* sanity check, should never happen */
188 if (~space
->context_tbl
[i
]) {
189 dev_err(&chip
->dev
, "context table is inconsistent");
193 rc
= tpm2_load_context(chip
, space
->context_buf
, &offset
,
194 &space
->context_tbl
[i
]);
199 for (i
= 0, offset
= 0; i
< ARRAY_SIZE(space
->session_tbl
); i
++) {
202 if (!space
->session_tbl
[i
])
205 rc
= tpm2_load_context(chip
, space
->session_buf
,
208 /* load failed, just forget session */
209 space
->session_tbl
[i
] = 0;
211 tpm2_flush_space(chip
);
214 if (handle
!= space
->session_tbl
[i
]) {
215 dev_warn(&chip
->dev
, "session restored to wrong handle\n");
216 tpm2_flush_space(chip
);
224 static bool tpm2_map_to_phandle(struct tpm_space
*space
, void *handle
)
226 u32 vhandle
= be32_to_cpup((__be32
*)handle
);
230 i
= 0xFFFFFF - (vhandle
& 0xFFFFFF);
231 if (i
>= ARRAY_SIZE(space
->context_tbl
) || !space
->context_tbl
[i
])
234 phandle
= space
->context_tbl
[i
];
235 *((__be32
*)handle
) = cpu_to_be32(phandle
);
239 static int tpm2_map_command(struct tpm_chip
*chip
, u32 cc
, u8
*cmd
)
241 struct tpm_space
*space
= &chip
->work_space
;
242 unsigned int nr_handles
;
247 i
= tpm2_find_cc(chip
, cc
);
251 attrs
= chip
->cc_attrs_tbl
[i
];
252 nr_handles
= (attrs
>> TPM2_CC_ATTR_CHANDLES
) & GENMASK(2, 0);
254 handle
= (__be32
*)&cmd
[TPM_HEADER_SIZE
];
255 for (i
= 0; i
< nr_handles
; i
++, handle
++) {
256 if ((be32_to_cpu(*handle
) & 0xFF000000) == TPM2_HT_TRANSIENT
) {
257 if (!tpm2_map_to_phandle(space
, handle
))
265 static int tpm_find_and_validate_cc(struct tpm_chip
*chip
,
266 struct tpm_space
*space
,
267 const void *cmd
, size_t len
)
269 const struct tpm_header
*header
= (const void *)cmd
;
273 unsigned int nr_handles
;
275 if (len
< TPM_HEADER_SIZE
|| !chip
->nr_commands
)
278 cc
= be32_to_cpu(header
->ordinal
);
280 i
= tpm2_find_cc(chip
, cc
);
282 dev_dbg(&chip
->dev
, "0x%04X is an invalid command\n",
287 attrs
= chip
->cc_attrs_tbl
[i
];
289 4 * ((attrs
>> TPM2_CC_ATTR_CHANDLES
) & GENMASK(2, 0));
290 if (len
< TPM_HEADER_SIZE
+ 4 * nr_handles
)
295 dev_dbg(&chip
->dev
, "%s: insufficient command length %zu", __func__
,
300 int tpm2_prepare_space(struct tpm_chip
*chip
, struct tpm_space
*space
, u8
*cmd
,
309 cc
= tpm_find_and_validate_cc(chip
, space
, cmd
, cmdsiz
);
313 memcpy(&chip
->work_space
.context_tbl
, &space
->context_tbl
,
314 sizeof(space
->context_tbl
));
315 memcpy(&chip
->work_space
.session_tbl
, &space
->session_tbl
,
316 sizeof(space
->session_tbl
));
317 memcpy(chip
->work_space
.context_buf
, space
->context_buf
,
319 memcpy(chip
->work_space
.session_buf
, space
->session_buf
,
322 rc
= tpm2_load_space(chip
);
324 tpm2_flush_space(chip
);
328 rc
= tpm2_map_command(chip
, cc
, cmd
);
330 tpm2_flush_space(chip
);
338 static bool tpm2_add_session(struct tpm_chip
*chip
, u32 handle
)
340 struct tpm_space
*space
= &chip
->work_space
;
343 for (i
= 0; i
< ARRAY_SIZE(space
->session_tbl
); i
++)
344 if (space
->session_tbl
[i
] == 0)
347 if (i
== ARRAY_SIZE(space
->session_tbl
))
350 space
->session_tbl
[i
] = handle
;
354 static u32
tpm2_map_to_vhandle(struct tpm_space
*space
, u32 phandle
, bool alloc
)
358 for (i
= 0; i
< ARRAY_SIZE(space
->context_tbl
); i
++) {
360 if (!space
->context_tbl
[i
]) {
361 space
->context_tbl
[i
] = phandle
;
364 } else if (space
->context_tbl
[i
] == phandle
)
368 if (i
== ARRAY_SIZE(space
->context_tbl
))
371 return TPM2_HT_TRANSIENT
| (0xFFFFFF - i
);
374 static int tpm2_map_response_header(struct tpm_chip
*chip
, u32 cc
, u8
*rsp
,
377 struct tpm_space
*space
= &chip
->work_space
;
378 struct tpm_header
*header
= (struct tpm_header
*)rsp
;
385 if (be32_to_cpu(header
->return_code
) != TPM2_RC_SUCCESS
)
388 i
= tpm2_find_cc(chip
, cc
);
389 /* sanity check, should never happen */
393 attrs
= chip
->cc_attrs_tbl
[i
];
394 if (!((attrs
>> TPM2_CC_ATTR_RHANDLE
) & 1))
397 phandle
= be32_to_cpup((__be32
*)&rsp
[TPM_HEADER_SIZE
]);
398 phandle_type
= phandle
& 0xFF000000;
400 switch (phandle_type
) {
401 case TPM2_HT_TRANSIENT
:
402 vhandle
= tpm2_map_to_vhandle(space
, phandle
, true);
406 *(__be32
*)&rsp
[TPM_HEADER_SIZE
] = cpu_to_be32(vhandle
);
408 case TPM2_HT_HMAC_SESSION
:
409 case TPM2_HT_POLICY_SESSION
:
410 if (!tpm2_add_session(chip
, phandle
))
414 dev_err(&chip
->dev
, "%s: unknown handle 0x%08X\n",
421 tpm2_flush_context(chip
, phandle
);
422 dev_warn(&chip
->dev
, "%s: out of slots for 0x%08X\n", __func__
,
427 struct tpm2_cap_handles
{
434 static int tpm2_map_response_body(struct tpm_chip
*chip
, u32 cc
, u8
*rsp
,
437 struct tpm_space
*space
= &chip
->work_space
;
438 struct tpm_header
*header
= (struct tpm_header
*)rsp
;
439 struct tpm2_cap_handles
*data
;
446 if (cc
!= TPM2_CC_GET_CAPABILITY
||
447 be32_to_cpu(header
->return_code
) != TPM2_RC_SUCCESS
) {
451 if (len
< TPM_HEADER_SIZE
+ 9)
454 data
= (void *)&rsp
[TPM_HEADER_SIZE
];
455 if (be32_to_cpu(data
->capability
) != TPM2_CAP_HANDLES
)
458 if (len
!= TPM_HEADER_SIZE
+ 9 + 4 * be32_to_cpu(data
->count
))
461 for (i
= 0, j
= 0; i
< be32_to_cpu(data
->count
); i
++) {
462 phandle
= be32_to_cpup((__be32
*)&data
->handles
[i
]);
463 phandle_type
= phandle
& 0xFF000000;
465 switch (phandle_type
) {
466 case TPM2_HT_TRANSIENT
:
467 vhandle
= tpm2_map_to_vhandle(space
, phandle
, false);
471 data
->handles
[j
] = cpu_to_be32(vhandle
);
476 data
->handles
[j
] = cpu_to_be32(phandle
);
483 header
->length
= cpu_to_be32(TPM_HEADER_SIZE
+ 9 + 4 * j
);
484 data
->count
= cpu_to_be32(j
);
488 static int tpm2_save_space(struct tpm_chip
*chip
)
490 struct tpm_space
*space
= &chip
->work_space
;
495 for (i
= 0, offset
= 0; i
< ARRAY_SIZE(space
->context_tbl
); i
++) {
496 if (!(space
->context_tbl
[i
] && ~space
->context_tbl
[i
]))
499 rc
= tpm2_save_context(chip
, space
->context_tbl
[i
],
500 space
->context_buf
, space
->buf_size
,
503 space
->context_tbl
[i
] = 0;
508 tpm2_flush_context(chip
, space
->context_tbl
[i
]);
509 space
->context_tbl
[i
] = ~0;
512 for (i
= 0, offset
= 0; i
< ARRAY_SIZE(space
->session_tbl
); i
++) {
513 if (!space
->session_tbl
[i
])
516 rc
= tpm2_save_context(chip
, space
->session_tbl
[i
],
517 space
->session_buf
, space
->buf_size
,
520 /* handle error saving session, just forget it */
521 space
->session_tbl
[i
] = 0;
523 tpm2_flush_space(chip
);
531 int tpm2_commit_space(struct tpm_chip
*chip
, struct tpm_space
*space
,
532 void *buf
, size_t *bufsiz
)
534 struct tpm_header
*header
= buf
;
540 rc
= tpm2_map_response_header(chip
, chip
->last_cc
, buf
, *bufsiz
);
542 tpm2_flush_space(chip
);
546 rc
= tpm2_map_response_body(chip
, chip
->last_cc
, buf
, *bufsiz
);
548 tpm2_flush_space(chip
);
552 rc
= tpm2_save_space(chip
);
554 tpm2_flush_space(chip
);
558 *bufsiz
= be32_to_cpu(header
->length
);
560 memcpy(&space
->context_tbl
, &chip
->work_space
.context_tbl
,
561 sizeof(space
->context_tbl
));
562 memcpy(&space
->session_tbl
, &chip
->work_space
.session_tbl
,
563 sizeof(space
->session_tbl
));
564 memcpy(space
->context_buf
, chip
->work_space
.context_buf
,
566 memcpy(space
->session_buf
, chip
->work_space
.session_buf
,
571 dev_err(&chip
->dev
, "%s: error %d\n", __func__
, rc
);