aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorDuncan Laurie <dlaurie@chromium.org>2013-03-17 17:56:39 -0400
committerKent Yoder <key@linux.vnet.ibm.com>2013-04-17 10:31:32 -0400
commit32d33b29ba077d6b45de35f2181e0a7411b162f4 (patch)
treee18ca0b8f478faca9d4bece15e3c8fe4710bf2f5 /drivers/char
parent6aa4ef4dab92fc8d4f0e5ea735ae3fd520af510b (diff)
TPM: Retry SaveState command in suspend path
If the TPM has already been sent a SaveState command before the driver is loaded it may have problems sending that same command again later. This issue is seen with the Chromebook Pixel due to a firmware bug in the legacy mode boot path which is sending the SaveState command before booting the kernel. More information is available at http://crbug.com/203524 This change introduces a retry of the SaveState command in the suspend path in order to work around this issue. A future firmware update should fix this but this is also a trivial workaround in the driver that has no effect on systems that do not show this problem. When this does happen the TPM responds with a non-fatal TPM_RETRY code that is defined in the specification: The TPM is too busy to respond to the command immediately, but the command could be resubmitted at a later time. The TPM MAY return TPM_RETRY for any command at any time. It can take several seconds before the TPM will respond again. I measured a typical time between 3 and 4 seconds and the timeout is set at a safe 5 seconds. It is also possible to reproduce this with commands via /dev/tpm0. The bug linked above has a python script attached which can be used to test for this problem. I tested a variety of TPMs from Infineon, Nuvoton, Atmel, and STMicro but was only able to reproduce this with LPC and I2C TPMs from Infineon. The TPM specification only loosely defines this behavior: TPM Main Level 2 Part 3 v1.2 r116, section 3.3. TPM_SaveState: The TPM MAY declare all preserved values invalid in response to any command other than TPM_Init. TCG PC Client BIOS Spec 1.21 section 8.3.1. After issuing a TPM_SaveState command, the OS SHOULD NOT issue TPM commands before transitioning to S3 without issuing another TPM_SaveState command. TCG PC Client TIS 1.21, section 4. Power Management: The TPM_SaveState command allows a Static OS to indicate to the TPM that the platform may enter a low power state where the TPM will be required to enter into the D3 power state. The use of the term "may" is significant in that there is no requirement for the platform to actually enter the low power state after sending the TPM_SaveState command. The software may, in fact, send subsequent commands after sending the TPM_SaveState command. Change-Id: I52b41e826412688e5b6c8ddd3bb16409939704e9 Signed-off-by: Duncan Laurie <dlaurie@chromium.org> Signed-off-by: Kent Yoder <key@linux.vnet.ibm.com>
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/tpm/tpm.c31
-rw-r--r--drivers/char/tpm/tpm.h3
2 files changed, 30 insertions, 4 deletions
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
index 0d2e82f95577..7c3b3dcbfbc8 100644
--- a/drivers/char/tpm/tpm.c
+++ b/drivers/char/tpm/tpm.c
@@ -1337,7 +1337,7 @@ int tpm_pm_suspend(struct device *dev)
1337{ 1337{
1338 struct tpm_chip *chip = dev_get_drvdata(dev); 1338 struct tpm_chip *chip = dev_get_drvdata(dev);
1339 struct tpm_cmd_t cmd; 1339 struct tpm_cmd_t cmd;
1340 int rc; 1340 int rc, try;
1341 1341
1342 u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 }; 1342 u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 };
1343 1343
@@ -1355,9 +1355,32 @@ int tpm_pm_suspend(struct device *dev)
1355 } 1355 }
1356 1356
1357 /* now do the actual savestate */ 1357 /* now do the actual savestate */
1358 cmd.header.in = savestate_header; 1358 for (try = 0; try < TPM_RETRY; try++) {
1359 rc = transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, 1359 cmd.header.in = savestate_header;
1360 "sending savestate before suspend"); 1360 rc = transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, NULL);
1361
1362 /*
1363 * If the TPM indicates that it is too busy to respond to
1364 * this command then retry before giving up. It can take
1365 * several seconds for this TPM to be ready.
1366 *
1367 * This can happen if the TPM has already been sent the
1368 * SaveState command before the driver has loaded. TCG 1.2
1369 * specification states that any communication after SaveState
1370 * may cause the TPM to invalidate previously saved state.
1371 */
1372 if (rc != TPM_WARN_RETRY)
1373 break;
1374 msleep(TPM_TIMEOUT_RETRY);
1375 }
1376
1377 if (rc)
1378 dev_err(chip->dev,
1379 "Error (%d) sending savestate before suspend\n", rc);
1380 else if (try > 0)
1381 dev_warn(chip->dev, "TPM savestate took %dms\n",
1382 try * TPM_TIMEOUT_RETRY);
1383
1361 return rc; 1384 return rc;
1362} 1385}
1363EXPORT_SYMBOL_GPL(tpm_pm_suspend); 1386EXPORT_SYMBOL_GPL(tpm_pm_suspend);
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 81b52015f669..0770d1d79366 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -32,10 +32,12 @@ enum tpm_const {
32 TPM_MINOR = 224, /* officially assigned */ 32 TPM_MINOR = 224, /* officially assigned */
33 TPM_BUFSIZE = 4096, 33 TPM_BUFSIZE = 4096,
34 TPM_NUM_DEVICES = 256, 34 TPM_NUM_DEVICES = 256,
35 TPM_RETRY = 50, /* 5 seconds */
35}; 36};
36 37
37enum tpm_timeout { 38enum tpm_timeout {
38 TPM_TIMEOUT = 5, /* msecs */ 39 TPM_TIMEOUT = 5, /* msecs */
40 TPM_TIMEOUT_RETRY = 100 /* msecs */
39}; 41};
40 42
41/* TPM addresses */ 43/* TPM addresses */
@@ -44,6 +46,7 @@ enum tpm_addr {
44 TPM_ADDR = 0x4E, 46 TPM_ADDR = 0x4E,
45}; 47};
46 48
49#define TPM_WARN_RETRY 0x800
47#define TPM_WARN_DOING_SELFTEST 0x802 50#define TPM_WARN_DOING_SELFTEST 0x802
48#define TPM_ERR_DEACTIVATED 0x6 51#define TPM_ERR_DEACTIVATED 0x6
49#define TPM_ERR_DISABLED 0x7 52#define TPM_ERR_DISABLED 0x7