Implemented sysfs attributes for TPM2 devices. TPM2 sysfs attributes
are mounted in the actual device associated with the chip instead of
platform device like with TPM1 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 |  72 +++++++
 drivers/char/tpm/Makefile                 |   2 +-
 drivers/char/tpm/tpm-chip.c               |  12 +-
 drivers/char/tpm/tpm.h                    |  24 +++
 drivers/char/tpm/tpm2-sysfs.c             | 314 ++++++++++++++++++++++++++++++
 5 files changed, 421 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..b3d20a4
--- /dev/null
+++ b/Documentation/ABI/stable/sysfs-class-tpm2
@@ -0,0 +1,72 @@
+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/sh_enabled
+Date:          October 2014
+KernelVersion: 3.19
+Contact:       [email protected]
+Description:   The "sh_enabled" property prints a '1' if the Storage Hierarchy
+               is enabled, i.e. if PM_PT_STARTUP_CLEAR.shEnable is set.
+
+What:          /sys/class/misc/tpmX/device/sh_owned
+Date:          October 2014
+KernelVersion: 3.19
+Contact:       [email protected]
+Description:   The "sh_owned" property prints a '1' if the ownership of the
+               Storage Hierarchy has been taken, i.e. if
+               TPM_PT_PERMANENT.ownerAuthSet is set.
+
+What:          /sys/class/misc/tpmX/device/eh_enabled
+Date:          October 2014
+KernelVersion: 3.19
+Contact:       [email protected]
+Description:   The "eh_enabled" property prints a '1' if the Endorsement
+               Hierarchy is enabled, i.e if PM_PT_STARTUP_CLEAR.ehEnable is
+               set.
+
+What:          /sys/class/misc/tpmX/device/eh_owned
+Date:          October 2014
+KernelVersion: 3.19
+Contact:       [email protected]
+Description:   The "eh_owned" property prints a '1' if the ownership of the
+               Endrosoment Hierarchy has been taken, i.e if
+               TPM_PT_PERMANENT.endorsementAuthSet is set.
+
+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 3bb0d9f..5fac0a8 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -140,9 +140,13 @@ int tpm_chip_register(struct tpm_chip *chip)
                return rc;
 
        if (chip->flags & TPM_CHIP_FLAG_TPM2) {
-               rc = tpm_add_ppi(&chip->vendor.miscdev.this_device->kobj);
+               rc = tpm2_sysfs_add_device(chip);
                if (rc)
                        goto del_misc;
+
+               rc = tpm_add_ppi(&chip->vendor.miscdev.this_device->kobj);
+               if (rc)
+                       goto del_sysfs;
        } else {
                rc = tpm_sysfs_add_device(chip);
                if (rc)
@@ -162,7 +166,10 @@ int tpm_chip_register(struct tpm_chip *chip)
 
        return 0;
 del_sysfs:
-       tpm_sysfs_del_device(chip);
+       if (chip->flags & TPM_CHIP_FLAG_TPM2)
+               tpm2_sysfs_del_device(chip);
+       else
+               tpm_sysfs_del_device(chip);
 del_misc:
        tpm_dev_del_device(chip);
        return rc;
@@ -190,6 +197,7 @@ void tpm_chip_unregister(struct tpm_chip *chip)
 
        if (chip->flags & TPM_CHIP_FLAG_TPM2) {
                tpm_remove_ppi(&chip->vendor.miscdev.this_device->kobj);
+               tpm2_sysfs_del_device(chip);
        } else {
                tpm_bios_log_teardown(chip->bios_dir);
                tpm_remove_ppi(&chip->dev->kobj);
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index b1de902..9f1f146 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;
 };
 
@@ -416,3 +437,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 tpm_chip *chip);
+void tpm2_sysfs_del_device(struct tpm_chip *chip);
diff --git a/drivers/char/tpm/tpm2-sysfs.c b/drivers/char/tpm/tpm2-sysfs.c
new file mode 100644
index 0000000..e502032
--- /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 sh_enabled_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(sh_enabled);
+
+static ssize_t sh_owned_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(sh_owned);
+
+static ssize_t eh_enabled_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(eh_enabled);
+
+static ssize_t eh_owned_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(eh_owned);
+
+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_sh_enabled.attr,
+       &dev_attr_sh_owned.attr,
+       &dev_attr_eh_enabled.attr,
+       &dev_attr_eh_owned.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 tpm_chip *chip)
+{
+       struct pcr_bank *pcr_bank;
+       struct kobject *pcrs_kobj;
+       struct device *dev = chip->dev;
+       int rc;
+
+       rc = sysfs_create_group(&chip->vendor.miscdev.this_device->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",
+                                          
&chip->vendor.miscdev.this_device->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(chip->vendor.miscdev.this_device,
+                                  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 tpm_chip *chip)
+{
+       kobject_put(chip->sha1_bank);
+       kobject_put(chip->pcrs_kobj);
+       sysfs_remove_group(&chip->vendor.miscdev.this_device->kobj,
+                          &tpm_dev_group);
+}
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to