[v3,3/4] tpm_tis: Use responseRetry to recover from data transfer errors

Message ID 20230613180259.3525-4-Alexander.Steffen@infineon.com
State New
Headers
Series Recovery from data transfer errors for tpm_tis |

Commit Message

Alexander Steffen June 13, 2023, 6:02 p.m. UTC
  TPM responses may become damaged during transmission, for example due to
bit flips on the wire. Instead of aborting when detecting such issues, the
responseRetry functionality can be used to make the TPM retransmit its
response and receive it again without errors.

Signed-off-by: Alexander Steffen <Alexander.Steffen@infineon.com>
---
 drivers/char/tpm/tpm_tis_core.c | 37 ++++++++++++++++++++++++++-------
 drivers/char/tpm/tpm_tis_core.h |  1 +
 2 files changed, 30 insertions(+), 8 deletions(-)
  

Comments

Jarkko Sakkinen July 10, 2023, 4:59 p.m. UTC | #1
On Tue, 2023-06-13 at 20:02 +0200, Alexander Steffen wrote:
> TPM responses may become damaged during transmission, for example due to
> bit flips on the wire. Instead of aborting when detecting such issues, the
> responseRetry functionality can be used to make the TPM retransmit its
> response and receive it again without errors.
> 
> Signed-off-by: Alexander Steffen <Alexander.Steffen@infineon.com>
> ---
>  drivers/char/tpm/tpm_tis_core.c | 37 ++++++++++++++++++++++++++-------
>  drivers/char/tpm/tpm_tis_core.h |  1 +
>  2 files changed, 30 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
> index 5ddaf24518be..a6d1396413a7 100644
> --- a/drivers/char/tpm/tpm_tis_core.c
> +++ b/drivers/char/tpm/tpm_tis_core.c
> @@ -337,7 +337,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
>  	return size;
>  }
>  
> -static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
> +static int tpm_tis_try_recv(struct tpm_chip *chip, u8 *buf, size_t count)
>  {
>  	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
>  	int size = 0;
> @@ -345,11 +345,6 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
>  	u32 expected;
>  	int rc;
>  
> -	if (count < TPM_HEADER_SIZE) {
> -		size = -EIO;
> -		goto out;
> -	}
> -
>  	size = recv_data(chip, buf, TPM_HEADER_SIZE);
>  	/* read first 10 bytes, including tag, paramsize, and result */
>  	if (size < TPM_HEADER_SIZE) {
> @@ -382,7 +377,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
>  		goto out;
>  	}
>  	status = tpm_tis_status(chip);
> -	if (status & TPM_STS_DATA_AVAIL) {	/* retry? */
> +	if (status & TPM_STS_DATA_AVAIL) {
>  		dev_err(&chip->dev, "Error left over data\n");
>  		size = -EIO;
>  		goto out;
> @@ -396,10 +391,36 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
>  	}
>  
>  out:
> -	tpm_tis_ready(chip);
>  	return size;
>  }
>  
> +static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
> +{
> +	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
> +	unsigned int try;
> +	int rc = 0;
> +
> +	if (count < TPM_HEADER_SIZE)
> +		return -EIO;
> +
> +	for (try = 0; try < TPM_RETRY; try++) {
> +		rc = tpm_tis_try_recv(chip, buf, count);
> +
> +		if (rc == -EIO)
> +			/* Data transfer errors, indicated by EIO, can be
> +			 * recovered by rereading the response.
> +			 */
> +			tpm_tis_write8(priv, TPM_STS(priv->locality),
> +				       TPM_STS_RESPONSE_RETRY);
> +		else
> +			break;
> +	}
> +
> +	tpm_tis_ready(chip);
> +
> +	return rc;
> +}
> +
>  /*
>   * If interrupts are used (signaled by an irq set in the vendor structure)
>   * tpm.c can skip polling for the data to be available as the interrupt is
> diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h
> index 610bfadb6acf..3a6b600d22ba 100644
> --- a/drivers/char/tpm/tpm_tis_core.h
> +++ b/drivers/char/tpm/tpm_tis_core.h
> @@ -34,6 +34,7 @@ enum tis_status {
>  	TPM_STS_GO = 0x20,
>  	TPM_STS_DATA_AVAIL = 0x10,
>  	TPM_STS_DATA_EXPECT = 0x08,
> +	TPM_STS_RESPONSE_RETRY = 0x02,
>  	TPM_STS_READ_ZERO = 0x23, /* bits that must be zero on read */
>  };
>  

Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>

BR, Jarkko
  

Patch

diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index 5ddaf24518be..a6d1396413a7 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -337,7 +337,7 @@  static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
 	return size;
 }
 
-static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+static int tpm_tis_try_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 {
 	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
 	int size = 0;
@@ -345,11 +345,6 @@  static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 	u32 expected;
 	int rc;
 
-	if (count < TPM_HEADER_SIZE) {
-		size = -EIO;
-		goto out;
-	}
-
 	size = recv_data(chip, buf, TPM_HEADER_SIZE);
 	/* read first 10 bytes, including tag, paramsize, and result */
 	if (size < TPM_HEADER_SIZE) {
@@ -382,7 +377,7 @@  static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 		goto out;
 	}
 	status = tpm_tis_status(chip);
-	if (status & TPM_STS_DATA_AVAIL) {	/* retry? */
+	if (status & TPM_STS_DATA_AVAIL) {
 		dev_err(&chip->dev, "Error left over data\n");
 		size = -EIO;
 		goto out;
@@ -396,10 +391,36 @@  static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 	}
 
 out:
-	tpm_tis_ready(chip);
 	return size;
 }
 
+static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+	unsigned int try;
+	int rc = 0;
+
+	if (count < TPM_HEADER_SIZE)
+		return -EIO;
+
+	for (try = 0; try < TPM_RETRY; try++) {
+		rc = tpm_tis_try_recv(chip, buf, count);
+
+		if (rc == -EIO)
+			/* Data transfer errors, indicated by EIO, can be
+			 * recovered by rereading the response.
+			 */
+			tpm_tis_write8(priv, TPM_STS(priv->locality),
+				       TPM_STS_RESPONSE_RETRY);
+		else
+			break;
+	}
+
+	tpm_tis_ready(chip);
+
+	return rc;
+}
+
 /*
  * If interrupts are used (signaled by an irq set in the vendor structure)
  * tpm.c can skip polling for the data to be available as the interrupt is
diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h
index 610bfadb6acf..3a6b600d22ba 100644
--- a/drivers/char/tpm/tpm_tis_core.h
+++ b/drivers/char/tpm/tpm_tis_core.h
@@ -34,6 +34,7 @@  enum tis_status {
 	TPM_STS_GO = 0x20,
 	TPM_STS_DATA_AVAIL = 0x10,
 	TPM_STS_DATA_EXPECT = 0x08,
+	TPM_STS_RESPONSE_RETRY = 0x02,
 	TPM_STS_READ_ZERO = 0x23, /* bits that must be zero on read */
 };