Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jason Gunthorpe and committed by
Peter Huewe
8e54caf4 3e14d83e

+75 -21
+41 -21
drivers/char/tpm/tpm-interface.c
··· 491 491 int tpm_get_timeouts(struct tpm_chip *chip) 492 492 { 493 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 496 struct duration_t *duration_cap; 496 497 ssize_t rc; 497 - u32 timeout; 498 - unsigned int scale = 1; 499 498 500 499 tpm_cmd.header.in = tpm_getcap_header; 501 500 tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; ··· 528 529 != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32)) 529 530 return -EINVAL; 530 531 531 - timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout; 532 - /* Don't overwrite default if value is 0 */ 533 - timeout = be32_to_cpu(timeout_cap->a); 534 - if (timeout && timeout < 1000) { 535 - /* timeouts in msec rather usec */ 536 - scale = 1000; 537 - chip->vendor.timeout_adjusted = true; 532 + old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a); 533 + old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b); 534 + old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c); 535 + old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d); 536 + memcpy(new_timeout, old_timeout, sizeof(new_timeout)); 537 + 538 + /* 539 + * Provide ability for vendor overrides of timeout values in case 540 + * of misreporting. 541 + */ 542 + if (chip->ops->update_timeouts != NULL) 543 + chip->vendor.timeout_adjusted = 544 + chip->ops->update_timeouts(chip, new_timeout); 545 + 546 + if (!chip->vendor.timeout_adjusted) { 547 + /* Don't overwrite default if value is 0 */ 548 + if (new_timeout[0] != 0 && new_timeout[0] < 1000) { 549 + int i; 550 + 551 + /* timeouts in msec rather usec */ 552 + for (i = 0; i != ARRAY_SIZE(new_timeout); i++) 553 + new_timeout[i] *= 1000; 554 + chip->vendor.timeout_adjusted = true; 555 + } 538 556 } 539 - if (timeout) 540 - chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale); 541 - timeout = be32_to_cpu(timeout_cap->b); 542 - if (timeout) 543 - chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale); 544 - timeout = be32_to_cpu(timeout_cap->c); 545 - if (timeout) 546 - chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale); 547 - timeout = be32_to_cpu(timeout_cap->d); 548 - if (timeout) 549 - chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale); 557 + 558 + /* Report adjusted timeouts */ 559 + if (chip->vendor.timeout_adjusted) { 560 + dev_info(chip->dev, 561 + HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n", 562 + old_timeout[0], new_timeout[0], 563 + old_timeout[1], new_timeout[1], 564 + old_timeout[2], new_timeout[2], 565 + old_timeout[3], new_timeout[3]); 566 + } 567 + 568 + chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]); 569 + chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]); 570 + chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]); 571 + chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]); 550 572 551 573 duration: 552 574 tpm_cmd.header.in = tpm_getcap_header;
+31
drivers/char/tpm/tpm_tis.c
··· 373 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 407 * Early probing for iTPM with STS_DATA_EXPECT flaw. 378 408 * Try sending command without itpm flag set and if that ··· 467 437 .recv = tpm_tis_recv, 468 438 .send = tpm_tis_send, 469 439 .cancel = tpm_tis_ready, 440 + .update_timeouts = tpm_tis_update_timeouts, 470 441 .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, 471 442 .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, 472 443 .req_canceled = tpm_tis_req_canceled,
+3
include/linux/tpm.h
··· 39 39 int (*send) (struct tpm_chip *chip, u8 *buf, size_t len); 40 40 void (*cancel) (struct tpm_chip *chip); 41 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 47 #if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE)