Skip to content

Commit 79ca6f7

Browse files
haowu-uiucjarkkojs
authored andcommitted
tpm: fix Atmel TPM crash caused by too frequent queries
The Atmel TPM 1.2 chips crash with error `tpm_try_transmit: send(): error -62` since kernel 4.14. It is observed from the kernel log after running `tpm_sealdata -z`. The error thrown from the command is as follows ``` $ tpm_sealdata -z Tspi_Key_LoadKey failed: 0x00001087 - layer=tddl, code=0087 (135), I/O error ``` The issue was reproduced with the following Atmel TPM chip: ``` $ tpm_version T0 TPM 1.2 Version Info: Chip Version: 1.2.66.1 Spec Level: 2 Errata Revision: 3 TPM Vendor ID: ATML TPM Version: 01010000 Manufacturer Info: 41544d4c ``` The root cause of the issue is due to the TPM calls to msleep() were replaced with usleep_range() [1], which reduces the actual timeout. Via experiments, it is observed that the original msleep(5) actually sleeps for 15ms. Because of a known timeout issue in Atmel TPM 1.2 chip, the shorter timeout than 15ms can cause the error described above. A few further changes in kernel 4.16 [2] and 4.18 [3, 4] further reduced the timeout to less than 1ms. With experiments, the problematic timeout in the latest kernel is the one for `wait_for_tpm_stat`. To fix it, the patch reverts the timeout of `wait_for_tpm_stat` to 15ms for all Atmel TPM 1.2 chips, but leave it untouched for Ateml TPM 2.0 chip, and chips from other vendors. As explained above, the chosen 15ms timeout is the actual timeout before this issue introduced, thus the old value is used here. Particularly, TPM_ATML_TIMEOUT_WAIT_STAT_MIN is set to 14700us, TPM_ATML_TIMEOUT_WAIT_STAT_MIN is set to 15000us according to the existing TPM_TIMEOUT_RANGE_US (300us). The fixed has been tested in the system with the affected Atmel chip with no issues observed after boot up. References: [1] 9f3fc7b tpm: replace msleep() with usleep_range() in TPM 1.2/2.0 generic drivers [2] cf151a9 tpm: reduce tpm polling delay in tpm_tis_core [3] 59f5a6b tpm: reduce poll sleep time in tpm_transmit() [4] 424eaf9 tpm: reduce polling time to usecs for even finer granularity Fixes: 9f3fc7b ("tpm: replace msleep() with usleep_range() in TPM 1.2/2.0 generic drivers") Link: https://patchwork.kernel.org/project/linux-integrity/patch/[email protected]/ Signed-off-by: Hao Wu <[email protected]> Reviewed-by: Jarkko Sakkinen <[email protected]> Signed-off-by: Jarkko Sakkinen <[email protected]>
1 parent a0bcce2 commit 79ca6f7

File tree

3 files changed

+23
-8
lines changed

3 files changed

+23
-8
lines changed

drivers/char/tpm/tpm_tis_core.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask,
4848
unsigned long timeout, wait_queue_head_t *queue,
4949
bool check_cancel)
5050
{
51+
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
5152
unsigned long stop;
5253
long rc;
5354
u8 status;
@@ -80,8 +81,8 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask,
8081
}
8182
} else {
8283
do {
83-
usleep_range(TPM_TIMEOUT_USECS_MIN,
84-
TPM_TIMEOUT_USECS_MAX);
84+
usleep_range(priv->timeout_min,
85+
priv->timeout_max);
8586
status = chip->ops->status(chip);
8687
if ((status & mask) == mask)
8788
return 0;
@@ -945,7 +946,22 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
945946
chip->timeout_b = msecs_to_jiffies(TIS_TIMEOUT_B_MAX);
946947
chip->timeout_c = msecs_to_jiffies(TIS_TIMEOUT_C_MAX);
947948
chip->timeout_d = msecs_to_jiffies(TIS_TIMEOUT_D_MAX);
949+
priv->timeout_min = TPM_TIMEOUT_USECS_MIN;
950+
priv->timeout_max = TPM_TIMEOUT_USECS_MAX;
948951
priv->phy_ops = phy_ops;
952+
953+
rc = tpm_tis_read32(priv, TPM_DID_VID(0), &vendor);
954+
if (rc < 0)
955+
goto out_err;
956+
957+
priv->manufacturer_id = vendor;
958+
959+
if (priv->manufacturer_id == TPM_VID_ATML &&
960+
!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
961+
priv->timeout_min = TIS_TIMEOUT_MIN_ATML;
962+
priv->timeout_max = TIS_TIMEOUT_MAX_ATML;
963+
}
964+
949965
dev_set_drvdata(&chip->dev, priv);
950966

951967
if (is_bsw()) {
@@ -988,12 +1004,6 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
9881004
if (rc)
9891005
goto out_err;
9901006

991-
rc = tpm_tis_read32(priv, TPM_DID_VID(0), &vendor);
992-
if (rc < 0)
993-
goto out_err;
994-
995-
priv->manufacturer_id = vendor;
996-
9971007
rc = tpm_tis_read8(priv, TPM_RID(0), &rid);
9981008
if (rc < 0)
9991009
goto out_err;

drivers/char/tpm/tpm_tis_core.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ enum tis_defaults {
5454
TIS_MEM_LEN = 0x5000,
5555
TIS_SHORT_TIMEOUT = 750, /* ms */
5656
TIS_LONG_TIMEOUT = 2000, /* 2 sec */
57+
TIS_TIMEOUT_MIN_ATML = 14700, /* usecs */
58+
TIS_TIMEOUT_MAX_ATML = 15000, /* usecs */
5759
};
5860

5961
/* Some timeout values are needed before it is known whether the chip is
@@ -98,6 +100,8 @@ struct tpm_tis_data {
98100
wait_queue_head_t read_queue;
99101
const struct tpm_tis_phy_ops *phy_ops;
100102
unsigned short rng_quality;
103+
unsigned int timeout_min; /* usecs */
104+
unsigned int timeout_max; /* usecs */
101105
};
102106

103107
struct tpm_tis_phy_ops {

include/linux/tpm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ enum tpm2_cc_attrs {
269269
#define TPM_VID_INTEL 0x8086
270270
#define TPM_VID_WINBOND 0x1050
271271
#define TPM_VID_STM 0x104A
272+
#define TPM_VID_ATML 0x1114
272273

273274
enum tpm_chip_flags {
274275
TPM_CHIP_FLAG_TPM2 = BIT(1),

0 commit comments

Comments
 (0)