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

tpm: Add Upgrade/Reduced mode support for TPM2 modules

If something went wrong during the TPM firmware upgrade, like power
failure or the firmware image file get corrupted, the TPM might end
up in Upgrade or Failure mode upon the next start. The state is
persistent between the TPM power cycle/restart.

According to TPM specification:
* If the TPM is in Upgrade mode, it will answer with TPM2_RC_UPGRADE
to all commands except TPM2_FieldUpgradeData(). It may also accept
other commands if it is able to complete them using the previously
installed firmware.
* If the TPM is in Failure mode, it will allow performing TPM
initialization but will not provide any crypto operations.
Will happily respond to Field Upgrade calls.

Change the behavior of the tpm2_auto_startup(), so it detects the active
running mode of the TPM by adding the following checks. If
tpm2_do_selftest() call returns TPM2_RC_UPGRADE, the TPM is in Upgrade
mode.
If the TPM is in Failure mode, it will successfully respond to both
tpm2_do_selftest() and tpm2_startup() calls. Although, will fail to
answer to tpm2_get_cc_attrs_tbl(). Use this fact to conclude that TPM is
in Failure mode.

If detected that the TPM is in the Upgrade or Failure mode, the function
sets TPM_CHIP_FLAG_FIRMWARE_UPGRADE_MODE flag.

The TPM_CHIP_FLAG_FIRMWARE_UPGRADE_MODE flag is used later during driver
initialization/deinitialization to disable functionality which makes no
sense or will fail in the current TPM state. Following functionality is
affected:
* Do not register TPM as a hwrng
* Do not register sysfs entries which provide information impossible to
obtain in limited mode
* Do not register resource managed character device

Signed-off-by: axelj <axelj@axis.com>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>

authored by

axelj and committed by
Jarkko Sakkinen
0aa69878 5887d7f4

+31 -7
+12 -7
drivers/char/tpm/tpm-chip.c
··· 444 444 return rc; 445 445 } 446 446 447 - if (chip->flags & TPM_CHIP_FLAG_TPM2) { 447 + if (chip->flags & TPM_CHIP_FLAG_TPM2 && !tpm_is_firmware_upgrade(chip)) { 448 448 rc = cdev_device_add(&chip->cdevs, &chip->devs); 449 449 if (rc) { 450 450 dev_err(&chip->devs, ··· 488 488 { 489 489 struct attribute **i; 490 490 491 - if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL)) 491 + if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL) || 492 + tpm_is_firmware_upgrade(chip)) 492 493 return; 493 494 494 495 sysfs_remove_link(&chip->dev.parent->kobj, "ppi"); ··· 507 506 struct attribute **i; 508 507 int rc; 509 508 510 - if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL)) 509 + if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL) || 510 + tpm_is_firmware_upgrade(chip)) 511 511 return 0; 512 512 513 513 rc = compat_only_sysfs_link_entry_to_kobj( ··· 538 536 539 537 static int tpm_add_hwrng(struct tpm_chip *chip) 540 538 { 541 - if (!IS_ENABLED(CONFIG_HW_RANDOM_TPM)) 539 + if (!IS_ENABLED(CONFIG_HW_RANDOM_TPM) || tpm_is_firmware_upgrade(chip)) 542 540 return 0; 543 541 544 542 snprintf(chip->hwrng_name, sizeof(chip->hwrng_name), ··· 551 549 static int tpm_get_pcr_allocation(struct tpm_chip *chip) 552 550 { 553 551 int rc; 552 + 553 + if (tpm_is_firmware_upgrade(chip)) 554 + return 0; 554 555 555 556 rc = (chip->flags & TPM_CHIP_FLAG_TPM2) ? 556 557 tpm2_get_pcr_allocation(chip) : ··· 617 612 return 0; 618 613 619 614 out_hwrng: 620 - if (IS_ENABLED(CONFIG_HW_RANDOM_TPM)) 615 + if (IS_ENABLED(CONFIG_HW_RANDOM_TPM) && !tpm_is_firmware_upgrade(chip)) 621 616 hwrng_unregister(&chip->hwrng); 622 617 out_ppi: 623 618 tpm_bios_log_teardown(chip); ··· 642 637 void tpm_chip_unregister(struct tpm_chip *chip) 643 638 { 644 639 tpm_del_legacy_sysfs(chip); 645 - if (IS_ENABLED(CONFIG_HW_RANDOM_TPM)) 640 + if (IS_ENABLED(CONFIG_HW_RANDOM_TPM) && !tpm_is_firmware_upgrade(chip)) 646 641 hwrng_unregister(&chip->hwrng); 647 642 tpm_bios_log_teardown(chip); 648 - if (chip->flags & TPM_CHIP_FLAG_TPM2) 643 + if (chip->flags & TPM_CHIP_FLAG_TPM2 && !tpm_is_firmware_upgrade(chip)) 649 644 cdev_device_del(&chip->cdevs, &chip->devs); 650 645 tpm_del_char_device(chip); 651 646 }
+3
drivers/char/tpm/tpm-sysfs.c
··· 480 480 481 481 WARN_ON(chip->groups_cnt != 0); 482 482 483 + if (tpm_is_firmware_upgrade(chip)) 484 + return; 485 + 483 486 if (chip->flags & TPM_CHIP_FLAG_TPM2) 484 487 chip->groups[chip->groups_cnt++] = &tpm2_dev_group; 485 488 else
+6
drivers/char/tpm/tpm2-cmd.c
··· 745 745 rc = tpm2_get_cc_attrs_tbl(chip); 746 746 747 747 out: 748 + if (rc == TPM2_RC_UPGRADE) { 749 + dev_info(&chip->dev, "TPM in field upgrade mode, requires firmware upgrade\n"); 750 + chip->flags |= TPM_CHIP_FLAG_FIRMWARE_UPGRADE; 751 + rc = 0; 752 + } 753 + 748 754 if (rc > 0) 749 755 rc = -ENODEV; 750 756 return rc;
+10
include/linux/tpm.h
··· 207 207 TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */ 208 208 TPM2_RC_FAILURE = 0x0101, 209 209 TPM2_RC_DISABLED = 0x0120, 210 + TPM2_RC_UPGRADE = 0x012D, 210 211 TPM2_RC_COMMAND_CODE = 0x0143, 211 212 TPM2_RC_TESTING = 0x090A, /* RC_WARN */ 212 213 TPM2_RC_REFERENCE_H0 = 0x0910, ··· 279 278 TPM_CHIP_FLAG_HAVE_TIMEOUTS = BIT(4), 280 279 TPM_CHIP_FLAG_ALWAYS_POWERED = BIT(5), 281 280 TPM_CHIP_FLAG_FIRMWARE_POWER_MANAGED = BIT(6), 281 + TPM_CHIP_FLAG_FIRMWARE_UPGRADE = BIT(7), 282 282 }; 283 283 284 284 #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) ··· 399 397 __be32 value2 = cpu_to_be32(value); 400 398 401 399 tpm_buf_append(buf, (u8 *) &value2, 4); 400 + } 401 + 402 + /* 403 + * Check if TPM device is in the firmware upgrade mode. 404 + */ 405 + static inline bool tpm_is_firmware_upgrade(struct tpm_chip *chip) 406 + { 407 + return chip->flags & TPM_CHIP_FLAG_FIRMWARE_UPGRADE; 402 408 } 403 409 404 410 static inline u32 tpm2_rc_value(u32 rc)