tpm: separate cmd_ready/go_idle from runtime_pm
authorTomas Winkler <tomas.winkler@intel.com>
Thu, 28 Jun 2018 15:13:33 +0000 (18:13 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 19 Sep 2018 20:43:38 +0000 (22:43 +0200)
commit 627448e85c766587f6fdde1ea3886d6615081c77 upstream.

Fix tpm ptt initialization error:
tpm tpm0: A TPM error (378) occurred get tpm pcr allocation.

We cannot use go_idle cmd_ready commands via runtime_pm handles
as with the introduction of localities this is no longer an optional
feature, while runtime pm can be not enabled.
Though cmd_ready/go_idle provides a power saving, it's also a part of
TPM2 protocol and should be called explicitly.
This patch exposes cmd_read/go_idle via tpm class ops and removes
runtime pm support as it is not used by any driver.

When calling from nested context always use both flags:
TPM_TRANSMIT_UNLOCKED and TPM_TRANSMIT_RAW. Both are needed to resolve
tpm spaces and locality request recursive calls to tpm_transmit().
TPM_TRANSMIT_RAW should never be used standalone as it will fail
on double locking. While TPM_TRANSMIT_UNLOCKED standalone should be
called from non-recursive locked contexts.

New wrappers are added tpm_cmd_ready() and tpm_go_idle() to
streamline tpm_try_transmit code.

tpm_crb no longer needs own power saving functions and can drop using
tpm_pm_suspend/resume.

This patch cannot be really separated from the locality fix.
Fixes: 888d867df441 (tpm: cmd_ready command can be issued only after granting locality)

Cc: stable@vger.kernel.org
Fixes: 888d867df441 (tpm: cmd_ready command can be issued only after granting locality)
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Tested-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/char/tpm/tpm-interface.c
drivers/char/tpm/tpm.h
drivers/char/tpm/tpm2-space.c
drivers/char/tpm/tpm_crb.c
include/linux/tpm.h

index 86b526b..a2070ab 100644 (file)
@@ -369,10 +369,13 @@ err_len:
        return -EINVAL;
 }
 
-static int tpm_request_locality(struct tpm_chip *chip)
+static int tpm_request_locality(struct tpm_chip *chip, unsigned int flags)
 {
        int rc;
 
+       if (flags & TPM_TRANSMIT_RAW)
+               return 0;
+
        if (!chip->ops->request_locality)
                return 0;
 
@@ -385,10 +388,13 @@ static int tpm_request_locality(struct tpm_chip *chip)
        return 0;
 }
 
-static void tpm_relinquish_locality(struct tpm_chip *chip)
+static void tpm_relinquish_locality(struct tpm_chip *chip, unsigned int flags)
 {
        int rc;
 
+       if (flags & TPM_TRANSMIT_RAW)
+               return;
+
        if (!chip->ops->relinquish_locality)
                return;
 
@@ -399,6 +405,28 @@ static void tpm_relinquish_locality(struct tpm_chip *chip)
        chip->locality = -1;
 }
 
+static int tpm_cmd_ready(struct tpm_chip *chip, unsigned int flags)
+{
+       if (flags & TPM_TRANSMIT_RAW)
+               return 0;
+
+       if (!chip->ops->cmd_ready)
+               return 0;
+
+       return chip->ops->cmd_ready(chip);
+}
+
+static int tpm_go_idle(struct tpm_chip *chip, unsigned int flags)
+{
+       if (flags & TPM_TRANSMIT_RAW)
+               return 0;
+
+       if (!chip->ops->go_idle)
+               return 0;
+
+       return chip->ops->go_idle(chip);
+}
+
 static ssize_t tpm_try_transmit(struct tpm_chip *chip,
                                struct tpm_space *space,
                                u8 *buf, size_t bufsiz,
@@ -449,14 +477,15 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip,
        /* Store the decision as chip->locality will be changed. */
        need_locality = chip->locality == -1;
 
-       if (!(flags & TPM_TRANSMIT_RAW) && need_locality) {
-               rc = tpm_request_locality(chip);
+       if (need_locality) {
+               rc = tpm_request_locality(chip, flags);
                if (rc < 0)
                        goto out_no_locality;
        }
 
-       if (chip->dev.parent)
-               pm_runtime_get_sync(chip->dev.parent);
+       rc = tpm_cmd_ready(chip, flags);
+       if (rc)
+               goto out;
 
        rc = tpm2_prepare_space(chip, space, ordinal, buf);
        if (rc)
@@ -516,13 +545,16 @@ out_recv:
        }
 
        rc = tpm2_commit_space(chip, space, ordinal, buf, &len);
+       if (rc)
+               dev_err(&chip->dev, "tpm2_commit_space: error %d\n", rc);
 
 out:
-       if (chip->dev.parent)
-               pm_runtime_put_sync(chip->dev.parent);
+       rc = tpm_go_idle(chip, flags);
+       if (rc)
+               goto out;
 
        if (need_locality)
-               tpm_relinquish_locality(chip);
+               tpm_relinquish_locality(chip, flags);
 
 out_no_locality:
        if (chip->ops->clk_enable != NULL)
index b83b30a..4bb9b4a 100644 (file)
@@ -511,9 +511,17 @@ extern const struct file_operations tpm_fops;
 extern const struct file_operations tpmrm_fops;
 extern struct idr dev_nums_idr;
 
+/**
+ * enum tpm_transmit_flags
+ *
+ * @TPM_TRANSMIT_UNLOCKED: used to lock sequence of tpm_transmit calls.
+ * @TPM_TRANSMIT_RAW: prevent recursive calls into setup steps
+ *                    (go idle, locality,..). Always use with UNLOCKED
+ *                    as it will fail on double locking.
+ */
 enum tpm_transmit_flags {
-       TPM_TRANSMIT_UNLOCKED   = BIT(0),
-       TPM_TRANSMIT_RAW        = BIT(1),
+       TPM_TRANSMIT_UNLOCKED = BIT(0),
+       TPM_TRANSMIT_RAW      = BIT(1),
 };
 
 ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
index d26ea75..dabb2ae 100644 (file)
@@ -39,7 +39,8 @@ static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space)
        for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
                if (space->session_tbl[i])
                        tpm2_flush_context_cmd(chip, space->session_tbl[i],
-                                              TPM_TRANSMIT_UNLOCKED);
+                                              TPM_TRANSMIT_UNLOCKED |
+                                              TPM_TRANSMIT_RAW);
        }
 }
 
