Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8077e8b0 authored by James Morris's avatar James Morris
Browse files

Merge branch 'for-james' of git://github.com/srajiv/tpm into next

parents 7845bc39 b9e3238a
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ if TCG_TPM

config TCG_TIS
	tristate "TPM Interface Specification 1.2 Interface"
	depends on X86
	---help---
	  If you have a TPM security chip that is compliant with the
	  TCG TIS 1.2 TPM specification say Yes and it will be accessible
@@ -35,6 +36,7 @@ config TCG_TIS

config TCG_NSC
	tristate "National Semiconductor TPM Interface"
	depends on X86
	---help---
	  If you have a TPM security chip from National Semiconductor 
	  say Yes and it will be accessible from within Linux.  To 
+117 −20
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/freezer.h>

#include "tpm.h"

@@ -440,7 +441,6 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
}

#define TPM_DIGEST_SIZE 20
#define TPM_ERROR_SIZE 10
#define TPM_RET_CODE_IDX 6

enum tpm_capabilities {
@@ -469,13 +469,15 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
	len = tpm_transmit(chip,(u8 *) cmd, len);
	if (len <  0)
		return len;
	if (len == TPM_ERROR_SIZE) {
	else if (len < TPM_HEADER_SIZE)
		return -EFAULT;

	err = be32_to_cpu(cmd->header.out.return_code);
		dev_dbg(chip->dev, "A TPM error (%d) occurred %s\n", err, desc);
	if (err != 0)
		dev_err(chip->dev, "A TPM error (%d) occurred %s\n", err, desc);

	return err;
}
	return 0;
}

#define TPM_INTERNAL_RESULT_SIZE 200
#define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
@@ -530,7 +532,7 @@ void tpm_gen_interrupt(struct tpm_chip *chip)
}
EXPORT_SYMBOL_GPL(tpm_gen_interrupt);

void tpm_get_timeouts(struct tpm_chip *chip)
int tpm_get_timeouts(struct tpm_chip *chip)
{
	struct tpm_cmd_t tpm_cmd;
	struct timeout_t *timeout_cap;
@@ -552,7 +554,7 @@ void tpm_get_timeouts(struct tpm_chip *chip)
	if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
	    be32_to_cpu(tpm_cmd.header.out.length)
	    != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32))
		return;
		return -EINVAL;

	timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
	/* Don't overwrite default if value is 0 */
@@ -583,12 +585,12 @@ void tpm_get_timeouts(struct tpm_chip *chip)
	rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
			"attempting to determine the durations");
	if (rc)
		return;
		return rc;

	if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
	    be32_to_cpu(tpm_cmd.header.out.length)
	    != sizeof(tpm_cmd.header.out) + sizeof(u32) + 3 * sizeof(u32))
		return;
		return -EINVAL;

	duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
	chip->vendor.duration[TPM_SHORT] =
@@ -610,20 +612,36 @@ void tpm_get_timeouts(struct tpm_chip *chip)
		chip->vendor.duration_adjusted = true;
		dev_info(chip->dev, "Adjusting TPM timeout parameters.");
	}
	return 0;
}
EXPORT_SYMBOL_GPL(tpm_get_timeouts);

void tpm_continue_selftest(struct tpm_chip *chip)
{
	u8 data[] = {
		0, 193,			/* TPM_TAG_RQU_COMMAND */
		0, 0, 0, 10,		/* length */
		0, 0, 0, 83,		/* TPM_ORD_ContinueSelfTest */
#define TPM_ORD_CONTINUE_SELFTEST 83
#define CONTINUE_SELFTEST_RESULT_SIZE 10

static struct tpm_input_header continue_selftest_header = {
	.tag = TPM_TAG_RQU_COMMAND,
	.length = cpu_to_be32(10),
	.ordinal = cpu_to_be32(TPM_ORD_CONTINUE_SELFTEST),
};

	tpm_transmit(chip, data, sizeof(data));
/**
 * tpm_continue_selftest -- run TPM's selftest
 * @chip: TPM chip to use
 *
 * Returns 0 on success, < 0 in case of fatal error or a value > 0 representing
 * a TPM error code.
 */
static int tpm_continue_selftest(struct tpm_chip *chip)
{
	int rc;
	struct tpm_cmd_t cmd;

	cmd.header.in = continue_selftest_header;
	rc = transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE,
			  "continue selftest");
	return rc;
}
EXPORT_SYMBOL_GPL(tpm_continue_selftest);

ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
			char *buf)
