linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/4] tpm: Command duration logging and chip-specific override
@ 2016-06-08  0:45 Ed Swierk
  2016-06-08  0:45 ` [PATCH v4 1/4] tpm_tis: Improve reporting of IO errors Ed Swierk
                   ` (4 more replies)
  0 siblings, 5 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-08  0:45 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel, linux-security-module

v4: Rework tpm_get_timeouts() to allow overriding both timeouts and
durations via a single callback.

This series
- improves TPM command error reporting
- adds optional logging of TPM command durations
- allows chip-specific override of command durations as well as protocol
  timeouts
- overrides ST19NP18 TPM command duration to avoid lockups

Ed Swierk (4):
  tpm_tis: Improve reporting of IO errors
  tpm: Add optional logging of TPM command durations
  tpm: Allow TPM chip drivers to override reported command durations
  tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup

 drivers/char/tpm/tpm-interface.c | 194 ++++++++++++++++++++++-----------------
 drivers/char/tpm/tpm_tis.c       |  48 +++++-----
 include/linux/tpm.h              |   3 +-
 3 files changed, 131 insertions(+), 114 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v4 1/4] tpm_tis: Improve reporting of IO errors
  2016-06-08  0:45 [PATCH v4 0/4] tpm: Command duration logging and chip-specific override Ed Swierk
@ 2016-06-08  0:45 ` Ed Swierk
  2016-06-08  0:45 ` [PATCH v4 2/4] tpm: Add optional logging of TPM command durations Ed Swierk
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-08  0:45 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel, linux-security-module

Mysterious TPM behavior can be difficult to track down through all the
layers of software. Add error messages for conditions that should
never happen. Also include the manufacturer ID along with other chip
data printed during init.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 drivers/char/tpm/tpm_tis.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 65f7eec..088fa86 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -299,6 +299,8 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 
 	expected = be32_to_cpu(*(__be32 *) (buf + 2));
 	if (expected > count) {
+		dev_err(chip->pdev, "Response too long (wanted %zd, got %d)\n",
+			count, expected);
 		size = -EIO;
 		goto out;
 	}
@@ -366,6 +368,8 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
 				  &chip->vendor.int_queue, false);
 		status = tpm_tis_status(chip);
 		if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
+			dev_err(chip->pdev, "Chip not accepting %zd bytes\n",
+				len - count);
 			rc = -EIO;
 			goto out_err;
 		}
@@ -378,6 +382,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
 			  &chip->vendor.int_queue, false);
 	status = tpm_tis_status(chip);
 	if ((status & TPM_STS_DATA_EXPECT) != 0) {
+		dev_err(chip->pdev, "Chip not accepting last byte\n");
 		rc = -EIO;
 		goto out_err;
 	}
@@ -689,8 +694,9 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
 	vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
 	chip->vendor.manufacturer_id = vendor;
 
-	dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n",
+	dev_info(dev, "%s TPM (manufacturer-id 0x%X, device-id 0x%X, rev-id %d)\n",
 		 (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2",
+		 chip->vendor.manufacturer_id,
 		 vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
 
 	if (!itpm) {
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v4 2/4] tpm: Add optional logging of TPM command durations
  2016-06-08  0:45 [PATCH v4 0/4] tpm: Command duration logging and chip-specific override Ed Swierk
  2016-06-08  0:45 ` [PATCH v4 1/4] tpm_tis: Improve reporting of IO errors Ed Swierk
@ 2016-06-08  0:45 ` Ed Swierk
  2016-06-08  0:45 ` [PATCH v4 3/4] tpm: Allow TPM chip drivers to override reported " Ed Swierk
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-08  0:45 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel, linux-security-module

Some TPMs violate their own advertised command durations. This is much
easier to debug with data about how long each command actually takes
to complete. Add debug messages that can be enabled by running

  echo -n 'module tpm +p' >/sys/kernel/debug/dynamic_debug/control

on a kernel configured with DYNAMIC_DEBUG=y.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 drivers/char/tpm/tpm-interface.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index c50637d..cc1e5bc 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -333,13 +333,14 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 {
 	ssize_t rc;
 	u32 count, ordinal;
-	unsigned long stop;
+	unsigned long start, stop;
 
 	if (bufsiz > TPM_BUFSIZE)
 		bufsiz = TPM_BUFSIZE;
 
 	count = be32_to_cpu(*((__be32 *) (buf + 2)));
 	ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+	dev_dbg(chip->pdev, "starting command %d count %d\n", ordinal, count);
 	if (count == 0)
 		return -ENODATA;
 	if (count > bufsiz) {
@@ -360,18 +361,24 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 	if (chip->vendor.irq)
 		goto out_recv;
 
+	start = jiffies;
 	if (chip->flags & TPM_CHIP_FLAG_TPM2)
-		stop = jiffies + tpm2_calc_ordinal_duration(chip, ordinal);
+		stop = start + tpm2_calc_ordinal_duration(chip, ordinal);
 	else
-		stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
+		stop = start + tpm_calc_ordinal_duration(chip, ordinal);
 	do {
 		u8 status = chip->ops->status(chip);
 		if ((status & chip->ops->req_complete_mask) ==
-		    chip->ops->req_complete_val)
+		    chip->ops->req_complete_val) {
+			dev_dbg(chip->pdev, "completed command %d in %d ms\n",
+				ordinal, jiffies_to_msecs(jiffies - start));
 			goto out_recv;
+		}
 
 		if (chip->ops->req_canceled(chip, status)) {
 			dev_err(chip->pdev, "Operation Canceled\n");
+			dev_dbg(chip->pdev, "canceled command %d after %d ms\n",
+				ordinal, jiffies_to_msecs(jiffies - start));
 			rc = -ECANCELED;
 			goto out;
 		}
@@ -382,6 +389,8 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 
 	chip->ops->cancel(chip);
 	dev_err(chip->pdev, "Operation Timed out\n");
+	dev_dbg(chip->pdev, "command %d timed out after %d ms\n", ordinal,
+		jiffies_to_msecs(jiffies - start));
 	rc = -ETIME;
 	goto out;
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v4 3/4] tpm: Allow TPM chip drivers to override reported command durations
  2016-06-08  0:45 [PATCH v4 0/4] tpm: Command duration logging and chip-specific override Ed Swierk
  2016-06-08  0:45 ` [PATCH v4 1/4] tpm_tis: Improve reporting of IO errors Ed Swierk
  2016-06-08  0:45 ` [PATCH v4 2/4] tpm: Add optional logging of TPM command durations Ed Swierk
@ 2016-06-08  0:45 ` Ed Swierk
  2016-06-08 19:05   ` [tpmdd-devel] " Jason Gunthorpe
  2016-06-08  0:45 ` [PATCH v4 4/4] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
  2016-06-08 23:00 ` [PATCH v5 0/4] tpm: Command duration logging and chip-specific override Ed Swierk
  4 siblings, 1 reply; 68+ messages in thread
From: Ed Swierk @ 2016-06-08  0:45 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel, linux-security-module

Some TPM chips report bogus command durations in their capabilities,
just as others report incorrect timeouts. Rework tpm_get_timeouts()
to allow chip drivers to override either via a single callback.
Also clean up handling of TPMs that report milliseconds instead of
microseconds.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm-interface.c | 177 +++++++++++++++++++++------------------
 drivers/char/tpm/tpm_tis.c       |  35 ++------
 include/linux/tpm.h              |   3 +-
 3 files changed, 106 insertions(+), 109 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index cc1e5bc..b8a08bb 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -502,123 +502,138 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
 				"attempting to start the TPM");
 }
 
-int tpm_get_timeouts(struct tpm_chip *chip)
+static int tpm_get_cap_prop(struct tpm_chip *chip, __be32 type, int size,
+			    cap_t *cap, char *desc)
 {
 	struct tpm_cmd_t tpm_cmd;
-	unsigned long new_timeout[4];
-	unsigned long old_timeout[4];
-	struct duration_t *duration_cap;
 	ssize_t rc;
 
 	tpm_cmd.header.in = tpm_getcap_header;
 	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
 	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
+	tpm_cmd.params.getcap_in.subcap = type;
 	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
 
 	if (rc == TPM_ERR_INVALID_POSTINIT) {
 		/* The TPM is not started, we are the first to talk to it.
 		   Execute a startup command. */
-		dev_info(chip->pdev, "Issuing TPM_STARTUP");
+		dev_info(chip->pdev, "Issuing TPM_STARTUP\n");
 		if (tpm_startup(chip, TPM_ST_CLEAR))
 			return rc;
 
 		tpm_cmd.header.in = tpm_getcap_header;
 		tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
 		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-		tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
+		tpm_cmd.params.getcap_in.subcap = type;
 		rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
 				  NULL);
 	}
+
 	if (rc) {
 		dev_err(chip->pdev,
-			"A TPM error (%zd) occurred attempting to determine the timeouts\n",
-			rc);
-		goto duration;
+			"Error %zd reading %s\n", rc, desc);
+		return -EINVAL;
 	}
 
 	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))
+	    != sizeof(tpm_cmd.header.out) + sizeof(u32) + size * sizeof(u32)) {
+		dev_err(chip->pdev,
+			"Bad return code or length reading %s\n", desc);
 		return -EINVAL;
-
-	old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
-	old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
-	old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
-	old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
-	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
-
-	/*
-	 * Provide ability for vendor overrides of timeout values in case
-	 * of misreporting.
-	 */
-	if (chip->ops->update_timeouts != NULL)
-		chip->vendor.timeout_adjusted =
-			chip->ops->update_timeouts(chip, new_timeout);
-
-	if (!chip->vendor.timeout_adjusted) {
-		/* Don't overwrite default if value is 0 */
-		if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
-			int i;
-
-			/* timeouts in msec rather usec */
-			for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
-				new_timeout[i] *= 1000;
-			chip->vendor.timeout_adjusted = true;
-		}
 	}
 
-	/* Report adjusted timeouts */
-	if (chip->vendor.timeout_adjusted) {
-		dev_info(chip->pdev,
-			 HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
-			 old_timeout[0], new_timeout[0],
-			 old_timeout[1], new_timeout[1],
-			 old_timeout[2], new_timeout[2],
-			 old_timeout[3], new_timeout[3]);
-	}
+	memcpy(cap, &tpm_cmd.params.getcap_out.cap, sizeof(cap_t));
 
-	chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
-	chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
-	chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
-	chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
+	return 0;
+}
 
-duration:
-	tpm_cmd.header.in = tpm_getcap_header;
-	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
-	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
+int tpm_get_timeouts(struct tpm_chip *chip)
+{
+	cap_t cap1, cap2;
+	int rc1, rc2;
+	struct tpm_vendor_specific orig_vendor;
+
+	rc1 = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_TIMEOUT, 4, &cap1,
+			       "timeouts");
+	if (rc1 == 0) {
+		be32_to_cpus(&cap1.timeout.a);
+		be32_to_cpus(&cap1.timeout.b);
+		be32_to_cpus(&cap1.timeout.c);
+		be32_to_cpus(&cap1.timeout.d);
+		chip->vendor.timeout_a = usecs_to_jiffies(cap1.timeout.a);
+		chip->vendor.timeout_b = usecs_to_jiffies(cap1.timeout.b);
+		chip->vendor.timeout_c = usecs_to_jiffies(cap1.timeout.c);
+		chip->vendor.timeout_d = usecs_to_jiffies(cap1.timeout.d);
+	}
+	rc2 = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_DURATION, 3, &cap2,
+			       "durations");
+	if (rc2 == 0) {
+		be32_to_cpus(&cap2.duration.tpm_short);
+		be32_to_cpus(&cap2.duration.tpm_medium);
+		be32_to_cpus(&cap2.duration.tpm_long);
+		chip->vendor.duration[TPM_SHORT] =
+			usecs_to_jiffies(cap2.duration.tpm_short);
+		chip->vendor.duration[TPM_MEDIUM] =
+			usecs_to_jiffies(cap2.duration.tpm_medium);
+		chip->vendor.duration[TPM_LONG] =
+			usecs_to_jiffies(cap2.duration.tpm_long);
+	}
 
-	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
-			      "attempting to determine the durations");
-	if (rc)
-		return rc;
+	memcpy(&orig_vendor, &chip->vendor, sizeof(orig_vendor));
+
+	/* Some TPMs report timeouts in milliseconds rather than
+	   microseconds. Use a value between 1 and 1000 as an
+	   indication that this is the case. */
+	if (rc1 == 0 && cap1.timeout.a > 0 && cap1.timeout.a < 1000) {
+		chip->vendor.timeout_a = msecs_to_jiffies(cap1.timeout.a);
+		chip->vendor.timeout_b = msecs_to_jiffies(cap1.timeout.b);
+		chip->vendor.timeout_c = msecs_to_jiffies(cap1.timeout.c);
+		chip->vendor.timeout_d = msecs_to_jiffies(cap1.timeout.d);
+		chip->vendor.timeout_adjusted = true;
+	}
+	/* Interpret duration values between 1 and 10000 as
+	   milliseconds to deal with TPMs like the Broadcom BCM0102 in
+	   the Dell Latitude D820. */
+	if (rc2 == 0 && cap2.duration.tpm_short > 0 &&
+	    cap2.duration.tpm_short < 10000) {
+		chip->vendor.duration[TPM_SHORT] =
+			msecs_to_jiffies(cap2.duration.tpm_short);
+		chip->vendor.duration[TPM_MEDIUM] =
+			msecs_to_jiffies(cap2.duration.tpm_medium);
+		chip->vendor.duration[TPM_LONG] =
+			msecs_to_jiffies(cap2.duration.tpm_long);
+		chip->vendor.duration_adjusted = true;
+	}
 
-	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 -EINVAL;
+	if (chip->ops->update_timeouts != NULL)
+		chip->ops->update_timeouts(chip);
 
-	duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
-	chip->vendor.duration[TPM_SHORT] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
-	chip->vendor.duration[TPM_MEDIUM] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
-	chip->vendor.duration[TPM_LONG] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
-
-	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
-	 * value wrong and apparently reports msecs rather than usecs. So we
-	 * fix up the resulting too-small TPM_SHORT value to make things work.
-	 * We also scale the TPM_MEDIUM and -_LONG values by 1000.
-	 */
-	if (chip->vendor.duration[TPM_SHORT] < (HZ / 100)) {
-		chip->vendor.duration[TPM_SHORT] = HZ;
-		chip->vendor.duration[TPM_MEDIUM] *= 1000;
-		chip->vendor.duration[TPM_LONG] *= 1000;
-		chip->vendor.duration_adjusted = true;
-		dev_info(chip->pdev, "Adjusting TPM timeout parameters.");
+	if (chip->vendor.timeout_adjusted) {
+		dev_info(chip->pdev,
+			 HW_ERR "Adjusted timeouts: A %u->%uus B %u->%uus"
+			 " C %u->%uus D %u->%uus\n",
+			 jiffies_to_usecs(orig_vendor.timeout_a),
+			 jiffies_to_usecs(chip->vendor.timeout_a),
+			 jiffies_to_usecs(orig_vendor.timeout_b),
+			 jiffies_to_usecs(chip->vendor.timeout_b),
+			 jiffies_to_usecs(orig_vendor.timeout_c),
+			 jiffies_to_usecs(chip->vendor.timeout_c),
+			 jiffies_to_usecs(orig_vendor.timeout_d),
+			 jiffies_to_usecs(chip->vendor.timeout_d));
+	}
+	if (chip->vendor.duration_adjusted) {
+		dev_info(chip->pdev,
+			 HW_ERR "Adjusted durations: short %u->%uus"
+			 " medium %u->%uus long %u->%uus\n",
+			 jiffies_to_usecs(orig_vendor.duration[TPM_SHORT]),
+			 jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
+			 jiffies_to_usecs(orig_vendor.duration[TPM_MEDIUM]),
+			 jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
+			 jiffies_to_usecs(orig_vendor.duration[TPM_LONG]),
+			 jiffies_to_usecs(chip->vendor.duration[TPM_LONG]));
 	}
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(tpm_get_timeouts);
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 088fa86..5c74980 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -475,34 +475,17 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
 	return rc;
 }
 
-struct tis_vendor_timeout_override {
-	u32 did_vid;
-	unsigned long timeout_us[4];
-};
-
-static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
-	/* Atmel 3204 */
-	{ 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
-			(TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
-};
-
-static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
-				    unsigned long *timeout_cap)
+static void tpm_tis_update_timeouts(struct tpm_chip *chip)
 {
-	int i;
-	u32 did_vid;
-
-	did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
-
-	for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
-		if (vendor_timeout_overrides[i].did_vid != did_vid)
-			continue;
-		memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
-		       sizeof(vendor_timeout_overrides[i].timeout_us));
-		return true;
+	switch (ioread32(chip->vendor.iobase + TPM_DID_VID(0))) {
+	case 0x32041114: /* Atmel 3204 */
+		chip->vendor.timeout_a = TIS_SHORT_TIMEOUT * HZ / 1000;
+		chip->vendor.timeout_b = TIS_LONG_TIMEOUT * HZ / 1000;
+		chip->vendor.timeout_c = TIS_SHORT_TIMEOUT * HZ / 1000;
+		chip->vendor.timeout_d = TIS_SHORT_TIMEOUT * HZ / 1000;
+		chip->vendor.timeout_adjusted = true;
+		break;
 	}
-
-	return false;
 }
 
 /*
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 706e63e..2380ebf 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -41,8 +41,7 @@ struct tpm_class_ops {
 	int (*send) (struct tpm_chip *chip, u8 *buf, size_t len);
 	void (*cancel) (struct tpm_chip *chip);
 	u8 (*status) (struct tpm_chip *chip);
-	bool (*update_timeouts)(struct tpm_chip *chip,
-				unsigned long *timeout_cap);
+	void (*update_timeouts)(struct tpm_chip *chip);
 
 };
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v4 4/4] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup
  2016-06-08  0:45 [PATCH v4 0/4] tpm: Command duration logging and chip-specific override Ed Swierk
                   ` (2 preceding siblings ...)
  2016-06-08  0:45 ` [PATCH v4 3/4] tpm: Allow TPM chip drivers to override reported " Ed Swierk
@ 2016-06-08  0:45 ` Ed Swierk
  2016-06-08 23:00 ` [PATCH v5 0/4] tpm: Command duration logging and chip-specific override Ed Swierk
  4 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-08  0:45 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel, linux-security-module

The STMicro ST19NP18-TPM sometimes takes much longer to execute
commands than it reports in its capabilities. For example, command 186
(TPM_FlushSpecific) has been observed to take 14560 msec to complete,
far longer than the 3000 msec limit for "short" commands reported by
the chip. The behavior has also been seen with command 101
(TPM_GetCapability).

Worse, when the tpm_tis driver attempts to cancel the current command
(by writing commandReady = 1 to TPM_STS_x), the chip locks up
completely, returning all-1s from all memory-mapped register
reads. The lockup can be cleared only by resetting the system.

The occurrence of this excessive command duration depends on the
sequence of commands preceding it. One sequence is creating at least 2
new keys via TPM_CreateWrapKey, then letting the TPM idle for at least
30 seconds, then loading a key via TPM_LoadKey2. The next
TPM_FlushSpecific occasionally takes tens of seconds to
complete. Another sequence is creating many keys in a row without
pause. The TPM_CreateWrapKey operation gets much slower after the
first few iterations, as one would expect when the pool of precomputed
keys is exhausted. Then after a 35-second pause, the same TPM_LoadKey2
followed by TPM_FlushSpecific sequence triggers the behavior.

Our working theory is that this older TPM sometimes pauses to
precompute keys, which modern chips implement as a background
process. Without access to the chip's implementation details it's
impossible to know whether any commands are immune to being blocked by
this process. So it seems safest to ignore the chip's reported command
durations, and use a value much higher than any observed duration,
like 180 sec (which is the duration this chip reports for "long"
commands).

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm_tis.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 5c74980..0041622 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -485,6 +485,11 @@ static void tpm_tis_update_timeouts(struct tpm_chip *chip)
 		chip->vendor.timeout_d = TIS_SHORT_TIMEOUT * HZ / 1000;
 		chip->vendor.timeout_adjusted = true;
 		break;
+	case 0x0000104a: /* STMicro ST19NP18-TPM */
+		chip->vendor.duration[TPM_SHORT] = 180 * HZ;
+		chip->vendor.duration[TPM_MEDIUM] = 180 * HZ;
+		chip->vendor.duration[TPM_LONG] = 180 * HZ;
+		chip->vendor.duration_adjusted = true;
 	}
 }
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [tpmdd-devel] [PATCH v4 3/4] tpm: Allow TPM chip drivers to override reported command durations
  2016-06-08  0:45 ` [PATCH v4 3/4] tpm: Allow TPM chip drivers to override reported " Ed Swierk
@ 2016-06-08 19:05   ` Jason Gunthorpe
  2016-06-08 20:41     ` Ed Swierk
  0 siblings, 1 reply; 68+ messages in thread
From: Jason Gunthorpe @ 2016-06-08 19:05 UTC (permalink / raw)
  To: Ed Swierk; +Cc: tpmdd-devel, linux-security-module, linux-kernel

On Tue, Jun 07, 2016 at 05:45:39PM -0700, Ed Swierk wrote:
> +	case 0x32041114: /* Atmel 3204 */
> +		chip->vendor.timeout_a = TIS_SHORT_TIMEOUT * HZ / 1000;
> +		chip->vendor.timeout_b = TIS_LONG_TIMEOUT * HZ / 1000;
> +		chip->vendor.timeout_c = TIS_SHORT_TIMEOUT * HZ / 1000;
> +		chip->vendor.timeout_d = TIS_SHORT_TIMEOUT * HZ / 1000;

Shouldn't these use  msec_to_jiffies?

Jason

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [tpmdd-devel] [PATCH v4 3/4] tpm: Allow TPM chip drivers to override reported command durations
  2016-06-08 19:05   ` [tpmdd-devel] " Jason Gunthorpe
@ 2016-06-08 20:41     ` Ed Swierk
  0 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-08 20:41 UTC (permalink / raw)
  To: Jason Gunthorpe; +Cc: tpmdd-devel, linux-security-module, linux-kernel

On Wed, Jun 8, 2016 at 12:05 PM, Jason Gunthorpe
<jgunthorpe@obsidianresearch.com> wrote:
> On Tue, Jun 07, 2016 at 05:45:39PM -0700, Ed Swierk wrote:
>> +     case 0x32041114: /* Atmel 3204 */
>> +             chip->vendor.timeout_a = TIS_SHORT_TIMEOUT * HZ / 1000;
>> +             chip->vendor.timeout_b = TIS_LONG_TIMEOUT * HZ / 1000;
>> +             chip->vendor.timeout_c = TIS_SHORT_TIMEOUT * HZ / 1000;
>> +             chip->vendor.timeout_d = TIS_SHORT_TIMEOUT * HZ / 1000;
>
> Shouldn't these use  msec_to_jiffies?

Indeed * HZ / 1000 can be one jiffy less than msec_to_jiffies(),
depending on HZ. I'll change it.

--Ed

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v5 0/4] tpm: Command duration logging and chip-specific override
  2016-06-08  0:45 [PATCH v4 0/4] tpm: Command duration logging and chip-specific override Ed Swierk
                   ` (3 preceding siblings ...)
  2016-06-08  0:45 ` [PATCH v4 4/4] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
@ 2016-06-08 23:00 ` Ed Swierk
  2016-06-08 23:00   ` [PATCH v5 1/4] tpm_tis: Improve reporting of IO errors Ed Swierk
                     ` (4 more replies)
  4 siblings, 5 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-08 23:00 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel,
	linux-security-module, jgunthorpe

v5: Use msecs_to_jiffies() instead of * HZ / 1000.

v4: Rework tpm_get_timeouts() to allow overriding both timeouts and
durations via a single callback.

This series
- improves TPM command error reporting
- adds optional logging of TPM command durations
- allows chip-specific override of command durations as well as protocol
  timeouts
- overrides ST19NP18 TPM command duration to avoid lockups

Ed Swierk (4):
  tpm_tis: Improve reporting of IO errors
  tpm: Add optional logging of TPM command durations
  tpm: Allow TPM chip drivers to override reported command durations
  tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup

 drivers/char/tpm/tpm-interface.c | 194 ++++++++++++++++++++++-----------------
 drivers/char/tpm/tpm_tis.c       |  48 +++++-----
 include/linux/tpm.h              |   3 +-
 3 files changed, 131 insertions(+), 114 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v5 1/4] tpm_tis: Improve reporting of IO errors
  2016-06-08 23:00 ` [PATCH v5 0/4] tpm: Command duration logging and chip-specific override Ed Swierk
@ 2016-06-08 23:00   ` Ed Swierk
  2016-06-08 23:00   ` [PATCH v5 2/4] tpm: Add optional logging of TPM command durations Ed Swierk
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-08 23:00 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel,
	linux-security-module, jgunthorpe

Mysterious TPM behavior can be difficult to track down through all the
layers of software. Add error messages for conditions that should
never happen. Also include the manufacturer ID along with other chip
data printed during init.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 drivers/char/tpm/tpm_tis.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 65f7eec..088fa86 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -299,6 +299,8 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 
 	expected = be32_to_cpu(*(__be32 *) (buf + 2));
 	if (expected > count) {
+		dev_err(chip->pdev, "Response too long (wanted %zd, got %d)\n",
+			count, expected);
 		size = -EIO;
 		goto out;
 	}
@@ -366,6 +368,8 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
 				  &chip->vendor.int_queue, false);
 		status = tpm_tis_status(chip);
 		if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
+			dev_err(chip->pdev, "Chip not accepting %zd bytes\n",
+				len - count);
 			rc = -EIO;
 			goto out_err;
 		}
@@ -378,6 +382,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
 			  &chip->vendor.int_queue, false);
 	status = tpm_tis_status(chip);
 	if ((status & TPM_STS_DATA_EXPECT) != 0) {
+		dev_err(chip->pdev, "Chip not accepting last byte\n");
 		rc = -EIO;
 		goto out_err;
 	}
@@ -689,8 +694,9 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
 	vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
 	chip->vendor.manufacturer_id = vendor;
 
