}
EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
-static int tpm_validate_command(struct tpm_chip *chip,
- struct tpm_space *space,
- const u8 *cmd,
- size_t len)
+static int tpm_validate_command(struct tpm_chip *chip, struct tpm_space *space,
+ const void *cmd, size_t len)
{
- const struct tpm_input_header *header = (const void *)cmd;
+ const struct tpm_header *header = cmd;
int i;
u32 cc;
u32 attrs;
return chip->ops->go_idle(chip);
}
-static ssize_t tpm_try_transmit(struct tpm_chip *chip,
- struct tpm_space *space,
- u8 *buf, size_t bufsiz,
- unsigned int flags)
+static ssize_t tpm_try_transmit(struct tpm_chip *chip, struct tpm_space *space,
+ void *buf, size_t bufsiz, unsigned int flags)
{
- struct tpm_output_header *header = (void *)buf;
+ struct tpm_header *header = buf;
int rc;
ssize_t len = 0;
u32 count, ordinal;
unsigned long stop;
- bool need_locality;
rc = tpm_validate_command(chip, space, buf, bufsiz);
if (rc == -EINVAL)
if (bufsiz > TPM_BUFSIZE)
bufsiz = TPM_BUFSIZE;
- count = be32_to_cpu(*((__be32 *) (buf + 2)));
- ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+ count = be32_to_cpu(header->length);
+ ordinal = be32_to_cpu(header->ordinal);
if (count == 0)
return -ENODATA;
if (count > bufsiz) {
return -E2BIG;
}
- if (!(flags & TPM_TRANSMIT_UNLOCKED) && !(flags & TPM_TRANSMIT_NESTED))
- mutex_lock(&chip->tpm_mutex);
-
- if (chip->ops->clk_enable != NULL)
- chip->ops->clk_enable(chip, true);
-
- /* Store the decision as chip->locality will be changed. */
- need_locality = chip->locality == -1;
-
- if (need_locality) {
- rc = tpm_request_locality(chip, flags);
- if (rc < 0) {
- need_locality = false;
- goto out_locality;
- }
- }
-
- rc = tpm_cmd_ready(chip, flags);
- if (rc)
- goto out_locality;
-
rc = tpm2_prepare_space(chip, space, ordinal, buf);
if (rc)
- goto out;
+ return rc;
rc = chip->ops->send(chip, buf, count);
if (rc < 0) {
if (rc != -EPIPE)
dev_err(&chip->dev,
- "%s: tpm_send: error %d\n", __func__, rc);
- goto out;
+ "%s: send(): error %d\n", __func__, rc);
+ goto out_rc;
+ }
+
+ /* A sanity check. send() should just return zero on success e.g.
+ * not the command length.
+ */
+ if (rc > 0) {
+ dev_warn(&chip->dev,
+ "%s: send(): invalid value %d\n", __func__, rc);
+ rc = 0;
}
if (chip->flags & TPM_CHIP_FLAG_IRQ)
if (chip->ops->req_canceled(chip, status)) {
dev_err(&chip->dev, "Operation Canceled\n");
rc = -ECANCELED;
- goto out;
+ goto out_rc;
}
tpm_msleep(TPM_TIMEOUT_POLL);
chip->ops->cancel(chip);
dev_err(&chip->dev, "Operation Timed out\n");
rc = -ETIME;
- goto out;
+ goto out_rc;
out_recv:
len = chip->ops->recv(chip, buf, bufsiz);
if (len < 0) {
rc = len;
- dev_err(&chip->dev,
- "tpm_transmit: tpm_recv: error %d\n", rc);
- goto out;
- } else if (len < TPM_HEADER_SIZE) {
+ dev_err(&chip->dev, "tpm_transmit: tpm_recv: error %d\n", rc);
+ } else if (len < TPM_HEADER_SIZE || len != be32_to_cpu(header->length))
rc = -EFAULT;
- goto out;
- }
-
- if (len != be32_to_cpu(header->length)) {
- rc = -EFAULT;
- goto out;
- }
- rc = tpm2_commit_space(chip, space, ordinal, buf, &len);
- if (rc)
- dev_err(&chip->dev, "tpm2_commit_space: error %d\n", rc);
-
-out:
- /* may fail but do not override previous error value in rc */
- tpm_go_idle(chip, flags);
-
-out_locality:
- if (need_locality)
- tpm_relinquish_locality(chip, flags);
-
- if (chip->ops->clk_enable != NULL)
- chip->ops->clk_enable(chip, false);
+out_rc:
+ if (!rc)
+ rc = tpm2_commit_space(chip, space, ordinal, buf, &len);
- if (!(flags & TPM_TRANSMIT_UNLOCKED) && !(flags & TPM_TRANSMIT_NESTED))
- mutex_unlock(&chip->tpm_mutex);
return rc ? rc : len;
}
/**
* tpm_transmit - Internal kernel interface to transmit TPM commands.
+ * @chip: a TPM chip to use
+ * @space: a TPM space
+ * @buf: a TPM command buffer
+ * @bufsiz: length of the TPM command buffer
+ * @flags: TPM transmit flags
*
- * @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.
*
- * 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
+ * Note that TPM 1.x never returns TPM2_RC_RETRY so the retry logic is TPM 2.0
+ * only.
*
* Return:
- * the length of the return when the operation is successful.
- * A negative number for system errors (errno).
+ * * The response length - OK
+ * * -errno - A system error
*/
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;
+ struct tpm_header *header = (struct tpm_header *)buf;
/* space for header and handles */
u8 save[TPM_HEADER_SIZE + 3*sizeof(u32)];
unsigned int delay_msec = TPM2_DURATION_SHORT;
+ bool has_locality = false;
u32 rc = 0;
ssize_t ret;
const size_t save_size = min(space ? sizeof(save) : TPM_HEADER_SIZE,
memcpy(save, buf, save_size);
for (;;) {
+ if (!(flags & TPM_TRANSMIT_UNLOCKED) &&
+ !(flags & TPM_TRANSMIT_NESTED))
+ mutex_lock(&chip->tpm_mutex);
+
+ if (chip->ops->clk_enable != NULL)
+ chip->ops->clk_enable(chip, true);
+
+ if (chip->locality == -1) {
+ ret = tpm_request_locality(chip, flags);
+ if (ret)
+ goto out_locality;
+ has_locality = true;
+ }
+
+ ret = tpm_cmd_ready(chip, flags);
+ if (ret)
+ goto out_locality;
+
ret = tpm_try_transmit(chip, space, buf, bufsiz, flags);
+
+ /* This may fail but do not override ret. */
+ tpm_go_idle(chip, flags);
+
+out_locality:
+ if (has_locality)
+ tpm_relinquish_locality(chip, flags);
+
+ if (chip->ops->clk_enable != NULL)
+ chip->ops->clk_enable(chip, false);
+
+ if (!(flags & TPM_TRANSMIT_UNLOCKED) &&
+ !(flags & TPM_TRANSMIT_NESTED))
+ mutex_unlock(&chip->tpm_mutex);
+
if (ret < 0)
break;
rc = be32_to_cpu(header->return_code);
}
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
- * @flags: tpm transmit flags - bitmap
- * @desc: command description used in the error message
+ * @chip: a TPM chip to use
+ * @space: a TPM space
+ * @buf: a TPM command buffer
+ * @min_rsp_body_length: minimum expected length of response body
+ * @flags: TPM transmit flags
+ * @desc: command description used in the error message
*
* Return:
- * 0 when the operation is successful.
- * A negative number for system errors (errno).
- * A positive number for a TPM error.
+ * * 0 - OK
+ * * -errno - A system error
+ * * TPM_RC - A TPM error
*/
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
- void *buf, size_t bufsiz,
- size_t min_rsp_body_length, unsigned int flags,
- const char *desc)
+ struct tpm_buf *buf, size_t min_rsp_body_length,
+ unsigned int flags, const char *desc)
{
- const struct tpm_output_header *header = buf;
+ const struct tpm_header *header = (struct tpm_header *)buf->data;
int err;
ssize_t len;
- len = tpm_transmit(chip, space, buf, bufsiz, flags);
+ len = tpm_transmit(chip, space, buf->data, PAGE_SIZE, flags);
if (len < 0)
return len;
err = be32_to_cpu(header->return_code);
if (err != 0 && err != TPM_ERR_DISABLED && err != TPM_ERR_DEACTIVATED
- && desc)
+ && err != TPM2_RC_TESTING && desc)
dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err,
desc);
if (err)
*/
int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen)
{
+ struct tpm_buf buf;
int rc;
chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
- rc = tpm_transmit_cmd(chip, NULL, cmd, buflen, 0, 0,
+ rc = tpm_buf_init(&buf, 0, 0);
+ if (rc)
+ goto out;
+
+ memcpy(buf.data, cmd, buflen);
+ rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0,
"attempting to a send a command");
+ tpm_buf_destroy(&buf);
+out:
tpm_put_ops(chip);
return rc;
}