CHROMIUM: tpm: always run ContinueSelftest at resume
[cascardo/linux.git] / drivers / char / tpm / tpm.c
index ad7c732..547f5df 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <linux/poll.h>
 #include <linux/slab.h>
+#include <linux/jiffies.h>
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
 #include <linux/freezer.h>
@@ -348,31 +349,31 @@ static void timeout_work(struct work_struct *work)
        mutex_unlock(&chip->buffer_mutex);
 }
 
-/*
- * Returns max number of jiffies to wait
- */
-unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip,
-                                          u32 ordinal)
+static void set_needs_resume(struct tpm_chip *chip)
 {
-       int duration_idx = TPM_UNDEFINED;
-       int duration = 0;
+       mutex_lock(&chip->resume_mutex);
+       chip->needs_resume = 1;
+       mutex_unlock(&chip->resume_mutex);
+}
 
-       if (ordinal < TPM_MAX_ORDINAL)
-               duration_idx = tpm_ordinal_duration[ordinal];
-       else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) <
-                TPM_MAX_PROTECTED_ORDINAL)
-               duration_idx =
-                   tpm_protected_ordinal_duration[ordinal &
-                                                  TPM_PROTECTED_ORDINAL_MASK];
+#define TPM_ORD_CONTINUE_SELFTEST 83
+#define CONTINUE_SELFTEST_RESULT_SIZE 10
 
-       if (duration_idx != TPM_UNDEFINED)
-               duration = chip->vendor.duration[duration_idx];
-       if (duration <= 0)
-               return 2 * 60 * HZ;
-       else
-               return duration;
-}
-EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
+#define TPM_INTERNAL_RESULT_SIZE 200
+#define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
+#define TPM_ORD_GET_CAP cpu_to_be32(101)
+
+static const struct tpm_input_header tpm_getcap_header = {
+       .tag = TPM_TAG_RQU_COMMAND,
+       .length = cpu_to_be32(22),
+       .ordinal = TPM_ORD_GET_CAP
+};
+
+static struct tpm_input_header continue_selftest_header = {
+       .tag = TPM_TAG_RQU_COMMAND,
+       .length = cpu_to_be32(10),
+       .ordinal = cpu_to_be32(TPM_ORD_CONTINUE_SELFTEST),
+};
 
 /*
  * Internal kernel interface to transmit TPM commands
@@ -440,32 +441,49 @@ out:
        return rc;
 }
 
-#define TPM_DIGEST_SIZE 20
-#define TPM_RET_CODE_IDX 6
-
-enum tpm_capabilities {
-       TPM_CAP_FLAG = cpu_to_be32(4),
-       TPM_CAP_PROP = cpu_to_be32(5),
-       CAP_VERSION_1_1 = cpu_to_be32(0x06),
-       CAP_VERSION_1_2 = cpu_to_be32(0x1A)
-};
-
-enum tpm_sub_capabilities {
-       TPM_CAP_PROP_PCR = cpu_to_be32(0x101),
-       TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103),
-       TPM_CAP_FLAG_PERM = cpu_to_be32(0x108),
-       TPM_CAP_FLAG_VOL = cpu_to_be32(0x109),
-       TPM_CAP_PROP_OWNER = cpu_to_be32(0x111),
-       TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115),
-       TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120),
+void tpm_continue_selftest_nocheck(struct tpm_chip *chip)
+{
+       struct tpm_cmd_t cmd;
+       cmd.header.in = continue_selftest_header;
+       tpm_transmit(chip, (u8 *) &cmd, CONTINUE_SELFTEST_RESULT_SIZE);
+}
+EXPORT_SYMBOL_GPL(tpm_continue_selftest_nocheck);
 
-};
+/* The maximum time in milliseconds that the TPM self test will take to
+ * complete.  TODO(semenzato): 1s should be plenty for all TPMs, but how can we
+ * ensure it?
+ */
+#define TPM_SELF_TEST_DURATION_MSEC 1000
+
+/* We don't want to wait for the self test to complete at resume, because it
+ * impacts the resume speed.  TPM commands are infrequent so the wait is
+ * usually not needed and is wasteful.  Instead, before we send any command, we
+ * check that enough time has elapsed from the resume so that we are
+ * comfortable that the self test has completed.  If not, we wait.  Unlike at
+ * boot, here we don't check the return code of continue_self_test, so we can
+ * use a code path which avoids recursion.  Furthermore, this only works when
+ * ContinueSelfTest is blocking, that is it returns only after the self test
+ * has completed, which is the case for the Infineon TPM.
+ */
+static void resume_if_needed(struct tpm_chip *chip)
+{
+       mutex_lock(&chip->resume_mutex);
+       if (chip->needs_resume) {
+               dev_info(chip->dev, "waiting for TPM self test\n");
+               tpm_continue_selftest_nocheck(chip);
+               chip->needs_resume = 0;
+               dev_info(chip->dev, "TPM delayed resume completed\n");
+       }
+       mutex_unlock(&chip->resume_mutex);
+}
 
-static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
-                           int len, const char *desc)
+ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
+                        int len, const char *desc)
 {
        int err;
 
+       resume_if_needed(chip);
+
        len = tpm_transmit(chip,(u8 *) cmd, len);
        if (len <  0)
                return len;
@@ -478,15 +496,72 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
 
        return err;
 }
+EXPORT_SYMBOL_GPL(tpm_transmit_cmd);
 
-#define TPM_INTERNAL_RESULT_SIZE 200
-#define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
-#define TPM_ORD_GET_CAP cpu_to_be32(101)
+/**
+ * tpm_continue_selftest -- run TPM's selftest
+ * @chip: TPM chip to use
+ *
+ * Returns 0 on success, < 0 in case of fatal error or a value > 0 representing
+ * a TPM error code.
+ */
+int tpm_continue_selftest(struct tpm_chip *chip)
+{
+       int rc;
+       struct tpm_cmd_t cmd;
+
+       cmd.header.in = continue_selftest_header;
+       rc = tpm_transmit_cmd(chip, &cmd,
+                             CONTINUE_SELFTEST_RESULT_SIZE, "continue selftest");
+       return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_continue_selftest);
+
+/*
+ * Returns max number of jiffies to wait
+ */
+unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip,
+                                          u32 ordinal)
+{
+       int duration_idx = TPM_UNDEFINED;
+       int duration = 0;
+
+       if (ordinal < TPM_MAX_ORDINAL)
+               duration_idx = tpm_ordinal_duration[ordinal];
+       else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) <
+                TPM_MAX_PROTECTED_ORDINAL)
+               duration_idx =
+                   tpm_protected_ordinal_duration[ordinal &
+                                                  TPM_PROTECTED_ORDINAL_MASK];
+
+       if (duration_idx != TPM_UNDEFINED)
+               duration = chip->vendor.duration[duration_idx];
+       if (duration <= 0)
+               return 2 * 60 * HZ;
+       else
+               return duration;
+}
+EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
+
+#define TPM_DIGEST_SIZE 20
+#define TPM_RET_CODE_IDX 6
+
+enum tpm_capabilities {
+       TPM_CAP_FLAG = cpu_to_be32(4),
+       TPM_CAP_PROP = cpu_to_be32(5),
+       CAP_VERSION_1_1 = cpu_to_be32(0x06),
+       CAP_VERSION_1_2 = cpu_to_be32(0x1A)
+};
+
+enum tpm_sub_capabilities {
+       TPM_CAP_PROP_PCR = cpu_to_be32(0x101),
+       TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103),
+       TPM_CAP_FLAG_PERM = cpu_to_be32(0x108),
+       TPM_CAP_FLAG_VOL = cpu_to_be32(0x109),
+       TPM_CAP_PROP_OWNER = cpu_to_be32(0x111),
+       TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115),
+       TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120),
 
-static const struct tpm_input_header tpm_getcap_header = {
-       .tag = TPM_TAG_RQU_COMMAND,
-       .length = cpu_to_be32(22),
-       .ordinal = TPM_ORD_GET_CAP
 };
 
 ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap,
@@ -511,7 +586,7 @@ ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap,
                tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
                tpm_cmd.params.getcap_in.subcap = subcap_id;
        }
-       rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
+       rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
        if (!rc)
                *cap = tpm_cmd.params.getcap_out.cap;
        return rc;
@@ -527,8 +602,8 @@ void tpm_gen_interrupt(struct tpm_chip *chip)
        tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
        tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
 
-       rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
-                       "attempting to determine the timeouts");
+       rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
+                             "attempting to determine the timeouts");
 }
 EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
 
@@ -546,8 +621,8 @@ int tpm_get_timeouts(struct tpm_chip *chip)
        tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
        tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
 
-       rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
-                       "attempting to determine the timeouts");
+       rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
+                             "attempting to determine the timeouts");
        if (rc)
                goto duration;
 
@@ -582,8 +657,8 @@ duration:
        tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
        tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
 
-       rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
-                       "attempting to determine the durations");
+       rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
+                             "attempting to determine the durations");
        if (rc)
                return rc;
 
@@ -610,39 +685,12 @@ duration:
                chip->vendor.duration[TPM_MEDIUM] *= 1000;
                chip->vendor.duration[TPM_LONG] *= 1000;
                chip->vendor.duration_adjusted = true;
