1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2016 Intel Corporation
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Authors:
6*4882a593Smuzhiyun * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Maintained by: <tpmdd-devel@lists.sourceforge.net>
9*4882a593Smuzhiyun *
10*4882a593Smuzhiyun * This file contains TPM2 protocol implementations of the commands
11*4882a593Smuzhiyun * used by the kernel internally.
12*4882a593Smuzhiyun */
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include <linux/gfp.h>
15*4882a593Smuzhiyun #include <asm/unaligned.h>
16*4882a593Smuzhiyun #include "tpm.h"
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun enum tpm2_handle_types {
19*4882a593Smuzhiyun TPM2_HT_HMAC_SESSION = 0x02000000,
20*4882a593Smuzhiyun TPM2_HT_POLICY_SESSION = 0x03000000,
21*4882a593Smuzhiyun TPM2_HT_TRANSIENT = 0x80000000,
22*4882a593Smuzhiyun };
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun struct tpm2_context {
25*4882a593Smuzhiyun __be64 sequence;
26*4882a593Smuzhiyun __be32 saved_handle;
27*4882a593Smuzhiyun __be32 hierarchy;
28*4882a593Smuzhiyun __be16 blob_size;
29*4882a593Smuzhiyun } __packed;
30*4882a593Smuzhiyun
tpm2_flush_sessions(struct tpm_chip * chip,struct tpm_space * space)31*4882a593Smuzhiyun static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space)
32*4882a593Smuzhiyun {
33*4882a593Smuzhiyun int i;
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
36*4882a593Smuzhiyun if (space->session_tbl[i])
37*4882a593Smuzhiyun tpm2_flush_context(chip, space->session_tbl[i]);
38*4882a593Smuzhiyun }
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun
tpm2_init_space(struct tpm_space * space,unsigned int buf_size)41*4882a593Smuzhiyun int tpm2_init_space(struct tpm_space *space, unsigned int buf_size)
42*4882a593Smuzhiyun {
43*4882a593Smuzhiyun space->context_buf = kzalloc(buf_size, GFP_KERNEL);
44*4882a593Smuzhiyun if (!space->context_buf)
45*4882a593Smuzhiyun return -ENOMEM;
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun space->session_buf = kzalloc(buf_size, GFP_KERNEL);
48*4882a593Smuzhiyun if (space->session_buf == NULL) {
49*4882a593Smuzhiyun kfree(space->context_buf);
50*4882a593Smuzhiyun /* Prevent caller getting a dangling pointer. */
51*4882a593Smuzhiyun space->context_buf = NULL;
52*4882a593Smuzhiyun return -ENOMEM;
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun space->buf_size = buf_size;
56*4882a593Smuzhiyun return 0;
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun
tpm2_del_space(struct tpm_chip * chip,struct tpm_space * space)59*4882a593Smuzhiyun void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun if (tpm_try_get_ops(chip) == 0) {
63*4882a593Smuzhiyun tpm2_flush_sessions(chip, space);
64*4882a593Smuzhiyun tpm_put_ops(chip);
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun kfree(space->context_buf);
68*4882a593Smuzhiyun kfree(space->session_buf);
69*4882a593Smuzhiyun }
70*4882a593Smuzhiyun
tpm2_load_context(struct tpm_chip * chip,u8 * buf,unsigned int * offset,u32 * handle)71*4882a593Smuzhiyun static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
72*4882a593Smuzhiyun unsigned int *offset, u32 *handle)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun struct tpm_buf tbuf;
75*4882a593Smuzhiyun struct tpm2_context *ctx;
76*4882a593Smuzhiyun unsigned int body_size;
77*4882a593Smuzhiyun int rc;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
80*4882a593Smuzhiyun if (rc)
81*4882a593Smuzhiyun return rc;
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun ctx = (struct tpm2_context *)&buf[*offset];
84*4882a593Smuzhiyun body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
85*4882a593Smuzhiyun tpm_buf_append(&tbuf, &buf[*offset], body_size);
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL);
88*4882a593Smuzhiyun if (rc < 0) {
89*4882a593Smuzhiyun dev_warn(&chip->dev, "%s: failed with a system error %d\n",
90*4882a593Smuzhiyun __func__, rc);
91*4882a593Smuzhiyun tpm_buf_destroy(&tbuf);
92*4882a593Smuzhiyun return -EFAULT;
93*4882a593Smuzhiyun } else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE ||
94*4882a593Smuzhiyun rc == TPM2_RC_REFERENCE_H0) {
95*4882a593Smuzhiyun /*
96*4882a593Smuzhiyun * TPM_RC_HANDLE means that the session context can't
97*4882a593Smuzhiyun * be loaded because of an internal counter mismatch
98*4882a593Smuzhiyun * that makes the TPM think there might have been a
99*4882a593Smuzhiyun * replay. This might happen if the context was saved
100*4882a593Smuzhiyun * and loaded outside the space.
101*4882a593Smuzhiyun *
102*4882a593Smuzhiyun * TPM_RC_REFERENCE_H0 means the session has been
103*4882a593Smuzhiyun * flushed outside the space
104*4882a593Smuzhiyun */
105*4882a593Smuzhiyun *handle = 0;
106*4882a593Smuzhiyun tpm_buf_destroy(&tbuf);
107*4882a593Smuzhiyun return -ENOENT;
108*4882a593Smuzhiyun } else if (rc > 0) {
109*4882a593Smuzhiyun dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
110*4882a593Smuzhiyun __func__, rc);
111*4882a593Smuzhiyun tpm_buf_destroy(&tbuf);
112*4882a593Smuzhiyun return -EFAULT;
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun *handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);
116*4882a593Smuzhiyun *offset += body_size;
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun tpm_buf_destroy(&tbuf);
119*4882a593Smuzhiyun return 0;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
tpm2_save_context(struct tpm_chip * chip,u32 handle,u8 * buf,unsigned int buf_size,unsigned int * offset)122*4882a593Smuzhiyun static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
123*4882a593Smuzhiyun unsigned int buf_size, unsigned int *offset)
124*4882a593Smuzhiyun {
125*4882a593Smuzhiyun struct tpm_buf tbuf;
126*4882a593Smuzhiyun unsigned int body_size;
127*4882a593Smuzhiyun int rc;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
130*4882a593Smuzhiyun if (rc)
131*4882a593Smuzhiyun return rc;
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun tpm_buf_append_u32(&tbuf, handle);
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun rc = tpm_transmit_cmd(chip, &tbuf, 0, NULL);
136*4882a593Smuzhiyun if (rc < 0) {
137*4882a593Smuzhiyun dev_warn(&chip->dev, "%s: failed with a system error %d\n",
138*4882a593Smuzhiyun __func__, rc);
139*4882a593Smuzhiyun tpm_buf_destroy(&tbuf);
140*4882a593Smuzhiyun return -EFAULT;
141*4882a593Smuzhiyun } else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {
142*4882a593Smuzhiyun tpm_buf_destroy(&tbuf);
143*4882a593Smuzhiyun return -ENOENT;
144*4882a593Smuzhiyun } else if (rc) {
145*4882a593Smuzhiyun dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
146*4882a593Smuzhiyun __func__, rc);
147*4882a593Smuzhiyun tpm_buf_destroy(&tbuf);
148*4882a593Smuzhiyun return -EFAULT;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;
152*4882a593Smuzhiyun if ((*offset + body_size) > buf_size) {
153*4882a593Smuzhiyun dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);
154*4882a593Smuzhiyun tpm_buf_destroy(&tbuf);
155*4882a593Smuzhiyun return -ENOMEM;
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);
159*4882a593Smuzhiyun *offset += body_size;
160*4882a593Smuzhiyun tpm_buf_destroy(&tbuf);
161*4882a593Smuzhiyun return 0;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun
tpm2_flush_space(struct tpm_chip * chip)164*4882a593Smuzhiyun void tpm2_flush_space(struct tpm_chip *chip)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun struct tpm_space *space = &chip->work_space;
167*4882a593Smuzhiyun int i;
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++)
170*4882a593Smuzhiyun if (space->context_tbl[i] && ~space->context_tbl[i])
171*4882a593Smuzhiyun tpm2_flush_context(chip, space->context_tbl[i]);
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun tpm2_flush_sessions(chip, space);
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun
tpm2_load_space(struct tpm_chip * chip)176*4882a593Smuzhiyun static int tpm2_load_space(struct tpm_chip *chip)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun struct tpm_space *space = &chip->work_space;
179*4882a593Smuzhiyun unsigned int offset;
180*4882a593Smuzhiyun int i;
181*4882a593Smuzhiyun int rc;
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
184*4882a593Smuzhiyun if (!space->context_tbl[i])
185*4882a593Smuzhiyun continue;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun /* sanity check, should never happen */
188*4882a593Smuzhiyun if (~space->context_tbl[i]) {
189*4882a593Smuzhiyun dev_err(&chip->dev, "context table is inconsistent");
190*4882a593Smuzhiyun return -EFAULT;
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun rc = tpm2_load_context(chip, space->context_buf, &offset,
194*4882a593Smuzhiyun &space->context_tbl[i]);
195*4882a593Smuzhiyun if (rc)
196*4882a593Smuzhiyun return rc;
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
200*4882a593Smuzhiyun u32 handle;
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun if (!space->session_tbl[i])
203*4882a593Smuzhiyun continue;
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun rc = tpm2_load_context(chip, space->session_buf,
206*4882a593Smuzhiyun &offset, &handle);
207*4882a593Smuzhiyun if (rc == -ENOENT) {
208*4882a593Smuzhiyun /* load failed, just forget session */
209*4882a593Smuzhiyun space->session_tbl[i] = 0;
210*4882a593Smuzhiyun } else if (rc) {
211*4882a593Smuzhiyun tpm2_flush_space(chip);
212*4882a593Smuzhiyun return rc;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun if (handle != space->session_tbl[i]) {
215*4882a593Smuzhiyun dev_warn(&chip->dev, "session restored to wrong handle\n");
216*4882a593Smuzhiyun tpm2_flush_space(chip);
217*4882a593Smuzhiyun return -EFAULT;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun return 0;
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun
tpm2_map_to_phandle(struct tpm_space * space,void * handle)224*4882a593Smuzhiyun static bool tpm2_map_to_phandle(struct tpm_space *space, void *handle)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun u32 vhandle = be32_to_cpup((__be32 *)handle);
227*4882a593Smuzhiyun u32 phandle;
228*4882a593Smuzhiyun int i;
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun i = 0xFFFFFF - (vhandle & 0xFFFFFF);
231*4882a593Smuzhiyun if (i >= ARRAY_SIZE(space->context_tbl) || !space->context_tbl[i])
232*4882a593Smuzhiyun return false;
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun phandle = space->context_tbl[i];
235*4882a593Smuzhiyun *((__be32 *)handle) = cpu_to_be32(phandle);
236*4882a593Smuzhiyun return true;
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun
tpm2_map_command(struct tpm_chip * chip,u32 cc,u8 * cmd)239*4882a593Smuzhiyun static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd)
240*4882a593Smuzhiyun {
241*4882a593Smuzhiyun struct tpm_space *space = &chip->work_space;
242*4882a593Smuzhiyun unsigned int nr_handles;
243*4882a593Smuzhiyun u32 attrs;
244*4882a593Smuzhiyun __be32 *handle;
245*4882a593Smuzhiyun int i;
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun i = tpm2_find_cc(chip, cc);
248*4882a593Smuzhiyun if (i < 0)
249*4882a593Smuzhiyun return -EINVAL;
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun attrs = chip->cc_attrs_tbl[i];
252*4882a593Smuzhiyun nr_handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun handle = (__be32 *)&cmd[TPM_HEADER_SIZE];
255*4882a593Smuzhiyun for (i = 0; i < nr_handles; i++, handle++) {
256*4882a593Smuzhiyun if ((be32_to_cpu(*handle) & 0xFF000000) == TPM2_HT_TRANSIENT) {
257*4882a593Smuzhiyun if (!tpm2_map_to_phandle(space, handle))
258*4882a593Smuzhiyun return -EINVAL;
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun return 0;
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun
tpm_find_and_validate_cc(struct tpm_chip * chip,struct tpm_space * space,const void * cmd,size_t len)265*4882a593Smuzhiyun static int tpm_find_and_validate_cc(struct tpm_chip *chip,
266*4882a593Smuzhiyun struct tpm_space *space,
267*4882a593Smuzhiyun const void *cmd, size_t len)
268*4882a593Smuzhiyun {
269*4882a593Smuzhiyun const struct tpm_header *header = (const void *)cmd;
270*4882a593Smuzhiyun int i;
271*4882a593Smuzhiyun u32 cc;
272*4882a593Smuzhiyun u32 attrs;
273*4882a593Smuzhiyun unsigned int nr_handles;
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun if (len < TPM_HEADER_SIZE || !chip->nr_commands)
276*4882a593Smuzhiyun return -EINVAL;
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun cc = be32_to_cpu(header->ordinal);
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun i = tpm2_find_cc(chip, cc);
281*4882a593Smuzhiyun if (i < 0) {
282*4882a593Smuzhiyun dev_dbg(&chip->dev, "0x%04X is an invalid command\n",
283*4882a593Smuzhiyun cc);
284*4882a593Smuzhiyun return -EOPNOTSUPP;
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun attrs = chip->cc_attrs_tbl[i];
288*4882a593Smuzhiyun nr_handles =
289*4882a593Smuzhiyun 4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0));
290*4882a593Smuzhiyun if (len < TPM_HEADER_SIZE + 4 * nr_handles)
291*4882a593Smuzhiyun goto err_len;
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun return cc;
294*4882a593Smuzhiyun err_len:
295*4882a593Smuzhiyun dev_dbg(&chip->dev, "%s: insufficient command length %zu", __func__,
296*4882a593Smuzhiyun len);
297*4882a593Smuzhiyun return -EINVAL;
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun
tpm2_prepare_space(struct tpm_chip * chip,struct tpm_space * space,u8 * cmd,size_t cmdsiz)300*4882a593Smuzhiyun int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u8 *cmd,
301*4882a593Smuzhiyun size_t cmdsiz)
302*4882a593Smuzhiyun {
303*4882a593Smuzhiyun int rc;
304*4882a593Smuzhiyun int cc;
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun if (!space)
307*4882a593Smuzhiyun return 0;
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun cc = tpm_find_and_validate_cc(chip, space, cmd, cmdsiz);
310*4882a593Smuzhiyun if (cc < 0)
311*4882a593Smuzhiyun return cc;
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun memcpy(&chip->work_space.context_tbl, &space->context_tbl,
314*4882a593Smuzhiyun sizeof(space->context_tbl));
315*4882a593Smuzhiyun memcpy(&chip->work_space.session_tbl, &space->session_tbl,
316*4882a593Smuzhiyun sizeof(space->session_tbl));
317*4882a593Smuzhiyun memcpy(chip->work_space.context_buf, space->context_buf,
318*4882a593Smuzhiyun space->buf_size);
319*4882a593Smuzhiyun memcpy(chip->work_space.session_buf, space->session_buf,
320*4882a593Smuzhiyun space->buf_size);
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun rc = tpm2_load_space(chip);
323*4882a593Smuzhiyun if (rc) {
324*4882a593Smuzhiyun tpm2_flush_space(chip);
325*4882a593Smuzhiyun return rc;
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun rc = tpm2_map_command(chip, cc, cmd);
329*4882a593Smuzhiyun if (rc) {
330*4882a593Smuzhiyun tpm2_flush_space(chip);
331*4882a593Smuzhiyun return rc;
332*4882a593Smuzhiyun }
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun chip->last_cc = cc;
335*4882a593Smuzhiyun return 0;
336*4882a593Smuzhiyun }
337*4882a593Smuzhiyun
tpm2_add_session(struct tpm_chip * chip,u32 handle)338*4882a593Smuzhiyun static bool tpm2_add_session(struct tpm_chip *chip, u32 handle)
339*4882a593Smuzhiyun {
340*4882a593Smuzhiyun struct tpm_space *space = &chip->work_space;
341*4882a593Smuzhiyun int i;
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++)
344*4882a593Smuzhiyun if (space->session_tbl[i] == 0)
345*4882a593Smuzhiyun break;
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun if (i == ARRAY_SIZE(space->session_tbl))
348*4882a593Smuzhiyun return false;
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun space->session_tbl[i] = handle;
351*4882a593Smuzhiyun return true;
352*4882a593Smuzhiyun }
353*4882a593Smuzhiyun
tpm2_map_to_vhandle(struct tpm_space * space,u32 phandle,bool alloc)354*4882a593Smuzhiyun static u32 tpm2_map_to_vhandle(struct tpm_space *space, u32 phandle, bool alloc)
355*4882a593Smuzhiyun {
356*4882a593Smuzhiyun int i;
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
359*4882a593Smuzhiyun if (alloc) {
360*4882a593Smuzhiyun if (!space->context_tbl[i]) {
361*4882a593Smuzhiyun space->context_tbl[i] = phandle;
362*4882a593Smuzhiyun break;
363*4882a593Smuzhiyun }
364*4882a593Smuzhiyun } else if (space->context_tbl[i] == phandle)
365*4882a593Smuzhiyun break;
366*4882a593Smuzhiyun }
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun if (i == ARRAY_SIZE(space->context_tbl))
369*4882a593Smuzhiyun return 0;
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun return TPM2_HT_TRANSIENT | (0xFFFFFF - i);
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun
tpm2_map_response_header(struct tpm_chip * chip,u32 cc,u8 * rsp,size_t len)374*4882a593Smuzhiyun static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp,
375*4882a593Smuzhiyun size_t len)
376*4882a593Smuzhiyun {
377*4882a593Smuzhiyun struct tpm_space *space = &chip->work_space;
378*4882a593Smuzhiyun struct tpm_header *header = (struct tpm_header *)rsp;
379*4882a593Smuzhiyun u32 phandle;
380*4882a593Smuzhiyun u32 phandle_type;
381*4882a593Smuzhiyun u32 vhandle;
382*4882a593Smuzhiyun u32 attrs;
383*4882a593Smuzhiyun int i;
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun if (be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS)
386*4882a593Smuzhiyun return 0;
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun i = tpm2_find_cc(chip, cc);
389*4882a593Smuzhiyun /* sanity check, should never happen */
390*4882a593Smuzhiyun if (i < 0)
391*4882a593Smuzhiyun return -EFAULT;
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun attrs = chip->cc_attrs_tbl[i];
394*4882a593Smuzhiyun if (!((attrs >> TPM2_CC_ATTR_RHANDLE) & 1))
395*4882a593Smuzhiyun return 0;
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun phandle = be32_to_cpup((__be32 *)&rsp[TPM_HEADER_SIZE]);
398*4882a593Smuzhiyun phandle_type = phandle & 0xFF000000;
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun switch (phandle_type) {
401*4882a593Smuzhiyun case TPM2_HT_TRANSIENT:
402*4882a593Smuzhiyun vhandle = tpm2_map_to_vhandle(space, phandle, true);
403*4882a593Smuzhiyun if (!vhandle)
404*4882a593Smuzhiyun goto out_no_slots;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun *(__be32 *)&rsp[TPM_HEADER_SIZE] = cpu_to_be32(vhandle);
407*4882a593Smuzhiyun break;
408*4882a593Smuzhiyun case TPM2_HT_HMAC_SESSION:
409*4882a593Smuzhiyun case TPM2_HT_POLICY_SESSION:
410*4882a593Smuzhiyun if (!tpm2_add_session(chip, phandle))
411*4882a593Smuzhiyun goto out_no_slots;
412*4882a593Smuzhiyun break;
413*4882a593Smuzhiyun default:
414*4882a593Smuzhiyun dev_err(&chip->dev, "%s: unknown handle 0x%08X\n",
415*4882a593Smuzhiyun __func__, phandle);
416*4882a593Smuzhiyun break;
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun return 0;
420*4882a593Smuzhiyun out_no_slots:
421*4882a593Smuzhiyun tpm2_flush_context(chip, phandle);
422*4882a593Smuzhiyun dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__,
423*4882a593Smuzhiyun phandle);
424*4882a593Smuzhiyun return -ENOMEM;
425*4882a593Smuzhiyun }
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun struct tpm2_cap_handles {
428*4882a593Smuzhiyun u8 more_data;
429*4882a593Smuzhiyun __be32 capability;
430*4882a593Smuzhiyun __be32 count;
431*4882a593Smuzhiyun __be32 handles[];
432*4882a593Smuzhiyun } __packed;
433*4882a593Smuzhiyun
tpm2_map_response_body(struct tpm_chip * chip,u32 cc,u8 * rsp,size_t len)434*4882a593Smuzhiyun static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp,
435*4882a593Smuzhiyun size_t len)
436*4882a593Smuzhiyun {
437*4882a593Smuzhiyun struct tpm_space *space = &chip->work_space;
438*4882a593Smuzhiyun struct tpm_header *header = (struct tpm_header *)rsp;
439*4882a593Smuzhiyun struct tpm2_cap_handles *data;
440*4882a593Smuzhiyun u32 phandle;
441*4882a593Smuzhiyun u32 phandle_type;
442*4882a593Smuzhiyun u32 vhandle;
443*4882a593Smuzhiyun int i;
444*4882a593Smuzhiyun int j;
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun if (cc != TPM2_CC_GET_CAPABILITY ||
447*4882a593Smuzhiyun be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS) {
448*4882a593Smuzhiyun return 0;
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun if (len < TPM_HEADER_SIZE + 9)
452*4882a593Smuzhiyun return -EFAULT;
453*4882a593Smuzhiyun
454*4882a593Smuzhiyun data = (void *)&rsp[TPM_HEADER_SIZE];
455*4882a593Smuzhiyun if (be32_to_cpu(data->capability) != TPM2_CAP_HANDLES)
456*4882a593Smuzhiyun return 0;
457*4882a593Smuzhiyun
458*4882a593Smuzhiyun if (be32_to_cpu(data->count) > (UINT_MAX - TPM_HEADER_SIZE - 9) / 4)
459*4882a593Smuzhiyun return -EFAULT;
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun if (len != TPM_HEADER_SIZE + 9 + 4 * be32_to_cpu(data->count))
462*4882a593Smuzhiyun return -EFAULT;
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun for (i = 0, j = 0; i < be32_to_cpu(data->count); i++) {
465*4882a593Smuzhiyun phandle = be32_to_cpup((__be32 *)&data->handles[i]);
466*4882a593Smuzhiyun phandle_type = phandle & 0xFF000000;
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun switch (phandle_type) {
469*4882a593Smuzhiyun case TPM2_HT_TRANSIENT:
470*4882a593Smuzhiyun vhandle = tpm2_map_to_vhandle(space, phandle, false);
471*4882a593Smuzhiyun if (!vhandle)
472*4882a593Smuzhiyun break;
473*4882a593Smuzhiyun
474*4882a593Smuzhiyun data->handles[j] = cpu_to_be32(vhandle);
475*4882a593Smuzhiyun j++;
476*4882a593Smuzhiyun break;
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun default:
479*4882a593Smuzhiyun data->handles[j] = cpu_to_be32(phandle);
480*4882a593Smuzhiyun j++;
481*4882a593Smuzhiyun break;
482*4882a593Smuzhiyun }
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun }
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun header->length = cpu_to_be32(TPM_HEADER_SIZE + 9 + 4 * j);
487*4882a593Smuzhiyun data->count = cpu_to_be32(j);
488*4882a593Smuzhiyun return 0;
489*4882a593Smuzhiyun }
490*4882a593Smuzhiyun
tpm2_save_space(struct tpm_chip * chip)491*4882a593Smuzhiyun static int tpm2_save_space(struct tpm_chip *chip)
492*4882a593Smuzhiyun {
493*4882a593Smuzhiyun struct tpm_space *space = &chip->work_space;
494*4882a593Smuzhiyun unsigned int offset;
495*4882a593Smuzhiyun int i;
496*4882a593Smuzhiyun int rc;
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
499*4882a593Smuzhiyun if (!(space->context_tbl[i] && ~space->context_tbl[i]))
500*4882a593Smuzhiyun continue;
501*4882a593Smuzhiyun
502*4882a593Smuzhiyun rc = tpm2_save_context(chip, space->context_tbl[i],
503*4882a593Smuzhiyun space->context_buf, space->buf_size,
504*4882a593Smuzhiyun &offset);
505*4882a593Smuzhiyun if (rc == -ENOENT) {
506*4882a593Smuzhiyun space->context_tbl[i] = 0;
507*4882a593Smuzhiyun continue;
508*4882a593Smuzhiyun } else if (rc)
509*4882a593Smuzhiyun return rc;
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun tpm2_flush_context(chip, space->context_tbl[i]);
512*4882a593Smuzhiyun space->context_tbl[i] = ~0;
513*4882a593Smuzhiyun }
514*4882a593Smuzhiyun
515*4882a593Smuzhiyun for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
516*4882a593Smuzhiyun if (!space->session_tbl[i])
517*4882a593Smuzhiyun continue;
518*4882a593Smuzhiyun
519*4882a593Smuzhiyun rc = tpm2_save_context(chip, space->session_tbl[i],
520*4882a593Smuzhiyun space->session_buf, space->buf_size,
521*4882a593Smuzhiyun &offset);
522*4882a593Smuzhiyun if (rc == -ENOENT) {
523*4882a593Smuzhiyun /* handle error saving session, just forget it */
524*4882a593Smuzhiyun space->session_tbl[i] = 0;
525*4882a593Smuzhiyun } else if (rc < 0) {
526*4882a593Smuzhiyun tpm2_flush_space(chip);
527*4882a593Smuzhiyun return rc;
528*4882a593Smuzhiyun }
529*4882a593Smuzhiyun }
530*4882a593Smuzhiyun
531*4882a593Smuzhiyun return 0;
532*4882a593Smuzhiyun }
533*4882a593Smuzhiyun
tpm2_commit_space(struct tpm_chip * chip,struct tpm_space * space,void * buf,size_t * bufsiz)534*4882a593Smuzhiyun int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
535*4882a593Smuzhiyun void *buf, size_t *bufsiz)
536*4882a593Smuzhiyun {
537*4882a593Smuzhiyun struct tpm_header *header = buf;
538*4882a593Smuzhiyun int rc;
539*4882a593Smuzhiyun
540*4882a593Smuzhiyun if (!space)
541*4882a593Smuzhiyun return 0;
542*4882a593Smuzhiyun
543*4882a593Smuzhiyun rc = tpm2_map_response_header(chip, chip->last_cc, buf, *bufsiz);
544*4882a593Smuzhiyun if (rc) {
545*4882a593Smuzhiyun tpm2_flush_space(chip);
546*4882a593Smuzhiyun goto out;
547*4882a593Smuzhiyun }
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun rc = tpm2_map_response_body(chip, chip->last_cc, buf, *bufsiz);
550*4882a593Smuzhiyun if (rc) {
551*4882a593Smuzhiyun tpm2_flush_space(chip);
552*4882a593Smuzhiyun goto out;
553*4882a593Smuzhiyun }
554*4882a593Smuzhiyun
555*4882a593Smuzhiyun rc = tpm2_save_space(chip);
556*4882a593Smuzhiyun if (rc) {
557*4882a593Smuzhiyun tpm2_flush_space(chip);
558*4882a593Smuzhiyun goto out;
559*4882a593Smuzhiyun }
560*4882a593Smuzhiyun
561*4882a593Smuzhiyun *bufsiz = be32_to_cpu(header->length);
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun memcpy(&space->context_tbl, &chip->work_space.context_tbl,
564*4882a593Smuzhiyun sizeof(space->context_tbl));
565*4882a593Smuzhiyun memcpy(&space->session_tbl, &chip->work_space.session_tbl,
566*4882a593Smuzhiyun sizeof(space->session_tbl));
567*4882a593Smuzhiyun memcpy(space->context_buf, chip->work_space.context_buf,
568*4882a593Smuzhiyun space->buf_size);
569*4882a593Smuzhiyun memcpy(space->session_buf, chip->work_space.session_buf,
570*4882a593Smuzhiyun space->buf_size);
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun return 0;
573*4882a593Smuzhiyun out:
574*4882a593Smuzhiyun dev_err(&chip->dev, "%s: error %d\n", __func__, rc);
575*4882a593Smuzhiyun return rc;
576*4882a593Smuzhiyun }
577*4882a593Smuzhiyun
578*4882a593Smuzhiyun /*
579*4882a593Smuzhiyun * Put the reference to the main device.
580*4882a593Smuzhiyun */
tpm_devs_release(struct device * dev)581*4882a593Smuzhiyun static void tpm_devs_release(struct device *dev)
582*4882a593Smuzhiyun {
583*4882a593Smuzhiyun struct tpm_chip *chip = container_of(dev, struct tpm_chip, devs);
584*4882a593Smuzhiyun
585*4882a593Smuzhiyun /* release the master device reference */
586*4882a593Smuzhiyun put_device(&chip->dev);
587*4882a593Smuzhiyun }
588*4882a593Smuzhiyun
589*4882a593Smuzhiyun /*
590*4882a593Smuzhiyun * Remove the device file for exposed TPM spaces and release the device
591*4882a593Smuzhiyun * reference. This may also release the reference to the master device.
592*4882a593Smuzhiyun */
tpm_devs_remove(struct tpm_chip * chip)593*4882a593Smuzhiyun void tpm_devs_remove(struct tpm_chip *chip)
594*4882a593Smuzhiyun {
595*4882a593Smuzhiyun cdev_device_del(&chip->cdevs, &chip->devs);
596*4882a593Smuzhiyun put_device(&chip->devs);
597*4882a593Smuzhiyun }
598*4882a593Smuzhiyun
599*4882a593Smuzhiyun /*
600*4882a593Smuzhiyun * Add a device file to expose TPM spaces. Also take a reference to the
601*4882a593Smuzhiyun * main device.
602*4882a593Smuzhiyun */
tpm_devs_add(struct tpm_chip * chip)603*4882a593Smuzhiyun int tpm_devs_add(struct tpm_chip *chip)
604*4882a593Smuzhiyun {
605*4882a593Smuzhiyun int rc;
606*4882a593Smuzhiyun
607*4882a593Smuzhiyun device_initialize(&chip->devs);
608*4882a593Smuzhiyun chip->devs.parent = chip->dev.parent;
609*4882a593Smuzhiyun chip->devs.class = tpmrm_class;
610*4882a593Smuzhiyun
611*4882a593Smuzhiyun /*
612*4882a593Smuzhiyun * Get extra reference on main device to hold on behalf of devs.
613*4882a593Smuzhiyun * This holds the chip structure while cdevs is in use. The
614*4882a593Smuzhiyun * corresponding put is in the tpm_devs_release.
615*4882a593Smuzhiyun */
616*4882a593Smuzhiyun get_device(&chip->dev);
617*4882a593Smuzhiyun chip->devs.release = tpm_devs_release;
618*4882a593Smuzhiyun chip->devs.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num + TPM_NUM_DEVICES);
619*4882a593Smuzhiyun cdev_init(&chip->cdevs, &tpmrm_fops);
620*4882a593Smuzhiyun chip->cdevs.owner = THIS_MODULE;
621*4882a593Smuzhiyun
622*4882a593Smuzhiyun rc = dev_set_name(&chip->devs, "tpmrm%d", chip->dev_num);
623*4882a593Smuzhiyun if (rc)
624*4882a593Smuzhiyun goto err_put_devs;
625*4882a593Smuzhiyun
626*4882a593Smuzhiyun rc = cdev_device_add(&chip->cdevs, &chip->devs);
627*4882a593Smuzhiyun if (rc) {
628*4882a593Smuzhiyun dev_err(&chip->devs,
629*4882a593Smuzhiyun "unable to cdev_device_add() %s, major %d, minor %d, err=%d\n",
630*4882a593Smuzhiyun dev_name(&chip->devs), MAJOR(chip->devs.devt),
631*4882a593Smuzhiyun MINOR(chip->devs.devt), rc);
632*4882a593Smuzhiyun goto err_put_devs;
633*4882a593Smuzhiyun }
634*4882a593Smuzhiyun
635*4882a593Smuzhiyun return 0;
636*4882a593Smuzhiyun
637*4882a593Smuzhiyun err_put_devs:
638*4882a593Smuzhiyun put_device(&chip->devs);
639*4882a593Smuzhiyun
640*4882a593Smuzhiyun return rc;
641*4882a593Smuzhiyun }
642