aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Gunthorpe <jgunthorpe@obsidianresearch.com>2014-05-21 20:26:44 -0400
committerPeter Huewe <peterhuewe@gmx.de>2014-07-29 17:10:56 -0400
commit8e54caf407b98efa05409e1fee0e5381abd2b088 (patch)
tree690ed020c643ca869e5fc27686d518cb3a9f5f1e
parent3e14d83ef94a5806a865b85b513b4e891923c19b (diff)
tpm: Provide a generic means to override the chip returned timeouts
Some Atmel TPMs provide completely wrong timeouts from their TPM_CAP_PROP_TIS_TIMEOUT query. This patch detects that and returns new correct values via a DID/VID table in the TIS driver. Tested on ARM using an AT97SC3204T FW version 37.16 Cc: <stable@vger.kernel.org> [PHuewe: without this fix these 'broken' Atmel TPMs won't function on older kernels] Signed-off-by: "Berg, Christopher" <Christopher.Berg@atmel.com> Signed-off-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com> Signed-off-by: Peter Huewe <peterhuewe@gmx.de>
-rw-r--r--drivers/char/tpm/tpm-interface.c62
-rw-r--r--drivers/char/tpm/tpm_tis.c31
-rw-r--r--include/linux/tpm.h3
3 files changed, 75 insertions, 21 deletions
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index bef6cceffc3a..6af17002a115 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -491,11 +491,10 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
491int tpm_get_timeouts(struct tpm_chip *chip) 491int tpm_get_timeouts(struct tpm_chip *chip)
492{ 492{
493 struct tpm_cmd_t tpm_cmd; 493 struct tpm_cmd_t tpm_cmd;
494 struct timeout_t *timeout_cap; 494 unsigned long new_timeout[4];
495 unsigned long old_timeout[4];
495 struct duration_t *duration_cap; 496 struct duration_t *duration_cap;
496 ssize_t rc; 497 ssize_t rc;
497 u32 timeout;
498 unsigned int scale = 1;
499 498
500 tpm_cmd.header.in = tpm_getcap_header; 499 tpm_cmd.header.in = tpm_getcap_header;
501 tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; 500 tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
@@ -529,25 +528,46 @@ int tpm_get_timeouts(struct tpm_chip *chip)
529 != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32)) 528 != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32))
530 return -EINVAL; 529 return -EINVAL;
531 530
532 timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout; 531 old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
533 /* Don't overwrite default if value is 0 */ 532 old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
534 timeout = be32_to_cpu(timeout_cap->a); 533 old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
535 if (timeout && timeout < 1000) { 534 old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
536 /* timeouts in msec rather usec */ 535 memcpy(new_timeout, old_timeout, sizeof(new_timeout));
537 scale = 1000; 536
538 chip->vendor.timeout_adjusted = true; 537 /*
538 * Provide ability for vendor overrides of timeout values in case
539 * of misreporting.
540 */
541 if (chip->ops->update_timeouts != NULL)
542 chip->vendor.timeout_adjusted =
543 chip->ops->update_timeouts(chip, new_timeout);
544
545 if (!chip->vendor.timeout_adjusted) {
546 /* Don't overwrite default if value is 0 */
547 if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
548 int i;
549
550 /* timeouts in msec rather usec */
551 for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
552 new_timeout[i] *= 1000;
553 chip->vendor.timeout_adjusted = true;
554 }
539 } 555 }
540 if (timeout) 556
541 chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale); 557 /* Report adjusted timeouts */
542 timeout = be32_to_cpu(timeout_cap->b); 558 if (chip->vendor.timeout_adjusted) {
543 if (timeout) 559 dev_info(chip->dev,
544 chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale); 560 HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
545 timeout = be32_to_cpu(timeout_cap->c); 561 old_timeout[0], new_timeout[0],
546 if (timeout) 562 old_timeout[1], new_timeout[1],
547 chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale); 563 old_timeout[2], new_timeout[2],
548 timeout = be32_to_cpu(timeout_cap->d); 564 old_timeout[3], new_timeout[3]);
549 if (timeout) 565 }
550 chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale); 566
567 chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
568 chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
569 chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
570 chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
551 571
552duration: 572duration:
553 tpm_cmd.header.in = tpm_getcap_header; 573 tpm_cmd.header.in = tpm_getcap_header;
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index a9ed2270c25d..2c46734b266d 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -373,6 +373,36 @@ out_err:
373 return rc; 373 return rc;
374} 374}
375 375
376struct tis_vendor_timeout_override {
377 u32 did_vid;
378 unsigned long timeout_us[4];
379};
380
381static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
382 /* Atmel 3204 */
383 { 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
384 (TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
385};
386
387static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
388 unsigned long *timeout_cap)
389{
390 int i;
391 u32 did_vid;
392
393 did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
394
395 for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
396 if (vendor_timeout_overrides[i].did_vid != did_vid)
397 continue;
398 memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
399 sizeof(vendor_timeout_overrides[i].timeout_us));
400 return true;
401 }
402
403 return false;
404}
405
376/* 406/*
377 * Early probing for iTPM with STS_DATA_EXPECT flaw. 407 * Early probing for iTPM with STS_DATA_EXPECT flaw.
378 * Try sending command without itpm flag set and if that 408 * Try sending command without itpm flag set and if that
@@ -437,6 +467,7 @@ static const struct tpm_class_ops tpm_tis = {
437 .recv = tpm_tis_recv, 467 .recv = tpm_tis_recv,
438 .send = tpm_tis_send, 468 .send = tpm_tis_send,
439 .cancel = tpm_tis_ready, 469 .cancel = tpm_tis_ready,
470 .update_timeouts = tpm_tis_update_timeouts,
440 .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, 471 .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
441 .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, 472 .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
442 .req_canceled = tpm_tis_req_canceled, 473 .req_canceled = tpm_tis_req_canceled,
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index fff1d0976f80..8350c538b486 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -39,6 +39,9 @@ struct tpm_class_ops {
39 int (*send) (struct tpm_chip *chip, u8 *buf, size_t len); 39 int (*send) (struct tpm_chip *chip, u8 *buf, size_t len);
40 void (*cancel) (struct tpm_chip *chip); 40 void (*cancel) (struct tpm_chip *chip);
41 u8 (*status) (struct tpm_chip *chip); 41 u8 (*status) (struct tpm_chip *chip);
42 bool (*update_timeouts)(struct tpm_chip *chip,
43 unsigned long *timeout_cap);
44
42}; 45};
43 46
44#if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE) 47#if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE)