Hi James,

Please pull from this new branch and ignore the 7-25-12 branch. This
new branch includes fixes for comments by hpa. I've also included one
additional patch from [1] to close a race and prevent possibly sensitive
data from being free'd before being zeroed. I'm attaching this entire
diff here since my fixes for hpa's comments aren't public yet.

Thanks,
Kent

[1] https://lkml.org/lkml/2012/7/25/431

The following changes since commit 663728418e3494f8e4a82f5d1b2f23c22d11be35:

  Smack: Maintainer Record (2012-07-13 15:59:44 -0700)

are available in the git repository at:
  git://github.com/shpedoikal/linux.git tpmdd-hpa-fixes-7-27-12

Bryan Freed (1):
      CHROMIUM: tpm: tpm_i2c_infineon: Lock the I2C adapter for a sequence of 
requests.

Kent Yoder (4):
      tpm: modularize event log collection
      tpm: Move tpm_get_random api into the TPM device driver
      hw_random: add support for the TPM chip as a hardware RNG source
      tpm: fix double write race and tpm_release free issue

Peter Huewe (1):
      char/tpm: Add new driver for Infineon I2C TIS TPM

 drivers/char/hw_random/Kconfig                  |   13 +
 drivers/char/hw_random/Makefile                 |    1 +
 drivers/char/hw_random/tpm-rng.c                |   55 ++
 drivers/char/tpm/Kconfig                        |   11 +
 drivers/char/tpm/Makefile                       |    2 +
 drivers/char/tpm/tpm.c                          |   68 ++-
 drivers/char/tpm/tpm.h                          |   23 +
 drivers/char/tpm/tpm_acpi.c                     |  104 ++++
 drivers/char/tpm/{tpm_bios.c => tpm_eventlog.c} |  147 +-----
 drivers/char/tpm/tpm_eventlog.h                 |   71 +++
 drivers/char/tpm/tpm_i2c_infineon.c             |  748 +++++++++++++++++++++++
 include/linux/tpm.h                             |    4 +
 security/keys/trusted.c                         |   48 +--
 13 files changed, 1108 insertions(+), 187 deletions(-)
 create mode 100644 drivers/char/hw_random/tpm-rng.c
 create mode 100644 drivers/char/tpm/tpm_acpi.c
 rename drivers/char/tpm/{tpm_bios.c => tpm_eventlog.c} (75%)
 create mode 100644 drivers/char/tpm/tpm_eventlog.h
 create mode 100644 drivers/char/tpm/tpm_i2c_infineon.c

diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index f45dad3..4e8c01a 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -263,3 +263,16 @@ config HW_RANDOM_PSERIES
          module will be called pseries-rng.
 
          If unsure, say Y.
+
+config HW_RANDOM_TPM
+       tristate "TPM HW Random Number Generator support"
+       depends on HW_RANDOM && TCG_TPM
+       default HW_RANDOM
+       ---help---
+         This driver provides kernel-side support for the Random Number
+         Generator in the Trusted Platform Module
+
+         To compile this driver as a module, choose M here: the
+         module will be called tpm-rng.
+
+         If unsure, say Y.
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index d901dfa..5dc10da 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o
 obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o
 obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o
 obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o
+obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o
diff --git a/drivers/char/hw_random/tpm-rng.c b/drivers/char/hw_random/tpm-rng.c
new file mode 100644
index 0000000..a869b42c
--- /dev/null
+++ b/drivers/char/hw_random/tpm-rng.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 Kent Yoder IBM Corporation
+ *
+ * HWRNG interfaces to pull RNG data from a TPM
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/hw_random.h>
+#include <linux/tpm.h>
+
+#define MODULE_NAME "tpm-rng"
+
+static int tpm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+       int err;
+       size_t tpm_max = max;
+
+       err = tpm_get_random(TPM_ANY_NUM, data, &tpm_max);
+
+       return err ? 0 : tpm_max;
+}
+
+static struct hwrng tpm_rng = {
+       .name = MODULE_NAME,
+       .read = tpm_rng_read,
+};
+
+static int __init rng_init(void)
+{
+       return hwrng_register(&tpm_rng);
+}
+module_init(rng_init);
+
+static void __exit rng_exit(void)
+{
+       hwrng_unregister(&tpm_rng);
+}
+module_exit(rng_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Kent Yoder <[email protected]>");
+MODULE_DESCRIPTION("RNG driver for TPM devices");
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index a048199..c4aac48 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -33,6 +33,17 @@ config TCG_TIS
          from within Linux.  To compile this driver as a module, choose
          M here; the module will be called tpm_tis.
 
+config TCG_TIS_I2C_INFINEON
+       tristate "TPM Interface Specification 1.2 Interface (I2C - Infineon)"
+       depends on I2C
+       ---help---
+         If you have a TPM security chip that is compliant with the
+         TCG TIS 1.2 TPM specification and Infineon's I2C Protocol Stack
+         Specification 0.20 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_tis_i2c_infineon.
+
 config TCG_NSC
        tristate "National Semiconductor TPM Interface"
        depends on X86
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index ea3a1e0..beac52f6 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -4,8 +4,10 @@
 obj-$(CONFIG_TCG_TPM) += tpm.o
 ifdef CONFIG_ACPI
        obj-$(CONFIG_TCG_TPM) += tpm_bios.o
+       tpm_bios-objs += tpm_eventlog.o tpm_acpi.o
 endif
 obj-$(CONFIG_TCG_TIS) += tpm_tis.o
+obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
 obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
 obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
 obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
index d39b1f6..fd6cab9 100644
--- a/drivers/char/tpm/tpm.c
+++ b/drivers/char/tpm/tpm.c
@@ -30,12 +30,7 @@
 #include <linux/freezer.h>
 
 #include "tpm.h"
-
-enum tpm_const {
-       TPM_MINOR = 224,        /* officially assigned */
-       TPM_BUFSIZE = 4096,
-       TPM_NUM_DEVICES = 256,
-};
+#include "tpm_eventlog.h"
 
 enum tpm_duration {
        TPM_SHORT = 0,
@@ -482,6 +477,7 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct 
tpm_cmd_t *cmd,
 #define TPM_INTERNAL_RESULT_SIZE 200
 #define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
 #define TPM_ORD_GET_CAP cpu_to_be32(101)
+#define TPM_ORD_GET_RANDOM cpu_to_be32(70)
 
 static const struct tpm_input_header tpm_getcap_header = {
        .tag = TPM_TAG_RQU_COMMAND,
@@ -1175,7 +1171,7 @@ int tpm_release(struct inode *inode, struct file *file)
        flush_work_sync(&chip->work);
        file->private_data = NULL;
        atomic_set(&chip->data_pending, 0);
-       kfree(chip->data_buffer);
+       kzfree(chip->data_buffer);
        clear_bit(0, &chip->is_open);
        put_device(chip->dev);
        return 0;
@@ -1227,7 +1223,6 @@ ssize_t tpm_read(struct file *file, char __user *buf,
        del_singleshot_timer_sync(&chip->user_read_timer);
        flush_work_sync(&chip->work);
        ret_size = atomic_read(&chip->data_pending);
-       atomic_set(&chip->data_pending, 0);
        if (ret_size > 0) {     /* relay data */
                ssize_t orig_ret_size = ret_size;
                if (size < ret_size)
@@ -1242,6 +1237,8 @@ ssize_t tpm_read(struct file *file, char __user *buf,
                mutex_unlock(&chip->buffer_mutex);
        }
 
+       atomic_set(&chip->data_pending, 0);
+
        return ret_size;
 }
 EXPORT_SYMBOL_GPL(tpm_read);
@@ -1326,6 +1323,61 @@ int tpm_pm_resume(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(tpm_pm_resume);
 
+#define TPM_GETRANDOM_RESULT_SIZE      18
+static struct tpm_input_header tpm_getrandom_header = {
+       .tag = TPM_TAG_RQU_COMMAND,
+       .length = cpu_to_be32(14),
+       .ordinal = TPM_ORD_GET_RANDOM
+};
+
+/**
+ * tpm_get_random() - Get random bytes from the tpm's RNG
+ * @chip_num: A specific chip number for the request or TPM_ANY_NUM
+ * @out: destination buffer for the random bytes
+ * @max: on input, the max number of bytes to write to @out, on output
+ *       this is set to the actual number of bytes written to @out
+ *
+ * Note that @max will be capped at TPM_MAX_RNG_DATA bytes.
+ */
+int tpm_get_random(u32 chip_num, u8 *out, size_t *max)
+{
+       struct tpm_chip *chip;
+       struct tpm_cmd_t tpm_cmd;
+       u32 recd, total = 0, num_bytes = min_t(u32, *max, TPM_MAX_RNG_DATA);
+       int err, retries = 5;
+       u8 *dest = out;
+
+       chip = tpm_chip_find_get(chip_num);
+       if (chip == NULL)
+               return -ENODEV;
+
+       if (!out || !num_bytes || *max > TPM_MAX_RNG_DATA)
+               return -EINVAL;
+
+       do {
+               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,
+                               TPM_GETRANDOM_RESULT_SIZE + num_bytes,
+                               "attempting get random");
+               if (err)
+                       goto out_err;
+
+               recd = be32_to_cpu(tpm_cmd.params.getrandom_out.rng_data_len);
+               memcpy(dest, tpm_cmd.params.getrandom_out.rng_data, recd);
+
+               dest += recd;
+               total += recd;
+               num_bytes -= recd;
+       } while (retries-- && total < *max);
+
+       err = ((total < *max) ? -EAGAIN : 0);
+out_err:
+       return err;
+}
+EXPORT_SYMBOL_GPL(tpm_get_random);
+
 /* In case vendor provided release function, call it too.*/
 
 void tpm_dev_vendor_release(struct tpm_chip *chip)
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index b1c5280..610fe42 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -28,6 +28,12 @@
 #include <linux/io.h>
 #include <linux/tpm.h>
 
+enum tpm_const {
+       TPM_MINOR = 224,        /* officially assigned */
+       TPM_BUFSIZE = 4096,
+       TPM_NUM_DEVICES = 256,
+};
+
 enum tpm_timeout {
        TPM_TIMEOUT = 5,        /* msecs */
 };
@@ -269,6 +275,21 @@ struct tpm_pcrextend_in {
        u8      hash[TPM_DIGEST_SIZE];
 }__attribute__((packed));
 
+/* 128 bytes is an arbitrary cap. This could be as large as TPM_BUFSIZE - 18
+ * bytes, but 128 is still a relatively large number of random bytes and
+ * anything much bigger causes users of struct tpm_cmd_t to start getting
+ * compiler warnings about stack frame size. */
+#define TPM_MAX_RNG_DATA       128
+
+struct tpm_getrandom_out {
+       __be32 rng_data_len;
+       u8     rng_data[TPM_MAX_RNG_DATA];
+}__attribute__((packed));
+
+struct tpm_getrandom_in {
+       __be32 num_bytes;
+}__attribute__((packed));
+
 typedef union {
        struct  tpm_getcap_params_out getcap_out;
        struct  tpm_readpubek_params_out readpubek_out;
@@ -277,6 +298,8 @@ typedef union {
        struct  tpm_pcrread_in  pcrread_in;
        struct  tpm_pcrread_out pcrread_out;
        struct  tpm_pcrextend_in pcrextend_in;
+       struct  tpm_getrandom_in getrandom_in;
+       struct  tpm_getrandom_out getrandom_out;
 } tpm_cmd_params;
 
 struct tpm_cmd_t {
diff --git a/drivers/char/tpm/tpm_acpi.c b/drivers/char/tpm/tpm_acpi.c
new file mode 100644
index 0000000..a1bb5a18
--- /dev/null
+++ b/drivers/char/tpm/tpm_acpi.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ *     Seiji Munetoh <[email protected]>
+ *     Stefan Berger <[email protected]>
+ *     Reiner Sailer <[email protected]>
+ *     Kylene Hall <[email protected]>
+ *
+ * Maintained by: <[email protected]>
+ *
+ * Access to the eventlog extended by the TCG BIOS of PC platform
+ *
+ * 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; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/seq_file.h>
+#include <linux/fs.h>
+#include <linux/security.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <acpi/acpi.h>
+
+#include "tpm.h"
+#include "tpm_eventlog.h"
+
+struct acpi_tcpa {
+       struct acpi_table_header hdr;
+       u16 platform_class;
+       union {
+               struct client_hdr {
+                       u32 log_max_len __attribute__ ((packed));
+                       u64 log_start_addr __attribute__ ((packed));
+               } client;
+               struct server_hdr {
+                       u16 reserved;
+                       u64 log_max_len __attribute__ ((packed));
+                       u64 log_start_addr __attribute__ ((packed));
+               } server;
+       };
+};
+
+/* read binary bios log */
+int read_log(struct tpm_bios_log *log)
+{
+       struct acpi_tcpa *buff;
+       acpi_status status;
+       struct acpi_table_header *virt;
+       u64 len, start;
+
+       if (log->bios_event_log != NULL) {
+               printk(KERN_ERR
+                      "%s: ERROR - Eventlog already initialized\n",
+                      __func__);
+               return -EFAULT;
+       }
+
+       /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */
+       status = acpi_get_table(ACPI_SIG_TCPA, 1,
+                               (struct acpi_table_header **)&buff);
+
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n",
+                      __func__);
+               return -EIO;
+       }
+
+       switch(buff->platform_class) {
+       case BIOS_SERVER:
+               len = buff->server.log_max_len;
+               start = buff->server.log_start_addr;
+               break;
+       case BIOS_CLIENT:
+       default:
+               len = buff->client.log_max_len;
+               start = buff->client.log_start_addr;
+               break;
+       }
+       if (!len) {
+               printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__);
+               return -EIO;
+       }
+
+       /* malloc EventLog space */
+       log->bios_event_log = kmalloc(len, GFP_KERNEL);
+       if (!log->bios_event_log) {
+               printk("%s: ERROR - Not enough  Memory for BIOS measurements\n",
+                       __func__);
+               return -ENOMEM;
+       }
+
+       log->bios_event_log_end = log->bios_event_log + len;
+
+       virt = acpi_os_map_memory(start, len);
+
+       memcpy(log->bios_event_log, virt, len);
+
+       acpi_os_unmap_memory(virt, len);
+       return 0;
+}
diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_eventlog.c
similarity index 75%
rename from drivers/char/tpm/tpm_bios.c
rename to drivers/char/tpm/tpm_eventlog.c
index 0636520..84ddc55 100644
--- a/drivers/char/tpm/tpm_bios.c
+++ b/drivers/char/tpm/tpm_eventlog.c
@@ -1,7 +1,8 @@
 /*
- * Copyright (C) 2005 IBM Corporation
+ * Copyright (C) 2005, 2012 IBM Corporation
  *
  * Authors:
+ *     Kent Yoder <[email protected]>
  *     Seiji Munetoh <[email protected]>
  *     Stefan Berger <[email protected]>
  *     Reiner Sailer <[email protected]>
@@ -9,7 +10,7 @@
  *
  * Maintained by: <[email protected]>
  *
- * Access to the eventlog extended by the TCG BIOS of PC platform
+ * Access to the eventlog created by a system's firmware / BIOS
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -23,67 +24,10 @@
 #include <linux/security.h>
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <acpi/acpi.h>
-#include "tpm.h"
-
-#define TCG_EVENT_NAME_LEN_MAX 255
-#define MAX_TEXT_EVENT         1000    /* Max event string length */
-#define ACPI_TCPA_SIG          "TCPA"  /* 0x41504354 /'TCPA' */
-
-enum bios_platform_class {
-       BIOS_CLIENT = 0x00,
-       BIOS_SERVER = 0x01,
-};
-
-struct tpm_bios_log {
-       void *bios_event_log;
-       void *bios_event_log_end;
-};
-
-struct acpi_tcpa {
-       struct acpi_table_header hdr;
-       u16 platform_class;
-       union {
-               struct client_hdr {
-                       u32 log_max_len __attribute__ ((packed));
-                       u64 log_start_addr __attribute__ ((packed));
-               } client;
-               struct server_hdr {
-                       u16 reserved;
-                       u64 log_max_len __attribute__ ((packed));
-                       u64 log_start_addr __attribute__ ((packed));
-               } server;
-       };
-};
 
-struct tcpa_event {
-       u32 pcr_index;
-       u32 event_type;
-       u8 pcr_value[20];       /* SHA1 */
-       u32 event_size;
-       u8 event_data[0];
-};
+#include "tpm.h"
+#include "tpm_eventlog.h"
 
-enum tcpa_event_types {
-       PREBOOT = 0,
-       POST_CODE,
-       UNUSED,
-       NO_ACTION,
-       SEPARATOR,
-       ACTION,
-       EVENT_TAG,
-       SCRTM_CONTENTS,
-       SCRTM_VERSION,
-       CPU_MICROCODE,
-       PLATFORM_CONFIG_FLAGS,
-       TABLE_OF_DEVICES,
-       COMPACT_HASH,
-       IPL,
-       IPL_PARTITION_DATA,
-       NONHOST_CODE,
-       NONHOST_CONFIG,
-       NONHOST_INFO,
-};
 
 static const char* tcpa_event_type_strings[] = {
        "PREBOOT",
@@ -106,28 +50,6 @@ static const char* tcpa_event_type_strings[] = {
        "Non-Host Info"
 };
 
-struct tcpa_pc_event {
-       u32 event_id;
-       u32 event_size;
-       u8 event_data[0];
-};
-
-enum tcpa_pc_event_ids {
-       SMBIOS = 1,
-       BIS_CERT,
-       POST_BIOS_ROM,
-       ESCD,
-       CMOS,
-       NVRAM,
-       OPTION_ROM_EXEC,
-       OPTION_ROM_CONFIG,
-       OPTION_ROM_MICROCODE = 10,
-       S_CRTM_VERSION,
-       S_CRTM_CONTENTS,
-       POST_CONTENTS,
-       HOST_TABLE_OF_DEVICES,
-};
-
 static const char* tcpa_pc_event_id_strings[] = {
        "",
        "SMBIOS",
@@ -358,65 +280,6 @@ static const struct seq_operations 
tpm_binary_b_measurments_seqops = {
        .show = tpm_binary_bios_measurements_show,
 };
 
-/* read binary bios log */
-static int read_log(struct tpm_bios_log *log)
-{
-       struct acpi_tcpa *buff;
-       acpi_status status;
-       struct acpi_table_header *virt;
-       u64 len, start;
-
-       if (log->bios_event_log != NULL) {
-               printk(KERN_ERR
-                      "%s: ERROR - Eventlog already initialized\n",
-                      __func__);
-               return -EFAULT;
-       }
-
-       /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */
-       status = acpi_get_table(ACPI_SIG_TCPA, 1,
-                               (struct acpi_table_header **)&buff);
-
-       if (ACPI_FAILURE(status)) {
-               printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n",
-                      __func__);
-               return -EIO;
-       }
-
-       switch(buff->platform_class) {
-       case BIOS_SERVER:
-               len = buff->server.log_max_len;
-               start = buff->server.log_start_addr;
-               break;
-       case BIOS_CLIENT:
-       default:
-               len = buff->client.log_max_len;
-               start = buff->client.log_start_addr;
-               break;
-       }
-       if (!len) {
-               printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__);
-               return -EIO;
-       }
-
-       /* malloc EventLog space */
-       log->bios_event_log = kmalloc(len, GFP_KERNEL);
-       if (!log->bios_event_log) {
-               printk("%s: ERROR - Not enough  Memory for BIOS measurements\n",
-                       __func__);
-               return -ENOMEM;
-       }
-
-       log->bios_event_log_end = log->bios_event_log + len;
-
-       virt = acpi_os_map_memory(start, len);
-
-       memcpy(log->bios_event_log, virt, len);
-
-       acpi_os_unmap_memory(virt, len);
-       return 0;
-}
-
 static int tpm_ascii_bios_measurements_open(struct inode *inode,
                                            struct file *file)
 {
diff --git a/drivers/char/tpm/tpm_eventlog.h b/drivers/char/tpm/tpm_eventlog.h
new file mode 100644
index 0000000..8e23ccd
--- /dev/null
+++ b/drivers/char/tpm/tpm_eventlog.h
@@ -0,0 +1,71 @@
+
+#ifndef __TPM_EVENTLOG_H__
+#define __TPM_EVENTLOG_H__
+
+#define TCG_EVENT_NAME_LEN_MAX 255
+#define MAX_TEXT_EVENT         1000    /* Max event string length */
+#define ACPI_TCPA_SIG          "TCPA"  /* 0x41504354 /'TCPA' */
+
+enum bios_platform_class {
+       BIOS_CLIENT = 0x00,
+       BIOS_SERVER = 0x01,
+};
+
+struct tpm_bios_log {
+       void *bios_event_log;
+       void *bios_event_log_end;
+};
+
+struct tcpa_event {
+       u32 pcr_index;
+       u32 event_type;
+       u8 pcr_value[20];       /* SHA1 */
+       u32 event_size;
+       u8 event_data[0];
+};
+
+enum tcpa_event_types {
+       PREBOOT = 0,
+       POST_CODE,
+       UNUSED,
+       NO_ACTION,
+       SEPARATOR,
+       ACTION,
+       EVENT_TAG,
+       SCRTM_CONTENTS,
+       SCRTM_VERSION,
+       CPU_MICROCODE,
+       PLATFORM_CONFIG_FLAGS,
+       TABLE_OF_DEVICES,
+       COMPACT_HASH,
+       IPL,
+       IPL_PARTITION_DATA,
+       NONHOST_CODE,
+       NONHOST_CONFIG,
+       NONHOST_INFO,
+};
+
+struct tcpa_pc_event {
+       u32 event_id;
+       u32 event_size;
+       u8 event_data[0];
+};
+
+enum tcpa_pc_event_ids {
+       SMBIOS = 1,
+       BIS_CERT,
+       POST_BIOS_ROM,
+       ESCD,
+       CMOS,
+       NVRAM,
+       OPTION_ROM_EXEC,
+       OPTION_ROM_CONFIG,
+       OPTION_ROM_MICROCODE = 10,
+       S_CRTM_VERSION,
+       S_CRTM_CONTENTS,
+       POST_CONTENTS,
+       HOST_TABLE_OF_DEVICES,
+};
+
+int read_log(struct tpm_bios_log *log);
+#endif
diff --git a/drivers/char/tpm/tpm_i2c_infineon.c 
b/drivers/char/tpm/tpm_i2c_infineon.c
new file mode 100644
index 0000000..1794a09
--- /dev/null
+++ b/drivers/char/tpm/tpm_i2c_infineon.c
@@ -0,0 +1,748 @@
+/*
+ * Copyright (C) 2012 Infineon Technologies
+ *
+ * Authors:
+ * Peter Huewe <[email protected]>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * This device driver implements the TPM interface as defined in
+ * the TCG TPM Interface Spec version 1.2, revision 1.0 and the
+ * Infineon I2C Protocol Stack Specification v0.20.
+ *
+ * It is based on the original tpm_tis device driver from Leendert van
+ * Dorn and Kyleen Hall.
+ *
+ * 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/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/wait.h>
+#include "tpm.h"
+
+/* max. buffer size supported by our TPM */
+#define TPM_BUFSIZE 1260
+
+/* max. number of iterations after I2C NAK */
+#define MAX_COUNT 3
+
+#define SLEEP_DURATION_LOW 55
+#define SLEEP_DURATION_HI 65
+
+/* max. number of iterations after I2C NAK for 'long' commands
+ * we need this especially for sending TPM_READY, since the cleanup after the
+ * transtion to the ready state may take some time, but it is unpredictable
+ * how long it will take.
+ */
+#define MAX_COUNT_LONG 50
+
+#define SLEEP_DURATION_LONG_LOW 200
+#define SLEEP_DURATION_LONG_HI 220
+
+/* After sending TPM_READY to 'reset' the TPM we have to sleep even longer */
+#define SLEEP_DURATION_RESET_LOW 2400
+#define SLEEP_DURATION_RESET_HI 2600
+
+/* we want to use usleep_range instead of msleep for the 5ms TPM_TIMEOUT */
+#define TPM_TIMEOUT_US_LOW (TPM_TIMEOUT * 1000)
+#define TPM_TIMEOUT_US_HI  (TPM_TIMEOUT_US_LOW + 2000)
+
+/* expected value for DIDVID register */
+#define TPM_TIS_I2C_DID_VID 0x000b15d1L
+
+/* Structure to store I2C TPM specific stuff */
+struct tpm_inf_dev {
+       struct i2c_client *client;
+       u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */
+       struct tpm_chip *chip;
+};
+
+static struct tpm_inf_dev tpm_dev;
+static struct i2c_driver tpm_tis_i2c_driver;
+
+
+/*
+ * Copy i2c-core:i2c_transfer() as close as possible without the adapter locks
+ * and algorithm check.  These are done by the caller for atomicity.
+ * Unfortunately we have to use this as a workaround in multislave environments
+ * as no other suitable and working mechanism is available.
+ */
+static int i2c_transfer_nolock(struct i2c_adapter *adap, struct i2c_msg *msgs,
+                              int num)
+{
+       unsigned long orig_jiffies;
+       int ret, try;
+
+       /* Retry automatically on arbitration loss */
+       orig_jiffies = jiffies;
+       for (ret = 0, try = 0; try <= adap->retries; try++) {
+               ret = adap->algo->master_xfer(adap, msgs, num);
+               if (ret != -EAGAIN)
+                       break;
+               if (time_after(jiffies, orig_jiffies + adap->timeout))
+                       break;
+       }
+       return ret;
+}
+
+/*
+ * iic_tpm_read() - read from TPM register
+ * @addr: register address to read from
+ * @buffer: provided by caller
+ * @len: number of bytes to read
+ *
+ * Read len bytes from TPM register and put them into
+ * buffer (little-endian format, i.e. first byte is put into buffer[0]).
+ *
+ * NOTE: TPM is big-endian for multi-byte values. Multi-byte
+ * values have to be swapped.
+ *
+ * NOTE: We can't unfortunately use the combined read/write functions
+ * provided by the i2c core as the TPM currently does not support the
+ * repeated start condition and due to it's special requirements.
+ * The i2c_smbus* functions do not work for this chip.
+ *
+ * Return -EIO on error, 0 on success.
+ */
+static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
+{
+
+       struct i2c_msg msg1 = { tpm_dev.client->addr, 0, 1, &addr };
+       struct i2c_msg msg2 = { tpm_dev.client->addr, I2C_M_RD, len, buffer };
+
+       int rc;
+       int count;
+
+       /* Lock the adapter for the duration of the whole sequence. */
+       if (!tpm_dev.client->adapter->algo->master_xfer)
+               return -EOPNOTSUPP;
+       i2c_lock_adapter(tpm_dev.client->adapter);
+
+       for (count = 0; count < MAX_COUNT; count++) {
+               rc = i2c_transfer_nolock(tpm_dev.client->adapter, &msg1, 1);
+               if (rc > 0)
+                       break;  /* break here to skip sleep */
+
+               usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
+       }
+
+       if (rc <= 0)
+               goto out;
+
+       /* After the TPM has successfully received the register address it needs
+        * some time, thus we're sleeping here again, before retrieving the data
+        */
+       for (count = 0; count < MAX_COUNT; count++) {
+               usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
+               rc = i2c_transfer_nolock(tpm_dev.client->adapter, &msg2, 1);
+               if (rc > 0)
+                       break;
+
+       }
+
+out:
+       i2c_unlock_adapter(tpm_dev.client->adapter);
+       if (rc <= 0)
+               return -EIO;
+
+       return 0;
+}
+
+static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len,
+                                unsigned int sleep_low,
+                                unsigned int sleep_hi, u8 max_count)
+{
+       int rc = -EIO;
+       int count;
+
+       struct i2c_msg msg1 = { tpm_dev.client->addr, 0, len + 1, tpm_dev.buf };
+
+       if (len > TPM_BUFSIZE)
+               return -EINVAL;
+
+       if (!tpm_dev.client->adapter->algo->master_xfer)
+               return -EOPNOTSUPP;
+       i2c_lock_adapter(tpm_dev.client->adapter);
+
+       /* prepend the 'register address' to the buffer */
+       tpm_dev.buf[0] = addr;
+       memcpy(&(tpm_dev.buf[1]), buffer, len);
+
+       /*
+        * NOTE: We have to use these special mechanisms here and unfortunately
+        * cannot rely on the standard behavior of i2c_transfer.
+        */
+       for (count = 0; count < max_count; count++) {
+               rc = i2c_transfer_nolock(tpm_dev.client->adapter, &msg1, 1);
+               if (rc > 0)
+                       break;
+
+               usleep_range(sleep_low, sleep_hi);
+       }
+
+       i2c_unlock_adapter(tpm_dev.client->adapter);
+       if (rc <= 0)
+               return -EIO;
+
+       return 0;
+}
+
+/*
+ * iic_tpm_write() - write to TPM register
+ * @addr: register address to write to
+ * @buffer: containing data to be written
+ * @len: number of bytes to write
+ *
+ * Write len bytes from provided buffer to TPM register (little
+ * endian format, i.e. buffer[0] is written as first byte).
+ *
+ * NOTE: TPM is big-endian for multi-byte values. Multi-byte
+ * values have to be swapped.
+ *
+ * NOTE: use this function instead of the iic_tpm_write_generic function.
+ *
+ * Return -EIO on error, 0 on success
+ */
+static int iic_tpm_write(u8 addr, u8 *buffer, size_t len)
+{
+       return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LOW,
+                                    SLEEP_DURATION_HI, MAX_COUNT);
+}
+
+/*
+ * This function is needed especially for the cleanup situation after
+ * sending TPM_READY
+ * */
+static int iic_tpm_write_long(u8 addr, u8 *buffer, size_t len)
+{
+       return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG_LOW,
+                                    SLEEP_DURATION_LONG_HI, MAX_COUNT_LONG);
+}
+
+enum tis_access {
+       TPM_ACCESS_VALID = 0x80,
+       TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
+       TPM_ACCESS_REQUEST_PENDING = 0x04,
+       TPM_ACCESS_REQUEST_USE = 0x02,
+};
+
+enum tis_status {
+       TPM_STS_VALID = 0x80,
+       TPM_STS_COMMAND_READY = 0x40,
+       TPM_STS_GO = 0x20,
+       TPM_STS_DATA_AVAIL = 0x10,
+       TPM_STS_DATA_EXPECT = 0x08,
+};
+
+enum tis_defaults {
+       TIS_SHORT_TIMEOUT = 750,        /* ms */
+       TIS_LONG_TIMEOUT = 2000,        /* 2 sec */
+};
+
+#define        TPM_ACCESS(l)                   (0x0000 | ((l) << 4))
+#define        TPM_STS(l)                      (0x0001 | ((l) << 4))
+#define        TPM_DATA_FIFO(l)                (0x0005 | ((l) << 4))
+#define        TPM_DID_VID(l)                  (0x0006 | ((l) << 4))
+
+static int check_locality(struct tpm_chip *chip, int loc)
+{
+       u8 buf;
+       int rc;
+
+       rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1);
+       if (rc < 0)
+               return rc;
+
+       if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
+           (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
+               chip->vendor.locality = loc;
+               return loc;
+       }
+
+       return -EIO;
+}
+
+/* implementation similar to tpm_tis */
+static void release_locality(struct tpm_chip *chip, int loc, int force)
+{
+       u8 buf;
+       if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0)
+               return;
+
+       if (force || (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
+           (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) {
+               buf = TPM_ACCESS_ACTIVE_LOCALITY;
+               iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
+       }
+}
+
+static int request_locality(struct tpm_chip *chip, int loc)
+{
+       unsigned long stop;
+       u8 buf = TPM_ACCESS_REQUEST_USE;
+
+       if (check_locality(chip, loc) >= 0)
+               return loc;
+
+       iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
+
+       /* wait for burstcount */
+       stop = jiffies + chip->vendor.timeout_a;
+       do {
+               if (check_locality(chip, loc) >= 0)
+                       return loc;
+               usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
+       } while (time_before(jiffies, stop));
+
+       return -ETIME;
+}
+
+static u8 tpm_tis_i2c_status(struct tpm_chip *chip)
+{
+       /* NOTE: since I2C read may fail, return 0 in this case --> time-out */
+       u8 buf;
+       if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0)
+               return 0;
+       else
+               return buf;
+}
+
+static void tpm_tis_i2c_ready(struct tpm_chip *chip)
+{
+       /* this causes the current command to be aborted */
+       u8 buf = TPM_STS_COMMAND_READY;
+       iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1);
+}
+
+static ssize_t get_burstcount(struct tpm_chip *chip)
+{
+       unsigned long stop;
+       ssize_t burstcnt;
+       u8 buf[3];
+
+       /* wait for burstcount */
+       /* which timeout value, spec has 2 answers (c & d) */
+       stop = jiffies + chip->vendor.timeout_d;
+       do {
+               /* Note: STS is little endian */
+               if (iic_tpm_read(TPM_STS(chip->vendor.locality)+1, buf, 3) < 0)
+                       burstcnt = 0;
+               else
+                       burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0];
+
+               if (burstcnt)
+                       return burstcnt;
+
+               usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
+       } while (time_before(jiffies, stop));
+       return -EBUSY;
+}
+
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
+                        int *status)
+{
+       unsigned long stop;
+
+       /* check current status */
+       *status = tpm_tis_i2c_status(chip);
+       if ((*status & mask) == mask)
+               return 0;
+
+       stop = jiffies + timeout;
+       do {
+               /* since we just checked the status, give the TPM some time */
+               usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
+               *status = tpm_tis_i2c_status(chip);
+               if ((*status & mask) == mask)
+                       return 0;
+
+       } while (time_before(jiffies, stop));
+
+       return -ETIME;
+}
+
+static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+       size_t size = 0;
+       ssize_t burstcnt;
+       u8 retries = 0;
+       int rc;
+
+       while (size < count) {
+               burstcnt = get_burstcount(chip);
+
+               /* burstcnt < 0 = TPM is busy */
+               if (burstcnt < 0)
+                       return burstcnt;
+
+               /* limit received data to max. left */
+               if (burstcnt > (count - size))
+                       burstcnt = count - size;
+
+               rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality),
+                                 &(buf[size]), burstcnt);
+               if (rc == 0)
+                       size += burstcnt;
+               else if (rc < 0)
+                       retries++;
+
+               /* avoid endless loop in case of broken HW */
+               if (retries > MAX_COUNT_LONG)
+                       return -EIO;
+
+       }
+       return size;
+}
+
+static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+       int size = 0;
+       int expected, status;
+
+       if (count < TPM_HEADER_SIZE) {
+               size = -EIO;
+               goto out;
+       }
+
+       /* read first 10 bytes, including tag, paramsize, and result */
+       size = recv_data(chip, buf, TPM_HEADER_SIZE);
+       if (size < TPM_HEADER_SIZE) {
+               dev_err(chip->dev, "Unable to read header\n");
+               goto out;
+       }
+
+       expected = be32_to_cpu(*(__be32 *)(buf + 2));
+       if ((size_t) expected > count) {
+               size = -EIO;
+               goto out;
+       }
+
+       size += recv_data(chip, &buf[TPM_HEADER_SIZE],
+                         expected - TPM_HEADER_SIZE);
+       if (size < expected) {
+               dev_err(chip->dev, "Unable to read remainder of result\n");
+               size = -ETIME;
+               goto out;
+       }
+
+       wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
+       if (status & TPM_STS_DATA_AVAIL) {      /* retry? */
+               dev_err(chip->dev, "Error left over data\n");
+               size = -EIO;
+               goto out;
+       }
+
+out:
+       tpm_tis_i2c_ready(chip);
+       /* The TPM needs some time to clean up here,
+        * so we sleep rather than keeping the bus busy
+        */
+       usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
+       release_locality(chip, chip->vendor.locality, 0);
+       return size;
+}
+
+static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+       int rc, status;
+       ssize_t burstcnt;
+       size_t count = 0;
+       u8 retries = 0;
+       u8 sts = TPM_STS_GO;
+
+       if (len > TPM_BUFSIZE)
+               return -E2BIG;  /* command is too long for our tpm, sorry */
+
+       if (request_locality(chip, 0) < 0)
+               return -EBUSY;
+
+       status = tpm_tis_i2c_status(chip);
+       if ((status & TPM_STS_COMMAND_READY) == 0) {
+               tpm_tis_i2c_ready(chip);
+               if (wait_for_stat
+                   (chip, TPM_STS_COMMAND_READY,
+                    chip->vendor.timeout_b, &status) < 0) {
+                       rc = -ETIME;
+                       goto out_err;
+               }
+       }
+
+       while (count < len - 1) {
+               burstcnt = get_burstcount(chip);
+
+               /* burstcnt < 0 = TPM is busy */
+               if (burstcnt < 0)
+                       return burstcnt;
+
+               if (burstcnt > (len - 1 - count))
+                       burstcnt = len - 1 - count;
+
+               rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality),
+                                  &(buf[count]), burstcnt);
+               if (rc == 0)
+                       count += burstcnt;
+               else if (rc < 0)
+                       retries++;
+
+               /* avoid endless loop in case of broken HW */
+               if (retries > MAX_COUNT_LONG) {
+                       rc = -EIO;
+                       goto out_err;
+               }
+
+               wait_for_stat(chip, TPM_STS_VALID,
+                             chip->vendor.timeout_c, &status);
+
+               if ((status & TPM_STS_DATA_EXPECT) == 0) {
+                       rc = -EIO;
+                       goto out_err;
+               }
+
+       }
+
+       /* write last byte */
+       iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1);
+       wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
+       if ((status & TPM_STS_DATA_EXPECT) != 0) {
+               rc = -EIO;
+               goto out_err;
+       }
+
+       /* go and do it */
+       iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1);
+
+       return len;
+out_err:
+       tpm_tis_i2c_ready(chip);
+       /* The TPM needs some time to clean up here,
+        * so we sleep rather than keeping the bus busy
+        */
+       usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
+       release_locality(chip, chip->vendor.locality, 0);
+       return rc;
+}
+
+static const struct file_operations tis_ops = {
+       .owner = THIS_MODULE,
+       .llseek = no_llseek,
+       .open = tpm_open,
+       .read = tpm_read,
+       .write = tpm_write,
+       .release = tpm_release,
+};
+
+static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
+static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
+static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
+static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
+static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
+static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL);
+static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
+static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
+static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
+static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
+
+static struct attribute *tis_attrs[] = {
+       &dev_attr_pubek.attr,
+       &dev_attr_pcrs.attr,
+       &dev_attr_enabled.attr,
+       &dev_attr_active.attr,
+       &dev_attr_owned.attr,
+       &dev_attr_temp_deactivated.attr,
+       &dev_attr_caps.attr,
+       &dev_attr_cancel.attr,
+       &dev_attr_durations.attr,
+       &dev_attr_timeouts.attr,
+       NULL,
+};
+
+static struct attribute_group tis_attr_grp = {
+       .attrs = tis_attrs
+};
+
+static struct tpm_vendor_specific tpm_tis_i2c = {
+       .status = tpm_tis_i2c_status,
+       .recv = tpm_tis_i2c_recv,
+       .send = tpm_tis_i2c_send,
+       .cancel = tpm_tis_i2c_ready,
+       .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+       .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+       .req_canceled = TPM_STS_COMMAND_READY,
+       .attr_group = &tis_attr_grp,
+       .miscdev.fops = &tis_ops,
+};
+
+static int __devinit tpm_tis_i2c_init(struct device *dev)
+{
+       u32 vendor;
+       int rc = 0;
+       struct tpm_chip *chip;
+
+       chip = tpm_register_hardware(dev, &tpm_tis_i2c);
+       if (!chip) {
+               rc = -ENODEV;
+               goto out_err;
+       }
+
+       /* Disable interrupts */
+       chip->vendor.irq = 0;
+
+       /* 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);
+
+       if (request_locality(chip, 0) != 0) {
+               rc = -ENODEV;
+               goto out_vendor;
+       }
+
+       /* read four bytes from DID_VID register */
+       if (iic_tpm_read(TPM_DID_VID(0), (u8 *)&vendor, 4) < 0) {
+               rc = -EIO;
+               goto out_release;
+       }
+
+       /* create DID_VID register value, after swapping to little-endian */
+       vendor = be32_to_cpu((__be32) vendor);
+
+       if (vendor != TPM_TIS_I2C_DID_VID) {
+               rc = -ENODEV;
+               goto out_release;
+       }
+
+       dev_info(dev, "1.2 TPM (device-id 0x%X)\n", vendor >> 16);
+
+       INIT_LIST_HEAD(&chip->vendor.list);
+       tpm_dev.chip = chip;
+
+       tpm_get_timeouts(chip);
+       tpm_do_selftest(chip);
+
+       return 0;
+
+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;
+       dev_set_drvdata(chip->dev, chip);
+out_err:
+       return rc;
+}
+
+static const struct i2c_device_id tpm_tis_i2c_table[] = {
+       {"tpm_i2c_infineon", 0},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_table);
+
+#ifdef CONFIG_PM
+/* NOTE:
+ * Suspend does currently not work Nvidias Tegra2 Platform
+ * but works fine on Beagleboard (arm omap).
+ *
+ * This function will block System Suspend if TPM is not initialized,
+ * however the TPM is usually initialized by BIOS/u-boot or by sending
+ * a TPM_Startup command.
+ */
+static int tpm_tis_i2c_suspend(struct device *dev)
+{
+       return tpm_pm_suspend(dev, dev->power.power_state);
+}
+
+static int tpm_tis_i2c_resume(struct device *dev)
+{
+       return tpm_pm_resume(dev);
+}
+
+static const struct dev_pm_ops tpm_tis_i2c_ops = {
+       .suspend = tpm_tis_i2c_suspend,
+       .resume = tpm_tis_i2c_resume,
+};
+#else
+#define tpm_tis_i2c_suspend NULL
+#define tpm_tis_i2c_resume NULL
+#define tpm_tis_i2c_ops NULL
+#endif
+
+static int __devinit tpm_tis_i2c_probe(struct i2c_client *client,
+                            const struct i2c_device_id *id)
+{
+       int rc;
+       if (tpm_dev.client != NULL)
+               return -EBUSY;  /* We only support one client */
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(&client->dev,
+                       "no algorithms associated to the i2c bus\n");
+               return -ENODEV;
+       }
+
+       client->driver = &tpm_tis_i2c_driver;
+       tpm_dev.client = client;
+       rc = tpm_tis_i2c_init(&client->dev);
+       if (rc != 0) {
+               client->driver = NULL;
+               tpm_dev.client = NULL;
+               rc = -ENODEV;
+       }
+       return rc;
+}
+
+static int __devexit 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;
+       dev_set_drvdata(chip->dev, chip);
+
+       return 0;
+}
+
+static struct i2c_driver tpm_tis_i2c_driver = {
+
+       .id_table = tpm_tis_i2c_table,
+       .probe = tpm_tis_i2c_probe,
+       .remove = tpm_tis_i2c_remove,
+       .driver = {
+                  .name = "tpm_i2c_infineon",
+                  .owner = THIS_MODULE,
+                  .pm = &tpm_tis_i2c_ops,
+                  },
+};
+
+module_i2c_driver(tpm_tis_i2c_driver);
+MODULE_AUTHOR("Peter Huewe <[email protected]>");
+MODULE_DESCRIPTION("TPM TIS I2C Infineon Driver");
+MODULE_VERSION("2.1.4");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index fdc718a..d5b2f2d 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -32,6 +32,7 @@
 extern int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf);
 extern int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash);
 extern int tpm_send(u32 chip_num, void *cmd, size_t buflen);
