Skip to content

Commit

Permalink
tpm: fix Atmel TPM crash caused by too frequent queries
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
haowu-uiuc authored and jarkkojs committed Oct 26, 2021
1 parent a0bcce2 commit 79ca6f7
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 8 deletions.
26 changes: 18 additions & 8 deletions drivers/char/tpm/tpm_tis_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask,
unsigned long timeout, wait_queue_head_t *queue,
bool check_cancel)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
unsigned long stop;
long rc;
u8 status;
Expand Down Expand Up @@ -80,8 +81,8 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask,
}
} else {
do {
usleep_range(TPM_TIMEOUT_USECS_MIN,
TPM_TIMEOUT_USECS_MAX);
usleep_range(priv->timeout_min,
priv->timeout_max);
status = chip->ops->status(chip);
if ((status & mask) == mask)
return 0;
Expand Down Expand Up @@ -945,7 +946,22 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
chip->timeout_b = msecs_to_jiffies(TIS_TIMEOUT_B_MAX);
chip->timeout_c = msecs_to_jiffies(TIS_TIMEOUT_C_MAX);
chip->timeout_d = msecs_to_jiffies(TIS_TIMEOUT_D_MAX);
priv->timeout_min = TPM_TIMEOUT_USECS_MIN;
priv->timeout_max = TPM_TIMEOUT_USECS_MAX;
priv->phy_ops = phy_ops;

rc = tpm_tis_read32(priv, TPM_DID_VID(0), &vendor);
if (rc < 0)
goto out_err;

priv->manufacturer_id = vendor;

if (priv->manufacturer_id == TPM_VID_ATML &&
!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
priv->timeout_min = TIS_TIMEOUT_MIN_ATML;
priv->timeout_max = TIS_TIMEOUT_MAX_ATML;
}

dev_set_drvdata(&chip->dev, priv);

if (is_bsw()) {
Expand Down Expand Up @@ -988,12 +1004,6 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
if (rc)
goto out_err;

rc = tpm_tis_read32(priv, TPM_DID_VID(0), &vendor);
if (rc < 0)
goto out_err;

priv->manufacturer_id = vendor;

rc = tpm_tis_read8(priv, TPM_RID(0), &rid);
if (rc < 0)
goto out_err;
Expand Down
4 changes: 4 additions & 0 deletions drivers/char/tpm/tpm_tis_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ enum tis_defaults {
TIS_MEM_LEN = 0x5000,
TIS_SHORT_TIMEOUT = 750, /* ms */
TIS_LONG_TIMEOUT = 2000, /* 2 sec */
TIS_TIMEOUT_MIN_ATML = 14700, /* usecs */
TIS_TIMEOUT_MAX_ATML = 15000, /* usecs */
};

/* Some timeout values are needed before it is known whether the chip is
Expand Down Expand Up @@ -98,6 +100,8 @@ struct tpm_tis_data {
wait_queue_head_t read_queue;
const struct tpm_tis_phy_ops *phy_ops;
unsigned short rng_quality;
unsigned int timeout_min; /* usecs */
unsigned int timeout_max; /* usecs */
};

struct tpm_tis_phy_ops {
Expand Down
1 change: 1 addition & 0 deletions include/linux/tpm.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ enum tpm2_cc_attrs {
#define TPM_VID_INTEL 0x8086
#define TPM_VID_WINBOND 0x1050
#define TPM_VID_STM 0x104A
#define TPM_VID_ATML 0x1114

enum tpm_chip_flags {
TPM_CHIP_FLAG_TPM2 = BIT(1),
Expand Down

0 comments on commit 79ca6f7

Please sign in to comment.