@@ -84,7 +85,7 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
        tpm_buf_append(&tbuf, &buf[*offset], body_size);
 
        rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 4,
-                             TPM_TRANSMIT_UNLOCKED, NULL);
+                             TPM_TRANSMIT_UNLOCKED | TPM_TRANSMIT_RAW, NULL);
        if (rc < 0) {
                dev_warn(&chip->dev, "%s: failed with a system error %d\n",
                         __func__, rc);
@@ -133,7 +134,7 @@ static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
        tpm_buf_append_u32(&tbuf, handle);
 
        rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 0,
-                             TPM_TRANSMIT_UNLOCKED, NULL);
+                             TPM_TRANSMIT_UNLOCKED | TPM_TRANSMIT_RAW, NULL);
        if (rc < 0) {
                dev_warn(&chip->dev, "%s: failed with a system error %d\n",
                         __func__, rc);
@@ -170,7 +171,8 @@ static void tpm2_flush_space(struct tpm_chip *chip)
        for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++)
                if (space->context_tbl[i] && ~space->context_tbl[i])
                        tpm2_flush_context_cmd(chip, space->context_tbl[i],
-                                              TPM_TRANSMIT_UNLOCKED);
+                                              TPM_TRANSMIT_UNLOCKED |
+                                              TPM_TRANSMIT_RAW);
 
        tpm2_flush_sessions(chip, space);
 }
@@ -377,7 +379,8 @@ static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp,
 
        return 0;
 out_no_slots:
-       tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_UNLOCKED);
+       tpm2_flush_context_cmd(chip, phandle,
+                              TPM_TRANSMIT_UNLOCKED | TPM_TRANSMIT_RAW);
        dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__,
                 phandle);
        return -ENOMEM;
@@ -465,7 +468,8 @@ static int tpm2_save_space(struct tpm_chip *chip)
                        return rc;
 
                tpm2_flush_context_cmd(chip, space->context_tbl[i],
-                                      TPM_TRANSMIT_UNLOCKED);
+                                      TPM_TRANSMIT_UNLOCKED |
+                                      TPM_TRANSMIT_RAW);
                space->context_tbl[i] = ~0;
        }
 
index bb756ad..5c7ce5a 100644 (file)
@@ -137,7 +137,7 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
 }
 
 /**
- * crb_go_idle - request tpm crb device to go the idle state
+ * __crb_go_idle - request tpm crb device to go the idle state
  *
  * @dev:  crb device
  * @priv: crb private data
@@ -151,7 +151,7 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
  *
  * Return: 0 always
  */
-static int crb_go_idle(struct device *dev, struct crb_priv *priv)
+static int __crb_go_idle(struct device *dev, struct crb_priv *priv)
 {
        if ((priv->flags & CRB_FL_ACPI_START) ||
            (priv->flags & CRB_FL_CRB_SMC_START))
@@ -166,11 +166,20 @@ static int crb_go_idle(struct device *dev, struct crb_priv *priv)
                dev_warn(dev, "goIdle timed out\n");
                return -ETIME;
        }
+
        return 0;
 }
 
+static int crb_go_idle(struct tpm_chip *chip)
+{
+       struct device *dev = &chip->dev;
+       struct crb_priv *priv = dev_get_drvdata(dev);
+
+       return __crb_go_idle(dev, priv);
+}
+
 /**
- * crb_cmd_ready - request tpm crb device to enter ready state
+ * __crb_cmd_ready - request tpm crb device to enter ready state
  *
  * @dev:  crb device
  * @priv: crb private data
@@ -183,7 +192,7 @@ static int crb_go_idle(struct device *dev, struct crb_priv *priv)
  *
  * Return: 0 on success -ETIME on timeout;
  */