-	dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n",
+	dev_info(dev, "%s TPM (manufacturer-id 0x%X, device-id 0x%X, rev-id %d)\n",
 		 (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2",
+		 chip->vendor.manufacturer_id,
 		 vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
 
 	if (!itpm) {
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v5 2/4] tpm: Add optional logging of TPM command durations
  2016-06-08 23:00 ` [PATCH v5 0/4] tpm: Command duration logging and chip-specific override Ed Swierk
  2016-06-08 23:00   ` [PATCH v5 1/4] tpm_tis: Improve reporting of IO errors Ed Swierk
@ 2016-06-08 23:00   ` Ed Swierk
  2016-06-08 23:00   ` [PATCH v5 3/4] tpm: Allow TPM chip drivers to override reported " Ed Swierk
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-08 23:00 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel,
	linux-security-module, jgunthorpe

Some TPMs violate their own advertised command durations. This is much
easier to debug with data about how long each command actually takes
to complete. Add debug messages that can be enabled by running

  echo -n 'module tpm +p' >/sys/kernel/debug/dynamic_debug/control

on a kernel configured with DYNAMIC_DEBUG=y.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 drivers/char/tpm/tpm-interface.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index c50637d..cc1e5bc 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -333,13 +333,14 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 {
 	ssize_t rc;
 	u32 count, ordinal;
-	unsigned long stop;
+	unsigned long start, stop;
 
 	if (bufsiz > TPM_BUFSIZE)
 		bufsiz = TPM_BUFSIZE;
 
 	count = be32_to_cpu(*((__be32 *) (buf + 2)));
 	ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+	dev_dbg(chip->pdev, "starting command %d count %d\n", ordinal, count);
 	if (count == 0)
 		return -ENODATA;
 	if (count > bufsiz) {
@@ -360,18 +361,24 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 	if (chip->vendor.irq)
 		goto out_recv;
 
+	start = jiffies;
 	if (chip->flags & TPM_CHIP_FLAG_TPM2)
-		stop = jiffies + tpm2_calc_ordinal_duration(chip, ordinal);
+		stop = start + tpm2_calc_ordinal_duration(chip, ordinal);
 	else
-		stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
+		stop = start + tpm_calc_ordinal_duration(chip, ordinal);
 	do {
 		u8 status = chip->ops->status(chip);
 		if ((status & chip->ops->req_complete_mask) ==
-		    chip->ops->req_complete_val)
+		    chip->ops->req_complete_val) {
+			dev_dbg(chip->pdev, "completed command %d in %d ms\n",
+				ordinal, jiffies_to_msecs(jiffies - start));
 			goto out_recv;
+		}
 
 		if (chip->ops->req_canceled(chip, status)) {
 			dev_err(chip->pdev, "Operation Canceled\n");
+			dev_dbg(chip->pdev, "canceled command %d after %d ms\n",
+				ordinal, jiffies_to_msecs(jiffies - start));
 			rc = -ECANCELED;
 			goto out;
 		}
@@ -382,6 +389,8 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 
 	chip->ops->cancel(chip);
 	dev_err(chip->pdev, "Operation Timed out\n");
+	dev_dbg(chip->pdev, "command %d timed out after %d ms\n", ordinal,
+		jiffies_to_msecs(jiffies - start));
 	rc = -ETIME;
 	goto out;
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v5 3/4] tpm: Allow TPM chip drivers to override reported command durations
  2016-06-08 23:00 ` [PATCH v5 0/4] tpm: Command duration logging and chip-specific override Ed Swierk
  2016-06-08 23:00   ` [PATCH v5 1/4] tpm_tis: Improve reporting of IO errors Ed Swierk
  2016-06-08 23:00   ` [PATCH v5 2/4] tpm: Add optional logging of TPM command durations Ed Swierk
@ 2016-06-08 23:00   ` Ed Swierk
  2016-06-10 12:19     ` Jarkko Sakkinen
  2016-06-08 23:00   ` [PATCH v5 4/4] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
  2016-06-11  1:55   ` [PATCH v6 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  4 siblings, 1 reply; 68+ messages in thread
From: Ed Swierk @ 2016-06-08 23:00 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel,
	linux-security-module, jgunthorpe

Some TPM chips report bogus command durations in their capabilities,
just as others report incorrect timeouts. Rework tpm_get_timeouts()
to allow chip drivers to override either via a single callback.
Also clean up handling of TPMs that report milliseconds instead of
microseconds.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm-interface.c | 177 +++++++++++++++++++++------------------
 drivers/char/tpm/tpm_tis.c       |  35 ++------
 include/linux/tpm.h              |   3 +-
 3 files changed, 106 insertions(+), 109 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index cc1e5bc..b8a08bb 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -502,123 +502,138 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
 				"attempting to start the TPM");
 }
 
-int tpm_get_timeouts(struct tpm_chip *chip)
+static int tpm_get_cap_prop(struct tpm_chip *chip, __be32 type, int size,
+			    cap_t *cap, char *desc)
 {
 	struct tpm_cmd_t tpm_cmd;
-	unsigned long new_timeout[4];
-	unsigned long old_timeout[4];
-	struct duration_t *duration_cap;
 	ssize_t rc;
 
 	tpm_cmd.header.in = tpm_getcap_header;
 	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
 	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
+	tpm_cmd.params.getcap_in.subcap = type;
 	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
 
 	if (rc == TPM_ERR_INVALID_POSTINIT) {
 		/* The TPM is not started, we are the first to talk to it.
 		   Execute a startup command. */
-		dev_info(chip->pdev, "Issuing TPM_STARTUP");
+		dev_info(chip->pdev, "Issuing TPM_STARTUP\n");
 		if (tpm_startup(chip, TPM_ST_CLEAR))
 			return rc;
 
 		tpm_cmd.header.in = tpm_getcap_header;
 		tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
 		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-		tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
+		tpm_cmd.params.getcap_in.subcap = type;
 		rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
 				  NULL);
 	}
+
 	if (rc) {
 		dev_err(chip->pdev,
-			"A TPM error (%zd) occurred attempting to determine the timeouts\n",
-			rc);
-		goto duration;
+			"Error %zd reading %s\n", rc, desc);
+		return -EINVAL;
 	}
 
 	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))
+	    != sizeof(tpm_cmd.header.out) + sizeof(u32) + size * sizeof(u32)) {
+		dev_err(chip->pdev,
+			"Bad return code or length reading %s\n", desc);
 		return -EINVAL;
-
-	old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
-	old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
-	old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
-	old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
-	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
-
-	/*
-	 * Provide ability for vendor overrides of timeout values in case
-	 * of misreporting.
-	 */
-	if (chip->ops->update_timeouts != NULL)
-		chip->vendor.timeout_adjusted =
-			chip->ops->update_timeouts(chip, new_timeout);
-
-	if (!chip->vendor.timeout_adjusted) {
-		/* Don't overwrite default if value is 0 */
-		if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
-			int i;
-
-			/* timeouts in msec rather usec */
-			for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
-				new_timeout[i] *= 1000;
-			chip->vendor.timeout_adjusted = true;
-		}
 	}
 
-	/* Report adjusted timeouts */
-	if (chip->vendor.timeout_adjusted) {
-		dev_info(chip->pdev,
-			 HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
-			 old_timeout[0], new_timeout[0],
-			 old_timeout[1], new_timeout[1],
-			 old_timeout[2], new_timeout[2],
-			 old_timeout[3], new_timeout[3]);
-	}
+	memcpy(cap, &tpm_cmd.params.getcap_out.cap, sizeof(cap_t));
 
-	chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
-	chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
-	chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
-	chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
+	return 0;
+}
 
-duration:
-	tpm_cmd.header.in = tpm_getcap_header;
-	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
-	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
+int tpm_get_timeouts(struct tpm_chip *chip)
+{
+	cap_t cap1, cap2;
+	int rc1, rc2;
+	struct tpm_vendor_specific orig_vendor;
+
+	rc1 = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_TIMEOUT, 4, &cap1,
+			       "timeouts");
+	if (rc1 == 0) {
+		be32_to_cpus(&cap1.timeout.a);
+		be32_to_cpus(&cap1.timeout.b);
+		be32_to_cpus(&cap1.timeout.c);
+		be32_to_cpus(&cap1.timeout.d);
+		chip->vendor.timeout_a = usecs_to_jiffies(cap1.timeout.a);
+		chip->vendor.timeout_b = usecs_to_jiffies(cap1.timeout.b);
+		chip->vendor.timeout_c = usecs_to_jiffies(cap1.timeout.c);
+		chip->vendor.timeout_d = usecs_to_jiffies(cap1.timeout.d);
+	}
+	rc2 = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_DURATION, 3, &cap2,
+			       "durations");
+	if (rc2 == 0) {
+		be32_to_cpus(&cap2.duration.tpm_short);
+		be32_to_cpus(&cap2.duration.tpm_medium);
+		be32_to_cpus(&cap2.duration.tpm_long);
+		chip->vendor.duration[TPM_SHORT] =
+			usecs_to_jiffies(cap2.duration.tpm_short);
+		chip->vendor.duration[TPM_MEDIUM] =
+			usecs_to_jiffies(cap2.duration.tpm_medium);
+		chip->vendor.duration[TPM_LONG] =
+			usecs_to_jiffies(cap2.duration.tpm_long);
+	}
 
-	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
-			      "attempting to determine the durations");
-	if (rc)
-		return rc;
+	memcpy(&orig_vendor, &chip->vendor, sizeof(orig_vendor));
+
+	/* Some TPMs report timeouts in milliseconds rather than
+	   microseconds. Use a value between 1 and 1000 as an
+	   indication that this is the case. */
+	if (rc1 == 0 && cap1.timeout.a > 0 && cap1.timeout.a < 1000) {
+		chip->vendor.timeout_a = msecs_to_jiffies(cap1.timeout.a);
+		chip->vendor.timeout_b = msecs_to_jiffies(cap1.timeout.b);
+		chip->vendor.timeout_c = msecs_to_jiffies(cap1.timeout.c);
+		chip->vendor.timeout_d = msecs_to_jiffies(cap1.timeout.d);
+		chip->vendor.timeout_adjusted = true;
+	}
+	/* Interpret duration values between 1 and 10000 as
+	   milliseconds to deal with TPMs like the Broadcom BCM0102 in
+	   the Dell Latitude D820. */
+	if (rc2 == 0 && cap2.duration.tpm_short > 0 &&
+	    cap2.duration.tpm_short < 10000) {
+		chip->vendor.duration[TPM_SHORT] =
+			msecs_to_jiffies(cap2.duration.tpm_short);
+		chip->vendor.duration[TPM_MEDIUM] =
+			msecs_to_jiffies(cap2.duration.tpm_medium);
+		chip->vendor.duration[TPM_LONG] =
+			msecs_to_jiffies(cap2.duration.tpm_long);
+		chip->vendor.duration_adjusted = true;
+	}
 
-	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 -EINVAL;
+	if (chip->ops->update_timeouts != NULL)
+		chip->ops->update_timeouts(chip);
 
-	duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
-	chip->vendor.duration[TPM_SHORT] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
-	chip->vendor.duration[TPM_MEDIUM] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
-	chip->vendor.duration[TPM_LONG] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
-
-	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
-	 * value wrong and apparently reports msecs rather than usecs. So we
-	 * fix up the resulting too-small TPM_SHORT value to make things work.
-	 * We also scale the TPM_MEDIUM and -_LONG values by 1000.
-	 */
-	if (chip->vendor.duration[TPM_SHORT] < (HZ / 100)) {
-		chip->vendor.duration[TPM_SHORT] = HZ;
-		chip->vendor.duration[TPM_MEDIUM] *= 1000;
-		chip->vendor.duration[TPM_LONG] *= 1000;
-		chip->vendor.duration_adjusted = true;
-		dev_info(chip->pdev, "Adjusting TPM timeout parameters.");
+	if (chip->vendor.timeout_adjusted) {
+		dev_info(chip->pdev,
+			 HW_ERR "Adjusted timeouts: A %u->%uus B %u->%uus"
+			 " C %u->%uus D %u->%uus\n",
+			 jiffies_to_usecs(orig_vendor.timeout_a),
+			 jiffies_to_usecs(chip->vendor.timeout_a),
+			 jiffies_to_usecs(orig_vendor.timeout_b),
+			 jiffies_to_usecs(chip->vendor.timeout_b),
+			 jiffies_to_usecs(orig_vendor.timeout_c),
+			 jiffies_to_usecs(chip->vendor.timeout_c),
+			 jiffies_to_usecs(orig_vendor.timeout_d),
+			 jiffies_to_usecs(chip->vendor.timeout_d));
+	}
+	if (chip->vendor.duration_adjusted) {
+		dev_info(chip->pdev,
+			 HW_ERR "Adjusted durations: short %u->%uus"
+			 " medium %u->%uus long %u->%uus\n",
+			 jiffies_to_usecs(orig_vendor.duration[TPM_SHORT]),
+			 jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
+			 jiffies_to_usecs(orig_vendor.duration[TPM_MEDIUM]),
+			 jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
+			 jiffies_to_usecs(orig_vendor.duration[TPM_LONG]),
+			 jiffies_to_usecs(chip->vendor.duration[TPM_LONG]));
 	}
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(tpm_get_timeouts);
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 088fa86..caf7278 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -475,34 +475,17 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
 	return rc;
 }
 
-struct tis_vendor_timeout_override {
-	u32 did_vid;
-	unsigned long timeout_us[4];
-};
-
-static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
-	/* Atmel 3204 */
-	{ 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
-			(TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
-};
-
-static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
-				    unsigned long *timeout_cap)
+static void tpm_tis_update_timeouts(struct tpm_chip *chip)
 {
-	int i;
-	u32 did_vid;
-
-	did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
-
-	for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
-		if (vendor_timeout_overrides[i].did_vid != did_vid)
-			continue;
-		memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
-		       sizeof(vendor_timeout_overrides[i].timeout_us));
-		return true;
+	switch (ioread32(chip->vendor.iobase + TPM_DID_VID(0))) {
+	case 0x32041114: /* Atmel 3204 */
+		chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+		chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
+		chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+		chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+		chip->vendor.timeout_adjusted = true;
+		break;
 	}
-
-	return false;
 }
 
 /*
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 706e63e..2380ebf 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -41,8 +41,7 @@ struct tpm_class_ops {
 	int (*send) (struct tpm_chip *chip, u8 *buf, size_t len);
 	void (*cancel) (struct tpm_chip *chip);
 	u8 (*status) (struct tpm_chip *chip);
-	bool (*update_timeouts)(struct tpm_chip *chip,
-				unsigned long *timeout_cap);
+	void (*update_timeouts)(struct tpm_chip *chip);
 
 };
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v5 4/4] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup
  2016-06-08 23:00 ` [PATCH v5 0/4] tpm: Command duration logging and chip-specific override Ed Swierk
                     ` (2 preceding siblings ...)
  2016-06-08 23:00   ` [PATCH v5 3/4] tpm: Allow TPM chip drivers to override reported " Ed Swierk
@ 2016-06-08 23:00   ` Ed Swierk
  2016-06-11  1:55   ` [PATCH v6 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  4 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-08 23:00 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel,
	linux-security-module, jgunthorpe

The STMicro ST19NP18-TPM sometimes takes much longer to execute
commands than it reports in its capabilities. For example, command 186
(TPM_FlushSpecific) has been observed to take 14560 msec to complete,
far longer than the 3000 msec limit for "short" commands reported by
the chip. The behavior has also been seen with command 101
(TPM_GetCapability).

Worse, when the tpm_tis driver attempts to cancel the current command
(by writing commandReady = 1 to TPM_STS_x), the chip locks up
completely, returning all-1s from all memory-mapped register
reads. The lockup can be cleared only by resetting the system.

The occurrence of this excessive command duration depends on the
sequence of commands preceding it. One sequence is creating at least 2
new keys via TPM_CreateWrapKey, then letting the TPM idle for at least
30 seconds, then loading a key via TPM_LoadKey2. The next
TPM_FlushSpecific occasionally takes tens of seconds to
complete. Another sequence is creating many keys in a row without
pause. The TPM_CreateWrapKey operation gets much slower after the
first few iterations, as one would expect when the pool of precomputed
keys is exhausted. Then after a 35-second pause, the same TPM_LoadKey2
followed by TPM_FlushSpecific sequence triggers the behavior.

Our working theory is that this older TPM sometimes pauses to
precompute keys, which modern chips implement as a background
process. Without access to the chip's implementation details it's
impossible to know whether any commands are immune to being blocked by
this process. So it seems safest to ignore the chip's reported command
durations, and use a value much higher than any observed duration,
like 180 sec (which is the duration this chip reports for "long"
commands).

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm_tis.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index caf7278..862c502 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -485,6 +485,11 @@ static void tpm_tis_update_timeouts(struct tpm_chip *chip)
 		chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
 		chip->vendor.timeout_adjusted = true;
 		break;
+	case 0x0000104a: /* STMicro ST19NP18-TPM */
+		chip->vendor.duration[TPM_SHORT] = 180 * HZ;
+		chip->vendor.duration[TPM_MEDIUM] = 180 * HZ;
+		chip->vendor.duration[TPM_LONG] = 180 * HZ;
+		chip->vendor.duration_adjusted = true;
 	}
 }
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v5 3/4] tpm: Allow TPM chip drivers to override reported command durations
  2016-06-08 23:00   ` [PATCH v5 3/4] tpm: Allow TPM chip drivers to override reported " Ed Swierk
@ 2016-06-10 12:19     ` Jarkko Sakkinen
  2016-06-10 17:34       ` Ed Swierk
  0 siblings, 1 reply; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-06-10 12:19 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, stefanb, linux-kernel, linux-security-module, jgunthorpe

On Wed, Jun 08, 2016 at 04:00:17PM -0700, Ed Swierk wrote:
> Some TPM chips report bogus command durations in their capabilities,
> just as others report incorrect timeouts. Rework tpm_get_timeouts()
> to allow chip drivers to override either via a single callback.
> Also clean up handling of TPMs that report milliseconds instead of
> microseconds.
> 
> Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
> ---
>  drivers/char/tpm/tpm-interface.c | 177 +++++++++++++++++++++------------------
>  drivers/char/tpm/tpm_tis.c       |  35 ++------
>  include/linux/tpm.h              |   3 +-
>  3 files changed, 106 insertions(+), 109 deletions(-)
> 
> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
> index cc1e5bc..b8a08bb 100644
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -502,123 +502,138 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
>  				"attempting to start the TPM");
>  }
>  
> -int tpm_get_timeouts(struct tpm_chip *chip)
> +static int tpm_get_cap_prop(struct tpm_chip *chip, __be32 type, int size,
> +			    cap_t *cap, char *desc)
>  {
>  	struct tpm_cmd_t tpm_cmd;
> -	unsigned long new_timeout[4];
> -	unsigned long old_timeout[4];
> -	struct duration_t *duration_cap;
>  	ssize_t rc;
>  
>  	tpm_cmd.header.in = tpm_getcap_header;
>  	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
>  	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> -	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
> +	tpm_cmd.params.getcap_in.subcap = type;
>  	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
>  
>  	if (rc == TPM_ERR_INVALID_POSTINIT) {
>  		/* The TPM is not started, we are the first to talk to it.
>  		   Execute a startup command. */
> -		dev_info(chip->pdev, "Issuing TPM_STARTUP");
> +		dev_info(chip->pdev, "Issuing TPM_STARTUP\n");
>  		if (tpm_startup(chip, TPM_ST_CLEAR))
>  			return rc;
>  
>  		tpm_cmd.header.in = tpm_getcap_header;
>  		tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
>  		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> -		tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
> +		tpm_cmd.params.getcap_in.subcap = type;
>  		rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
>  				  NULL);
>  	}
> +
>  	if (rc) {
>  		dev_err(chip->pdev,
> -			"A TPM error (%zd) occurred attempting to determine the timeouts\n",
> -			rc);
> -		goto duration;
> +			"Error %zd reading %s\n", rc, desc);
> +		return -EINVAL;
>  	}
>  
>  	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))
> +	    != sizeof(tpm_cmd.header.out) + sizeof(u32) + size * sizeof(u32)) {
> +		dev_err(chip->pdev,
> +			"Bad return code or length reading %s\n", desc);
>  		return -EINVAL;
> -
> -	old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
> -	old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
> -	old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
> -	old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
> -	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
> -
> -	/*
> -	 * Provide ability for vendor overrides of timeout values in case
> -	 * of misreporting.
> -	 */
> -	if (chip->ops->update_timeouts != NULL)
> -		chip->vendor.timeout_adjusted =
> -			chip->ops->update_timeouts(chip, new_timeout);
> -
> -	if (!chip->vendor.timeout_adjusted) {
> -		/* Don't overwrite default if value is 0 */
> -		if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
> -			int i;
> -
> -			/* timeouts in msec rather usec */
> -			for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
> -				new_timeout[i] *= 1000;
> -			chip->vendor.timeout_adjusted = true;
> -		}
>  	}
>  
> -	/* Report adjusted timeouts */
> -	if (chip->vendor.timeout_adjusted) {
> -		dev_info(chip->pdev,
> -			 HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
> -			 old_timeout[0], new_timeout[0],
> -			 old_timeout[1], new_timeout[1],
> -			 old_timeout[2], new_timeout[2],
> -			 old_timeout[3], new_timeout[3]);
> -	}
> +	memcpy(cap, &tpm_cmd.params.getcap_out.cap, sizeof(cap_t));
>  
> -	chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
> -	chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
> -	chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
> -	chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
> +	return 0;
> +}
>  
> -duration:
> -	tpm_cmd.header.in = tpm_getcap_header;
> -	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
> -	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> -	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
> +int tpm_get_timeouts(struct tpm_chip *chip)
> +{
> +	cap_t cap1, cap2;
> +	int rc1, rc2;
> +	struct tpm_vendor_specific orig_vendor;
> +
> +	rc1 = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_TIMEOUT, 4, &cap1,
> +			       "timeouts");
> +	if (rc1 == 0) {
> +		be32_to_cpus(&cap1.timeout.a);
> +		be32_to_cpus(&cap1.timeout.b);
> +		be32_to_cpus(&cap1.timeout.c);
> +		be32_to_cpus(&cap1.timeout.d);
> +		chip->vendor.timeout_a = usecs_to_jiffies(cap1.timeout.a);
> +		chip->vendor.timeout_b = usecs_to_jiffies(cap1.timeout.b);
> +		chip->vendor.timeout_c = usecs_to_jiffies(cap1.timeout.c);
> +		chip->vendor.timeout_d = usecs_to_jiffies(cap1.timeout.d);
> +	}
> +	rc2 = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_DURATION, 3, &cap2,
> +			       "durations");
> +	if (rc2 == 0) {
> +		be32_to_cpus(&cap2.duration.tpm_short);
> +		be32_to_cpus(&cap2.duration.tpm_medium);
> +		be32_to_cpus(&cap2.duration.tpm_long);
> +		chip->vendor.duration[TPM_SHORT] =
> +			usecs_to_jiffies(cap2.duration.tpm_short);
> +		chip->vendor.duration[TPM_MEDIUM] =
> +			usecs_to_jiffies(cap2.duration.tpm_medium);
> +		chip->vendor.duration[TPM_LONG] =
> +			usecs_to_jiffies(cap2.duration.tpm_long);
> +	}

This is major change to the semantics. Before -EINVAL would have been
return on error condition.

PS If you want to encapsulate tpm_get_cap_prop(), that step should be
a separate commit (prepend this one).

/Jarkko

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v5 3/4] tpm: Allow TPM chip drivers to override reported command durations
  2016-06-10 12:19     ` Jarkko Sakkinen
@ 2016-06-10 17:34       ` Ed Swierk
  2016-06-10 19:42         ` Jarkko Sakkinen
  0 siblings, 1 reply; 68+ messages in thread
From: Ed Swierk @ 2016-06-10 17:34 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: tpmdd-devel, Stefan Berger, linux-kernel, linux-security-module,
	Jason Gunthorpe

On Fri, Jun 10, 2016 at 5:19 AM, Jarkko Sakkinen
<jarkko.sakkinen@linux.intel.com> wrote:
> On Wed, Jun 08, 2016 at 04:00:17PM -0700, Ed Swierk wrote:
>> Some TPM chips report bogus command durations in their capabilities,
>> just as others report incorrect timeouts. Rework tpm_get_timeouts()
>> to allow chip drivers to override either via a single callback.
>> Also clean up handling of TPMs that report milliseconds instead of
>> microseconds.
>>
>> Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
>> ---
>>  drivers/char/tpm/tpm-interface.c | 177 +++++++++++++++++++++------------------
>>  drivers/char/tpm/tpm_tis.c       |  35 ++------
>>  include/linux/tpm.h              |   3 +-
>>  3 files changed, 106 insertions(+), 109 deletions(-)
>>
>> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
>> index cc1e5bc..b8a08bb 100644
>> --- a/drivers/char/tpm/tpm-interface.c
>> +++ b/drivers/char/tpm/tpm-interface.c
>> @@ -502,123 +502,138 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
>>                               "attempting to start the TPM");
>>  }
>>
>> -int tpm_get_timeouts(struct tpm_chip *chip)
>> +static int tpm_get_cap_prop(struct tpm_chip *chip, __be32 type, int size,
>> +                         cap_t *cap, char *desc)
>>  {
>>       struct tpm_cmd_t tpm_cmd;
>> -     unsigned long new_timeout[4];
>> -     unsigned long old_timeout[4];
>> -     struct duration_t *duration_cap;
>>       ssize_t rc;
>>
>>       tpm_cmd.header.in = tpm_getcap_header;
>>       tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
>>       tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
>> -     tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
>> +     tpm_cmd.params.getcap_in.subcap = type;
>>       rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
>>
>>       if (rc == TPM_ERR_INVALID_POSTINIT) {
>>               /* The TPM is not started, we are the first to talk to it.
>>                  Execute a startup command. */
>> -             dev_info(chip->pdev, "Issuing TPM_STARTUP");
>> +             dev_info(chip->pdev, "Issuing TPM_STARTUP\n");
>>               if (tpm_startup(chip, TPM_ST_CLEAR))
>>                       return rc;
>>
>>               tpm_cmd.header.in = tpm_getcap_header;
>>               tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
>>               tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
>> -             tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
>> +             tpm_cmd.params.getcap_in.subcap = type;
>>               rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
>>                                 NULL);
>>       }
>> +
>>       if (rc) {
>>               dev_err(chip->pdev,
>> -                     "A TPM error (%zd) occurred attempting to determine the timeouts\n",
>> -                     rc);
>> -             goto duration;
>> +                     "Error %zd reading %s\n", rc, desc);
>> +             return -EINVAL;
>>       }
>>
>>       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))
>> +         != sizeof(tpm_cmd.header.out) + sizeof(u32) + size * sizeof(u32)) {
>> +             dev_err(chip->pdev,
>> +                     "Bad return code or length reading %s\n", desc);
>>               return -EINVAL;
>> -
>> -     old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
>> -     old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
>> -     old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
>> -     old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
>> -     memcpy(new_timeout, old_timeout, sizeof(new_timeout));
>> -
>> -     /*
>> -      * Provide ability for vendor overrides of timeout values in case
>> -      * of misreporting.
>> -      */
>> -     if (chip->ops->update_timeouts != NULL)
>> -             chip->vendor.timeout_adjusted =
>> -                     chip->ops->update_timeouts(chip, new_timeout);
>> -
>> -     if (!chip->vendor.timeout_adjusted) {
>> -             /* Don't overwrite default if value is 0 */
>> -             if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
>> -                     int i;
>> -
>> -                     /* timeouts in msec rather usec */
>> -                     for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
>> -                             new_timeout[i] *= 1000;
>> -                     chip->vendor.timeout_adjusted = true;
>> -             }
>>       }
>>
>> -     /* Report adjusted timeouts */
>> -     if (chip->vendor.timeout_adjusted) {
>> -             dev_info(chip->pdev,
>> -                      HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
>> -                      old_timeout[0], new_timeout[0],
>> -                      old_timeout[1], new_timeout[1],
>> -                      old_timeout[2], new_timeout[2],
>> -                      old_timeout[3], new_timeout[3]);
>> -     }
>> +     memcpy(cap, &tpm_cmd.params.getcap_out.cap, sizeof(cap_t));
>>
>> -     chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
>> -     chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
>> -     chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
>> -     chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
>> +     return 0;
>> +}
>>
>> -duration:
>> -     tpm_cmd.header.in = tpm_getcap_header;
>> -     tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
>> -     tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
>> -     tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
>> +int tpm_get_timeouts(struct tpm_chip *chip)
>> +{
>> +     cap_t cap1, cap2;
>> +     int rc1, rc2;
>> +     struct tpm_vendor_specific orig_vendor;
>> +
>> +     rc1 = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_TIMEOUT, 4, &cap1,
>> +                            "timeouts");
>> +     if (rc1 == 0) {
>> +             be32_to_cpus(&cap1.timeout.a);
>> +             be32_to_cpus(&cap1.timeout.b);
>> +             be32_to_cpus(&cap1.timeout.c);
>> +             be32_to_cpus(&cap1.timeout.d);
>> +             chip->vendor.timeout_a = usecs_to_jiffies(cap1.timeout.a);
>> +             chip->vendor.timeout_b = usecs_to_jiffies(cap1.timeout.b);
>> +             chip->vendor.timeout_c = usecs_to_jiffies(cap1.timeout.c);
>> +             chip->vendor.timeout_d = usecs_to_jiffies(cap1.timeout.d);
>> +     }
>> +     rc2 = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_DURATION, 3, &cap2,
>> +                            "durations");
>> +     if (rc2 == 0) {
>> +             be32_to_cpus(&cap2.duration.tpm_short);
>> +             be32_to_cpus(&cap2.duration.tpm_medium);
>> +             be32_to_cpus(&cap2.duration.tpm_long);
>> +             chip->vendor.duration[TPM_SHORT] =
>> +                     usecs_to_jiffies(cap2.duration.tpm_short);
>> +             chip->vendor.duration[TPM_MEDIUM] =
>> +                     usecs_to_jiffies(cap2.duration.tpm_medium);
>> +             chip->vendor.duration[TPM_LONG] =
>> +                     usecs_to_jiffies(cap2.duration.tpm_long);
>> +     }
>
> This is major change to the semantics. Before -EINVAL would have been
> return on error condition.
>
> PS If you want to encapsulate tpm_get_cap_prop(), that step should be
> a separate commit (prepend this one).

Good points.

I'm confused about the error semantics in the first (timeouts) part of
tpm_get_timeouts(). If tpm_transmit_cmd() returns zero and a header
check fails, it returns -EINVAL. But if tpm_transmit_cmd() returns
nonzero, it swallows the error.

In contrast, in the second (durations) part, an error is returned in
either case.

Is this difference intentional, or should tpm_get_timeouts() return
errors immediately in all cases?

--Ed

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v5 3/4] tpm: Allow TPM chip drivers to override reported command durations
  2016-06-10 17:34       ` Ed Swierk
@ 2016-06-10 19:42         ` Jarkko Sakkinen
  2016-06-11  1:54           ` Ed Swierk
  0 siblings, 1 reply; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-06-10 19:42 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, Stefan Berger, linux-kernel, linux-security-module,
	Jason Gunthorpe

On Fri, Jun 10, 2016 at 10:34:15AM -0700, Ed Swierk wrote:
> On Fri, Jun 10, 2016 at 5:19 AM, Jarkko Sakkinen
> <jarkko.sakkinen@linux.intel.com> wrote:
> > On Wed, Jun 08, 2016 at 04:00:17PM -0700, Ed Swierk wrote:
> >> Some TPM chips report bogus command durations in their capabilities,
> >> just as others report incorrect timeouts. Rework tpm_get_timeouts()
> >> to allow chip drivers to override either via a single callback.
> >> Also clean up handling of TPMs that report milliseconds instead of
> >> microseconds.
> >>
> >> Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
> >> ---
> >>  drivers/char/tpm/tpm-interface.c | 177 +++++++++++++++++++++------------------
> >>  drivers/char/tpm/tpm_tis.c       |  35 ++------
> >>  include/linux/tpm.h              |   3 +-
> >>  3 files changed, 106 insertions(+), 109 deletions(-)
> >>
> >> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
> >> index cc1e5bc..b8a08bb 100644
> >> --- a/drivers/char/tpm/tpm-interface.c
> >> +++ b/drivers/char/tpm/tpm-interface.c
> >> @@ -502,123 +502,138 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
> >>                               "attempting to start the TPM");
> >>  }
> >>
> >> -int tpm_get_timeouts(struct tpm_chip *chip)
> >> +static int tpm_get_cap_prop(struct tpm_chip *chip, __be32 type, int size,
> >> +                         cap_t *cap, char *desc)
> >>  {
> >>       struct tpm_cmd_t tpm_cmd;
> >> -     unsigned long new_timeout[4];
> >> -     unsigned long old_timeout[4];
> >> -     struct duration_t *duration_cap;
> >>       ssize_t rc;
> >>
> >>       tpm_cmd.header.in = tpm_getcap_header;
> >>       tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
> >>       tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> >> -     tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
> >> +     tpm_cmd.params.getcap_in.subcap = type;
> >>       rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
> >>
> >>       if (rc == TPM_ERR_INVALID_POSTINIT) {
> >>               /* The TPM is not started, we are the first to talk to it.
> >>                  Execute a startup command. */
> >> -             dev_info(chip->pdev, "Issuing TPM_STARTUP");
> >> +             dev_info(chip->pdev, "Issuing TPM_STARTUP\n");
> >>               if (tpm_startup(chip, TPM_ST_CLEAR))
> >>                       return rc;
> >>
> >>               tpm_cmd.header.in = tpm_getcap_header;
> >>               tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
> >>               tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> >> -             tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
> >> +             tpm_cmd.params.getcap_in.subcap = type;
> >>               rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
> >>                                 NULL);
> >>       }
> >> +
> >>       if (rc) {
> >>               dev_err(chip->pdev,
> >> -                     "A TPM error (%zd) occurred attempting to determine the timeouts\n",
> >> -                     rc);
> >> -             goto duration;
> >> +                     "Error %zd reading %s\n", rc, desc);
> >> +             return -EINVAL;
> >>       }
> >>
> >>       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))
> >> +         != sizeof(tpm_cmd.header.out) + sizeof(u32) + size * sizeof(u32)) {
> >> +             dev_err(chip->pdev,
> >> +                     "Bad return code or length reading %s\n", desc);
> >>               return -EINVAL;
> >> -
> >> -     old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
> >> -     old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
> >> -     old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
> >> -     old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
> >> -     memcpy(new_timeout, old_timeout, sizeof(new_timeout));
> >> -
> >> -     /*
> >> -      * Provide ability for vendor overrides of timeout values in case
> >> -      * of misreporting.
> >> -      */
> >> -     if (chip->ops->update_timeouts != NULL)
> >> -             chip->vendor.timeout_adjusted =
> >> -                     chip->ops->update_timeouts(chip, new_timeout);
> >> -
> >> -     if (!chip->vendor.timeout_adjusted) {
> >> -             /* Don't overwrite default if value is 0 */
> >> -             if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
> >> -                     int i;
> >> -
> >> -                     /* timeouts in msec rather usec */
> >> -                     for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
> >> -                             new_timeout[i] *= 1000;
> >> -                     chip->vendor.timeout_adjusted = true;
> >> -             }
> >>       }
> >>
> >> -     /* Report adjusted timeouts */
> >> -     if (chip->vendor.timeout_adjusted) {
> >> -             dev_info(chip->pdev,
> >> -                      HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
> >> -                      old_timeout[0], new_timeout[0],
> >> -                      old_timeout[1], new_timeout[1],
> >> -                      old_timeout[2], new_timeout[2],
> >> -                      old_timeout[3], new_timeout[3]);
> >> -     }
> >> +     memcpy(cap, &tpm_cmd.params.getcap_out.cap, sizeof(cap_t));
> >>
> >> -     chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
> >> -     chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
> >> -     chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
> >> -     chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
> >> +     return 0;
> >> +}
> >>
> >> -duration:
> >> -     tpm_cmd.header.in = tpm_getcap_header;
> >> -     tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
> >> -     tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> >> -     tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
> >> +int tpm_get_timeouts(struct tpm_chip *chip)
> >> +{
> >> +     cap_t cap1, cap2;
> >> +     int rc1, rc2;
> >> +     struct tpm_vendor_specific orig_vendor;
> >> +
> >> +     rc1 = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_TIMEOUT, 4, &cap1,
> >> +                            "timeouts");
> >> +     if (rc1 == 0) {
> >> +             be32_to_cpus(&cap1.timeout.a);
> >> +             be32_to_cpus(&cap1.timeout.b);
> >> +             be32_to_cpus(&cap1.timeout.c);
> >> +             be32_to_cpus(&cap1.timeout.d);
> >> +             chip->vendor.timeout_a = usecs_to_jiffies(cap1.timeout.a);
> >> +             chip->vendor.timeout_b = usecs_to_jiffies(cap1.timeout.b);
> >> +             chip->vendor.timeout_c = usecs_to_jiffies(cap1.timeout.c);
> >> +             chip->vendor.timeout_d = usecs_to_jiffies(cap1.timeout.d);
> >> +     }
> >> +     rc2 = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_DURATION, 3, &cap2,
> >> +                            "durations");
> >> +     if (rc2 == 0) {
> >> +             be32_to_cpus(&cap2.duration.tpm_short);
> >> +             be32_to_cpus(&cap2.duration.tpm_medium);
> >> +             be32_to_cpus(&cap2.duration.tpm_long);
> >> +             chip->vendor.duration[TPM_SHORT] =
> >> +                     usecs_to_jiffies(cap2.duration.tpm_short);
> >> +             chip->vendor.duration[TPM_MEDIUM] =
> >> +                     usecs_to_jiffies(cap2.duration.tpm_medium);
> >> +             chip->vendor.duration[TPM_LONG] =
> >> +                     usecs_to_jiffies(cap2.duration.tpm_long);
> >> +     }
> >
> > This is major change to the semantics. Before -EINVAL would have been
> > return on error condition.
> >
> > PS If you want to encapsulate tpm_get_cap_prop(), that step should be
> > a separate commit (prepend this one).
> 
> Good points.
> 
> I'm confused about the error semantics in the first (timeouts) part of
> tpm_get_timeouts(). If tpm_transmit_cmd() returns zero and a header
> check fails, it returns -EINVAL. But if tpm_transmit_cmd() returns
> nonzero, it swallows the error.
> 
> In contrast, in the second (durations) part, an error is returned in
> either case.
> 
> Is this difference intentional, or should tpm_get_timeouts() return
> errors immediately in all cases?

Sometimes these kinds of things are just "evolutional" :)

There are also couple of other things that don't look right:

* tpm_transmit_cmd() already prints the TPM error
* If you use dev_err(), you must fail. If we ought to continue, it
  should be at most dev_warn().

In my opinion it is just plain wrong to continue to the durations part
in the case of TPM error. I would like to hear a second opinion, though.

> --Ed

/Jarkko

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v5 3/4] tpm: Allow TPM chip drivers to override reported command durations
  2016-06-10 19:42         ` Jarkko Sakkinen
@ 2016-06-11  1:54           ` Ed Swierk
  0 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-11  1:54 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: tpmdd-devel, Stefan Berger, linux-kernel, linux-security-module,
	Jason Gunthorpe

On Fri, Jun 10, 2016 at 12:42 PM, Jarkko Sakkinen
<jarkko.sakkinen@linux.intel.com> wrote:
> On Fri, Jun 10, 2016 at 10:34:15AM -0700, Ed Swierk wrote:
>> On Fri, Jun 10, 2016 at 5:19 AM, Jarkko Sakkinen
>> <jarkko.sakkinen@linux.intel.com> wrote:
>> > On Wed, Jun 08, 2016 at 04:00:17PM -0700, Ed Swierk wrote:
>> >> Some TPM chips report bogus command durations in their capabilities,
>> >> just as others report incorrect timeouts. Rework tpm_get_timeouts()
>> >> to allow chip drivers to override either via a single callback.
>> >> Also clean up handling of TPMs that report milliseconds instead of
>> >> microseconds.
>> >>
>> >> Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
>> >> ---
>> >>  drivers/char/tpm/tpm-interface.c | 177 +++++++++++++++++++++------------------
>> >>  drivers/char/tpm/tpm_tis.c       |  35 ++------
>> >>  include/linux/tpm.h              |   3 +-
>> >>  3 files changed, 106 insertions(+), 109 deletions(-)
>> >>
>> >> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
>> >> index cc1e5bc..b8a08bb 100644
>> >> --- a/drivers/char/tpm/tpm-interface.c
>> >> +++ b/drivers/char/tpm/tpm-interface.c
>> >> @@ -502,123 +502,138 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
>> >>                               "attempting to start the TPM");
>> >>  }
>> >>
>> >> -int tpm_get_timeouts(struct tpm_chip *chip)
>> >> +static int tpm_get_cap_prop(struct tpm_chip *chip, __be32 type, int size,
>> >> +                         cap_t *cap, char *desc)
>> >>  {
>> >>       struct tpm_cmd_t tpm_cmd;
>> >> -     unsigned long new_timeout[4];
>> >> -     unsigned long old_timeout[4];
>> >> -     struct duration_t *duration_cap;
>> >>       ssize_t rc;
>> >>
>> >>       tpm_cmd.header.in = tpm_getcap_header;
>> >>       tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
>> >>       tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
>> >> -     tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
>> >> +     tpm_cmd.params.getcap_in.subcap = type;
>> >>       rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
>> >>
>> >>       if (rc == TPM_ERR_INVALID_POSTINIT) {
>> >>               /* The TPM is not started, we are the first to talk to it.
>> >>                  Execute a startup command. */
>> >> -             dev_info(chip->pdev, "Issuing TPM_STARTUP");
>> >> +             dev_info(chip->pdev, "Issuing TPM_STARTUP\n");
>> >>               if (tpm_startup(chip, TPM_ST_CLEAR))
>> >>                       return rc;
>> >>
>> >>               tpm_cmd.header.in = tpm_getcap_header;
>> >>               tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
>> >>               tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
>> >> -             tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
>> >> +             tpm_cmd.params.getcap_in.subcap = type;
>> >>               rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
>> >>                                 NULL);
>> >>       }
>> >> +
>> >>       if (rc) {
>> >>               dev_err(chip->pdev,
>> >> -                     "A TPM error (%zd) occurred attempting to determine the timeouts\n",
>> >> -                     rc);
>> >> -             goto duration;
>> >> +                     "Error %zd reading %s\n", rc, desc);
>> >> +             return -EINVAL;
>> >>       }
>> >>
>> >>       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))
>> >> +         != sizeof(tpm_cmd.header.out) + sizeof(u32) + size * sizeof(u32)) {
>> >> +             dev_err(chip->pdev,
>> >> +                     "Bad return code or length reading %s\n", desc);
>> >>               return -EINVAL;
>> >> -
>> >> -     old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
>> >> -     old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
>> >> -     old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
>> >> -     old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
>> >> -     memcpy(new_timeout, old_timeout, sizeof(new_timeout));
>> >> -
>> >> -     /*
>> >> -      * Provide ability for vendor overrides of timeout values in case
>> >> -      * of misreporting.
>> >> -      */
>> >> -     if (chip->ops->update_timeouts != NULL)
>> >> -             chip->vendor.timeout_adjusted =
>> >> -                     chip->ops->update_timeouts(chip, new_timeout);
>> >> -
>> >> -     if (!chip->vendor.timeout_adjusted) {
>> >> -             /* Don't overwrite default if value is 0 */
>> >> -             if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
>> >> -                     int i;
>> >> -
>> >> -                     /* timeouts in msec rather usec */
>> >> -                     for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
>> >> -                             new_timeout[i] *= 1000;
>> >> -                     chip->vendor.timeout_adjusted = true;
>> >> -             }
>> >>       }
>> >>
>> >> -     /* Report adjusted timeouts */
>> >> -     if (chip->vendor.timeout_adjusted) {
>> >> -             dev_info(chip->pdev,
>> >> -                      HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
>> >> -                      old_timeout[0], new_timeout[0],
>> >> -                      old_timeout[1], new_timeout[1],
>> >> -                      old_timeout[2], new_timeout[2],
>> >> -                      old_timeout[3], new_timeout[3]);
>> >> -     }
>> >> +     memcpy(cap, &tpm_cmd.params.getcap_out.cap, sizeof(cap_t));
>> >>
>> >> -     chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
>> >> -     chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
>> >> -     chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
>> >> -     chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
>> >> +     return 0;
>> >> +}
>> >>
>> >> -duration:
>> >> -     tpm_cmd.header.in = tpm_getcap_header;
>> >> -     tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
>> >> -     tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
>> >> -     tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
>> >> +int tpm_get_timeouts(struct tpm_chip *chip)
>> >> +{
>> >> +     cap_t cap1, cap2;
>> >> +     int rc1, rc2;
>> >> +     struct tpm_vendor_specific orig_vendor;
>> >> +
>> >> +     rc1 = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_TIMEOUT, 4, &cap1,
>> >> +                            "timeouts");
>> >> +     if (rc1 == 0) {
>> >> +             be32_to_cpus(&cap1.timeout.a);
>> >> +             be32_to_cpus(&cap1.timeout.b);
>> >> +             be32_to_cpus(&cap1.timeout.c);
>> >> +             be32_to_cpus(&cap1.timeout.d);
>> >> +             chip->vendor.timeout_a = usecs_to_jiffies(cap1.timeout.a);
>> >> +             chip->vendor.timeout_b = usecs_to_jiffies(cap1.timeout.b);
>> >> +             chip->vendor.timeout_c = usecs_to_jiffies(cap1.timeout.c);
>> >> +             chip->vendor.timeout_d = usecs_to_jiffies(cap1.timeout.d);
>> >> +     }
>> >> +     rc2 = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_DURATION, 3, &cap2,
>> >> +                            "durations");
>> >> +     if (rc2 == 0) {
>> >> +             be32_to_cpus(&cap2.duration.tpm_short);
>> >> +             be32_to_cpus(&cap2.duration.tpm_medium);
>> >> +             be32_to_cpus(&cap2.duration.tpm_long);
>> >> +             chip->vendor.duration[TPM_SHORT] =
>> >> +                     usecs_to_jiffies(cap2.duration.tpm_short);
>> >> +             chip->vendor.duration[TPM_MEDIUM] =
>> >> +                     usecs_to_jiffies(cap2.duration.tpm_medium);
>> >> +             chip->vendor.duration[TPM_LONG] =
>> >> +                     usecs_to_jiffies(cap2.duration.tpm_long);
>> >> +     }
>> >
>> > This is major change to the semantics. Before -EINVAL would have been
>> > return on error condition.
>> >
>> > PS If you want to encapsulate tpm_get_cap_prop(), that step should be
>> > a separate commit (prepend this one).
>>
>> Good points.
>>
>> I'm confused about the error semantics in the first (timeouts) part of
>> tpm_get_timeouts(). If tpm_transmit_cmd() returns zero and a header
>> check fails, it returns -EINVAL. But if tpm_transmit_cmd() returns
>> nonzero, it swallows the error.
>>
>> In contrast, in the second (durations) part, an error is returned in
>> either case.
>>
>> Is this difference intentional, or should tpm_get_timeouts() return
>> errors immediately in all cases?
>
> Sometimes these kinds of things are just "evolutional" :)
>
> There are also couple of other things that don't look right:
>
> * tpm_transmit_cmd() already prints the TPM error
> * If you use dev_err(), you must fail. If we ought to continue, it
>   should be at most dev_warn().
>
> In my opinion it is just plain wrong to continue to the durations part
> in the case of TPM error. I would like to hear a second opinion, though.
>
>> --Ed
>
> /Jarkko

I'll split tpm_get_cap_prop() out from tpm_get_timeouts() in a
separate commit, and adjust the error handling on the assumption that
TPM errors should always be propagated.

--Ed

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v6 0/5] tpm: Command duration logging and chip-specific override
  2016-06-08 23:00 ` [PATCH v5 0/4] tpm: Command duration logging and chip-specific override Ed Swierk
                     ` (3 preceding siblings ...)
  2016-06-08 23:00   ` [PATCH v5 4/4] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
@ 2016-06-11  1:55   ` Ed Swierk
  2016-06-11  1:55     ` [PATCH v6 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
                       ` (5 more replies)
  4 siblings, 6 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-11  1:55 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel,
	linux-security-module, jgunthorpe

v6: Split tpm_get_cap_prop() out of tpm_get_timeouts(); always return
error on TPM command failure.

v5: Use msecs_to_jiffies() instead of * HZ / 1000.

v4: Rework tpm_get_timeouts() to allow overriding both timeouts and
durations via a single callback.

This series
- improves TPM command error reporting
- adds optional logging of TPM command durations
- allows chip-specific override of command durations as well as protocol
  timeouts
- overrides ST19NP18 TPM command duration to avoid lockups

Ed Swierk (5):
  tpm_tis: Improve reporting of IO errors
  tpm: Add optional logging of TPM command durations
  tpm: Factor out reading of timeout and duration capabilities
  tpm: Allow TPM chip drivers to override reported command durations
  tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup

 drivers/char/tpm/tpm-interface.c | 192 ++++++++++++++++++++++-----------------
 drivers/char/tpm/tpm_tis.c       |  48 +++++-----
 include/linux/tpm.h              |   3 +-
 3 files changed, 132 insertions(+), 111 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v6 1/5] tpm_tis: Improve reporting of IO errors
  2016-06-11  1:55   ` [PATCH v6 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
@ 2016-06-11  1:55     ` Ed Swierk
  2016-06-11  1:55     ` [PATCH v6 2/5] tpm: Add optional logging of TPM command durations Ed Swierk
                       ` (4 subsequent siblings)
  5 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-11  1:55 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel,
	linux-security-module, jgunthorpe

Mysterious TPM behavior can be difficult to track down through all the
layers of software. Add error messages for conditions that should
never happen. Also include the manufacturer ID along with other chip
data printed during init.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 drivers/char/tpm/tpm_tis.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 65f7eec..088fa86 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -299,6 +299,8 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 
 	expected = be32_to_cpu(*(__be32 *) (buf + 2));
 	if (expected > count) {
+		dev_err(chip->pdev, "Response too long (wanted %zd, got %d)\n",
+			count, expected);
 		size = -EIO;
 		goto out;
 	}
@@ -366,6 +368,8 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
 				  &chip->vendor.int_queue, false);
 		status = tpm_tis_status(chip);
 		if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
+			dev_err(chip->pdev, "Chip not accepting %zd bytes\n",
+				len - count);
 			rc = -EIO;
 			goto out_err;
 		}
@@ -378,6 +382,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
 			  &chip->vendor.int_queue, false);
 	status = tpm_tis_status(chip);
 	if ((status & TPM_STS_DATA_EXPECT) != 0) {
+		dev_err(chip->pdev, "Chip not accepting last byte\n");
 		rc = -EIO;
 		goto out_err;
 	}
@@ -689,8 +694,9 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
 	vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
 	chip->vendor.manufacturer_id = vendor;
 
-	dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n",
+	dev_info(dev, "%s TPM (manufacturer-id 0x%X, device-id 0x%X, rev-id %d)\n",
 		 (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2",
+		 chip->vendor.manufacturer_id,
 		 vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
 
 	if (!itpm) {
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v6 2/5] tpm: Add optional logging of TPM command durations
  2016-06-11  1:55   ` [PATCH v6 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  2016-06-11  1:55     ` [PATCH v6 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
@ 2016-06-11  1:55     ` Ed Swierk
  2016-06-11  1:55     ` [PATCH v6 3/5] tpm: Factor out reading of timeout and duration capabilities Ed Swierk
                       ` (3 subsequent siblings)
  5 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-11  1:55 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel,
	linux-security-module, jgunthorpe

Some TPMs violate their own advertised command durations. This is much
easier to debug with data about how long each command actually takes
to complete. Add debug messages that can be enabled by running

  echo -n 'module tpm +p' >/sys/kernel/debug/dynamic_debug/control

on a kernel configured with DYNAMIC_DEBUG=y.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 drivers/char/tpm/tpm-interface.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index c50637d..cc1e5bc 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -333,13 +333,14 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 {
 	ssize_t rc;
 	u32 count, ordinal;
-	unsigned long stop;
+	unsigned long start, stop;
 
 	if (bufsiz > TPM_BUFSIZE)
 		bufsiz = TPM_BUFSIZE;
 
 	count = be32_to_cpu(*((__be32 *) (buf + 2)));
 	ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+	dev_dbg(chip->pdev, "starting command %d count %d\n", ordinal, count);
 	if (count == 0)
 		return -ENODATA;
 	if (count > bufsiz) {
@@ -360,18 +361,24 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 	if (chip->vendor.irq)
 		goto out_recv;
 
+	start = jiffies;
 	if (chip->flags & TPM_CHIP_FLAG_TPM2)
-		stop = jiffies + tpm2_calc_ordinal_duration(chip, ordinal);
+		stop = start + tpm2_calc_ordinal_duration(chip, ordinal);
 	else
-		stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
+		stop = start + tpm_calc_ordinal_duration(chip, ordinal);
 	do {
 		u8 status = chip->ops->status(chip);
 		if ((status & chip->ops->req_complete_mask) ==
-		    chip->ops->req_complete_val)
+		    chip->ops->req_complete_val) {
+			dev_dbg(chip->pdev, "completed command %d in %d ms\n",
+				ordinal, jiffies_to_msecs(jiffies - start));
 			goto out_recv;
+		}
 
 		if (chip->ops->req_canceled(chip, status)) {
 			dev_err(chip->pdev, "Operation Canceled\n");
+			dev_dbg(chip->pdev, "canceled command %d after %d ms\n",
+				ordinal, jiffies_to_msecs(jiffies - start));
 			rc = -ECANCELED;
 			goto out;
 		}
@@ -382,6 +389,8 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 
 	chip->ops->cancel(chip);
 	dev_err(chip->pdev, "Operation Timed out\n");
+	dev_dbg(chip->pdev, "command %d timed out after %d ms\n", ordinal,
+		jiffies_to_msecs(jiffies - start));
 	rc = -ETIME;
 	goto out;
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v6 3/5] tpm: Factor out reading of timeout and duration capabilities
  2016-06-11  1:55   ` [PATCH v6 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  2016-06-11  1:55     ` [PATCH v6 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
  2016-06-11  1:55     ` [PATCH v6 2/5] tpm: Add optional logging of TPM command durations Ed Swierk
@ 2016-06-11  1:55     ` Ed Swierk
  2016-06-16 20:20       ` Jarkko Sakkinen
  2016-06-19 12:12       ` Jarkko Sakkinen
  2016-06-11  1:55     ` [PATCH v6 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
                       ` (2 subsequent siblings)
  5 siblings, 2 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-11  1:55 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel,
	linux-security-module, jgunthorpe

Factor sending the TPM_GetCapability command and validating the result
from tpm_get_timeouts() into a new function. Return all errors to the
caller rather than swallowing them (e.g. when tpm_transmit_cmd()
returns nonzero).

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm-interface.c | 96 ++++++++++++++++++++++------------------
 1 file changed, 52 insertions(+), 44 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index cc1e5bc..4d1f62c 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -502,6 +502,52 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
 				"attempting to start the TPM");
 }
 
+static int tpm_get_cap_prop(struct tpm_chip *chip, __be32 type, int size,
+			    cap_t *cap, char *desc)
+{
+	struct tpm_cmd_t tpm_cmd;
+	ssize_t rc;
+
+	tpm_cmd.header.in = tpm_getcap_header;
+	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+	tpm_cmd.params.getcap_in.subcap = type;
+	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
+
+	if (rc == TPM_ERR_INVALID_POSTINIT) {
+		/* The TPM is not started, we are the first to talk to it.
+		   Execute a startup command. */
+		dev_info(chip->pdev, "Issuing TPM_STARTUP\n");
+		if (tpm_startup(chip, TPM_ST_CLEAR))
+			return rc;
+
+		tpm_cmd.header.in = tpm_getcap_header;
+		tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+		tpm_cmd.params.getcap_in.subcap = type;
+		rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
+				  NULL);
+	}
+
+	if (rc) {
+		dev_err(chip->pdev,
+			"Error %zd reading %s\n", rc, desc);
+		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) + size * sizeof(u32)) {
+		dev_err(chip->pdev,
+			"Bad return code or length reading %s\n", desc);
+		return -EINVAL;
+	}
+
+	memcpy(cap, &tpm_cmd.params.getcap_out.cap, sizeof(cap_t));
+
+	return 0;
+}
+
 int tpm_get_timeouts(struct tpm_chip *chip)
 {
 	struct tpm_cmd_t tpm_cmd;
@@ -510,37 +556,10 @@ int tpm_get_timeouts(struct tpm_chip *chip)
 	struct duration_t *duration_cap;
 	ssize_t rc;
 
-	tpm_cmd.header.in = tpm_getcap_header;
-	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
-	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
-	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
-
-	if (rc == TPM_ERR_INVALID_POSTINIT) {
-		/* The TPM is not started, we are the first to talk to it.
-		   Execute a startup command. */
-		dev_info(chip->pdev, "Issuing TPM_STARTUP");
-		if (tpm_startup(chip, TPM_ST_CLEAR))
-			return rc;
-
-		tpm_cmd.header.in = tpm_getcap_header;
-		tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
-		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-		tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
-		rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
-				  NULL);
-	}
-	if (rc) {
-		dev_err(chip->pdev,
-			"A TPM error (%zd) occurred attempting to determine the timeouts\n",
-			rc);
-		goto duration;
-	}
-
-	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 -EINVAL;
+	rc = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_TIMEOUT, 4,
+			      &tpm_cmd.params.getcap_out.cap, "timeouts");
+	if (rc)
+		return rc;
 
 	old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
 	old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
@@ -583,22 +602,11 @@ int tpm_get_timeouts(struct tpm_chip *chip)
 	chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
 	chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
 
-duration:
-	tpm_cmd.header.in = tpm_getcap_header;
-	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
-	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
-
-	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
-			      "attempting to determine the durations");
+	rc = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_DURATION, 3,
+			      &tpm_cmd.params.getcap_out.cap, "durations");
 	if (rc)
 		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 -EINVAL;
-
 	duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
 	chip->vendor.duration[TPM_SHORT] =
 	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v6 4/5] tpm: Allow TPM chip drivers to override reported command durations
  2016-06-11  1:55   ` [PATCH v6 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
                       ` (2 preceding siblings ...)
  2016-06-11  1:55     ` [PATCH v6 3/5] tpm: Factor out reading of timeout and duration capabilities Ed Swierk
@ 2016-06-11  1:55     ` Ed Swierk
  2016-06-16 20:26       ` Jarkko Sakkinen
  2016-06-11  1:55     ` [PATCH v6 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
  2016-06-21  1:53     ` [PATCH v7 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  5 siblings, 1 reply; 68+ messages in thread
From: Ed Swierk @ 2016-06-11  1:55 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel,
	linux-security-module, jgunthorpe

Some TPM chips report bogus command durations in their capabilities,
just as others report incorrect timeouts. Rework tpm_get_timeouts() to
allow chip drivers to override either via a single callback. Also
clean up handling of TPMs that report milliseconds instead of
microseconds.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm-interface.c | 139 +++++++++++++++++++++------------------
 drivers/char/tpm/tpm_tis.c       |  35 +++-------
 include/linux/tpm.h              |   3 +-
 3 files changed, 85 insertions(+), 92 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 4d1f62c..a14adfd 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -550,83 +550,94 @@ static int tpm_get_cap_prop(struct tpm_chip *chip, __be32 type, int size,
 
 int tpm_get_timeouts(struct tpm_chip *chip)
 {
-	struct tpm_cmd_t tpm_cmd;
-	unsigned long new_timeout[4];
-	unsigned long old_timeout[4];
-	struct duration_t *duration_cap;
-	ssize_t rc;
+	cap_t cap1, cap2;
+	int rc;
+	struct tpm_vendor_specific orig_vendor;
 
-	rc = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_TIMEOUT, 4,
-			      &tpm_cmd.params.getcap_out.cap, "timeouts");
+	rc = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_TIMEOUT, 4, &cap1,
+			      "timeouts");
 	if (rc)
 		return rc;
 
-	old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
-	old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
-	old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
-	old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
-	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
+	rc = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_DURATION, 3, &cap2,
+			       "durations");
+	if (rc)
+		return rc;
 
-	/*
-	 * Provide ability for vendor overrides of timeout values in case
-	 * of misreporting.
-	 */
-	if (chip->ops->update_timeouts != NULL)
-		chip->vendor.timeout_adjusted =
-			chip->ops->update_timeouts(chip, new_timeout);
+	be32_to_cpus(&cap1.timeout.a);
+	be32_to_cpus(&cap1.timeout.b);
+	be32_to_cpus(&cap1.timeout.c);
+	be32_to_cpus(&cap1.timeout.d);
+	chip->vendor.timeout_a = usecs_to_jiffies(cap1.timeout.a);
+	chip->vendor.timeout_b = usecs_to_jiffies(cap1.timeout.b);
+	chip->vendor.timeout_c = usecs_to_jiffies(cap1.timeout.c);
+	chip->vendor.timeout_d = usecs_to_jiffies(cap1.timeout.d);
 
-	if (!chip->vendor.timeout_adjusted) {
-		/* Don't overwrite default if value is 0 */
-		if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
-			int i;
-
-			/* timeouts in msec rather usec */
-			for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
-				new_timeout[i] *= 1000;
-			chip->vendor.timeout_adjusted = true;
-		}
+	/* Some TPMs report timeouts in milliseconds rather than
+	   microseconds. Use a value between 1 and 1000 as an
+	   indication that this is the case. */
+	if (cap1.timeout.a > 0 && cap1.timeout.a < 1000) {
+		chip->vendor.timeout_a = msecs_to_jiffies(cap1.timeout.a);
+		chip->vendor.timeout_b = msecs_to_jiffies(cap1.timeout.b);
+		chip->vendor.timeout_c = msecs_to_jiffies(cap1.timeout.c);
+		chip->vendor.timeout_d = msecs_to_jiffies(cap1.timeout.d);
+		chip->vendor.timeout_adjusted = true;
 	}
 
-	/* Report adjusted timeouts */
+	be32_to_cpus(&cap2.duration.tpm_short);
+	be32_to_cpus(&cap2.duration.tpm_medium);
+	be32_to_cpus(&cap2.duration.tpm_long);
+	chip->vendor.duration[TPM_SHORT] =
+		usecs_to_jiffies(cap2.duration.tpm_short);
+	chip->vendor.duration[TPM_MEDIUM] =
+		usecs_to_jiffies(cap2.duration.tpm_medium);
+	chip->vendor.duration[TPM_LONG] =
+		usecs_to_jiffies(cap2.duration.tpm_long);
+
+	memcpy(&orig_vendor, &chip->vendor, sizeof(orig_vendor));
+
+	/* Interpret duration values between 1 and 10000 as
+	   milliseconds to deal with TPMs like the Broadcom BCM0102 in
+	   the Dell Latitude D820. */
+	if (cap2.duration.tpm_short > 0 && cap2.duration.tpm_short < 10000) {
+		chip->vendor.duration[TPM_SHORT] =
+			msecs_to_jiffies(cap2.duration.tpm_short);
+		chip->vendor.duration[TPM_MEDIUM] =
+			msecs_to_jiffies(cap2.duration.tpm_medium);
+		chip->vendor.duration[TPM_LONG] =
+			msecs_to_jiffies(cap2.duration.tpm_long);
+		chip->vendor.duration_adjusted = true;
+	}
+
+	if (chip->ops->update_timeouts != NULL)
+		chip->ops->update_timeouts(chip);
+
 	if (chip->vendor.timeout_adjusted) {
 		dev_info(chip->pdev,
-			 HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
-			 old_timeout[0], new_timeout[0],
-			 old_timeout[1], new_timeout[1],
-			 old_timeout[2], new_timeout[2],
-			 old_timeout[3], new_timeout[3]);
+			 HW_ERR "Adjusted timeouts: A %u->%uus B %u->%uus"
+			 " C %u->%uus D %u->%uus\n",
+			 jiffies_to_usecs(orig_vendor.timeout_a),
+			 jiffies_to_usecs(chip->vendor.timeout_a),
+			 jiffies_to_usecs(orig_vendor.timeout_b),
+			 jiffies_to_usecs(chip->vendor.timeout_b),
+			 jiffies_to_usecs(orig_vendor.timeout_c),
+			 jiffies_to_usecs(chip->vendor.timeout_c),
+			 jiffies_to_usecs(orig_vendor.timeout_d),
+			 jiffies_to_usecs(chip->vendor.timeout_d));
 	}
 
-	chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
-	chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
-	chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
-	chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
-
-	rc = tpm_get_cap_prop(chip, TPM_CAP_PROP_TIS_DURATION, 3,
-			      &tpm_cmd.params.getcap_out.cap, "durations");
-	if (rc)
-		return rc;
-
-	duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
-	chip->vendor.duration[TPM_SHORT] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
-	chip->vendor.duration[TPM_MEDIUM] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
-	chip->vendor.duration[TPM_LONG] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
-
-	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
-	 * value wrong and apparently reports msecs rather than usecs. So we
-	 * fix up the resulting too-small TPM_SHORT value to make things work.
-	 * We also scale the TPM_MEDIUM and -_LONG values by 1000.
-	 */
-	if (chip->vendor.duration[TPM_SHORT] < (HZ / 100)) {
-		chip->vendor.duration[TPM_SHORT] = HZ;
-		chip->vendor.duration[TPM_MEDIUM] *= 1000;
-		chip->vendor.duration[TPM_LONG] *= 1000;
-		chip->vendor.duration_adjusted = true;
-		dev_info(chip->pdev, "Adjusting TPM timeout parameters.");
+	if (chip->vendor.duration_adjusted) {
+		dev_info(chip->pdev,
+			 HW_ERR "Adjusted durations: short %u->%uus"
+			 " medium %u->%uus long %u->%uus\n",
+			 jiffies_to_usecs(orig_vendor.duration[TPM_SHORT]),
+			 jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
+			 jiffies_to_usecs(orig_vendor.duration[TPM_MEDIUM]),
+			 jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
+			 jiffies_to_usecs(orig_vendor.duration[TPM_LONG]),
+			 jiffies_to_usecs(chip->vendor.duration[TPM_LONG]));
 	}
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(tpm_get_timeouts);
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 088fa86..caf7278 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -475,34 +475,17 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
 	return rc;
 }
 
-struct tis_vendor_timeout_override {
-	u32 did_vid;
-	unsigned long timeout_us[4];
-};
-
-static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
-	/* Atmel 3204 */
-	{ 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
-			(TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
-};
-
-static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
-				    unsigned long *timeout_cap)
+static void tpm_tis_update_timeouts(struct tpm_chip *chip)
 {
-	int i;
-	u32 did_vid;
-
-	did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
-
-	for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
-		if (vendor_timeout_overrides[i].did_vid != did_vid)
-			continue;
-		memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
-		       sizeof(vendor_timeout_overrides[i].timeout_us));
-		return true;
+	switch (ioread32(chip->vendor.iobase + TPM_DID_VID(0))) {
+	case 0x32041114: /* Atmel 3204 */
+		chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+		chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
+		chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+		chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+		chip->vendor.timeout_adjusted = true;
+		break;
 	}
-
-	return false;
 }
 
 /*
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 706e63e..2380ebf 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -41,8 +41,7 @@ struct tpm_class_ops {
 	int (*send) (struct tpm_chip *chip, u8 *buf, size_t len);
 	void (*cancel) (struct tpm_chip *chip);
 	u8 (*status) (struct tpm_chip *chip);
-	bool (*update_timeouts)(struct tpm_chip *chip,
-				unsigned long *timeout_cap);
+	void (*update_timeouts)(struct tpm_chip *chip);
 
 };
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v6 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup
  2016-06-11  1:55   ` [PATCH v6 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
                       ` (3 preceding siblings ...)
  2016-06-11  1:55     ` [PATCH v6 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
@ 2016-06-11  1:55     ` Ed Swierk
  2016-06-21  1:53     ` [PATCH v7 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  5 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-11  1:55 UTC (permalink / raw)
  To: tpmdd-devel
  Cc: eswierk, stefanb, jarkko.sakkinen, linux-kernel,
	linux-security-module, jgunthorpe

The STMicro ST19NP18-TPM sometimes takes much longer to execute
commands than it reports in its capabilities. For example, command 186
(TPM_FlushSpecific) has been observed to take 14560 msec to complete,
far longer than the 3000 msec limit for "short" commands reported by
the chip. The behavior has also been seen with command 101
(TPM_GetCapability).

Worse, when the tpm_tis driver attempts to cancel the current command
(by writing commandReady = 1 to TPM_STS_x), the chip locks up
completely, returning all-1s from all memory-mapped register
reads. The lockup can be cleared only by resetting the system.

The occurrence of this excessive command duration depends on the
sequence of commands preceding it. One sequence is creating at least 2
new keys via TPM_CreateWrapKey, then letting the TPM idle for at least
30 seconds, then loading a key via TPM_LoadKey2. The next
TPM_FlushSpecific occasionally takes tens of seconds to
complete. Another sequence is creating many keys in a row without
pause. The TPM_CreateWrapKey operation gets much slower after the
first few iterations, as one would expect when the pool of precomputed
keys is exhausted. Then after a 35-second pause, the same TPM_LoadKey2
followed by TPM_FlushSpecific sequence triggers the behavior.

Our working theory is that this older TPM sometimes pauses to
precompute keys, which modern chips implement as a background
process. Without access to the chip's implementation details it's
impossible to know whether any commands are immune to being blocked by
this process. So it seems safest to ignore the chip's reported command
durations, and use a value much higher than any observed duration,
like 180 sec (which is the duration this chip reports for "long"
commands).

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm_tis.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index caf7278..862c502 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -485,6 +485,11 @@ static void tpm_tis_update_timeouts(struct tpm_chip *chip)
 		chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
 		chip->vendor.timeout_adjusted = true;
 		break;
+	case 0x0000104a: /* STMicro ST19NP18-TPM */
+		chip->vendor.duration[TPM_SHORT] = 180 * HZ;
+		chip->vendor.duration[TPM_MEDIUM] = 180 * HZ;
+		chip->vendor.duration[TPM_LONG] = 180 * HZ;
+		chip->vendor.duration_adjusted = true;
 	}
 }
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v6 3/5] tpm: Factor out reading of timeout and duration capabilities
  2016-06-11  1:55     ` [PATCH v6 3/5] tpm: Factor out reading of timeout and duration capabilities Ed Swierk
@ 2016-06-16 20:20       ` Jarkko Sakkinen
  2016-06-19 12:12       ` Jarkko Sakkinen
  1 sibling, 0 replies; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-06-16 20:20 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, stefanb, linux-kernel, linux-security-module, jgunthorpe

On Fri, Jun 10, 2016 at 06:55:05PM -0700, Ed Swierk wrote:
> Factor sending the TPM_GetCapability command and validating the result
> from tpm_get_timeouts() into a new function. Return all errors to the
> caller rather than swallowing them (e.g. when tpm_transmit_cmd()
> returns nonzero).

LGTM but I have to test this on next week. I'll give final
Reviewed/Tested-by after that. Thanks for the good work.

> Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
> ---
>  drivers/char/tpm/tpm-interface.c | 96 ++++++++++++++++++++++------------------
>  1 file changed, 52 insertions(+), 44 deletions(-)

/Jarkko

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v6 4/5] tpm: Allow TPM chip drivers to override reported command durations
  2016-06-11  1:55     ` [PATCH v6 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
@ 2016-06-16 20:26       ` Jarkko Sakkinen
  0 siblings, 0 replies; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-06-16 20:26 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, stefanb, linux-kernel, linux-security-module, jgunthorpe

On Fri, Jun 10, 2016 at 06:55:06PM -0700, Ed Swierk wrote:
> Some TPM chips report bogus command durations in their capabilities,
> just as others report incorrect timeouts. Rework tpm_get_timeouts() to
> allow chip drivers to override either via a single callback. Also
> clean up handling of TPMs that report milliseconds instead of
> microseconds.
> 
> Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>

Again, LGTM but have to test :)

> ---
>  drivers/char/tpm/tpm-interface.c | 139 +++++++++++++++++++++------------------
>  drivers/char/tpm/tpm_tis.c       |  35 +++-------
>  include/linux/tpm.h              |   3 +-
>  3 files changed, 85 insertions(+), 92 deletions(-)

/Jarkko

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v6 3/5] tpm: Factor out reading of timeout and duration capabilities
  2016-06-11  1:55     ` [PATCH v6 3/5] tpm: Factor out reading of timeout and duration capabilities Ed Swierk
  2016-06-16 20:20       ` Jarkko Sakkinen
@ 2016-06-19 12:12       ` Jarkko Sakkinen
  1 sibling, 0 replies; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-06-19 12:12 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, stefanb, linux-kernel, linux-security-module, jgunthorpe

On Fri, Jun 10, 2016 at 06:55:05PM -0700, Ed Swierk wrote:
> Factor sending the TPM_GetCapability command and validating the result
> from tpm_get_timeouts() into a new function. Return all errors to the
> caller rather than swallowing them (e.g. when tpm_transmit_cmd()
> returns nonzero).
> 
> Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
> ---
>  drivers/char/tpm/tpm-interface.c | 96 ++++++++++++++++++++++------------------
>  1 file changed, 52 insertions(+), 44 deletions(-)

I'm sorry but just now that I started applying these patches this patch
started to bother me.

> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
> index cc1e5bc..4d1f62c 100644
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -502,6 +502,52 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
>  				"attempting to start the TPM");
>  }
>  
> +static int tpm_get_cap_prop(struct tpm_chip *chip, __be32 type, int size,
> +			    cap_t *cap, char *desc)
> +{
> +	struct tpm_cmd_t tpm_cmd;
> +	ssize_t rc;
> +
> +	tpm_cmd.header.in = tpm_getcap_header;
> +	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
> +	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> +	tpm_cmd.params.getcap_in.subcap = type;
> +	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
> +
> +	if (rc == TPM_ERR_INVALID_POSTINIT) {
> +		/* The TPM is not started, we are the first to talk to it.
> +		   Execute a startup command. */
> +		dev_info(chip->pdev, "Issuing TPM_STARTUP\n");
> +		if (tpm_startup(chip, TPM_ST_CLEAR))
> +			return rc;
> +
> +		tpm_cmd.header.in = tpm_getcap_header;
> +		tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
> +		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> +		tpm_cmd.params.getcap_in.subcap = type;
> +		rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
> +				  NULL);
> +	}

