From: Amir Mizinski <amirmizi6@xxxxxxxxx> Currently, the driver polls the TPM_STS.stsValid field until TRUE; then it reads TPM_STS register again to verify only that TPM_STS.expect field is FALSE (i.e., it ignores TPM_STS.stsValid). Since TPM_STS.stsValid represents the TPM_STS.expect validity, a check of only one of these fields is wrong. Fix this condition so that both fields are checked in the same TPM_STS register read. Modify the signature of wait_for_tpm_stat() to tpm_tis_wait_for_stat(), adding an additional "expected" parameter to its call. tpm_tis_wait_for_stat() is now polling the TPM_STS with a mask and waits for the value in "expected". This modification adds the ability to check if certain TPM_STS bits have been cleared. For example, use the new parameter to check in status that TPM_STS_VALID is set and also that TPM_STS_EXPECT is zeroed. This prevents a racy check. Suggested-by: Benoit Houyere <benoit.houyere@xxxxxx> Signed-off-by: Amir Mizinski <amirmizi6@xxxxxxxxx> --- drivers/char/tpm/tpm_tis_core.c | 61 ++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 69579ef..f833f35 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -44,9 +44,9 @@ static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask, return false; } -static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, - unsigned long timeout, wait_queue_head_t *queue, - bool check_cancel) +static int tpm_tis_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 expected, + unsigned long timeout, + wait_queue_head_t *queue, bool check_cancel) { unsigned long stop; long rc; @@ -55,7 +55,7 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, /* check current status */ status = chip->ops->status(chip); - if ((status & mask) == mask) + if ((status & mask) == expected) return 0; stop = jiffies + timeout; @@ -83,7 +83,7 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, usleep_range(TPM_TIMEOUT_USECS_MIN, TPM_TIMEOUT_USECS_MAX); status = chip->ops->status(chip); - if ((status & mask) == mask) + if ((status & mask) == expected) return 0; } while (time_before(jiffies, stop)); } @@ -259,10 +259,11 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) int size = 0, burstcnt, rc; while (size < count) { - rc = wait_for_tpm_stat(chip, - TPM_STS_DATA_AVAIL | TPM_STS_VALID, - chip->timeout_c, - &priv->read_queue, true); + rc = tpm_tis_wait_for_stat(chip, + TPM_STS_DATA_AVAIL | TPM_STS_VALID, + TPM_STS_DATA_AVAIL | TPM_STS_VALID, + chip->timeout_c, &priv->read_queue, + true); if (rc < 0) return rc; burstcnt = get_burstcount(chip); @@ -315,8 +316,9 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) goto out; } - if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, - &priv->int_queue, false) < 0) { + if (tpm_tis_wait_for_stat(chip, TPM_STS_VALID, TPM_STS_VALID, + chip->timeout_c, &priv->int_queue, + false) < 0) { size = -ETIME; goto out; } @@ -342,14 +344,14 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len) struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); int rc, status, burstcnt; size_t count = 0; - bool itpm = priv->flags & TPM_TIS_ITPM_WORKAROUND; status = tpm_tis_status(chip); if ((status & TPM_STS_COMMAND_READY) == 0) { tpm_tis_ready(chip); - if (wait_for_tpm_stat - (chip, TPM_STS_COMMAND_READY, chip->timeout_b, - &priv->int_queue, false) < 0) { + if (tpm_tis_wait_for_stat(chip, TPM_STS_COMMAND_READY, + TPM_STS_COMMAND_READY, + chip->timeout_b, &priv->int_queue, + false) < 0) { rc = -ETIME; goto out_err; } @@ -369,34 +371,24 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len) goto out_err; count += burstcnt; - - if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, - &priv->int_queue, false) < 0) { + if (tpm_tis_wait_for_stat(chip, TPM_STS_VALID | TPM_STS_DATA_EXPECT, + TPM_STS_VALID | TPM_STS_DATA_EXPECT, chip->timeout_a, + &priv->int_queue, false) < 0) { rc = -ETIME; goto out_err; } - status = tpm_tis_status(chip); - if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) { - rc = -EIO; - goto out_err; - } } /* write last byte */ rc = tpm_tis_write8(priv, TPM_DATA_FIFO(priv->locality), buf[count]); if (rc < 0) goto out_err; - - if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, - &priv->int_queue, false) < 0) { + if (tpm_tis_wait_for_stat(chip, TPM_STS_VALID | TPM_STS_DATA_EXPECT, + TPM_STS_VALID, chip->timeout_a, + &priv->int_queue, false) < 0) { rc = -ETIME; goto out_err; } - status = tpm_tis_status(chip); - if (!itpm && (status & TPM_STS_DATA_EXPECT) != 0) { - rc = -EIO; - goto out_err; - } return 0; @@ -451,9 +443,10 @@ static int tpm_tis_send_main(struct tpm_chip *chip, const u8 *buf, size_t len) ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); dur = tpm_calc_ordinal_duration(chip, ordinal); - if (wait_for_tpm_stat - (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, dur, - &priv->read_queue, false) < 0) { + if (tpm_tis_wait_for_stat(chip, + TPM_STS_DATA_AVAIL | TPM_STS_VALID, + TPM_STS_DATA_AVAIL | TPM_STS_VALID, + dur, &priv->read_queue, false) < 0) { rc = -ETIME; goto out_err; } -- 2.7.4