Linux Audio

Check our new training course

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