tpm_crb: request and relinquish locality 0
authorJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Fri, 24 Mar 2017 09:45:49 +0000 (11:45 +0200)
committerJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Mon, 3 Apr 2017 19:46:02 +0000 (22:46 +0300)
This commit adds support for requesting and relinquishing locality 0 in
tpm_crb for the course of command transmission.

In order to achieve this, two new callbacks are added to struct
tpm_class_ops:

- request_locality
- relinquish_locality

With CRB interface you first set either requestAccess or relinquish bit
from TPM_LOC_CTRL_x register and then wait for locAssigned and
tpmRegValidSts bits to be set in the TPM_LOC_STATE_x register.

The reason why were are doing this is to make sure that the driver
will work properly with Intel TXT that uses locality 2. There's no
explicit guarantee that it would relinquish this locality. In more
general sense this commit enables tpm_crb to be a well behaving
citizen in a multi locality environment.

Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Reviewed-by: Jerry Snitselaar <jsnitsel@redhat.com>
Tested-by: Jerry Snitselaar <jsnitsel@redhat.com>
drivers/char/tpm/tpm-chip.c
drivers/char/tpm/tpm-interface.c
drivers/char/tpm/tpm.h
drivers/char/tpm/tpm_crb.c
include/linux/tpm.h

index aade6995f31065017a1a500ff8ab2223096c9245..a321bd57f3e9b69d0b204ed61cb2070008ff87a9 100644 (file)
@@ -231,6 +231,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev,
                goto out;
        }
 
+       chip->locality = -1;
        return chip;
 
 out:
index 16abbf9cb53a3f4111d596d5c31ab9152ad25dd9..158c1db83f0516a28def0de92ccad928fd77233e 100644 (file)
@@ -389,6 +389,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
        ssize_t len = 0;
        u32 count, ordinal;
        unsigned long stop;
+       bool need_locality;
 
        if (!tpm_validate_command(chip, space, buf, bufsiz))
                return -EINVAL;
@@ -412,6 +413,16 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
        if (chip->dev.parent)
                pm_runtime_get_sync(chip->dev.parent);
 
+       /* Store the decision as chip->locality will be changed. */
+       need_locality = chip->locality == -1;
+
+       if (need_locality && chip->ops->request_locality)  {
+               rc = chip->ops->request_locality(chip, 0);
+               if (rc < 0)
+                       goto out_no_locality;
+               chip->locality = rc;
+       }
+
        rc = tpm2_prepare_space(chip, space, ordinal, buf);
        if (rc)
                goto out;
@@ -471,6 +482,11 @@ out_recv:
        rc = tpm2_commit_space(chip, space, ordinal, buf, &len);
 
 out:
+       if (need_locality && chip->ops->relinquish_locality) {
+               chip->ops->relinquish_locality(chip, chip->locality);
+               chip->locality = -1;
+       }
+out_no_locality:
        if (chip->dev.parent)
                pm_runtime_put_sync(chip->dev.parent);
 
index 5eacb3fd2ed26fd7c59b1067f44683526afa7b3f..4b4c8dee30965a44eca191212919db035c19d799 100644 (file)
@@ -228,6 +228,9 @@ struct tpm_chip {
        struct tpm_space work_space;
        u32 nr_commands;
        u32 *cc_attrs_tbl;
+
+       /* active locality */
+       int locality;
 };
 
 #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
index 9f316091215236270dc4680cb4f8d4bc4cbdd682..d91e47dc2d7945dabdf45c1d7d99e447329ede66 100644 (file)
@@ -34,6 +34,16 @@ enum crb_defaults {
        CRB_ACPI_START_INDEX = 1,
 };
 
+enum crb_loc_ctrl {
+       CRB_LOC_CTRL_REQUEST_ACCESS     = BIT(0),
+       CRB_LOC_CTRL_RELINQUISH         = BIT(1),
+};
+
+enum crb_loc_state {
+       CRB_LOC_STATE_LOC_ASSIGNED      = BIT(1),
+       CRB_LOC_STATE_TPM_REG_VALID_STS = BIT(7),
+};
+
 enum crb_ctrl_req {
        CRB_CTRL_REQ_CMD_READY  = BIT(0),
        CRB_CTRL_REQ_GO_IDLE    = BIT(1),
@@ -172,6 +182,35 @@ static int __maybe_unused crb_cmd_ready(struct device *dev,
        return 0;
 }
 
+static int crb_request_locality(struct tpm_chip *chip, int loc)
+{
+       struct crb_priv *priv = dev_get_drvdata(&chip->dev);
+       u32 value = CRB_LOC_STATE_LOC_ASSIGNED |
+               CRB_LOC_STATE_TPM_REG_VALID_STS;
+
+       if (!priv->regs_h)
+               return 0;
+
+       iowrite32(CRB_LOC_CTRL_REQUEST_ACCESS, &priv->regs_h->loc_ctrl);
+       if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, value, value,
+                                TPM2_TIMEOUT_C)) {
+               dev_warn(&chip->dev, "TPM_LOC_STATE_x.requestAccess timed out\n");
+               return -ETIME;
+       }
+
+       return 0;
+}
+
+static void crb_relinquish_locality(struct tpm_chip *chip, int loc)
+{
+       struct crb_priv *priv = dev_get_drvdata(&chip->dev);
+
+       if (!priv->regs_h)
+               return;
+
+       iowrite32(CRB_LOC_CTRL_RELINQUISH, &priv->regs_h->loc_ctrl);
+}
+
 static u8 crb_status(struct tpm_chip *chip)
 {
        struct crb_priv *priv = dev_get_drvdata(&chip->dev);
@@ -278,6 +317,8 @@ static const struct tpm_class_ops tpm_crb = {
        .send = crb_send,
        .cancel = crb_cancel,
        .req_canceled = crb_req_canceled,
+       .request_locality = crb_request_locality,
+       .relinquish_locality = crb_relinquish_locality,
        .req_complete_mask = CRB_DRV_STS_COMPLETE,
        .req_complete_val = CRB_DRV_STS_COMPLETE,
 };
index da158f06e0b2fec35f10e2faea57d806696e4d20..5a090f5ab3356f5f26bb21581d9a3df8ea488aa0 100644 (file)
@@ -48,7 +48,8 @@ struct tpm_class_ops {
        u8 (*status) (struct tpm_chip *chip);
        bool (*update_timeouts)(struct tpm_chip *chip,
                                unsigned long *timeout_cap);
-
+       int (*request_locality)(struct tpm_chip *chip, int loc);
+       void (*relinquish_locality)(struct tpm_chip *chip, int loc);
 };
 
 #if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE)