[PATCH v19 1/5] tpm_tis: Fix expected bit handling
From: <hidden>
Date: 2021-11-04 14:05:59
Also in:
linux-integrity, lkml
Subsystem:
the rest, tpm device driver · Maintainers:
Linus Torvalds, Peter Huewe, Jarkko Sakkinen
From: Amir Mizinski <redacted> 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 <redacted> Signed-off-by: Amir Mizinski <redacted> Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org> --- 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