+extern int tpm_get_random(u32 chip_num, u8 *data, size_t *max);
 #else
 static inline int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) {
        return -ENODEV;
@@ -42,5 +43,8 @@ static inline int tpm_pcr_extend(u32 chip_num, int pcr_idx, 
const u8 *hash) {
 static inline int tpm_send(u32 chip_num, void *cmd, size_t buflen) {
        return -ENODEV;
 }
+static inline int tpm_get_random(u32 chip_num, u8 *data, size_t *max) {
+       return -ENODEV;
+}
 #endif
 #endif
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index 2d5d041..2f6218c 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -369,38 +369,6 @@ static int trusted_tpm_send(const u32 chip_num, unsigned 
char *cmd,
 }
 
 /*
- * get a random value from TPM
- */
-static int tpm_get_random(struct tpm_buf *tb, unsigned char *buf, uint32_t len)
-{
-       int ret;
-
-       INIT_BUF(tb);
-       store16(tb, TPM_TAG_RQU_COMMAND);
-       store32(tb, TPM_GETRANDOM_SIZE);
-       store32(tb, TPM_ORD_GETRANDOM);
-       store32(tb, len);
-       ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, sizeof tb->data);
-       if (!ret)
-               memcpy(buf, tb->data + TPM_GETRANDOM_SIZE, len);
-       return ret;
-}
-
-static int my_get_random(unsigned char *buf, int len)
-{
-       struct tpm_buf *tb;
-       int ret;
-
-       tb = kmalloc(sizeof *tb, GFP_KERNEL);
-       if (!tb)
-               return -ENOMEM;
-       ret = tpm_get_random(tb, buf, len);
-
-       kfree(tb);
-       return ret;
-}
-
-/*
  * Lock a trusted key, by extending a selected PCR.
  *
  * Prevents a trusted key that is sealed to PCRs from being accessed.
@@ -409,11 +377,12 @@ static int my_get_random(unsigned char *buf, int len)
 static int pcrlock(const int pcrnum)
 {
        unsigned char hash[SHA1_DIGEST_SIZE];
+       size_t digest_size = SHA1_DIGEST_SIZE;
        int ret;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
-       ret = my_get_random(hash, SHA1_DIGEST_SIZE);
+       ret = tpm_get_random(TPM_ANY_NUM, hash, &digest_size);
        if (ret < 0)
                return ret;
        return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0;
@@ -427,9 +396,10 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
 {
        unsigned char enonce[TPM_NONCE_SIZE];
        unsigned char ononce[TPM_NONCE_SIZE];
+       size_t nonce_size = TPM_NONCE_SIZE;
        int ret;
 
-       ret = tpm_get_random(tb, ononce, TPM_NONCE_SIZE);
+       ret = tpm_get_random(TPM_ANY_NUM, ononce, &nonce_size);
        if (ret < 0)
                return ret;
 
@@ -500,6 +470,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
        uint32_t ordinal;
        uint32_t pcrsize;
        uint32_t datsize;
+       size_t nonce_size = TPM_NONCE_SIZE;
        int sealinfosize;
        int encdatasize;
        int storedsize;
@@ -524,7 +495,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
        if (ret < 0)
                goto out;
 
-       ret = tpm_get_random(tb, td->nonceodd, TPM_NONCE_SIZE);
+       ret = tpm_get_random(TPM_ANY_NUM, td->nonceodd, &nonce_size);
        if (ret < 0)
                goto out;
        ordinal = htonl(TPM_ORD_SEAL);
@@ -618,6 +589,7 @@ static int tpm_unseal(struct tpm_buf *tb,
        unsigned char cont = 0;
        uint32_t ordinal;
        uint32_t keyhndl;
+       size_t nonce_size = TPM_NONCE_SIZE;
        int ret;
 
        /* sessions for unsealing key and data */
@@ -634,7 +606,7 @@ static int tpm_unseal(struct tpm_buf *tb,
 
        ordinal = htonl(TPM_ORD_UNSEAL);
        keyhndl = htonl(SRKHANDLE);
-       ret = tpm_get_random(tb, nonceodd, TPM_NONCE_SIZE);
+       ret = tpm_get_random(TPM_ANY_NUM, nonceodd, &nonce_size);
        if (ret < 0) {
                pr_info("trusted_key: tpm_get_random failed (%d)\n", ret);
                return ret;
@@ -935,6 +907,7 @@ static int trusted_instantiate(struct key *key, const void 
*data,
        char *datablob;
        int ret = 0;
        int key_cmd;
+       size_t key_len;
 
        if (datalen <= 0 || datalen > 32767 || !data)
                return -EINVAL;
@@ -974,7 +947,8 @@ static int trusted_instantiate(struct key *key, const void 
*data,
                        pr_info("trusted_key: key_unseal failed (%d)\n", ret);
                break;
        case Opt_new:
-               ret = my_get_random(payload->key, payload->key_len);
+               key_len = payload->key_len;
+               ret = tpm_get_random(TPM_ANY_NUM, payload->key, &key_len);
                if (ret < 0) {
                        pr_info("trusted_key: key_create failed (%d)\n", ret);
                        goto out;

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

Reply via email to