diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/tpm/tpm-interface.c | 75 | ||||
-rw-r--r-- | drivers/char/tpm/tpm.h | 1 |
2 files changed, 61 insertions, 15 deletions
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 22288ff70a0b..d5379a79274c 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -399,21 +399,10 @@ static void tpm_relinquish_locality(struct tpm_chip *chip) chip->locality = -1; } -/** - * 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 - * - * 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 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; @@ -545,6 +534,62 @@ out_no_locality: } /** + * 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); + + /* + * 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) + break; + delay_msec *= 2; + if (delay_msec > TPM2_DURATION_LONG) { + dev_err(&chip->dev, "TPM is in retry loop\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 * diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index ab3bcdd4d328..67656a97793a 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -115,6 +115,7 @@ enum tpm2_return_codes { TPM2_RC_COMMAND_CODE = 0x0143, TPM2_RC_TESTING = 0x090A, /* RC_WARN */ TPM2_RC_REFERENCE_H0 = 0x0910, + TPM2_RC_RETRY = 0x0922, }; enum tpm2_algorithms { |