18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2004 IBM Corporation
48c2ecf20Sopenharmony_ci * Copyright (C) 2014 Intel Corporation
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Authors:
78c2ecf20Sopenharmony_ci * Leendert van Doorn <leendert@watson.ibm.com>
88c2ecf20Sopenharmony_ci * Dave Safford <safford@watson.ibm.com>
98c2ecf20Sopenharmony_ci * Reiner Sailer <sailer@watson.ibm.com>
108c2ecf20Sopenharmony_ci * Kylene Hall <kjhall@us.ibm.com>
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Maintained by: <tpmdd-devel@lists.sourceforge.net>
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Device driver for TCG/TCPA TPM (trusted platform module).
158c2ecf20Sopenharmony_ci * Specifications at www.trustedcomputinggroup.org
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Note, the TPM chip is not interrupt driven (only polling)
188c2ecf20Sopenharmony_ci * and can have very long timeouts (minutes!). Hence the unusual
198c2ecf20Sopenharmony_ci * calls to msleep.
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <linux/poll.h>
238c2ecf20Sopenharmony_ci#include <linux/slab.h>
248c2ecf20Sopenharmony_ci#include <linux/mutex.h>
258c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
268c2ecf20Sopenharmony_ci#include <linux/suspend.h>
278c2ecf20Sopenharmony_ci#include <linux/freezer.h>
288c2ecf20Sopenharmony_ci#include <linux/tpm_eventlog.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "tpm.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/*
338c2ecf20Sopenharmony_ci * Bug workaround - some TPM's don't flush the most
348c2ecf20Sopenharmony_ci * recently changed pcr on suspend, so force the flush
358c2ecf20Sopenharmony_ci * with an extend to the selected _unused_ non-volatile pcr.
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_cistatic u32 tpm_suspend_pcr;
388c2ecf20Sopenharmony_cimodule_param_named(suspend_pcr, tpm_suspend_pcr, uint, 0644);
398c2ecf20Sopenharmony_ciMODULE_PARM_DESC(suspend_pcr,
408c2ecf20Sopenharmony_ci		 "PCR to use for dummy writes to facilitate flush on suspend.");
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/**
438c2ecf20Sopenharmony_ci * tpm_calc_ordinal_duration() - calculate the maximum command duration
448c2ecf20Sopenharmony_ci * @chip:    TPM chip to use.
458c2ecf20Sopenharmony_ci * @ordinal: TPM command ordinal.
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * The function returns the maximum amount of time the chip could take
488c2ecf20Sopenharmony_ci * to return the result for a particular ordinal in jiffies.
498c2ecf20Sopenharmony_ci *
508c2ecf20Sopenharmony_ci * Return: A maximal duration time for an ordinal in jiffies.
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_ciunsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	if (chip->flags & TPM_CHIP_FLAG_TPM2)
558c2ecf20Sopenharmony_ci		return tpm2_calc_ordinal_duration(chip, ordinal);
568c2ecf20Sopenharmony_ci	else
578c2ecf20Sopenharmony_ci		return tpm1_calc_ordinal_duration(chip, ordinal);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct tpm_header *header = buf;
648c2ecf20Sopenharmony_ci	int rc;
658c2ecf20Sopenharmony_ci	ssize_t len = 0;
668c2ecf20Sopenharmony_ci	u32 count, ordinal;
678c2ecf20Sopenharmony_ci	unsigned long stop;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (bufsiz < TPM_HEADER_SIZE)
708c2ecf20Sopenharmony_ci		return -EINVAL;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (bufsiz > TPM_BUFSIZE)
738c2ecf20Sopenharmony_ci		bufsiz = TPM_BUFSIZE;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	count = be32_to_cpu(header->length);
768c2ecf20Sopenharmony_ci	ordinal = be32_to_cpu(header->ordinal);
778c2ecf20Sopenharmony_ci	if (count == 0)
788c2ecf20Sopenharmony_ci		return -ENODATA;
798c2ecf20Sopenharmony_ci	if (count > bufsiz) {
808c2ecf20Sopenharmony_ci		dev_err(&chip->dev,
818c2ecf20Sopenharmony_ci			"invalid count value %x %zx\n", count, bufsiz);
828c2ecf20Sopenharmony_ci		return -E2BIG;
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	rc = chip->ops->send(chip, buf, count);
868c2ecf20Sopenharmony_ci	if (rc < 0) {
878c2ecf20Sopenharmony_ci		if (rc != -EPIPE)
888c2ecf20Sopenharmony_ci			dev_err(&chip->dev,
898c2ecf20Sopenharmony_ci				"%s: send(): error %d\n", __func__, rc);
908c2ecf20Sopenharmony_ci		return rc;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/* A sanity check. send() should just return zero on success e.g.
948c2ecf20Sopenharmony_ci	 * not the command length.
958c2ecf20Sopenharmony_ci	 */
968c2ecf20Sopenharmony_ci	if (rc > 0) {
978c2ecf20Sopenharmony_ci		dev_warn(&chip->dev,
988c2ecf20Sopenharmony_ci			 "%s: send(): invalid value %d\n", __func__, rc);
998c2ecf20Sopenharmony_ci		rc = 0;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (chip->flags & TPM_CHIP_FLAG_IRQ)
1038c2ecf20Sopenharmony_ci		goto out_recv;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
1068c2ecf20Sopenharmony_ci	do {
1078c2ecf20Sopenharmony_ci		u8 status = chip->ops->status(chip);
1088c2ecf20Sopenharmony_ci		if ((status & chip->ops->req_complete_mask) ==
1098c2ecf20Sopenharmony_ci		    chip->ops->req_complete_val)
1108c2ecf20Sopenharmony_ci			goto out_recv;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		if (chip->ops->req_canceled(chip, status)) {
1138c2ecf20Sopenharmony_ci			dev_err(&chip->dev, "Operation Canceled\n");
1148c2ecf20Sopenharmony_ci			return -ECANCELED;
1158c2ecf20Sopenharmony_ci		}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		tpm_msleep(TPM_TIMEOUT_POLL);
1188c2ecf20Sopenharmony_ci		rmb();
1198c2ecf20Sopenharmony_ci	} while (time_before(jiffies, stop));
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	chip->ops->cancel(chip);
1228c2ecf20Sopenharmony_ci	dev_err(&chip->dev, "Operation Timed out\n");
1238c2ecf20Sopenharmony_ci	return -ETIME;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ciout_recv:
1268c2ecf20Sopenharmony_ci	len = chip->ops->recv(chip, buf, bufsiz);
1278c2ecf20Sopenharmony_ci	if (len < 0) {
1288c2ecf20Sopenharmony_ci		rc = len;
1298c2ecf20Sopenharmony_ci		dev_err(&chip->dev, "tpm_transmit: tpm_recv: error %d\n", rc);
1308c2ecf20Sopenharmony_ci	} else if (len < TPM_HEADER_SIZE || len != be32_to_cpu(header->length))
1318c2ecf20Sopenharmony_ci		rc = -EFAULT;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return rc ? rc : len;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/**
1378c2ecf20Sopenharmony_ci * tpm_transmit - Internal kernel interface to transmit TPM commands.
1388c2ecf20Sopenharmony_ci * @chip:	a TPM chip to use
1398c2ecf20Sopenharmony_ci * @buf:	a TPM command buffer
1408c2ecf20Sopenharmony_ci * @bufsiz:	length of the TPM command buffer
1418c2ecf20Sopenharmony_ci *
1428c2ecf20Sopenharmony_ci * A wrapper around tpm_try_transmit() that handles TPM2_RC_RETRY returns from
1438c2ecf20Sopenharmony_ci * the TPM and retransmits the command after a delay up to a maximum wait of
1448c2ecf20Sopenharmony_ci * TPM2_DURATION_LONG.
1458c2ecf20Sopenharmony_ci *
1468c2ecf20Sopenharmony_ci * Note that TPM 1.x never returns TPM2_RC_RETRY so the retry logic is TPM 2.0
1478c2ecf20Sopenharmony_ci * only.
1488c2ecf20Sopenharmony_ci *
1498c2ecf20Sopenharmony_ci * Return:
1508c2ecf20Sopenharmony_ci * * The response length	- OK
1518c2ecf20Sopenharmony_ci * * -errno			- A system error
1528c2ecf20Sopenharmony_ci */
1538c2ecf20Sopenharmony_cissize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	struct tpm_header *header = (struct tpm_header *)buf;
1568c2ecf20Sopenharmony_ci	/* space for header and handles */
1578c2ecf20Sopenharmony_ci	u8 save[TPM_HEADER_SIZE + 3*sizeof(u32)];
1588c2ecf20Sopenharmony_ci	unsigned int delay_msec = TPM2_DURATION_SHORT;
1598c2ecf20Sopenharmony_ci	u32 rc = 0;
1608c2ecf20Sopenharmony_ci	ssize_t ret;
1618c2ecf20Sopenharmony_ci	const size_t save_size = min(sizeof(save), bufsiz);
1628c2ecf20Sopenharmony_ci	/* the command code is where the return code will be */
1638c2ecf20Sopenharmony_ci	u32 cc = be32_to_cpu(header->return_code);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/*
1668c2ecf20Sopenharmony_ci	 * Subtlety here: if we have a space, the handles will be
1678c2ecf20Sopenharmony_ci	 * transformed, so when we restore the header we also have to
1688c2ecf20Sopenharmony_ci	 * restore the handles.
1698c2ecf20Sopenharmony_ci	 */
1708c2ecf20Sopenharmony_ci	memcpy(save, buf, save_size);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	for (;;) {
1738c2ecf20Sopenharmony_ci		ret = tpm_try_transmit(chip, buf, bufsiz);
1748c2ecf20Sopenharmony_ci		if (ret < 0)
1758c2ecf20Sopenharmony_ci			break;
1768c2ecf20Sopenharmony_ci		rc = be32_to_cpu(header->return_code);
1778c2ecf20Sopenharmony_ci		if (rc != TPM2_RC_RETRY && rc != TPM2_RC_TESTING)
1788c2ecf20Sopenharmony_ci			break;
1798c2ecf20Sopenharmony_ci		/*
1808c2ecf20Sopenharmony_ci		 * return immediately if self test returns test
1818c2ecf20Sopenharmony_ci		 * still running to shorten boot time.
1828c2ecf20Sopenharmony_ci		 */
1838c2ecf20Sopenharmony_ci		if (rc == TPM2_RC_TESTING && cc == TPM2_CC_SELF_TEST)
1848c2ecf20Sopenharmony_ci			break;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		if (delay_msec > TPM2_DURATION_LONG) {
1878c2ecf20Sopenharmony_ci			if (rc == TPM2_RC_RETRY)
1888c2ecf20Sopenharmony_ci				dev_err(&chip->dev, "in retry loop\n");
1898c2ecf20Sopenharmony_ci			else
1908c2ecf20Sopenharmony_ci				dev_err(&chip->dev,
1918c2ecf20Sopenharmony_ci					"self test is still running\n");
1928c2ecf20Sopenharmony_ci			break;
1938c2ecf20Sopenharmony_ci		}
1948c2ecf20Sopenharmony_ci		tpm_msleep(delay_msec);
1958c2ecf20Sopenharmony_ci		delay_msec *= 2;
1968c2ecf20Sopenharmony_ci		memcpy(buf, save, save_size);
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci	return ret;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/**
2028c2ecf20Sopenharmony_ci * tpm_transmit_cmd - send a tpm command to the device
2038c2ecf20Sopenharmony_ci * @chip:			a TPM chip to use
2048c2ecf20Sopenharmony_ci * @buf:			a TPM command buffer
2058c2ecf20Sopenharmony_ci * @min_rsp_body_length:	minimum expected length of response body
2068c2ecf20Sopenharmony_ci * @desc:			command description used in the error message
2078c2ecf20Sopenharmony_ci *
2088c2ecf20Sopenharmony_ci * Return:
2098c2ecf20Sopenharmony_ci * * 0		- OK
2108c2ecf20Sopenharmony_ci * * -errno	- A system error
2118c2ecf20Sopenharmony_ci * * TPM_RC	- A TPM error
2128c2ecf20Sopenharmony_ci */
2138c2ecf20Sopenharmony_cissize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf,
2148c2ecf20Sopenharmony_ci			 size_t min_rsp_body_length, const char *desc)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	const struct tpm_header *header = (struct tpm_header *)buf->data;
2178c2ecf20Sopenharmony_ci	int err;
2188c2ecf20Sopenharmony_ci	ssize_t len;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	len = tpm_transmit(chip, buf->data, PAGE_SIZE);
2218c2ecf20Sopenharmony_ci	if (len <  0)
2228c2ecf20Sopenharmony_ci		return len;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	err = be32_to_cpu(header->return_code);
2258c2ecf20Sopenharmony_ci	if (err != 0 && err != TPM_ERR_DISABLED && err != TPM_ERR_DEACTIVATED
2268c2ecf20Sopenharmony_ci	    && err != TPM2_RC_TESTING && desc)
2278c2ecf20Sopenharmony_ci		dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err,
2288c2ecf20Sopenharmony_ci			desc);
2298c2ecf20Sopenharmony_ci	if (err)
2308c2ecf20Sopenharmony_ci		return err;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (len < min_rsp_body_length + TPM_HEADER_SIZE)
2338c2ecf20Sopenharmony_ci		return -EFAULT;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	return 0;
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_transmit_cmd);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ciint tpm_get_timeouts(struct tpm_chip *chip)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	if (chip->flags & TPM_CHIP_FLAG_HAVE_TIMEOUTS)
2428c2ecf20Sopenharmony_ci		return 0;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (chip->flags & TPM_CHIP_FLAG_TPM2)
2458c2ecf20Sopenharmony_ci		return tpm2_get_timeouts(chip);
2468c2ecf20Sopenharmony_ci	else
2478c2ecf20Sopenharmony_ci		return tpm1_get_timeouts(chip);
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_get_timeouts);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/**
2528c2ecf20Sopenharmony_ci * tpm_is_tpm2 - do we a have a TPM2 chip?
2538c2ecf20Sopenharmony_ci * @chip:	a &struct tpm_chip instance, %NULL for the default chip
2548c2ecf20Sopenharmony_ci *
2558c2ecf20Sopenharmony_ci * Return:
2568c2ecf20Sopenharmony_ci * 1 if we have a TPM2 chip.
2578c2ecf20Sopenharmony_ci * 0 if we don't have a TPM2 chip.
2588c2ecf20Sopenharmony_ci * A negative number for system errors (errno).
2598c2ecf20Sopenharmony_ci */
2608c2ecf20Sopenharmony_ciint tpm_is_tpm2(struct tpm_chip *chip)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	int rc;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	chip = tpm_find_get_ops(chip);
2658c2ecf20Sopenharmony_ci	if (!chip)
2668c2ecf20Sopenharmony_ci		return -ENODEV;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	tpm_put_ops(chip);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return rc;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_is_tpm2);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci/**
2778c2ecf20Sopenharmony_ci * tpm_pcr_read - read a PCR value from SHA1 bank
2788c2ecf20Sopenharmony_ci * @chip:	a &struct tpm_chip instance, %NULL for the default chip
2798c2ecf20Sopenharmony_ci * @pcr_idx:	the PCR to be retrieved
2808c2ecf20Sopenharmony_ci * @digest:	the PCR bank and buffer current PCR value is written to
2818c2ecf20Sopenharmony_ci *
2828c2ecf20Sopenharmony_ci * Return: same as with tpm_transmit_cmd()
2838c2ecf20Sopenharmony_ci */
2848c2ecf20Sopenharmony_ciint tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
2858c2ecf20Sopenharmony_ci		 struct tpm_digest *digest)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	int rc;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	chip = tpm_find_get_ops(chip);
2908c2ecf20Sopenharmony_ci	if (!chip)
2918c2ecf20Sopenharmony_ci		return -ENODEV;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	if (chip->flags & TPM_CHIP_FLAG_TPM2)
2948c2ecf20Sopenharmony_ci		rc = tpm2_pcr_read(chip, pcr_idx, digest, NULL);
2958c2ecf20Sopenharmony_ci	else
2968c2ecf20Sopenharmony_ci		rc = tpm1_pcr_read(chip, pcr_idx, digest->digest);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	tpm_put_ops(chip);
2998c2ecf20Sopenharmony_ci	return rc;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_pcr_read);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci/**
3048c2ecf20Sopenharmony_ci * tpm_pcr_extend - extend a PCR value in SHA1 bank.
3058c2ecf20Sopenharmony_ci * @chip:	a &struct tpm_chip instance, %NULL for the default chip
3068c2ecf20Sopenharmony_ci * @pcr_idx:	the PCR to be retrieved
3078c2ecf20Sopenharmony_ci * @digests:	array of tpm_digest structures used to extend PCRs
3088c2ecf20Sopenharmony_ci *
3098c2ecf20Sopenharmony_ci * Note: callers must pass a digest for every allocated PCR bank, in the same
3108c2ecf20Sopenharmony_ci * order of the banks in chip->allocated_banks.
3118c2ecf20Sopenharmony_ci *
3128c2ecf20Sopenharmony_ci * Return: same as with tpm_transmit_cmd()
3138c2ecf20Sopenharmony_ci */
3148c2ecf20Sopenharmony_ciint tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
3158c2ecf20Sopenharmony_ci		   struct tpm_digest *digests)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	int rc;
3188c2ecf20Sopenharmony_ci	int i;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	chip = tpm_find_get_ops(chip);
3218c2ecf20Sopenharmony_ci	if (!chip)
3228c2ecf20Sopenharmony_ci		return -ENODEV;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	for (i = 0; i < chip->nr_allocated_banks; i++) {
3258c2ecf20Sopenharmony_ci		if (digests[i].alg_id != chip->allocated_banks[i].alg_id) {
3268c2ecf20Sopenharmony_ci			rc = -EINVAL;
3278c2ecf20Sopenharmony_ci			goto out;
3288c2ecf20Sopenharmony_ci		}
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	if (chip->flags & TPM_CHIP_FLAG_TPM2) {
3328c2ecf20Sopenharmony_ci		rc = tpm2_pcr_extend(chip, pcr_idx, digests);
3338c2ecf20Sopenharmony_ci		goto out;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	rc = tpm1_pcr_extend(chip, pcr_idx, digests[0].digest,
3378c2ecf20Sopenharmony_ci			     "attempting extend a PCR value");
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ciout:
3408c2ecf20Sopenharmony_ci	tpm_put_ops(chip);
3418c2ecf20Sopenharmony_ci	return rc;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_pcr_extend);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci/**
3468c2ecf20Sopenharmony_ci * tpm_send - send a TPM command
3478c2ecf20Sopenharmony_ci * @chip:	a &struct tpm_chip instance, %NULL for the default chip
3488c2ecf20Sopenharmony_ci * @cmd:	a TPM command buffer
3498c2ecf20Sopenharmony_ci * @buflen:	the length of the TPM command buffer
3508c2ecf20Sopenharmony_ci *
3518c2ecf20Sopenharmony_ci * Return: same as with tpm_transmit_cmd()
3528c2ecf20Sopenharmony_ci */
3538c2ecf20Sopenharmony_ciint tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	struct tpm_buf buf;
3568c2ecf20Sopenharmony_ci	int rc;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	chip = tpm_find_get_ops(chip);
3598c2ecf20Sopenharmony_ci	if (!chip)
3608c2ecf20Sopenharmony_ci		return -ENODEV;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	buf.data = cmd;
3638c2ecf20Sopenharmony_ci	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to a send a command");
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	tpm_put_ops(chip);
3668c2ecf20Sopenharmony_ci	return rc;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_send);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ciint tpm_auto_startup(struct tpm_chip *chip)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	int rc;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	if (!(chip->ops->flags & TPM_OPS_AUTO_STARTUP))
3758c2ecf20Sopenharmony_ci		return 0;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (chip->flags & TPM_CHIP_FLAG_TPM2)
3788c2ecf20Sopenharmony_ci		rc = tpm2_auto_startup(chip);
3798c2ecf20Sopenharmony_ci	else
3808c2ecf20Sopenharmony_ci		rc = tpm1_auto_startup(chip);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	return rc;
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci/*
3868c2ecf20Sopenharmony_ci * We are about to suspend. Save the TPM state
3878c2ecf20Sopenharmony_ci * so that it can be restored.
3888c2ecf20Sopenharmony_ci */
3898c2ecf20Sopenharmony_ciint tpm_pm_suspend(struct device *dev)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct tpm_chip *chip = dev_get_drvdata(dev);
3928c2ecf20Sopenharmony_ci	int rc = 0;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (!chip)
3958c2ecf20Sopenharmony_ci		return -ENODEV;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	if (chip->flags & TPM_CHIP_FLAG_ALWAYS_POWERED)
3988c2ecf20Sopenharmony_ci		goto suspended;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if ((chip->flags & TPM_CHIP_FLAG_FIRMWARE_POWER_MANAGED) &&
4018c2ecf20Sopenharmony_ci	    !pm_suspend_via_firmware())
4028c2ecf20Sopenharmony_ci		goto suspended;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	rc = tpm_try_get_ops(chip);
4058c2ecf20Sopenharmony_ci	if (!rc) {
4068c2ecf20Sopenharmony_ci		if (chip->flags & TPM_CHIP_FLAG_TPM2)
4078c2ecf20Sopenharmony_ci			tpm2_shutdown(chip, TPM2_SU_STATE);
4088c2ecf20Sopenharmony_ci		else
4098c2ecf20Sopenharmony_ci			rc = tpm1_pm_suspend(chip, tpm_suspend_pcr);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci		tpm_put_ops(chip);
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cisuspended:
4158c2ecf20Sopenharmony_ci	return rc;
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_pm_suspend);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci/*
4208c2ecf20Sopenharmony_ci * Resume from a power safe. The BIOS already restored
4218c2ecf20Sopenharmony_ci * the TPM state.
4228c2ecf20Sopenharmony_ci */
4238c2ecf20Sopenharmony_ciint tpm_pm_resume(struct device *dev)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct tpm_chip *chip = dev_get_drvdata(dev);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (chip == NULL)
4288c2ecf20Sopenharmony_ci		return -ENODEV;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	return 0;
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_pm_resume);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci/**
4358c2ecf20Sopenharmony_ci * tpm_get_random() - get random bytes from the TPM's RNG
4368c2ecf20Sopenharmony_ci * @chip:	a &struct tpm_chip instance, %NULL for the default chip
4378c2ecf20Sopenharmony_ci * @out:	destination buffer for the random bytes
4388c2ecf20Sopenharmony_ci * @max:	the max number of bytes to write to @out
4398c2ecf20Sopenharmony_ci *
4408c2ecf20Sopenharmony_ci * Return: number of random bytes read or a negative error value.
4418c2ecf20Sopenharmony_ci */
4428c2ecf20Sopenharmony_ciint tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	int rc;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (!out || max > TPM_MAX_RNG_DATA)
4478c2ecf20Sopenharmony_ci		return -EINVAL;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	chip = tpm_find_get_ops(chip);
4508c2ecf20Sopenharmony_ci	if (!chip)
4518c2ecf20Sopenharmony_ci		return -ENODEV;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if (chip->flags & TPM_CHIP_FLAG_TPM2)
4548c2ecf20Sopenharmony_ci		rc = tpm2_get_random(chip, out, max);
4558c2ecf20Sopenharmony_ci	else
4568c2ecf20Sopenharmony_ci		rc = tpm1_get_random(chip, out, max);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	tpm_put_ops(chip);
4598c2ecf20Sopenharmony_ci	return rc;
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_get_random);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic int __init tpm_init(void)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	int rc;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	tpm_class = class_create(THIS_MODULE, "tpm");
4688c2ecf20Sopenharmony_ci	if (IS_ERR(tpm_class)) {
4698c2ecf20Sopenharmony_ci		pr_err("couldn't create tpm class\n");
4708c2ecf20Sopenharmony_ci		return PTR_ERR(tpm_class);
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	tpmrm_class = class_create(THIS_MODULE, "tpmrm");
4748c2ecf20Sopenharmony_ci	if (IS_ERR(tpmrm_class)) {
4758c2ecf20Sopenharmony_ci		pr_err("couldn't create tpmrm class\n");
4768c2ecf20Sopenharmony_ci		rc = PTR_ERR(tpmrm_class);
4778c2ecf20Sopenharmony_ci		goto out_destroy_tpm_class;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	rc = alloc_chrdev_region(&tpm_devt, 0, 2*TPM_NUM_DEVICES, "tpm");
4818c2ecf20Sopenharmony_ci	if (rc < 0) {
4828c2ecf20Sopenharmony_ci		pr_err("tpm: failed to allocate char dev region\n");
4838c2ecf20Sopenharmony_ci		goto out_destroy_tpmrm_class;
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	rc = tpm_dev_common_init();
4878c2ecf20Sopenharmony_ci	if (rc) {
4888c2ecf20Sopenharmony_ci		pr_err("tpm: failed to allocate char dev region\n");
4898c2ecf20Sopenharmony_ci		goto out_unreg_chrdev;
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	return 0;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ciout_unreg_chrdev:
4958c2ecf20Sopenharmony_ci	unregister_chrdev_region(tpm_devt, 2 * TPM_NUM_DEVICES);
4968c2ecf20Sopenharmony_ciout_destroy_tpmrm_class:
4978c2ecf20Sopenharmony_ci	class_destroy(tpmrm_class);
4988c2ecf20Sopenharmony_ciout_destroy_tpm_class:
4998c2ecf20Sopenharmony_ci	class_destroy(tpm_class);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	return rc;
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cistatic void __exit tpm_exit(void)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	idr_destroy(&dev_nums_idr);
5078c2ecf20Sopenharmony_ci	class_destroy(tpm_class);
5088c2ecf20Sopenharmony_ci	class_destroy(tpmrm_class);
5098c2ecf20Sopenharmony_ci	unregister_chrdev_region(tpm_devt, 2*TPM_NUM_DEVICES);
5108c2ecf20Sopenharmony_ci	tpm_dev_common_exit();
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cisubsys_initcall(tpm_init);
5148c2ecf20Sopenharmony_cimodule_exit(tpm_exit);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
5178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TPM Driver");
5188c2ecf20Sopenharmony_ciMODULE_VERSION("2.0");
5198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
520