tpm: Introduce function to poll for result of self test
authorStefan Berger <stefanb@linux.vnet.ibm.com>
Fri, 11 Nov 2011 17:57:04 +0000 (12:57 -0500)
committerRajiv Andrade <srajiv@linux.vnet.ibm.com>
Wed, 16 Nov 2011 11:42:59 +0000 (09:42 -0200)
This patch introduces a function that runs the TPM_ContinueSelfTest()
function and then polls the TPM to check whether it finished the selftest
and can receive new commands.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
drivers/char/tpm/tpm.c
drivers/char/tpm/tpm.h
drivers/char/tpm/tpm_tis.c

index 9bd4668e28553157fa1d02311bfef9f5e5eb2ab1..2e12b3f9813971b59f6bb7db9a3c0937b507a330 100644 (file)
@@ -627,7 +627,7 @@ static struct tpm_input_header continue_selftest_header = {
  * 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)
+static int tpm_continue_selftest(struct tpm_chip *chip)
 {
        int rc;
        struct tpm_cmd_t cmd;
@@ -637,7 +637,6 @@ int tpm_continue_selftest(struct tpm_chip *chip)
                          "continue selftest");
        return rc;
 }
-EXPORT_SYMBOL_GPL(tpm_continue_selftest);
 
 ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
                        char *buf)
@@ -732,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;
@@ -812,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;
index 5d2be8ae1b48ba4e54b75b57c522c88f57ed0696..e264de16285f8c7d38077320bc11c79edbebf6f4 100644 (file)
@@ -38,6 +38,8 @@ enum tpm_addr {
        TPM_ADDR = 0x4E,
 };
 
+#define TPM_WARN_DOING_SELFTEST 0x802
+
 extern ssize_t tpm_show_pubek(struct device *, struct device_attribute *attr,
                                char *);
 extern ssize_t tpm_show_pcrs(struct device *, struct device_attribute *attr,
@@ -281,7 +283,7 @@ ssize_t     tpm_getcap(struct device *, __be32, cap_t *, const char *);
 
 extern int tpm_get_timeouts(struct tpm_chip *);
 extern void tpm_gen_interrupt(struct tpm_chip *);
-extern int tpm_continue_selftest(struct tpm_chip *);
+extern int tpm_do_selftest(struct tpm_chip *);
 extern unsigned long tpm_calc_ordinal_duration(struct tpm_chip *, u32);
 extern struct tpm_chip* tpm_register_hardware(struct device *,
                                 const struct tpm_vendor_specific *);
index 3f4051a7c5a770440d8d0b32893c1885fa796480..d30d5c3c6c02350bac6874226b424c2abbaaf570 100644 (file)
@@ -616,6 +616,12 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
        /* get the timeouts before testing for irqs */
        tpm_get_timeouts(chip);
 
+       if (tpm_do_selftest(chip)) {
+               dev_err(dev, "TPM self test failed\n");
+               rc = -ENODEV;
+               goto out_err;
+       }
+
        /* INTERRUPT Setup */
        init_waitqueue_head(&chip->vendor.read_queue);
        init_waitqueue_head(&chip->vendor.int_queue);
@@ -722,7 +728,6 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
        list_add(&chip->vendor.list, &tis_chips);
        spin_unlock(&tis_lock);
 
-       tpm_continue_selftest(chip);
 
        return 0;
 out_err:
@@ -790,7 +795,7 @@ static int tpm_tis_pnp_resume(struct pnp_dev *dev)
 
        ret = tpm_pm_resume(&dev->dev);
        if (!ret)
-               tpm_continue_selftest(chip);
+               tpm_do_selftest(chip);
 
        return ret;
 }