|
@@ -369,20 +369,40 @@ err_len:
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * tmp_transmit - Internal kernel interface to transmit TPM commands.
|
|
|
- *
|
|
|
- * @chip: TPM chip to use
|
|
|
- * @buf: TPM command buffer
|
|
|
- * @bufsiz: length of the TPM command buffer
|
|
|
- * @flags: tpm transmit flags - bitmap
|
|
|
- *
|
|
|
- * Return:
|
|
|
- * 0 when the operation is successful.
|
|
|
- * A negative number for system errors (errno).
|
|
|
- */
|
|
|
-ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
|
|
- u8 *buf, size_t bufsiz, unsigned int flags)
|
|
|
+static int tpm_request_locality(struct tpm_chip *chip)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ if (!chip->ops->request_locality)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ rc = chip->ops->request_locality(chip, 0);
|
|
|
+ if (rc < 0)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ chip->locality = rc;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void tpm_relinquish_locality(struct tpm_chip *chip)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ if (!chip->ops->relinquish_locality)
|
|
|
+ return;
|
|
|
+
|
|
|
+ rc = chip->ops->relinquish_locality(chip, chip->locality);
|
|
|
+ if (rc)
|
|
|
+ dev_err(&chip->dev, "%s: : error %d\n", __func__, rc);
|
|
|
+
|
|
|
+ chip->locality = -1;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t tpm_try_transmit(struct tpm_chip *chip,
|
|
|
+ struct tpm_space *space,
|
|
|
+ u8 *buf, size_t bufsiz,
|
|
|
+ unsigned int flags)
|
|
|
{
|
|
|
struct tpm_output_header *header = (void *)buf;
|
|
|
int rc;
|
|
@@ -422,8 +442,6 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
|
|
if (!(flags & TPM_TRANSMIT_UNLOCKED))
|
|
|
mutex_lock(&chip->tpm_mutex);
|
|
|
|
|
|
- if (chip->dev.parent)
|
|
|
- pm_runtime_get_sync(chip->dev.parent);
|
|
|
|
|
|
if (chip->ops->clk_enable != NULL)
|
|
|
chip->ops->clk_enable(chip, true);
|
|
@@ -431,19 +449,20 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
|
|
/* Store the decision as chip->locality will be changed. */
|
|
|
need_locality = chip->locality == -1;
|
|
|
|
|
|
- if (!(flags & TPM_TRANSMIT_RAW) &&
|
|
|
- need_locality && chip->ops->request_locality) {
|
|
|
- rc = chip->ops->request_locality(chip, 0);
|
|
|
+ if (!(flags & TPM_TRANSMIT_RAW) && need_locality) {
|
|
|
+ rc = tpm_request_locality(chip);
|
|
|
if (rc < 0)
|
|
|
goto out_no_locality;
|
|
|
- chip->locality = rc;
|
|
|
}
|
|
|
|
|
|
+ if (chip->dev.parent)
|
|
|
+ pm_runtime_get_sync(chip->dev.parent);
|
|
|
+
|
|
|
rc = tpm2_prepare_space(chip, space, ordinal, buf);
|
|
|
if (rc)
|
|
|
goto out;
|
|
|
|
|
|
- rc = chip->ops->send(chip, (u8 *) buf, count);
|
|
|
+ rc = chip->ops->send(chip, buf, count);
|
|
|
if (rc < 0) {
|
|
|
if (rc != -EPIPE)
|
|
|
dev_err(&chip->dev,
|
|
@@ -480,7 +499,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
|
|
goto out;
|
|
|
|
|
|
out_recv:
|
|
|
- len = chip->ops->recv(chip, (u8 *) buf, bufsiz);
|
|
|
+ len = chip->ops->recv(chip, buf, bufsiz);
|
|
|
if (len < 0) {
|
|
|
rc = len;
|
|
|
dev_err(&chip->dev,
|
|
@@ -499,27 +518,95 @@ out_recv:
|
|
|
rc = tpm2_commit_space(chip, space, ordinal, buf, &len);
|
|
|
|
|
|
out:
|
|
|
- if (need_locality && chip->ops->relinquish_locality) {
|
|
|
- chip->ops->relinquish_locality(chip, chip->locality);
|
|
|
- chip->locality = -1;
|
|
|
- }
|
|
|
+ if (chip->dev.parent)
|
|
|
+ pm_runtime_put_sync(chip->dev.parent);
|
|
|
+
|
|
|
+ if (need_locality)
|
|
|
+ tpm_relinquish_locality(chip);
|
|
|
+
|
|
|
out_no_locality:
|
|
|
if (chip->ops->clk_enable != NULL)
|
|
|
chip->ops->clk_enable(chip, false);
|
|
|
|
|
|
- if (chip->dev.parent)
|
|
|
- pm_runtime_put_sync(chip->dev.parent);
|
|
|
-
|
|
|
if (!(flags & TPM_TRANSMIT_UNLOCKED))
|
|
|
mutex_unlock(&chip->tpm_mutex);
|
|
|
return rc ? rc : len;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * tmp_transmit_cmd - send a tpm command to the device
|
|
|
+ * tpm_transmit - Internal kernel interface to transmit TPM commands.
|
|
|
+ *
|
|
|
+ * @chip: TPM chip to use
|
|
|
+ * @space: tpm space
|
|
|
+ * @buf: TPM command buffer
|
|
|
+ * @bufsiz: length of the TPM command buffer
|
|
|
+ * @flags: tpm transmit flags - bitmap
|
|
|
+ *
|
|
|
+ * A wrapper around tpm_try_transmit that handles TPM2_RC_RETRY
|
|
|
+ * returns from the TPM and retransmits the command after a delay up
|
|
|
+ * to a maximum wait of TPM2_DURATION_LONG.
|
|
|
+ *
|
|
|
+ * Note: TPM1 never returns TPM2_RC_RETRY so the retry logic is TPM2
|
|
|
+ * only
|
|
|
+ *
|
|
|
+ * Return:
|
|
|
+ * the length of the return when the operation is successful.
|
|
|
+ * A negative number for system errors (errno).
|
|
|
+ */
|
|
|
+ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
|
|
+ u8 *buf, size_t bufsiz, unsigned int flags)
|
|
|
+{
|
|
|
+ struct tpm_output_header *header = (struct tpm_output_header *)buf;
|
|
|
+ /* space for header and handles */
|
|
|
+ u8 save[TPM_HEADER_SIZE + 3*sizeof(u32)];
|
|
|
+ unsigned int delay_msec = TPM2_DURATION_SHORT;
|
|
|
+ u32 rc = 0;
|
|
|
+ ssize_t ret;
|
|
|
+ const size_t save_size = min(space ? sizeof(save) : TPM_HEADER_SIZE,
|
|
|
+ bufsiz);
|
|
|
+ /* the command code is where the return code will be */
|
|
|
+ u32 cc = be32_to_cpu(header->return_code);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Subtlety here: if we have a space, the handles will be
|
|
|
+ * transformed, so when we restore the header we also have to
|
|
|
+ * restore the handles.
|
|
|
+ */
|
|
|
+ memcpy(save, buf, save_size);
|
|
|
+
|
|
|
+ for (;;) {
|
|
|
+ ret = tpm_try_transmit(chip, space, buf, bufsiz, flags);
|
|
|
+ if (ret < 0)
|
|
|
+ break;
|
|
|
+ rc = be32_to_cpu(header->return_code);
|
|
|
+ if (rc != TPM2_RC_RETRY && rc != TPM2_RC_TESTING)
|
|
|
+ break;
|
|
|
+ /*
|
|
|
+ * return immediately if self test returns test
|
|
|
+ * still running to shorten boot time.
|
|
|
+ */
|
|
|
+ if (rc == TPM2_RC_TESTING && cc == TPM2_CC_SELF_TEST)
|
|
|
+ break;
|
|
|
+ delay_msec *= 2;
|
|
|
+ if (delay_msec > TPM2_DURATION_LONG) {
|
|
|
+ if (rc == TPM2_RC_RETRY)
|
|
|
+ dev_err(&chip->dev, "in retry loop\n");
|
|
|
+ else
|
|
|
+ dev_err(&chip->dev,
|
|
|
+ "self test is still running\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ tpm_msleep(delay_msec);
|
|
|
+ memcpy(buf, save, save_size);
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+/**
|
|
|
+ * tpm_transmit_cmd - send a tpm command to the device
|
|
|
* The function extracts tpm out header return code
|
|
|
*
|
|
|
* @chip: TPM chip to use
|
|
|
+ * @space: tpm space
|
|
|
* @buf: TPM command buffer
|
|
|
* @bufsiz: length of the buffer
|
|
|
* @min_rsp_body_length: minimum expected length of response body
|
|
@@ -532,7 +619,7 @@ out_no_locality:
|
|
|
* A positive number for a TPM error.
|
|
|
*/
|
|
|
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
|
|
|
- const void *buf, size_t bufsiz,
|
|
|
+ void *buf, size_t bufsiz,
|
|
|
size_t min_rsp_body_length, unsigned int flags,
|
|
|
const char *desc)
|
|
|
{
|
|
@@ -540,7 +627,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
|
|
|
int err;
|
|
|
ssize_t len;
|
|
|
|
|
|
- len = tpm_transmit(chip, space, (u8 *)buf, bufsiz, flags);
|
|
|
+ len = tpm_transmit(chip, space, buf, bufsiz, flags);
|
|
|
if (len < 0)
|
|
|
return len;
|
|
|
|
|
@@ -666,6 +753,8 @@ int tpm_get_timeouts(struct tpm_chip *chip)
|
|
|
msecs_to_jiffies(TPM2_DURATION_MEDIUM);
|
|
|
chip->duration[TPM_LONG] =
|
|
|
msecs_to_jiffies(TPM2_DURATION_LONG);
|
|
|
+ chip->duration[TPM_LONG_LONG] =
|
|
|
+ msecs_to_jiffies(TPM2_DURATION_LONG_LONG);
|
|
|
|
|
|
chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS;
|
|
|
return 0;
|
|
@@ -754,6 +843,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
|
|
|
usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
|
|
|
chip->duration[TPM_LONG] =
|
|
|
usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
|
|
|
+ chip->duration[TPM_LONG_LONG] = 0; /* not used under 1.2 */
|
|
|
|
|
|
/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
|
|
|
* value wrong and apparently reports msecs rather than usecs. So we
|
|
@@ -969,6 +1059,10 @@ int tpm_do_selftest(struct tpm_chip *chip)
|
|
|
loops = jiffies_to_msecs(duration) / delay_msec;
|
|
|
|
|
|
rc = tpm_continue_selftest(chip);
|
|
|
+ if (rc == TPM_ERR_INVALID_POSTINIT) {
|
|
|
+ chip->flags |= TPM_CHIP_FLAG_ALWAYS_POWERED;
|
|
|
+ dev_info(&chip->dev, "TPM not ready (%d)\n", rc);
|
|
|
+ }
|
|
|
/* This may fail if there was no TPM driver during a suspend/resume
|
|
|
* cycle; some may return 10 (BAD_ORDINAL), others 28 (FAILEDSELFTEST)
|
|
|
*/
|