Skip to content

Commit

Permalink
tpmlib/tis.c: work around poor TPM implementations
Browse files Browse the repository at this point in the history
Release versions of SKL hanged in tis_send(), while versions with serial
output worked, most likely thanks to increased delay between DRTM
sequence and next TPM command. Apparently, some TPMs don't properly
handle setting STS.commandReady when all of the following conditions are
met:

- TPM has recently finished DRTM sequence (internal work may still be
  happening at that point, there is no way to be sure),
- TPM just transitioned to Idle state (e.g. by changing locality),
- STS.commandReady is written periodically before TPM reports it is in
  Ready state.

When all of the above applies, STS.commandReady is always read as 0,
as if the TPM restarted transition from Idle to Ready each time it is
asked to do so. To work around this, set this bit once and keep checking
in a loop until TIMEOUT_B (2 seconds). Well behaving TPM must be able to
enter Ready state before that time, if it doesn't, error is returned.

Signed-off-by: Krystian Hebel <[email protected]>
  • Loading branch information
krystian-hebel committed Apr 16, 2024
1 parent 75f3f0f commit f16a0d0
Showing 1 changed file with 15 additions and 2 deletions.
17 changes: 15 additions & 2 deletions tpmlib/tis.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,28 @@ size_t tis_send(struct tpmbuff *buf)
u8 status, *buf_ptr;
u32 burstcnt = 0;
u32 count = 0;
int i;

if (locality > TPM_MAX_LOCALITY)
return 0;

for (status = 0; (status & STS_COMMAND_READY) == 0; ) {
tpm_write8(STS_COMMAND_READY, STS(locality));
/*
* TPM may go directly to Ready if time since entering Idle < TIMEOUT_B
* (2 seconds). Some TPMs (e.g. Infineon SLB 9665) don't like having
* STS_COMMAND_READY set during that time and immediately checked, which
* results in bit never being set. To work around this, check status once
* before delaying and don't keep setting STS_COMMAND_READY in a loop.
*/
tpm_write8(STS_COMMAND_READY, STS(locality));
status = tpm_read8(STS(locality));
for (i = 0; (status & STS_COMMAND_READY) == 0 && i < 200; i++) {
tpm_mdelay(10);
status = tpm_read8(STS(locality));
}

if ((status & STS_COMMAND_READY) == 0)
return 0;

buf_ptr = buf->head;

/* send all but the last byte */
Expand Down

0 comments on commit f16a0d0

Please sign in to comment.