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}
v4.17
 
  1/*
  2 * Copyright (C) 2016 Intel Corporation
  3 *
  4 * Authors:
  5 * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
  6 *
  7 * Maintained by: <tpmdd-devel@lists.sourceforge.net>
  8 *
  9 * This file contains TPM2 protocol implementations of the commands
 10 * used by the kernel internally.
 11 *
 12 * This program is free software; you can redistribute it and/or
 13 * modify it under the terms of the GNU General Public License
 14 * as published by the Free Software Foundation; version 2
 15 * of the License.
 16 */
 17
 18#include <linux/gfp.h>
 19#include <asm/unaligned.h>
 20#include "tpm.h"
 21
 22enum tpm2_handle_types {
 23	TPM2_HT_HMAC_SESSION	= 0x02000000,
 24	TPM2_HT_POLICY_SESSION	= 0x03000000,
 25	TPM2_HT_TRANSIENT	= 0x80000000,
 26};
 27
 28struct tpm2_context {
 29	__be64 sequence;
 30	__be32 saved_handle;
 31	__be32 hierarchy;
 32	__be16 blob_size;
 33} __packed;
 34
 35static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space)
 36{
 37	int i;
 38
 39	for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
 40		if (space->session_tbl[i])
 41			tpm2_flush_context_cmd(chip, space->session_tbl[i],
 42					       TPM_TRANSMIT_UNLOCKED);
 43	}
 44}
 45
 46int tpm2_init_space(struct tpm_space *space)
 47{
 48	space->context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
 49	if (!space->context_buf)
 50		return -ENOMEM;
 51
 52	space->session_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
 53	if (space->session_buf == NULL) {
 54		kfree(space->context_buf);
 
 
 55		return -ENOMEM;
 56	}
 57
 
 58	return 0;
 59}
 60
 61void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
 62{
 63	mutex_lock(&chip->tpm_mutex);
 64	tpm2_flush_sessions(chip, space);
 65	mutex_unlock(&chip->tpm_mutex);
 
 
 
 66	kfree(space->context_buf);
 67	kfree(space->session_buf);
 68}
 69
 70static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
 71			     unsigned int *offset, u32 *handle)
 72{
 73	struct tpm_buf tbuf;
 74	struct tpm2_context *ctx;
 75	unsigned int body_size;
 76	int rc;
 77
 78	rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
 79	if (rc)
 80		return rc;
 81
 82	ctx = (struct tpm2_context *)&buf[*offset];
 83	body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
 84	tpm_buf_append(&tbuf, &buf[*offset], body_size);
 85
 86	rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 4,
 87			      TPM_TRANSMIT_UNLOCKED, 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		rc = -ENOENT;
106		tpm_buf_destroy(&tbuf);
 
107	} else if (rc > 0) {
108		dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
109			 __func__, rc);
110		tpm_buf_destroy(&tbuf);
111		return -EFAULT;
112	}
113
114	*handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);
115	*offset += body_size;
116
117	tpm_buf_destroy(&tbuf);
118	return 0;
119}
120
121static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
122			     unsigned int buf_size, unsigned int *offset)
123{
124	struct tpm_buf tbuf;
125	unsigned int body_size;
126	int rc;
127
128	rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
129	if (rc)
130		return rc;
131
132	tpm_buf_append_u32(&tbuf, handle);
133
134	rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 0,
135			      TPM_TRANSMIT_UNLOCKED, 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
164static void 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_cmd(chip, space->context_tbl[i],
172					       TPM_TRANSMIT_UNLOCKED);
173
174	tpm2_flush_sessions(chip, space);
175}
176
177static int tpm2_load_space(struct tpm_chip *chip)
178{
179	struct tpm_space *space = &chip->work_space;
180	unsigned int offset;
181	int i;
182	int rc;
183
184	for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
185		if (!space->context_tbl[i])
186			continue;
187
188		/* sanity check, should never happen */
189		if (~space->context_tbl[i]) {
190			dev_err(&chip->dev, "context table is inconsistent");
191			return -EFAULT;
192		}
193
194		rc = tpm2_load_context(chip, space->context_buf, &offset,
195				       &space->context_tbl[i]);
196		if (rc)
197			return rc;
198	}
199
200	for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
201		u32 handle;
202
203		if (!space->session_tbl[i])
204			continue;
205
206		rc = tpm2_load_context(chip, space->session_buf,
207				       &offset, &handle);
208		if (rc == -ENOENT) {
209			/* load failed, just forget session */
210			space->session_tbl[i] = 0;
211		} else if (rc) {
212			tpm2_flush_space(chip);
213			return rc;
214		}
215		if (handle != space->session_tbl[i]) {
216			dev_warn(&chip->dev, "session restored to wrong handle\n");
217			tpm2_flush_space(chip);
218			return -EFAULT;
219		}
220	}
221
222	return 0;
223}
224
225static bool tpm2_map_to_phandle(struct tpm_space *space, void *handle)
226{
227	u32 vhandle = be32_to_cpup((__be32 *)handle);
228	u32 phandle;
229	int i;
230
231	i = 0xFFFFFF - (vhandle & 0xFFFFFF);
232	if (i >= ARRAY_SIZE(space->context_tbl) || !space->context_tbl[i])
233		return false;
234
235	phandle = space->context_tbl[i];
236	*((__be32 *)handle) = cpu_to_be32(phandle);
237	return true;
238}
239
240static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd)
241{
242	struct tpm_space *space = &chip->work_space;
243	unsigned int nr_handles;
244	u32 attrs;
245	__be32 *handle;
246	int i;
247
248	i = tpm2_find_cc(chip, cc);
249	if (i < 0)
250		return -EINVAL;
251
252	attrs = chip->cc_attrs_tbl[i];
253	nr_handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
254
255	handle = (__be32 *)&cmd[TPM_HEADER_SIZE];
256	for (i = 0; i < nr_handles; i++, handle++) {
257		if ((be32_to_cpu(*handle) & 0xFF000000) == TPM2_HT_TRANSIENT) {
258			if (!tpm2_map_to_phandle(space, handle))
259				return -EINVAL;
260		}
261	}
262
263	return 0;
264}
265
266int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc,
267		       u8 *cmd)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268{
269	int rc;
 
270
271	if (!space)
272		return 0;
273
 
 
 
 
274	memcpy(&chip->work_space.context_tbl, &space->context_tbl,
275	       sizeof(space->context_tbl));
276	memcpy(&chip->work_space.session_tbl, &space->session_tbl,
277	       sizeof(space->session_tbl));
278	memcpy(chip->work_space.context_buf, space->context_buf, PAGE_SIZE);
279	memcpy(chip->work_space.session_buf, space->session_buf, PAGE_SIZE);
 
 
280
281	rc = tpm2_load_space(chip);
282	if (rc) {
283		tpm2_flush_space(chip);
284		return rc;
285	}
286
287	rc = tpm2_map_command(chip, cc, cmd);
288	if (rc) {
289		tpm2_flush_space(chip);
290		return rc;
291	}
292
 
293	return 0;
294}
295
296static bool tpm2_add_session(struct tpm_chip *chip, u32 handle)
297{
298	struct tpm_space *space = &chip->work_space;
299	int i;
300
301	for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++)
302		if (space->session_tbl[i] == 0)
303			break;
304
305	if (i == ARRAY_SIZE(space->session_tbl))
306		return false;
307
308	space->session_tbl[i] = handle;
309	return true;
310}
311
312static u32 tpm2_map_to_vhandle(struct tpm_space *space, u32 phandle, bool alloc)
313{
314	int i;
315
316	for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
317		if (alloc) {
318			if (!space->context_tbl[i]) {
319				space->context_tbl[i] = phandle;
320				break;
321			}
322		} else if (space->context_tbl[i] == phandle)
323			break;
324	}
325
326	if (i == ARRAY_SIZE(space->context_tbl))
327		return 0;
328
329	return TPM2_HT_TRANSIENT | (0xFFFFFF - i);
330}
331
332static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp,
333				    size_t len)
334{
335	struct tpm_space *space = &chip->work_space;
336	struct tpm_output_header *header = (void *)rsp;
337	u32 phandle;
338	u32 phandle_type;
339	u32 vhandle;
340	u32 attrs;
341	int i;
342
343	if (be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS)
344		return 0;
345
346	i = tpm2_find_cc(chip, cc);
347	/* sanity check, should never happen */
348	if (i < 0)
349		return -EFAULT;
350
351	attrs = chip->cc_attrs_tbl[i];
352	if (!((attrs >> TPM2_CC_ATTR_RHANDLE) & 1))
353		return 0;
354
355	phandle = be32_to_cpup((__be32 *)&rsp[TPM_HEADER_SIZE]);
356	phandle_type = phandle & 0xFF000000;
357
358	switch (phandle_type) {
359	case TPM2_HT_TRANSIENT:
360		vhandle = tpm2_map_to_vhandle(space, phandle, true);
361		if (!vhandle)
362			goto out_no_slots;
363
364		*(__be32 *)&rsp[TPM_HEADER_SIZE] = cpu_to_be32(vhandle);
365		break;
366	case TPM2_HT_HMAC_SESSION:
367	case TPM2_HT_POLICY_SESSION:
368		if (!tpm2_add_session(chip, phandle))
369			goto out_no_slots;
370		break;
371	default:
372		dev_err(&chip->dev, "%s: unknown handle 0x%08X\n",
373			__func__, phandle);
374		break;
375	};
376
377	return 0;
378out_no_slots:
379	tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_UNLOCKED);
380	dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__,
381		 phandle);
382	return -ENOMEM;
383}
384
385struct tpm2_cap_handles {
386	u8 more_data;
387	__be32 capability;
388	__be32 count;
389	__be32 handles[];
390} __packed;
391
392static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp,
393				  size_t len)
394{
395	struct tpm_space *space = &chip->work_space;
396	struct tpm_output_header *header = (void *)rsp;
397	struct tpm2_cap_handles *data;
398	u32 phandle;
399	u32 phandle_type;
400	u32 vhandle;
401	int i;
402	int j;
403
404	if (cc != TPM2_CC_GET_CAPABILITY ||
405	    be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS) {
406		return 0;
407	}
408
409	if (len < TPM_HEADER_SIZE + 9)
410		return -EFAULT;
411
412	data = (void *)&rsp[TPM_HEADER_SIZE];
413	if (be32_to_cpu(data->capability) != TPM2_CAP_HANDLES)
414		return 0;
415
 
 
 
416	if (len != TPM_HEADER_SIZE + 9 + 4 * be32_to_cpu(data->count))
417		return -EFAULT;
418
419	for (i = 0, j = 0; i < be32_to_cpu(data->count); i++) {
420		phandle = be32_to_cpup((__be32 *)&data->handles[i]);
421		phandle_type = phandle & 0xFF000000;
422
423		switch (phandle_type) {
424		case TPM2_HT_TRANSIENT:
425			vhandle = tpm2_map_to_vhandle(space, phandle, false);
426			if (!vhandle)
427				break;
428
429			data->handles[j] = cpu_to_be32(vhandle);
430			j++;
431			break;
432
433		default:
434			data->handles[j] = cpu_to_be32(phandle);
435			j++;
436			break;
437		}
438
439	}
440
441	header->length = cpu_to_be32(TPM_HEADER_SIZE + 9 + 4 * j);
442	data->count = cpu_to_be32(j);
443	return 0;
444}
445
446static int tpm2_save_space(struct tpm_chip *chip)
447{
448	struct tpm_space *space = &chip->work_space;
449	unsigned int offset;
450	int i;
451	int rc;
452
453	for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
454		if (!(space->context_tbl[i] && ~space->context_tbl[i]))
455			continue;
456
457		rc = tpm2_save_context(chip, space->context_tbl[i],
458				       space->context_buf, PAGE_SIZE,
459				       &offset);
460		if (rc == -ENOENT) {
461			space->context_tbl[i] = 0;
462			continue;
463		} else if (rc)
464			return rc;
465
466		tpm2_flush_context_cmd(chip, space->context_tbl[i],
467				       TPM_TRANSMIT_UNLOCKED);
468		space->context_tbl[i] = ~0;
469	}
470
471	for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
472		if (!space->session_tbl[i])
473			continue;
474
475		rc = tpm2_save_context(chip, space->session_tbl[i],
476				       space->session_buf, PAGE_SIZE,
477				       &offset);
478
479		if (rc == -ENOENT) {
480			/* handle error saving session, just forget it */
481			space->session_tbl[i] = 0;
482		} else if (rc < 0) {
483			tpm2_flush_space(chip);
484			return rc;
485		}
486	}
487
488	return 0;
489}
490
491int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
492		      u32 cc, u8 *buf, size_t *bufsiz)
493{
494	struct tpm_output_header *header = (void *)buf;
495	int rc;
496
497	if (!space)
498		return 0;
499
500	rc = tpm2_map_response_header(chip, cc, buf, *bufsiz);
501	if (rc) {
502		tpm2_flush_space(chip);
503		return rc;
504	}
505
506	rc = tpm2_map_response_body(chip, cc, buf, *bufsiz);
507	if (rc) {
508		tpm2_flush_space(chip);
509		return rc;
510	}
511
512	rc = tpm2_save_space(chip);
513	if (rc) {
514		tpm2_flush_space(chip);
515		return rc;
516	}
517
518	*bufsiz = be32_to_cpu(header->length);
519
520	memcpy(&space->context_tbl, &chip->work_space.context_tbl,
521	       sizeof(space->context_tbl));
522	memcpy(&space->session_tbl, &chip->work_space.session_tbl,
523	       sizeof(space->session_tbl));
524	memcpy(space->context_buf, chip->work_space.context_buf, PAGE_SIZE);
525	memcpy(space->session_buf, chip->work_space.session_buf, PAGE_SIZE);
 
 
526
527	return 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
528}