tpm: fix suspend/resume paths for TPM 2.0
[cascardo/linux.git] / drivers / char / tpm / tpm-interface.c
index 6af1700..e85d341 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2004 IBM Corporation
+ * Copyright (C) 2014 Intel Corporation
  *
  * Authors:
  * Leendert van Doorn <leendert@watson.ibm.com>
@@ -47,10 +48,6 @@ module_param_named(suspend_pcr, tpm_suspend_pcr, uint, 0644);
 MODULE_PARM_DESC(suspend_pcr,
                 "PCR to use for dummy writes to faciltate flush on suspend.");
 
-static LIST_HEAD(tpm_chip_list);
-static DEFINE_SPINLOCK(driver_lock);
-static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);
-
 /*
  * Array with one entry per ordinal defining the maximum amount
  * of time the chip could take to return the result.  The ordinal
@@ -346,7 +343,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
        if (count == 0)
                return -ENODATA;
        if (count > bufsiz) {
-               dev_err(chip->dev,
+               dev_err(chip->pdev,
                        "invalid count value %x %zx\n", count, bufsiz);
                return -E2BIG;
        }
@@ -355,7 +352,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
 
        rc = chip->ops->send(chip, (u8 *) buf, count);
        if (rc < 0) {
-               dev_err(chip->dev,
+               dev_err(chip->pdev,
                        "tpm_transmit: tpm_send: error %zd\n", rc);
                goto out;
        }
@@ -363,7 +360,10 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
        if (chip->vendor.irq)
                goto out_recv;
 
-       stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
+       if (chip->flags & TPM_CHIP_FLAG_TPM2)
+               stop = jiffies + tpm2_calc_ordinal_duration(chip, ordinal);
+       else
+               stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
        do {
                u8 status = chip->ops->status(chip);
                if ((status & chip->ops->req_complete_mask) ==
@@ -371,7 +371,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
                        goto out_recv;
 
                if (chip->ops->req_canceled(chip, status)) {
-                       dev_err(chip->dev, "Operation Canceled\n");
+                       dev_err(chip->pdev, "Operation Canceled\n");
                        rc = -ECANCELED;
                        goto out;
                }
@@ -381,14 +381,14 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
        } while (time_before(jiffies, stop));
 
        chip->ops->cancel(chip);
-       dev_err(chip->dev, "Operation Timed out\n");
+       dev_err(chip->pdev, "Operation Timed out\n");
        rc = -ETIME;
        goto out;
 
 out_recv:
        rc = chip->ops->recv(chip, (u8 *) buf, bufsiz);
        if (rc < 0)
-               dev_err(chip->dev,
+               dev_err(chip->pdev,
                        "tpm_transmit: tpm_recv: error %zd\n", rc);
 out:
        mutex_unlock(&chip->tpm_mutex);
@@ -398,9 +398,10 @@ out:
 #define TPM_DIGEST_SIZE 20
 #define TPM_RET_CODE_IDX 6
 
-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, void *cmd,
+                        int len, const char *desc)
 {
+       struct tpm_output_header *header;
        int err;
 
        len = tpm_transmit(chip, (u8 *) cmd, len);
@@ -409,9 +410,12 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
        else if (len < TPM_HEADER_SIZE)
                return -EFAULT;
 
-       err = be32_to_cpu(cmd->header.out.return_code);
+       header = cmd;
+
+       err = be32_to_cpu(header->return_code);
        if (err != 0 && desc)
-               dev_err(chip->dev, "A TPM error (%d) occurred %s\n", err, desc);
+               dev_err(chip->pdev, "A TPM error (%d) occurred %s\n", err,
+                       desc);
 
        return err;
 }
@@ -448,7 +452,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;
@@ -464,8 +468,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);
 
@@ -483,9 +487,10 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
 {
        struct tpm_cmd_t start_cmd;
        start_cmd.header.in = tpm_startup_header;
+
        start_cmd.params.startup_in.startup_type = startup_type;
-       return transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE,
-                           "attempting to start the TPM");
+       return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE,
+                               "attempting to start the TPM");
 }
 
 int tpm_get_timeouts(struct tpm_chip *chip)
@@ -500,12 +505,12 @@ int tpm_get_timeouts(struct tpm_chip *chip)
        tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
        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, NULL);
+       rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
 
        if (rc == TPM_ERR_INVALID_POSTINIT) {
                /* The TPM is not started, we are the first to talk to it.
                   Execute a startup command. */
-               dev_info(chip->dev, "Issuing TPM_STARTUP");
+               dev_info(chip->pdev, "Issuing TPM_STARTUP");
                if (tpm_startup(chip, TPM_ST_CLEAR))
                        return rc;
 
@@ -513,11 +518,11 @@ int tpm_get_timeouts(struct tpm_chip *chip)
                tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
                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,
