]>
git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - drivers/char/tpm/tpm2-space.c
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
)
43 space
->context_buf
= kzalloc(PAGE_SIZE
, GFP_KERNEL
);
44 if (!space
->context_buf
)
47 space
->session_buf
= kzalloc(PAGE_SIZE
, GFP_KERNEL
);
48 if (space
->session_buf
== NULL
) {
49 kfree(space
->context_buf
);
56 void tpm2_del_space(struct tpm_chip
*chip
, struct tpm_space
*space
)
58 mutex_lock(&chip
->tpm_mutex
);
59 if (!tpm_chip_start(chip
)) {
60 tpm2_flush_sessions(chip
, space
);
63 mutex_unlock(&chip
->tpm_mutex
);
64 kfree(space
->context_buf
);
65 kfree(space
->session_buf
);
68 static int tpm2_load_context(struct tpm_chip
*chip
, u8
*buf
,
69 unsigned int *offset
, u32
*handle
)
72 struct tpm2_context
*ctx
;
73 unsigned int body_size
;
76 rc
= tpm_buf_init(&tbuf
, TPM2_ST_NO_SESSIONS
, TPM2_CC_CONTEXT_LOAD
);
80 ctx
= (struct tpm2_context
*)&buf
[*offset
];
81 body_size
= sizeof(*ctx
) + be16_to_cpu(ctx
->blob_size
);
82 tpm_buf_append(&tbuf
, &buf
[*offset
], body_size
);
84 rc
= tpm_transmit_cmd(chip
, &tbuf
, 4, NULL
);
86 dev_warn(&chip
->dev
, "%s: failed with a system error %d\n",
88 tpm_buf_destroy(&tbuf
);
90 } else if (tpm2_rc_value(rc
) == TPM2_RC_HANDLE
||
91 rc
== TPM2_RC_REFERENCE_H0
) {
93 * TPM_RC_HANDLE means that the session context can't
94 * be loaded because of an internal counter mismatch
95 * that makes the TPM think there might have been a
96 * replay. This might happen if the context was saved
97 * and loaded outside the space.
99 * TPM_RC_REFERENCE_H0 means the session has been
100 * flushed outside the space
103 tpm_buf_destroy(&tbuf
);
106 dev_warn(&chip
->dev
, "%s: failed with a TPM error 0x%04X\n",
108 tpm_buf_destroy(&tbuf
);
112 *handle
= be32_to_cpup((__be32
*)&tbuf
.data
[TPM_HEADER_SIZE
]);
113 *offset
+= body_size
;
115 tpm_buf_destroy(&tbuf
);
119 static int tpm2_save_context(struct tpm_chip
*chip
, u32 handle
, u8
*buf
,
120 unsigned int buf_size
, unsigned int *offset
)
123 unsigned int body_size
;
126 rc
= tpm_buf_init(&tbuf
, TPM2_ST_NO_SESSIONS
, TPM2_CC_CONTEXT_SAVE
);
130 tpm_buf_append_u32(&tbuf
, handle
);
132 rc
= tpm_transmit_cmd(chip
, &tbuf
, 0, NULL
);
134 dev_warn(&chip
->dev
, "%s: failed with a system error %d\n",
136 tpm_buf_destroy(&tbuf
);
138 } else if (tpm2_rc_value(rc
) == TPM2_RC_REFERENCE_H0
) {
139 tpm_buf_destroy(&tbuf
);
142 dev_warn(&chip
->dev
, "%s: failed with a TPM error 0x%04X\n",
144 tpm_buf_destroy(&tbuf
);
148 body_size
= tpm_buf_length(&tbuf
) - TPM_HEADER_SIZE
;
149 if ((*offset
+ body_size
) > buf_size
) {
150 dev_warn(&chip
->dev
, "%s: out of backing storage\n", __func__
);
151 tpm_buf_destroy(&tbuf
);
155 memcpy(&buf
[*offset
], &tbuf
.data
[TPM_HEADER_SIZE
], body_size
);
156 *offset
+= body_size
;
157 tpm_buf_destroy(&tbuf
);
161 void tpm2_flush_space(struct tpm_chip
*chip
)
163 struct tpm_space
*space
= &chip
->work_space
;
166 for (i
= 0; i
< ARRAY_SIZE(space
->context_tbl
); i
++)
167 if (space
->context_tbl
[i
] && ~space
->context_tbl
[i
])
168 tpm2_flush_context(chip
, space
->context_tbl
[i
]);
170 tpm2_flush_sessions(chip
, space
);
173 static int tpm2_load_space(struct tpm_chip
*chip
)
175 struct tpm_space
*space
= &chip
->work_space
;
180 for (i
= 0, offset
= 0; i
< ARRAY_SIZE(space
->context_tbl
); i
++) {
181 if (!space
->context_tbl
[i
])
184 /* sanity check, should never happen */
185 if (~space
->context_tbl
[i
]) {
186 dev_err(&chip
->dev
, "context table is inconsistent");
190 rc
= tpm2_load_context(chip
, space
->context_buf
, &offset
,
191 &space
->context_tbl
[i
]);
196 for (i
= 0, offset
= 0; i
< ARRAY_SIZE(space
->session_tbl
); i
++) {
199 if (!space
->session_tbl
[i
])
202 rc
= tpm2_load_context(chip
, space
->session_buf
,
205 /* load failed, just forget session */
206 space
->session_tbl
[i
] = 0;
208 tpm2_flush_space(chip
);
211 if (handle
!= space
->session_tbl
[i
]) {
212 dev_warn(&chip
->dev
, "session restored to wrong handle\n");
213 tpm2_flush_space(chip
);
221 static bool tpm2_map_to_phandle(struct tpm_space
*space
, void *handle
)
223 u32 vhandle
= be32_to_cpup((__be32
*)handle
);
227 i
= 0xFFFFFF - (vhandle
& 0xFFFFFF);
228 if (i
>= ARRAY_SIZE(space
->context_tbl
) || !space
->context_tbl
[i
])
231 phandle
= space
->context_tbl
[i
];
232 *((__be32
*)handle
) = cpu_to_be32(phandle
);
236 static int tpm2_map_command(struct tpm_chip
*chip
, u32 cc
, u8
*cmd
)
238 struct tpm_space
*space
= &chip
->work_space
;
239 unsigned int nr_handles
;
244 i
= tpm2_find_cc(chip
, cc
);
248 attrs
= chip
->cc_attrs_tbl
[i
];
249 nr_handles
= (attrs
>> TPM2_CC_ATTR_CHANDLES
) & GENMASK(2, 0);
251 handle
= (__be32
*)&cmd
[TPM_HEADER_SIZE
];
252 for (i
= 0; i
< nr_handles
; i
++, handle
++) {
253 if ((be32_to_cpu(*handle
) & 0xFF000000) == TPM2_HT_TRANSIENT
) {
254 if (!tpm2_map_to_phandle(space
, handle
))
262 static int tpm_find_and_validate_cc(struct tpm_chip
*chip
,
263 struct tpm_space
*space
,
264 const void *cmd
, size_t len
)
266 const struct tpm_header
*header
= (const void *)cmd
;
270 unsigned int nr_handles
;
272 if (len
< TPM_HEADER_SIZE
|| !chip
->nr_commands
)
275 cc
= be32_to_cpu(header
->ordinal
);
277 i
= tpm2_find_cc(chip
, cc
);
279 dev_dbg(&chip
->dev
, "0x%04X is an invalid command\n",
284 attrs
= chip
->cc_attrs_tbl
[i
];
286 4 * ((attrs
>> TPM2_CC_ATTR_CHANDLES
) & GENMASK(2, 0));
287 if (len
< TPM_HEADER_SIZE
+ 4 * nr_handles
)
292 dev_dbg(&chip
->dev
, "%s: insufficient command length %zu", __func__
,
297 int tpm2_prepare_space(struct tpm_chip
*chip
, struct tpm_space
*space
, u8
*cmd
,
306 cc
= tpm_find_and_validate_cc(chip
, space
, cmd
, cmdsiz
);
310 memcpy(&chip
->work_space
.context_tbl
, &space
->context_tbl
,
311 sizeof(space
->context_tbl
));
312 memcpy(&chip
->work_space
.session_tbl
, &space
->session_tbl
,
313 sizeof(space
->session_tbl
));
314 memcpy(chip
->work_space
.context_buf
, space
->context_buf
, PAGE_SIZE
);
315 memcpy(chip
->work_space
.session_buf
, space
->session_buf
, PAGE_SIZE
);
317 rc
= tpm2_load_space(chip
);
319 tpm2_flush_space(chip
);
323 rc
= tpm2_map_command(chip
, cc
, cmd
);
325 tpm2_flush_space(chip
);
333 static bool tpm2_add_session(struct tpm_chip
*chip
, u32 handle
)
335 struct tpm_space
*space
= &chip
->work_space
;
338 for (i
= 0; i
< ARRAY_SIZE(space
->session_tbl
); i
++)
339 if (space
->session_tbl
[i
] == 0)
342 if (i
== ARRAY_SIZE(space
->session_tbl
))
345 space
->session_tbl
[i
] = handle
;
349 static u32
tpm2_map_to_vhandle(struct tpm_space
*space
, u32 phandle
, bool alloc
)
353 for (i
= 0; i
< ARRAY_SIZE(space
->context_tbl
); i
++) {
355 if (!space
->context_tbl
[i
]) {
356 space
->context_tbl
[i
] = phandle
;
359 } else if (space
->context_tbl
[i
] == phandle
)
363 if (i
== ARRAY_SIZE(space
->context_tbl
))
366 return TPM2_HT_TRANSIENT
| (0xFFFFFF - i
);
369 static int tpm2_map_response_header(struct tpm_chip
*chip
, u32 cc
, u8
*rsp
,
372 struct tpm_space
*space
= &chip
->work_space
;
373 struct tpm_header
*header
= (struct tpm_header
*)rsp
;
380 if (be32_to_cpu(header
->return_code
) != TPM2_RC_SUCCESS
)
383 i
= tpm2_find_cc(chip
, cc
);
384 /* sanity check, should never happen */
388 attrs
= chip
->cc_attrs_tbl
[i
];
389 if (!((attrs
>> TPM2_CC_ATTR_RHANDLE
) & 1))
392 phandle
= be32_to_cpup((__be32
*)&rsp
[TPM_HEADER_SIZE
]);
393 phandle_type
= phandle
& 0xFF000000;
395 switch (phandle_type
) {
396 case TPM2_HT_TRANSIENT
:
397 vhandle
= tpm2_map_to_vhandle(space
, phandle
, true);
401 *(__be32
*)&rsp
[TPM_HEADER_SIZE
] = cpu_to_be32(vhandle
);
403 case TPM2_HT_HMAC_SESSION
:
404 case TPM2_HT_POLICY_SESSION
:
405 if (!tpm2_add_session(chip
, phandle
))
409 dev_err(&chip
->dev
, "%s: unknown handle 0x%08X\n",
416 tpm2_flush_context(chip
, phandle
);
417 dev_warn(&chip
->dev
, "%s: out of slots for 0x%08X\n", __func__
,
422 struct tpm2_cap_handles
{
429 static int tpm2_map_response_body(struct tpm_chip
*chip
, u32 cc
, u8
*rsp
,
432 struct tpm_space
*space
= &chip
->work_space
;
433 struct tpm_header
*header
= (struct tpm_header
*)rsp
;
434 struct tpm2_cap_handles
*data
;
441 if (cc
!= TPM2_CC_GET_CAPABILITY
||
442 be32_to_cpu(header
->return_code
) != TPM2_RC_SUCCESS
) {
446 if (len
< TPM_HEADER_SIZE
+ 9)
449 data
= (void *)&rsp
[TPM_HEADER_SIZE
];
450 if (be32_to_cpu(data
->capability
) != TPM2_CAP_HANDLES
)
453 if (len
!= TPM_HEADER_SIZE
+ 9 + 4 * be32_to_cpu(data
->count
))
456 for (i
= 0, j
= 0; i
< be32_to_cpu(data
->count
); i
++) {
457 phandle
= be32_to_cpup((__be32
*)&data
->handles
[i
]);
458 phandle_type
= phandle
& 0xFF000000;
460 switch (phandle_type
) {
461 case TPM2_HT_TRANSIENT
:
462 vhandle
= tpm2_map_to_vhandle(space
, phandle
, false);
466 data
->handles
[j
] = cpu_to_be32(vhandle
);
471 data
->handles
[j
] = cpu_to_be32(phandle
);
478 header
->length
= cpu_to_be32(TPM_HEADER_SIZE
+ 9 + 4 * j
);
479 data
->count
= cpu_to_be32(j
);
483 static int tpm2_save_space(struct tpm_chip
*chip
)
485 struct tpm_space
*space
= &chip
->work_space
;
490 for (i
= 0, offset
= 0; i
< ARRAY_SIZE(space
->context_tbl
); i
++) {
491 if (!(space
->context_tbl
[i
] && ~space
->context_tbl
[i
]))
494 rc
= tpm2_save_context(chip
, space
->context_tbl
[i
],
495 space
->context_buf
, PAGE_SIZE
,
498 space
->context_tbl
[i
] = 0;
503 tpm2_flush_context(chip
, space
->context_tbl
[i
]);
504 space
->context_tbl
[i
] = ~0;
507 for (i
= 0, offset
= 0; i
< ARRAY_SIZE(space
->session_tbl
); i
++) {
508 if (!space
->session_tbl
[i
])
511 rc
= tpm2_save_context(chip
, space
->session_tbl
[i
],
512 space
->session_buf
, PAGE_SIZE
,
516 /* handle error saving session, just forget it */
517 space
->session_tbl
[i
] = 0;
519 tpm2_flush_space(chip
);
527 int tpm2_commit_space(struct tpm_chip
*chip
, struct tpm_space
*space
,
528 void *buf
, size_t *bufsiz
)
530 struct tpm_header
*header
= buf
;
536 rc
= tpm2_map_response_header(chip
, chip
->last_cc
, buf
, *bufsiz
);
538 tpm2_flush_space(chip
);
542 rc
= tpm2_map_response_body(chip
, chip
->last_cc
, buf
, *bufsiz
);
544 tpm2_flush_space(chip
);
548 rc
= tpm2_save_space(chip
);
550 tpm2_flush_space(chip
);
554 *bufsiz
= be32_to_cpu(header
->length
);
556 memcpy(&space
->context_tbl
, &chip
->work_space
.context_tbl
,
557 sizeof(space
->context_tbl
));
558 memcpy(&space
->session_tbl
, &chip
->work_space
.session_tbl
,
559 sizeof(space
->session_tbl
));
560 memcpy(space
->context_buf
, chip
->work_space
.context_buf
, PAGE_SIZE
);
561 memcpy(space
->session_buf
, chip
->work_space
.session_buf
, PAGE_SIZE
);
565 dev_err(&chip
->dev
, "%s: error %d\n", __func__
, rc
);