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