-static int crb_cmd_ready(struct device *dev, struct crb_priv *priv)
+static int __crb_cmd_ready(struct device *dev, struct crb_priv *priv)
 {
        if ((priv->flags & CRB_FL_ACPI_START) ||
            (priv->flags & CRB_FL_CRB_SMC_START))
@@ -201,6 +210,14 @@ static int crb_cmd_ready(struct device *dev, struct crb_priv *priv)
        return 0;
 }
 
+static int crb_cmd_ready(struct tpm_chip *chip)
+{
+       struct device *dev = &chip->dev;
+       struct crb_priv *priv = dev_get_drvdata(dev);
+
+       return __crb_cmd_ready(dev, priv);
+}
+
 static int __crb_request_locality(struct device *dev,
                                  struct crb_priv *priv, int loc)
 {
@@ -393,6 +410,8 @@ static const struct tpm_class_ops tpm_crb = {
        .send = crb_send,
        .cancel = crb_cancel,
        .req_canceled = crb_req_canceled,
+       .go_idle  = crb_go_idle,
+       .cmd_ready = crb_cmd_ready,
        .request_locality = crb_request_locality,
        .relinquish_locality = crb_relinquish_locality,
        .req_complete_mask = CRB_DRV_STS_COMPLETE,
@@ -508,7 +527,7 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
         * PTT HW bug w/a: wake up the device to access
         * possibly not retained registers.
         */
-       ret = crb_cmd_ready(dev, priv);
+       ret = __crb_cmd_ready(dev, priv);
        if (ret)
                return ret;
 
@@ -553,7 +572,7 @@ out:
        if (!ret)
                priv->cmd_size = cmd_size;
 
-       crb_go_idle(dev, priv);
+       __crb_go_idle(dev, priv);
 
        __crb_relinquish_locality(dev, priv, 0);
 
@@ -624,32 +643,7 @@ static int crb_acpi_add(struct acpi_device *device)
        chip->acpi_dev_handle = device->handle;
        chip->flags = TPM_CHIP_FLAG_TPM2;
 
-       rc = __crb_request_locality(dev, priv, 0);
-       if (rc)
-               return rc;
-
-       rc  = crb_cmd_ready(dev, priv);
-       if (rc)
-               goto out;
-
-       pm_runtime_get_noresume(dev);
-       pm_runtime_set_active(dev);
-       pm_runtime_enable(dev);
-
-       rc = tpm_chip_register(chip);
-       if (rc) {
-               crb_go_idle(dev, priv);
-               pm_runtime_put_noidle(dev);
-               pm_runtime_disable(dev);
-               goto out;
-       }
-
-       pm_runtime_put_sync(dev);
-
-out:
-       __crb_relinquish_locality(dev, priv, 0);
-
-       return rc;
+       return tpm_chip_register(chip);
 }
 
 static int crb_acpi_remove(struct acpi_device *device)
@@ -659,52 +653,11 @@ static int crb_acpi_remove(struct acpi_device *device)
 
        tpm_chip_unregister(chip);
 
-       pm_runtime_disable(dev);
-
        return 0;
 }
 
-static int __maybe_unused crb_pm_runtime_suspend(struct device *dev)
-{
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       struct crb_priv *priv = dev_get_drvdata(&chip->dev);
-
-       return crb_go_idle(dev, priv);
-}
-
-static int __maybe_unused crb_pm_runtime_resume(struct device *dev)
-{
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       struct crb_priv *priv = dev_get_drvdata(&chip->dev);
-
-       return crb_cmd_ready(dev, priv);
-}
-
-static int __maybe_unused crb_pm_suspend(struct device *dev)
-{
-       int ret;
-
-       ret = tpm_pm_suspend(dev);
-       if (ret)
-               return ret;
-
-       return crb_pm_runtime_suspend(dev);
-}
-
-static int __maybe_unused crb_pm_resume(struct device *dev)
-{
-       int ret;
-
-       ret = crb_pm_runtime_resume(dev);
-       if (ret)
-               return ret;
-
-       return tpm_pm_resume(dev);
-}
-
 static const struct dev_pm_ops crb_pm = {
-       SET_SYSTEM_SLEEP_PM_OPS(crb_pm_suspend, crb_pm_resume)
-       SET_RUNTIME_PM_OPS(crb_pm_runtime_suspend, crb_pm_runtime_resume, NULL)
+       SET_SYSTEM_SLEEP_PM_OPS(tpm_pm_suspend, tpm_pm_resume)
 };
 
 static const struct acpi_device_id crb_device_ids[] = {
index 2a6c3d9..7f7b29f 100644 (file)
@@ -48,6 +48,8 @@ struct tpm_class_ops {
        u8 (*status) (struct tpm_chip *chip);
        bool (*update_timeouts)(struct tpm_chip *chip,
                                unsigned long *timeout_cap);
+       int (*go_idle)(struct tpm_chip *chip);
+       int (*cmd_ready)(struct tpm_chip *chip);
        int (*request_locality)(struct tpm_chip *chip, int loc);
        int (*relinquish_locality)(struct tpm_chip *chip, int loc);
        void (*clk_enable)(struct tpm_chip *chip, bool value);