I think inside tpm_get_timeouts() I'd rather something along the lines
(with error handling and such details taken away):

rc = tpm_getcap(...);

if (rc == TPM_ERR_INVALID_POSTINIT) {
	tpm_startup(...);
	tpm_getca(...);
}


> +	if (rc) {
> +		dev_err(chip->pdev,
> +			"Error %zd reading %s\n", rc, desc);
> +		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) + size * sizeof(u32)) {
> +		dev_err(chip->pdev,
> +			"Bad return code or length reading %s\n", desc);
> +		return -EINVAL;
> +	}

This is bogus code. All this kind of checks should be contained in
tpm_transmit_cmd(). This is easily "fixed" by moving tpm_getcap() :)

/Jarkko

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v7 0/5] tpm: Command duration logging and chip-specific override
  2016-06-11  1:55   ` [PATCH v6 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
                       ` (4 preceding siblings ...)
  2016-06-11  1:55     ` [PATCH v6 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
@ 2016-06-21  1:53     ` Ed Swierk
  2016-06-21  1:53       ` [PATCH v7 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
                         ` (5 more replies)
  5 siblings, 6 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-21  1:53 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

v7: Use tpm_getcap() instead of a redundant new function.

v6: Split tpm_get_cap_prop() out of tpm_get_timeouts(); always return
error on TPM command failure.

v5: Use msecs_to_jiffies() instead of * HZ / 1000.

v4: Rework tpm_get_timeouts() to allow overriding both timeouts and
durations via a single callback.

This series
- improves TPM command error reporting
- adds optional logging of TPM command durations
- allows chip-specific override of command durations as well as protocol
  timeouts
- overrides ST19NP18 TPM command duration to avoid lockups

Ed Swierk (5):
  tpm_tis: Improve reporting of IO errors
  tpm: Add optional logging of TPM command durations
  tpm: Clean up reading of timeout and duration capabilities
  tpm: Allow TPM chip drivers to override reported command durations
  tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup

 drivers/char/tpm/tpm-interface.c | 212 ++++++++++++++++++++-------------------
 drivers/char/tpm/tpm_tis.c       |  49 ++++-----
 include/linux/tpm.h              |   3 +-
 3 files changed, 130 insertions(+), 134 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v7 1/5] tpm_tis: Improve reporting of IO errors
  2016-06-21  1:53     ` [PATCH v7 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
@ 2016-06-21  1:53       ` Ed Swierk
  2016-06-21  1:53       ` [PATCH v7 2/5] tpm: Add optional logging of TPM command durations Ed Swierk
                         ` (4 subsequent siblings)
  5 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-21  1:53 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

Mysterious TPM behavior can be difficult to track down through all the
layers of software. Add error messages for conditions that should
never happen. Also include the manufacturer ID along with other chip
data printed during init.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 drivers/char/tpm/tpm_tis.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 65f7eec..088fa86 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -299,6 +299,8 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 
 	expected = be32_to_cpu(*(__be32 *) (buf + 2));
 	if (expected > count) {
+		dev_err(chip->pdev, "Response too long (wanted %zd, got %d)\n",
+			count, expected);
 		size = -EIO;
 		goto out;
 	}
@@ -366,6 +368,8 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
 				  &chip->vendor.int_queue, false);
 		status = tpm_tis_status(chip);
 		if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
+			dev_err(chip->pdev, "Chip not accepting %zd bytes\n",
+				len - count);
 			rc = -EIO;
 			goto out_err;
 		}
@@ -378,6 +382,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
 			  &chip->vendor.int_queue, false);
 	status = tpm_tis_status(chip);
 	if ((status & TPM_STS_DATA_EXPECT) != 0) {
+		dev_err(chip->pdev, "Chip not accepting last byte\n");
 		rc = -EIO;
 		goto out_err;
 	}
@@ -689,8 +694,9 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
 	vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
 	chip->vendor.manufacturer_id = vendor;
 
