]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/char/tpm/tpm.c
tpm: Introduce function to poll for result of self test
[mv-sheeva.git] / drivers / char / tpm / tpm.c
index caf8012ef47ce1c69af6f07df4f62e7c5676a581..2e12b3f9813971b59f6bb7db9a3c0937b507a330 100644 (file)
@@ -527,7 +527,7 @@ void tpm_gen_interrupt(struct tpm_chip *chip)
 }
 EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
 
-void tpm_get_timeouts(struct tpm_chip *chip)
+int tpm_get_timeouts(struct tpm_chip *chip)
 {
        struct tpm_cmd_t tpm_cmd;
        struct timeout_t *timeout_cap;
@@ -549,7 +549,7 @@ void tpm_get_timeouts(struct tpm_chip *chip)
        if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
            be32_to_cpu(tpm_cmd.header.out.length)
            != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32))
-               return;
+               return -EINVAL;
 
        timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
        /* Don't overwrite default if value is 0 */
@@ -580,12 +580,12 @@ duration:
        rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
                        "attempting to determine the durations");
        if (rc)
-               return;
+               return rc;
 
        if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
            be32_to_cpu(tpm_cmd.header.out.length)
            != sizeof(tpm_cmd.header.out) + sizeof(u32) + 3 * sizeof(u32))
-               return;
+               return -EINVAL;
 
        duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
        chip->vendor.duration[TPM_SHORT] =
@@ -607,20 +607,36 @@ duration:
                chip->vendor.duration_adjusted = true;
                dev_info(chip->dev, "Adjusting TPM timeout parameters.");
        }
+       return 0;
 }
 EXPORT_SYMBOL_GPL(tpm_get_timeouts);
 
-void tpm_continue_selftest(struct tpm_chip *chip)
+#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)
 {
-       u8 data[] = {
-               0, 193,                 /* TPM_TAG_RQU_COMMAND */
-               0, 0, 0, 10,            /* length */
-               0, 0, 0, 83,            /* TPM_ORD_ContinueSelfTest */
-       };
+       int rc;
+       struct tpm_cmd_t cmd;
 
-       tpm_transmit(chip, data, sizeof(data));
+       cmd.header.in = continue_selftest_header;
+       rc = transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE,
+                         "continue selftest");
+       return rc;
 }
-EXPORT_SYMBOL_GPL(tpm_continue_selftest);
 
 ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
                        char *buf)
@@ -715,7 +731,7 @@ static struct tpm_input_header pcrread_header = {
        .ordinal = TPM_ORDINAL_PCRREAD
 };
 
-int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
+static int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
 {
        int rc;
        struct tpm_cmd_t cmd;
@@ -795,6 +811,45 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
 }
 EXPORT_SYMBOL_GPL(tpm_pcr_extend);
 
+/**
+ * tpm_do_selftest - have the TPM continue its selftest and wait until it
+ *                   can receive further commands
+ * @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_do_selftest(struct tpm_chip *chip)
+{
+       int rc;
+       u8 digest[TPM_DIGEST_SIZE];
+       unsigned int loops;
+       unsigned int delay_msec = 1000;
+       unsigned long duration;
+
+       duration = tpm_calc_ordinal_duration(chip,
+                                            TPM_ORD_CONTINUE_SELFTEST);
+
+       loops = jiffies_to_msecs(duration) / delay_msec;
+
+       rc = tpm_continue_selftest(chip);
+       /* This may fail if there was no TPM driver during a suspend/resume
+        * cycle; some may return 10 (BAD_ORDINAL), others 28 (FAILEDSELFTEST)
+        */
+       if (rc)
+               return rc;
+
+       do {
+               rc = __tpm_pcr_read(chip, 0, digest);
+               if (rc != TPM_WARN_DOING_SELFTEST)
+                       return rc;
+               msleep(delay_msec);
+       } while (--loops > 0);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_do_selftest);
+
 int tpm_send(u32 chip_num, void *cmd, size_t buflen)
 {
        struct tpm_chip *chip;
@@ -963,6 +1018,9 @@ ssize_t tpm_show_durations(struct device *dev, struct device_attribute *attr,
 {
        struct tpm_chip *chip = dev_get_drvdata(dev);
 
+       if (chip->vendor.duration[TPM_LONG] == 0)
+               return 0;
+
        return sprintf(buf, "%d %d %d [%s]\n",
                       jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
                       jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),