-               dev_info(chip->dev, "Adjusting TPM timeout parameters.");
+               dev_info(chip->dev, "Adjusting TPM timeout parameters.\n");
        }
        return 0;
 }
 EXPORT_SYMBOL_GPL(tpm_get_timeouts);
 
-#define TPM_ORD_CONTINUE_SELFTEST 83
-#define CONTINUE_SELFTEST_RESULT_SIZE 10
-
-static struct tpm_input_header continue_selftest_header = {
-       .tag = TPM_TAG_RQU_COMMAND,
-       .length = cpu_to_be32(10),
-       .ordinal = cpu_to_be32(TPM_ORD_CONTINUE_SELFTEST),
-};
-
-/**
- * tpm_continue_selftest -- run TPM's selftest
- * @chip: TPM chip to use
- *
- * Returns 0 on success, < 0 in case of fatal error or a value > 0 representing
- * a TPM error code.
- */
-static int tpm_continue_selftest(struct tpm_chip *chip)
-{
-       int rc;
-       struct tpm_cmd_t cmd;
-
-       cmd.header.in = continue_selftest_header;
-       rc = transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE,
-                         "continue selftest");
-       return rc;
-}
-
 ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
                        char *buf)
 {
@@ -743,8 +791,8 @@ static int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
 
        cmd.header.in = pcrread_header;
        cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx);
-       rc = transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE,
-                         "attempting to read a pcr value");
+       rc = tpm_transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE,
+                             "attempting to read a pcr value");
 
        if (rc == 0)
                memcpy(res_buf, cmd.params.pcrread_out.pcr_result,
@@ -808,8 +856,8 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
        cmd.header.in = pcrextend_header;
        cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx);
        memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE);
-       rc = transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
-                         "attempting extend a PCR value");
+       rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
+                             "attempting extend a PCR value");
 
        tpm_chip_put(chip);
        return rc;
@@ -873,7 +921,7 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen)
        if (chip == NULL)
                return -ENODEV;
 
-       rc = transmit_cmd(chip, cmd, buflen, "attempting tpm_cmd");
+       rc = tpm_transmit_cmd(chip, cmd, buflen, "attempting tpm_cmd");
 
        tpm_chip_put(chip);
        return rc;
@@ -929,8 +977,8 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
        struct tpm_chip *chip = dev_get_drvdata(dev);
 
        tpm_cmd.header.in = tpm_readpubek_header;
-       err = transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
-                       "attempting to read the PUBEK");
+       err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
+                              "attempting to read the PUBEK");
        if (err)
                goto out;
 
@@ -990,7 +1038,7 @@ ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr,
                       be32_to_cpu(cap.manufacturer_id));
 
        rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap,
-                       "attempting to determine the 1.1 version");
+                       "attempting to determine the 1.1 version");
        if (rc)
                return 0;
        str += sprintf(str,
@@ -1185,6 +1233,8 @@ ssize_t tpm_write(struct file *file, const char __user *buf,
        while (atomic_read(&chip->data_pending) != 0)
                msleep(TPM_TIMEOUT);
 
+       resume_if_needed(chip);
+
        mutex_lock(&chip->buffer_mutex);
 
        if (in_size > TPM_BUFSIZE)
@@ -1219,6 +1269,7 @@ ssize_t tpm_read(struct file *file, char __user *buf,
        del_singleshot_timer_sync(&chip->user_read_timer);
        flush_work_sync(&chip->work);
        ret_size = atomic_read(&chip->data_pending);
+       /* TODO(wad): atomic_set should come AFTER the buffer is copied. */
        atomic_set(&chip->data_pending, 0);
        if (ret_size > 0) {     /* relay data */
                ssize_t orig_ret_size = ret_size;
@@ -1291,14 +1342,14 @@ int tpm_pm_suspend(struct device *dev, pm_message_t pm_state)
                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");
+               rc = tpm_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");
+       rc = tpm_transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE,
+                             "sending savestate before suspend");
        return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_pm_suspend);
@@ -1314,6 +1365,7 @@ int tpm_pm_resume(struct device *dev)
        if (chip == NULL)
                return -ENODEV;
 
+       set_needs_resume(chip);
        return 0;
 }
 EXPORT_SYMBOL_GPL(tpm_pm_resume);
@@ -1370,6 +1422,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev,
 
        mutex_init(&chip->buffer_mutex);
        mutex_init(&chip->tpm_mutex);
+       mutex_init(&chip->resume_mutex);
        INIT_LIST_HEAD(&chip->list);
 
        INIT_WORK(&chip->work, timeout_work);