Some errors during command transmission and response reception are recoverable. Implement the specified retry mechanisms. Recoverable errors during response reception: * invalid response size during header read * left over data: a communication error can lead to a FIFO read of 0xFFs and an unexpected STS.dataAvail = 1, subsequently * CRC mismatch Recoverable errors during transmit: * CRC mismatch Signed-off-by: Johannes Holland <johannes.holland@xxxxxxxxxxxx> --- drivers/char/tpm/tpm_tis_core.c | 98 +++++++++++++++++++-------------- drivers/char/tpm/tpm_tis_core.h | 1 + 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index f1c893a5a38f..a2b6fba7f719 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -287,6 +287,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); int size = 0; + int i = 0; int status; u32 expected; int rc; @@ -296,45 +297,52 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) goto out; } - size = recv_data(chip, buf, TPM_HEADER_SIZE); - /* read first 10 bytes, including tag, paramsize, and result */ - if (size < TPM_HEADER_SIZE) { - dev_err(&chip->dev, "Unable to read header\n"); - goto out; - } + do { + if (size < 0) + tpm_tis_write8(priv, TPM_STS(priv->locality), + TPM_STS_RESPONSE_RETRY); + + size = recv_data(chip, buf, TPM_HEADER_SIZE); + /* read first 10 bytes, including tag, paramsize, and result */ + if (size < TPM_HEADER_SIZE) { + dev_err(&chip->dev, "Unable to read header\n"); + goto out; + } - expected = be32_to_cpu(*(__be32 *) (buf + 2)); - if (expected > count || expected < TPM_HEADER_SIZE) { - size = -EIO; - goto out; - } + expected = be32_to_cpu(*(__be32 *)(buf + 2)); + if (expected > count || expected < TPM_HEADER_SIZE) { + dev_info(&chip->dev, "Bad response size: %d. Retry...\n", expected); + size = -EIO; + continue; + } - size += recv_data(chip, &buf[TPM_HEADER_SIZE], - expected - TPM_HEADER_SIZE); - if (size < expected) { - dev_err(&chip->dev, "Unable to read remainder of result\n"); - size = -ETIME; - goto out; - } + size += recv_data(chip, &buf[TPM_HEADER_SIZE], + expected - TPM_HEADER_SIZE); + if (size < expected) { + dev_err(&chip->dev, "Unable to read remainder of result\n"); + size = -ETIME; + goto out; + } - if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, - &priv->int_queue, false) < 0) { - size = -ETIME; - goto out; - } - status = tpm_tis_status(chip); - if (status & TPM_STS_DATA_AVAIL) { /* retry? */ - dev_err(&chip->dev, "Error left over data\n"); - size = -EIO; - goto out; - } + if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, + &priv->int_queue, false) < 0) { + size = -ETIME; + goto out; + } + status = tpm_tis_status(chip); + if (status & TPM_STS_DATA_AVAIL) { + dev_info(&chip->dev, "Error left over data. Retry...\n"); + size = -EIO; + continue; + } - rc = tpm_tis_verify_crc(priv, (size_t)size, buf); - if (rc < 0) { - dev_err(&chip->dev, "Error crc mismatch for response.\n"); - size = rc; - goto out; - } + rc = tpm_tis_verify_crc(priv, (size_t)size, buf); + if (rc < 0) { + dev_info(&chip->dev, "Error crc mismatch for response. Retry...\n"); + size = rc; + continue; + } + } while (unlikely(size < 0) && i++ < TPM_RETRY); out: tpm_tis_ready(chip); @@ -444,18 +452,24 @@ static int tpm_tis_send_main(struct tpm_chip *chip, const u8 *buf, size_t len) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); int rc; + int i = 0; u32 ordinal; unsigned long dur; - rc = tpm_tis_send_data(chip, buf, len); - if (rc < 0) - return rc; + do { + rc = tpm_tis_send_data(chip, buf, len); + if (rc < 0) + return rc; - rc = tpm_tis_verify_crc(priv, len, buf); - if (rc < 0) { - dev_err(&chip->dev, "Error crc mismatch for command.\n"); + rc = tpm_tis_verify_crc(priv, len, buf); + if (rc < 0) { + dev_info(&chip->dev, "Error crc mismatch for command. Retry...\n"); + tpm_tis_ready(chip); + } + } while (unlikely(rc < 0) && i++ < TPM_RETRY); + + if (rc < 0) return rc; - } /* go and do it */ rc = tpm_tis_write8(priv, TPM_STS(priv->locality), TPM_STS_GO); diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index 21b4a67c9aac..b4b36d5c5514 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 */ }; -- 2.31.1.windows.1