+               rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
                                  NULL);
        }
        if (rc) {
-               dev_err(chip->dev,
+               dev_err(chip->pdev,
                        "A TPM error (%zd) occurred attempting to determine the timeouts\n",
                        rc);
                goto duration;
@@ -556,7 +561,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
 
        /* Report adjusted timeouts */
        if (chip->vendor.timeout_adjusted) {
-               dev_info(chip->dev,
+               dev_info(chip->pdev,
                         HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
                         old_timeout[0], new_timeout[0],
                         old_timeout[1], new_timeout[1],
@@ -575,8 +580,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;
 
@@ -603,7 +608,7 @@ 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->pdev, "Adjusting TPM timeout parameters.");
        }
        return 0;
 }
@@ -631,32 +636,11 @@ static int tpm_continue_selftest(struct tpm_chip *chip)
        struct tpm_cmd_t cmd;
 
        cmd.header.in = continue_selftest_header;
-       rc = transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE,
-                         "continue selftest");
+       rc = tpm_transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE,
+                             "continue selftest");
        return rc;
 }
 
-/*
- * tpm_chip_find_get - return tpm_chip for given chip number
- */
-static struct tpm_chip *tpm_chip_find_get(int chip_num)
-{
-       struct tpm_chip *pos, *chip = NULL;
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
-               if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num)
-                       continue;
-
-               if (try_module_get(pos->dev->driver->owner)) {
-                       chip = pos;
-                       break;
-               }
-       }
-       rcu_read_unlock();
-       return chip;
-}
-
 #define TPM_ORDINAL_PCRREAD cpu_to_be32(21)
 #define READ_PCR_RESULT_SIZE 30
 static struct tpm_input_header pcrread_header = {
@@ -672,8 +656,8 @@ int tpm_pcr_read_dev(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,
@@ -700,7 +684,10 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf)
        chip = tpm_chip_find_get(chip_num);
        if (chip == NULL)
                return -ENODEV;
-       rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf);
+       if (chip->flags & TPM_CHIP_FLAG_TPM2)
+               rc = tpm2_pcr_read(chip, pcr_idx, res_buf);
+       else
+               rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf);
        tpm_chip_put(chip);
        return rc;
 }
@@ -734,11 +721,17 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
        if (chip == NULL)
                return -ENODEV;
 
+       if (chip->flags & TPM_CHIP_FLAG_TPM2) {
+               rc = tpm2_pcr_extend(chip, pcr_idx, hash);
+               tpm_chip_put(chip);
+               return rc;
+       }
+
        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;
@@ -781,7 +774,7 @@ int tpm_do_selftest(struct tpm_chip *chip)
                 * around 300ms while the self test is ongoing, keep trying
                 * until the self test duration expires. */
                if (rc == -ETIME) {
-                       dev_info(chip->dev, HW_ERR "TPM command timed out during continue self test");
+                       dev_info(chip->pdev, HW_ERR "TPM command timed out during continue self test");
                        msleep(delay_msec);
                        continue;
                }
@@ -791,7 +784,7 @@ int tpm_do_selftest(struct tpm_chip *chip)
 
                rc = be32_to_cpu(cmd.header.out.return_code);
                if (rc == TPM_ERR_DISABLED || rc == TPM_ERR_DEACTIVATED) {
-                       dev_info(chip->dev,
+                       dev_info(chip->pdev,
                                 "TPM is disabled/deactivated (0x%X)\n", rc);
                        /* TPM is disabled and/or deactivated; driver can
                         * proceed and TPM does handle commands for
@@ -817,7 +810,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;
@@ -884,30 +877,6 @@ again:
 }
 EXPORT_SYMBOL_GPL(wait_for_tpm_stat);
 
-void tpm_remove_hardware(struct device *dev)
-{
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-
-       if (chip == NULL) {
-               dev_err(dev, "No device data found\n");
-               return;
-       }
-
-       spin_lock(&driver_lock);
-       list_del_rcu(&chip->list);
-       spin_unlock(&driver_lock);
-       synchronize_rcu();
-
-       tpm_dev_del_device(chip);
-       tpm_sysfs_del_device(chip);
-       tpm_remove_ppi(&dev->kobj);
-       tpm_bios_log_teardown(chip->bios_dir);
-
-       /* write it this way to be explicit (chip->dev == dev) */
-       put_device(chip->dev);
-}
-EXPORT_SYMBOL_GPL(tpm_remove_hardware);
-
 #define TPM_ORD_SAVESTATE cpu_to_be32(152)
 #define SAVESTATE_RESULT_SIZE 10
 
@@ -932,20 +901,25 @@ int tpm_pm_suspend(struct device *dev)
        if (chip == NULL)
                return -ENODEV;
 
+       if (chip->flags & TPM_CHIP_FLAG_TPM2) {
+               tpm2_shutdown(chip, TPM2_SU_STATE);
+               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");
+               rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
+                                     "extending dummy pcr before suspend");
        }
 
        /* now do the actual savestate */
        for (try = 0; try < TPM_RETRY; try++) {
                cmd.header.in = savestate_header;
-               rc = transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, NULL);
+               rc = tpm_transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, NULL);
 
                /*
                 * If the TPM indicates that it is too busy to respond to
@@ -963,10 +937,10 @@ int tpm_pm_suspend(struct device *dev)
        }
 
        if (rc)
-               dev_err(chip->dev,
+               dev_err(chip->pdev,
                        "Error (%d) sending savestate before suspend\n", rc);
        else if (try > 0)
-               dev_warn(chip->dev, "TPM savestate took %dms\n",
+               dev_warn(chip->pdev, "TPM savestate took %dms\n",
                         try * TPM_TIMEOUT_RETRY);
 
        return rc;
@@ -1018,11 +992,17 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
        if (chip == NULL)
                return -ENODEV;
 
+       if (chip->flags & TPM_CHIP_FLAG_TPM2) {
+               err = tpm2_get_random(chip, out, max);
+               tpm_chip_put(chip);
+               return err;
+       }
+
        do {
                tpm_cmd.header.in = tpm_getrandom_header;
                tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes);
 
-               err = transmit_cmd(chip, &tpm_cmd,
+               err = tpm_transmit_cmd(chip, &tpm_cmd,
                                   TPM_GETRANDOM_RESULT_SIZE + num_bytes,
                                   "attempting get random");
                if (err)
@@ -1041,103 +1021,34 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
 }
 EXPORT_SYMBOL_GPL(tpm_get_random);
 
-/* In case vendor provided release function, call it too.*/
-
-void tpm_dev_vendor_release(struct tpm_chip *chip)
+static int __init tpm_init(void)
 {
-       if (!chip)
-               return;
-
-       clear_bit(chip->dev_num, dev_mask);
-}
-EXPORT_SYMBOL_GPL(tpm_dev_vendor_release);
-
-
-/*
- * Once all references to platform device are down to 0,
- * release all allocated structures.
- */
-static void tpm_dev_release(struct device *dev)
-{
-       struct tpm_chip *chip = dev_get_drvdata(dev);
+       int rc;
 
-       if (!chip)
-               return;
+       tpm_class = class_create(THIS_MODULE, "tpm");
+       if (IS_ERR(tpm_class)) {
+               pr_err("couldn't create tpm class\n");
+               return PTR_ERR(tpm_class);
+       }
 
-       tpm_dev_vendor_release(chip);
+       rc = alloc_chrdev_region(&tpm_devt, 0, TPM_NUM_DEVICES, "tpm");
+       if (rc < 0) {
+               pr_err("tpm: failed to allocate char dev region\n");
+               class_destroy(tpm_class);
+               return rc;
+       }
 
-       chip->release(dev);
-       kfree(chip);
+       return 0;
 }
 
-/*
- * Called from tpm_<specific>.c probe function only for devices
- * the driver has determined it should claim.  Prior to calling
- * this function the specific probe function has called pci_enable_device
- * upon errant exit from this function specific probe function should call
- * pci_disable_device
- */
-struct tpm_chip *tpm_register_hardware(struct device *dev,
-                                      const struct tpm_class_ops *ops)
+static void __exit tpm_exit(void)
 {
-       struct tpm_chip *chip;
-
-       /* Driver specific per-device data */
-       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-
-       if (chip == NULL)
-               return NULL;
-
-       mutex_init(&chip->tpm_mutex);
-       INIT_LIST_HEAD(&chip->list);
-
-       chip->ops = ops;
-       chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES);
-
-       if (chip->dev_num >= TPM_NUM_DEVICES) {
-               dev_err(dev, "No available tpm device numbers\n");
-               goto out_free;
-       }
-
-       set_bit(chip->dev_num, dev_mask);
-
-       scnprintf(chip->devname, sizeof(chip->devname), "%s%d", "tpm",
-                 chip->dev_num);
-
-       chip->dev = get_device(dev);
-       chip->release = dev->release;
-       dev->release = tpm_dev_release;
-       dev_set_drvdata(dev, chip);
-
-       if (tpm_dev_add_device(chip))
-               goto put_device;
-
-       if (tpm_sysfs_add_device(chip))
-               goto del_misc;
-
-       if (tpm_add_ppi(&dev->kobj))
-               goto del_sysfs;
-
-       chip->bios_dir = tpm_bios_log_setup(chip->devname);
-
-       /* Make chip available */
-       spin_lock(&driver_lock);
-       list_add_rcu(&chip->list, &tpm_chip_list);
-       spin_unlock(&driver_lock);
-
-       return chip;
-
-del_sysfs:
-       tpm_sysfs_del_device(chip);
-del_misc:
-       tpm_dev_del_device(chip);
-put_device:
-       put_device(chip->dev);
-out_free:
-       kfree(chip);
-       return NULL;
+       class_destroy(tpm_class);
+       unregister_chrdev_region(tpm_devt, TPM_NUM_DEVICES);
 }
-EXPORT_SYMBOL_GPL(tpm_register_hardware);
+
+subsys_initcall(tpm_init);
+module_exit(tpm_exit);
 
 MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
 MODULE_DESCRIPTION("TPM Driver");