]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
tpm, tpm_tis: Workaround failed command reception on Infineon devices
authorJonathan McDowell <noodles@meta.com>
Wed, 12 Mar 2025 05:26:18 +0000 (07:26 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 25 Apr 2025 08:45:20 +0000 (10:45 +0200)
[ Upstream commit de9e33df7762abbfc2a1568291f2c3a3154c6a9d ]

Some Infineon devices have a issue where the status register will get
stuck with a quick REQUEST_USE / COMMAND_READY sequence. This is not
simply a matter of requiring a longer timeout; the work around is to
retry the command submission. Add appropriate logic to do this in the
send path.

This is fixed in later firmware revisions, but those are not always
available, and cannot generally be easily updated from outside a
firmware environment.

Testing has been performed with a simple repeated loop of doing a
TPM2_CC_GET_CAPABILITY for TPM_CAP_PROP_MANUFACTURER using the Go code
at:

  https://the.earth.li/~noodles/tpm-stuff/timeout-reproducer-simple.go

It can take several hours to reproduce, and several million operations.

Signed-off-by: Jonathan McDowell <noodles@meta.com>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/char/tpm/tpm_tis_core.c
drivers/char/tpm/tpm_tis_core.h
include/linux/tpm.h

index f6aa0dfadb93ee03d0d8207e6bf09455ad669169..7ade8bd12ab26834d3bae83d28b2f4cd2fca8738 100644 (file)
@@ -464,7 +464,10 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len)
 
                if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
                                        &priv->int_queue, false) < 0) {
-                       rc = -ETIME;
+                       if (test_bit(TPM_TIS_STATUS_VALID_RETRY, &priv->flags))
+                               rc = -EAGAIN;
+                       else
+                               rc = -ETIME;
                        goto out_err;
                }
                status = tpm_tis_status(chip);
@@ -481,7 +484,10 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len)
 
        if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
                                &priv->int_queue, false) < 0) {
-               rc = -ETIME;
+               if (test_bit(TPM_TIS_STATUS_VALID_RETRY, &priv->flags))
+                       rc = -EAGAIN;
+               else
+                       rc = -ETIME;
                goto out_err;
        }
        status = tpm_tis_status(chip);
@@ -546,9 +552,11 @@ static int tpm_tis_send_main(struct tpm_chip *chip, const u8 *buf, size_t len)
                if (rc >= 0)
                        /* Data transfer done successfully */
                        break;
-               else if (rc != -EIO)
+               else if (rc != -EAGAIN && rc != -EIO)
                        /* Data transfer failed, not recoverable */
                        return rc;
+
+               usleep_range(priv->timeout_min, priv->timeout_max);
        }
 
        /* go and do it */
@@ -1147,6 +1155,9 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
                priv->timeout_max = TIS_TIMEOUT_MAX_ATML;
        }
 
+       if (priv->manufacturer_id == TPM_VID_IFX)
+               set_bit(TPM_TIS_STATUS_VALID_RETRY, &priv->flags);
+
        if (is_bsw()) {
                priv->ilb_base_addr = ioremap(INTEL_LEGACY_BLK_BASE_ADDR,
                                        ILB_REMAP_SIZE);
index 13e99cf65efe4483053c2478aff2d3834c68609b..369496a6aebf13c8b15cae763adec33f6bdcfe1c 100644 (file)
@@ -89,6 +89,7 @@ enum tpm_tis_flags {
        TPM_TIS_INVALID_STATUS          = 1,
        TPM_TIS_DEFAULT_CANCELLATION    = 2,
        TPM_TIS_IRQ_TESTED              = 3,
+       TPM_TIS_STATUS_VALID_RETRY      = 4,
 };
 
 struct tpm_tis_data {
index 4ee9d13749adc1ae0a6bd47136694c280ca943ae..5f4998626a98891ad64fd0a0c528b1d2ccf7dd12 100644 (file)
@@ -272,6 +272,7 @@ enum tpm2_cc_attrs {
 #define TPM_VID_WINBOND  0x1050
 #define TPM_VID_STM      0x104A
 #define TPM_VID_ATML     0x1114
+#define TPM_VID_IFX      0x15D1
 
 enum tpm_chip_flags {
        TPM_CHIP_FLAG_BOOTSTRAPPED              = BIT(0),