@@ -718,7 +736,7 @@ static struct tpm_input_header pcrread_header = {
	.ordinal = TPM_ORDINAL_PCRREAD
};

int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
static int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
{
	int rc;
	struct tpm_cmd_t cmd;
@@ -798,6 +816,45 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
}
EXPORT_SYMBOL_GPL(tpm_pcr_extend);

/**
 * tpm_do_selftest - have the TPM continue its selftest and wait until it
 *                   can receive further commands
 * @chip: TPM chip to use
 *
 * Returns 0 on success, < 0 in case of fatal error or a value > 0 representing
 * a TPM error code.
 */
int tpm_do_selftest(struct tpm_chip *chip)
{
	int rc;
	u8 digest[TPM_DIGEST_SIZE];
	unsigned int loops;
	unsigned int delay_msec = 1000;
	unsigned long duration;

	duration = tpm_calc_ordinal_duration(chip,
	                                     TPM_ORD_CONTINUE_SELFTEST);

	loops = jiffies_to_msecs(duration) / delay_msec;

	rc = tpm_continue_selftest(chip);
	/* This may fail if there was no TPM driver during a suspend/resume
	 * cycle; some may return 10 (BAD_ORDINAL), others 28 (FAILEDSELFTEST)
	 */
	if (rc)
		return rc;

	do {
		rc = __tpm_pcr_read(chip, 0, digest);
		if (rc != TPM_WARN_DOING_SELFTEST)
			return rc;
		msleep(delay_msec);
	} while (--loops > 0);

	return rc;
}
EXPORT_SYMBOL_GPL(tpm_do_selftest);

int tpm_send(u32 chip_num, void *cmd, size_t buflen)
{
	struct tpm_chip *chip;
@@ -1005,6 +1062,46 @@ ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr,
}
EXPORT_SYMBOL_GPL(tpm_store_cancel);

int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
			 wait_queue_head_t *queue)
{
	unsigned long stop;
	long rc;
	u8 status;

	/* check current status */
	status = chip->vendor.status(chip);
	if ((status & mask) == mask)
		return 0;

	stop = jiffies + timeout;

	if (chip->vendor.irq) {
again:
		timeout = stop - jiffies;
		if ((long)timeout <= 0)
			return -ETIME;
		rc = wait_event_interruptible_timeout(*queue,
						      ((chip->vendor.status(chip)
						      & mask) == mask),
						      timeout);
		if (rc > 0)
			return 0;
		if (rc == -ERESTARTSYS && freezing(current)) {
			clear_thread_flag(TIF_SIGPENDING);
			goto again;
		}
	} else {
		do {
			msleep(TPM_TIMEOUT);
			status = chip->vendor.status(chip);
			if ((status & mask) == mask)
				return 0;
		} while (time_before(jiffies, stop));
	}
	return -ETIME;
}
EXPORT_SYMBOL_GPL(wait_for_tpm_stat);
/*
 * Device file system interface to the TPM
 *
+6 −3
Original line number Diff line number Diff line
@@ -38,6 +38,8 @@ enum tpm_addr {
	TPM_ADDR = 0x4E,
};

#define TPM_WARN_DOING_SELFTEST 0x802
#define TPM_HEADER_SIZE		10
extern ssize_t tpm_show_pubek(struct device *, struct device_attribute *attr,
				char *);
extern ssize_t tpm_show_pcrs(struct device *, struct device_attribute *attr,
@@ -279,9 +281,9 @@ struct tpm_cmd_t {

ssize_t	tpm_getcap(struct device *, __be32, cap_t *, const char *);

extern void tpm_get_timeouts(struct tpm_chip *);
extern int tpm_get_timeouts(struct tpm_chip *);
extern void tpm_gen_interrupt(struct tpm_chip *);
extern void tpm_continue_selftest(struct tpm_chip *);
extern int tpm_do_selftest(struct tpm_chip *);
extern unsigned long tpm_calc_ordinal_duration(struct tpm_chip *, u32);
extern struct tpm_chip* tpm_register_hardware(struct device *,
				 const struct tpm_vendor_specific *);
@@ -294,7 +296,8 @@ extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *);
extern void tpm_remove_hardware(struct device *);
extern int tpm_pm_suspend(struct device *, pm_message_t);
extern int tpm_pm_resume(struct device *);

extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
			     wait_queue_head_t *);
#ifdef CONFIG_ACPI
extern struct dentry ** tpm_bios_log_setup(char *);
extern void tpm_bios_log_teardown(struct dentry **);
+27 −57
Original line number Diff line number Diff line
@@ -29,8 +29,6 @@
#include <linux/freezer.h>
#include "tpm.h"

#define TPM_HEADER_SIZE 10

enum tis_access {
	TPM_ACCESS_VALID = 0x80,
	TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
@@ -193,51 +191,11 @@ static int get_burstcount(struct tpm_chip *chip)
	return -EBUSY;
}

static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
			 wait_queue_head_t *queue)
{
	unsigned long stop;
	long rc;
	u8 status;

	/* check current status */
	status = tpm_tis_status(chip);
	if ((status & mask) == mask)
		return 0;

	stop = jiffies + timeout;

	if (chip->vendor.irq) {
again:
		timeout = stop - jiffies;
		if ((long)timeout <= 0)
			return -ETIME;
		rc = wait_event_interruptible_timeout(*queue,
						      ((tpm_tis_status
							(chip) & mask) ==
						       mask), timeout);
		if (rc > 0)
			return 0;
		if (rc == -ERESTARTSYS && freezing(current)) {
			clear_thread_flag(TIF_SIGPENDING);
			goto again;
		}
	} else {
		do {
			msleep(TPM_TIMEOUT);
			status = tpm_tis_status(chip);
			if ((status & mask) == mask)
				return 0;
		} while (time_before(jiffies, stop));
	}
	return -ETIME;
}

static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
{
	int size = 0, burstcnt;
	while (size < count &&
	       wait_for_stat(chip,
	       wait_for_tpm_stat(chip,
				 TPM_STS_DATA_AVAIL | TPM_STS_VALID,
				 chip->vendor.timeout_c,
				 &chip->vendor.read_queue)
@@ -282,7 +240,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
		goto out;
	}

	wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
	wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
			  &chip->vendor.int_queue);
	status = tpm_tis_status(chip);
	if (status & TPM_STS_DATA_AVAIL) {	/* retry? */
@@ -317,7 +275,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
	status = tpm_tis_status(chip);
	if ((status & TPM_STS_COMMAND_READY) == 0) {
		tpm_tis_ready(chip);
		if (wait_for_stat
		if (wait_for_tpm_stat
		    (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
		     &chip->vendor.int_queue) < 0) {
			rc = -ETIME;
@@ -333,7 +291,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
			count++;
		}

		wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
		wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
				  &chip->vendor.int_queue);
		status = tpm_tis_status(chip);
		if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
@@ -345,7 +303,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
	/* write last byte */
	iowrite8(buf[count],
		 chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality));
	wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
	wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
			  &chip->vendor.int_queue);
	status = tpm_tis_status(chip);
	if ((status & TPM_STS_DATA_EXPECT) != 0) {
@@ -381,7 +339,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)

	if (chip->vendor.irq) {
		ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
		if (wait_for_stat
		if (wait_for_tpm_stat
		    (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
		     tpm_calc_ordinal_duration(chip, ordinal),
		     &chip->vendor.read_queue) < 0) {
@@ -432,6 +390,9 @@ static int probe_itpm(struct tpm_chip *chip)
out:
	itpm = rem_itpm;
	tpm_tis_ready(chip);
	/* some TPMs need a break here otherwise they will not work
	 * correctly on the immediately subsequent command */
	msleep(chip->vendor.timeout_b);
	release_locality(chip, chip->vendor.locality, 0);

	return rc;
@@ -614,7 +575,17 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
		dev_dbg(dev, "\tData Avail Int Support\n");

	/* get the timeouts before testing for irqs */
	tpm_get_timeouts(chip);
	if (tpm_get_timeouts(chip)) {
		dev_err(dev, "Could not get TPM timeouts and durations\n");
		rc = -ENODEV;
		goto out_err;
	}

	if (tpm_do_selftest(chip)) {
		dev_err(dev, "TPM self test failed\n");
		rc = -ENODEV;
		goto out_err;
	}

	/* INTERRUPT Setup */
	init_waitqueue_head(&chip->vendor.read_queue);
@@ -722,7 +693,6 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
	list_add(&chip->vendor.list, &tis_chips);
	spin_unlock(&tis_lock);

	tpm_continue_selftest(chip);

	return 0;
out_err:
@@ -790,7 +760,7 @@ static int tpm_tis_pnp_resume(struct pnp_dev *dev)

	ret = tpm_pm_resume(&dev->dev);
	if (!ret)
		tpm_continue_selftest(chip);
		tpm_do_selftest(chip);

	return ret;
}