2014-10-07 17:02:04

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v2 0/7] TPM 2.0 support

This patch set enables TPM2 protocol and provides drivers for FIFO and
CRB interfaces.

Major changes since v1:

- Improved struct tpm_chip life-cycle by taking advantage of devres
API.
- Refined sysfs attributes as simple key-values thereby not repeating
mistakes in TPM1 sysfs attributes.
- Documented functions in tpm-chip.c and tpm2-cmd.c.
- Documented sysfs attributes.

Jarkko Sakkinen (6):
tpm: merge duplicate transmit_cmd() functions
tpm: two-phase chip management functions
tpm: clean up tpm_tis driver life-cycle
tpm: TPM 2.0 commands
tpm: TPM 2.0 sysfs attributes
tpm: TPM 2.0 CRB Interface

Will Arthur (1):
tpm: TPM 2.0 FIFO Interface

Documentation/ABI/stable/sysfs-class-tpm2 | 69 ++++
drivers/char/tpm/Kconfig | 9 +
drivers/char/tpm/Makefile | 3 +-
drivers/char/tpm/tpm-chip.c | 184 ++++++++++
drivers/char/tpm/tpm-dev.c | 4 +-
drivers/char/tpm/tpm-interface.c | 201 ++++-------
drivers/char/tpm/tpm-sysfs.c | 23 +-
drivers/char/tpm/tpm.h | 103 +++++-
drivers/char/tpm/tpm2-cmd.c | 543 ++++++++++++++++++++++++++++++
drivers/char/tpm/tpm2-sysfs.c | 314 +++++++++++++++++
drivers/char/tpm/tpm_crb.c | 329 ++++++++++++++++++
drivers/char/tpm/tpm_i2c_atmel.c | 5 -
drivers/char/tpm/tpm_i2c_infineon.c | 10 -
drivers/char/tpm/tpm_i2c_nuvoton.c | 5 -
drivers/char/tpm/tpm_infineon.c | 1 -
drivers/char/tpm/tpm_tis.c | 142 +++++---
16 files changed, 1696 insertions(+), 249 deletions(-)
create mode 100644 Documentation/ABI/stable/sysfs-class-tpm2
create mode 100644 drivers/char/tpm/tpm-chip.c
create mode 100644 drivers/char/tpm/tpm2-cmd.c
create mode 100644 drivers/char/tpm/tpm2-sysfs.c
create mode 100644 drivers/char/tpm/tpm_crb.c

--
2.1.0


2014-10-07 17:01:46

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v2 2/7] tpm: two-phase chip management functions

Added tpm_chip_alloc() and tpm_chip_register() where tpm_chip_alloc()
reserves memory resources and tpm_chip_register() initializes the
device driver. This way it is possible to alter struct tpm_chip
attributes before passing it to tpm_chip_register().

The framework takes care of freeing struct tpm_chip by using devres
API. The broken release callback has been wiped.

tpm_register_hardware() and tpm_remove_hardware are still available
as a wrapper in order to make this change less intrusive. However,
device drivers should eventually move to the tpm_chip_* functions.

Signed-off-by: Jarkko Sakkinen <[email protected]>
---
drivers/char/tpm/Makefile | 2 +-
drivers/char/tpm/tpm-chip.c | 178 ++++++++++++++++++++++++++++++++++++
drivers/char/tpm/tpm-interface.c | 124 ++-----------------------
drivers/char/tpm/tpm.h | 8 +-
drivers/char/tpm/tpm_i2c_atmel.c | 5 -
drivers/char/tpm/tpm_i2c_infineon.c | 10 --
drivers/char/tpm/tpm_i2c_nuvoton.c | 5 -
drivers/char/tpm/tpm_infineon.c | 1 -
drivers/char/tpm/tpm_tis.c | 5 +-
9 files changed, 194 insertions(+), 144 deletions(-)
create mode 100644 drivers/char/tpm/tpm-chip.c

diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 4d85dd6..837da04 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -2,7 +2,7 @@
# Makefile for the kernel tpm device drivers.
#
obj-$(CONFIG_TCG_TPM) += tpm.o
-tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o
+tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o
tpm-$(CONFIG_ACPI) += tpm_ppi.o

ifdef CONFIG_ACPI
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
new file mode 100644
index 0000000..6cc4cee
--- /dev/null
+++ b/drivers/char/tpm/tpm-chip.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Authors:
+ * Jarkko Sakkinen <[email protected]>
+ * Leendert van Doorn <[email protected]>
+ * Dave Safford <[email protected]>
+ * Reiner Sailer <[email protected]>
+ * Kylene Hall <[email protected]>
+ *
+ * Maintained by: <[email protected]>
+ *
+ * TPM chip management routines.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ */
+
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/freezer.h>
+#include "tpm.h"
+#include "tpm_eventlog.h"
+
+static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);
+static LIST_HEAD(tpm_chip_list);
+static DEFINE_SPINLOCK(driver_lock);
+
+/*
+ * tpm_chip_find_get - return tpm_chip for a given chip number
+ * @chip_num the device number for the chip
+ */
+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;
+}
+
+/**
+ * tpm_devm_chip_remove() - free chip memory and device number
+ * @data: points to struct tpm_chip instance
+ *
+ * This is used internally by tpm_chip_alloc() and called by devres
+ * when the device is released. This funcion does the opposite of
+ * tpm_chip_alloc() freeing memory and the device number.
+ */
+static void tpm_devm_chip_remove(void *data)
+{
+ struct tpm_chip *chip = (struct tpm_chip *) data;
+ dev_dbg(chip->dev, "%s\n", __func__);
+ clear_bit(chip->dev_num, dev_mask);
+ kfree(chip);
+}
+
+/**
+ * tpm_chip_alloc() - allocate a new struct tpm_chip instance
+ * @dev: device to which the chip is associated
+ * @ops: struct tpm_class_ops instance
+ *
+ * Allocates a new struct tpm_chip instance and assigns a free
+ * device number for it. Caller does not have to worry about
+ * freeing the allocated resources. When the devices is removed
+ * devres calls tpm_devm_chip_remove() to do the job.
+ */
+struct tpm_chip *tpm_chip_alloc(struct device *dev,
+ const struct tpm_class_ops *ops)
+{
+ struct tpm_chip *chip;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ 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");
+ kfree(chip);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ set_bit(chip->dev_num, dev_mask);
+
+ scnprintf(chip->devname, sizeof(chip->devname), "%s%d", "tpm",
+ chip->dev_num);
+
+ chip->dev = dev;
+ devm_add_action(dev, tpm_devm_chip_remove, chip);
+ dev_set_drvdata(dev, chip);
+
+ return chip;
+}
+EXPORT_SYMBOL_GPL(tpm_chip_alloc);
+
+/*
+ * tpm_chip_register() - create a misc driver for the TPM chip
+ * @chip: TPM chip to use.
+ *
+ * Creates a misc driver for the TPM chip and adds sysfs interfaces for
+ * the device, PPI and TCPA. As the last step this function adds the
+ * chip to the list of TPM chips available for use.
+ */
+int tpm_chip_register(struct tpm_chip *chip)
+{
+ int rc;
+
+ rc = tpm_dev_add_device(chip);
+ if (rc)
+ return rc;
+
+ rc = tpm_sysfs_add_device(chip);
+ if (rc)
+ goto del_misc;
+
+ rc = tpm_add_ppi(&chip->dev->kobj);
+ if (rc)
+ goto del_sysfs;
+
+ chip->bios_dir = tpm_bios_log_setup(chip->devname);
+
+ /* Make the chip available. */
+ spin_lock(&driver_lock);
+ list_add_rcu(&chip->list, &tpm_chip_list);
+ spin_unlock(&driver_lock);
+
+ return 0;
+del_sysfs:
+ tpm_sysfs_del_device(chip);
+del_misc:
+ tpm_dev_del_device(chip);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_chip_register);
+
+/*
+ * tpm_chip_unregister() - release the TPM driver
+ * @chip: TPM chip to use.
+ *
+ * Takes the chip first away from the list of available TPM chips and then
+ * cleans up all the resources reserved by tpm_chip_register().
+ */
+void tpm_chip_unregister(struct tpm_chip *chip)
+{
+ dev_dbg(chip->dev, "%s\n", __func__);
+
+ 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(&chip->dev->kobj);
+ tpm_bios_log_teardown(chip->bios_dir);
+}
+EXPORT_SYMBOL_GPL(tpm_chip_unregister);
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index fedb4d5..1ce3ad3 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2004 IBM Corporation
+ * Copyright (C) 2014 Intel Corporation
*
* Authors:
* Leendert van Doorn <[email protected]>
@@ -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
@@ -639,27 +636,6 @@ static int tpm_continue_selftest(struct tpm_chip *chip)
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 = {
@@ -896,18 +872,7 @@ void tpm_remove_hardware(struct device *dev)
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);
+ tpm_chip_unregister(chip);
}
EXPORT_SYMBOL_GPL(tpm_remove_hardware);

@@ -1044,35 +1009,6 @@ 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)
-{
- 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);
-
- if (!chip)
- return;
-
- tpm_dev_vendor_release(chip);
-
- chip->release(dev);
- kfree(chip);
-}
-
/*
* Called from tpm_<specific>.c probe function only for devices
* the driver has determined it should claim. Prior to calling
@@ -1084,61 +1020,17 @@ struct tpm_chip *tpm_register_hardware(struct device *dev,
const struct tpm_class_ops *ops)
{
struct tpm_chip *chip;
+ int rc;

- /* Driver specific per-device data */
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-
- if (chip == NULL)
+ chip = tpm_chip_alloc(dev, ops);
+ if (IS_ERR(chip))
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);
+ rc = tpm_chip_register(chip);
+ if (rc)
+ return NULL;

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;
}
EXPORT_SYMBOL_GPL(tpm_register_hardware);

diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 12326e1..5eb89897 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -110,7 +110,6 @@ struct tpm_chip {
struct dentry **bios_dir;

struct list_head list;
- void (*release) (struct device *);
};

#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor)
@@ -322,13 +321,18 @@ 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_class_ops *ops);
-extern void tpm_dev_vendor_release(struct tpm_chip *);
extern void tpm_remove_hardware(struct device *);
extern int tpm_pm_suspend(struct device *);
extern int tpm_pm_resume(struct device *);
extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
wait_queue_head_t *, bool);

+struct tpm_chip *tpm_chip_find_get(int chip_num);
+extern struct tpm_chip *tpm_chip_alloc(struct device *dev,
+ const struct tpm_class_ops *ops);
+extern int tpm_chip_register(struct tpm_chip *chip);
+extern void tpm_chip_unregister(struct tpm_chip *chip);
+
int tpm_dev_add_device(struct tpm_chip *chip);
void tpm_dev_del_device(struct tpm_chip *chip);
int tpm_sysfs_add_device(struct tpm_chip *chip);
diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c
index 7727292..1b52045 100644
--- a/drivers/char/tpm/tpm_i2c_atmel.c
+++ b/drivers/char/tpm/tpm_i2c_atmel.c
@@ -192,7 +192,6 @@ static int i2c_atmel_probe(struct i2c_client *client,
return 0;

out_err:
- tpm_dev_vendor_release(chip);
tpm_remove_hardware(chip->dev);
return rc;
}
@@ -200,12 +199,8 @@ out_err:
static int i2c_atmel_remove(struct i2c_client *client)
{
struct device *dev = &(client->dev);
- struct tpm_chip *chip = dev_get_drvdata(dev);

- if (chip)
- tpm_dev_vendor_release(chip);
tpm_remove_hardware(dev);
- kfree(chip);
return 0;
}

diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c
index 472af4b..9d9834d 100644
--- a/drivers/char/tpm/tpm_i2c_infineon.c
+++ b/drivers/char/tpm/tpm_i2c_infineon.c
@@ -634,15 +634,10 @@ out_release:
release_locality(chip, chip->vendor.locality, 1);

out_vendor:
- /* close file handles */
- tpm_dev_vendor_release(chip);
-
/* remove hardware */
tpm_remove_hardware(chip->dev);

/* reset these pointers, otherwise we oops */
- chip->dev->release = NULL;
- chip->release = NULL;
tpm_dev.client = NULL;
out_err:
return rc;
@@ -714,15 +709,10 @@ static int tpm_tis_i2c_remove(struct i2c_client *client)
struct tpm_chip *chip = tpm_dev.chip;
release_locality(chip, chip->vendor.locality, 1);

- /* close file handles */
- tpm_dev_vendor_release(chip);
-
/* remove hardware */
tpm_remove_hardware(chip->dev);

/* reset these pointers, otherwise we oops */
- chip->dev->release = NULL;
- chip->release = NULL;
tpm_dev.client = NULL;

return 0;
diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c
index 7b158ef..3a2caec 100644
--- a/drivers/char/tpm/tpm_i2c_nuvoton.c
+++ b/drivers/char/tpm/tpm_i2c_nuvoton.c
@@ -615,7 +615,6 @@ static int i2c_nuvoton_probe(struct i2c_client *client,
return 0;

out_err:
- tpm_dev_vendor_release(chip);
tpm_remove_hardware(chip->dev);
return rc;
}
@@ -623,12 +622,8 @@ out_err:
static int i2c_nuvoton_remove(struct i2c_client *client)
{
struct device *dev = &(client->dev);
- struct tpm_chip *chip = dev_get_drvdata(dev);

- if (chip)
- tpm_dev_vendor_release(chip);
tpm_remove_hardware(dev);
- kfree(chip);
return 0;
}

diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c
index dc0a255..0a72840 100644
--- a/drivers/char/tpm/tpm_infineon.c
+++ b/drivers/char/tpm/tpm_infineon.c
@@ -581,7 +581,6 @@ static void tpm_inf_pnp_remove(struct pnp_dev *dev)
iounmap(tpm_dev.mem_base);
release_mem_region(tpm_dev.map_base, tpm_dev.map_size);
}
- tpm_dev_vendor_release(chip);
tpm_remove_hardware(chip->dev);
}
}
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 2c46734..a2780df 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -811,10 +811,7 @@ MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl);
static void tpm_tis_pnp_remove(struct pnp_dev *dev)
{
struct tpm_chip *chip = pnp_get_drvdata(dev);
-
- tpm_dev_vendor_release(chip);
-
- kfree(chip);
+ tpm_remove_hardware(chip->dev);
}


--
2.1.0

2014-10-07 17:02:09

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v2 1/7] tpm: merge duplicate transmit_cmd() functions

Merged transmit_cmd() functions in tpm-interface.c and tpm-sysfs.c.
Added "tpm_" prefix for consistency sake. Changed cmd parameter as
opaque. This enables to use separate command structures for TPM1
and TPM2 commands instead of putting everything to struct tpm_cmd_t.

Signed-off-by: Jarkko Sakkinen <[email protected]>
---
drivers/char/tpm/tpm-dev.c | 4 +--
drivers/char/tpm/tpm-interface.c | 53 +++++++++++++++++++++-------------------
drivers/char/tpm/tpm-sysfs.c | 23 ++---------------
drivers/char/tpm/tpm.h | 5 ++--
4 files changed, 34 insertions(+), 51 deletions(-)

diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
index d9b774e..bd79d33 100644
--- a/drivers/char/tpm/tpm-dev.c
+++ b/drivers/char/tpm/tpm-dev.c
@@ -140,8 +140,8 @@ static ssize_t tpm_write(struct file *file, const char __user *buf,
}

/* atomic tpm command send and result receive */
- out_size = tpm_transmit(priv->chip, priv->data_buffer,
- sizeof(priv->data_buffer));
+ out_size = tpm_transmit_cmd(priv->chip, priv->data_buffer,
+ sizeof(priv->data_buffer), NULL);
if (out_size < 0) {
mutex_unlock(&priv->buffer_mutex);
return out_size;
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 6af1700..fedb4d5 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -331,8 +331,8 @@ EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
/*
* Internal kernel interface to transmit TPM commands
*/
-ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
- size_t bufsiz)
+static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
+ size_t bufsiz)
{
ssize_t rc;
u32 count, ordinal;
@@ -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,7 +410,9 @@ 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 = (struct tpm_output_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);

@@ -448,7 +451,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 +467,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);

@@ -484,8 +487,8 @@ 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,7 +503,7 @@ 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.
@@ -513,7 +516,7 @@ 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) {
@@ -575,8 +578,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;

@@ -631,8 +634,8 @@ 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;
}

@@ -672,8 +675,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,
@@ -737,8 +740,8 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
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;
@@ -817,7 +820,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;
@@ -938,14 +941,14 @@ int tpm_pm_suspend(struct device *dev)
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
@@ -1022,7 +1025,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
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)
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index 01730a2..8ecb052 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -20,25 +20,6 @@
#include <linux/device.h>
#include "tpm.h"

-/* XXX for now this helper is duplicated in tpm-interface.c */
-static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
- int len, const char *desc)
-{
- int err;
-
- len = tpm_transmit(chip, (u8 *) cmd, len);
- if (len < 0)
- return len;
- else if (len < TPM_HEADER_SIZE)
- return -EFAULT;
-
- err = be32_to_cpu(cmd->header.out.return_code);
- if (err != 0 && desc)
- dev_err(chip->dev, "A TPM error (%d) occurred %s\n", err, desc);
-
- return err;
-}
-
#define READ_PUBEK_RESULT_SIZE 314
#define TPM_ORD_READPUBEK cpu_to_be32(124)
static struct tpm_input_header tpm_readpubek_header = {
@@ -58,8 +39,8 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
struct tpm_chip *chip = dev_get_drvdata(dev);

tpm_cmd.header.in = tpm_readpubek_header;
- err = transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
- "attempting to read the PUBEK");
+ err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
+ "attempting to read the PUBEK");
if (err)
goto out;

diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index e4d0888..12326e1 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -314,9 +314,8 @@ struct tpm_cmd_t {
} __packed;

ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *);
-
-ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
- size_t bufsiz);
+ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd, int len,
+ const char *desc);
extern int tpm_get_timeouts(struct tpm_chip *);
extern void tpm_gen_interrupt(struct tpm_chip *);
extern int tpm_do_selftest(struct tpm_chip *);
--
2.1.0

2014-10-07 17:02:17

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v2 3/7] tpm: clean up tpm_tis driver life-cycle

Updated tpm_tis to properly use tpm-chip API instead of using ad hoc
methods. tpm_chip_unregister() is called on remove event when PNP driver
is used and on module removal when platform driver is used.

Signed-off-by: Jarkko Sakkinen <[email protected]>
---
drivers/char/tpm/tpm_tis.c | 64 ++++++++++++++++++++--------------------------
1 file changed, 28 insertions(+), 36 deletions(-)

diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index a2780df..df04ce6 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -75,9 +75,6 @@ enum tis_defaults {
#define TPM_DID_VID(l) (0x0F00 | ((l) << 12))
#define TPM_RID(l) (0x0F04 | ((l) << 12))

-static LIST_HEAD(tis_chips);
-static DEFINE_MUTEX(tis_lock);
-
#if defined(CONFIG_PNP) && defined(CONFIG_ACPI)
static int is_itpm(struct pnp_dev *dev)
{
@@ -535,14 +532,17 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
int rc, i, irq_s, irq_e, probe;
struct tpm_chip *chip;

- if (!(chip = tpm_register_hardware(dev, &tpm_tis)))
+ chip = tpm_chip_alloc(dev, &tpm_tis);
+ if (!chip)
return -ENODEV;

- chip->vendor.iobase = ioremap(start, len);
- if (!chip->vendor.iobase) {
- rc = -EIO;
- goto out_err;
- }
+ chip->vendor.iobase = devm_ioremap(dev, start, len);
+ if (!chip->vendor.iobase)
+ return -EIO;
+
+ rc = tpm_chip_register(chip);
+ if (rc)
+ return -ENODEV;

/* Default timeouts */
chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
@@ -720,16 +720,10 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
}

INIT_LIST_HEAD(&chip->vendor.list);
- mutex_lock(&tis_lock);
- list_add(&chip->vendor.list, &tis_chips);
- mutex_unlock(&tis_lock);
-

return 0;
out_err:
- if (chip->vendor.iobase)
- iounmap(chip->vendor.iobase);
- tpm_remove_hardware(chip->dev);
+ tpm_chip_unregister(chip);
return rc;
}

@@ -808,13 +802,27 @@ static struct pnp_device_id tpm_pnp_tbl[] = {
};
MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl);

+static void tpm_tis_chip_remove(struct tpm_chip *chip)
+{
+ iowrite32(~TPM_GLOBAL_INT_ENABLE &
+ ioread32(chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.
+ locality)),
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+ release_locality(chip, chip->vendor.locality, 1);
+ if (chip->vendor.irq)
+ free_irq(chip->vendor.irq, chip);
+
+ tpm_chip_unregister(chip);
+}
+
static void tpm_tis_pnp_remove(struct pnp_dev *dev)
{
struct tpm_chip *chip = pnp_get_drvdata(dev);
- tpm_remove_hardware(chip->dev);
+ tpm_tis_chip_remove(chip);
}

-
static struct pnp_driver tis_pnp_driver = {
.name = "tpm_tis",
.id_table = tpm_pnp_tbl,
@@ -873,31 +881,15 @@ err_dev:

static void __exit cleanup_tis(void)
{
- struct tpm_vendor_specific *i, *j;
struct tpm_chip *chip;
- mutex_lock(&tis_lock);
- list_for_each_entry_safe(i, j, &tis_chips, list) {
- chip = to_tpm_chip(i);
- tpm_remove_hardware(chip->dev);
- iowrite32(~TPM_GLOBAL_INT_ENABLE &
- ioread32(chip->vendor.iobase +
- TPM_INT_ENABLE(chip->vendor.
- locality)),
- chip->vendor.iobase +
- TPM_INT_ENABLE(chip->vendor.locality));
- release_locality(chip, chip->vendor.locality, 1);
- if (chip->vendor.irq)
- free_irq(chip->vendor.irq, chip);
- iounmap(i->iobase);
- list_del(&i->list);
- }
- mutex_unlock(&tis_lock);
#ifdef CONFIG_PNP
if (!force) {
pnp_unregister_driver(&tis_pnp_driver);
return;
}
#endif
+ chip = dev_get_drvdata(&pdev->dev);
+ tpm_tis_chip_remove(chip);
platform_device_unregister(pdev);
platform_driver_unregister(&tis_drv);
}
--
2.1.0

2014-10-07 17:02:41

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v2 5/7] tpm: TPM 2.0 sysfs attributes

Implemented sysfs attributes for TPM2 devices.

Documentation/ABI/stable/sysfs-class/tpm2 contains descriptions
of these attributes.

Signed-off-by: Jarkko Sakkinen <[email protected]>
---
Documentation/ABI/stable/sysfs-class-tpm2 | 69 +++++++
drivers/char/tpm/Makefile | 2 +-
drivers/char/tpm/tpm-chip.c | 10 +-
drivers/char/tpm/tpm.h | 24 +++
drivers/char/tpm/tpm2-sysfs.c | 314 ++++++++++++++++++++++++++++++
5 files changed, 416 insertions(+), 3 deletions(-)
create mode 100644 Documentation/ABI/stable/sysfs-class-tpm2
create mode 100644 drivers/char/tpm/tpm2-sysfs.c

diff --git a/Documentation/ABI/stable/sysfs-class-tpm2 b/Documentation/ABI/stable/sysfs-class-tpm2
new file mode 100644
index 0000000..e9d4b6a
--- /dev/null
+++ b/Documentation/ABI/stable/sysfs-class-tpm2
@@ -0,0 +1,69 @@
+What: /sys/class/misc/tpmX/device/
+Date: October 2014
+KernelVersion: 3.19
+Contact: [email protected]
+Description: The device/ directory under a specific TPM instance exposes
+ the properties of that TPM chip.
+
+What: /sys/class/misc/tpmX/device/version
+Date: October 2014
+KernelVersion: 3.19
+Contact: [email protected]
+Description: The "version" property prints the protocol version number
+ in the major.minor format.
+
+What: /sys/class/misc/tpmX/device/enabled_sh
+Date: October 2014
+KernelVersion: 3.19
+Contact: [email protected]
+Description: The "enabled_sh" property prints a '1' if the Storage Hierarchy
+ is enabled.
+
+What: /sys/class/misc/tpmX/device/enabled_eh
+Date: October 2014
+KernelVersion: 3.19
+Contact: [email protected]
+Description: The "enabled_eh" property prints a '1' if the Endorsement Hierarchy
+ is enabled.
+
+What: /sys/class/misc/tpmX/device/owned_sh
+Date: October 2014
+KernelVersion: 3.19
+Contact: [email protected]
+Description: The "owned_sh" property prints a '1' if the ownership of the
+ Storage Hierarchy has been taken.
+
+What: /sys/class/misc/tpmX/device/owned_eh
+Date: October 2014
+KernelVersion: 3.19
+Contact: [email protected]
+Description: The "owned_sh" property prints a '1' if the ownership of the
+ Endrosoment Hierarchy has been taken.
+
+What: /sys/class/misc/tpmX/device/manufacturer
+Date: October 2014
+KernelVersion: 3.19
+Contact: [email protected]
+Description: The "manufacturer" property prints the vendor ID of the TPM
+ manufacturer.
+
+What: /sys/class/misc/tpmX/device/firmware
+Date: October 2014
+KernelVersion: 3.19
+Contact: [email protected]
+Description: The property prints the vendor-specific value indicating the
+ version of the firmware.
+
+What: /sys/class/misc/tpmX/device/pcr/sha1/X
+Date: October 2014
+KernelVersion: 3.19
+Contact: [email protected]
+Description: These files print PCR values for the SHA-1 bank.
+
+What: /sys/class/misc/tpmX/device/cancel
+Date: October 2014
+KernelVersion: 3.19
+Contact: [email protected]
+Description: The "cancel" property allows you to cancel the currently
+ pending TPM command. Writing any value to cancel will call the
+ TPM chip specific cancel operation.
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index ae56af9..d3cf905 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -2,7 +2,7 @@
# Makefile for the kernel tpm device drivers.
#
obj-$(CONFIG_TCG_TPM) += tpm.o
-tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o
+tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o tpm2-sysfs.o
tpm-$(CONFIG_ACPI) += tpm_ppi.o

ifdef CONFIG_ACPI
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 6cc4cee..5c50fd7 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -130,7 +130,10 @@ int tpm_chip_register(struct tpm_chip *chip)
if (rc)
return rc;

- rc = tpm_sysfs_add_device(chip);
+ if (chip->flags & TPM_CHIP_FLAG_TPM2)
+ rc = tpm2_sysfs_add_device(chip->dev);
+ else
+ rc = tpm_sysfs_add_device(chip);
if (rc)
goto del_misc;

@@ -171,7 +174,10 @@ void tpm_chip_unregister(struct tpm_chip *chip)
synchronize_rcu();

tpm_dev_del_device(chip);
- tpm_sysfs_del_device(chip);
+ if (chip->flags & TPM_CHIP_FLAG_TPM2)
+ tpm2_sysfs_del_device(chip->dev);
+ else
+ tpm_sysfs_del_device(chip);
tpm_remove_ppi(&chip->dev->kobj);
tpm_bios_log_teardown(chip->bios_dir);
}
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index d141639..4678cdf 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -107,6 +107,24 @@ enum tpm2_capabilities {
TPM2_CAP_TPM_PROPERTIES = 6,
};

+enum tpm2_tpm_properties {
+ TPM2_PT_MANUFACTURER = 0x00000105,
+ TPM2_PT_FIRMWARE_VERSION_1 = 0x00000111,
+ TPM2_PT_FIRMWARE_VERSION_2 = 0x00000111,
+ TPM2_PT_PERMANENT = 0x00000200,
+ TPM2_PT_STARTUP_CLEAR = 0x00000201,
+};
+
+enum tpm2_pt_startup_clear {
+ TPM2_PT_SC_SH_ENABLE = BIT(1),
+ TPM2_PT_SC_EH_ENABLE = BIT(2),
+};
+
+enum tpm2_pt_permanent {
+ TPM2_PT_PM_OWNER_AUTH_SET = BIT(0),
+ TPM2_PT_PM_ENDORSEMENT_AUTH_SET = BIT(1),
+};
+
enum tpm2_startup_types {
TPM2_SU_CLEAR = 0x0000,
TPM2_SU_STATE = 0x0001,
@@ -165,6 +183,9 @@ struct tpm_chip {

struct dentry **bios_dir;

+ struct kobject *pcrs_kobj;
+ void *sha1_bank;
+
struct list_head list;
};

@@ -419,3 +440,6 @@ extern ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
u32* value, const char *desc);
extern unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *, u32);
extern int tpm2_do_selftest(struct tpm_chip *chip);
+
+int tpm2_sysfs_add_device(struct device *dev);
+void tpm2_sysfs_del_device(struct device *dev);
diff --git a/drivers/char/tpm/tpm2-sysfs.c b/drivers/char/tpm/tpm2-sysfs.c
new file mode 100644
index 0000000..e6e603e
--- /dev/null
+++ b/drivers/char/tpm/tpm2-sysfs.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ * Copyright (C) 2004 IBM Corporation
+ * Copyright (C) 2013 Obsidian Research Corp
+ *
+ * Authors:
+ * Jarkko Sakkinen <[email protected]>
+ *
+ * sysfs filesystem inspection interface to the TPM
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ */
+#include <linux/device.h>
+#include <linux/slab.h>
+#include "tpm.h"
+
+static ssize_t enabled_sh_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+ u32 value;
+ ssize_t rc;
+
+ rc = tpm2_get_tpm_pt(chip, TPM2_PT_STARTUP_CLEAR, &value,
+ "could not retrieve STARTUP_CLEAR property");
+ if (rc)
+ return 0;
+
+ rc = sprintf(buf, "%d\n", (value & TPM2_PT_SC_SH_ENABLE) > 0);
+ return rc;
+}
+static DEVICE_ATTR_RO(enabled_sh);
+
+static ssize_t enabled_eh_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+ u32 value;
+ ssize_t rc;
+
+ rc = tpm2_get_tpm_pt(chip, TPM2_PT_STARTUP_CLEAR, &value,
+ "could not retrieve STARTUP_CLEAR property");
+ if (rc)
+ return 0;
+
+ rc = sprintf(buf, "%d\n", (value & TPM2_PT_SC_EH_ENABLE) > 0);
+ return rc;
+}
+static DEVICE_ATTR_RO(enabled_eh);
+
+static ssize_t owned_sh_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+ u32 value;
+ ssize_t rc;
+
+ rc = tpm2_get_tpm_pt(chip, TPM2_PT_PERMANENT, &value,
+ "could not retrieve PERMANENT property");
+ if (rc)
+ return 0;
+
+ rc = sprintf(buf, "%d\n", (value & TPM2_PT_PM_OWNER_AUTH_SET) > 0);
+ return rc;
+}
+static DEVICE_ATTR_RO(owned_sh);
+
+static ssize_t owned_eh_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+ u32 value;
+ ssize_t rc;
+
+ rc = tpm2_get_tpm_pt(chip, TPM2_PT_PERMANENT, &value,
+ "could not retrieve PERMANENT property");
+ if (rc)
+ return 0;
+
+ rc = sprintf(buf, "%d\n", (value & TPM2_PT_PM_ENDORSEMENT_AUTH_SET) > 0);
+ return rc;
+}
+static DEVICE_ATTR_RO(owned_eh);
+
+static ssize_t manufacturer_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+ u32 manufacturer;
+ ssize_t rc;
+ char *str = buf;
+
+ rc = tpm2_get_tpm_pt(chip, TPM2_PT_MANUFACTURER, (u32 *) &manufacturer,
+ "could not retrieve MANUFACTURER property");
+ if (rc)
+ return 0;
+
+ str += sprintf(str, "0x%08x\n", be32_to_cpu(manufacturer));
+
+ return str - buf;
+}
+static DEVICE_ATTR_RO(manufacturer);
+
+static ssize_t firmware_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+ u32 firmware1;
+ u32 firmware2;
+ ssize_t rc;
+ char *str = buf;
+
+ rc = tpm2_get_tpm_pt(chip, TPM2_PT_FIRMWARE_VERSION_1, (u32 *) &firmware1,
+ "could not retrieve FIRMWARE_VERSION_1 property");
+ if (rc)
+ return 0;
+
+ rc = tpm2_get_tpm_pt(chip, TPM2_PT_FIRMWARE_VERSION_2, (u32 *) &firmware2,
+ "could not retrieve FIRMWARE_VERSION_2 property");
+ if (rc)
+ return 0;
+
+ str += sprintf(str, "0x%08x.0x%08x\n", firmware1, firmware2);
+
+ return str - buf;
+}
+static DEVICE_ATTR_RO(firmware);
+
+static ssize_t cancel_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+ if (chip == NULL)
+ return 0;
+
+ chip->ops->cancel(chip);
+ return count;
+}
+static DEVICE_ATTR_WO(cancel);
+
+static ssize_t version_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ char *str = buf;
+
+ str += sprintf(str, "2.0\n");
+
+ return str - buf;
+}
+static DEVICE_ATTR_RO(version);
+
+static struct attribute *tpm_dev_attrs[] = {
+ &dev_attr_enabled_sh.attr,
+ &dev_attr_enabled_eh.attr,
+ &dev_attr_owned_sh.attr,
+ &dev_attr_owned_eh.attr,
+ &dev_attr_manufacturer.attr,
+ &dev_attr_firmware.attr,
+ &dev_attr_cancel.attr,
+ &dev_attr_version.attr,
+ NULL,
+};
+
+static const struct attribute_group tpm_dev_group = {
+ .attrs = tpm_dev_attrs,
+};
+
+struct pcr_attr {
+ struct attribute attr;
+ unsigned int index;
+ char name[3];
+};
+
+struct pcr_bank {
+ struct kobject kobj;
+ struct kobj_type ktype;
+ struct device *dev;
+ struct pcr_attr pcr_attrs[TPM2_PLATFORM_PCR];
+ struct attribute *attrs[TPM2_PLATFORM_PCR + 1];
+};
+
+static ssize_t pcr_bank_attr_show(struct kobject *kobj,
+ struct attribute *attr,
+ char *buf)
+{
+ u8 digest[TPM_DIGEST_SIZE];
+ ssize_t rc;
+ int i;
+ char *str = buf;
+ struct tpm_chip *chip;
+ struct pcr_attr *pcr_attr;
+ struct pcr_bank *pcr_bank;
+
+ pcr_attr = container_of(attr, struct pcr_attr, attr);
+ pcr_bank = container_of(kobj, struct pcr_bank, kobj);
+ chip = dev_get_drvdata(pcr_bank->dev);
+
+ rc = tpm2_pcr_read(chip, pcr_attr->index, digest);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < TPM_DIGEST_SIZE; i++)
+ str += sprintf(str, "%02X", digest[i]);
+
+ str += sprintf(str, "\n");
+
+ return str - buf;
+}
+
+static void pcr_bank_release(struct kobject *kobj)
+{
+ struct pcr_bank *pcr_bank;
+ pcr_bank = container_of(kobj, struct pcr_bank, kobj);
+ kfree(pcr_bank);
+}
+
+static const struct sysfs_ops pcr_bank_sysfs_ops = {
+ .show = pcr_bank_attr_show,
+};
+
+static struct pcr_bank *pcr_bank_create(struct device *dev,
+ struct kobject *parent_kobj)
+{
+ struct pcr_bank *pcr_bank;
+ struct pcr_attr *pcr_attr;
+ struct attribute *attr;
+ int i;
+ int rc;
+
+ pcr_bank = kzalloc(sizeof(*pcr_bank), GFP_KERNEL);
+ if (!pcr_bank)
+ return NULL;
+
+ pcr_bank->dev = dev;
+ pcr_bank->ktype.sysfs_ops = &pcr_bank_sysfs_ops;
+ pcr_bank->ktype.default_attrs = pcr_bank->attrs;
+ pcr_bank->ktype.release = pcr_bank_release;
+
+ for (i = 0; i < TPM2_PLATFORM_PCR; i++) {
+ pcr_attr = &pcr_bank->pcr_attrs[i];
+ pcr_attr->index = i;
+ sprintf(pcr_attr->name, "%d", i);
+
+ attr = &pcr_attr->attr;
+ attr->name = pcr_attr->name;
+ attr->mode = S_IRUGO;
+
+ pcr_bank->attrs[i] = attr;
+ }
+
+ pcr_bank->attrs[i] = NULL;
+
+ rc = kobject_init_and_add(&pcr_bank->kobj, &pcr_bank->ktype,
+ parent_kobj, "sha1");
+ if (rc) {
+ kfree(pcr_bank);
+ return NULL;
+ }
+
+ return pcr_bank;
+}
+
+int tpm2_sysfs_add_device(struct device *dev)
+{
+ struct tpm_chip *chip;
+ struct pcr_bank *pcr_bank;
+ struct kobject *pcrs_kobj;
+ int rc;
+
+ rc = sysfs_create_group(&dev->kobj, &tpm_dev_group);
+ if (rc) {
+ dev_err(dev, "failed to create sysfs attributes, %d\n", rc);
+ return rc;
+ }
+
+ pcrs_kobj = kobject_create_and_add("pcrs", &dev->kobj);
+ if (!pcrs_kobj) {
+ sysfs_remove_group(&dev->kobj, &tpm_dev_group);
+ dev_err(dev, "failed to create sysfs attributes, %d\n", rc);
+ return -ENOMEM;
+ }
+
+ pcr_bank = pcr_bank_create(dev, pcrs_kobj);
+ if (!pcr_bank) {
+ kobject_put(pcrs_kobj);
+ sysfs_remove_group(&dev->kobj, &tpm_dev_group);
+ dev_err(dev, "failed to create sysfs attributes, %d\n", rc);
+ return -ENOMEM;
+ }
+
+ kobject_uevent(pcrs_kobj, KOBJ_ADD);
+
+ chip = dev_get_drvdata(dev);
+ chip->pcrs_kobj = pcrs_kobj;
+ chip->sha1_bank = &pcr_bank->kobj;
+
+ return 0;
+}
+
+void tpm2_sysfs_del_device(struct device *dev)
+{
+ struct tpm_chip *chip;
+
+ chip = dev_get_drvdata(dev);
+
+ kobject_put(chip->sha1_bank);
+ kobject_put(chip->pcrs_kobj);
+ sysfs_remove_group(&dev->kobj, &tpm_dev_group);
+}
--
2.1.0

2014-10-07 17:02:59

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v2 6/7] tpm: TPM 2.0 CRB Interface

tpm_crb is a driver for TPM 2.0 Command Response Buffer (CRB) Interface
as defined in PC Client Platform TPM Profile (PTP) Specification.

Only polling and single locality is supported as these are the limitations
of the available hardware, Platform Trust Techonlogy (PTT) in Haswell
CPUs.

The driver always applies CRB with ACPI start because PTT reports using
only ACPI start as start method but as a result of my testing it requires
also CRB start.

Signed-off-by: Jarkko Sakkinen <[email protected]>
---
drivers/char/tpm/Kconfig | 9 ++
drivers/char/tpm/Makefile | 1 +
drivers/char/tpm/tpm_crb.c | 329 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 339 insertions(+)
create mode 100644 drivers/char/tpm/tpm_crb.c

diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index c54cac3..10c9419 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -122,4 +122,13 @@ config TCG_XEN
To compile this driver as a module, choose M here; the module
will be called xen-tpmfront.

+config TCG_CRB
+ tristate "TPM 2.0 CRB Interface"
+ depends on X86 && ACPI
+ ---help---
+ If you have a TPM security chip that is compliant with the
+ TCG CRB 2.0 TPM specification say Yes and it will be accessible
+ from within Linux. To compile this driver as a module, choose
+ M here; the module will be called tpm_crb.
+
endif # TCG_TPM
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index d3cf905..15e3b4c 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
obj-$(CONFIG_TCG_ST33_I2C) += tpm_i2c_stm_st33.o
obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o
+obj-$(CONFIG_TCG_CRB) += tpm_crb.o
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
new file mode 100644
index 0000000..f808a40
--- /dev/null
+++ b/drivers/char/tpm/tpm_crb.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Authors:
+ * Jarkko Sakkinen <[email protected]>
+ *
+ * Maintained by: <[email protected]>
+ *
+ * This device driver implements the TPM interface as defined in
+ * the TCG CRB 2.0 TPM specification.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#include <linux/acpi.h>
+#include <linux/highmem.h>
+#include <linux/rculist.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "tpm.h"
+
+#define ACPI_SIG_TPM2 "TPM2"
+
+static const u8 CRB_ACPI_START_UUID[] = {
+ /* 0000 */ 0xAB, 0x6C, 0xBF, 0x6B, 0x63, 0x54, 0x14, 0x47,
+ /* 0008 */ 0xB7, 0xCD, 0xF0, 0x20, 0x3C, 0x03, 0x68, 0xD4
+};
+
+enum crb_defaults {
+ CRB_ACPI_START_REVISION_ID = 1,
+ CRB_ACPI_START_INDEX = 1,
+};
+
+enum crb_start_method {
+ CRB_SM_ACPI_START = 2,
+ CRB_SM_CRB = 7,
+ CRB_SM_CRB_WITH_ACPI_START = 8,
+};
+
+struct acpi_tpm2 {
+ struct acpi_table_header hdr;
+ u16 platform_class;
+ u16 reserved;
+ u64 control_area_pa;
+ u32 start_method;
+};
+
+enum crb_ca_request {
+ CRB_CA_REQ_GO_IDLE = BIT(0),
+ CRB_CA_REQ_CMD_READY = BIT(1),
+};
+
+enum crb_ca_status {
+ CRB_CA_STS_ERROR = BIT(0),
+ CRB_CA_STS_TPM_IDLE = BIT(1),
+};
+
+struct crb_control_area {
+ u32 req;
+ u32 sts;
+ u32 cancel;
+ u32 start;
+ u32 int_enable;
+ u32 int_sts;
+ u32 cmd_size;
+ u64 cmd_pa;
+ u32 rsp_size;
+ u64 rsp_pa;
+} __packed;
+
+enum crb_status {
+ CRB_STS_COMPLETE = BIT(0),
+};
+
+enum crb_flags {
+ CRB_FL_ACPI_START = BIT(0),
+ CRB_FL_CRB_START = BIT(1),
+};
+
+struct crb_priv {
+ unsigned int flags;
+ struct crb_control_area *cca;
+ unsigned long cca_pa;
+ acpi_handle dev_handle;
+};
+
+#ifdef CONFIG_PM_SLEEP
+int crb_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int crb_resume(struct device *dev)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+
+ (void) tpm2_do_selftest(chip);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(crb_pm, crb_suspend, crb_resume);
+
+static u8 crb_status(struct tpm_chip *chip)
+{
+ struct crb_priv *priv = chip->vendor.priv;
+ u8 sts = 0;
+
+ if ((le32_to_cpu(priv->cca->start) & 1) != 1)
+ sts |= CRB_STS_COMPLETE;
+
+ return sts;
+}
+
+static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ struct crb_priv *priv = chip->vendor.priv;
+ struct crb_control_area *cca;
+ unsigned int expected;
+ unsigned long offset;
+ u8 *resp;
+
+ cca = priv->cca;
+ if (le32_to_cpu(cca->sts) & CRB_CA_STS_ERROR)
+ return -EIO;
+
+ offset = le64_to_cpu(cca->rsp_pa) - priv->cca_pa;
+ resp = (u8 *) ((unsigned long) cca + offset);
+ memcpy(buf, resp, 6);
+ expected = be32_to_cpup((__be32 *) &buf[2]);
+
+ if (expected > count)
+ return -EIO;
+
+ memcpy(&buf[6], &resp[6], expected - 6);
+
+ return expected;
+}
+
+static int crb_do_acpi_start(struct tpm_chip *chip)
+{
+ struct crb_priv *priv = chip->vendor.priv;
+ union acpi_object *obj;
+ int rc;
+
+ obj = acpi_evaluate_dsm(priv->dev_handle,
+ CRB_ACPI_START_UUID,
+ CRB_ACPI_START_REVISION_ID,
+ CRB_ACPI_START_INDEX,
+ NULL);
+ if (!obj)
+ return -ENXIO;
+ rc = obj->integer.value == 0 ? 0 : -ENXIO;
+ ACPI_FREE(obj);
+ return rc;
+}
+
+static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+ struct crb_priv *priv = chip->vendor.priv;
+ struct crb_control_area *cca;
+ u8 *cmd;
+ int rc = 0;
+
+ cca = priv->cca;
+
+ if (len > le32_to_cpu(cca->cmd_size)) {
+ dev_err(chip->dev,
+ "invalid command count value %x %zx\n",
+ (unsigned int) len,
+ (size_t) le32_to_cpu(cca->cmd_size));
+ return -E2BIG;
+ }
+
+ cmd = (u8 *) ((unsigned long) cca + le64_to_cpu(cca->cmd_pa) - priv->cca_pa);
+ memcpy(cmd, buf, len);
+ wmb();
+
+ cca->start = cpu_to_le32(1);
+ rc = crb_do_acpi_start(chip);
+ return rc;
+}
+
+static void crb_cancel(struct tpm_chip *chip)
+{
+ struct crb_priv *priv = chip->vendor.priv;
+ struct crb_control_area *cca;
+
+ cca = priv->cca;
+ cca->cancel = cpu_to_le32(1);
+ wmb();
+
+ if (crb_do_acpi_start(chip))
+ dev_err(chip->dev, "ACPI Start failed\n");
+
+ cca->cancel = 0;
+}
+
+static bool crb_req_canceled(struct tpm_chip *chip, u8 status)
+{
+ struct crb_priv *priv = chip->vendor.priv;
+
+ return (le32_to_cpu(priv->cca->cancel) & 1) == 1;
+}
+
+static const struct tpm_class_ops tpm_crb = {
+ .status = crb_status,
+ .recv = crb_recv,
+ .send = crb_send,
+ .cancel = crb_cancel,
+ .req_canceled = crb_req_canceled,
+ .req_complete_mask = CRB_STS_COMPLETE,
+ .req_complete_val = CRB_STS_COMPLETE,
+};
+
+static int crb_acpi_add(struct acpi_device *device)
+{
+ struct tpm_chip *chip;
+ struct acpi_tpm2 *buf;
+ struct crb_priv *priv;
+ struct device *dev = &device->dev;
+ acpi_status status;
+ u32 sm;
+ int rc;
+
+ chip = tpm_chip_alloc(dev, &tpm_crb);
+ if (IS_ERR(chip))
+ return -ENODEV;
+
+ chip->flags = TPM_CHIP_FLAG_TPM2;
+
+ rc = tpm_chip_register(chip);
+ if (rc)
+ return -ENODEV;
+
+ status = acpi_get_table(ACPI_SIG_TPM2, 1,
+ (struct acpi_table_header **) &buf);
+ if (ACPI_FAILURE(status)) {
+ dev_err(dev, "could not get TPM2 ACPI table\n");
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ priv = (struct crb_priv *) devm_kzalloc(dev, sizeof(struct crb_priv),
+ GFP_KERNEL);
+ if (!priv) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ sm = le32_to_cpu(buf->start_method);
+
+ if (sm == CRB_SM_CRB || sm == CRB_SM_CRB_WITH_ACPI_START)
+ priv->flags |= CRB_FL_CRB_START;
+
+ if (sm == CRB_SM_ACPI_START || sm == CRB_SM_CRB_WITH_ACPI_START)
+ priv->flags |= CRB_FL_ACPI_START;
+
+ priv->dev_handle = device->handle;
+ priv->cca_pa = le32_to_cpu(buf->control_area_pa);
+ priv->cca = (struct crb_control_area *)
+ devm_ioremap_nocache(dev, buf->control_area_pa, 0x1000);
+ if (!priv->cca) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ chip->vendor.priv = priv;
+
+ /* Default timeouts and durations */
+ chip->vendor.timeout_a = usecs_to_jiffies(TPM2_TIMEOUT_A);
+ chip->vendor.timeout_b = usecs_to_jiffies(TPM2_TIMEOUT_B);
+ chip->vendor.timeout_c = usecs_to_jiffies(TPM2_TIMEOUT_C);
+ chip->vendor.timeout_d = usecs_to_jiffies(TPM2_TIMEOUT_D);
+ chip->vendor.duration[TPM_SHORT] =
+ usecs_to_jiffies(TPM2_DURATION_SHORT);
+ chip->vendor.duration[TPM_MEDIUM] =
+ usecs_to_jiffies(TPM2_DURATION_MEDIUM);
+ chip->vendor.duration[TPM_LONG] =
+ usecs_to_jiffies(TPM2_DURATION_LONG);
+
+ rc = tpm2_do_selftest(chip);
+ if (rc) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ return 0;
+out_err:
+ tpm_chip_unregister(chip);
+ return rc;
+}
+
+int crb_acpi_remove(struct acpi_device *device)
+{
+ struct device *dev = &device->dev;
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+
+ tpm_chip_unregister(chip);
+ return 0;
+}
+
+static struct acpi_device_id crb_device_ids[] = {
+ {"MSFT0101", 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, crb_device_ids);
+
+static struct acpi_driver crb_acpi_driver = {
+ .name = "tpm_crb",
+ .ids = crb_device_ids,
+ .ops = {
+ .add = crb_acpi_add,
+ .remove = crb_acpi_remove,
+ },
+ .drv = {
+ .pm = &crb_pm,
+ },
+};
+
+module_acpi_driver(crb_acpi_driver);
+MODULE_AUTHOR("Jarkko Sakkinen <[email protected]>");
+MODULE_DESCRIPTION("TPM2 Driver");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
--
2.1.0

2014-10-07 17:04:00

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v2 7/7] tpm: TPM 2.0 FIFO Interface

From: Will Arthur <[email protected]>

Detect TPM 2.0 by using the extended STS (STS3) register. For TPM 2.0,
instead of calling tpm_get_timeouts(), assign duration and timeout
values defined in the TPM 2.0 PTP specification.

Signed-off-by: Will Arthur <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
drivers/char/tpm/tpm_tis.c | 77 ++++++++++++++++++++++++++++++++++++----------
1 file changed, 60 insertions(+), 17 deletions(-)

diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index df04ce6..5c5a0cc 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2005, 2006 IBM Corporation
+ * Copyright (C) 2014 Intel Corporation
*
* Authors:
* Leendert van Doorn <[email protected]>
@@ -44,6 +45,10 @@ enum tis_status {
TPM_STS_DATA_EXPECT = 0x08,
};

+enum tis_status3 {
+ TPM_STS3_TPM2_FAM = 0x04,
+};
+
enum tis_int_flags {
TPM_GLOBAL_INT_ENABLE = 0x80000000,
TPM_INTF_BURST_COUNT_STATIC = 0x100,
@@ -70,6 +75,7 @@ enum tis_defaults {
#define TPM_INT_STATUS(l) (0x0010 | ((l) << 12))
#define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12))
#define TPM_STS(l) (0x0018 | ((l) << 12))
+#define TPM_STS3(l) (0x001b | ((l) << 12))
#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12))

#define TPM_DID_VID(l) (0x0F00 | ((l) << 12))
@@ -344,6 +350,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
{
int rc;
u32 ordinal;
+ unsigned long dur;

rc = tpm_tis_send_data(chip, buf, len);
if (rc < 0)
@@ -355,9 +362,14 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)

if (chip->vendor.irq) {
ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+
+ if (chip->flags & TPM_CHIP_FLAG_TPM2)
+ dur = tpm_calc_ordinal_duration(chip, ordinal);
+ else
+ dur = tpm_calc_ordinal_duration(chip, ordinal);
+
if (wait_for_tpm_stat
- (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
- tpm_calc_ordinal_duration(chip, ordinal),
+ (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, dur,
&chip->vendor.read_queue, false) < 0) {
rc = -ETIME;
goto out_err;
@@ -531,24 +543,43 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
u32 vendor, intfcaps, intmask;
int rc, i, irq_s, irq_e, probe;
struct tpm_chip *chip;
+ u8 sts3;
+ u32 dummy;

chip = tpm_chip_alloc(dev, &tpm_tis);
- if (!chip)
+ if (IS_ERR(chip))
return -ENODEV;

chip->vendor.iobase = devm_ioremap(dev, start, len);
if (!chip->vendor.iobase)
return -EIO;

+ sts3 = ioread8(chip->vendor.iobase + TPM_STS3(1));
+ if (sts3 & TPM_STS3_TPM2_FAM)
+ chip->flags = TPM_CHIP_FLAG_TPM2;
+
rc = tpm_chip_register(chip);
if (rc)
return -ENODEV;

- /* Default timeouts */
- chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
- chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
- chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
- chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+ /* Default timeouts */
+ if (chip->flags & TPM_CHIP_FLAG_TPM2) {
+ chip->vendor.timeout_a = usecs_to_jiffies(TPM2_TIMEOUT_A);
+ chip->vendor.timeout_b = usecs_to_jiffies(TPM2_TIMEOUT_B);
+ chip->vendor.timeout_c = usecs_to_jiffies(TPM2_TIMEOUT_C);
+ chip->vendor.timeout_d = usecs_to_jiffies(TPM2_TIMEOUT_D);
+ chip->vendor.duration[TPM_SHORT] =
+ usecs_to_jiffies(TPM2_DURATION_SHORT);
+ chip->vendor.duration[TPM_MEDIUM] =
+ usecs_to_jiffies(TPM2_DURATION_MEDIUM);
+ chip->vendor.duration[TPM_LONG] =
+ usecs_to_jiffies(TPM2_DURATION_LONG);
+ } else {
+ chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+ chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
+ chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+ chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+ }

if (wait_startup(chip, 0) != 0) {
rc = -ENODEV;
@@ -563,9 +594,9 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
chip->vendor.manufacturer_id = vendor;

- dev_info(dev,
- "1.2 TPM (device-id 0x%X, rev-id %d)\n",
- vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
+ dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n",
+ (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2",
+ vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));

if (!itpm) {
probe = probe_itpm(chip);
@@ -612,7 +643,11 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
goto out_err;
}

- if (tpm_do_selftest(chip)) {
+ if (chip->flags & TPM_CHIP_FLAG_TPM2)
+ rc = tpm2_do_selftest(chip);
+ else
+ rc = tpm_do_selftest(chip);
+ if (rc) {
dev_err(dev, "TPM self test failed\n");
rc = -ENODEV;
goto out_err;
@@ -673,7 +708,11 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
chip->vendor.probed_irq = 0;

/* Generate Interrupts */
- tpm_gen_interrupt(chip);
+ if (chip->flags & TPM_CHIP_FLAG_TPM2)
+ rc = tpm2_get_tpm_pt(chip, TPM2_CAP_TPM_PROPERTIES, &dummy,
+ "attempting to generate an interrupt");
+ else
+ tpm_gen_interrupt(chip);

chip->vendor.irq = chip->vendor.probed_irq;

@@ -752,14 +791,18 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
static int tpm_tis_resume(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
- int ret;
+ int ret = 0;

if (chip->vendor.irq)
tpm_tis_reenable_interrupts(chip);

- ret = tpm_pm_resume(dev);
- if (!ret)
- tpm_do_selftest(chip);
+ if (chip->flags & TPM_CHIP_FLAG_TPM2)
+ tpm2_do_selftest(chip);
+ else {
+ ret = tpm_pm_resume(dev);
+ if (!ret)
+ tpm_do_selftest(chip);
+ }

return ret;
}
--
2.1.0

2014-10-07 17:04:18

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v2 4/7] tpm: TPM 2.0 commands

This patch contains the following internal helper functions for
tpm.ko:

- tpm2_get_random()
- tpm2_get_tpm_pt()
- tpm2_pcr_extend()
- tpm2_pcr_read()
- tpm2_startup()

and the following exported functions for implementing new device
drivers:

- tpm2_do_selftest()
- tpm2_calc_ordinal_durations()
- tpm2_gen_interrupt()

Functions that are defined in include/linux/tpm.h and tpm_transmit()
use TPM2 commands for TPM2 chips.

Added a new field "flags" to struct tpm_chip and enum tpm_chip_flags.
TPM2 chips can set TPM_CHIP_FLAG_TPM2 after tpm_chip_alloc() to
enable TPM2 commands.

The code for tpm2_calc_ordinal_duration() and tpm2_startup() is
derived from the code originally made by Will Arthur.

Signed-off-by: Jarkko Sakkinen <[email protected]>
Signed-off-by: Will Arthur <[email protected]>
---
drivers/char/tpm/Makefile | 2 +-
drivers/char/tpm/tpm-interface.c | 24 +-
drivers/char/tpm/tpm.h | 66 +++++
drivers/char/tpm/tpm2-cmd.c | 543 +++++++++++++++++++++++++++++++++++++++
4 files changed, 631 insertions(+), 4 deletions(-)
create mode 100644 drivers/char/tpm/tpm2-cmd.c

diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 837da04..ae56af9 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -2,7 +2,7 @@
# Makefile for the kernel tpm device drivers.
#
obj-$(CONFIG_TCG_TPM) += tpm.o
-tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o
+tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o
tpm-$(CONFIG_ACPI) += tpm_ppi.o

ifdef CONFIG_ACPI
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 1ce3ad3..ec28b5f 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -360,7 +360,10 @@ static 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) ==
@@ -482,7 +485,7 @@ static const struct tpm_input_header tpm_startup_header = {
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 tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE,
"attempting to start the TPM");
@@ -679,7 +682,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;
}
@@ -713,6 +719,12 @@ 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);
@@ -986,6 +998,12 @@ 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);
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 5eb89897..d141639 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -61,6 +61,57 @@ enum tpm_duration {
#define TPM_ERR_INVALID_POSTINIT 38

#define TPM_HEADER_SIZE 10
+
+enum tpm2_const {
+ TPM2_PLATFORM_PCR = 24,
+ TPM2_PCR_SELECT_MIN = ((TPM2_PLATFORM_PCR + 7) / 8),
+ TPM2_TIMEOUT_A = 750 * 1000,
+ TPM2_TIMEOUT_B = 2000 * 1000,
+ TPM2_TIMEOUT_C = 200 * 1000,
+ TPM2_TIMEOUT_D = 30 * 1000,
+ TPM2_DURATION_SHORT = 20 * 1000,
+ TPM2_DURATION_MEDIUM = 750 * 1000,
+ TPM2_DURATION_LONG = 2000 * 1000,
+};
+
+enum tpm2_structures {
+ TPM2_ST_NO_SESSIONS = 0x8001,
+ TPM2_ST_SESSIONS = 0x8002,
+};
+
+enum tpm2_return_codes {
+ TPM2_RC_TESTING = 0x090A,
+ TPM2_RC_DISABLED = 0x0120,
+};
+
+enum tpm2_algorithms {
+ TPM2_ALG_SHA1 = 0x0004,
+};
+
+enum tpm2_command_codes {
+ TPM2_CC_FIRST = 0x011F,
+ TPM2_CC_SELF_TEST = 0x0143,
+ TPM2_CC_STARTUP = 0x0144,
+ TPM2_CC_GET_CAPABILITY = 0x017A,
+ TPM2_CC_GET_RANDOM = 0x017B,
+ TPM2_CC_PCR_READ = 0x017E,
+ TPM2_CC_PCR_EXTEND = 0x0182,
+ TPM2_CC_LAST = 0x018F,
+};
+
+enum tpm2_permanent_handles {
+ TPM2_RS_PW = 0x40000009,
+};
+
+enum tpm2_capabilities {
+ TPM2_CAP_TPM_PROPERTIES = 6,
+};
+
+enum tpm2_startup_types {
+ TPM2_SU_CLEAR = 0x0000,
+ TPM2_SU_STATE = 0x0001,
+};
+
struct tpm_chip;

struct tpm_vendor_specific {
@@ -94,9 +145,14 @@ struct tpm_vendor_specific {
#define TPM_VID_WINBOND 0x1050
#define TPM_VID_STM 0x104A

+enum tpm_chip_flags {
+ TPM_CHIP_FLAG_TPM2 = BIT(0),
+};
+
struct tpm_chip {
struct device *dev; /* Device stuff */
const struct tpm_class_ops *ops;
+ unsigned int flags;

int dev_num; /* /dev/tpm# */
char devname[7];
@@ -353,3 +409,13 @@ static inline void tpm_remove_ppi(struct kobject *parent)
{
}
#endif
+
+int tpm2_startup(struct tpm_chip *chip, __be16 startup_type);
+int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf);
+int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash);
+int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max);
+
+extern ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
+ u32* value, const char *desc);
+extern unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *, u32);
+extern int tpm2_do_selftest(struct tpm_chip *chip);
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
new file mode 100644
index 0000000..e0c455d
--- /dev/null
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Authors:
+ * Jarkko Sakkinen <[email protected]>
+ *
+ * Maintained by: <[email protected]>
+ *
+ * This file contains TPM2 protocol implementations of the commands
+ * used by the kernel internally.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#include "tpm.h"
+
+struct tpm2_startup_in {
+ __be16 startup_type;
+} __packed;
+
+struct tpm2_self_test_in {
+ u8 full_test;
+} __packed;
+
+struct tpm2_pcr_read_in {
+ __be32 pcr_selects_cnt;
+ __be16 hash_alg;
+ u8 pcr_select_size;
+ u8 pcr_select[TPM2_PCR_SELECT_MIN];
+} __packed;
+
+struct tpm2_pcr_read_out {
+ __be32 update_cnt;
+ __be32 pcr_selects_cnt;
+ __be16 hash_alg;
+ u8 pcr_select_size;
+ u8 pcr_select[TPM2_PCR_SELECT_MIN];
+ __be32 digests_cnt;
+ __be16 digest_size;
+ u8 digest[TPM_DIGEST_SIZE];
+} __packed;
+
+struct tpm2_null_auth_area {
+ __be32 handle;
+ __be16 nonce_size;
+ u8 attributes;
+ __be16 auth_size;
+} __packed;
+
+struct tpm2_pcr_extend_in {
+ __be32 pcr_idx;
+ __be32 auth_area_size;
+ struct tpm2_null_auth_area auth_area;
+ __be32 digest_cnt;
+ __be16 hash_alg;
+ u8 digest[TPM_DIGEST_SIZE];
+} __packed;
+
+struct tpm2_get_tpm_pt_in {
+ __be32 cap_id;
+ __be32 property_id;
+ __be32 property_cnt;
+} __packed;
+
+struct tpm2_get_tpm_pt_out {
+ u8 more_data;
+ __be32 subcap_id;
+ __be32 property_cnt;
+ __be32 property_id;
+ __be32 value;
+} __packed;
+
+struct tpm2_get_random_in {
+ __be16 size;
+} __packed;
+
+struct tpm2_get_random_out {
+ __be16 size;
+ u8 buffer[TPM_MAX_RNG_DATA];
+} __packed;
+
+union tpm2_cmd_params {
+ struct tpm2_startup_in startup_in;
+ struct tpm2_self_test_in selftest_in;
+ struct tpm2_pcr_read_in pcrread_in;
+ struct tpm2_pcr_read_out pcrread_out;
+ struct tpm2_pcr_extend_in pcrextend_in;
+ struct tpm2_get_tpm_pt_in get_tpm_pt_in;
+ struct tpm2_get_tpm_pt_out get_tpm_pt_out;
+ struct tpm2_get_random_in getrandom_in;
+ struct tpm2_get_random_out getrandom_out;
+};
+
+struct tpm2_cmd {
+ tpm_cmd_header header;
+ union tpm2_cmd_params params;
+} __packed;
+
+/*
+ * Array with one entry per ordinal defining the maximum amount
+ * of time the chip could take to return the result. The values
+ * of the SHORT, MEDIUM, and LONG durations are taken from the
+ * PC Client Profile (PTP) specification.
+ */
+static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = {
+ TPM_UNDEFINED, /* 11F */
+ TPM_UNDEFINED, /* 120 */
+ TPM_LONG, /* 121 */
+ TPM_UNDEFINED, /* 122 */
+ TPM_UNDEFINED, /* 123 */
+ TPM_UNDEFINED, /* 124 */
+ TPM_UNDEFINED, /* 125 */
+ TPM_UNDEFINED, /* 126 */
+ TPM_UNDEFINED, /* 127 */
+ TPM_UNDEFINED, /* 128 */
+ TPM_LONG, /* 129 */
+ TPM_UNDEFINED, /* 12a */
+ TPM_UNDEFINED, /* 12b */
+ TPM_UNDEFINED, /* 12c */
+ TPM_UNDEFINED, /* 12d */
+ TPM_UNDEFINED, /* 12e */
+ TPM_UNDEFINED, /* 12f */
+ TPM_UNDEFINED, /* 130 */
+ TPM_UNDEFINED, /* 131 */
+ TPM_UNDEFINED, /* 132 */
+ TPM_UNDEFINED, /* 133 */
+ TPM_UNDEFINED, /* 134 */
+ TPM_UNDEFINED, /* 135 */
+ TPM_UNDEFINED, /* 136 */
+ TPM_UNDEFINED, /* 137 */
+ TPM_UNDEFINED, /* 138 */
+ TPM_UNDEFINED, /* 139 */
+ TPM_UNDEFINED, /* 13a */
+ TPM_UNDEFINED, /* 13b */
+ TPM_UNDEFINED, /* 13c */
+ TPM_UNDEFINED, /* 13d */
+ TPM_MEDIUM, /* 13e */
+ TPM_UNDEFINED, /* 13f */
+ TPM_UNDEFINED, /* 140 */
+ TPM_UNDEFINED, /* 141 */
+ TPM_UNDEFINED, /* 142 */
+ TPM_LONG, /* 143 */
+ TPM_MEDIUM, /* 144 */
+ TPM_UNDEFINED, /* 145 */
+ TPM_UNDEFINED, /* 146 */
+ TPM_UNDEFINED, /* 147 */
+ TPM_UNDEFINED, /* 148 */
+ TPM_UNDEFINED, /* 149 */
+ TPM_UNDEFINED, /* 14a */
+ TPM_UNDEFINED, /* 14b */
+ TPM_UNDEFINED, /* 14c */
+ TPM_UNDEFINED, /* 14d */
+ TPM_LONG, /* 14e */
+ TPM_UNDEFINED, /* 14f */
+ TPM_UNDEFINED, /* 150 */
+ TPM_UNDEFINED, /* 151 */
+ TPM_UNDEFINED, /* 152 */
+ TPM_UNDEFINED, /* 153 */
+ TPM_UNDEFINED, /* 154 */
+ TPM_UNDEFINED, /* 155 */
+ TPM_UNDEFINED, /* 156 */
+ TPM_UNDEFINED, /* 157 */
+ TPM_UNDEFINED, /* 158 */
+ TPM_UNDEFINED, /* 159 */
+ TPM_UNDEFINED, /* 15a */
+ TPM_UNDEFINED, /* 15b */
+ TPM_MEDIUM, /* 15c */
+ TPM_UNDEFINED, /* 15d */
+ TPM_UNDEFINED, /* 15e */
+ TPM_UNDEFINED, /* 15f */
+ TPM_UNDEFINED, /* 160 */
+ TPM_UNDEFINED, /* 161 */
+ TPM_UNDEFINED, /* 162 */
+ TPM_UNDEFINED, /* 163 */
+ TPM_UNDEFINED, /* 164 */
+ TPM_UNDEFINED, /* 165 */
+ TPM_UNDEFINED, /* 166 */
+ TPM_UNDEFINED, /* 167 */
+ TPM_UNDEFINED, /* 168 */
+ TPM_UNDEFINED, /* 169 */
+ TPM_UNDEFINED, /* 16a */
+ TPM_UNDEFINED, /* 16b */
+ TPM_UNDEFINED, /* 16c */
+ TPM_UNDEFINED, /* 16d */
+ TPM_UNDEFINED, /* 16e */
+ TPM_UNDEFINED, /* 16f */
+ TPM_UNDEFINED, /* 170 */
+ TPM_UNDEFINED, /* 171 */
+ TPM_UNDEFINED, /* 172 */
+ TPM_UNDEFINED, /* 173 */
+ TPM_UNDEFINED, /* 174 */
+ TPM_UNDEFINED, /* 175 */
+ TPM_UNDEFINED, /* 176 */
+ TPM_LONG, /* 177 */
+ TPM_UNDEFINED, /* 178 */
+ TPM_UNDEFINED, /* 179 */
+ TPM_MEDIUM, /* 17a */
+ TPM_LONG, /* 17b */
+ TPM_UNDEFINED, /* 17c */
+ TPM_UNDEFINED, /* 17d */
+ TPM_UNDEFINED, /* 17e */
+ TPM_UNDEFINED, /* 17f */
+ TPM_UNDEFINED, /* 180 */
+ TPM_UNDEFINED, /* 181 */
+ TPM_MEDIUM, /* 182 */
+ TPM_UNDEFINED, /* 183 */
+ TPM_UNDEFINED, /* 184 */
+ TPM_MEDIUM, /* 185 */
+ TPM_MEDIUM, /* 186 */
+ TPM_UNDEFINED, /* 187 */
+ TPM_UNDEFINED, /* 188 */
+ TPM_UNDEFINED, /* 189 */
+ TPM_UNDEFINED, /* 18a */
+ TPM_UNDEFINED, /* 18b */
+ TPM_UNDEFINED, /* 18c */
+ TPM_UNDEFINED, /* 18d */
+ TPM_UNDEFINED, /* 18e */
+ TPM_UNDEFINED /* 18f */
+};
+
+static const struct tpm_input_header tpm2_startup_header = {
+ .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+ .length = cpu_to_be32(12),
+ .ordinal = cpu_to_be32(TPM2_CC_STARTUP)
+};
+
+/**
+ * tpm2_startup() - send startup command to the TPM chip
+ * @chip: TPM chip to use.
+ * @startup_type startup type. The value is either
+ * TPM_SU_CLEAR or TPM_SU_STATE.
+ *
+ * 0 is returned when the operation is succesful. When a negative number is
+ * returned it remarks a POSIX error code. When a positive number is returned
+ * it remarks a TPM error.
+ */
+int tpm2_startup(struct tpm_chip *chip, __be16 startup_type)
+{
+ struct tpm2_cmd cmd;
+
+ cmd.header.in = tpm2_startup_header;
+
+ cmd.params.startup_in.startup_type = startup_type;
+ return tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+ "attempting to start the TPM");
+}
+
+#define TPM2_PCR_READ_IN_SIZE \
+ (sizeof(struct tpm_input_header) + \
+ sizeof(struct tpm2_pcr_read_in))
+
+static const struct tpm_input_header tpm2_pcrread_header = {
+ .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+ .length = cpu_to_be32(TPM2_PCR_READ_IN_SIZE),
+ .ordinal = cpu_to_be32(TPM2_CC_PCR_READ)
+};
+
+/**
+ * tpm2_pcr_read() - read a PCR value
+ * @chip: TPM chip to use.
+ * @pcr_idx: index of the PCR to read.
+ * @ref_buf: buffer to store the resulting hash,
+ *
+ * 0 is returned when the operation is succesful. When a negative number is
+ * returned it remarks a POSIX error code. When a positive number is returned
+ * it remarks a TPM error.
+ */
+int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
+{
+ int rc;
+ struct tpm2_cmd cmd;
+ u8 *buf;
+ int i, j;
+
+ if (pcr_idx >= TPM2_PLATFORM_PCR)
+ return -EINVAL;
+
+ cmd.header.in = tpm2_pcrread_header;
+ cmd.params.pcrread_in.pcr_selects_cnt = cpu_to_be32(1);
+ cmd.params.pcrread_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1);
+ cmd.params.pcrread_in.pcr_select_size = TPM2_PCR_SELECT_MIN;
+
+ for (i = 0; i < TPM2_PCR_SELECT_MIN; i++) {
+ j = pcr_idx - i * 8;
+
+ cmd.params.pcrread_in.pcr_select[i] =
+ (j >= 0 && j < 8) ? 1 << j : 0;
+ }
+
+ rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+ "attempting to read a pcr value");
+
+ if (rc == 0) {
+ buf = cmd.params.pcrread_out.digest;
+ memcpy(res_buf, buf, TPM_DIGEST_SIZE);
+ }
+
+ return rc;
+}
+
+/**
+ * tpm2_pcr_extend() - extend a PCR value
+ * @chip: TPM chip to use.
+ * @pcr_idx: index of the PCR.
+ * @hash: hash value to use for the extend operation.
+ *
+ * 0 is returned when the operation is succesful. When a negative number is
+ * returned it remarks a POSIX error code. When a positive number is returned
+ * it remarks a TPM error.
+ */
+static const struct tpm_input_header tpm2_pcrextend_header = {
+ .tag = cpu_to_be16(TPM2_ST_SESSIONS),
+ .length = cpu_to_be32(sizeof(struct tpm_input_header) +
+ sizeof(struct tpm2_pcr_extend_in)),
+ .ordinal = cpu_to_be32(TPM2_CC_PCR_EXTEND)
+};
+
+int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash)
+{
+ struct tpm2_cmd cmd;
+ int rc;
+
+ cmd.header.in = tpm2_pcrextend_header;
+ cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx);
+ cmd.params.pcrextend_in.auth_area_size =
+ cpu_to_be32(sizeof(struct tpm2_null_auth_area));
+ cmd.params.pcrextend_in.auth_area.handle =
+ cpu_to_be32(TPM2_RS_PW);
+ cmd.params.pcrextend_in.auth_area.nonce_size = 0;
+ cmd.params.pcrextend_in.auth_area.attributes = 0;
+ cmd.params.pcrextend_in.auth_area.auth_size = 0;
+ cmd.params.pcrextend_in.digest_cnt = cpu_to_be32(1);
+ cmd.params.pcrextend_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1);
+ memcpy(cmd.params.pcrextend_in.digest, hash, TPM_DIGEST_SIZE);
+
+ rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+ "attempting extend a PCR value");
+
+ return rc;
+}
+
+static const struct tpm_input_header tpm2_getrandom_header = {
+ .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+ .length = cpu_to_be32(sizeof(struct tpm_input_header) +
+ sizeof(struct tpm2_get_random_in)),
+ .ordinal = cpu_to_be32(TPM2_CC_GET_RANDOM)
+};
+
+/**
+ * tpm2_get_random() - get random bytes from the TPM RNG
+ * @chip: TPM chip to use
+ * @out: destination buffer for the random bytes
+ * @max: the max number of bytes to write to @out
+ *
+ * 0 is returned when the operation is succesful. When a negative number is
+ * returned it remarks a POSIX error code. When a positive number is returned
+ * it remarks a TPM error.
+ */
+int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max)
+{
+ struct tpm2_cmd cmd;
+ u32 recd, num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA);
+ int err, total = 0, retries = 5;
+ u8 *dest = out;
+
+ if (!out || !num_bytes || max > TPM_MAX_RNG_DATA)
+ return -EINVAL;
+
+ do {
+ cmd.header.in = tpm2_getrandom_header;
+ cmd.params.getrandom_in.size = cpu_to_be16(num_bytes);
+
+ err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+ "attempting get random");
+ if (err)
+ break;
+
+ recd = be16_to_cpu(cmd.params.getrandom_out.size);
+ memcpy(dest, cmd.params.getrandom_out.buffer, recd);
+
+ dest += recd;
+ total += recd;
+ num_bytes -= recd;
+ } while (retries-- && total < max);
+
+ return total ? total : -EIO;
+}
+
+#define TPM2_GET_TPM_PT_IN_SIZE \
+ (sizeof(struct tpm_input_header) + \
+ sizeof(struct tpm2_get_tpm_pt_in))
+
+static const struct tpm_input_header tpm2_get_tpm_pt_header = {
+ .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+ .length = cpu_to_be32(TPM2_GET_TPM_PT_IN_SIZE),
+ .ordinal = cpu_to_be32(TPM2_CC_GET_CAPABILITY)
+};
+
+/**
+ * tpm2_get_tpm_pt() - get value of a TPM_CAP_TPM_PROPERTIES type property
+ * @chip: TPM chip to use.
+ * @property_id: property ID.
+ * @value: output variable.
+ * @desc: passed to tpm_transmit_cmd()
+ *
+ * 0 is returned when the operation is succesful. When a negative number is
+ * returned it remarks a POSIX error code. When a positive number is returned
+ * it remarks a TPM error.
+ */
+ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32* value,
+ const char *desc)
+{
+ struct tpm2_cmd cmd;
+ int rc;
+
+ cmd.header.in = tpm2_get_tpm_pt_header;
+ cmd.params.get_tpm_pt_in.cap_id = cpu_to_be32(TPM2_CAP_TPM_PROPERTIES);
+ cmd.params.get_tpm_pt_in.property_id = property_id;
+ cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1);
+
+ rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), desc);
+ if (!rc)
+ *value = cmd.params.get_tpm_pt_out.value;
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
+
+/*
+ * tpm2_calc_ordinal_duration() - maximum duration for a command
+ * @chip: TPM chip to use.
+ * @ordinal: command code number.
+ *
+ * 0 is returned when the operation is succesful. When a negative number is
+ * returned it remarks a POSIX error code. When a positive number is returned
+ * it remarks a TPM error.
+ */
+unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
+{
+ int index = TPM_UNDEFINED;
+ int duration = 0;
+
+ if (ordinal >= TPM2_CC_FIRST && ordinal <= TPM2_CC_LAST)
+ index = tpm2_ordinal_duration[ordinal - TPM2_CC_FIRST];
+
+ if (index != TPM_UNDEFINED)
+ duration = chip->vendor.duration[index];
+ if (duration <= 0)
+ return 2 * 60 * HZ;
+ else
+ return duration;
+}
+EXPORT_SYMBOL_GPL(tpm2_calc_ordinal_duration);
+
+static const struct tpm_input_header tpm2_selftest_header = {
+ .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+ .length = cpu_to_be32(sizeof(struct tpm_input_header) +
+ sizeof(struct tpm2_self_test_in)),
+ .ordinal = cpu_to_be32(TPM2_CC_SELF_TEST)
+};
+
+#define TPM2_SELF_TEST_IN_SIZE \
+ (sizeof(struct tpm_input_header) + sizeof(struct tpm2_self_test_in))
+
+/**
+ * tpm2_continue_selftest() - start a self test
+ * @chip: TPM chip to use
+ * @full: test all commands instead of testing only those that were not
+ * previously tested.
+ *
+ * 0 is returned when the operation is succesful. When a negative number is
+ * returned it remarks a POSIX error code. When a positive number is returned
+ * it remarks a TPM error.
+ */
+static int tpm2_start_selftest(struct tpm_chip *chip, bool full)
+{
+ int rc;
+ struct tpm2_cmd cmd;
+
+ cmd.header.in = tpm2_selftest_header;
+ cmd.params.selftest_in.full_test = full;
+
+ rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE,
+ "continue selftest");
+
+ return rc;
+}
+
+/**
+ * tpm2_do_selftest() - run a full self test
+ * @chip: TPM chip to use
+ *
+ * During the self test TPM2 commands return with the error code RC_TESTING.
+ * Waiting is done by issuing PCR read until it executes succesfully.
+ *
+ * 0 is returned when the operation is succesful. When a negative number is
+ * returned it remarks a POSIX error code. When a positive number is returned
+ * it remarks a TPM error.
+ */
+int tpm2_do_selftest(struct tpm_chip *chip)
+{
+ int rc;
+ unsigned int loops;
+ unsigned int delay_msec = 100;
+ unsigned long duration;
+ struct tpm2_cmd cmd;
+ int i;
+
+ duration = tpm2_calc_ordinal_duration(chip, TPM2_CC_SELF_TEST);
+
+ loops = jiffies_to_msecs(duration) / delay_msec;
+
+ rc = tpm2_start_selftest(chip, true);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < loops; i++) {
+ /* Attempt to read a PCR value */
+ cmd.header.in = tpm2_pcrread_header;
+ cmd.params.pcrread_in.pcr_selects_cnt = cpu_to_be32(1);
+ cmd.params.pcrread_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1);
+ cmd.params.pcrread_in.pcr_select_size = TPM2_PCR_SELECT_MIN;
+ cmd.params.pcrread_in.pcr_select[0] = 0x01;
+ cmd.params.pcrread_in.pcr_select[1] = 0x00;
+ cmd.params.pcrread_in.pcr_select[2] = 0x00;
+
+ rc = tpm_transmit_cmd(chip, (u8 *) &cmd, sizeof(cmd), NULL);
+ if (rc < 0)
+ break;
+
+ rc = be32_to_cpu(cmd.header.out.return_code);
+ if (rc != TPM2_RC_TESTING)
+ break;
+
+ msleep(delay_msec);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tpm2_do_selftest);
--
2.1.0

2014-10-07 17:50:30

by Jason Gunthorpe

[permalink] [raw]
Subject: Re: [tpmdd-devel] [PATCH v2 2/7] tpm: two-phase chip management functions

On Tue, Oct 07, 2014 at 08:01:12PM +0300, Jarkko Sakkinen wrote:
> Added tpm_chip_alloc() and tpm_chip_register() where tpm_chip_alloc()
> reserves memory resources and tpm_chip_register() initializes the
> device driver. This way it is possible to alter struct tpm_chip
> attributes before passing it to tpm_chip_register().

This looks broadly reasonable to me

Please add a note to the commit that this is known to still have
problems with resource reference counting, but they are less severe
than what existed before, and this is only an interm step.

> +/**
> + * tpm_chip_alloc() - allocate a new struct tpm_chip instance

This is using devm so it should be called 'tpmm_chip_alloc()' for
clarity


I know that was there before, but it sure is racy:

> + chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES);
[..]
> + set_bit(chip->dev_num, dev_mask);

Someday it should use IDR.


> @@ -896,18 +872,7 @@ void tpm_remove_hardware(struct device *dev)
> 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);
> + tpm_chip_unregister(chip);
> }
> EXPORT_SYMBOL_GPL(tpm_remove_hardware);

This can move to tpm-chip too, same with tpm_register_hardware

> @@ -714,15 +709,10 @@ static int tpm_tis_i2c_remove(struct i2c_client *client)
> struct tpm_chip *chip = tpm_dev.chip;
> release_locality(chip, chip->vendor.locality, 1);
>
> - /* close file handles */
> - tpm_dev_vendor_release(chip);
> -
> /* remove hardware */
> tpm_remove_hardware(chip->dev);

Wrong ordering here, tpm_remove_hardware should always be first -
drivers should not tear down internal state before calling it, so
release_locality should be second.

Noting that since we use devm the kfree will not happen until
remove returns, so the chip pointer is still valid.

> /* reset these pointers, otherwise we oops */
> - chip->dev->release = NULL;
> - chip->release = NULL;
> tpm_dev.client = NULL;

The comment can go too

Note: tpm_dev should be driver private data, but that is not your
problem..

Did you test compile all the drivers? One of my git commits on github
has some hackery to make that possible on x86.

Jason

2014-10-07 17:53:39

by Jason Gunthorpe

[permalink] [raw]
Subject: Re: [tpmdd-devel] [PATCH v2 3/7] tpm: clean up tpm_tis driver life-cycle

On Tue, Oct 07, 2014 at 08:01:13PM +0300, Jarkko Sakkinen wrote:

> + chip = tpm_chip_alloc(dev, &tpm_tis);
> + if (!chip)
> return -ENODEV;

Needs to use ERR_PTR

> + rc = tpm_chip_register(chip);
> + if (rc)
> + return -ENODEV;

Wrong ordering, this needs to be last in the probe function

Return rc not -ENODEV

> +static void tpm_tis_chip_remove(struct tpm_chip *chip)
> +{
> + iowrite32(~TPM_GLOBAL_INT_ENABLE &
> + ioread32(chip->vendor.iobase +
> + TPM_INT_ENABLE(chip->vendor.
> + locality)),
> + chip->vendor.iobase +
> + TPM_INT_ENABLE(chip->vendor.locality));
> + release_locality(chip, chip->vendor.locality, 1);
> + if (chip->vendor.irq)
> + free_irq(chip->vendor.irq, chip);
> +
> + tpm_chip_unregister(chip);
> +}

Wrong ordering, tpm_chip_unregister needs to be first

> + chip = dev_get_drvdata(&pdev->dev);
> + tpm_tis_chip_remove(chip);
> platform_device_unregister(pdev);

I'm under the impression devm does not work outside a device driver
context, so adding devm breaks force mode in this driver. Do you see
differently?

AFAIK the two options are to fix force mode so that it attaches the
dummy platform driver (that is what it is for after all) or remove
force mode.

Jason

2014-10-07 17:54:44

by Jason Gunthorpe

[permalink] [raw]
Subject: Re: [tpmdd-devel] [PATCH v2 6/7] tpm: TPM 2.0 CRB Interface

On Tue, Oct 07, 2014 at 08:01:16PM +0300, Jarkko Sakkinen wrote:

> + rc = tpm_chip_register(chip);
> + if (rc)
> + return -ENODEV;

Again, wrong order, this needs to be last in the probe function

Jason

2014-10-07 18:04:26

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [tpmdd-devel] [PATCH v2 2/7] tpm: two-phase chip management functions

Hi

And thanks for the feedback. Change requests look very reasonable.

On Tue, Oct 07, 2014 at 11:50:17AM -0600, Jason Gunthorpe wrote:
> On Tue, Oct 07, 2014 at 08:01:12PM +0300, Jarkko Sakkinen wrote:
> > Added tpm_chip_alloc() and tpm_chip_register() where tpm_chip_alloc()
> > reserves memory resources and tpm_chip_register() initializes the
> > device driver. This way it is possible to alter struct tpm_chip
> > attributes before passing it to tpm_chip_register().
>
> This looks broadly reasonable to me
>
> Please add a note to the commit that this is known to still have
> problems with resource reference counting, but they are less severe
> than what existed before, and this is only an interm step.
>
> > +/**
> > + * tpm_chip_alloc() - allocate a new struct tpm_chip instance
>
> This is using devm so it should be called 'tpmm_chip_alloc()' for
> clarity
>
>
> I know that was there before, but it sure is racy:
>
> > + chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES);
> [..]
> > + set_bit(chip->dev_num, dev_mask);
>
> Someday it should use IDR.
>
>
> > @@ -896,18 +872,7 @@ void tpm_remove_hardware(struct device *dev)
> > 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);
> > + tpm_chip_unregister(chip);
> > }
> > EXPORT_SYMBOL_GPL(tpm_remove_hardware);
>
> This can move to tpm-chip too, same with tpm_register_hardware
>
> > @@ -714,15 +709,10 @@ static int tpm_tis_i2c_remove(struct i2c_client *client)
> > struct tpm_chip *chip = tpm_dev.chip;
> > release_locality(chip, chip->vendor.locality, 1);
> >
> > - /* close file handles */
> > - tpm_dev_vendor_release(chip);
> > -
> > /* remove hardware */
> > tpm_remove_hardware(chip->dev);
>
> Wrong ordering here, tpm_remove_hardware should always be first -
> drivers should not tear down internal state before calling it, so
> release_locality should be second.
>
> Noting that since we use devm the kfree will not happen until
> remove returns, so the chip pointer is still valid.
>
> > /* reset these pointers, otherwise we oops */
> > - chip->dev->release = NULL;
> > - chip->release = NULL;
> > tpm_dev.client = NULL;
>
> The comment can go too
>
> Note: tpm_dev should be driver private data, but that is not your
> problem..
>
> Did you test compile all the drivers? One of my git commits on github
> has some hackery to make that possible on x86.

Yeah, I compiled all the drivers:

$ grep CONFIG_TCG .config
CONFIG_TCG_TPM=m
CONFIG_TCG_TIS=m
CONFIG_TCG_TIS_I2C_ATMEL=m
CONFIG_TCG_TIS_I2C_INFINEON=m
CONFIG_TCG_TIS_I2C_NUVOTON=m
CONFIG_TCG_NSC=m
CONFIG_TCG_ATMEL=m
CONFIG_TCG_INFINEON=m
CONFIG_TCG_ST33_I2C=m
CONFIG_TCG_XEN=m
CONFIG_TCG_CRB=m

> Jason

/Jarkko

2014-10-07 18:28:06

by Jason Gunthorpe

[permalink] [raw]
Subject: Re: [tpmdd-devel] [PATCH v2 2/7] tpm: two-phase chip management functions

On Tue, Oct 07, 2014 at 09:04:17PM +0300, Jarkko Sakkinen wrote:

> > Did you test compile all the drivers? One of my git commits on github
> > has some hackery to make that possible on x86.
>
> Yeah, I compiled all the drivers:
>
> $ grep CONFIG_TCG .config
> CONFIG_TCG_TPM=m
> CONFIG_TCG_TIS=m
> CONFIG_TCG_TIS_I2C_ATMEL=m
> CONFIG_TCG_TIS_I2C_INFINEON=m
> CONFIG_TCG_TIS_I2C_NUVOTON=m
> CONFIG_TCG_NSC=m
> CONFIG_TCG_ATMEL=m
> CONFIG_TCG_INFINEON=m
> CONFIG_TCG_ST33_I2C=m
> CONFIG_TCG_XEN=m
> CONFIG_TCG_CRB=m

CONFIG_TCG_IBMVTPM is missing, apply this:

https://github.com/jgunthorpe/linux/commit/74afd69de603783858283f9b337e1694988964f8

Jason

2014-10-07 19:15:49

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [tpmdd-devel] [PATCH v2 2/7] tpm: two-phase chip management functions

On Tue, Oct 07, 2014 at 12:27:56PM -0600, Jason Gunthorpe wrote:
> On Tue, Oct 07, 2014 at 09:04:17PM +0300, Jarkko Sakkinen wrote:
>
> > > Did you test compile all the drivers? One of my git commits on github
> > > has some hackery to make that possible on x86.
> >
> > Yeah, I compiled all the drivers:
> >
> > $ grep CONFIG_TCG .config
> > CONFIG_TCG_TPM=m
> > CONFIG_TCG_TIS=m
> > CONFIG_TCG_TIS_I2C_ATMEL=m
> > CONFIG_TCG_TIS_I2C_INFINEON=m
> > CONFIG_TCG_TIS_I2C_NUVOTON=m
> > CONFIG_TCG_NSC=m
> > CONFIG_TCG_ATMEL=m
> > CONFIG_TCG_INFINEON=m
> > CONFIG_TCG_ST33_I2C=m
> > CONFIG_TCG_XEN=m
> > CONFIG_TCG_CRB=m
>
> CONFIG_TCG_IBMVTPM is missing, apply this:
>
> https://github.com/jgunthorpe/linux/commit/74afd69de603783858283f9b337e1694988964f8

Oops, how did that go unnoticed. Will do.

> Jason

/Jarkko

2014-10-07 22:29:05

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [tpmdd-devel] [PATCH v2 2/7] tpm: two-phase chip management functions

On Tue, Oct 07, 2014 at 11:50:17AM -0600, Jason Gunthorpe wrote:
> On Tue, Oct 07, 2014 at 08:01:12PM +0300, Jarkko Sakkinen wrote:
> > Added tpm_chip_alloc() and tpm_chip_register() where tpm_chip_alloc()
> > reserves memory resources and tpm_chip_register() initializes the
> > device driver. This way it is possible to alter struct tpm_chip
> > attributes before passing it to tpm_chip_register().
>
> This looks broadly reasonable to me
>
> Please add a note to the commit that this is known to still have
> problems with resource reference counting, but they are less severe
> than what existed before, and this is only an interm step.
>
> > +/**
> > + * tpm_chip_alloc() - allocate a new struct tpm_chip instance
>
> This is using devm so it should be called 'tpmm_chip_alloc()' for
> clarity
>
>
> I know that was there before, but it sure is racy:
>
> > + chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES);
> [..]
> > + set_bit(chip->dev_num, dev_mask);
>
> Someday it should use IDR.
>
>
> > @@ -896,18 +872,7 @@ void tpm_remove_hardware(struct device *dev)
> > 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);
> > + tpm_chip_unregister(chip);
> > }
> > EXPORT_SYMBOL_GPL(tpm_remove_hardware);
>
> This can move to tpm-chip too, same with tpm_register_hardware
>
> > @@ -714,15 +709,10 @@ static int tpm_tis_i2c_remove(struct i2c_client *client)
> > struct tpm_chip *chip = tpm_dev.chip;
> > release_locality(chip, chip->vendor.locality, 1);
> >
> > - /* close file handles */
> > - tpm_dev_vendor_release(chip);
> > -
> > /* remove hardware */
> > tpm_remove_hardware(chip->dev);
>
> Wrong ordering here, tpm_remove_hardware should always be first -
> drivers should not tear down internal state before calling it, so
> release_locality should be second.
>
> Noting that since we use devm the kfree will not happen until
> remove returns, so the chip pointer is still valid.

Should I fix this ordering? I was thinking to focus putting proper
patterns in place only in tpm_tis and tpm_crb because they are the
that I'm able to test easily and then they can work as guideline for
other drivers.

> > /* reset these pointers, otherwise we oops */
> > - chip->dev->release = NULL;
> > - chip->release = NULL;
> > tpm_dev.client = NULL;
>
> The comment can go too
>
> Note: tpm_dev should be driver private data, but that is not your
> problem..
>
> Did you test compile all the drivers? One of my git commits on github
> has some hackery to make that possible on x86.
>
> Jason

/Jarkko

2014-10-07 22:34:49

by Jason Gunthorpe

[permalink] [raw]
Subject: Re: [tpmdd-devel] [PATCH v2 2/7] tpm: two-phase chip management functions

On Wed, Oct 08, 2014 at 01:28:14AM +0300, Jarkko Sakkinen wrote:

> > > @@ -714,15 +709,10 @@ static int tpm_tis_i2c_remove(struct i2c_client *client)
> > > struct tpm_chip *chip = tpm_dev.chip;
> > > release_locality(chip, chip->vendor.locality, 1);
> > >
> > > - /* close file handles */
> > > - tpm_dev_vendor_release(chip);
> > > -
> > > /* remove hardware */
> > > tpm_remove_hardware(chip->dev);
> >
> > Wrong ordering here, tpm_remove_hardware should always be first -
> > drivers should not tear down internal state before calling it, so
> > release_locality should be second.
> >
> > Noting that since we use devm the kfree will not happen until
> > remove returns, so the chip pointer is still valid.
>
> Should I fix this ordering? I was thinking to focus putting proper
> patterns in place only in tpm_tis and tpm_crb because they are the
> that I'm able to test easily and then they can work as guideline for
> other drivers.

I think since this patch is already touching this function there is
no reason not to make it be correct (especially since it was noticed)

The rest can wait till we globally replace tpm_remove_hardware with
tpm_unregister - at that time the ordering can be audited and
checked.

Then the drivers will be clean and the core can finally be fixed.

Jason

2014-10-09 09:07:57

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [tpmdd-devel] [PATCH v2 2/7] tpm: two-phase chip management functions

On Tue, Oct 07, 2014 at 04:34:42PM -0600, Jason Gunthorpe wrote:
> On Wed, Oct 08, 2014 at 01:28:14AM +0300, Jarkko Sakkinen wrote:
>
> > > > @@ -714,15 +709,10 @@ static int tpm_tis_i2c_remove(struct i2c_client *client)
> > > > struct tpm_chip *chip = tpm_dev.chip;
> > > > release_locality(chip, chip->vendor.locality, 1);
> > > >
> > > > - /* close file handles */
> > > > - tpm_dev_vendor_release(chip);
> > > > -
> > > > /* remove hardware */
> > > > tpm_remove_hardware(chip->dev);
> > >
> > > Wrong ordering here, tpm_remove_hardware should always be first -
> > > drivers should not tear down internal state before calling it, so
> > > release_locality should be second.
> > >
> > > Noting that since we use devm the kfree will not happen until
> > > remove returns, so the chip pointer is still valid.
> >
> > Should I fix this ordering? I was thinking to focus putting proper
> > patterns in place only in tpm_tis and tpm_crb because they are the
> > that I'm able to test easily and then they can work as guideline for
> > other drivers.
>
> I think since this patch is already touching this function there is
> no reason not to make it be correct (especially since it was noticed)
>
> The rest can wait till we globally replace tpm_remove_hardware with
> tpm_unregister - at that time the ordering can be audited and
> checked.
>
> Then the drivers will be clean and the core can finally be fixed.

This makes sense. I'll also document this. And I decided to completely
wipe old tpm_register/remove_hardware() completely from v3 because they
only cause confusion.

I pushed patch that should implement fix for the ordering into tpm2-v2
branch:

https://github.com/jsakkine/linux-tpm2/commit/63ab650fa6f8dddd95100869e50275801d7d9360

> Jason

/Jarkko