xref: /OK3568_Linux_fs/kernel/drivers/char/tpm/tpm2-space.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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