Commit 8e54caf407b98efa05409e1fee0e5381abd2b088

Authored by Jason Gunthorpe
Committed by Peter Huewe
1 parent 3e14d83ef9

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>

Showing 3 changed files with 75 additions and 21 deletions Side-by-side Diff

drivers/char/tpm/tpm-interface.c
... ... @@ -491,11 +491,10 @@
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;
502 501  
... ... @@ -529,25 +528,46 @@
529 528 != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32))
530 529 return -EINVAL;
531 530  
532   - timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
533   - /* Don't overwrite default if value is 0 */
534   - timeout = be32_to_cpu(timeout_cap->a);
535   - if (timeout && timeout < 1000) {
536   - /* timeouts in msec rather usec */
537   - scale = 1000;
538   - chip->vendor.timeout_adjusted = true;
  531 + old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
  532 + old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
  533 + old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
  534 + old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
  535 + memcpy(new_timeout, old_timeout, sizeof(new_timeout));
  536 +
  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)
541   - chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale);
542   - timeout = be32_to_cpu(timeout_cap->b);
543   - if (timeout)
544   - chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale);
545   - timeout = be32_to_cpu(timeout_cap->c);
546   - if (timeout)
547   - chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale);
548   - timeout = be32_to_cpu(timeout_cap->d);
549   - if (timeout)
550   - chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale);
  556 +
  557 + /* Report adjusted timeouts */
  558 + if (chip->vendor.timeout_adjusted) {
  559 + dev_info(chip->dev,
  560 + HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
  561 + old_timeout[0], new_timeout[0],
  562 + old_timeout[1], new_timeout[1],
  563 + old_timeout[2], new_timeout[2],
  564 + old_timeout[3], new_timeout[3]);
  565 + }
  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 572 duration:
553 573 tpm_cmd.header.in = tpm_getcap_header;
drivers/char/tpm/tpm_tis.c
... ... @@ -373,6 +373,36 @@
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
... ... @@ -437,6 +467,7 @@
437 467 .recv = tpm_tis_recv,
438 468 .send = tpm_tis_send,
439 469 .cancel = tpm_tis_ready,
  470 + .update_timeouts = tpm_tis_update_timeouts,
440 471 .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
441 472 .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
442 473 .req_canceled = tpm_tis_req_canceled,
... ... @@ -39,6 +39,9 @@
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)