-	dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n",
+	dev_info(dev, "%s TPM (manufacturer-id 0x%X, device-id 0x%X, rev-id %d)\n",
 		 (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2",
+		 chip->vendor.manufacturer_id,
 		 vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
 
 	if (!itpm) {
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v7 2/5] tpm: Add optional logging of TPM command durations
  2016-06-21  1:53     ` [PATCH v7 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  2016-06-21  1:53       ` [PATCH v7 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
@ 2016-06-21  1:53       ` Ed Swierk
  2016-06-21  1:54       ` [PATCH v7 3/5] tpm: Clean up reading of timeout and duration capabilities Ed Swierk
                         ` (3 subsequent siblings)
  5 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-21  1:53 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

Some TPMs violate their own advertised command durations. This is much
easier to debug with data about how long each command actually takes
to complete. Add debug messages that can be enabled by running

  echo -n 'module tpm +p' >/sys/kernel/debug/dynamic_debug/control

on a kernel configured with DYNAMIC_DEBUG=y.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 drivers/char/tpm/tpm-interface.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index c50637d..cc1e5bc 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -333,13 +333,14 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 {
 	ssize_t rc;
 	u32 count, ordinal;
-	unsigned long stop;
+	unsigned long start, stop;
 
 	if (bufsiz > TPM_BUFSIZE)
 		bufsiz = TPM_BUFSIZE;
 
 	count = be32_to_cpu(*((__be32 *) (buf + 2)));
 	ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+	dev_dbg(chip->pdev, "starting command %d count %d\n", ordinal, count);
 	if (count == 0)
 		return -ENODATA;
 	if (count > bufsiz) {
@@ -360,18 +361,24 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 	if (chip->vendor.irq)
 		goto out_recv;
 
+	start = jiffies;
 	if (chip->flags & TPM_CHIP_FLAG_TPM2)
-		stop = jiffies + tpm2_calc_ordinal_duration(chip, ordinal);
+		stop = start + tpm2_calc_ordinal_duration(chip, ordinal);
 	else
-		stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
+		stop = start + tpm_calc_ordinal_duration(chip, ordinal);
 	do {
 		u8 status = chip->ops->status(chip);
 		if ((status & chip->ops->req_complete_mask) ==
-		    chip->ops->req_complete_val)
+		    chip->ops->req_complete_val) {
+			dev_dbg(chip->pdev, "completed command %d in %d ms\n",
+				ordinal, jiffies_to_msecs(jiffies - start));
 			goto out_recv;
+		}
 
 		if (chip->ops->req_canceled(chip, status)) {
 			dev_err(chip->pdev, "Operation Canceled\n");
+			dev_dbg(chip->pdev, "canceled command %d after %d ms\n",
+				ordinal, jiffies_to_msecs(jiffies - start));
 			rc = -ECANCELED;
 			goto out;
 		}
@@ -382,6 +389,8 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 
 	chip->ops->cancel(chip);
 	dev_err(chip->pdev, "Operation Timed out\n");
+	dev_dbg(chip->pdev, "command %d timed out after %d ms\n", ordinal,
+		jiffies_to_msecs(jiffies - start));
 	rc = -ETIME;
 	goto out;
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v7 3/5] tpm: Clean up reading of timeout and duration capabilities
  2016-06-21  1:53     ` [PATCH v7 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  2016-06-21  1:53       ` [PATCH v7 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
  2016-06-21  1:53       ` [PATCH v7 2/5] tpm: Add optional logging of TPM command durations Ed Swierk
@ 2016-06-21  1:54       ` Ed Swierk
  2016-06-21 20:52         ` Jarkko Sakkinen
  2016-06-22  0:21         ` Ed Swierk
  2016-06-21  1:54       ` [PATCH v7 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
                         ` (2 subsequent siblings)
  5 siblings, 2 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-21  1:54 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

Call tpm_getcap() from tpm_get_timeouts() to eliminate redundant
code. Return all errors to the caller rather than swallowing them
(e.g. when tpm_transmit_cmd() returns nonzero).

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm-interface.c | 74 +++++++++++++++-------------------------
 1 file changed, 27 insertions(+), 47 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index cc1e5bc..73c3ee0 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -461,9 +461,19 @@ ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap,
 		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
 		tpm_cmd.params.getcap_in.subcap = subcap_id;
 	}
+
 	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
+
+	if (!rc &&
+	    ((subcap_id == TPM_CAP_PROP_TIS_TIMEOUT &&
+	      be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 5) ||
+	     (subcap_id == TPM_CAP_PROP_TIS_DURATION &&
+	      be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 4)))
+		rc = -EINVAL;
+
 	if (!rc)
 		*cap = tpm_cmd.params.getcap_out.cap;
+
 	return rc;
 }
 
@@ -504,48 +514,30 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
 
 int tpm_get_timeouts(struct tpm_chip *chip)
 {
-	struct tpm_cmd_t tpm_cmd;
+	cap_t cap;
 	unsigned long new_timeout[4];
 	unsigned long old_timeout[4];
-	struct duration_t *duration_cap;
 	ssize_t rc;
 
-	tpm_cmd.header.in = tpm_getcap_header;
-	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
-	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
-	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
-
+	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
+			"attempting to determine the timeouts");
 	if (rc == TPM_ERR_INVALID_POSTINIT) {
 		/* The TPM is not started, we are the first to talk to it.
 		   Execute a startup command. */
-		dev_info(chip->pdev, "Issuing TPM_STARTUP");
+		dev_info(chip->pdev, "Issuing TPM_STARTUP\n");
 		if (tpm_startup(chip, TPM_ST_CLEAR))
 			return rc;
 
-		tpm_cmd.header.in = tpm_getcap_header;
-		tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
-		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-		tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
-		rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
-				  NULL);
-	}
-	if (rc) {
-		dev_err(chip->pdev,
-			"A TPM error (%zd) occurred attempting to determine the timeouts\n",
-			rc);
-		goto duration;
+		rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
+				"attempting to determine the timeouts");
 	}
+	if (rc)
+		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) + 4 * sizeof(u32))
-		return -EINVAL;
-
-	old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
-	old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
-	old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
-	old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
+	old_timeout[0] = be32_to_cpu(cap.timeout.a);
+	old_timeout[1] = be32_to_cpu(cap.timeout.b);
+	old_timeout[2] = be32_to_cpu(cap.timeout.c);
+	old_timeout[3] = be32_to_cpu(cap.timeout.d);
 	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
 
 	/*
@@ -583,29 +575,17 @@ int tpm_get_timeouts(struct tpm_chip *chip)
 	chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
 	chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
 
-duration:
-	tpm_cmd.header.in = tpm_getcap_header;
-	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
-	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
-
-	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
-			      "attempting to determine the durations");
+	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_DURATION, &cap,
+			"attempting to determine the durations");
 	if (rc)
 		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 -EINVAL;
-
-	duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
 	chip->vendor.duration[TPM_SHORT] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
+		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_short));
 	chip->vendor.duration[TPM_MEDIUM] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
+		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
 	chip->vendor.duration[TPM_LONG] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
+		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
 
 	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
 	 * value wrong and apparently reports msecs rather than usecs. So we
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v7 4/5] tpm: Allow TPM chip drivers to override reported command durations
  2016-06-21  1:53     ` [PATCH v7 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
                         ` (2 preceding siblings ...)
  2016-06-21  1:54       ` [PATCH v7 3/5] tpm: Clean up reading of timeout and duration capabilities Ed Swierk
@ 2016-06-21  1:54       ` Ed Swierk
  2016-06-21 20:54         ` Jarkko Sakkinen
  2016-06-21  1:54       ` [PATCH v7 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
  2016-06-22  1:10       ` [PATCH v8 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  5 siblings, 1 reply; 68+ messages in thread
From: Ed Swierk @ 2016-06-21  1:54 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

Some TPM chips report bogus command durations in their capabilities,
just as others report incorrect timeouts. Rework tpm_get_timeouts() to
allow chip drivers to override either via a single callback. Also
clean up handling of TPMs that report milliseconds instead of
microseconds.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm-interface.c | 143 +++++++++++++++++++++------------------
 drivers/char/tpm/tpm_tis.c       |  35 +++-------
 include/linux/tpm.h              |   3 +-
 3 files changed, 88 insertions(+), 93 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 73c3ee0..36a6861 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -514,12 +514,11 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
 
 int tpm_get_timeouts(struct tpm_chip *chip)
 {
-	cap_t cap;
-	unsigned long new_timeout[4];
-	unsigned long old_timeout[4];
-	ssize_t rc;
+	cap_t cap1, cap2;
+	int rc;
+	struct tpm_vendor_specific orig_vendor;
 
-	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
+	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap1,
 			"attempting to determine the timeouts");
 	if (rc == TPM_ERR_INVALID_POSTINIT) {
 		/* The TPM is not started, we are the first to talk to it.
@@ -528,77 +527,91 @@ int tpm_get_timeouts(struct tpm_chip *chip)
 		if (tpm_startup(chip, TPM_ST_CLEAR))
 			return rc;
 
-		rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
+		rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap1,
 				"attempting to determine the timeouts");
 	}
 	if (rc)
 		return rc;
 
-	old_timeout[0] = be32_to_cpu(cap.timeout.a);
-	old_timeout[1] = be32_to_cpu(cap.timeout.b);
-	old_timeout[2] = be32_to_cpu(cap.timeout.c);
-	old_timeout[3] = be32_to_cpu(cap.timeout.d);
-	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
-
-	/*
-	 * Provide ability for vendor overrides of timeout values in case
-	 * of misreporting.
-	 */
-	if (chip->ops->update_timeouts != NULL)
-		chip->vendor.timeout_adjusted =
-			chip->ops->update_timeouts(chip, new_timeout);
-
-	if (!chip->vendor.timeout_adjusted) {
-		/* Don't overwrite default if value is 0 */
-		if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
-			int i;
-
-			/* timeouts in msec rather usec */
-			for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
-				new_timeout[i] *= 1000;
-			chip->vendor.timeout_adjusted = true;
-		}
-	}
-
-	/* Report adjusted timeouts */
-	if (chip->vendor.timeout_adjusted) {
-		dev_info(chip->pdev,
-			 HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
-			 old_timeout[0], new_timeout[0],
-			 old_timeout[1], new_timeout[1],
-			 old_timeout[2], new_timeout[2],
-			 old_timeout[3], new_timeout[3]);
-	}
-
-	chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
-	chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
-	chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
-	chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
-
-	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_DURATION, &cap,
+	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_DURATION, &cap2,
 			"attempting to determine the durations");
 	if (rc)
 		return rc;
 
-	chip->vendor.duration[TPM_SHORT] =
-		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_short));
-	chip->vendor.duration[TPM_MEDIUM] =
-		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
-	chip->vendor.duration[TPM_LONG] =
-		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
+	be32_to_cpus(&cap1.timeout.a);
+	be32_to_cpus(&cap1.timeout.b);
+	be32_to_cpus(&cap1.timeout.c);
+	be32_to_cpus(&cap1.timeout.d);
+	chip->vendor.timeout_a = usecs_to_jiffies(cap1.timeout.a);
+	chip->vendor.timeout_b = usecs_to_jiffies(cap1.timeout.b);
+	chip->vendor.timeout_c = usecs_to_jiffies(cap1.timeout.c);
+	chip->vendor.timeout_d = usecs_to_jiffies(cap1.timeout.d);
 
-	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
-	 * value wrong and apparently reports msecs rather than usecs. So we
-	 * fix up the resulting too-small TPM_SHORT value to make things work.
-	 * We also scale the TPM_MEDIUM and -_LONG values by 1000.
-	 */
-	if (chip->vendor.duration[TPM_SHORT] < (HZ / 100)) {
-		chip->vendor.duration[TPM_SHORT] = HZ;
-		chip->vendor.duration[TPM_MEDIUM] *= 1000;
-		chip->vendor.duration[TPM_LONG] *= 1000;
-		chip->vendor.duration_adjusted = true;
-		dev_info(chip->pdev, "Adjusting TPM timeout parameters.");
+	/* Some TPMs report timeouts in milliseconds rather than
+	   microseconds. Use a value between 1 and 1000 as an
+	   indication that this is the case. */
+	if (cap1.timeout.a > 0 && cap1.timeout.a < 1000) {
+		chip->vendor.timeout_a = msecs_to_jiffies(cap1.timeout.a);
+		chip->vendor.timeout_b = msecs_to_jiffies(cap1.timeout.b);
+		chip->vendor.timeout_c = msecs_to_jiffies(cap1.timeout.c);
+		chip->vendor.timeout_d = msecs_to_jiffies(cap1.timeout.d);
+		chip->vendor.timeout_adjusted = true;
 	}
+
+	be32_to_cpus(&cap2.duration.tpm_short);
+	be32_to_cpus(&cap2.duration.tpm_medium);
+	be32_to_cpus(&cap2.duration.tpm_long);
+	chip->vendor.duration[TPM_SHORT] =
+		usecs_to_jiffies(cap2.duration.tpm_short);
+	chip->vendor.duration[TPM_MEDIUM] =
+		usecs_to_jiffies(cap2.duration.tpm_medium);
+	chip->vendor.duration[TPM_LONG] =
+		usecs_to_jiffies(cap2.duration.tpm_long);
+
+	memcpy(&orig_vendor, &chip->vendor, sizeof(orig_vendor));
+
+	/* Interpret duration values between 1 and 10000 as
+	   milliseconds to deal with TPMs like the Broadcom BCM0102 in
+	   the Dell Latitude D820. */
+	if (cap2.duration.tpm_short > 0 && cap2.duration.tpm_short < 10000) {
+		chip->vendor.duration[TPM_SHORT] =
+			msecs_to_jiffies(cap2.duration.tpm_short);
+		chip->vendor.duration[TPM_MEDIUM] =
+			msecs_to_jiffies(cap2.duration.tpm_medium);
+		chip->vendor.duration[TPM_LONG] =
+			msecs_to_jiffies(cap2.duration.tpm_long);
+		chip->vendor.duration_adjusted = true;
+	}
+
+	if (chip->ops->update_timeouts != NULL)
+		chip->ops->update_timeouts(chip);
+
+	if (chip->vendor.timeout_adjusted) {
+		dev_info(chip->pdev,
+			 HW_ERR "Adjusted timeouts: A %u->%uus B %u->%uus"
+			 " C %u->%uus D %u->%uus\n",
+			 jiffies_to_usecs(orig_vendor.timeout_a),
+			 jiffies_to_usecs(chip->vendor.timeout_a),
+			 jiffies_to_usecs(orig_vendor.timeout_b),
+			 jiffies_to_usecs(chip->vendor.timeout_b),
+			 jiffies_to_usecs(orig_vendor.timeout_c),
+			 jiffies_to_usecs(chip->vendor.timeout_c),
+			 jiffies_to_usecs(orig_vendor.timeout_d),
+			 jiffies_to_usecs(chip->vendor.timeout_d));
+	}
+
+	if (chip->vendor.duration_adjusted) {
+		dev_info(chip->pdev,
+			 HW_ERR "Adjusted durations: short %u->%uus"
+			 " medium %u->%uus long %u->%uus\n",
+			 jiffies_to_usecs(orig_vendor.duration[TPM_SHORT]),
+			 jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
+			 jiffies_to_usecs(orig_vendor.duration[TPM_MEDIUM]),
+			 jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
+			 jiffies_to_usecs(orig_vendor.duration[TPM_LONG]),
+			 jiffies_to_usecs(chip->vendor.duration[TPM_LONG]));
+	}
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(tpm_get_timeouts);
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 088fa86..caf7278 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -475,34 +475,17 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
 	return rc;
 }
 
-struct tis_vendor_timeout_override {
-	u32 did_vid;
-	unsigned long timeout_us[4];
-};
-
-static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
-	/* Atmel 3204 */
-	{ 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
-			(TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
-};
-
-static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
-				    unsigned long *timeout_cap)
+static void tpm_tis_update_timeouts(struct tpm_chip *chip)
 {
-	int i;
-	u32 did_vid;
-
-	did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
-
-	for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
-		if (vendor_timeout_overrides[i].did_vid != did_vid)
-			continue;
-		memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
-		       sizeof(vendor_timeout_overrides[i].timeout_us));
-		return true;
+	switch (ioread32(chip->vendor.iobase + TPM_DID_VID(0))) {
+	case 0x32041114: /* Atmel 3204 */
+		chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+		chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
+		chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+		chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+		chip->vendor.timeout_adjusted = true;
+		break;
 	}
-
-	return false;
 }
 
 /*
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 706e63e..2380ebf 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -41,8 +41,7 @@ struct tpm_class_ops {
 	int (*send) (struct tpm_chip *chip, u8 *buf, size_t len);
 	void (*cancel) (struct tpm_chip *chip);
 	u8 (*status) (struct tpm_chip *chip);
-	bool (*update_timeouts)(struct tpm_chip *chip,
-				unsigned long *timeout_cap);
+	void (*update_timeouts)(struct tpm_chip *chip);
 
 };
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v7 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup
  2016-06-21  1:53     ` [PATCH v7 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
                         ` (3 preceding siblings ...)
  2016-06-21  1:54       ` [PATCH v7 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
@ 2016-06-21  1:54       ` Ed Swierk
  2016-06-21 20:55         ` Jarkko Sakkinen
  2016-06-22  1:10       ` [PATCH v8 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  5 siblings, 1 reply; 68+ messages in thread
From: Ed Swierk @ 2016-06-21  1:54 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

The STMicro ST19NP18-TPM sometimes takes much longer to execute
commands than it reports in its capabilities. For example, command 186
(TPM_FlushSpecific) has been observed to take 14560 msec to complete,
far longer than the 3000 msec limit for "short" commands reported by
the chip. The behavior has also been seen with command 101
(TPM_GetCapability).

Worse, when the tpm_tis driver attempts to cancel the current command
(by writing commandReady = 1 to TPM_STS_x), the chip locks up
completely, returning all-1s from all memory-mapped register
reads. The lockup can be cleared only by resetting the system.

The occurrence of this excessive command duration depends on the
sequence of commands preceding it. One sequence is creating at least 2
new keys via TPM_CreateWrapKey, then letting the TPM idle for at least
30 seconds, then loading a key via TPM_LoadKey2. The next
TPM_FlushSpecific occasionally takes tens of seconds to
complete. Another sequence is creating many keys in a row without
pause. The TPM_CreateWrapKey operation gets much slower after the
first few iterations, as one would expect when the pool of precomputed
keys is exhausted. Then after a 35-second pause, the same TPM_LoadKey2
followed by TPM_FlushSpecific sequence triggers the behavior.

Our working theory is that this older TPM sometimes pauses to
precompute keys, which modern chips implement as a background
process. Without access to the chip's implementation details it's
impossible to know whether any commands are immune to being blocked by
this process. So it seems safest to ignore the chip's reported command
durations, and use a value much higher than any observed duration,
like 180 sec (which is the duration this chip reports for "long"
commands).

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm_tis.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index caf7278..8355b45 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -485,6 +485,12 @@ static void tpm_tis_update_timeouts(struct tpm_chip *chip)
 		chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
 		chip->vendor.timeout_adjusted = true;
 		break;
+	case 0x0000104a: /* STMicro ST19NP18-TPM */
+		chip->vendor.duration[TPM_SHORT] = 180 * HZ;
+		chip->vendor.duration[TPM_MEDIUM] = 180 * HZ;
+		chip->vendor.duration[TPM_LONG] = 180 * HZ;
+		chip->vendor.duration_adjusted = true;
+		break;
 	}
 }
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v7 3/5] tpm: Clean up reading of timeout and duration capabilities
  2016-06-21  1:54       ` [PATCH v7 3/5] tpm: Clean up reading of timeout and duration capabilities Ed Swierk
@ 2016-06-21 20:52         ` Jarkko Sakkinen
  2016-06-22  0:21         ` Ed Swierk
  1 sibling, 0 replies; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-06-21 20:52 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, linux-kernel, linux-security-module, jgunthorpe, stefanb

On Mon, Jun 20, 2016 at 06:54:00PM -0700, Ed Swierk wrote:
> Call tpm_getcap() from tpm_get_timeouts() to eliminate redundant
> code. Return all errors to the caller rather than swallowing them
> (e.g. when tpm_transmit_cmd() returns nonzero).
> 
> Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>

Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>

/Jarkko

> ---
>  drivers/char/tpm/tpm-interface.c | 74 +++++++++++++++-------------------------
>  1 file changed, 27 insertions(+), 47 deletions(-)
> 
> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
> index cc1e5bc..73c3ee0 100644
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -461,9 +461,19 @@ ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap,
>  		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
>  		tpm_cmd.params.getcap_in.subcap = subcap_id;
>  	}
> +
>  	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
> +
> +	if (!rc &&
> +	    ((subcap_id == TPM_CAP_PROP_TIS_TIMEOUT &&
> +	      be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 5) ||
> +	     (subcap_id == TPM_CAP_PROP_TIS_DURATION &&
> +	      be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 4)))
> +		rc = -EINVAL;
> +
>  	if (!rc)
>  		*cap = tpm_cmd.params.getcap_out.cap;
> +
>  	return rc;
>  }
>  
> @@ -504,48 +514,30 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
>  
>  int tpm_get_timeouts(struct tpm_chip *chip)
>  {
> -	struct tpm_cmd_t tpm_cmd;
> +	cap_t cap;
>  	unsigned long new_timeout[4];
>  	unsigned long old_timeout[4];
> -	struct duration_t *duration_cap;
>  	ssize_t rc;
>  
> -	tpm_cmd.header.in = tpm_getcap_header;
> -	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
> -	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> -	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
> -	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
> -
> +	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
> +			"attempting to determine the timeouts");
>  	if (rc == TPM_ERR_INVALID_POSTINIT) {
>  		/* The TPM is not started, we are the first to talk to it.
>  		   Execute a startup command. */
> -		dev_info(chip->pdev, "Issuing TPM_STARTUP");
> +		dev_info(chip->pdev, "Issuing TPM_STARTUP\n");
>  		if (tpm_startup(chip, TPM_ST_CLEAR))
>  			return rc;
>  
> -		tpm_cmd.header.in = tpm_getcap_header;
> -		tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
> -		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> -		tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
> -		rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
> -				  NULL);
> -	}
> -	if (rc) {
> -		dev_err(chip->pdev,
> -			"A TPM error (%zd) occurred attempting to determine the timeouts\n",
> -			rc);
> -		goto duration;
> +		rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
> +				"attempting to determine the timeouts");
>  	}
> +	if (rc)
> +		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) + 4 * sizeof(u32))
> -		return -EINVAL;
> -
> -	old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
> -	old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
> -	old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
> -	old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
> +	old_timeout[0] = be32_to_cpu(cap.timeout.a);
> +	old_timeout[1] = be32_to_cpu(cap.timeout.b);
> +	old_timeout[2] = be32_to_cpu(cap.timeout.c);
> +	old_timeout[3] = be32_to_cpu(cap.timeout.d);
>  	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
>  
>  	/*
> @@ -583,29 +575,17 @@ int tpm_get_timeouts(struct tpm_chip *chip)
>  	chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
>  	chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
>  
> -duration:
> -	tpm_cmd.header.in = tpm_getcap_header;
> -	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
> -	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> -	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
> -
> -	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
> -			      "attempting to determine the durations");
> +	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_DURATION, &cap,
> +			"attempting to determine the durations");
>  	if (rc)
>  		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 -EINVAL;
> -
> -	duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
>  	chip->vendor.duration[TPM_SHORT] =
> -	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
> +		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_short));
>  	chip->vendor.duration[TPM_MEDIUM] =
> -	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
> +		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
>  	chip->vendor.duration[TPM_LONG] =
> -	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
> +		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
>  
>  	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
>  	 * value wrong and apparently reports msecs rather than usecs. So we
> -- 
> 1.9.1
> 

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v7 4/5] tpm: Allow TPM chip drivers to override reported command durations
  2016-06-21  1:54       ` [PATCH v7 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
@ 2016-06-21 20:54         ` Jarkko Sakkinen
  0 siblings, 0 replies; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-06-21 20:54 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, linux-kernel, linux-security-module, jgunthorpe, stefanb

On Mon, Jun 20, 2016 at 06:54:01PM -0700, Ed Swierk wrote:
> Some TPM chips report bogus command durations in their capabilities,
> just as others report incorrect timeouts. Rework tpm_get_timeouts() to
> allow chip drivers to override either via a single callback. Also
> clean up handling of TPMs that report milliseconds instead of
> microseconds.
> 
> Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>

Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>

/Jarkko

> ---
>  drivers/char/tpm/tpm-interface.c | 143 +++++++++++++++++++++------------------
>  drivers/char/tpm/tpm_tis.c       |  35 +++-------
>  include/linux/tpm.h              |   3 +-
>  3 files changed, 88 insertions(+), 93 deletions(-)
> 
> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
> index 73c3ee0..36a6861 100644
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -514,12 +514,11 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
>  
>  int tpm_get_timeouts(struct tpm_chip *chip)
>  {
> -	cap_t cap;
> -	unsigned long new_timeout[4];
> -	unsigned long old_timeout[4];
> -	ssize_t rc;
> +	cap_t cap1, cap2;
> +	int rc;
> +	struct tpm_vendor_specific orig_vendor;
>  
> -	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
> +	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap1,
>  			"attempting to determine the timeouts");
>  	if (rc == TPM_ERR_INVALID_POSTINIT) {
>  		/* The TPM is not started, we are the first to talk to it.
> @@ -528,77 +527,91 @@ int tpm_get_timeouts(struct tpm_chip *chip)
>  		if (tpm_startup(chip, TPM_ST_CLEAR))
>  			return rc;
>  
> -		rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
> +		rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap1,
>  				"attempting to determine the timeouts");
>  	}
>  	if (rc)
>  		return rc;
>  
> -	old_timeout[0] = be32_to_cpu(cap.timeout.a);
> -	old_timeout[1] = be32_to_cpu(cap.timeout.b);
> -	old_timeout[2] = be32_to_cpu(cap.timeout.c);
> -	old_timeout[3] = be32_to_cpu(cap.timeout.d);
> -	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
> -
> -	/*
> -	 * Provide ability for vendor overrides of timeout values in case
> -	 * of misreporting.
> -	 */
> -	if (chip->ops->update_timeouts != NULL)
> -		chip->vendor.timeout_adjusted =
> -			chip->ops->update_timeouts(chip, new_timeout);
> -
> -	if (!chip->vendor.timeout_adjusted) {
> -		/* Don't overwrite default if value is 0 */
> -		if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
> -			int i;
> -
> -			/* timeouts in msec rather usec */
> -			for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
> -				new_timeout[i] *= 1000;
> -			chip->vendor.timeout_adjusted = true;
> -		}
> -	}
> -
> -	/* Report adjusted timeouts */
> -	if (chip->vendor.timeout_adjusted) {
> -		dev_info(chip->pdev,
> -			 HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
> -			 old_timeout[0], new_timeout[0],
> -			 old_timeout[1], new_timeout[1],
> -			 old_timeout[2], new_timeout[2],
> -			 old_timeout[3], new_timeout[3]);
> -	}
> -
> -	chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
> -	chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
> -	chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
> -	chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
> -
> -	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_DURATION, &cap,
> +	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_DURATION, &cap2,
>  			"attempting to determine the durations");
>  	if (rc)
>  		return rc;
>  
> -	chip->vendor.duration[TPM_SHORT] =
> -		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_short));
> -	chip->vendor.duration[TPM_MEDIUM] =
> -		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
> -	chip->vendor.duration[TPM_LONG] =
> -		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
> +	be32_to_cpus(&cap1.timeout.a);
> +	be32_to_cpus(&cap1.timeout.b);
> +	be32_to_cpus(&cap1.timeout.c);
> +	be32_to_cpus(&cap1.timeout.d);
> +	chip->vendor.timeout_a = usecs_to_jiffies(cap1.timeout.a);
> +	chip->vendor.timeout_b = usecs_to_jiffies(cap1.timeout.b);
> +	chip->vendor.timeout_c = usecs_to_jiffies(cap1.timeout.c);
> +	chip->vendor.timeout_d = usecs_to_jiffies(cap1.timeout.d);
>  
> -	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
> -	 * value wrong and apparently reports msecs rather than usecs. So we
> -	 * fix up the resulting too-small TPM_SHORT value to make things work.
> -	 * We also scale the TPM_MEDIUM and -_LONG values by 1000.
> -	 */
> -	if (chip->vendor.duration[TPM_SHORT] < (HZ / 100)) {
> -		chip->vendor.duration[TPM_SHORT] = HZ;
> -		chip->vendor.duration[TPM_MEDIUM] *= 1000;
> -		chip->vendor.duration[TPM_LONG] *= 1000;
> -		chip->vendor.duration_adjusted = true;
> -		dev_info(chip->pdev, "Adjusting TPM timeout parameters.");
> +	/* Some TPMs report timeouts in milliseconds rather than
> +	   microseconds. Use a value between 1 and 1000 as an
> +	   indication that this is the case. */
> +	if (cap1.timeout.a > 0 && cap1.timeout.a < 1000) {
> +		chip->vendor.timeout_a = msecs_to_jiffies(cap1.timeout.a);
> +		chip->vendor.timeout_b = msecs_to_jiffies(cap1.timeout.b);
> +		chip->vendor.timeout_c = msecs_to_jiffies(cap1.timeout.c);
> +		chip->vendor.timeout_d = msecs_to_jiffies(cap1.timeout.d);
> +		chip->vendor.timeout_adjusted = true;
>  	}
> +
> +	be32_to_cpus(&cap2.duration.tpm_short);
> +	be32_to_cpus(&cap2.duration.tpm_medium);
> +	be32_to_cpus(&cap2.duration.tpm_long);
> +	chip->vendor.duration[TPM_SHORT] =
> +		usecs_to_jiffies(cap2.duration.tpm_short);
> +	chip->vendor.duration[TPM_MEDIUM] =
> +		usecs_to_jiffies(cap2.duration.tpm_medium);
> +	chip->vendor.duration[TPM_LONG] =
> +		usecs_to_jiffies(cap2.duration.tpm_long);
> +
> +	memcpy(&orig_vendor, &chip->vendor, sizeof(orig_vendor));
> +
> +	/* Interpret duration values between 1 and 10000 as
> +	   milliseconds to deal with TPMs like the Broadcom BCM0102 in
> +	   the Dell Latitude D820. */
> +	if (cap2.duration.tpm_short > 0 && cap2.duration.tpm_short < 10000) {
> +		chip->vendor.duration[TPM_SHORT] =
> +			msecs_to_jiffies(cap2.duration.tpm_short);
> +		chip->vendor.duration[TPM_MEDIUM] =
> +			msecs_to_jiffies(cap2.duration.tpm_medium);
> +		chip->vendor.duration[TPM_LONG] =
> +			msecs_to_jiffies(cap2.duration.tpm_long);
> +		chip->vendor.duration_adjusted = true;
> +	}
> +
> +	if (chip->ops->update_timeouts != NULL)
> +		chip->ops->update_timeouts(chip);
> +
> +	if (chip->vendor.timeout_adjusted) {
> +		dev_info(chip->pdev,
> +			 HW_ERR "Adjusted timeouts: A %u->%uus B %u->%uus"
> +			 " C %u->%uus D %u->%uus\n",
> +			 jiffies_to_usecs(orig_vendor.timeout_a),
> +			 jiffies_to_usecs(chip->vendor.timeout_a),
> +			 jiffies_to_usecs(orig_vendor.timeout_b),
> +			 jiffies_to_usecs(chip->vendor.timeout_b),
> +			 jiffies_to_usecs(orig_vendor.timeout_c),
> +			 jiffies_to_usecs(chip->vendor.timeout_c),
> +			 jiffies_to_usecs(orig_vendor.timeout_d),
> +			 jiffies_to_usecs(chip->vendor.timeout_d));
> +	}
> +
> +	if (chip->vendor.duration_adjusted) {
> +		dev_info(chip->pdev,
> +			 HW_ERR "Adjusted durations: short %u->%uus"
> +			 " medium %u->%uus long %u->%uus\n",
> +			 jiffies_to_usecs(orig_vendor.duration[TPM_SHORT]),
> +			 jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
> +			 jiffies_to_usecs(orig_vendor.duration[TPM_MEDIUM]),
> +			 jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
> +			 jiffies_to_usecs(orig_vendor.duration[TPM_LONG]),
> +			 jiffies_to_usecs(chip->vendor.duration[TPM_LONG]));
> +	}
> +
>  	return 0;
>  }
>  EXPORT_SYMBOL_GPL(tpm_get_timeouts);
> diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
> index 088fa86..caf7278 100644
> --- a/drivers/char/tpm/tpm_tis.c
> +++ b/drivers/char/tpm/tpm_tis.c
> @@ -475,34 +475,17 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
>  	return rc;
>  }
>  
> -struct tis_vendor_timeout_override {
> -	u32 did_vid;
> -	unsigned long timeout_us[4];
> -};
> -
> -static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
> -	/* Atmel 3204 */
> -	{ 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
> -			(TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
> -};
> -
> -static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
> -				    unsigned long *timeout_cap)
> +static void tpm_tis_update_timeouts(struct tpm_chip *chip)
>  {
> -	int i;
> -	u32 did_vid;
> -
> -	did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
> -
> -	for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
> -		if (vendor_timeout_overrides[i].did_vid != did_vid)
> -			continue;
> -		memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
> -		       sizeof(vendor_timeout_overrides[i].timeout_us));
> -		return true;
> +	switch (ioread32(chip->vendor.iobase + TPM_DID_VID(0))) {
> +	case 0x32041114: /* Atmel 3204 */
> +		chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
> +		chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
> +		chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
> +		chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
> +		chip->vendor.timeout_adjusted = true;
> +		break;
>  	}
> -
> -	return false;
>  }
>  
>  /*
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index 706e63e..2380ebf 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -41,8 +41,7 @@ struct tpm_class_ops {
>  	int (*send) (struct tpm_chip *chip, u8 *buf, size_t len);
>  	void (*cancel) (struct tpm_chip *chip);
>  	u8 (*status) (struct tpm_chip *chip);
> -	bool (*update_timeouts)(struct tpm_chip *chip,
> -				unsigned long *timeout_cap);
> +	void (*update_timeouts)(struct tpm_chip *chip);
>  
>  };
>  
> -- 
> 1.9.1
> 

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v7 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup
  2016-06-21  1:54       ` [PATCH v7 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
@ 2016-06-21 20:55         ` Jarkko Sakkinen
  0 siblings, 0 replies; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-06-21 20:55 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, linux-kernel, linux-security-module, jgunthorpe, stefanb

On Mon, Jun 20, 2016 at 06:54:02PM -0700, Ed Swierk wrote:
> The STMicro ST19NP18-TPM sometimes takes much longer to execute
> commands than it reports in its capabilities. For example, command 186
> (TPM_FlushSpecific) has been observed to take 14560 msec to complete,
> far longer than the 3000 msec limit for "short" commands reported by
> the chip. The behavior has also been seen with command 101
> (TPM_GetCapability).
> 
> Worse, when the tpm_tis driver attempts to cancel the current command
> (by writing commandReady = 1 to TPM_STS_x), the chip locks up
> completely, returning all-1s from all memory-mapped register
> reads. The lockup can be cleared only by resetting the system.
> 
> The occurrence of this excessive command duration depends on the
> sequence of commands preceding it. One sequence is creating at least 2
> new keys via TPM_CreateWrapKey, then letting the TPM idle for at least
> 30 seconds, then loading a key via TPM_LoadKey2. The next
> TPM_FlushSpecific occasionally takes tens of seconds to
> complete. Another sequence is creating many keys in a row without
> pause. The TPM_CreateWrapKey operation gets much slower after the
> first few iterations, as one would expect when the pool of precomputed
> keys is exhausted. Then after a 35-second pause, the same TPM_LoadKey2
> followed by TPM_FlushSpecific sequence triggers the behavior.
> 
> Our working theory is that this older TPM sometimes pauses to
> precompute keys, which modern chips implement as a background
> process. Without access to the chip's implementation details it's
> impossible to know whether any commands are immune to being blocked by
> this process. So it seems safest to ignore the chip's reported command
> durations, and use a value much higher than any observed duration,
> like 180 sec (which is the duration this chip reports for "long"
> commands).
> 
> Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>

I think this fine but I would like to hear other opinions on this.

Stefan?

/Jarkko

> ---
>  drivers/char/tpm/tpm_tis.c | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
> index caf7278..8355b45 100644
> --- a/drivers/char/tpm/tpm_tis.c
> +++ b/drivers/char/tpm/tpm_tis.c
> @@ -485,6 +485,12 @@ static void tpm_tis_update_timeouts(struct tpm_chip *chip)
>  		chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
>  		chip->vendor.timeout_adjusted = true;
>  		break;
> +	case 0x0000104a: /* STMicro ST19NP18-TPM */
> +		chip->vendor.duration[TPM_SHORT] = 180 * HZ;
> +		chip->vendor.duration[TPM_MEDIUM] = 180 * HZ;
> +		chip->vendor.duration[TPM_LONG] = 180 * HZ;
> +		chip->vendor.duration_adjusted = true;
> +		break;
>  	}
>  }
>  
> -- 
> 1.9.1
> 

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v7 3/5] tpm: Clean up reading of timeout and duration capabilities
  2016-06-21  1:54       ` [PATCH v7 3/5] tpm: Clean up reading of timeout and duration capabilities Ed Swierk
  2016-06-21 20:52         ` Jarkko Sakkinen
@ 2016-06-22  0:21         ` Ed Swierk
  2016-06-22 10:46           ` Jarkko Sakkinen
  1 sibling, 1 reply; 68+ messages in thread
From: Ed Swierk @ 2016-06-22  0:21 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: Jarkko Sakkinen, Jason Gunthorpe, Stefan Berger, Ed Swierk

On Mon, Jun 20, 2016 at 6:54 PM, Ed Swierk <eswierk@skyportsystems.com> wrote:
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -461,9 +461,19 @@ ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap,
>                 tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
>                 tpm_cmd.params.getcap_in.subcap = subcap_id;
>         }
> +
>         rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
> +
> +       if (!rc &&
> +           ((subcap_id == TPM_CAP_PROP_TIS_TIMEOUT &&
> +             be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 5) ||
> +            (subcap_id == TPM_CAP_PROP_TIS_DURATION &&
> +             be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 4)))
> +               rc = -EINVAL;
> +

Woops, a totally innocuous last-minute (post-testing) cleanup broke
this code; should be TPM_HEADER_SIZE + 20 and + 16. I'll push out v8
as soon as I redo my tests.

--Ed

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v8 0/5] tpm: Command duration logging and chip-specific override
  2016-06-21  1:53     ` [PATCH v7 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
                         ` (4 preceding siblings ...)
  2016-06-21  1:54       ` [PATCH v7 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
@ 2016-06-22  1:10       ` Ed Swierk
  2016-06-22  1:10         ` [PATCH v8 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
                           ` (5 more replies)
  5 siblings, 6 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-22  1:10 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

v8: Fix v7 goof-up in tpm_getcap().

v7: Use tpm_getcap() instead of a redundant new function.

v6: Split tpm_get_cap_prop() out of tpm_get_timeouts(); always return
error on TPM command failure.

v5: Use msecs_to_jiffies() instead of * HZ / 1000.

v4: Rework tpm_get_timeouts() to allow overriding both timeouts and
durations via a single callback.

This series
- improves TPM command error reporting
- adds optional logging of TPM command durations
- allows chip-specific override of command durations as well as protocol
  timeouts
- overrides ST19NP18 TPM command duration to avoid lockups

Ed Swierk (5):
  tpm_tis: Improve reporting of IO errors
  tpm: Add optional logging of TPM command durations
  tpm: Clean up reading of timeout and duration capabilities
  tpm: Allow TPM chip drivers to override reported command durations
  tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup

 drivers/char/tpm/tpm-interface.c | 212 ++++++++++++++++++++-------------------
 drivers/char/tpm/tpm_tis.c       |  49 ++++-----
 include/linux/tpm.h              |   3 +-
 3 files changed, 130 insertions(+), 134 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v8 1/5] tpm_tis: Improve reporting of IO errors
  2016-06-22  1:10       ` [PATCH v8 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
@ 2016-06-22  1:10         ` Ed Swierk
  2016-06-24 18:25           ` Jason Gunthorpe
  2016-06-22  1:10         ` [PATCH v8 2/5] tpm: Add optional logging of TPM command durations Ed Swierk
                           ` (4 subsequent siblings)
  5 siblings, 1 reply; 68+ messages in thread
From: Ed Swierk @ 2016-06-22  1:10 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

Mysterious TPM behavior can be difficult to track down through all the
layers of software. Add error messages for conditions that should
never happen. Also include the manufacturer ID along with other chip
data printed during init.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 drivers/char/tpm/tpm_tis.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 65f7eec..088fa86 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -299,6 +299,8 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 
 	expected = be32_to_cpu(*(__be32 *) (buf + 2));
 	if (expected > count) {
+		dev_err(chip->pdev, "Response too long (wanted %zd, got %d)\n",
+			count, expected);
 		size = -EIO;
 		goto out;
 	}
@@ -366,6 +368,8 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
 				  &chip->vendor.int_queue, false);
 		status = tpm_tis_status(chip);
 		if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
+			dev_err(chip->pdev, "Chip not accepting %zd bytes\n",
+				len - count);
 			rc = -EIO;
 			goto out_err;
 		}
@@ -378,6 +382,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
 			  &chip->vendor.int_queue, false);
 	status = tpm_tis_status(chip);
 	if ((status & TPM_STS_DATA_EXPECT) != 0) {
+		dev_err(chip->pdev, "Chip not accepting last byte\n");
 		rc = -EIO;
 		goto out_err;
 	}
@@ -689,8 +694,9 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
 	vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
 	chip->vendor.manufacturer_id = vendor;
 
-	dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n",
+	dev_info(dev, "%s TPM (manufacturer-id 0x%X, device-id 0x%X, rev-id %d)\n",
 		 (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2",
+		 chip->vendor.manufacturer_id,
 		 vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
 
 	if (!itpm) {
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v8 2/5] tpm: Add optional logging of TPM command durations
  2016-06-22  1:10       ` [PATCH v8 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  2016-06-22  1:10         ` [PATCH v8 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
@ 2016-06-22  1:10         ` Ed Swierk
  2016-06-24 18:27           ` Jason Gunthorpe
  2016-06-22  1:10         ` [PATCH v8 3/5] tpm: Clean up reading of timeout and duration capabilities Ed Swierk
                           ` (3 subsequent siblings)
  5 siblings, 1 reply; 68+ messages in thread
From: Ed Swierk @ 2016-06-22  1:10 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

Some TPMs violate their own advertised command durations. This is much
easier to debug with data about how long each command actually takes
to complete. Add debug messages that can be enabled by running

  echo -n 'module tpm +p' >/sys/kernel/debug/dynamic_debug/control

on a kernel configured with DYNAMIC_DEBUG=y.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 drivers/char/tpm/tpm-interface.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index c50637d..cc1e5bc 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -333,13 +333,14 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 {
 	ssize_t rc;
 	u32 count, ordinal;
-	unsigned long stop;
+	unsigned long start, stop;
 
 	if (bufsiz > TPM_BUFSIZE)
 		bufsiz = TPM_BUFSIZE;
 
 	count = be32_to_cpu(*((__be32 *) (buf + 2)));
 	ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+	dev_dbg(chip->pdev, "starting command %d count %d\n", ordinal, count);
 	if (count == 0)
 		return -ENODATA;
 	if (count > bufsiz) {
@@ -360,18 +361,24 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 	if (chip->vendor.irq)
 		goto out_recv;
 
+	start = jiffies;
 	if (chip->flags & TPM_CHIP_FLAG_TPM2)
-		stop = jiffies + tpm2_calc_ordinal_duration(chip, ordinal);
+		stop = start + tpm2_calc_ordinal_duration(chip, ordinal);
 	else
-		stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
+		stop = start + tpm_calc_ordinal_duration(chip, ordinal);
 	do {
 		u8 status = chip->ops->status(chip);
 		if ((status & chip->ops->req_complete_mask) ==
-		    chip->ops->req_complete_val)
+		    chip->ops->req_complete_val) {
+			dev_dbg(chip->pdev, "completed command %d in %d ms\n",
+				ordinal, jiffies_to_msecs(jiffies - start));
 			goto out_recv;
+		}
 
 		if (chip->ops->req_canceled(chip, status)) {
 			dev_err(chip->pdev, "Operation Canceled\n");
+			dev_dbg(chip->pdev, "canceled command %d after %d ms\n",
+				ordinal, jiffies_to_msecs(jiffies - start));
 			rc = -ECANCELED;
 			goto out;
 		}
@@ -382,6 +389,8 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 
 	chip->ops->cancel(chip);
 	dev_err(chip->pdev, "Operation Timed out\n");
+	dev_dbg(chip->pdev, "command %d timed out after %d ms\n", ordinal,
+		jiffies_to_msecs(jiffies - start));
 	rc = -ETIME;
 	goto out;
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v8 3/5] tpm: Clean up reading of timeout and duration capabilities
  2016-06-22  1:10       ` [PATCH v8 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  2016-06-22  1:10         ` [PATCH v8 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
  2016-06-22  1:10         ` [PATCH v8 2/5] tpm: Add optional logging of TPM command durations Ed Swierk
@ 2016-06-22  1:10         ` Ed Swierk
  2016-06-22  1:10         ` [PATCH v8 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
                           ` (2 subsequent siblings)
  5 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-22  1:10 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

Call tpm_getcap() from tpm_get_timeouts() to eliminate redundant
code. Return all errors to the caller rather than swallowing them
(e.g. when tpm_transmit_cmd() returns nonzero).

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm-interface.c | 74 +++++++++++++++-------------------------
 1 file changed, 27 insertions(+), 47 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index cc1e5bc..b65c139 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -461,9 +461,19 @@ ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap,
 		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
 		tpm_cmd.params.getcap_in.subcap = subcap_id;
 	}
+
 	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
+
+	if (!rc &&
+	    ((subcap_id == TPM_CAP_PROP_TIS_TIMEOUT &&
+	      be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 20) ||
+	     (subcap_id == TPM_CAP_PROP_TIS_DURATION &&
+	      be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 16)))
+		rc = -EINVAL;
+
 	if (!rc)
 		*cap = tpm_cmd.params.getcap_out.cap;
+
 	return rc;
 }
 
@@ -504,48 +514,30 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
 
 int tpm_get_timeouts(struct tpm_chip *chip)
 {
-	struct tpm_cmd_t tpm_cmd;
+	cap_t cap;
 	unsigned long new_timeout[4];
 	unsigned long old_timeout[4];
-	struct duration_t *duration_cap;
 	ssize_t rc;
 
-	tpm_cmd.header.in = tpm_getcap_header;
-	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
-	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
-	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
-
+	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
+			"attempting to determine the timeouts");
 	if (rc == TPM_ERR_INVALID_POSTINIT) {
 		/* The TPM is not started, we are the first to talk to it.
 		   Execute a startup command. */
-		dev_info(chip->pdev, "Issuing TPM_STARTUP");
+		dev_info(chip->pdev, "Issuing TPM_STARTUP\n");
 		if (tpm_startup(chip, TPM_ST_CLEAR))
 			return rc;
 
-		tpm_cmd.header.in = tpm_getcap_header;
-		tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
-		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-		tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
-		rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
-				  NULL);
-	}
-	if (rc) {
-		dev_err(chip->pdev,
-			"A TPM error (%zd) occurred attempting to determine the timeouts\n",
-			rc);
-		goto duration;
+		rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
+				"attempting to determine the timeouts");
 	}
+	if (rc)
+		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) + 4 * sizeof(u32))
-		return -EINVAL;
-
-	old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
-	old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
-	old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
-	old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
+	old_timeout[0] = be32_to_cpu(cap.timeout.a);
+	old_timeout[1] = be32_to_cpu(cap.timeout.b);
+	old_timeout[2] = be32_to_cpu(cap.timeout.c);
+	old_timeout[3] = be32_to_cpu(cap.timeout.d);
 	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
 
 	/*
@@ -583,29 +575,17 @@ int tpm_get_timeouts(struct tpm_chip *chip)
 	chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
 	chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
 
-duration:
-	tpm_cmd.header.in = tpm_getcap_header;
-	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
-	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
-
-	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
-			      "attempting to determine the durations");
+	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_DURATION, &cap,
+			"attempting to determine the durations");
 	if (rc)
 		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 -EINVAL;
-
-	duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
 	chip->vendor.duration[TPM_SHORT] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
+		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_short));
 	chip->vendor.duration[TPM_MEDIUM] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
+		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
 	chip->vendor.duration[TPM_LONG] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
+		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
 
 	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
 	 * value wrong and apparently reports msecs rather than usecs. So we
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v8 4/5] tpm: Allow TPM chip drivers to override reported command durations
  2016-06-22  1:10       ` [PATCH v8 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
                           ` (2 preceding siblings ...)
  2016-06-22  1:10         ` [PATCH v8 3/5] tpm: Clean up reading of timeout and duration capabilities Ed Swierk
@ 2016-06-22  1:10         ` Ed Swierk
  2016-06-22  1:10         ` [PATCH v8 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
  2016-07-13 16:19         ` [PATCH v9 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  5 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-22  1:10 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

Some TPM chips report bogus command durations in their capabilities,
just as others report incorrect timeouts. Rework tpm_get_timeouts() to
allow chip drivers to override either via a single callback. Also
clean up handling of TPMs that report milliseconds instead of
microseconds.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm-interface.c | 143 +++++++++++++++++++++------------------
 drivers/char/tpm/tpm_tis.c       |  35 +++-------
 include/linux/tpm.h              |   3 +-
 3 files changed, 88 insertions(+), 93 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index b65c139..5f98488 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -514,12 +514,11 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
 
 int tpm_get_timeouts(struct tpm_chip *chip)
 {
-	cap_t cap;
-	unsigned long new_timeout[4];
-	unsigned long old_timeout[4];
-	ssize_t rc;
+	cap_t cap1, cap2;
+	int rc;
+	struct tpm_vendor_specific orig_vendor;
 
-	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
+	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap1,
 			"attempting to determine the timeouts");
 	if (rc == TPM_ERR_INVALID_POSTINIT) {
 		/* The TPM is not started, we are the first to talk to it.
@@ -528,77 +527,91 @@ int tpm_get_timeouts(struct tpm_chip *chip)
 		if (tpm_startup(chip, TPM_ST_CLEAR))
 			return rc;
 
-		rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
+		rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_TIMEOUT, &cap1,
 				"attempting to determine the timeouts");
 	}
 	if (rc)
 		return rc;
 
-	old_timeout[0] = be32_to_cpu(cap.timeout.a);
-	old_timeout[1] = be32_to_cpu(cap.timeout.b);
-	old_timeout[2] = be32_to_cpu(cap.timeout.c);
-	old_timeout[3] = be32_to_cpu(cap.timeout.d);
-	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
-
-	/*
-	 * Provide ability for vendor overrides of timeout values in case
-	 * of misreporting.
-	 */
-	if (chip->ops->update_timeouts != NULL)
-		chip->vendor.timeout_adjusted =
-			chip->ops->update_timeouts(chip, new_timeout);
-
-	if (!chip->vendor.timeout_adjusted) {
-		/* Don't overwrite default if value is 0 */
-		if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
-			int i;
-
-			/* timeouts in msec rather usec */
-			for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
-				new_timeout[i] *= 1000;
-			chip->vendor.timeout_adjusted = true;
-		}
-	}
-
-	/* Report adjusted timeouts */
-	if (chip->vendor.timeout_adjusted) {
-		dev_info(chip->pdev,
-			 HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
-			 old_timeout[0], new_timeout[0],
-			 old_timeout[1], new_timeout[1],
-			 old_timeout[2], new_timeout[2],
-			 old_timeout[3], new_timeout[3]);
-	}
-
-	chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
-	chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
-	chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
-	chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
-
-	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_DURATION, &cap,
+	rc = tpm_getcap(chip->pdev, TPM_CAP_PROP_TIS_DURATION, &cap2,
 			"attempting to determine the durations");
 	if (rc)
 		return rc;
 
-	chip->vendor.duration[TPM_SHORT] =
-		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_short));
-	chip->vendor.duration[TPM_MEDIUM] =
-		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
-	chip->vendor.duration[TPM_LONG] =
-		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
+	be32_to_cpus(&cap1.timeout.a);
+	be32_to_cpus(&cap1.timeout.b);
+	be32_to_cpus(&cap1.timeout.c);
+	be32_to_cpus(&cap1.timeout.d);
+	chip->vendor.timeout_a = usecs_to_jiffies(cap1.timeout.a);
+	chip->vendor.timeout_b = usecs_to_jiffies(cap1.timeout.b);
+	chip->vendor.timeout_c = usecs_to_jiffies(cap1.timeout.c);
+	chip->vendor.timeout_d = usecs_to_jiffies(cap1.timeout.d);
 
-	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
-	 * value wrong and apparently reports msecs rather than usecs. So we
-	 * fix up the resulting too-small TPM_SHORT value to make things work.
-	 * We also scale the TPM_MEDIUM and -_LONG values by 1000.
-	 */
-	if (chip->vendor.duration[TPM_SHORT] < (HZ / 100)) {
-		chip->vendor.duration[TPM_SHORT] = HZ;
-		chip->vendor.duration[TPM_MEDIUM] *= 1000;
-		chip->vendor.duration[TPM_LONG] *= 1000;
-		chip->vendor.duration_adjusted = true;
-		dev_info(chip->pdev, "Adjusting TPM timeout parameters.");
+	/* Some TPMs report timeouts in milliseconds rather than
+	   microseconds. Use a value between 1 and 1000 as an
+	   indication that this is the case. */
+	if (cap1.timeout.a > 0 && cap1.timeout.a < 1000) {
+		chip->vendor.timeout_a = msecs_to_jiffies(cap1.timeout.a);
+		chip->vendor.timeout_b = msecs_to_jiffies(cap1.timeout.b);
+		chip->vendor.timeout_c = msecs_to_jiffies(cap1.timeout.c);
+		chip->vendor.timeout_d = msecs_to_jiffies(cap1.timeout.d);
+		chip->vendor.timeout_adjusted = true;
 	}
+
+	be32_to_cpus(&cap2.duration.tpm_short);
+	be32_to_cpus(&cap2.duration.tpm_medium);
+	be32_to_cpus(&cap2.duration.tpm_long);
+	chip->vendor.duration[TPM_SHORT] =
+		usecs_to_jiffies(cap2.duration.tpm_short);
+	chip->vendor.duration[TPM_MEDIUM] =
+		usecs_to_jiffies(cap2.duration.tpm_medium);
+	chip->vendor.duration[TPM_LONG] =
+		usecs_to_jiffies(cap2.duration.tpm_long);
+
+	memcpy(&orig_vendor, &chip->vendor, sizeof(orig_vendor));
+
+	/* Interpret duration values between 1 and 10000 as
+	   milliseconds to deal with TPMs like the Broadcom BCM0102 in
+	   the Dell Latitude D820. */
+	if (cap2.duration.tpm_short > 0 && cap2.duration.tpm_short < 10000) {
+		chip->vendor.duration[TPM_SHORT] =
+			msecs_to_jiffies(cap2.duration.tpm_short);
+		chip->vendor.duration[TPM_MEDIUM] =
+			msecs_to_jiffies(cap2.duration.tpm_medium);
+		chip->vendor.duration[TPM_LONG] =
+			msecs_to_jiffies(cap2.duration.tpm_long);
+		chip->vendor.duration_adjusted = true;
+	}
+
+	if (chip->ops->update_timeouts != NULL)
+		chip->ops->update_timeouts(chip);
+
+	if (chip->vendor.timeout_adjusted) {
+		dev_info(chip->pdev,
+			 HW_ERR "Adjusted timeouts: A %u->%uus B %u->%uus"
+			 " C %u->%uus D %u->%uus\n",
+			 jiffies_to_usecs(orig_vendor.timeout_a),
+			 jiffies_to_usecs(chip->vendor.timeout_a),
+			 jiffies_to_usecs(orig_vendor.timeout_b),
+			 jiffies_to_usecs(chip->vendor.timeout_b),
+			 jiffies_to_usecs(orig_vendor.timeout_c),
+			 jiffies_to_usecs(chip->vendor.timeout_c),
+			 jiffies_to_usecs(orig_vendor.timeout_d),
+			 jiffies_to_usecs(chip->vendor.timeout_d));
+	}
+
+	if (chip->vendor.duration_adjusted) {
+		dev_info(chip->pdev,
+			 HW_ERR "Adjusted durations: short %u->%uus"
+			 " medium %u->%uus long %u->%uus\n",
+			 jiffies_to_usecs(orig_vendor.duration[TPM_SHORT]),
+			 jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
+			 jiffies_to_usecs(orig_vendor.duration[TPM_MEDIUM]),
+			 jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
+			 jiffies_to_usecs(orig_vendor.duration[TPM_LONG]),
+			 jiffies_to_usecs(chip->vendor.duration[TPM_LONG]));
+	}
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(tpm_get_timeouts);
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 088fa86..caf7278 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -475,34 +475,17 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
 	return rc;
 }
 
-struct tis_vendor_timeout_override {
-	u32 did_vid;
-	unsigned long timeout_us[4];
-};
-
-static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
-	/* Atmel 3204 */
-	{ 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
-			(TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
-};
-
-static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
-				    unsigned long *timeout_cap)
+static void tpm_tis_update_timeouts(struct tpm_chip *chip)
 {
-	int i;
-	u32 did_vid;
-
-	did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
-
-	for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
-		if (vendor_timeout_overrides[i].did_vid != did_vid)
-			continue;
-		memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
-		       sizeof(vendor_timeout_overrides[i].timeout_us));
-		return true;
+	switch (ioread32(chip->vendor.iobase + TPM_DID_VID(0))) {
+	case 0x32041114: /* Atmel 3204 */
+		chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+		chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
+		chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+		chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+		chip->vendor.timeout_adjusted = true;
+		break;
 	}
-
-	return false;
 }
 
 /*
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 706e63e..2380ebf 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -41,8 +41,7 @@ struct tpm_class_ops {
 	int (*send) (struct tpm_chip *chip, u8 *buf, size_t len);
 	void (*cancel) (struct tpm_chip *chip);
 	u8 (*status) (struct tpm_chip *chip);
-	bool (*update_timeouts)(struct tpm_chip *chip,
-				unsigned long *timeout_cap);
+	void (*update_timeouts)(struct tpm_chip *chip);
 
 };
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v8 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup
  2016-06-22  1:10       ` [PATCH v8 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
                           ` (3 preceding siblings ...)
  2016-06-22  1:10         ` [PATCH v8 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
@ 2016-06-22  1:10         ` Ed Swierk
  2016-07-13 16:19         ` [PATCH v9 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  5 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-06-22  1:10 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

The STMicro ST19NP18-TPM sometimes takes much longer to execute
commands than it reports in its capabilities. For example, command 186
(TPM_FlushSpecific) has been observed to take 14560 msec to complete,
far longer than the 3000 msec limit for "short" commands reported by
the chip. The behavior has also been seen with command 101
(TPM_GetCapability).

Worse, when the tpm_tis driver attempts to cancel the current command
(by writing commandReady = 1 to TPM_STS_x), the chip locks up
completely, returning all-1s from all memory-mapped register
reads. The lockup can be cleared only by resetting the system.

The occurrence of this excessive command duration depends on the
sequence of commands preceding it. One sequence is creating at least 2
new keys via TPM_CreateWrapKey, then letting the TPM idle for at least
30 seconds, then loading a key via TPM_LoadKey2. The next
TPM_FlushSpecific occasionally takes tens of seconds to
complete. Another sequence is creating many keys in a row without
pause. The TPM_CreateWrapKey operation gets much slower after the
first few iterations, as one would expect when the pool of precomputed
keys is exhausted. Then after a 35-second pause, the same TPM_LoadKey2
followed by TPM_FlushSpecific sequence triggers the behavior.

Our working theory is that this older TPM sometimes pauses to
precompute keys, which modern chips implement as a background
process. Without access to the chip's implementation details it's
impossible to know whether any commands are immune to being blocked by
this process. So it seems safest to ignore the chip's reported command
durations, and use a value much higher than any observed duration,
like 180 sec (which is the duration this chip reports for "long"
commands).

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm_tis.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index caf7278..8355b45 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -485,6 +485,12 @@ static void tpm_tis_update_timeouts(struct tpm_chip *chip)
 		chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
 		chip->vendor.timeout_adjusted = true;
 		break;
+	case 0x0000104a: /* STMicro ST19NP18-TPM */
+		chip->vendor.duration[TPM_SHORT] = 180 * HZ;
+		chip->vendor.duration[TPM_MEDIUM] = 180 * HZ;
+		chip->vendor.duration[TPM_LONG] = 180 * HZ;
+		chip->vendor.duration_adjusted = true;
+		break;
 	}
 }
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v7 3/5] tpm: Clean up reading of timeout and duration capabilities
  2016-06-22  0:21         ` Ed Swierk
@ 2016-06-22 10:46           ` Jarkko Sakkinen
  0 siblings, 0 replies; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-06-22 10:46 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, linux-kernel, linux-security-module,
	Jason Gunthorpe, Stefan Berger

On Tue, Jun 21, 2016 at 05:21:27PM -0700, Ed Swierk wrote:
> On Mon, Jun 20, 2016 at 6:54 PM, Ed Swierk <eswierk@skyportsystems.com> wrote:
> > --- a/drivers/char/tpm/tpm-interface.c
> > +++ b/drivers/char/tpm/tpm-interface.c
> > @@ -461,9 +461,19 @@ ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap,
> >                 tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> >                 tpm_cmd.params.getcap_in.subcap = subcap_id;
> >         }
> > +
> >         rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
> > +
> > +       if (!rc &&
> > +           ((subcap_id == TPM_CAP_PROP_TIS_TIMEOUT &&
> > +             be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 5) ||
> > +            (subcap_id == TPM_CAP_PROP_TIS_DURATION &&
> > +             be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 4)))
> > +               rc = -EINVAL;
> > +
> 
> Woops, a totally innocuous last-minute (post-testing) cleanup broke
> this code; should be TPM_HEADER_SIZE + 20 and + 16. I'll push out v8
> as soon as I redo my tests.

OK, that's cool. Haven't yet got into testing it anyway.

1. This is too late for 4.8 release.
2. I'm on four week leave starting from week after next week but before
   I go to my leave I will apply these commits to my master branch so that
   they get exposure.
3. I try to do testing for my part before going to the leave.

> --Ed

/Jarkko

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v8 1/5] tpm_tis: Improve reporting of IO errors
  2016-06-22  1:10         ` [PATCH v8 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
@ 2016-06-24 18:25           ` Jason Gunthorpe
  2016-06-24 20:21             ` Jarkko Sakkinen
  0 siblings, 1 reply; 68+ messages in thread
From: Jason Gunthorpe @ 2016-06-24 18:25 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, linux-kernel, linux-security-module,
	jarkko.sakkinen, stefanb

>  	expected = be32_to_cpu(*(__be32 *) (buf + 2));
>  	if (expected > count) {
> +		dev_err(chip->pdev, "Response too long (wanted %zd, got %d)\n",
> +			count, expected);

This all needs to be rebased on Jarkko's tree I guess, chip->pdev is
gone now.

http://git.infradead.org/users/jjs/linux-tpmdd.git/shortlog/refs/heads/master

Jarkko, did you miss a pull request for 4.7 or something? This is
4 month old stuff???

Jason

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v8 2/5] tpm: Add optional logging of TPM command durations
  2016-06-22  1:10         ` [PATCH v8 2/5] tpm: Add optional logging of TPM command durations Ed Swierk
@ 2016-06-24 18:27           ` Jason Gunthorpe
  2016-06-24 20:24             ` Jarkko Sakkinen
  0 siblings, 1 reply; 68+ messages in thread
From: Jason Gunthorpe @ 2016-06-24 18:27 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, linux-kernel, linux-security-module,
	jarkko.sakkinen, stefanb

On Tue, Jun 21, 2016 at 06:10:28PM -0700, Ed Swierk wrote:

>  		if (chip->ops->req_canceled(chip, status)) {
>  			dev_err(chip->pdev, "Operation Canceled\n");
> +			dev_dbg(chip->pdev, "canceled command %d after %d ms\n",
> +				ordinal, jiffies_to_msecs(jiffies -
>  			start));

[..]

>  	chip->ops->cancel(chip);
>  	dev_err(chip->pdev, "Operation Timed out\n");
> +	dev_dbg(chip->pdev, "command %d timed out after %d ms\n", ordinal,
> +		jiffies_to_msecs(jiffies - start));

No sense in logging twice, just enhance the existingerror message.

Jason

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v8 1/5] tpm_tis: Improve reporting of IO errors
  2016-06-24 18:25           ` Jason Gunthorpe
@ 2016-06-24 20:21             ` Jarkko Sakkinen
  2016-06-24 20:23               ` Jarkko Sakkinen
  2016-06-24 20:26               ` Jason Gunthorpe
  0 siblings, 2 replies; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-06-24 20:21 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: Ed Swierk, tpmdd-devel, linux-kernel, linux-security-module, stefanb

Hi Jason,

On Fri, Jun 24, 2016 at 12:25:15PM -0600, Jason Gunthorpe wrote:
> >  	expected = be32_to_cpu(*(__be32 *) (buf + 2));
> >  	if (expected > count) {
> > +		dev_err(chip->pdev, "Response too long (wanted %zd, got %d)\n",
> > +			count, expected);
> 
> This all needs to be rebased on Jarkko's tree I guess, chip->pdev is
> gone now.
> 
> http://git.infradead.org/users/jjs/linux-tpmdd.git/shortlog/refs/heads/master
> 
> Jarkko, did you miss a pull request for 4.7 or something? This is
> 4 month old stuff???

Hmm... Do you mean by 4 month old stuff the stuff that is in mainline
and not in my master branch?

I'm not sure what happened with 4.7. I merged the changes for in about
4.6-rc5. There was one issue that I fixed that Stephen reported.

At the moment linux-next seems contain the stuff that I have in my
next.

> Jason

/Jarkko

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v8 1/5] tpm_tis: Improve reporting of IO errors
  2016-06-24 20:21             ` Jarkko Sakkinen
@ 2016-06-24 20:23               ` Jarkko Sakkinen
  2016-06-24 20:26               ` Jason Gunthorpe
  1 sibling, 0 replies; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-06-24 20:23 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: Ed Swierk, tpmdd-devel, linux-kernel, linux-security-module, stefanb

On Fri, Jun 24, 2016 at 11:21:31PM +0300, Jarkko Sakkinen wrote:
> Hi Jason,
> 
> On Fri, Jun 24, 2016 at 12:25:15PM -0600, Jason Gunthorpe wrote:
> > >  	expected = be32_to_cpu(*(__be32 *) (buf + 2));
> > >  	if (expected > count) {
> > > +		dev_err(chip->pdev, "Response too long (wanted %zd, got %d)\n",
> > > +			count, expected);
> > 
> > This all needs to be rebased on Jarkko's tree I guess, chip->pdev is
> > gone now.
> > 
> > http://git.infradead.org/users/jjs/linux-tpmdd.git/shortlog/refs/heads/master
> > 
> > Jarkko, did you miss a pull request for 4.7 or something? This is
> > 4 month old stuff???
> 
> Hmm... Do you mean by 4 month old stuff the stuff that is in mainline
> and not in my master branch?
> 
> I'm not sure what happened with 4.7. I merged the changes for in about
> 4.6-rc5. There was one issue that I fixed that Stephen reported.
> 
> At the moment linux-next seems contain the stuff that I have in my
> next.

Ed:

I was planning to applying these patches to my master next week and run
tests on them. If they do not apply it would be good if you could rebase
your series to apply to my master.

/Jarkko

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v8 2/5] tpm: Add optional logging of TPM command durations
  2016-06-24 18:27           ` Jason Gunthorpe
@ 2016-06-24 20:24             ` Jarkko Sakkinen
  0 siblings, 0 replies; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-06-24 20:24 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: Ed Swierk, tpmdd-devel, linux-kernel, linux-security-module, stefanb

On Fri, Jun 24, 2016 at 12:27:27PM -0600, Jason Gunthorpe wrote:
> On Tue, Jun 21, 2016 at 06:10:28PM -0700, Ed Swierk wrote:
> 
> >  		if (chip->ops->req_canceled(chip, status)) {
> >  			dev_err(chip->pdev, "Operation Canceled\n");
> > +			dev_dbg(chip->pdev, "canceled command %d after %d ms\n",
> > +				ordinal, jiffies_to_msecs(jiffies -
> >  			start));
> 
> [..]
> 
> >  	chip->ops->cancel(chip);
> >  	dev_err(chip->pdev, "Operation Timed out\n");
> > +	dev_dbg(chip->pdev, "command %d timed out after %d ms\n", ordinal,
> > +		jiffies_to_msecs(jiffies - start));
> 
> No sense in logging twice, just enhance the existingerror message.

Absolutely agree. Jason, thanks for pointing this out!

/Jarkko

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v8 1/5] tpm_tis: Improve reporting of IO errors
  2016-06-24 20:21             ` Jarkko Sakkinen
  2016-06-24 20:23               ` Jarkko Sakkinen
@ 2016-06-24 20:26               ` Jason Gunthorpe
  2016-06-25 15:24                 ` Jarkko Sakkinen
  1 sibling, 1 reply; 68+ messages in thread
From: Jason Gunthorpe @ 2016-06-24 20:26 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: Ed Swierk, tpmdd-devel, linux-kernel, linux-security-module, stefanb

On Fri, Jun 24, 2016 at 11:21:31PM +0300, Jarkko Sakkinen wrote:
> Hmm... Do you mean by 4 month old stuff the stuff that is in mainline
> and not in my master branch?

I mean the stuff that is in your branch but not in mainline.

$ git log --pretty=oneline jarkko/master ^v4.7-rc3 | wc -l
73

> I'm not sure what happened with 4.7. I merged the changes for in about
> 4.6-rc5. There was one issue that I fixed that Stephen reported.
> 
> At the moment linux-next seems contain the stuff that I have in my
> next.

linux-next is just pulling directly from your tree, you still have to
ensure that James gets and processes your pull request during the
merge window. If he dropped a pull request you should follow up and
ask why, if you never sent one then ... oops :)

Jason

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v8 1/5] tpm_tis: Improve reporting of IO errors
  2016-06-24 20:26               ` Jason Gunthorpe
@ 2016-06-25 15:24                 ` Jarkko Sakkinen
  2016-06-25 15:47                   ` Jarkko Sakkinen
  0 siblings, 1 reply; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-06-25 15:24 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: Ed Swierk, tpmdd-devel, linux-kernel, linux-security-module, stefanb

On Fri, Jun 24, 2016 at 02:26:15PM -0600, Jason Gunthorpe wrote:
> On Fri, Jun 24, 2016 at 11:21:31PM +0300, Jarkko Sakkinen wrote:
> > Hmm... Do you mean by 4 month old stuff the stuff that is in mainline
> > and not in my master branch?
> 
> I mean the stuff that is in your branch but not in mainline.
> 
> $ git log --pretty=oneline jarkko/master ^v4.7-rc3 | wc -l
> 73
> 
> > I'm not sure what happened with 4.7. I merged the changes for in about
> > 4.6-rc5. There was one issue that I fixed that Stephen reported.
> > 
> > At the moment linux-next seems contain the stuff that I have in my
> > next.
> 
> linux-next is just pulling directly from your tree, you still have to
> ensure that James gets and processes your pull request during the
> merge window. If he dropped a pull request you should follow up and
> ask why, if you never sent one then ... oops :)

For 4.6 I used pull request with a signed tag and everything went quite
well.

For 4.7 I did re-read the whole development process documentation but it
only speaks about pull requests and does not clearly state what you just
stated.

To summarize I screwed this one up but I guess the only big harm is that
vTPM support will skip to 4.8. I guess not big harm done?

My master is now rebased and this is what I get:

$ git log --oneline security/next...master | wc -l
67

I don't think that is too bad.

> Jason

/Jarkko

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v8 1/5] tpm_tis: Improve reporting of IO errors
  2016-06-25 15:24                 ` Jarkko Sakkinen
@ 2016-06-25 15:47                   ` Jarkko Sakkinen
  2016-06-27 17:55                     ` Jason Gunthorpe
  0 siblings, 1 reply; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-06-25 15:47 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: Ed Swierk, tpmdd-devel, linux-kernel, linux-security-module, stefanb

On Sat, Jun 25, 2016 at 06:24:30PM +0300, Jarkko Sakkinen wrote:
> On Fri, Jun 24, 2016 at 02:26:15PM -0600, Jason Gunthorpe wrote:
> > On Fri, Jun 24, 2016 at 11:21:31PM +0300, Jarkko Sakkinen wrote:
> > > Hmm... Do you mean by 4 month old stuff the stuff that is in mainline
> > > and not in my master branch?
> > 
> > I mean the stuff that is in your branch but not in mainline.
> > 
> > $ git log --pretty=oneline jarkko/master ^v4.7-rc3 | wc -l
> > 73
> > 
> > > I'm not sure what happened with 4.7. I merged the changes for in about
> > > 4.6-rc5. There was one issue that I fixed that Stephen reported.
> > > 
> > > At the moment linux-next seems contain the stuff that I have in my
> > > next.
> > 
> > linux-next is just pulling directly from your tree, you still have to
> > ensure that James gets and processes your pull request during the
> > merge window. If he dropped a pull request you should follow up and
> > ask why, if you never sent one then ... oops :)
> 
> For 4.6 I used pull request with a signed tag and everything went quite
> well.
> 
> For 4.7 I did re-read the whole development process documentation but it
> only speaks about pull requests and does not clearly state what you just
> stated.
> 
> To summarize I screwed this one up but I guess the only big harm is that
> vTPM support will skip to 4.8. I guess not big harm done?
> 
> My master is now rebased and this is what I get:
> 
> $ git log --oneline security/next...master | wc -l
> 67
> 
> I don't think that is too bad.

My repositories are ready for next pull request. The master has been
rebased to James' tree and merged to next.

I won't add any new commits expect critical bug fixes for 4.8 release
content.

/Jarkko

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v8 1/5] tpm_tis: Improve reporting of IO errors
  2016-06-25 15:47                   ` Jarkko Sakkinen
@ 2016-06-27 17:55                     ` Jason Gunthorpe
  0 siblings, 0 replies; 68+ messages in thread
From: Jason Gunthorpe @ 2016-06-27 17:55 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: Ed Swierk, tpmdd-devel, linux-kernel, linux-security-module, stefanb

On Sat, Jun 25, 2016 at 06:47:45PM +0300, Jarkko Sakkinen wrote:

> My repositories are ready for next pull request. The master has been
> rebased to James' tree and merged to next.

This seems fine..

Generally you shouldn't rebase to create pull requests, but this
seemed needed..

Organize your git tree so that it is always pullable and just use
merges/resets/etc in the -next branch

There is no reason to hold off on more stuff for 4.8, if another batch
is ready before the merge window then send it, James already took the
stuff you sent.

Jason

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v9 0/5] tpm: Command duration logging and chip-specific override
  2016-06-22  1:10       ` [PATCH v8 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
                           ` (4 preceding siblings ...)
  2016-06-22  1:10         ` [PATCH v8 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
@ 2016-07-13 16:19         ` Ed Swierk
  2016-07-13 16:19           ` [PATCH v9 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
                             ` (6 more replies)
  5 siblings, 7 replies; 68+ messages in thread
From: Ed Swierk @ 2016-07-13 16:19 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

v9: Include command duration in existing error messages rather than
logging an extra debug message. Rebase onto Jarkko's tree.

v8: Fix v7 goof-up in tpm_getcap().

v7: Use tpm_getcap() instead of a redundant new function.

v6: Split tpm_get_cap_prop() out of tpm_get_timeouts(); always return
error on TPM command failure.

v5: Use msecs_to_jiffies() instead of * HZ / 1000.

v4: Rework tpm_get_timeouts() to allow overriding both timeouts and
durations via a single callback.

This series
- improves TPM command error reporting
- adds optional logging of TPM command durations
- allows chip-specific override of command durations as well as protocol
  timeouts
- overrides ST19NP18 TPM command duration to avoid lockups

Ed Swierk (5):
  tpm_tis: Improve reporting of IO errors
  tpm: Add optional logging of TPM command durations
  tpm: Clean up reading of timeout and duration capabilities
  tpm: Allow TPM chip drivers to override reported command durations
  tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup

 drivers/char/tpm/tpm-interface.c | 219 ++++++++++++++++++++-------------------
 drivers/char/tpm/tpm_tis_core.c  |  46 ++++----
 include/linux/tpm.h              |   3 +-
 3 files changed, 136 insertions(+), 132 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v9 1/5] tpm_tis: Improve reporting of IO errors
  2016-07-13 16:19         ` [PATCH v9 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
@ 2016-07-13 16:19           ` Ed Swierk
  2016-07-13 16:19           ` [PATCH v9 2/5] tpm: Add optional logging of TPM command durations Ed Swierk
                             ` (5 subsequent siblings)
  6 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-07-13 16:19 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

Mysterious TPM behavior can be difficult to track down through all the
layers of software. Add error messages for conditions that should
never happen. Also include the manufacturer ID along with other chip
data printed during init.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 drivers/char/tpm/tpm_tis_core.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index 8110b52..e62fdeb 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -217,6 +217,8 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 
 	expected = be32_to_cpu(*(__be32 *) (buf + 2));
 	if (expected > count) {
+		dev_err(&chip->dev, "Response too long (wanted %zd, got %d)\n",
+			count, expected);
 		size = -EIO;
 		goto out;
 	}
@@ -283,6 +285,8 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
 				  &priv->int_queue, false);
 		status = tpm_tis_status(chip);
 		if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
+			dev_err(&chip->dev, "Chip not accepting %zd bytes\n",
+				len - count);
 			rc = -EIO;
 			goto out_err;
 		}
@@ -297,6 +301,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
 			  &priv->int_queue, false);
 	status = tpm_tis_status(chip);
 	if (!itpm && (status & TPM_STS_DATA_EXPECT) != 0) {
+		dev_err(&chip->dev, "Chip not accepting last byte\n");
 		rc = -EIO;
 		goto out_err;
 	}
@@ -707,8 +712,9 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
 	if (rc < 0)
 		goto out_err;
 
-	dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n",
+	dev_info(dev, "%s TPM (manufacturer-id 0x%X, device-id 0x%X, rev-id %d)\n",
 		 (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2",
+		 priv->manufacturer_id,
 		 vendor >> 16, rid);
 
 	if (!(priv->flags & TPM_TIS_ITPM_POSSIBLE)) {
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v9 2/5] tpm: Add optional logging of TPM command durations
  2016-07-13 16:19         ` [PATCH v9 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  2016-07-13 16:19           ` [PATCH v9 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
@ 2016-07-13 16:19           ` Ed Swierk
  2016-07-13 16:19           ` [PATCH v9 3/5] tpm: Clean up reading of timeout and duration capabilities Ed Swierk
                             ` (4 subsequent siblings)
  6 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-07-13 16:19 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

Some TPMs violate their own advertised command durations. This is much
easier to debug with data about how long each command actually takes
to complete. Add debug messages that can be enabled by running

  echo -n 'module tpm +p' >/sys/kernel/debug/dynamic_debug/control

on a kernel configured with DYNAMIC_DEBUG=y.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 drivers/char/tpm/tpm-interface.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 5e3c1b6..a4beb53 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -335,13 +335,14 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 {
 	ssize_t rc;
 	u32 count, ordinal;
-	unsigned long stop;
+	unsigned long start, stop;
 
 	if (bufsiz > TPM_BUFSIZE)
 		bufsiz = TPM_BUFSIZE;
 
 	count = be32_to_cpu(*((__be32 *) (buf + 2)));
 	ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+	dev_dbg(&chip->dev, "starting command %d count %d\n", ordinal, count);
 	if (count == 0)
 		return -ENODATA;
 	if (count > bufsiz) {
@@ -362,18 +363,23 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 	if (chip->flags & TPM_CHIP_FLAG_IRQ)
 		goto out_recv;
 
+	start = jiffies;
 	if (chip->flags & TPM_CHIP_FLAG_TPM2)
-		stop = jiffies + tpm2_calc_ordinal_duration(chip, ordinal);
+		stop = start + tpm2_calc_ordinal_duration(chip, ordinal);
 	else
-		stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
+		stop = start + tpm_calc_ordinal_duration(chip, ordinal);
 	do {
 		u8 status = chip->ops->status(chip);
 		if ((status & chip->ops->req_complete_mask) ==
-		    chip->ops->req_complete_val)
+		    chip->ops->req_complete_val) {
+			dev_dbg(&chip->dev, "completed command %d in %d ms\n",
+				ordinal, jiffies_to_msecs(jiffies - start));
 			goto out_recv;
+		}
 
 		if (chip->ops->req_canceled(chip, status)) {
-			dev_err(&chip->dev, "Operation Canceled\n");
+			dev_err(&chip->dev, "canceled command %d after %d ms\n",
+				ordinal, jiffies_to_msecs(jiffies - start));
 			rc = -ECANCELED;
 			goto out;
 		}
@@ -383,7 +389,8 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 	} while (time_before(jiffies, stop));
 
 	chip->ops->cancel(chip);
-	dev_err(&chip->dev, "Operation Timed out\n");
+	dev_err(&chip->dev, "command %d timed out after %d ms\n", ordinal,
+		jiffies_to_msecs(jiffies - start));
 	rc = -ETIME;
 	goto out;
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v9 3/5] tpm: Clean up reading of timeout and duration capabilities
  2016-07-13 16:19         ` [PATCH v9 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  2016-07-13 16:19           ` [PATCH v9 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
  2016-07-13 16:19           ` [PATCH v9 2/5] tpm: Add optional logging of TPM command durations Ed Swierk
@ 2016-07-13 16:19           ` Ed Swierk
  2016-07-18 18:15             ` Jarkko Sakkinen
  2016-07-18 18:19             ` Jarkko Sakkinen
  2016-07-13 16:19           ` [PATCH v9 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
                             ` (3 subsequent siblings)
  6 siblings, 2 replies; 68+ messages in thread
From: Ed Swierk @ 2016-07-13 16:19 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

Call tpm_getcap() from tpm_get_timeouts() to eliminate redundant
code. Return all errors to the caller rather than swallowing them
(e.g. when tpm_transmit_cmd() returns nonzero).

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm-interface.c | 74 +++++++++++++++-------------------------
 1 file changed, 27 insertions(+), 47 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index a4beb53..dc492ee 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -460,9 +460,19 @@ ssize_t tpm_getcap(struct tpm_chip *chip, __be32 subcap_id, cap_t *cap,
 		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
 		tpm_cmd.params.getcap_in.subcap = subcap_id;
 	}
+
 	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
+
+	if (!rc &&
+	    ((subcap_id == TPM_CAP_PROP_TIS_TIMEOUT &&
+	      be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 20) ||
+	     (subcap_id == TPM_CAP_PROP_TIS_DURATION &&
+	      be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 16)))
+		rc = -EINVAL;
+
 	if (!rc)
 		*cap = tpm_cmd.params.getcap_out.cap;
+
 	return rc;
 }
 
@@ -503,10 +513,9 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
 
 int tpm_get_timeouts(struct tpm_chip *chip)
 {
-	struct tpm_cmd_t tpm_cmd;
+	cap_t cap;
 	unsigned long new_timeout[4];
 	unsigned long old_timeout[4];
-	struct duration_t *duration_cap;
 	ssize_t rc;
 
 	if (chip->flags & TPM_CHIP_FLAG_TPM2) {
@@ -524,42 +533,25 @@ int tpm_get_timeouts(struct tpm_chip *chip)
 		return 0;
 	}
 
-	tpm_cmd.header.in = tpm_getcap_header;
-	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
-	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
-	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
-
+	rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
+			"attempting to determine the timeouts");
 	if (rc == TPM_ERR_INVALID_POSTINIT) {
 		/* The TPM is not started, we are the first to talk to it.
 		   Execute a startup command. */
-		dev_info(&chip->dev, "Issuing TPM_STARTUP");
+		dev_info(&chip->dev, "Issuing TPM_STARTUP\n");
 		if (tpm_startup(chip, TPM_ST_CLEAR))
 			return rc;
 
-		tpm_cmd.header.in = tpm_getcap_header;
-		tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
-		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-		tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
-		rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
-				  NULL);
-	}
-	if (rc) {
-		dev_err(&chip->dev,
-			"A TPM error (%zd) occurred attempting to determine the timeouts\n",
-			rc);
-		goto duration;
+		rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
+				"attempting to determine the timeouts");
 	}
+	if (rc)
+		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) + 4 * sizeof(u32))
-		return -EINVAL;
-
-	old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
-	old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
-	old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
-	old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
+	old_timeout[0] = be32_to_cpu(cap.timeout.a);
+	old_timeout[1] = be32_to_cpu(cap.timeout.b);
+	old_timeout[2] = be32_to_cpu(cap.timeout.c);
+	old_timeout[3] = be32_to_cpu(cap.timeout.d);
 	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
 
 	/*
@@ -597,29 +589,17 @@ int tpm_get_timeouts(struct tpm_chip *chip)
 	chip->timeout_c = usecs_to_jiffies(new_timeout[2]);
 	chip->timeout_d = usecs_to_jiffies(new_timeout[3]);
 
-duration:
-	tpm_cmd.header.in = tpm_getcap_header;
-	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
-	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
-	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
-
-	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
-			      "attempting to determine the durations");
+	rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_DURATION, &cap,
+			"attempting to determine the durations");
 	if (rc)
 		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 -EINVAL;
-
-	duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
 	chip->duration[TPM_SHORT] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
+		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_short));
 	chip->duration[TPM_MEDIUM] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
+		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
 	chip->duration[TPM_LONG] =
-	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
+		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
 
 	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
 	 * value wrong and apparently reports msecs rather than usecs. So we
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v9 4/5] tpm: Allow TPM chip drivers to override reported command durations
  2016-07-13 16:19         ` [PATCH v9 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
                             ` (2 preceding siblings ...)
  2016-07-13 16:19           ` [PATCH v9 3/5] tpm: Clean up reading of timeout and duration capabilities Ed Swierk
@ 2016-07-13 16:19           ` Ed Swierk
  2016-07-13 17:04             ` kbuild test robot
  2016-07-18 18:40             ` Jarkko Sakkinen
  2016-07-13 16:19           ` [PATCH v9 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
                             ` (2 subsequent siblings)
  6 siblings, 2 replies; 68+ messages in thread
From: Ed Swierk @ 2016-07-13 16:19 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

Some TPM chips report bogus command durations in their capabilities,
just as others report incorrect timeouts. Rework tpm_get_timeouts() to
allow chip drivers to override either via a single callback. Also
clean up handling of TPMs that report milliseconds instead of
microseconds.

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm-interface.c | 148 ++++++++++++++++++++++-----------------
 drivers/char/tpm/tpm_tis_core.c  |  32 +++------
 include/linux/tpm.h              |   3 +-
 3 files changed, 94 insertions(+), 89 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index dc492ee..9dafc25 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -513,10 +513,10 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
 
 int tpm_get_timeouts(struct tpm_chip *chip)
 {
-	cap_t cap;
-	unsigned long new_timeout[4];
-	unsigned long old_timeout[4];
-	ssize_t rc;
+	cap_t cap1, cap2;
+	int rc;
+	unsigned long orig_timeout_a, orig_timeout_b, orig_timeout_c,
+		orig_timeout_d, orig_duration[3];
 
 	if (chip->flags & TPM_CHIP_FLAG_TPM2) {
 		/* Fixed timeouts for TPM2 */
@@ -533,7 +533,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
 		return 0;
 	}
 
-	rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
+	rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap1,
 			"attempting to determine the timeouts");
 	if (rc == TPM_ERR_INVALID_POSTINIT) {
 		/* The TPM is not started, we are the first to talk to it.
@@ -542,77 +542,95 @@ int tpm_get_timeouts(struct tpm_chip *chip)
 		if (tpm_startup(chip, TPM_ST_CLEAR))
 			return rc;
 
-		rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
+		rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap1,
 				"attempting to determine the timeouts");
 	}
 	if (rc)
 		return rc;
 
-	old_timeout[0] = be32_to_cpu(cap.timeout.a);
-	old_timeout[1] = be32_to_cpu(cap.timeout.b);
-	old_timeout[2] = be32_to_cpu(cap.timeout.c);
-	old_timeout[3] = be32_to_cpu(cap.timeout.d);
-	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
-
-	/*
-	 * Provide ability for vendor overrides of timeout values in case
-	 * of misreporting.
-	 */
-	if (chip->ops->update_timeouts != NULL)
-		chip->timeout_adjusted =
-			chip->ops->update_timeouts(chip, new_timeout);
-
-	if (!chip->timeout_adjusted) {
-		/* Don't overwrite default if value is 0 */
-		if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
-			int i;
-
-			/* timeouts in msec rather usec */
-			for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
-				new_timeout[i] *= 1000;
-			chip->timeout_adjusted = true;
-		}
-	}
-
-	/* Report adjusted timeouts */
-	if (chip->timeout_adjusted) {
-		dev_info(&chip->dev,
-			 HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
-			 old_timeout[0], new_timeout[0],
-			 old_timeout[1], new_timeout[1],
-			 old_timeout[2], new_timeout[2],
-			 old_timeout[3], new_timeout[3]);
-	}
-
-	chip->timeout_a = usecs_to_jiffies(new_timeout[0]);
-	chip->timeout_b = usecs_to_jiffies(new_timeout[1]);
-	chip->timeout_c = usecs_to_jiffies(new_timeout[2]);
-	chip->timeout_d = usecs_to_jiffies(new_timeout[3]);
-
-	rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_DURATION, &cap,
+	rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_DURATION, &cap2,
 			"attempting to determine the durations");
 	if (rc)
 		return rc;
 
-	chip->duration[TPM_SHORT] =
-		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_short));
-	chip->duration[TPM_MEDIUM] =
-		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
-	chip->duration[TPM_LONG] =
-		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
+	be32_to_cpus(&cap1.timeout.a);
+	be32_to_cpus(&cap1.timeout.b);
+	be32_to_cpus(&cap1.timeout.c);
+	be32_to_cpus(&cap1.timeout.d);
+	chip->timeout_a = usecs_to_jiffies(cap1.timeout.a);
+	chip->timeout_b = usecs_to_jiffies(cap1.timeout.b);
+	chip->timeout_c = usecs_to_jiffies(cap1.timeout.c);
+	chip->timeout_d = usecs_to_jiffies(cap1.timeout.d);
 
-	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
-	 * value wrong and apparently reports msecs rather than usecs. So we
-	 * fix up the resulting too-small TPM_SHORT value to make things work.
-	 * We also scale the TPM_MEDIUM and -_LONG values by 1000.
-	 */
-	if (chip->duration[TPM_SHORT] < (HZ / 100)) {
-		chip->duration[TPM_SHORT] = HZ;
-		chip->duration[TPM_MEDIUM] *= 1000;
-		chip->duration[TPM_LONG] *= 1000;
-		chip->duration_adjusted = true;
-		dev_info(&chip->dev, "Adjusting TPM timeout parameters.");
+	/* Some TPMs report timeouts in milliseconds rather than
+	   microseconds. Use a value between 1 and 1000 as an
+	   indication that this is the case. */
+	if (cap1.timeout.a > 0 && cap1.timeout.a < 1000) {
+		chip->timeout_a = msecs_to_jiffies(cap1.timeout.a);
+		chip->timeout_b = msecs_to_jiffies(cap1.timeout.b);
+		chip->timeout_c = msecs_to_jiffies(cap1.timeout.c);
+		chip->timeout_d = msecs_to_jiffies(cap1.timeout.d);
+		chip->timeout_adjusted = true;
 	}
+
+	be32_to_cpus(&cap2.duration.tpm_short);
+	be32_to_cpus(&cap2.duration.tpm_medium);
+	be32_to_cpus(&cap2.duration.tpm_long);
+	chip->duration[TPM_SHORT] =
+		usecs_to_jiffies(cap2.duration.tpm_short);
+	chip->duration[TPM_MEDIUM] =
+		usecs_to_jiffies(cap2.duration.tpm_medium);
+	chip->duration[TPM_LONG] =
+		usecs_to_jiffies(cap2.duration.tpm_long);
+
+	orig_timeout_a = chip->timeout_a;
+	orig_timeout_b = chip->timeout_b;
+	orig_timeout_c = chip->timeout_c;
+	orig_timeout_d = chip->timeout_d;
+	memcpy(orig_duration, chip->duration, 3 * sizeof(unsigned long));
+
+	/* Interpret duration values between 1 and 10000 as
+	   milliseconds to deal with TPMs like the Broadcom BCM0102 in
+	   the Dell Latitude D820. */
+	if (cap2.duration.tpm_short > 0 && cap2.duration.tpm_short < 10000) {
+		chip->duration[TPM_SHORT] =
+			msecs_to_jiffies(cap2.duration.tpm_short);
+		chip->duration[TPM_MEDIUM] =
+			msecs_to_jiffies(cap2.duration.tpm_medium);
+		chip->duration[TPM_LONG] =
+			msecs_to_jiffies(cap2.duration.tpm_long);
+		chip->duration_adjusted = true;
+	}
+
+	if (chip->ops->update_timeouts != NULL)
+		chip->ops->update_timeouts(chip);
+
+	if (chip->timeout_adjusted) {
+		dev_info(&chip->dev,
+			 HW_ERR "Adjusted timeouts: A %u->%uus B %u->%uus"
+			 " C %u->%uus D %u->%uus\n",
+			 jiffies_to_usecs(orig_timeout_a),
+			 jiffies_to_usecs(chip->timeout_a),
+			 jiffies_to_usecs(orig_timeout_b),
+			 jiffies_to_usecs(chip->timeout_b),
+			 jiffies_to_usecs(orig_timeout_c),
+			 jiffies_to_usecs(chip->timeout_c),
+			 jiffies_to_usecs(orig_timeout_d),
+			 jiffies_to_usecs(chip->timeout_d));
+	}
+
+	if (chip->duration_adjusted) {
+		dev_info(&chip->dev,
+			 HW_ERR "Adjusted durations: short %u->%uus"
+			 " medium %u->%uus long %u->%uus\n",
+			 jiffies_to_usecs(orig_duration[TPM_SHORT]),
+			 jiffies_to_usecs(chip->duration[TPM_SHORT]),
+			 jiffies_to_usecs(orig_duration[TPM_MEDIUM]),
+			 jiffies_to_usecs(chip->duration[TPM_MEDIUM]),
+			 jiffies_to_usecs(orig_duration[TPM_LONG]),
+			 jiffies_to_usecs(chip->duration[TPM_LONG]));
+	}
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(tpm_get_timeouts);
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index e62fdeb..f013664 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -398,37 +398,25 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
 	return rc;
 }
 
-struct tis_vendor_timeout_override {
-	u32 did_vid;
-	unsigned long timeout_us[4];
-};
-
-static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
-	/* Atmel 3204 */
-	{ 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
-			(TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
-};
-
-static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
-				    unsigned long *timeout_cap)
+static void tpm_tis_update_timeouts(struct tpm_chip *chip)
 {
 	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
-	int i, rc;
+	int rc;
 	u32 did_vid;
 
 	rc = tpm_tis_read32(priv, TPM_DID_VID(0), &did_vid);
 	if (rc < 0)
 		return rc;
 
-	for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
-		if (vendor_timeout_overrides[i].did_vid != did_vid)
-			continue;
-		memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
-		       sizeof(vendor_timeout_overrides[i].timeout_us));
-		return true;
+	switch (did_vid) {
+	case 0x32041114: /* Atmel 3204 */
+		chip->timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+		chip->timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
+		chip->timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+		chip->timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+		chip->timeout_adjusted = true;
+		break;
 	}
-
-	return false;
 }
 
 /*
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 706e63e..2380ebf 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -41,8 +41,7 @@ struct tpm_class_ops {
 	int (*send) (struct tpm_chip *chip, u8 *buf, size_t len);
 	void (*cancel) (struct tpm_chip *chip);
 	u8 (*status) (struct tpm_chip *chip);
-	bool (*update_timeouts)(struct tpm_chip *chip,
-				unsigned long *timeout_cap);
+	void (*update_timeouts)(struct tpm_chip *chip);
 
 };
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v9 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup
  2016-07-13 16:19         ` [PATCH v9 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
                             ` (3 preceding siblings ...)
  2016-07-13 16:19           ` [PATCH v9 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
@ 2016-07-13 16:19           ` Ed Swierk
  2016-07-13 16:44           ` [PATCH v9 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
  2016-07-18 18:07           ` Jarkko Sakkinen
  6 siblings, 0 replies; 68+ messages in thread
From: Ed Swierk @ 2016-07-13 16:19 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: jarkko.sakkinen, jgunthorpe, stefanb, eswierk

The STMicro ST19NP18-TPM sometimes takes much longer to execute
commands than it reports in its capabilities. For example, command 186
(TPM_FlushSpecific) has been observed to take 14560 msec to complete,
far longer than the 3000 msec limit for "short" commands reported by
the chip. The behavior has also been seen with command 101
(TPM_GetCapability).

Worse, when the tpm_tis driver attempts to cancel the current command
(by writing commandReady = 1 to TPM_STS_x), the chip locks up
completely, returning all-1s from all memory-mapped register
reads. The lockup can be cleared only by resetting the system.

The occurrence of this excessive command duration depends on the
sequence of commands preceding it. One sequence is creating at least 2
new keys via TPM_CreateWrapKey, then letting the TPM idle for at least
30 seconds, then loading a key via TPM_LoadKey2. The next
TPM_FlushSpecific occasionally takes tens of seconds to
complete. Another sequence is creating many keys in a row without
pause. The TPM_CreateWrapKey operation gets much slower after the
first few iterations, as one would expect when the pool of precomputed
keys is exhausted. Then after a 35-second pause, the same TPM_LoadKey2
followed by TPM_FlushSpecific sequence triggers the behavior.

Our working theory is that this older TPM sometimes pauses to
precompute keys, which modern chips implement as a background
process. Without access to the chip's implementation details it's
impossible to know whether any commands are immune to being blocked by
this process. So it seems safest to ignore the chip's reported command
durations, and use a value much higher than any observed duration,
like 180 sec (which is the duration this chip reports for "long"
commands).

Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
---
 drivers/char/tpm/tpm_tis_core.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index f013664..84d6008 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -416,6 +416,12 @@ static void tpm_tis_update_timeouts(struct tpm_chip *chip)
 		chip->timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
 		chip->timeout_adjusted = true;
 		break;
+	case 0x0000104a: /* STMicro ST19NP18-TPM */
+		chip->duration[TPM_SHORT] = 180 * HZ;
+		chip->duration[TPM_MEDIUM] = 180 * HZ;
+		chip->duration[TPM_LONG] = 180 * HZ;
+		chip->duration_adjusted = true;
+		break;
 	}
 }
 
-- 
1.9.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v9 0/5] tpm: Command duration logging and chip-specific override
  2016-07-13 16:19         ` [PATCH v9 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
                             ` (4 preceding siblings ...)
  2016-07-13 16:19           ` [PATCH v9 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
@ 2016-07-13 16:44           ` Ed Swierk
  2016-07-13 17:36             ` Jason Gunthorpe
  2016-07-18 18:07           ` Jarkko Sakkinen
  6 siblings, 1 reply; 68+ messages in thread
From: Ed Swierk @ 2016-07-13 16:44 UTC (permalink / raw)
  To: tpmdd-devel, linux-kernel, linux-security-module
  Cc: Jarkko Sakkinen, Jason Gunthorpe, Stefan Berger, Ed Swierk

On Wed, Jul 13, 2016 at 9:19 AM, Ed Swierk <eswierk@skyportsystems.com> wrote:
> v9: Include command duration in existing error messages rather than
> logging an extra debug message. Rebase onto Jarkko's tree.

Incidentally, with Jarkko's tree the tpm_tis module refuses to
initialize (with or without force=1):

tpm_tis 00:03: can't request region for resource [mem 0xfed40000-0xfed44fff]
tpm_tis: probe of 00:03 failed with error -16

The memory region is not marked reserved by the BIOS:

e820: BIOS-provided physical RAM map:
Xen: [mem 0x0000000000000000-0x000000000005ffff] usable
Xen: [mem 0x0000000000060000-0x0000000000067fff] reserved
Xen: [mem 0x0000000000068000-0x000000000009afff] usable
Xen: [mem 0x00000000000a0000-0x00000000000fffff] reserved
Xen: [mem 0x0000000000100000-0x0000000075b01fff] usable
Xen: [mem 0x0000000075baf000-0x000000007a1f5fff] usable
Xen: [mem 0x000000007b7d7000-0x000000007b7fffff] usable
Xen: [mem 0x000000007bf00000-0x000000007bffffff] usable
Xen: [mem 0x00000000c7ffc000-0x00000000c7ffcfff] reserved
Xen: [mem 0x00000000fbffc000-0x00000000fbffcfff] reserved
Xen: [mem 0x00000000fec00000-0x00000000fec01fff] reserved
Xen: [mem 0x00000000fec40000-0x00000000fec40fff] reserved
Xen: [mem 0x00000000fed20000-0x00000000fed2ffff] usable
Xen: [mem 0x00000000fee00000-0x00000000feefffff] reserved
Xen: [mem 0x0000000100000000-0x0000000505deafff] usable

With force=1, /proc/iomem shows:

fed30000-fedfffff : RAM buffer
  fed40000-fed44fff : tpm_tis

I can work around this problem by passing memmap=0x5000$0xfed40000 on
the kernel command line.

--Ed

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v9 4/5] tpm: Allow TPM chip drivers to override reported command durations
  2016-07-13 16:19           ` [PATCH v9 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
@ 2016-07-13 17:04             ` kbuild test robot
  2016-07-18 18:40             ` Jarkko Sakkinen
  1 sibling, 0 replies; 68+ messages in thread
From: kbuild test robot @ 2016-07-13 17:04 UTC (permalink / raw)
  To: Ed Swierk
  Cc: kbuild-all, tpmdd-devel, linux-kernel, linux-security-module,
	jarkko.sakkinen, jgunthorpe, stefanb, eswierk

[-- Attachment #1: Type: text/plain, Size: 3257 bytes --]

Hi,

[auto build test WARNING on next-20160712]
[cannot apply to char-misc/char-misc-testing v4.7-rc7 v4.7-rc6 v4.7-rc5 v4.7-rc7]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Ed-Swierk/tpm-Command-duration-logging-and-chip-specific-override/20160714-002547
config: i386-randconfig-s1-201628 (attached as .config)
compiler: gcc-6 (Debian 6.1.1-1) 6.1.1 20160430
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All warnings (new ones prefixed by >>):

   drivers/char/tpm/tpm_tis_core.c: In function 'tpm_tis_update_timeouts':
>> drivers/char/tpm/tpm_tis_core.c:414:10: warning: 'return' with a value, in function returning void
      return rc;
             ^~
   drivers/char/tpm/tpm_tis_core.c:406:13: note: declared here
    static void tpm_tis_update_timeouts(struct tpm_chip *chip)
                ^~~~~~~~~~~~~~~~~~~~~~~

vim +/return +414 drivers/char/tpm/tpm_tis_core.c

41a5e1cf Christophe Ricard 2016-05-19  398  	if (!priv->irq_tested)
41a5e1cf Christophe Ricard 2016-05-19  399  		msleep(1);
41a5e1cf Christophe Ricard 2016-05-19  400  	if (!priv->irq_tested)
41a5e1cf Christophe Ricard 2016-05-19  401  		disable_interrupts(chip);
41a5e1cf Christophe Ricard 2016-05-19  402  	priv->irq_tested = true;
41a5e1cf Christophe Ricard 2016-05-19  403  	return rc;
41a5e1cf Christophe Ricard 2016-05-19  404  }
41a5e1cf Christophe Ricard 2016-05-19  405  
a7da7fe7 Ed Swierk         2016-07-13  406  static void tpm_tis_update_timeouts(struct tpm_chip *chip)
41a5e1cf Christophe Ricard 2016-05-19  407  {
41a5e1cf Christophe Ricard 2016-05-19  408  	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
a7da7fe7 Ed Swierk         2016-07-13  409  	int rc;
41a5e1cf Christophe Ricard 2016-05-19  410  	u32 did_vid;
41a5e1cf Christophe Ricard 2016-05-19  411  
41a5e1cf Christophe Ricard 2016-05-19  412  	rc = tpm_tis_read32(priv, TPM_DID_VID(0), &did_vid);
41a5e1cf Christophe Ricard 2016-05-19  413  	if (rc < 0)
41a5e1cf Christophe Ricard 2016-05-19 @414  		return rc;
41a5e1cf Christophe Ricard 2016-05-19  415  
a7da7fe7 Ed Swierk         2016-07-13  416  	switch (did_vid) {
a7da7fe7 Ed Swierk         2016-07-13  417  	case 0x32041114: /* Atmel 3204 */
a7da7fe7 Ed Swierk         2016-07-13  418  		chip->timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
a7da7fe7 Ed Swierk         2016-07-13  419  		chip->timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
a7da7fe7 Ed Swierk         2016-07-13  420  		chip->timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
a7da7fe7 Ed Swierk         2016-07-13  421  		chip->timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
a7da7fe7 Ed Swierk         2016-07-13  422  		chip->timeout_adjusted = true;

:::::: The code at line 414 was first introduced by commit
:::::: 41a5e1cf1fe151ed48b4b3106c748d03a85133ce tpm/tpm_tis: Split tpm_tis driver into a core and TCG TIS compliant phy

:::::: TO: Christophe Ricard <christophe.ricard@gmail.com>
:::::: CC: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 22818 bytes --]

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v9 0/5] tpm: Command duration logging and chip-specific override
  2016-07-13 16:44           ` [PATCH v9 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
@ 2016-07-13 17:36             ` Jason Gunthorpe
  2016-07-13 20:00               ` Ed Swierk
  0 siblings, 1 reply; 68+ messages in thread
From: Jason Gunthorpe @ 2016-07-13 17:36 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, linux-kernel, linux-security-module,
	Jarkko Sakkinen, Stefan Berger

On Wed, Jul 13, 2016 at 09:44:05AM -0700, Ed Swierk wrote:
> On Wed, Jul 13, 2016 at 9:19 AM, Ed Swierk <eswierk@skyportsystems.com> wrote:
> > v9: Include command duration in existing error messages rather than
> > logging an extra debug message. Rebase onto Jarkko's tree.
> 
> Incidentally, with Jarkko's tree the tpm_tis module refuses to
> initialize (with or without force=1):
> 
> tpm_tis 00:03: can't request region for resource [mem 0xfed40000-0xfed44fff]
> tpm_tis: probe of 00:03 failed with error -16
>
> The memory region is not marked reserved by the BIOS:
> fed30000-fedfffff : RAM buffer

I think your bios is broken?

A working BIOS will look like this:

 $ cat /proc/iomem  | grep -i fed400
 fed40000-fed44fff : pnp 00:00

It sets aside the struct resource during pnp:

 [    0.097318] pnp: PnP ACPI init
 [    0.097366] system 00:00: [mem 0xfed40000-0xfed44fff] has been reserved

What did your system do?

You should see prints like this:

                printk(KERN_DEBUG
                       "e820: reserve RAM buffer [mem %#010llx-%#010llx]\n",
                       start, end);

Which only happen if E820_RAM is set, which is certainly not right for
TPM memory.

I don't know what kernel convention is to handle these sorts of
defects?

Is the use of the memmap kernel command line an appropriate work
around?

Jason

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v9 0/5] tpm: Command duration logging and chip-specific override
  2016-07-13 17:36             ` Jason Gunthorpe
@ 2016-07-13 20:00               ` Ed Swierk
  2016-07-13 20:58                 ` Eric W. Biederman
  2016-07-13 20:59                 ` Jason Gunthorpe
  0 siblings, 2 replies; 68+ messages in thread
From: Ed Swierk @ 2016-07-13 20:00 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: tpmdd-devel, linux-kernel, linux-security-module,
	Jarkko Sakkinen, Stefan Berger

On Wed, Jul 13, 2016 at 10:36 AM, Jason Gunthorpe
<jgunthorpe@obsidianresearch.com> wrote:
> I think your bios is broken?

The BIOS is broken in many ways. I already have to pass
memmap=256M$0x80000000, otherwise PCIe extended config space
(MMCONFIG) is inaccessible. Also I found memmap=0x7000$0x7a7d0000
works around "APEI: Can not request [mem 0x7a7d0018-0x7a7d0067] for
APEI ERST registers", as the BIOS seems to be mistakenly reserving
0x7b7d0000-7b7d7000 instead.

> A working BIOS will look like this:
>
>  $ cat /proc/iomem  | grep -i fed400
>  fed40000-fed44fff : pnp 00:00
>
> It sets aside the struct resource during pnp:
>
>  [    0.097318] pnp: PnP ACPI init
>  [    0.097366] system 00:00: [mem 0xfed40000-0xfed44fff] has been reserved
>
> What did your system do?
>
> You should see prints like this:
>
>                 printk(KERN_DEBUG
>                        "e820: reserve RAM buffer [mem %#010llx-%#010llx]\n",
>                        start, end);
>
> Which only happen if E820_RAM is set, which is certainly not right for
> TPM memory.

On my system I see

e820: reserve RAM buffer [mem 0x0009b000-0x0009ffff]
e820: reserve RAM buffer [mem 0x75b02000-0x77ffffff]
e820: reserve RAM buffer [mem 0x7a1f6000-0x7bffffff]
e820: reserve RAM buffer [mem 0x7b800000-0x7bffffff]
e820: reserve RAM buffer [mem 0xfed30000-0xffffffff]
e820: reserve RAM buffer [mem 0x505deb000-0x507ffffff]

which doesn't make a whole lot of sense, as several of those areas
overlap each other, never mind devices.

> I don't know what kernel convention is to handle these sorts of
> defects?
>
> Is the use of the memmap kernel command line an appropriate work
> around?

It works for me, though I would like to know if there's another approach.

--Ed

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v9 0/5] tpm: Command duration logging and chip-specific override
  2016-07-13 20:00               ` Ed Swierk
@ 2016-07-13 20:58                 ` Eric W. Biederman
  2016-07-13 20:59                 ` Jason Gunthorpe
  1 sibling, 0 replies; 68+ messages in thread
From: Eric W. Biederman @ 2016-07-13 20:58 UTC (permalink / raw)
  To: Ed Swierk
  Cc: Jason Gunthorpe, tpmdd-devel, linux-kernel,
	linux-security-module, Jarkko Sakkinen, Stefan Berger

Ed Swierk <eswierk@skyportsystems.com> writes:

> On Wed, Jul 13, 2016 at 10:36 AM, Jason Gunthorpe
> <jgunthorpe@obsidianresearch.com> wrote:
>> I think your bios is broken?
>
> The BIOS is broken in many ways. I already have to pass
> memmap=256M$0x80000000, otherwise PCIe extended config space
> (MMCONFIG) is inaccessible. Also I found memmap=0x7000$0x7a7d0000
> works around "APEI: Can not request [mem 0x7a7d0018-0x7a7d0067] for
> APEI ERST registers", as the BIOS seems to be mistakenly reserving
> 0x7b7d0000-7b7d7000 instead.
>
>> A working BIOS will look like this:
>>
>>  $ cat /proc/iomem  | grep -i fed400
>>  fed40000-fed44fff : pnp 00:00
>>
>> It sets aside the struct resource during pnp:
>>
>>  [    0.097318] pnp: PnP ACPI init
>>  [    0.097366] system 00:00: [mem 0xfed40000-0xfed44fff] has been reserved
>>
>> What did your system do?
>>
>> You should see prints like this:
>>
>>                 printk(KERN_DEBUG
>>                        "e820: reserve RAM buffer [mem %#010llx-%#010llx]\n",
>>                        start, end);
>>
>> Which only happen if E820_RAM is set, which is certainly not right for
>> TPM memory.
>
> On my system I see
>
> e820: reserve RAM buffer [mem 0x0009b000-0x0009ffff]
> e820: reserve RAM buffer [mem 0x75b02000-0x77ffffff]
> e820: reserve RAM buffer [mem 0x7a1f6000-0x7bffffff]
> e820: reserve RAM buffer [mem 0x7b800000-0x7bffffff]
> e820: reserve RAM buffer [mem 0xfed30000-0xffffffff]
> e820: reserve RAM buffer [mem 0x505deb000-0x507ffffff]
>
> which doesn't make a whole lot of sense, as several of those areas
> overlap each other, never mind devices.
>
>> I don't know what kernel convention is to handle these sorts of
>> defects?
>>
>> Is the use of the memmap kernel command line an appropriate work
>> around?
>
> It works for me, though I would like to know if there's another
> approach.

There is always poke your BIOS vendor until they deliver code that is
not so b0rked it can not be used.

You can also add a quirk based on the BIOS's mainboard identification
string that fixes up the data provided by the BIOS.  I remember a fair
number of those dealing with reboot behavior and the like.

Eric

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v9 0/5] tpm: Command duration logging and chip-specific override
  2016-07-13 20:00               ` Ed Swierk
  2016-07-13 20:58                 ` Eric W. Biederman
@ 2016-07-13 20:59                 ` Jason Gunthorpe
  1 sibling, 0 replies; 68+ messages in thread
From: Jason Gunthorpe @ 2016-07-13 20:59 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, linux-kernel, linux-security-module,
	Jarkko Sakkinen, Stefan Berger

On Wed, Jul 13, 2016 at 01:00:28PM -0700, Ed Swierk wrote:
> On Wed, Jul 13, 2016 at 10:36 AM, Jason Gunthorpe
> <jgunthorpe@obsidianresearch.com> wrote:
> > I think your bios is broken?
> 
> The BIOS is broken in many ways. I already have to pass
> memmap=256M$0x80000000, otherwise PCIe extended config space
> (MMCONFIG) is inaccessible. Also I found memmap=0x7000$0x7a7d0000
> works around "APEI: Can not request [mem 0x7a7d0018-0x7a7d0067] for
> APEI ERST registers", as the BIOS seems to be mistakenly reserving
> 0x7b7d0000-7b7d7000 instead.

Is it is possible whatever causes the 'reserve RAM buffer' behavior in
the kernel is wonky with your BIOS? That seems to be a special Linux
action..

Could it be Xen related? The Xen hypervisor replaces the physical ram
map for the dom0 guest, which is why you get these sorts of prints:
 Xen: [mem 0x0000000000000000-0x000000000005ffff] usable
and not:
[    0.000000] BIOS-e820: [mem 0x0000000000000000-0x0000000000057fff] usable

Jason

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v9 0/5] tpm: Command duration logging and chip-specific override
  2016-07-13 16:19         ` [PATCH v9 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
                             ` (5 preceding siblings ...)
  2016-07-13 16:44           ` [PATCH v9 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
@ 2016-07-18 18:07           ` Jarkko Sakkinen
  6 siblings, 0 replies; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-07-18 18:07 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, linux-kernel, linux-security-module, jgunthorpe, stefanb

On Wed, Jul 13, 2016 at 09:19:31AM -0700, Ed Swierk wrote:
> v9: Include command duration in existing error messages rather than
> logging an extra debug message. Rebase onto Jarkko's tree.
> 
> v8: Fix v7 goof-up in tpm_getcap().
> 
> v7: Use tpm_getcap() instead of a redundant new function.
> 
> v6: Split tpm_get_cap_prop() out of tpm_get_timeouts(); always return
> error on TPM command failure.
> 
> v5: Use msecs_to_jiffies() instead of * HZ / 1000.
> 
> v4: Rework tpm_get_timeouts() to allow overriding both timeouts and
> durations via a single callback.
> 
> This series
> - improves TPM command error reporting
> - adds optional logging of TPM command durations
> - allows chip-specific override of command durations as well as protocol
>   timeouts
> - overrides ST19NP18 TPM command duration to avoid lockups

For the next version please write something more readable. Describe the
motivation and solution in big picture, why and how. This list does no
good because it just mimics the list of patches.

/Jarkko

> Ed Swierk (5):
>   tpm_tis: Improve reporting of IO errors
>   tpm: Add optional logging of TPM command durations
>   tpm: Clean up reading of timeout and duration capabilities
>   tpm: Allow TPM chip drivers to override reported command durations
>   tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup
> 
>  drivers/char/tpm/tpm-interface.c | 219 ++++++++++++++++++++-------------------
>  drivers/char/tpm/tpm_tis_core.c  |  46 ++++----
>  include/linux/tpm.h              |   3 +-
>  3 files changed, 136 insertions(+), 132 deletions(-)
> 
> -- 
> 1.9.1
> 

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v9 3/5] tpm: Clean up reading of timeout and duration capabilities
  2016-07-13 16:19           ` [PATCH v9 3/5] tpm: Clean up reading of timeout and duration capabilities Ed Swierk
@ 2016-07-18 18:15             ` Jarkko Sakkinen
  2016-07-18 18:19             ` Jarkko Sakkinen
  1 sibling, 0 replies; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-07-18 18:15 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, linux-kernel, linux-security-module, jgunthorpe, stefanb

On Wed, Jul 13, 2016 at 09:19:34AM -0700, Ed Swierk wrote:
> Call tpm_getcap() from tpm_get_timeouts() to eliminate redundant
> code. Return all errors to the caller rather than swallowing them
> (e.g. when tpm_transmit_cmd() returns nonzero).
> 
> Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>

Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>

/Jarkko

> ---
>  drivers/char/tpm/tpm-interface.c | 74 +++++++++++++++-------------------------
>  1 file changed, 27 insertions(+), 47 deletions(-)
> 
> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
> index a4beb53..dc492ee 100644
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -460,9 +460,19 @@ ssize_t tpm_getcap(struct tpm_chip *chip, __be32 subcap_id, cap_t *cap,
>  		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
>  		tpm_cmd.params.getcap_in.subcap = subcap_id;
>  	}
> +
>  	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
> +
> +	if (!rc &&
> +	    ((subcap_id == TPM_CAP_PROP_TIS_TIMEOUT &&
> +	      be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 20) ||
> +	     (subcap_id == TPM_CAP_PROP_TIS_DURATION &&
> +	      be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 16)))
> +		rc = -EINVAL;
> +
>  	if (!rc)
>  		*cap = tpm_cmd.params.getcap_out.cap;
> +
>  	return rc;
>  }
>  
> @@ -503,10 +513,9 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
>  
>  int tpm_get_timeouts(struct tpm_chip *chip)
>  {
> -	struct tpm_cmd_t tpm_cmd;
> +	cap_t cap;
>  	unsigned long new_timeout[4];
>  	unsigned long old_timeout[4];
> -	struct duration_t *duration_cap;
>  	ssize_t rc;
>  
>  	if (chip->flags & TPM_CHIP_FLAG_TPM2) {
> @@ -524,42 +533,25 @@ int tpm_get_timeouts(struct tpm_chip *chip)
>  		return 0;
>  	}
>  
> -	tpm_cmd.header.in = tpm_getcap_header;
> -	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
> -	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> -	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
> -	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
> -
> +	rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
> +			"attempting to determine the timeouts");
>  	if (rc == TPM_ERR_INVALID_POSTINIT) {
>  		/* The TPM is not started, we are the first to talk to it.
>  		   Execute a startup command. */
> -		dev_info(&chip->dev, "Issuing TPM_STARTUP");
> +		dev_info(&chip->dev, "Issuing TPM_STARTUP\n");
>  		if (tpm_startup(chip, TPM_ST_CLEAR))
>  			return rc;
>  
> -		tpm_cmd.header.in = tpm_getcap_header;
> -		tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
> -		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> -		tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
> -		rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
> -				  NULL);
> -	}
> -	if (rc) {
> -		dev_err(&chip->dev,
> -			"A TPM error (%zd) occurred attempting to determine the timeouts\n",
> -			rc);
> -		goto duration;
> +		rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
> +				"attempting to determine the timeouts");
>  	}
> +	if (rc)
> +		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) + 4 * sizeof(u32))
> -		return -EINVAL;
> -
> -	old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
> -	old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
> -	old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
> -	old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
> +	old_timeout[0] = be32_to_cpu(cap.timeout.a);
> +	old_timeout[1] = be32_to_cpu(cap.timeout.b);
> +	old_timeout[2] = be32_to_cpu(cap.timeout.c);
> +	old_timeout[3] = be32_to_cpu(cap.timeout.d);
>  	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
>  
>  	/*
> @@ -597,29 +589,17 @@ int tpm_get_timeouts(struct tpm_chip *chip)
>  	chip->timeout_c = usecs_to_jiffies(new_timeout[2]);
>  	chip->timeout_d = usecs_to_jiffies(new_timeout[3]);
>  
> -duration:
> -	tpm_cmd.header.in = tpm_getcap_header;
> -	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
> -	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> -	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
> -
> -	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
> -			      "attempting to determine the durations");
> +	rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_DURATION, &cap,
> +			"attempting to determine the durations");
>  	if (rc)
>  		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 -EINVAL;
> -
> -	duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
>  	chip->duration[TPM_SHORT] =
> -	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
> +		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_short));
>  	chip->duration[TPM_MEDIUM] =
> -	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
> +		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
>  	chip->duration[TPM_LONG] =
> -	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
> +		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
>  
>  	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
>  	 * value wrong and apparently reports msecs rather than usecs. So we
> -- 
> 1.9.1
> 

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v9 3/5] tpm: Clean up reading of timeout and duration capabilities
  2016-07-13 16:19           ` [PATCH v9 3/5] tpm: Clean up reading of timeout and duration capabilities Ed Swierk
  2016-07-18 18:15             ` Jarkko Sakkinen
@ 2016-07-18 18:19             ` Jarkko Sakkinen
  2016-07-18 18:20               ` Jarkko Sakkinen
  1 sibling, 1 reply; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-07-18 18:19 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, linux-kernel, linux-security-module, jgunthorpe, stefanb

On Wed, Jul 13, 2016 at 09:19:34AM -0700, Ed Swierk wrote:
> Call tpm_getcap() from tpm_get_timeouts() to eliminate redundant
> code. Return all errors to the caller rather than swallowing them
> (e.g. when tpm_transmit_cmd() returns nonzero).
> 
> Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>

You have to fix the reported kbuild errors.

/Jarkko

> ---
>  drivers/char/tpm/tpm-interface.c | 74 +++++++++++++++-------------------------
>  1 file changed, 27 insertions(+), 47 deletions(-)
> 
> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
> index a4beb53..dc492ee 100644
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -460,9 +460,19 @@ ssize_t tpm_getcap(struct tpm_chip *chip, __be32 subcap_id, cap_t *cap,
>  		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
>  		tpm_cmd.params.getcap_in.subcap = subcap_id;
>  	}
> +
>  	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
> +
> +	if (!rc &&
> +	    ((subcap_id == TPM_CAP_PROP_TIS_TIMEOUT &&
> +	      be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 20) ||
> +	     (subcap_id == TPM_CAP_PROP_TIS_DURATION &&
> +	      be32_to_cpu(tpm_cmd.header.out.length) != TPM_HEADER_SIZE + 16)))
> +		rc = -EINVAL;
> +
>  	if (!rc)
>  		*cap = tpm_cmd.params.getcap_out.cap;
> +
>  	return rc;
>  }
>  
> @@ -503,10 +513,9 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
>  
>  int tpm_get_timeouts(struct tpm_chip *chip)
>  {
> -	struct tpm_cmd_t tpm_cmd;
> +	cap_t cap;
>  	unsigned long new_timeout[4];
>  	unsigned long old_timeout[4];
> -	struct duration_t *duration_cap;
>  	ssize_t rc;
>  
>  	if (chip->flags & TPM_CHIP_FLAG_TPM2) {
> @@ -524,42 +533,25 @@ int tpm_get_timeouts(struct tpm_chip *chip)
>  		return 0;
>  	}
>  
> -	tpm_cmd.header.in = tpm_getcap_header;
> -	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
> -	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> -	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
> -	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
> -
> +	rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
> +			"attempting to determine the timeouts");
>  	if (rc == TPM_ERR_INVALID_POSTINIT) {
>  		/* The TPM is not started, we are the first to talk to it.
>  		   Execute a startup command. */
> -		dev_info(&chip->dev, "Issuing TPM_STARTUP");
> +		dev_info(&chip->dev, "Issuing TPM_STARTUP\n");
>  		if (tpm_startup(chip, TPM_ST_CLEAR))
>  			return rc;
>  
> -		tpm_cmd.header.in = tpm_getcap_header;
> -		tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
> -		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> -		tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
> -		rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
> -				  NULL);
> -	}
> -	if (rc) {
> -		dev_err(&chip->dev,
> -			"A TPM error (%zd) occurred attempting to determine the timeouts\n",
> -			rc);
> -		goto duration;
> +		rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
> +				"attempting to determine the timeouts");
>  	}
> +	if (rc)
> +		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) + 4 * sizeof(u32))
> -		return -EINVAL;
> -
> -	old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
> -	old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
> -	old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
> -	old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
> +	old_timeout[0] = be32_to_cpu(cap.timeout.a);
> +	old_timeout[1] = be32_to_cpu(cap.timeout.b);
> +	old_timeout[2] = be32_to_cpu(cap.timeout.c);
> +	old_timeout[3] = be32_to_cpu(cap.timeout.d);
>  	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
>  
>  	/*
> @@ -597,29 +589,17 @@ int tpm_get_timeouts(struct tpm_chip *chip)
>  	chip->timeout_c = usecs_to_jiffies(new_timeout[2]);
>  	chip->timeout_d = usecs_to_jiffies(new_timeout[3]);
>  
> -duration:
> -	tpm_cmd.header.in = tpm_getcap_header;
> -	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
> -	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
> -	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
> -
> -	rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
> -			      "attempting to determine the durations");
> +	rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_DURATION, &cap,
> +			"attempting to determine the durations");
>  	if (rc)
>  		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 -EINVAL;
> -
> -	duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
>  	chip->duration[TPM_SHORT] =
> -	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
> +		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_short));
>  	chip->duration[TPM_MEDIUM] =
> -	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
> +		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
>  	chip->duration[TPM_LONG] =
> -	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
> +		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
>  
>  	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
>  	 * value wrong and apparently reports msecs rather than usecs. So we
> -- 
> 1.9.1
> 

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v9 3/5] tpm: Clean up reading of timeout and duration capabilities
  2016-07-18 18:19             ` Jarkko Sakkinen
@ 2016-07-18 18:20               ` Jarkko Sakkinen
  0 siblings, 0 replies; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-07-18 18:20 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, linux-kernel, linux-security-module, jgunthorpe, stefanb

On Mon, Jul 18, 2016 at 09:19:53PM +0300, Jarkko Sakkinen wrote:
> On Wed, Jul 13, 2016 at 09:19:34AM -0700, Ed Swierk wrote:
> > Call tpm_getcap() from tpm_get_timeouts() to eliminate redundant
> > code. Return all errors to the caller rather than swallowing them
> > (e.g. when tpm_transmit_cmd() returns nonzero).
> > 
> > Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
> 
> You have to fix the reported kbuild errors.

Please ignore this :) Pressed send button by mistake in mutt.

/Jarkko

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v9 4/5] tpm: Allow TPM chip drivers to override reported command durations
  2016-07-13 16:19           ` [PATCH v9 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
  2016-07-13 17:04             ` kbuild test robot
@ 2016-07-18 18:40             ` Jarkko Sakkinen
  1 sibling, 0 replies; 68+ messages in thread
From: Jarkko Sakkinen @ 2016-07-18 18:40 UTC (permalink / raw)
  To: Ed Swierk
  Cc: tpmdd-devel, linux-kernel, linux-security-module, jgunthorpe, stefanb

On Wed, Jul 13, 2016 at 09:19:35AM -0700, Ed Swierk wrote:
> Some TPM chips report bogus command durations in their capabilities,
> just as others report incorrect timeouts. Rework tpm_get_timeouts() to
> allow chip drivers to override either via a single callback. Also
> clean up handling of TPMs that report milliseconds instead of
> microseconds.

I don't really undestand why you need to turn things so much over.
There's too much noise in this commit to reasonably evaluate it.

> Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
> ---
>  drivers/char/tpm/tpm-interface.c | 148 ++++++++++++++++++++++-----------------
>  drivers/char/tpm/tpm_tis_core.c  |  32 +++------
>  include/linux/tpm.h              |   3 +-
>  3 files changed, 94 insertions(+), 89 deletions(-)
> 
> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
> index dc492ee..9dafc25 100644
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -513,10 +513,10 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
>  
>  int tpm_get_timeouts(struct tpm_chip *chip)
>  {
> -	cap_t cap;

cap_t cap2;

> -	unsigned long new_timeout[4];
> -	unsigned long old_timeout[4];
> -	ssize_t rc;
> +	cap_t cap1, cap2;

Do not use cap1.

> +	int rc;
> +	unsigned long orig_timeout_a, orig_timeout_b, orig_timeout_c,
> +		orig_timeout_d, orig_duration[3];

Use old_timeout array just to reduce the diff.

>  
>  	if (chip->flags & TPM_CHIP_FLAG_TPM2) {
>  		/* Fixed timeouts for TPM2 */
> @@ -533,7 +533,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
>  		return 0;
>  	}
>  
> -	rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
> +	rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap1,
>  			"attempting to determine the timeouts");
>  	if (rc == TPM_ERR_INVALID_POSTINIT) {
>  		/* The TPM is not started, we are the first to talk to it.
> @@ -542,77 +542,95 @@ int tpm_get_timeouts(struct tpm_chip *chip)
>  		if (tpm_startup(chip, TPM_ST_CLEAR))
>  			return rc;
>  
> -		rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
> +		rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap1,
>  				"attempting to determine the timeouts");
>  	}
>  	if (rc)
>  		return rc;
>  
> -	old_timeout[0] = be32_to_cpu(cap.timeout.a);
> -	old_timeout[1] = be32_to_cpu(cap.timeout.b);
> -	old_timeout[2] = be32_to_cpu(cap.timeout.c);
> -	old_timeout[3] = be32_to_cpu(cap.timeout.d);
> -	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
> -
> -	/*
> -	 * Provide ability for vendor overrides of timeout values in case
> -	 * of misreporting.
> -	 */
> -	if (chip->ops->update_timeouts != NULL)
> -		chip->timeout_adjusted =
> -			chip->ops->update_timeouts(chip, new_timeout);
> -
> -	if (!chip->timeout_adjusted) {
> -		/* Don't overwrite default if value is 0 */
> -		if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
> -			int i;
> -
> -			/* timeouts in msec rather usec */
> -			for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
> -				new_timeout[i] *= 1000;
> -			chip->timeout_adjusted = true;
> -		}
> -	}
> -
> -	/* Report adjusted timeouts */
> -	if (chip->timeout_adjusted) {
> -		dev_info(&chip->dev,
> -			 HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
> -			 old_timeout[0], new_timeout[0],
> -			 old_timeout[1], new_timeout[1],
> -			 old_timeout[2], new_timeout[2],
> -			 old_timeout[3], new_timeout[3]);
> -	}
> -
> -	chip->timeout_a = usecs_to_jiffies(new_timeout[0]);
> -	chip->timeout_b = usecs_to_jiffies(new_timeout[1]);
> -	chip->timeout_c = usecs_to_jiffies(new_timeout[2]);
> -	chip->timeout_d = usecs_to_jiffies(new_timeout[3]);
> -
> -	rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_DURATION, &cap,
> +	rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_DURATION, &cap2,
>  			"attempting to determine the durations");
>  	if (rc)
>  		return rc;
>  
> -	chip->duration[TPM_SHORT] =
> -		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_short));
> -	chip->duration[TPM_MEDIUM] =
> -		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
> -	chip->duration[TPM_LONG] =
> -		usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
> +	be32_to_cpus(&cap1.timeout.a);
> +	be32_to_cpus(&cap1.timeout.b);
> +	be32_to_cpus(&cap1.timeout.c);
> +	be32_to_cpus(&cap1.timeout.d);
> +	chip->timeout_a = usecs_to_jiffies(cap1.timeout.a);
> +	chip->timeout_b = usecs_to_jiffies(cap1.timeout.b);
> +	chip->timeout_c = usecs_to_jiffies(cap1.timeout.c);
> +	chip->timeout_d = usecs_to_jiffies(cap1.timeout.d);
>  
> -	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
> -	 * value wrong and apparently reports msecs rather than usecs. So we
> -	 * fix up the resulting too-small TPM_SHORT value to make things work.
> -	 * We also scale the TPM_MEDIUM and -_LONG values by 1000.
> -	 */
> -	if (chip->duration[TPM_SHORT] < (HZ / 100)) {
> -		chip->duration[TPM_SHORT] = HZ;
> -		chip->duration[TPM_MEDIUM] *= 1000;
> -		chip->duration[TPM_LONG] *= 1000;
> -		chip->duration_adjusted = true;
> -		dev_info(&chip->dev, "Adjusting TPM timeout parameters.");
> +	/* Some TPMs report timeouts in milliseconds rather than
> +	   microseconds. Use a value between 1 and 1000 as an
> +	   indication that this is the case. */
> +	if (cap1.timeout.a > 0 && cap1.timeout.a < 1000) {
> +		chip->timeout_a = msecs_to_jiffies(cap1.timeout.a);
> +		chip->timeout_b = msecs_to_jiffies(cap1.timeout.b);
> +		chip->timeout_c = msecs_to_jiffies(cap1.timeout.c);
> +		chip->timeout_d = msecs_to_jiffies(cap1.timeout.d);
> +		chip->timeout_adjusted = true;
>  	}
> +
> +	be32_to_cpus(&cap2.duration.tpm_short);
> +	be32_to_cpus(&cap2.duration.tpm_medium);
> +	be32_to_cpus(&cap2.duration.tpm_long);
> +	chip->duration[TPM_SHORT] =
> +		usecs_to_jiffies(cap2.duration.tpm_short);
> +	chip->duration[TPM_MEDIUM] =
> +		usecs_to_jiffies(cap2.duration.tpm_medium);
> +	chip->duration[TPM_LONG] =
> +		usecs_to_jiffies(cap2.duration.tpm_long);
> +
> +	orig_timeout_a = chip->timeout_a;
> +	orig_timeout_b = chip->timeout_b;
> +	orig_timeout_c = chip->timeout_c;
> +	orig_timeout_d = chip->timeout_d;
> +	memcpy(orig_duration, chip->duration, 3 * sizeof(unsigned long));
> +
> +	/* Interpret duration values between 1 and 10000 as
> +	   milliseconds to deal with TPMs like the Broadcom BCM0102 in
> +	   the Dell Latitude D820. */
> +	if (cap2.duration.tpm_short > 0 && cap2.duration.tpm_short < 10000) {
> +		chip->duration[TPM_SHORT] =
> +			msecs_to_jiffies(cap2.duration.tpm_short);
> +		chip->duration[TPM_MEDIUM] =
> +			msecs_to_jiffies(cap2.duration.tpm_medium);
> +		chip->duration[TPM_LONG] =
> +			msecs_to_jiffies(cap2.duration.tpm_long);
> +		chip->duration_adjusted = true;
> +	}
> +
> +	if (chip->ops->update_timeouts != NULL)
> +		chip->ops->update_timeouts(chip);
> +
> +	if (chip->timeout_adjusted) {
> +		dev_info(&chip->dev,
> +			 HW_ERR "Adjusted timeouts: A %u->%uus B %u->%uus"
> +			 " C %u->%uus D %u->%uus\n",
> +			 jiffies_to_usecs(orig_timeout_a),
> +			 jiffies_to_usecs(chip->timeout_a),
> +			 jiffies_to_usecs(orig_timeout_b),
> +			 jiffies_to_usecs(chip->timeout_b),
> +			 jiffies_to_usecs(orig_timeout_c),
> +			 jiffies_to_usecs(chip->timeout_c),
> +			 jiffies_to_usecs(orig_timeout_d),
> +			 jiffies_to_usecs(chip->timeout_d));
> +	}
> +
> +	if (chip->duration_adjusted) {
> +		dev_info(&chip->dev,
> +			 HW_ERR "Adjusted durations: short %u->%uus"
> +			 " medium %u->%uus long %u->%uus\n",
> +			 jiffies_to_usecs(orig_duration[TPM_SHORT]),
> +			 jiffies_to_usecs(chip->duration[TPM_SHORT]),
> +			 jiffies_to_usecs(orig_duration[TPM_MEDIUM]),
> +			 jiffies_to_usecs(chip->duration[TPM_MEDIUM]),
> +			 jiffies_to_usecs(orig_duration[TPM_LONG]),
> +			 jiffies_to_usecs(chip->duration[TPM_LONG]));
> +	}
> +
>  	return 0;
>  }
>  EXPORT_SYMBOL_GPL(tpm_get_timeouts);
> diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
> index e62fdeb..f013664 100644
> --- a/drivers/char/tpm/tpm_tis_core.c
> +++ b/drivers/char/tpm/tpm_tis_core.c
> @@ -398,37 +398,25 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
>  	return rc;
>  }
>  
> -struct tis_vendor_timeout_override {
> -	u32 did_vid;
> -	unsigned long timeout_us[4];
> -};
> -
> -static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
> -	/* Atmel 3204 */
> -	{ 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
> -			(TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
> -};
> -
> -static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
> -				    unsigned long *timeout_cap)
> +static void tpm_tis_update_timeouts(struct tpm_chip *chip)
>  {
>  	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
> -	int i, rc;
> +	int rc;
>  	u32 did_vid;
>  
>  	rc = tpm_tis_read32(priv, TPM_DID_VID(0), &did_vid);
>  	if (rc < 0)
>  		return rc;
>  
> -	for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
> -		if (vendor_timeout_overrides[i].did_vid != did_vid)
> -			continue;
> -		memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
> -		       sizeof(vendor_timeout_overrides[i].timeout_us));
> -		return true;
> +	switch (did_vid) {
> +	case 0x32041114: /* Atmel 3204 */
> +		chip->timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
> +		chip->timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
> +		chip->timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
> +		chip->timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
> +		chip->timeout_adjusted = true;
> +		break;

Is this needed if we have the special case for milliseconds in
tpm_get_timeouts?

>  	}
> -
> -	return false;
>  }
>  
>  /*
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index 706e63e..2380ebf 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -41,8 +41,7 @@ struct tpm_class_ops {
>  	int (*send) (struct tpm_chip *chip, u8 *buf, size_t len);
>  	void (*cancel) (struct tpm_chip *chip);
>  	u8 (*status) (struct tpm_chip *chip);
> -	bool (*update_timeouts)(struct tpm_chip *chip,
> -				unsigned long *timeout_cap);
> +	void (*update_timeouts)(struct tpm_chip *chip);
>  
>  };
>  
> -- 
> 1.9.1
> 

/Jarkko

^ permalink raw reply	[flat|nested] 68+ messages in thread

end of thread, other threads:[~2016-07-18 18:40 UTC | newest]

Thread overview: 68+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-08  0:45 [PATCH v4 0/4] tpm: Command duration logging and chip-specific override Ed Swierk
2016-06-08  0:45 ` [PATCH v4 1/4] tpm_tis: Improve reporting of IO errors Ed Swierk
2016-06-08  0:45 ` [PATCH v4 2/4] tpm: Add optional logging of TPM command durations Ed Swierk
2016-06-08  0:45 ` [PATCH v4 3/4] tpm: Allow TPM chip drivers to override reported " Ed Swierk
2016-06-08 19:05   ` [tpmdd-devel] " Jason Gunthorpe
2016-06-08 20:41     ` Ed Swierk
2016-06-08  0:45 ` [PATCH v4 4/4] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
2016-06-08 23:00 ` [PATCH v5 0/4] tpm: Command duration logging and chip-specific override Ed Swierk
2016-06-08 23:00   ` [PATCH v5 1/4] tpm_tis: Improve reporting of IO errors Ed Swierk
2016-06-08 23:00   ` [PATCH v5 2/4] tpm: Add optional logging of TPM command durations Ed Swierk
2016-06-08 23:00   ` [PATCH v5 3/4] tpm: Allow TPM chip drivers to override reported " Ed Swierk
2016-06-10 12:19     ` Jarkko Sakkinen
2016-06-10 17:34       ` Ed Swierk
2016-06-10 19:42         ` Jarkko Sakkinen
2016-06-11  1:54           ` Ed Swierk
2016-06-08 23:00   ` [PATCH v5 4/4] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
2016-06-11  1:55   ` [PATCH v6 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
2016-06-11  1:55     ` [PATCH v6 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
2016-06-11  1:55     ` [PATCH v6 2/5] tpm: Add optional logging of TPM command durations Ed Swierk
2016-06-11  1:55     ` [PATCH v6 3/5] tpm: Factor out reading of timeout and duration capabilities Ed Swierk
2016-06-16 20:20       ` Jarkko Sakkinen
2016-06-19 12:12       ` Jarkko Sakkinen
2016-06-11  1:55     ` [PATCH v6 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
2016-06-16 20:26       ` Jarkko Sakkinen
2016-06-11  1:55     ` [PATCH v6 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
2016-06-21  1:53     ` [PATCH v7 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
2016-06-21  1:53       ` [PATCH v7 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
2016-06-21  1:53       ` [PATCH v7 2/5] tpm: Add optional logging of TPM command durations Ed Swierk
2016-06-21  1:54       ` [PATCH v7 3/5] tpm: Clean up reading of timeout and duration capabilities Ed Swierk
2016-06-21 20:52         ` Jarkko Sakkinen
2016-06-22  0:21         ` Ed Swierk
2016-06-22 10:46           ` Jarkko Sakkinen
2016-06-21  1:54       ` [PATCH v7 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
2016-06-21 20:54         ` Jarkko Sakkinen
2016-06-21  1:54       ` [PATCH v7 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
2016-06-21 20:55         ` Jarkko Sakkinen
2016-06-22  1:10       ` [PATCH v8 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
2016-06-22  1:10         ` [PATCH v8 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
2016-06-24 18:25           ` Jason Gunthorpe
2016-06-24 20:21             ` Jarkko Sakkinen
2016-06-24 20:23               ` Jarkko Sakkinen
2016-06-24 20:26               ` Jason Gunthorpe
2016-06-25 15:24                 ` Jarkko Sakkinen
2016-06-25 15:47                   ` Jarkko Sakkinen
2016-06-27 17:55                     ` Jason Gunthorpe
2016-06-22  1:10         ` [PATCH v8 2/5] tpm: Add optional logging of TPM command durations Ed Swierk
2016-06-24 18:27           ` Jason Gunthorpe
2016-06-24 20:24             ` Jarkko Sakkinen
2016-06-22  1:10         ` [PATCH v8 3/5] tpm: Clean up reading of timeout and duration capabilities Ed Swierk
2016-06-22  1:10         ` [PATCH v8 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
2016-06-22  1:10         ` [PATCH v8 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
2016-07-13 16:19         ` [PATCH v9 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
2016-07-13 16:19           ` [PATCH v9 1/5] tpm_tis: Improve reporting of IO errors Ed Swierk
2016-07-13 16:19           ` [PATCH v9 2/5] tpm: Add optional logging of TPM command durations Ed Swierk
2016-07-13 16:19           ` [PATCH v9 3/5] tpm: Clean up reading of timeout and duration capabilities Ed Swierk
2016-07-18 18:15             ` Jarkko Sakkinen
2016-07-18 18:19             ` Jarkko Sakkinen
2016-07-18 18:20               ` Jarkko Sakkinen
2016-07-13 16:19           ` [PATCH v9 4/5] tpm: Allow TPM chip drivers to override reported command durations Ed Swierk
2016-07-13 17:04             ` kbuild test robot
2016-07-18 18:40             ` Jarkko Sakkinen
2016-07-13 16:19           ` [PATCH v9 5/5] tpm_tis: Increase ST19NP18 TPM command duration to avoid chip lockup Ed Swierk
2016-07-13 16:44           ` [PATCH v9 0/5] tpm: Command duration logging and chip-specific override Ed Swierk
2016-07-13 17:36             ` Jason Gunthorpe
2016-07-13 20:00               ` Ed Swierk
2016-07-13 20:58                 ` Eric W. Biederman
2016-07-13 20:59                 ` Jason Gunthorpe
2016-07-18 18:07           ` Jarkko Sakkinen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).