diff options
| author | Jason Gunthorpe <jgunthorpe@obsidianresearch.com> | 2014-05-21 20:26:44 -0400 |
|---|---|---|
| committer | Peter Huewe <peterhuewe@gmx.de> | 2014-07-29 17:10:56 -0400 |
| commit | 8e54caf407b98efa05409e1fee0e5381abd2b088 (patch) | |
| tree | 690ed020c643ca869e5fc27686d518cb3a9f5f1e | |
| parent | 3e14d83ef94a5806a865b85b513b4e891923c19b (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.c | 62 | ||||
| -rw-r--r-- | drivers/char/tpm/tpm_tis.c | 31 | ||||
| -rw-r--r-- | include/linux/tpm.h | 3 |
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) | |||
| 491 | int tpm_get_timeouts(struct tpm_chip *chip) | 491 | int 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 | ||
| 552 | duration: | 572 | duration: |
| 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 | ||
| 376 | struct tis_vendor_timeout_override { | ||
| 377 | u32 did_vid; | ||
| 378 | unsigned long timeout_us[4]; | ||
| 379 | }; | ||
| 380 | |||
| 381 | static 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 | |||
| 387 | static 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) |
