]> Pileus Git - ~andy/linux/blobdiff - drivers/char/tpm/tpm.c
Merge branch 'master' into next
[~andy/linux] / drivers / char / tpm / tpm.c
index 068c816e69423bb0065100f4599310907eb08bc9..05ad4a17a28f238ecc197e67051f60a50faad769 100644 (file)
@@ -1068,6 +1068,27 @@ void tpm_remove_hardware(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(tpm_remove_hardware);
 
+#define TPM_ORD_SAVESTATE cpu_to_be32(152)
+#define SAVESTATE_RESULT_SIZE 10
+
+static struct tpm_input_header savestate_header = {
+       .tag = TPM_TAG_RQU_COMMAND,
+       .length = cpu_to_be32(10),
+       .ordinal = TPM_ORD_SAVESTATE
+};
+
+/* Bug workaround - some TPM's don't flush the most
+ * recently changed pcr on suspend, so force the flush
+ * with an extend to the selected _unused_ non-volatile pcr.
+ */
+static int tpm_suspend_pcr;
+static int __init tpm_suspend_setup(char *str)
+{
+       get_option(&str, &tpm_suspend_pcr);
+       return 1;
+}
+__setup("tpm_suspend_pcr=", tpm_suspend_setup);
+
 /*
  * We are about to suspend. Save the TPM state
  * so that it can be restored.
@@ -1075,17 +1096,29 @@ EXPORT_SYMBOL_GPL(tpm_remove_hardware);
 int tpm_pm_suspend(struct device *dev, pm_message_t pm_state)
 {
        struct tpm_chip *chip = dev_get_drvdata(dev);
-       u8 savestate[] = {
-               0, 193,         /* TPM_TAG_RQU_COMMAND */
-               0, 0, 0, 10,    /* blob length (in bytes) */
-               0, 0, 0, 152    /* TPM_ORD_SaveState */
-       };
+       struct tpm_cmd_t cmd;
+       int rc;
+
+       u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 };
 
        if (chip == NULL)
                return -ENODEV;
 
-       tpm_transmit(chip, savestate, sizeof(savestate));
-       return 0;
+       /* for buggy tpm, flush pcrs with extend to selected dummy */
+       if (tpm_suspend_pcr) {
+               cmd.header.in = pcrextend_header;
+               cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(tpm_suspend_pcr);
+               memcpy(cmd.params.pcrextend_in.hash, dummy_hash,
+                      TPM_DIGEST_SIZE);
+               rc = transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
+                                 "extending dummy pcr before suspend");
+       }
+
+       /* now do the actual savestate */
+       cmd.header.in = savestate_header;
+       rc = transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE,
+                         "sending savestate before suspend");
+       return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_pm_suspend);