tpm: TPM 2.0 baseline support
authorJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Fri, 12 Dec 2014 19:46:38 +0000 (11:46 -0800)
committerPeter Huewe <peterhuewe@gmx.de>
Sat, 17 Jan 2015 13:00:11 +0000 (14:00 +0100)
TPM 2.0 devices are separated by adding a field 'flags' to struct
tpm_chip and defining a flag TPM_CHIP_FLAG_TPM2 for tagging them.

This patch adds the following internal functions:

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

Additionally, the following exported functions are implemented for
implementing TPM 2.0 device drivers:

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

The existing functions that are exported for the use for existing
subsystems have been changed to check the flags field in struct
tpm_chip and use appropriate TPM 2.0 counterpart if
TPM_CHIP_FLAG_TPM2 is est.

The code for tpm2_calc_ordinal_duration() and tpm2_startup() were
originally written by Will Arthur.

Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Will Arthur <will.c.arthur@intel.com>
Reviewed-by: Jasob Gunthorpe <jason.gunthorpe@obsidianresearch.com>
Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Reviewed-by: Peter Huewe <peterhuewe@gmx.de>
Tested-by: Peter Huewe <peterhuewe@gmx.de>
[phuewe: Fixed copy paste error * 2]
Signed-off-by: Peter Huewe <peterhuewe@gmx.de>
drivers/char/tpm/Makefile
drivers/char/tpm/tpm-chip.c
drivers/char/tpm/tpm-interface.c
drivers/char/tpm/tpm.h
drivers/char/tpm/tpm2-cmd.c [new file with mode: 0644]

index c715596..88848ed 100644 (file)
@@ -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
index 7596eef..6459af7 100644 (file)
@@ -195,15 +195,18 @@ int tpm_chip_register(struct tpm_chip *chip)
        if (rc)
                return rc;
 
-       rc = tpm_sysfs_add_device(chip);
-       if (rc)
-               goto del_misc;
+       /* Populate sysfs for TPM1 devices. */
+       if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
+               rc = tpm_sysfs_add_device(chip);
+               if (rc)
+                       goto del_misc;
 
-       rc = tpm_add_ppi(chip);
-       if (rc)
-               goto del_sysfs;
+               rc = tpm_add_ppi(chip);
+               if (rc)
+                       goto del_sysfs;
 
-       chip->bios_dir = tpm_bios_log_setup(chip->devname);
+               chip->bios_dir = tpm_bios_log_setup(chip->devname);
+       }
 
        /* Make the chip available. */
        spin_lock(&driver_lock);
@@ -241,10 +244,12 @@ void tpm_chip_unregister(struct tpm_chip *chip)
        spin_unlock(&driver_lock);
        synchronize_rcu();
 
-       if (chip->bios_dir)
-               tpm_bios_log_teardown(chip->bios_dir);
-       tpm_remove_ppi(chip);
-       tpm_sysfs_del_device(chip);
+       if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
+               if (chip->bios_dir)
+                       tpm_bios_log_teardown(chip->bios_dir);
+               tpm_remove_ppi(chip);
+               tpm_sysfs_del_device(chip);
+       }
 
        tpm_dev_del_device(chip);
 }
index b6f6b17..20cf94d 100644 (file)
@@ -360,7 +360,10 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
        if (chip->vendor.irq)
                goto out_recv;
 
-       stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
+       if (chip->flags & TPM_CHIP_FLAG_TPM2)
+               stop = jiffies + tpm2_calc_ordinal_duration(chip, ordinal);
+       else
+               stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
        do {
                u8 status = chip->ops->status(chip);
                if ((status & chip->ops->req_complete_mask) ==
@@ -484,6 +487,7 @@ 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");
@@ -680,7 +684,10 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf)
        chip = tpm_chip_find_get(chip_num);
        if (chip == NULL)
                return -ENODEV;
-       rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf);
+       if (chip->flags & TPM_CHIP_FLAG_TPM2)
+               rc = tpm2_pcr_read(chip, pcr_idx, res_buf);
+       else
+               rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf);
        tpm_chip_put(chip);
        return rc;
 }
@@ -714,6 +721,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);
@@ -974,6 +987,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);
index d46765b..cc421cf 100644 (file)
@@ -62,6 +62,59 @@ 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,
+       TPM2_TIMEOUT_B          = 2000,
+       TPM2_TIMEOUT_C          = 200,
+       TPM2_TIMEOUT_D          = 30,
+       TPM2_DURATION_SHORT     = 20,
+       TPM2_DURATION_MEDIUM    = 750,
+       TPM2_DURATION_LONG      = 2000,
+};
+
+enum tpm2_structures {
+       TPM2_ST_NO_SESSIONS     = 0x8001,
+       TPM2_ST_SESSIONS        = 0x8002,
+};
+
+enum tpm2_return_codes {
+       TPM2_RC_INITIALIZE      = 0x0100,
+       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_SHUTDOWN        = 0x0145,
+       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 {
@@ -99,6 +152,7 @@ struct tpm_vendor_specific {
 enum tpm_chip_flags {
        TPM_CHIP_FLAG_REGISTERED        = BIT(0),
        TPM_CHIP_FLAG_PPI               = BIT(1),
+       TPM_CHIP_FLAG_TPM2              = BIT(2),
 };
 
 struct tpm_chip {
@@ -370,3 +424,15 @@ static inline void tpm_remove_ppi(struct tpm_chip *chip)
 {
 }
 #endif
+
+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);
+ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
+                       u32 *value, const char *desc);
+
+extern int tpm2_startup(struct tpm_chip *chip, u16 startup_type);
+extern int tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type);
+extern unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *, u32);
+extern int tpm2_do_selftest(struct tpm_chip *chip);
+extern int tpm2_gen_interrupt(struct tpm_chip *chip, bool quiet);
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
new file mode 100644 (file)
index 0000000..1abe650
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Authors:
+ * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
+ *
+ * Maintained by: <tpmdd-devel@lists.sourceforge.net>
+ *
+ * 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 */
+};
+
+#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 successful. If a negative number is
+ * returned it remarks a POSIX error code. If 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;
+
+       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;
+
+       memset(cmd.params.pcrread_in.pcr_select, 0,
+              sizeof(cmd.params.pcrread_in.pcr_select));
+       cmd.params.pcrread_in.pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
+
+       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;
+}
+
+#define TPM2_GET_PCREXTEND_IN_SIZE \
+       (sizeof(struct tpm_input_header) + \
+        sizeof(struct tpm2_pcr_extend_in))
+
+static const struct tpm_input_header tpm2_pcrextend_header = {
+       .tag = cpu_to_be16(TPM2_ST_SESSIONS),
+       .length = cpu_to_be32(TPM2_GET_PCREXTEND_IN_SIZE),
+       .ordinal = cpu_to_be32(TPM2_CC_PCR_EXTEND)
+};
+
+/**
+ * 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 successful. If a negative number is
+ * returned it remarks a POSIX error code. If a positive number is returned
+ * it remarks a TPM error.
+ */
+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;
+}
+
+#define TPM2_GETRANDOM_IN_SIZE \
+       (sizeof(struct tpm_input_header) + \
+        sizeof(struct tpm2_get_random_in))
+
+static const struct tpm_input_header tpm2_getrandom_header = {
+       .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+       .length = cpu_to_be32(TPM2_GETRANDOM_IN_SIZE),
+       .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 successful. If a negative number is
+ * returned it remarks a POSIX error code. If 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;
+       u32 num_bytes;
+       int err;
+       int total = 0;
+       int retries = 5;
+       u8 *dest = out;
+
+       num_bytes = min_t(u32, max, sizeof(cmd.params.getrandom_out.buffer));
+
+       if (!out || !num_bytes ||
+           max > sizeof(cmd.params.getrandom_out.buffer))
+               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 = min_t(u32, be16_to_cpu(cmd.params.getrandom_out.size),
+                            num_bytes);
+               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 successful. If a negative number is
+ * returned it remarks a POSIX error code. If 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 = cpu_to_be32(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;
+}
+
+#define TPM2_STARTUP_IN_SIZE \
+       (sizeof(struct tpm_input_header) + \
+        sizeof(struct tpm2_startup_in))
+
+static const struct tpm_input_header tpm2_startup_header = {
+       .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+       .length = cpu_to_be32(TPM2_STARTUP_IN_SIZE),
+       .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 successful. If a negative number is
+ * returned it remarks a POSIX error code. If a positive number is returned
+ * it remarks a TPM error.
+ */
+int tpm2_startup(struct tpm_chip *chip, u16 startup_type)
+{
+       struct tpm2_cmd cmd;
+
+       cmd.header.in = tpm2_startup_header;
+
+       cmd.params.startup_in.startup_type = cpu_to_be16(startup_type);
+       return tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+                               "attempting to start the TPM");
+}
+EXPORT_SYMBOL_GPL(tpm2_startup);
+
+#define TPM2_SHUTDOWN_IN_SIZE \
+       (sizeof(struct tpm_input_header) + \
+        sizeof(struct tpm2_startup_in))
+
+static const struct tpm_input_header tpm2_shutdown_header = {
+       .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+       .length = cpu_to_be32(TPM2_SHUTDOWN_IN_SIZE),
+       .ordinal = cpu_to_be32(TPM2_CC_SHUTDOWN)
+};
+
+/**
+ * tpm2_shutdown() - send shutdown command to the TPM chip
+ * @chip:              TPM chip to use.
+ * @shutdown_type      shutdown type. The value is either
+ *                     TPM_SU_CLEAR or TPM_SU_STATE.
+ *
+ * 0 is returned when the operation is successful. If a negative number is
+ * returned it remarks a POSIX error code. If a positive number is returned
+ * it remarks a TPM error.
+ */
+int tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
+{
+       struct tpm2_cmd cmd;
+
+       cmd.header.in = tpm2_shutdown_header;
+
+       cmd.params.startup_in.startup_type = cpu_to_be16(shutdown_type);
+       return tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+                               "stopping the TPM");
+}
+EXPORT_SYMBOL_GPL(tpm2_shutdown);
+
+/*
+ * 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 successful. If a negative number is
+ * returned it remarks a POSIX error code. If 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)
+               duration = 2 * 60 * HZ;
+
+       return duration;
+}
+EXPORT_SYMBOL_GPL(tpm2_calc_ordinal_duration);
+
+#define TPM2_SELF_TEST_IN_SIZE \
+       (sizeof(struct tpm_input_header) + \
+        sizeof(struct tpm2_self_test_in))
+
+static const struct tpm_input_header tpm2_selftest_header = {
+       .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+       .length = cpu_to_be32(TPM2_SELF_TEST_IN_SIZE),
+       .ordinal = cpu_to_be32(TPM2_CC_SELF_TEST)
+};
+
+/**
+ * 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 successful. If a negative number is
+ * returned it remarks a POSIX error code. If 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");
+
+       /* At least some prototype chips seem to give RC_TESTING error
+        * immediately. This is a workaround for that.
+        */
+       if (rc == TPM2_RC_TESTING) {
+               dev_warn(chip->pdev, "Got RC_TESTING, ignoring\n");
+               rc = 0;
+       }
+
+       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 successfully.
+ *
+ * 0 is returned when the operation is successful. If a negative number is
+ * returned it remarks a POSIX error code. If 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);
+
+/**
+ * tpm2_gen_interrupt() - generate an interrupt
+ * @chip: TPM chip to use
+ * @quiet: surpress the error message
+ *
+ * 0 is returned when the operation is successful. If a negative number is
+ * returned it remarks a POSIX error code. If a positive number is returned
+ * it remarks a TPM error.
+ */
+int tpm2_gen_interrupt(struct tpm_chip *chip, bool quiet)
+{
+       const char *desc = NULL;
+       u32 dummy;
+
+       if (!quiet)
+               desc = "attempting to generate an interrupt";
+
+       return tpm2_get_tpm_pt(chip, TPM2_CAP_TPM_PROPERTIES, &dummy, desc);
+}
+EXPORT_SYMBOL_GPL(tpm2_gen_interrupt);