linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/1] driver: Tpm hardware enablement
@ 2004-12-09 15:25 Kylene Hall
  2004-12-09 15:48 ` Arjan van de Ven
                   ` (2 more replies)
  0 siblings, 3 replies; 43+ messages in thread
From: Kylene Hall @ 2004-12-09 15:25 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, sailer, leendert, emilyr, toml, tpmdd-devel

This patch is a device driver to enable new hardware.  The new hardware is
the TPM chip as described by specifications at trustedcomputinggroup.org.
The TPM chip will enable you to use hardware to securely store and protect
your keys and personal data.  To use the chip according to the 
specification, you will need the Trusted Software Stack (TSS) of which an 
implementation for Linux is available at: 
http://sourceforge.net/projects/trousers.

Thanks,
Kylene

Signed-off-by: Leendert van Doorn <leendert@watson.ibm.com>
Signed-off-by: Reiner Sailer <sailer@watson.ibm.com>
Signed-off-by: Dave Safford <safford@watson.ibm.com>
Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---
diff -uprN linux-2.6.9/drivers/char/Kconfig linux-2.6.9-tpm/drivers/char/Kconfig
--- linux-2.6.9/drivers/char/Kconfig	2004-10-18 16:53:07.000000000 -0500
+++ linux-2.6.9-tpm/drivers/char/Kconfig	2004-12-08 10:45:10.000000000 -0600
@@ -989,5 +989,20 @@ config MMTIMER
 	  The mmtimer device allows direct userspace access to the
 	  Altix system timer.
 
+config TCG_TPM
+	tristate "TPM Hardware Support"
+	depends on EXPERIMENTAL
+	---help---
+	  If you have a TPM security chip in your system, which
+	  implements the Trusted Computing Group's specification,
+	  say Yes and it will be accessible from within Linux. To 
+	  compile this driver as a module, choose M here; the module 
+	  will be called tpm. For more information see 
+	  www.trustedcomputinggroup.org. A implementation of the 
+	  Trusted Software Stack (TSS), the userspace enablement piece 
+	  of the specification, can be obtained at 
+	  http://sourceforge.net/projects/trousers
+	  If unsure, say N.
+
 endmenu
 
diff -uprN linux-2.6.9/drivers/char/Makefile linux-2.6.9-tpm/drivers/char/Makefile
--- linux-2.6.9/drivers/char/Makefile	2004-10-18 16:55:28.000000000 -0500
+++ linux-2.6.9-tpm/drivers/char/Makefile	2004-11-22 17:33:17.000000000 -0600
@@ -88,6 +88,7 @@ obj-$(CONFIG_PCMCIA) += pcmcia/
 obj-$(CONFIG_IPMI_HANDLER) += ipmi/
 
 obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
+obj-$(CONFIG_TCG_TPM) += tpm.o
 
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c qtronixmap.c
diff -uprN linux-2.6.9/drivers/char/tpm.c linux-2.6.9-tpm/drivers/char/tpm.c
--- linux-2.6.9/drivers/char/tpm.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm.c	2004-12-07 12:54:49.000000000 -0600
@@ -0,0 +1,964 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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.
+ * 
+ * Note, the ATMEL chip is not interrupt driven (only polling)
+ * and can have very long timeouts (minutes!). Hence the unusual
+ * calls to schedule.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include <linux/miscdevice.h>
+
+#define	TPM_MINOR			224	/* officially assigned */
+
+#define	TPM_BUFSIZE			2048
+
+/* PCI configuration addresses */
+#define	PCI_GEN_PMCON_1			0xA0
+#define	PCI_GEN1_DEC			0xE4
+#define	PCI_LPC_EN			0xE6
+#define	PCI_GEN2_DEC			0xEC
+
+/* TPM addresses */
+#define	TPM_ADDR			0x4E
+#define	TPM_DATA			0x4F
+
+/* National definitions */
+#define	TPM_NSC_BASE			0x360
+#define	TPM_NSC_IRQ			0x07
+
+#define	NSC_LDN_INDEX			0x07
+#define	NSC_SID_INDEX			0x20
+#define	NSC_LDC_INDEX			0x30
+#define	NSC_DIO_INDEX			0x60
+#define	NSC_CIO_INDEX			0x62
+#define	NSC_IRQ_INDEX			0x70
+#define	NSC_ITS_INDEX			0x71
+
+#define	NSC_STATUS			0x01
+#define	NSC_COMMAND			0x01
+#define	NSC_DATA			0x00
+
+/* status bits */
+#define	NSC_STATUS_OBF			0x01	/* output buffer full */
+#define	NSC_STATUS_IBF			0x02	/* input buffer full */
+#define	NSC_STATUS_F0			0x04	/* F0 */
+#define	NSC_STATUS_A2			0x08	/* A2 */
+#define	NSC_STATUS_RDY			0x10	/* ready to receive command */
+#define	NSC_STATUS_IBR			0x20	/* ready to receive data */
+
+/* command bits */
+#define	NSC_COMMAND_NORMAL		0x01	/* normal mode */
+#define	NSC_COMMAND_BURST		0x81	/* burst mode */
+#define	NSC_COMMAND_EOC			0x03
+#define	NSC_COMMAND_CANCEL		0x22
+
+/* Atmel definitions */
+#define	TPM_ATML_BASE			0x400
+
+/* write status bits */
+#define	ATML_STATUS_ABORT		0x01
+#define	ATML_STATUS_LASTBYTE		0x04
+
+/* read status bits */
+#define	ATML_STATUS_BUSY		0x01
+#define	ATML_STATUS_DATA_AVAIL		0x02
+#define	ATML_STATUS_REWRITE		0x04
+
+struct tpm_chip {
+	struct pci_dev *pci_dev;	/* PCI device stuff */
+	u16 base;		/* TPM base address */
+	u16 irq;		/* TPM irq address */
+
+	u8 *userspace_buffer;
+	atomic_t data_pending;
+	int num_opens;
+	struct timer_list user_read_timer;
+	struct timer_list tpm_timer;
+	struct semaphore user_mutex;
+	struct semaphore timer_mutex;
+	int tpm_time_expired;
+	struct list_head list;
+	struct miscdevice miscdev;
+
+	u8 req_complete_mask;
+	u8 req_complete_val;
+
+	int (*recv) (struct tpm_chip *, u8 *, size_t);
+	int (*send) (struct tpm_chip *, u8 *, size_t);
+	void (*cancel) (struct tpm_chip *);
+};
+
+static struct list_head tpm_chip_list;
+static spinlock_t driver_lock;
+static int dev_cnt;
+
+static void user_reader_timeout(unsigned long ptr)
+{
+	struct tpm_chip *chip = (struct tpm_chip *) ptr;
+
+	if (down_trylock(&chip->timer_mutex) == 0) {
+		atomic_set(&chip->data_pending, 0);
+		memset(chip->userspace_buffer, 0, TPM_BUFSIZE);
+		up(&chip->user_mutex);
+		up(&chip->timer_mutex);
+	}
+}
+
+static void tpm_time_expired(unsigned long ptr)
+{
+	int *exp = (int *) ptr;
+	*exp = 1;
+}
+
+static int rdx(int index)
+{
+	outb(index, TPM_ADDR);
+	return inb(TPM_DATA) & 0xFF;
+}
+
+static void wrx(int index, int value)
+{
+	outb(index, TPM_ADDR);
+	outb(value & 0xFF, TPM_DATA);
+}
+
+/*
+ * Initialize the LPC bus and enable the TPM ports
+ */
+static int lpc_bus_init(struct tpm_chip *chip)
+{
+	struct pci_dev *pci_dev = chip->pci_dev;
+	u32 lpcenable, tmp;
+	int is_lpcm = 0;
+
+	switch (pci_dev->vendor) {
+	case PCI_VENDOR_ID_INTEL:
+		switch (pci_dev->device) {
+		case PCI_DEVICE_ID_INTEL_82801CA_12:
+		case PCI_DEVICE_ID_INTEL_82801DB_12:
+			is_lpcm = 1;
+			break;
+		}
+		/* init ICH (enable LPC) */
+		pci_read_config_dword(pci_dev, PCI_GEN1_DEC, &lpcenable);
+		lpcenable |= 0x20000000;
+		pci_write_config_dword(pci_dev, PCI_GEN1_DEC, lpcenable);
+
+		if (is_lpcm) {
+			pci_read_config_dword(pci_dev,
+					      PCI_GEN1_DEC, &lpcenable);
+			if ((lpcenable & 0x20000000) == 0) {
+				dev_err(&chip->pci_dev->dev,
+					"cannot enable LPC\n");
+				return -ENODEV;
+			}
+		}
+
+		/* initialize TPM registers */
+		pci_read_config_dword(pci_dev, PCI_GEN2_DEC, &tmp);
+
+		if (!is_lpcm)
+			tmp = (tmp & 0xFFFF0000) | (chip->base & 0xFFF0);
+		else
+			tmp = (tmp & 0xFFFF0000) |
+			    (chip->base & 0xFFF0) | 0x00000001;
+
+		pci_write_config_dword(pci_dev, PCI_GEN2_DEC, tmp);
+
+		if (is_lpcm) {
+			pci_read_config_dword(pci_dev, PCI_GEN_PMCON_1,
+					      &tmp);
+			tmp |= 0x00000004;	/* enable CLKRUN */
+			pci_write_config_dword(pci_dev, PCI_GEN_PMCON_1,
+					       tmp);
+		}
+		outb(0x0D, TPM_ADDR);	/* unlock 4F */
+		outb(0x55, TPM_DATA);
+		outb(0x0A, TPM_ADDR);	/* int disable */
+		outb(0x00, TPM_DATA);
+		outb(0x08, TPM_ADDR);	/* base addr lo */
+		outb(chip->base & 0xFF, TPM_DATA);
+		outb(0x09, TPM_ADDR);	/* base addr hi */
+		outb((chip->base & 0xFF00) >> 8, TPM_DATA);
+		outb(0x0D, TPM_ADDR);	/* lock 4F */
+		outb(0xAA, TPM_DATA);
+		break;
+	case PCI_VENDOR_ID_AMD:
+		/* nothing yet */
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * Wait for a certain status to appear
+ */
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
+{
+	int expired = 0;
+	struct timer_list status_timer =
+	    TIMER_INITIALIZER(tpm_time_expired, jiffies + 10 * HZ,
+			      (unsigned long) &expired);
+
+	/* status immediately available check */
+	*data = inb(chip->base + 1);
+	if ((*data & mask) == val)
+		return 0;
+
+	/* wait for status */
+	add_timer(&status_timer);
+	do {
+		schedule();
+		*data = inb(chip->base + 1);
+		if ((*data & mask) == val) {
+			del_singleshot_timer_sync(&status_timer);
+			return 0;
+		}
+	} while (!expired);
+
+	return -EBUSY;
+}
+
+/*
+ * National (safekeeper) specific code
+ */
+static int tpm_nsc_init(struct tpm_chip *chip)
+{
+	chip->irq = TPM_NSC_IRQ;
+	chip->base = TPM_NSC_BASE;
+
+	if (lpc_bus_init(chip))
+		return -ENODEV;
+
+	/* verify that it is a National part (SID) */
+	if (rdx(NSC_SID_INDEX) != 0xEF) {
+		return -ENODEV;
+	}
+
+	dev_dbg(&chip->pci_dev->dev, "NSC TPM detected\n");
+	dev_dbg(&chip->pci_dev->dev,
+		"NSC LDN 0x%x, SID 0x%x, SRID 0x%x\n", rdx(0x07),
+		rdx(0x20), rdx(0x27));
+	dev_dbg(&chip->pci_dev->dev,
+		"NSC SIOCF1 0x%x SIOCF5 0x%x SIOCF6 0x%x SIOCF8 0x%x\n",
+		rdx(0x21), rdx(0x25), rdx(0x26), rdx(0x28));
+	dev_dbg(&chip->pci_dev->dev, "NSC IO Base0 0x%x\n",
+		(rdx(0x60) << 8) | rdx(0x61));
+	dev_dbg(&chip->pci_dev->dev, "NSC IO Base1 0x%x\n",
+		(rdx(0x62) << 8) | rdx(0x63));
+	dev_dbg(&chip->pci_dev->dev,
+		"NSC Interrupt number and wakeup 0x%x\n", rdx(0x70));
+	dev_dbg(&chip->pci_dev->dev, "NSC IRQ type select 0x%x\n",
+		rdx(0x71));
+	dev_dbg(&chip->pci_dev->dev,
+		"NSC DMA channel select0 0x%x, select1 0x%x\n", rdx(0x74),
+		rdx(0x75));
+	dev_dbg(&chip->pci_dev->dev,
+		"NSC Config "
+		"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+		rdx(0xF0), rdx(0xF1), rdx(0xF2), rdx(0xF3), rdx(0xF4),
+		rdx(0xF5), rdx(0xF6), rdx(0xF7), rdx(0xF8), rdx(0xF9));
+
+	dev_info(&chip->pci_dev->dev,
+		 "NSC PC21100 TPM revision %d\n", rdx(0x27) & 0x1F);
+
+	if (rdx(NSC_LDC_INDEX) == 0)
+		dev_info(&chip->pci_dev->dev, ": NSC TPM not active\n");
+
+	/* select PM channel 1 */
+	wrx(NSC_LDN_INDEX, 0x12);
+	rdx(NSC_LDN_INDEX);
+
+	/* disable the DPM module */
+	wrx(NSC_LDC_INDEX, 0);
+	rdx(NSC_LDC_INDEX);
+
+	/* set the data register base addresses */
+	wrx(NSC_DIO_INDEX, chip->base >> 8);
+	wrx(NSC_DIO_INDEX + 1, chip->base);
+	rdx(NSC_DIO_INDEX);
+	rdx(NSC_DIO_INDEX + 1);
+
+	/* set the command register base addresses */
+	wrx(NSC_CIO_INDEX, (chip->base + 1) >> 8);
+	wrx(NSC_CIO_INDEX + 1, (chip->base + 1));
+	rdx(NSC_DIO_INDEX);
+	rdx(NSC_DIO_INDEX + 1);
+
+	/* set the interrupt number to be used for the host interface */
+	wrx(NSC_IRQ_INDEX, chip->irq);
+	wrx(NSC_ITS_INDEX, 0x00);
+	rdx(NSC_IRQ_INDEX);
+
+	/* enable the DPM module */
+	wrx(NSC_LDC_INDEX, 0x01);
+	rdx(NSC_LDC_INDEX);
+
+	return 0;
+}
+
+static int nsc_wait_for_ready(struct tpm_chip *chip)
+{
+	int status;
+	int expired = 0;
+	struct timer_list status_timer =
+	    TIMER_INITIALIZER(tpm_time_expired, jiffies + 100,
+			      (unsigned long) &expired);
+
+	/* status immediately available check */
+	status = inb(chip->base + NSC_STATUS);
+	if (status & NSC_STATUS_OBF)
+		status = inb(chip->base + NSC_DATA);
+	if (status & NSC_STATUS_RDY)
+		return 0;
+
+	/* wait for status */
+	add_timer(&status_timer);
+	do {
+		schedule();
+		status = inb(chip->base + NSC_STATUS);
+		if (status & NSC_STATUS_OBF)
+			status = inb(chip->base + NSC_DATA);
+		if (status & NSC_STATUS_RDY) {
+			del_singleshot_timer_sync(&status_timer);
+			return 0;
+		}
+	} while (!expired);
+
+	dev_info(&chip->pci_dev->dev, "wait for ready failed\n");
+	return -EBUSY;
+}
+
+static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 *buffer = buf;
+	u8 data, *p;
+	u32 size;
+	__be32 *native_size;
+
+	if (count < 6)
+		return -EIO;
+
+	if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "F0 timeout\n");
+		return -EIO;
+	}
+	if ((data = inb(chip->base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
+		dev_err(&chip->pci_dev->dev,
+			"not in normal mode (0x%x)\n", data);
+		return -EIO;
+	}
+
+	/* read the whole packet */
+	for (p = buffer; p < &buffer[count]; p++) {
+		if (wait_for_stat(chip, NSC_STATUS_OBF,
+				  NSC_STATUS_OBF, &data) < 0) {
+			dev_err(&chip->pci_dev->dev,
+				"OBF timeout (while reading data)\n");
+			return -EIO;
+		}
+		if (data & NSC_STATUS_F0)
+			break;
+		*p = inb(chip->base + NSC_DATA);
+	}
+
+	if ((data & NSC_STATUS_F0) == 0) {
+		dev_err(&chip->pci_dev->dev, "F0 not set\n");
+		return -EIO;
+	}
+	if ((data = inb(chip->base + NSC_DATA)) != NSC_COMMAND_EOC) {
+		dev_err(&chip->pci_dev->dev,
+			"expected end of command(0x%x)\n", data);
+		return -EIO;
+	}
+
+	native_size = (__force __be32 *) (buf + 2);
+	size = be32_to_cpu(*native_size);
+
+	if (count < size)
+		return -EIO;
+
+	return size;
+}
+
+static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 data;
+	int i;
+
+	/*
+	 * If we hit the chip with back to back commands it locks up
+	 * and never set IBF. Hitting it with this "hammer" seems to
+	 * fix it. Not sure why this is needed, we followed the flow
+	 * chart in the manual to the letter.
+	 */
+	outb(NSC_COMMAND_CANCEL, chip->base + NSC_COMMAND);
+
+	if (nsc_wait_for_ready(chip) != 0)
+		return -EIO;
+
+	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+		return -EIO;
+	}
+
+	outb(NSC_COMMAND_NORMAL, chip->base + NSC_COMMAND);
+	if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBR timeout\n");
+		return -EIO;
+	}
+
+	for (i = 0; i < count; i++) {
+		if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+			dev_err(&chip->pci_dev->dev,
+				"IBF timeout (while writing data)\n");
+			return -EIO;
+		}
+		outb(buf[i], chip->base + NSC_DATA);
+	}
+
+	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+		return -EIO;
+	}
+	outb(NSC_COMMAND_EOC, chip->base + NSC_COMMAND);
+
+	return count;
+}
+
+static void tpm_nsc_cancel(struct tpm_chip *chip)
+{
+	outb(NSC_COMMAND_CANCEL, chip->base + NSC_COMMAND);
+}
+
+/*
+ * Atmel specific code
+ */
+static int tpm_atml_init(struct tpm_chip *chip)
+{
+	u8 version[4];
+
+	chip->irq = 0;
+	chip->base = TPM_ATML_BASE;
+
+	if (lpc_bus_init(chip))
+		return -ENODEV;
+
+	/* verify that it is an Atmel part */
+	if (rdx(4) != 'A' || rdx(5) != 'T' || rdx(6) != 'M'
+	    || rdx(7) != 'L') {
+		return -ENODEV;
+	}
+
+	/* query chip for its version number */
+	if ((version[0] = rdx(0x00)) != 0xFF) {
+		version[1] = rdx(0x01);
+		version[2] = rdx(0x02);
+		version[3] = rdx(0x03);
+	} else {
+		dev_info(&chip->pci_dev->dev, "version query failed\n");
+		return -ENODEV;
+	}
+
+	dev_info(&chip->pci_dev->dev,
+		 "Atmel TPM version %d.%d.%d.%d\n", version[0], version[1],
+		 version[2], version[3]);
+
+	return 0;
+}
+
+static int tpm_atml_recv(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 status, *hdr = buf;
+	u32 size;
+	int i;
+	__be32 *native_size;
+
+	/* start reading header */
+	if (count < 6)
+		return -EIO;
+
+	for (i = 0; i < 6; i++) {
+		status = inb(chip->base + 1);
+		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+			dev_err(&chip->pci_dev->dev,
+				"error reading header\n");
+			return -EIO;
+		}
+		*buf++ = inb(chip->base);
+	}
+
+	/* size of the data received */
+	native_size = (__force __be32 *) (hdr + 2);
+	size = be32_to_cpu(*native_size);
+
+	if (count < size)
+		return -EIO;
+
+	/* read all the data available */
+	for (; i < size; i++) {
+		status = inb(chip->base + 1);
+		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+			dev_err(&chip->pci_dev->dev,
+				"error reading data\n");
+			return -EIO;
+		}
+		*buf++ = inb(chip->base);
+	}
+
+	/* make sure data available is gone */
+	status = inb(chip->base + 1);
+	if (status & ATML_STATUS_DATA_AVAIL) {
+		dev_err(&chip->pci_dev->dev, "data available is stuck\n");
+		return -EIO;
+	}
+
+	return size;
+}
+
+static int tpm_atml_send(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	int i;
+
+	dev_dbg(&chip->pci_dev->dev, "tpm_atml_send: ");
+	for (i = 0; i < count; i++)
+		dev_dbg(&chip->pci_dev->dev, "0x%x(%d) ", buf[i], buf[i]);
+
+	for (i = 0; i < count; i++)
+		outb(buf[i], chip->base);
+
+	return count;
+}
+
+static void tpm_atml_cancel(struct tpm_chip *chip)
+{
+	outb(ATML_STATUS_ABORT, chip->base + 1);
+}
+
+/*
+ * Internal kernel interface to transmit TPM commands
+ */
+static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
+			    size_t bufsiz)
+{
+	ssize_t len;
+	u32 count;
+	__be32 *native_size;
+
+	native_size = (__force __be32 *) (buf + 2);
+	count = be32_to_cpu(*native_size);
+
+	if (count == 0)
+		return -ENODATA;
+	if (count > bufsiz) {
+		dev_err(&chip->pci_dev->dev,
+			"invalid count value %x %x \n", count, bufsiz);
+		return -E2BIG;
+	}
+
+	if ((len = chip->send(chip, (u8 *) buf, count)) < 0) {
+		dev_err(&chip->pci_dev->dev,
+			"tpm_transmit: tpm_send: error %d\n", len);
+		return len;
+	}
+
+	chip->tpm_time_expired = 0;
+	init_timer(&chip->tpm_timer);
+	chip->tpm_timer.function = tpm_time_expired;
+	chip->tpm_timer.expires = jiffies + 2 * 60 * HZ;
+	chip->tpm_timer.data = (unsigned long) &chip->tpm_time_expired;
+	add_timer(&chip->tpm_timer);
+
+	while (!chip->tpm_time_expired) {
+		u8 status = inb(chip->base + 1);
+		if ((status & chip->req_complete_mask) ==
+		    chip->req_complete_val) {
+			del_singleshot_timer_sync(&chip->tpm_timer);
+			break;
+		}
+		schedule();
+	}
+
+	if (!chip->tpm_time_expired)
+		len = chip->recv(chip, (u8 *) buf, bufsiz);
+	else {
+		chip->cancel(chip);
+		len = -EIO;
+		dev_err(&chip->pci_dev->dev, "Time expired\n");
+	}
+	if (len < 0)
+		dev_err(&chip->pci_dev->dev,
+			"tpm_transmit: tpm_recv: error %d\n", len);
+
+	return len;
+}
+
+/*
+ * Device file system interface to the TPM
+ */
+static int tpm_open(struct inode *inode, struct file *file)
+{
+	int rc = 0, minor = iminor(inode);
+	struct tpm_chip *chip = NULL, *pos;
+
+	spin_lock(&driver_lock);
+
+	list_for_each_entry(pos, &tpm_chip_list, list) {
+		if (pos->miscdev.minor == minor) {
+			chip = pos;
+			break;
+		}
+	}
+	if (chip == NULL) {
+		rc = -ENODEV;
+		goto err_out;
+	}
+
+	if (chip->num_opens) {
+		dev_dbg(&chip->pci_dev->dev,
+			"Another process owns this TPM\n");
+		rc = -EBUSY;
+		goto err_out;
+	}
+
+	chip->num_opens++;
+	pci_dev_get(chip->pci_dev);
+
+	spin_unlock(&driver_lock);
+
+	chip->userspace_buffer =
+	    kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
+	if (chip->userspace_buffer == NULL) {
+		chip->num_opens--;
+		pci_dev_put(chip->pci_dev);
+		return -ENOMEM;
+	}
+
+	atomic_set(&chip->data_pending, 0);
+
+	file->private_data = chip;
+	return 0;
+
+err_out:
+	spin_unlock(&driver_lock);
+	return rc;
+}
+
+static int tpm_release(struct inode *inode, struct file *file)
+{
+	struct tpm_chip *chip = file->private_data;
+
+	if (chip == NULL)
+		return -ENODEV;
+
+	spin_lock(&driver_lock);
+	chip->num_opens--;
+	if (chip->num_opens == 0) {
+		kfree(chip->userspace_buffer);
+		atomic_set(&chip->data_pending, 0);
+	}
+
+	pci_dev_put(chip->pci_dev);
+	file->private_data = NULL;
+	spin_unlock(&driver_lock);
+	return 0;
+}
+
+static ssize_t tpm_write(struct file *file, const char __user * buf,
+			 size_t size, loff_t * off)
+{
+	struct tpm_chip *chip = file->private_data;
+	int out_size;
+
+	if (chip == NULL)
+		return -ENODEV;
+
+	down(&chip->user_mutex);
+
+	if (copy_from_user(chip->userspace_buffer,
+			   (void __user *) buf, size)) {
+		up(&chip->user_mutex);
+		return -EFAULT;
+	}
+	out_size = tpm_transmit(chip, chip->userspace_buffer, TPM_BUFSIZE);
+
+	init_timer(&chip->user_read_timer);
+	chip->user_read_timer.function = user_reader_timeout;
+	chip->user_read_timer.data = (unsigned long) chip;
+	chip->user_read_timer.expires = jiffies + (60 * HZ);
+	add_timer(&chip->user_read_timer);
+
+	atomic_set(&chip->data_pending, out_size);
+
+	return size;
+}
+
+static ssize_t tpm_read(struct file *file, char __user * buf,
+			size_t size, loff_t * off)
+{
+	struct tpm_chip *chip = file->private_data;
+	int write_size;
+
+	if (chip == NULL)
+		return -ENODEV;
+
+	if (down_trylock(&chip->timer_mutex) != 0) {
+		dev_err(&chip->pci_dev->dev, "Timeout occured\n");
+		return -ETIME;
+	}
+
+	write_size = atomic_read(&chip->data_pending);
+	atomic_set(&chip->data_pending, 0);
+
+	if (write_size == 0) {
+		dev_err(&chip->pci_dev->dev, "No data pending\n");
+		up(&chip->timer_mutex);
+		return -ENODATA;
+	}
+
+	del_singleshot_timer_sync(&chip->user_read_timer);
+	up(&chip->timer_mutex);
+
+	if (write_size < 0)
+		goto out;
+
+	if (size < write_size)
+		write_size = size;
+
+	if (copy_to_user((void __user *) buf,
+			 chip->userspace_buffer, write_size)) {
+		write_size = -EFAULT;
+		goto out;
+	}
+
+out:
+	up(&chip->user_mutex);
+	return write_size;
+}
+
+static struct file_operations tpm_fops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.open = tpm_open,
+	.read = tpm_read,
+	.write = tpm_write,
+	.release = tpm_release,
+};
+
+static int __devinit tpm_probe(struct pci_dev *pci_dev,
+			       const struct pci_device_id *pci_id)
+{
+	char *devname = "tpm ";
+	int rc = 0;
+	struct tpm_chip *chip;
+
+	if (pci_enable_device(pci_dev))
+		return -EIO;
+
+	/* Driver specific per-device data */
+	chip = kmalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		rc = -ENOMEM;
+		goto out_disable;
+	}
+	memset(chip, 0, sizeof(struct tpm_chip));
+
+	init_MUTEX(&chip->user_mutex);
+	init_MUTEX(&chip->timer_mutex);
+	INIT_LIST_HEAD(&chip->list);
+
+	if (dev_cnt == 0)
+		chip->miscdev.minor = TPM_MINOR;
+	else
+		chip->miscdev.minor = MISC_DYNAMIC_MINOR;
+
+	devname[3] = (u8) (dev_cnt + '0');
+	chip->miscdev.name = devname;
+
+	chip->miscdev.dev = &(pci_dev->dev);
+	chip->miscdev.fops = &tpm_fops;
+	chip->pci_dev = pci_dev_get(pci_dev);
+
+	spin_lock(&driver_lock);
+
+	/* Determine chip type */
+	if (tpm_nsc_init(chip) == 0) {
+		chip->recv = tpm_nsc_recv;
+		chip->send = tpm_nsc_send;
+		chip->cancel = tpm_nsc_cancel;
+		chip->req_complete_mask = NSC_STATUS_OBF;
+		chip->req_complete_val = NSC_STATUS_OBF;
+	} else if (tpm_atml_init(chip) == 0) {
+		chip->recv = tpm_atml_recv;
+		chip->send = tpm_atml_send;
+		chip->cancel = tpm_atml_cancel;
+		chip->req_complete_mask =
+		    ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL;
+		chip->req_complete_val = ATML_STATUS_DATA_AVAIL;
+	} else {
+		rc = -ENODEV;
+		goto out_release;
+	}
+
+	if (misc_register(&chip->miscdev)) {
+		dev_err(&chip->pci_dev->dev,
+			"unable to misc_register %s, minor %d\n",
+			chip->miscdev.name, chip->miscdev.minor);
+		rc = -ENODEV;
+		goto out_release;
+	}
+
+	pci_set_drvdata(pci_dev, chip);
+
+	list_add(&chip->list, &tpm_chip_list);
+	spin_unlock(&driver_lock);
+
+	dev_cnt++;
+	return 0;
+
+	/* Error path */
+out_release:
+	spin_unlock(&driver_lock);
+	pci_dev_put(pci_dev);
+	kfree(chip);
+out_disable:
+	pci_disable_device(pci_dev);
+	return rc;
+}
+
+static void __devexit tpm_remove(struct pci_dev *pci_dev)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+
+	if (chip == NULL) {
+		dev_err(&pci_dev->dev, "No device data found\n");
+		return;
+	}
+
+	spin_lock(&driver_lock);
+
+	if (chip->num_opens != 0) {
+		dev_err(&chip->pci_dev->dev,
+			"Device still open (%d times) in userspace\n",
+			chip->num_opens);
+		spin_unlock(&driver_lock);
+		return;
+	}
+
+	list_del(&chip->list);
+
+	pci_set_drvdata(pci_dev, NULL);
+
+	misc_deregister(&chip->miscdev);
+	spin_unlock(&driver_lock);
+
+	pci_disable_device(pci_dev);
+
+	kfree(chip);
+	dev_cnt--;
+
+	pci_dev_put(pci_dev);
+}
+
+static u8 savestate[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 10,		/* blob length (in bytes) */
+	0, 0, 0, 152		/* TPM_ORD_SaveState */
+};
+
+/*
+ * We are about to suspend. Save the TPM state
+ * so that it can be restored.
+ */
+static int tpm_pm_suspend(struct pci_dev *pci_dev, u32 pm_state)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+	if (chip == NULL)
+		return -ENODEV;
+
+	chip->send(chip, savestate, sizeof(savestate));
+	return 0;
+}
+
+/*
+ * Resume from a power safe. The BIOS already restored
+ * the TPM state.
+ */
+static int tpm_pm_resume(struct pci_dev *pci_dev)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+	if (chip == NULL)
+		return -ENODEV;
+
+	spin_lock(&driver_lock);
+	lpc_bus_init(chip);
+	spin_unlock(&driver_lock);
+
+	return 0;
+}
+
+static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
+
+static struct pci_driver tpm_pci_driver = {
+	.name = "tpm",
+	.id_table = tpm_pci_tbl,
+	.probe = tpm_probe,
+	.remove = __devexit_p(tpm_remove),
+	.suspend = tpm_pm_suspend,
+	.resume = tpm_pm_resume,
+};
+
+static int __init init_tpm(void)
+{
+	INIT_LIST_HEAD(&tpm_chip_list);
+	spin_lock_init(&driver_lock);
+	return pci_register_driver(&tpm_pci_driver);
+}
+
+static void __exit cleanup_tpm(void)
+{
+	pci_unregister_driver(&tpm_pci_driver);
+}
+
+module_init(init_tpm);
+module_exit(cleanup_tpm);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.9/include/linux/pci_ids.h linux-2.6.9-tpm/include/linux/pci_ids.h
--- linux-2.6.9/include/linux/pci_ids.h	2004-12-06 16:53:35.000000000 -0600
+++ linux-2.6.9-tpm/include/linux/pci_ids.h	2004-12-06 14:27:05.000000000 -0600
@@ -494,6 +494,7 @@
 #define PCI_DEVICE_ID_AMD_OPUS_7449	0x7449
 #	define PCI_DEVICE_ID_AMD_VIPER_7449	PCI_DEVICE_ID_AMD_OPUS_7449
 #define PCI_DEVICE_ID_AMD_8111_LAN	0x7462
+#define PCI_DEVICE_ID_AMD_8111_LPC	0x7468
 #define PCI_DEVICE_ID_AMD_8111_IDE	0x7469
 #define PCI_DEVICE_ID_AMD_8111_AUDIO	0x746d
 #define PCI_DEVICE_ID_AMD_8151_0	0x7454
diff -uprN linux-2.6.9/MAINTAINERS linux-2.6.9-tpm/MAINTAINERS
--- linux-2.6.9/MAINTAINERS	2004-10-18 16:54:37.000000000 -0500
+++ linux-2.6.9-tpm/MAINTAINERS	2004-12-07 12:39:10.000000000 -0600
@@ -2144,6 +2144,12 @@ L:	tlinux-users@tce.toshiba-dme.co.jp
 W:	http://www.buzzard.org.uk/toshiba/
 S:	Maintained
 
+TPM DRIVER
+P:	Kylene Hall
+M:	tpmdd-devel@lists.sourceforge.net
+L:	tpmdd-devel@lists.sourceforge.net
+S:	Maintained
+
 TRIDENT 4DWAVE/SIS 7018 PCI AUDIO CORE
 P:	Muli Ben-Yehuda
 M:	mulix@mulix.org

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement
  2004-12-09 15:25 [PATCH 1/1] driver: Tpm hardware enablement Kylene Hall
@ 2004-12-09 15:48 ` Arjan van de Ven
  2004-12-09 17:06   ` Kylie Hall
  2004-12-10 20:45   ` Alan Cox
  2004-12-10 10:56 ` Ian Campbell
  2004-12-16 22:37 ` [PATCH 1/1] driver: Tpm hardware enablement --updated version Kylene Hall
  2 siblings, 2 replies; 43+ messages in thread
From: Arjan van de Ven @ 2004-12-09 15:48 UTC (permalink / raw)
  To: Kylene Hall
  Cc: linux-kernel, greg, sailer, leendert, emilyr, toml, tpmdd-devel

On Thu, 2004-12-09 at 09:25 -0600, Kylene Hall wrote:
> +	/* wait for status */
> +	add_timer(&status_timer);
> +	do {
> +		schedule();
> +		*data = inb(chip->base + 1);
> +		if ((*data & mask) == val) {
> +			del_singleshot_timer_sync(&status_timer);
> +			return 0;
> +		}
> +	} while (!expired);

this is busy waiting. Can't you do it with msleep() or some such ?
Or like 100 iterations without delays (in case the chip returns fast),
and then start sleeping, but please do sleep for a real time, not just
yield the cpu. Powermanagement and lots of other things really like to
see that.
> +	/* wait for status */
> +	add_timer(&status_timer);
> +	do {
> +		schedule();
> +		status = inb(chip->base + NSC_STATUS);
> +		if (status & NSC_STATUS_OBF)
> +			status = inb(chip->base + NSC_DATA);
> +		if (status & NSC_STATUS_RDY) {
> +			del_singleshot_timer_sync(&status_timer);
> +			return 0;
> +		}
> +	} while (!expired);

same comment. Also the timer handling looks suspect... can you guarantee
100% sure that the timer is gone when the while falls through ?


> +	chip->userspace_buffer =
> +	    kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);

that sounds like a really deceptive name to me ... since it's kernel
memory ;)

> +static int tpm_release(struct inode *inode, struct file *file)
> +{
> +	struct tpm_chip *chip = file->private_data;
> +
> +	if (chip == NULL)
> +		return -ENODEV;
> +
> +	spin_lock(&driver_lock);
> +	chip->num_opens--;

why do you need to keep track of the number of openers? Can't you have
the kernel fs layer keep track of this ?
> +	chip->user_read_timer.function = user_reader_timeout;
> +	chip->user_read_timer.data = (unsigned long) chip;
> +	chip->user_read_timer.expires = jiffies + (60 * HZ);
> +	add_timer(&chip->user_read_timer);
> +
> +	atomic_set(&chip->data_pending, out_size);
> +
> +	return size;

what prevents the module from being unloaded ?
(eg user calls write(); close(); and then does rmmod before the timer
expires )

> +/*
> + * Resume from a power safe. The BIOS already restored
> + * the TPM state.
> + */

are there any special security things needed after resume ?
Or maybe at suspend time, to wipe secrets from the TPM or somesuch..





^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement
  2004-12-09 15:48 ` Arjan van de Ven
@ 2004-12-09 17:06   ` Kylie Hall
  2004-12-11  8:31     ` Nish Aravamudan
  2004-12-10 20:45   ` Alan Cox
  1 sibling, 1 reply; 43+ messages in thread
From: Kylie Hall @ 2004-12-09 17:06 UTC (permalink / raw)
  To: Arjan van de Ven
  Cc: linux-kernel, greg, sailer, leendert, Emily Ratliff,
	Tom Lendacky, tpmdd-devel

On Thu, 2004-12-09 at 09:48, Arjan van de Ven wrote: 
> On Thu, 2004-12-09 at 09:25 -0600, Kylene Hall wrote:
> > +	/* wait for status */
> > +	add_timer(&status_timer);
> > +	do {
> > +		schedule();
> > +		*data = inb(chip->base + 1);
> > +		if ((*data & mask) == val) {
> > +			del_singleshot_timer_sync(&status_timer);
> > +			return 0;
> > +		}
> > +	} while (!expired);
> 
> this is busy waiting. Can't you do it with msleep() or some such ?
> Or like 100 iterations without delays (in case the chip returns fast),
> and then start sleeping, but please do sleep for a real time, not just
> yield the cpu. Powermanagement and lots of other things really like to
> see that.
I don't see a problem with changing the schedule to an msleep.  I'll
change it.

> > +	/* wait for status */
> > +	add_timer(&status_timer);
> > +	do {
> > +		schedule();
> > +		status = inb(chip->base + NSC_STATUS);
> > +		if (status & NSC_STATUS_OBF)
> > +			status = inb(chip->base + NSC_DATA);
> > +		if (status & NSC_STATUS_RDY) {
> > +			del_singleshot_timer_sync(&status_timer);
> > +			return 0;
> > +		}
> > +	} while (!expired);
> 
> same comment. Also the timer handling looks suspect... can you guarantee
> 100% sure that the timer is gone when the while falls through ?
> 
Since the only way that expired becomes non-zero is for the timer to
expire and since the function that the timer calls on expiration does
not restart the timer the timer will be gone when the while falls
through.

> 
> > +	chip->userspace_buffer =
> > +	    kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
> 
> that sounds like a really deceptive name to me ... since it's kernel
> memory ;)
Agreed.  It will be changed.

> > +static int tpm_release(struct inode *inode, struct file *file)
> > +{
> > +	struct tpm_chip *chip = file->private_data;
> > +
> > +	if (chip == NULL)
> > +		return -ENODEV;
> > +
> > +	spin_lock(&driver_lock);
> > +	chip->num_opens--;
> 
> why do you need to keep track of the number of openers? Can't you have
> the kernel fs layer keep track of this ?
The specification only allows the Trusted Software Stack (TSS)
application to open/read/write to the device.  I am keeping track of the
number of opens to only allow one open (for the TSS).

> > +	chip->user_read_timer.function = user_reader_timeout;
> > +	chip->user_read_timer.data = (unsigned long) chip;
> > +	chip->user_read_timer.expires = jiffies + (60 * HZ);
> > +	add_timer(&chip->user_read_timer);
> > +
> > +	atomic_set(&chip->data_pending, out_size);
> > +
> > +	return size;
> 
> what prevents the module from being unloaded ?
> (eg user calls write(); close(); and then does rmmod before the timer
> expires )
> 
You're right this is a problem.  I think the solution is to put locks around the timer 
manipulation code and make a call to timer_pending in the close function.  If the timer
is pending I will then call del_timer from close.  Sound reasonable?
> 
> > +/*
> > + * Resume from a power safe. The BIOS already restored
> > + * the TPM state.
> > + */
> 
> are there any special security things needed after resume ?
> Or maybe at suspend time, to wipe secrets from the TPM or somesuch..
> 
Nothing that I am aware of.

Thanks,
Kylene



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement
  2004-12-09 15:25 [PATCH 1/1] driver: Tpm hardware enablement Kylene Hall
  2004-12-09 15:48 ` Arjan van de Ven
@ 2004-12-10 10:56 ` Ian Campbell
  2004-12-10 15:28   ` Kylene Hall
  2004-12-16 22:37 ` [PATCH 1/1] driver: Tpm hardware enablement --updated version Kylene Hall
  2 siblings, 1 reply; 43+ messages in thread
From: Ian Campbell @ 2004-12-10 10:56 UTC (permalink / raw)
  To: Kylene Hall
  Cc: linux-kernel, greg, sailer, leendert, emilyr, toml, tpmdd-devel

Hi, 

On Thu, 2004-12-09 at 09:25 -0600, Kylene Hall wrote:
> +	/* Determine chip type */
> +	if (tpm_nsc_init(chip) == 0) {
> +		chip->recv = tpm_nsc_recv;
> +		chip->send = tpm_nsc_send;
> +		chip->cancel = tpm_nsc_cancel;
> +		chip->req_complete_mask = NSC_STATUS_OBF;
> +		chip->req_complete_val = NSC_STATUS_OBF;
> +	} else if (tpm_atml_init(chip) == 0) {
> +		chip->recv = tpm_atml_recv;
> +		chip->send = tpm_atml_send;
> +		chip->cancel = tpm_atml_cancel;
> +		chip->req_complete_mask =
> +		    ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL;
> +		chip->req_complete_val = ATML_STATUS_DATA_AVAIL;
> +	} else {
> +		rc = -ENODEV;
> +		goto out_release;
> +	}

The atmel part at least also comes as an I2C variant. 

We could continue to add to the ifelse here but perhaps it might be
beneficial to split the individual chip specific stuff into separate
files now and perhaps register them via some sort of
register_tpm_hardware(struct tpm_chip_ops *) type interface?

Ian.

-- 
Ian Campbell, Senior Design Engineer
                                        Web: http://www.arcom.com
Arcom, Clifton Road,                    Direct: +44 (0)1223 403 465
Cambridge CB1 7EA, United Kingdom       Phone:  +44 (0)1223 411 200


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement
  2004-12-10 10:56 ` Ian Campbell
@ 2004-12-10 15:28   ` Kylene Hall
  2004-12-10 15:41     ` Ian Campbell
  0 siblings, 1 reply; 43+ messages in thread
From: Kylene Hall @ 2004-12-10 15:28 UTC (permalink / raw)
  To: Ian Campbell
  Cc: linux-kernel, greg, sailer, leendert, Emily Ratliff,
	Tom Lendacky, tpmdd-devel

On Fri, 2004-12-10 at 04:56, Ian Campbell wrote:
> Hi, 
> 
> On Thu, 2004-12-09 at 09:25 -0600, Kylene Hall wrote:
> > +	/* Determine chip type */
> > +	if (tpm_nsc_init(chip) == 0) {
> > +		chip->recv = tpm_nsc_recv;
> > +		chip->send = tpm_nsc_send;
> > +		chip->cancel = tpm_nsc_cancel;
> > +		chip->req_complete_mask = NSC_STATUS_OBF;
> > +		chip->req_complete_val = NSC_STATUS_OBF;
> > +	} else if (tpm_atml_init(chip) == 0) {
> > +		chip->recv = tpm_atml_recv;
> > +		chip->send = tpm_atml_send;
> > +		chip->cancel = tpm_atml_cancel;
> > +		chip->req_complete_mask =
> > +		    ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL;
> > +		chip->req_complete_val = ATML_STATUS_DATA_AVAIL;
> > +	} else {
> > +		rc = -ENODEV;
> > +		goto out_release;
> > +	}
> 
> The atmel part at least also comes as an I2C variant. 
> 
> We could continue to add to the ifelse here but perhaps it might be
> beneficial to split the individual chip specific stuff into separate
> files now and perhaps register them via some sort of
> register_tpm_hardware(struct tpm_chip_ops *) type interface?
> 
Good point.  Splitting this out (esp. because there will be more in the
future) is a good idea.  What is the usual way to do this?  For example,
what function in the chip specific file would call
register_tpm_hardware, how do I make sure that gets called etc.

Thanks,
Kylene

> Ian.


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement
  2004-12-10 15:28   ` Kylene Hall
@ 2004-12-10 15:41     ` Ian Campbell
  2004-12-10 18:39       ` [tpmdd-devel] " Kylene Hall
  0 siblings, 1 reply; 43+ messages in thread
From: Ian Campbell @ 2004-12-10 15:41 UTC (permalink / raw)
  To: Kylene Hall
  Cc: linux-kernel, greg, sailer, leendert, Emily Ratliff,
	Tom Lendacky, tpmdd-devel

On Fri, 2004-12-10 at 09:28 -0600, Kylene Hall wrote:
> Good point.  Splitting this out (esp. because there will be more in the
> future) is a good idea.  What is the usual way to do this?  For example,
> what function in the chip specific file would call
> register_tpm_hardware, how do I make sure that gets called etc.

I guess you could have multiple modules, one providing the generic code
and the dev interface etc (tpm.ko) and then one per hardware chip
(tmp-nsc.ko, tpm-atmel.ko, tpm-atmel-i2c.ko). 

The hardware modules can then call tpm_register_hardware() in their
module_init function. 

Perhaps it would make sense to put the PCI bus stuff and things like
that in the hardware module as well, since e.g. an i2c chip would not
fit into that model

Ian.
-- 
Ian Campbell, Senior Design Engineer
                                        Web: http://www.arcom.com
Arcom, Clifton Road,                    Direct: +44 (0)1223 403 465
Cambridge CB1 7EA, United Kingdom       Phone:  +44 (0)1223 411 200


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [tpmdd-devel] Re: [PATCH 1/1] driver: Tpm hardware enablement
  2004-12-10 15:41     ` Ian Campbell
@ 2004-12-10 18:39       ` Kylene Hall
  2004-12-14  9:59         ` Ian Campbell
  0 siblings, 1 reply; 43+ messages in thread
From: Kylene Hall @ 2004-12-10 18:39 UTC (permalink / raw)
  To: Ian Campbell
  Cc: linux-kernel, greg, sailer, leendert, Emily Ratliff,
	Tom Lendacky, tpmdd-devel

On Fri, 2004-12-10 at 09:41, Ian Campbell wrote:
> On Fri, 2004-12-10 at 09:28 -0600, Kylene Hall wrote:
> > Good point.  Splitting this out (esp. because there will be more in the
> > future) is a good idea.  What is the usual way to do this?  For example,
> > what function in the chip specific file would call
> > register_tpm_hardware, how do I make sure that gets called etc.
> 
> I guess you could have multiple modules, one providing the generic code
> and the dev interface etc (tpm.ko) and then one per hardware chip
> (tmp-nsc.ko, tpm-atmel.ko, tpm-atmel-i2c.ko). 
> 
> The hardware modules can then call tpm_register_hardware() in their
> module_init function. 
> 
I have begun to implement it this but the problem I have now is that
this setup makes tpm_atmel and tpm_nsc dependent on tpm.  Since tpm
calls pci_register_driver in its init (which has to happen before
tpm_<specific> init) probe is called before the "interfaces" are
registered and thus the tpm_probe fails to find the device.  Do I move
the pci_register?  If so what is the proper place to register it? When
one interface registers?  If so then I think devices for the second and
subsequent interfaces would never be discovered for the same reason as I
am currently experiencing.  Do I need to move the current tpm probe
logic to the hw specific drivers?

Thanks,
Kylene


> Ian.


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement
  2004-12-09 15:48 ` Arjan van de Ven
  2004-12-09 17:06   ` Kylie Hall
@ 2004-12-10 20:45   ` Alan Cox
  1 sibling, 0 replies; 43+ messages in thread
From: Alan Cox @ 2004-12-10 20:45 UTC (permalink / raw)
  To: Arjan van de Ven
  Cc: Kylene Hall, Linux Kernel Mailing List, greg, sailer, leendert,
	emilyr, toml, tpmdd-devel

On Iau, 2004-12-09 at 15:48, Arjan van de Ven wrote:
> > +	/* wait for status */
> > +	add_timer(&status_timer);
> > +	do {
> > +		schedule();
> > +		status = inb(chip->base + NSC_STATUS);
> > +		if (status & NSC_STATUS_OBF)
> > +			status = inb(chip->base + NSC_DATA);
> > +		if (status & NSC_STATUS_RDY) {
> > +			del_singleshot_timer_sync(&status_timer);
> > +			return 0;
> > +		}
> > +	} while (!expired);
> 
> same comment. Also the timer handling looks suspect... can you guarantee
> 100% sure that the timer is gone when the while falls through ?

Yes but you can't be sure it ever will - needs an "rmb()" barrier so
that the compiler doesn't sneak off and optimise expired



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement
  2004-12-09 17:06   ` Kylie Hall
@ 2004-12-11  8:31     ` Nish Aravamudan
  0 siblings, 0 replies; 43+ messages in thread
From: Nish Aravamudan @ 2004-12-11  8:31 UTC (permalink / raw)
  To: Kylie Hall
  Cc: Arjan van de Ven, linux-kernel, greg, sailer, leendert,
	Emily Ratliff, Tom Lendacky, tpmdd-devel

On Thu, 09 Dec 2004 11:06:27 -0600, Kylie Hall <kjhall@us.ibm.com> wrote:
> On Thu, 2004-12-09 at 09:48, Arjan van de Ven wrote:
> 
> 
> > On Thu, 2004-12-09 at 09:25 -0600, Kylene Hall wrote:
> > > +   /* wait for status */
> > > +   add_timer(&status_timer);
> > > +   do {
> > > +           schedule();
> > > +           *data = inb(chip->base + 1);
> > > +           if ((*data & mask) == val) {
> > > +                   del_singleshot_timer_sync(&status_timer);
> > > +                   return 0;
> > > +           }
> > > +   } while (!expired);
> >
> > this is busy waiting. Can't you do it with msleep() or some such ?
> > Or like 100 iterations without delays (in case the chip returns fast),
> > and then start sleeping, but please do sleep for a real time, not just
> > yield the cpu. Powermanagement and lots of other things really like to
> > see that.
> I don't see a problem with changing the schedule to an msleep.  I'll
> change it.

Keep in mind that msleep() will ignore all signals & waitqueue events
(the latter doesn't apply here, I don't think) until the specified
number of milliseconds has gone by. msleep_interruptible() may be
preferrable, as you will then receive signals (but still not waitqueue
events). I'm actually not sure if either of these will do what you
need as the existing code does. You may just want to use
schedule_timeout() appropriately, with the remaining time you wish to
sleep for.

-Nish

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [tpmdd-devel] Re: [PATCH 1/1] driver: Tpm hardware enablement
  2004-12-10 18:39       ` [tpmdd-devel] " Kylene Hall
@ 2004-12-14  9:59         ` Ian Campbell
  0 siblings, 0 replies; 43+ messages in thread
From: Ian Campbell @ 2004-12-14  9:59 UTC (permalink / raw)
  To: Kylene Hall
  Cc: linux-kernel, greg, sailer, leendert, Emily Ratliff,
	Tom Lendacky, tpmdd-devel

On Fri, 2004-12-10 at 12:39 -0600, Kylene Hall wrote:
> On Fri, 2004-12-10 at 09:41, Ian Campbell wrote:
> > On Fri, 2004-12-10 at 09:28 -0600, Kylene Hall wrote:
> > > Good point.  Splitting this out (esp. because there will be more in the
> > > future) is a good idea.  What is the usual way to do this?  For example,
> > > what function in the chip specific file would call
> > > register_tpm_hardware, how do I make sure that gets called etc.
> > 
> > I guess you could have multiple modules, one providing the generic code
> > and the dev interface etc (tpm.ko) and then one per hardware chip
> > (tmp-nsc.ko, tpm-atmel.ko, tpm-atmel-i2c.ko). 
> > 
> > The hardware modules can then call tpm_register_hardware() in their
> > module_init function. 
> > 
> I have begun to implement it this but the problem I have now is that
> this setup makes tpm_atmel and tpm_nsc dependent on tpm.  Since tpm
> calls pci_register_driver in its init (which has to happen before
> tpm_<specific> init) probe is called before the "interfaces" are
> registered and thus the tpm_probe fails to find the device.  Do I move
> the pci_register?  If so what is the proper place to register it? When
> one interface registers?  If so then I think devices for the second and
> subsequent interfaces would never be discovered for the same reason as I
> am currently experiencing.  Do I need to move the current tpm probe
> logic to the hw specific drivers?

I'm not too sure about the relationship between the different types of
TPM chip you seem to be coping with and the PCI id's associated with
them. I think the normal way would be to have a separate self-contained
driver encapsulating the complete hardware specific bits for each
possible type of pci hardware device, or indeed i2c device rather than
mixing support for multiple different chips in a single driver.

I think often you would have tpm_atmel's module_init contain a call to
pci_register_driver to register the device id's associated with the
Atmel parts. Then the probe call does tpm_<specific>_init and registers
the tpm h/w device with the hardware independent bit using
tpm_register_hardware().

I would then be able to support the i2c variant of the atmel part by
registering an i2c part instead of a PCI one and calling
tpm_register_hardware from the equivalent i2c probe function.

Ian.

-- 
Ian Campbell, Senior Design Engineer
                                        Web: http://www.arcom.com
Arcom, Clifton Road,                    Direct: +44 (0)1223 403 465
Cambridge CB1 7EA, United Kingdom       Phone:  +44 (0)1223 411 200


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement --updated version
  2004-12-09 15:25 [PATCH 1/1] driver: Tpm hardware enablement Kylene Hall
  2004-12-09 15:48 ` Arjan van de Ven
  2004-12-10 10:56 ` Ian Campbell
@ 2004-12-16 22:37 ` Kylene Hall
  2004-12-16 22:48   ` Greg KH
                     ` (3 more replies)
  2 siblings, 4 replies; 43+ messages in thread
From: Kylene Hall @ 2004-12-16 22:37 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, sailer, leendert, emilyr, toml, tpmdd-devel

This patch is a device driver to enable new hardware.  The new hardware is
the TPM chip as described by specifications at trustedcomputinggroup.org.
The TPM chip will enable you to use hardware to securely store and protect
your keys and personal data.  To use the chip according to the 
specification, you will need the Trusted Software Stack (TSS) of which an 
implementation for Linux is available at: 
http://sourceforge.net/projects/trousers.

Updates include: splitting out the vendor specific code into separated 
drivers from the base TPM functionality, fixed busy waiting loops, 
concerns about timer handling and rmmod, buffer name.

Thanks,
Kylene

Signed-off-by: Leendert van Doorn <leendert@watson.ibm.com>
Signed-off-by: Reiner Sailer <sailer@watson.ibm.com>
Signed-off-by: Dave Safford <safford@watson.ibm.com>
Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---
diff -uprN linux-2.6.9/drivers/char/Kconfig linux-2.6.9-tpm/drivers/char/Kconfig
--- linux-2.6.9/drivers/char/Kconfig	2004-10-18 16:53:07.000000000 -0500
+++ linux-2.6.9-tpm/drivers/char/Kconfig	2004-12-16 13:36:02.000000000 -0600
@@ -989,5 +989,28 @@ config MMTIMER
 	  The mmtimer device allows direct userspace access to the
 	  Altix system timer.
 
+config TCG_TPM
+	tristate "TPM Hardware Support"
+	depends on EXPERIMENTAL
+	---help---
+	  If you have a TPM security chip in your system, which
+	  implements the Trusted Computing Group's specification,
+	  say Yes and it will be accessible from within Linux. To 
+	  compile this driver as a module, choose M here; the module 
+	  will be called tpm. For more information see 
+	  www.trustedcomputinggroup.org. A implementation of the 
+	  Trusted Software Stack (TSS), the userspace enablement piece 
+	  of the specification, can be obtained at 
+	  http://sourceforge.net/projects/trousers
+	  If unsure, say N.
+
+config TCG_NSC
+	tristate "National Semiconductor TPM Interface"
+	depends on TCG_TPM
+
+config TCG_ATMEL
+	tristate "Atmel TPM Interface"
+	depends on TCG_TPM
+
 endmenu
 
diff -uprN linux-2.6.9/drivers/char/Makefile linux-2.6.9-tpm/drivers/char/Makefile
--- linux-2.6.9/drivers/char/Makefile	2004-10-18 16:55:28.000000000 -0500
+++ linux-2.6.9-tpm/drivers/char/Makefile	2004-12-16 13:35:57.000000000 -0600
@@ -88,6 +88,9 @@ obj-$(CONFIG_PCMCIA) += pcmcia/
 obj-$(CONFIG_IPMI_HANDLER) += ipmi/
 
 obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
+obj-$(CONFIG_TCG_TPM) += tpm.o
+obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
+obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
 
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c qtronixmap.c
diff -uprN linux-2.6.9/drivers/char/tpm_atmel.c linux-2.6.9-tpm/drivers/char/tpm_atmel.c
--- linux-2.6.9/drivers/char/tpm_atmel.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm_atmel.c	2004-12-16 17:14:31.000000000 -0600
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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"
+
+/* Atmel definitions */
+#define	TPM_ATML_BASE			0x400
+
+/* write status bits */
+#define	ATML_STATUS_ABORT		0x01
+#define	ATML_STATUS_LASTBYTE		0x04
+
+/* read status bits */
+#define	ATML_STATUS_BUSY		0x01
+#define	ATML_STATUS_DATA_AVAIL		0x02
+#define	ATML_STATUS_REWRITE		0x04
+
+
+static int tpm_atml_recv(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 status, *hdr = buf;
+	u32 size;
+	int i;
+	__be32 *native_size;
+
+	/* start reading header */
+	if (count < 6)
+		return -EIO;
+
+	for (i = 0; i < 6; i++) {
+		status = inb(chip->base + 1);
+		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+			dev_err(&chip->pci_dev->dev,
+				"error reading header\n");
+			return -EIO;
+		}
+		*buf++ = inb(chip->base);
+	}
+
+	/* size of the data received */
+	native_size = (__force __be32 *) (hdr + 2);
+	size = be32_to_cpu(*native_size);
+
+	if (count < size)
+		return -EIO;
+
+	/* read all the data available */
+	for (; i < size; i++) {
+		status = inb(chip->base + 1);
+		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+			dev_err(&chip->pci_dev->dev,
+				"error reading data\n");
+			return -EIO;
+		}
+		*buf++ = inb(chip->base);
+	}
+
+	/* make sure data available is gone */
+	status = inb(chip->base + 1);
+	if (status & ATML_STATUS_DATA_AVAIL) {
+		dev_err(&chip->pci_dev->dev, "data available is stuck\n");
+		return -EIO;
+	}
+
+	return size;
+}
+
+static int tpm_atml_send(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	int i;
+
+	dev_dbg(&chip->pci_dev->dev, "tpm_atml_send: ");
+	for (i = 0; i < count; i++)
+		dev_dbg(&chip->pci_dev->dev, "0x%x(%d) ", buf[i], buf[i]);
+
+	for (i = 0; i < count; i++)
+		outb(buf[i], chip->base);
+
+	return count;
+}
+
+static void tpm_atml_cancel(struct tpm_chip *chip)
+{
+	outb(ATML_STATUS_ABORT, chip->base + 1);
+}
+
+static struct file_operations atmel_ops = {
+	.owner = THIS_MODULE,
+};
+
+static struct tpm_chip_ops tpm_atmel = {
+	.recv = tpm_atml_recv,
+	.send = tpm_atml_send,
+	.cancel = tpm_atml_cancel,
+	.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
+	.req_complete_val = ATML_STATUS_DATA_AVAIL,
+	.miscdev.fops = &atmel_ops,
+};
+
+static int __devinit tpm_atml_init(struct pci_dev *pci_dev,
+				   const struct pci_device_id *pci_id)
+{
+	u8 version[4];
+	int rc = 0;
+
+	if (pci_enable_device(pci_dev))
+		return -EIO;
+
+	if (lpc_bus_init(pci_dev, TPM_ATML_BASE)) {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	/* verify that it is an Atmel part */
+	if (rdx(4) != 'A' || rdx(5) != 'T' || rdx(6) != 'M'
+	    || rdx(7) != 'L') {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+
+	/* query chip for its version number */
+	if ((version[0] = rdx(0x00)) != 0xFF) {
+		version[1] = rdx(0x01);
+		version[2] = rdx(0x02);
+		version[3] = rdx(0x03);
+	} else {
+		dev_info(&pci_dev->dev, "version query failed\n");
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	if ((rc =
+	     register_tpm_hardware(pci_dev, &tpm_atmel,
+				   TPM_ATML_BASE)) < 0)
+		goto out_err;
+
+	dev_info(&pci_dev->dev,
+		 "Atmel TPM version %d.%d.%d.%d\n", version[0], version[1],
+		 version[2], version[3]);
+
+	return 0;
+out_err:
+	pci_disable_device(pci_dev);
+	return rc;
+}
+
+static struct pci_driver atmel_pci_driver = {
+	.name = "tpm_atmel",
+	.probe = tpm_atml_init,
+};
+
+static int __init init_atmel(void)
+{
+	return register_tpm_driver(&atmel_pci_driver);
+}
+
+static void __exit cleanup_atmel(void)
+{
+	unregister_tpm_driver(&atmel_pci_driver);
+}
+
+module_init(init_atmel);
+module_exit(cleanup_atmel);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.9/drivers/char/tpm.c linux-2.6.9-tpm/drivers/char/tpm.c
--- linux-2.6.9/drivers/char/tpm.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm.c	2004-12-16 17:24:55.000000000 -0600
@@ -0,0 +1,581 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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.
+ * 
+ * Note, the TPM chip is not interrupt driven (only polling)
+ * and can have very long timeouts (minutes!). Hence the unusual
+ * calls to schedule_timeout.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include "tpm.h"
+
+#define	TPM_MINOR			224	/* officially assigned */
+
+#define	TPM_BUFSIZE			2048
+
+/* PCI configuration addresses */
+#define	PCI_GEN_PMCON_1			0xA0
+#define	PCI_GEN1_DEC			0xE4
+#define	PCI_LPC_EN			0xE6
+#define	PCI_GEN2_DEC			0xEC
+
+/* TPM addresses */
+#define	TPM_ADDR			0x4E
+#define	TPM_DATA			0x4F
+
+static struct list_head tpm_chip_list;
+static spinlock_t driver_lock;
+static int dev_mask[32];
+
+static void user_reader_timeout(unsigned long ptr)
+{
+	struct tpm_chip *chip = (struct tpm_chip *) ptr;
+
+	if (down_trylock(&chip->timer_mutex) == 0) {
+		atomic_set(&chip->data_pending, 0);
+		memset(chip->tpm_result_buffer, 0, TPM_BUFSIZE);
+		up(&chip->user_mutex);
+		up(&chip->timer_mutex);
+	}
+}
+
+void tpm_time_expired(unsigned long ptr)
+{
+	int *exp = (int *) ptr;
+	*exp = 1;
+}
+
+EXPORT_SYMBOL(tpm_time_expired);
+
+int rdx(int index)
+{
+	outb(index, TPM_ADDR);
+	return inb(TPM_DATA) & 0xFF;
+}
+
+EXPORT_SYMBOL(rdx);
+
+void wrx(int index, int value)
+{
+	outb(index, TPM_ADDR);
+	outb(value & 0xFF, TPM_DATA);
+}
+
+EXPORT_SYMBOL(wrx);
+
+/*
+ * Initialize the LPC bus and enable the TPM ports
+ */
+int lpc_bus_init(struct pci_dev *pci_dev, u16 base)
+{
+	u32 lpcenable, tmp;
+	int is_lpcm = 0;
+
+	switch (pci_dev->vendor) {
+	case PCI_VENDOR_ID_INTEL:
+		switch (pci_dev->device) {
+		case PCI_DEVICE_ID_INTEL_82801CA_12:
+		case PCI_DEVICE_ID_INTEL_82801DB_12:
+			is_lpcm = 1;
+			break;
+		}
+		/* init ICH (enable LPC) */
+		pci_read_config_dword(pci_dev, PCI_GEN1_DEC, &lpcenable);
+		lpcenable |= 0x20000000;
+		pci_write_config_dword(pci_dev, PCI_GEN1_DEC, lpcenable);
+
+		if (is_lpcm) {
+			pci_read_config_dword(pci_dev, PCI_GEN1_DEC,
+					      &lpcenable);
+			if ((lpcenable & 0x20000000) == 0) {
+				dev_err(&pci_dev->dev,
+					"cannot enable LPC\n");
+				return -ENODEV;
+			}
+		}
+
+		/* initialize TPM registers */
+		pci_read_config_dword(pci_dev, PCI_GEN2_DEC, &tmp);
+
+		if (!is_lpcm)
+			tmp = (tmp & 0xFFFF0000) | (base & 0xFFF0);
+		else
+			tmp =
+			    (tmp & 0xFFFF0000) | (base & 0xFFF0) |
+			    0x00000001;
+
+		pci_write_config_dword(pci_dev, PCI_GEN2_DEC, tmp);
+
+		if (is_lpcm) {
+			pci_read_config_dword(pci_dev, PCI_GEN_PMCON_1,
+					      &tmp);
+			tmp |= 0x00000004;	/* enable CLKRUN */
+			pci_write_config_dword(pci_dev, PCI_GEN_PMCON_1,
+					       tmp);
+		}
+		outb(0x0D, TPM_ADDR);	/* unlock 4F */
+		outb(0x55, TPM_DATA);
+		outb(0x0A, TPM_ADDR);	/* int disable */
+		outb(0x00, TPM_DATA);
+		outb(0x08, TPM_ADDR);	/* base addr lo */
+		outb(base & 0xFF, TPM_DATA);
+		outb(0x09, TPM_ADDR);	/* base addr hi */
+		outb((base & 0xFF00) >> 8, TPM_DATA);
+		outb(0x0D, TPM_ADDR);	/* lock 4F */
+		outb(0xAA, TPM_DATA);
+		break;
+	case PCI_VENDOR_ID_AMD:
+		/* nothing yet */
+		break;
+	}
+
+	return 0;
+}
+
+EXPORT_SYMBOL(lpc_bus_init);
+
+/*
+ * Internal kernel interface to transmit TPM commands
+ */
+static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
+			    size_t bufsiz)
+{
+	ssize_t len;
+	u32 count;
+	__be32 *native_size;
+
+	native_size = (__force __be32 *) (buf + 2);
+	count = be32_to_cpu(*native_size);
+
+	if (count == 0)
+		return -ENODATA;
+	if (count > bufsiz) {
+		dev_err(&chip->pci_dev->dev,
+			"invalid count value %x %x \n", count, bufsiz);
+		return -E2BIG;
+	}
+
+	if ((len = chip->ops->send(chip, (u8 *) buf, count)) < 0) {
+		dev_err(&chip->pci_dev->dev,
+			"tpm_transmit: tpm_send: error %d\n", len);
+		return len;
+	}
+
+	down(&chip->sync_mutex);
+	chip->tpm_time_expired = 0;
+	init_timer(&chip->tpm_timer);
+	chip->tpm_timer.function = tpm_time_expired;
+	chip->tpm_timer.expires = jiffies + 2 * 60 * HZ;
+	chip->tpm_timer.data = (unsigned long) &chip->tpm_time_expired;
+	add_timer(&chip->tpm_timer);
+	up(&chip->sync_mutex);
+
+	do {
+		u8 status = inb(chip->base + 1);
+		if ((status & chip->ops->req_complete_mask) ==
+		    chip->ops->req_complete_val) {
+			down(&chip->sync_mutex);
+			del_singleshot_timer_sync(&chip->tpm_timer);
+			up(&chip->sync_mutex);
+			goto out_recv;
+		}
+		schedule_timeout(TPM_TIMEOUT);
+		rmb();
+	} while (!chip->tpm_time_expired);
+
+
+	chip->ops->cancel(chip);
+	dev_err(&chip->pci_dev->dev, "Time expired\n");
+	return -EIO;
+
+out_recv:
+	len = chip->ops->recv(chip, (u8 *) buf, bufsiz);
+	if (len < 0)
+		dev_err(&chip->pci_dev->dev,
+			"tpm_transmit: tpm_recv: error %d\n", len);
+	return len;
+}
+
+/*
+ * Device file system interface to the TPM
+ */
+static int tpm_open(struct inode *inode, struct file *file)
+{
+	int rc = 0, minor = iminor(inode);
+	struct tpm_chip *chip = NULL, *pos;
+
+	spin_lock(&driver_lock);
+
+	list_for_each_entry(pos, &tpm_chip_list, list) {
+		if (pos->ops->miscdev.minor == minor) {
+			chip = pos;
+			break;
+		}
+	}
+	if (chip == NULL) {
+		rc = -ENODEV;
+		goto err_out;
+	}
+
+	if (chip->num_opens) {
+		dev_dbg(&chip->pci_dev->dev,
+			"Another process owns this TPM\n");
+		rc = -EBUSY;
+		goto err_out;
+	}
+
+	chip->num_opens++;
+	pci_dev_get(chip->pci_dev);
+
+	spin_unlock(&driver_lock);
+
+	chip->tpm_result_buffer =
+	    kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
+	if (chip->tpm_result_buffer == NULL) {
+		chip->num_opens--;
+		pci_dev_put(chip->pci_dev);
+		return -ENOMEM;
+	}
+
+	atomic_set(&chip->data_pending, 0);
+
+	file->private_data = chip;
+	return 0;
+
+err_out:
+	spin_unlock(&driver_lock);
+	return rc;
+}
+
+static int tpm_release(struct inode *inode, struct file *file)
+{
+	struct tpm_chip *chip = file->private_data;
+
+	if (chip == NULL)
+		return -ENODEV;
+
+	spin_lock(&driver_lock);
+	chip->num_opens--;
+	if (chip->num_opens == 0) {
+		down(&chip->sync_mutex);
+		if (timer_pending(&chip->user_read_timer))
+			del_singleshot_timer_sync(&chip->user_read_timer);
+		else if (timer_pending(&chip->tpm_timer))
+			del_singleshot_timer_sync(&chip->tpm_timer);
+		up(&chip->sync_mutex);
+		kfree(chip->tpm_result_buffer);
+		atomic_set(&chip->data_pending, 0);
+	}
+
+	pci_dev_put(chip->pci_dev);
+	file->private_data = NULL;
+	spin_unlock(&driver_lock);
+	return 0;
+}
+
+static ssize_t tpm_write(struct file *file, const char __user * buf,
+			 size_t size, loff_t * off)
+{
+	struct tpm_chip *chip = file->private_data;
+	int out_size;
+
+	if (chip == NULL)
+		return -ENODEV;
+
+	down(&chip->user_mutex);
+
+	if (copy_from_user
+	    (chip->tpm_result_buffer, (void __user *) buf, size)) {
+		up(&chip->user_mutex);
+		return -EFAULT;
+	}
+	out_size =
+	    tpm_transmit(chip, chip->tpm_result_buffer, TPM_BUFSIZE);
+
+	down(&chip->sync_mutex);
+	init_timer(&chip->user_read_timer);
+	chip->user_read_timer.function = user_reader_timeout;
+	chip->user_read_timer.data = (unsigned long) chip;
+	chip->user_read_timer.expires = jiffies + (60 * HZ);
+	add_timer(&chip->user_read_timer);
+	up(&chip->sync_mutex);
+
+	atomic_set(&chip->data_pending, out_size);
+
+	return size;
+}
+
+static ssize_t tpm_read(struct file *file, char __user * buf,
+			size_t size, loff_t * off)
+{
+	struct tpm_chip *chip = file->private_data;
+	int write_size;
+
+	if (chip == NULL)
+		return -ENODEV;
+
+	if (down_trylock(&chip->timer_mutex) != 0) {
+		dev_err(&chip->pci_dev->dev, "Timeout occured\n");
+		return -ETIME;
+	}
+
+	write_size = atomic_read(&chip->data_pending);
+	atomic_set(&chip->data_pending, 0);
+
+	if (write_size == 0) {
+		dev_err(&chip->pci_dev->dev, "No data pending\n");
+		up(&chip->timer_mutex);
+		return -ENODATA;
+	}
+
+	down(&chip->sync_mutex);
+	del_singleshot_timer_sync(&chip->user_read_timer);
+	up(&chip->sync_mutex);
+
+	up(&chip->timer_mutex);
+
+	if (write_size < 0)
+		goto out;
+
+	if (size < write_size)
+		write_size = size;
+
+	if (copy_to_user
+	    ((void __user *) buf, chip->tpm_result_buffer, write_size)) {
+		write_size = -EFAULT;
+		goto out;
+	}
+
+out:
+	up(&chip->user_mutex);
+	return write_size;
+}
+
+static void __devexit tpm_remove(struct pci_dev *pci_dev)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+
+	if (chip == NULL) {
+		dev_err(&pci_dev->dev, "No device data found\n");
+		return;
+	}
+
+	spin_lock(&driver_lock);
+
+	if (chip->num_opens != 0) {
+		dev_err(&chip->pci_dev->dev,
+			"Device still open (%d times) in userspace\n",
+			chip->num_opens);
+		spin_unlock(&driver_lock);
+		return;
+	}
+
+	list_del(&chip->list);
+
+	pci_set_drvdata(pci_dev, NULL);
+	misc_deregister(&chip->ops->miscdev);
+	spin_unlock(&driver_lock);
+
+	pci_disable_device(pci_dev);
+
+	dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
+
+	kfree(chip);
+
+	pci_dev_put(pci_dev);
+}
+
+static u8 savestate[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 10,		/* blob length (in bytes) */
+	0, 0, 0, 152		/* TPM_ORD_SaveState */
+};
+
+/*
+ * We are about to suspend. Save the TPM state
+ * so that it can be restored.
+ */
+static int tpm_pm_suspend(struct pci_dev *pci_dev, u32 pm_state)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+	if (chip == NULL)
+		return -ENODEV;
+
+	tpm_transmit(chip, savestate, sizeof(savestate));
+	return 0;
+}
+
+/*
+ * Resume from a power safe. The BIOS already restored
+ * the TPM state.
+ */
+static int tpm_pm_resume(struct pci_dev *pci_dev)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+	if (chip == NULL)
+		return -ENODEV;
+
+	spin_lock(&driver_lock);
+	lpc_bus_init(pci_dev, chip->base);
+	spin_unlock(&driver_lock);
+
+	return 0;
+}
+
+static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
+
+/*
+ * Vendor specific TPMs will have a unique name and probe function.
+ * Those fields should be populated prior to calling this function in
+ * tpm_<specific>.c's module init function.
+ */
+int register_tpm_driver(struct pci_driver *drv)
+{
+	drv->id_table = tpm_pci_tbl;
+	drv->remove = __devexit_p(tpm_remove);
+	drv->suspend = tpm_pm_suspend;
+	drv->resume = tpm_pm_resume;
+
+	return pci_register_driver(drv);
+}
+
+EXPORT_SYMBOL(register_tpm_driver);
+
+void unregister_tpm_driver(struct pci_driver *drv)
+{
+	pci_unregister_driver(drv);
+}
+
+EXPORT_SYMBOL(unregister_tpm_driver);
+
+/*
+ * Called from tpm_<specific>.c probe function only for devices 
+ * the driver has determined it should claim.  Prior to calling
+ * this function the specific probe function has called pci_enable_device
+ * upon errant exit from this function specific probe function should call
+ * pci_disable_device
+ */
+int register_tpm_hardware(struct pci_dev *pci_dev,
+			  struct tpm_chip_ops *entry, u16 base)
+{
+	char devname[7];
+	struct tpm_chip *chip;
+	int i, j;
+
+	/* Driver specific per-device data */
+	chip = kmalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	memset(chip, 0, sizeof(struct tpm_chip));
+
+	init_MUTEX(&chip->user_mutex);
+	init_MUTEX(&chip->timer_mutex);
+	init_MUTEX(&chip->sync_mutex);
+	INIT_LIST_HEAD(&chip->list);
+	chip->base = base;
+
+	chip->ops = entry;
+
+	chip->dev_num = -1;
+
+	for (i = 0; i < 32; i++)
+		for (j = 0; j < 8; j++)
+			if ((dev_mask[i] & (1 << j)) == 0) {
+				chip->dev_num = i * 32 + j;
+				dev_mask[i] |= 1 << j;
+				goto dev_num_search_complete;
+			}
+
+dev_num_search_complete:
+	if (chip->dev_num < 0) {
+		dev_err(&pci_dev->dev, "No available tpm device numbers\n");
+		kfree(chip);
+		return -ENODEV;
+	} else if (chip->dev_num == 0)
+		chip->ops->miscdev.minor = TPM_MINOR;
+	else
+		chip->ops->miscdev.minor = MISC_DYNAMIC_MINOR;
+
+	snprintf(devname, sizeof(devname), "%s%d", "tpm", chip->dev_num);
+	chip->ops->miscdev.name = devname;
+
+	chip->ops->miscdev.fops->llseek = no_llseek;
+	chip->ops->miscdev.fops->open = tpm_open;
+	chip->ops->miscdev.fops->read = tpm_read;
+	chip->ops->miscdev.fops->write = tpm_write;
+	chip->ops->miscdev.fops->release = tpm_release;
+
+	chip->ops->miscdev.dev = &(pci_dev->dev);
+	chip->pci_dev = pci_dev_get(pci_dev);
+
+	spin_lock(&driver_lock);
+
+	if (misc_register(&chip->ops->miscdev)) {
+		dev_err(&chip->pci_dev->dev,
+			"unable to misc_register %s, minor %d\n",
+			chip->ops->miscdev.name, chip->ops->miscdev.minor);
+		pci_dev_put(pci_dev);
+		spin_unlock(&driver_lock);
+		kfree(chip);
+		dev_mask[i] &= !(1 << j);
+		return -ENODEV;
+	}
+
+	pci_set_drvdata(pci_dev, chip);
+
+	list_add(&chip->list, &tpm_chip_list);
+	spin_unlock(&driver_lock);
+	return 0;
+}
+
+EXPORT_SYMBOL(register_tpm_hardware);
+
+static int __init init_tpm(void)
+{
+	INIT_LIST_HEAD(&tpm_chip_list);
+	spin_lock_init(&driver_lock);
+	return 0;
+}
+
+static void __exit cleanup_tpm(void)
+{
+
+}
+
+module_init(init_tpm);
+module_exit(cleanup_tpm);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.9/drivers/char/tpm.h linux-2.6.9-tpm/drivers/char/tpm.h
--- linux-2.6.9/drivers/char/tpm.h	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm.h	2004-12-16 17:16:50.000000000 -0600
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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/module.h>
+#include <linux/version.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/miscdevice.h>
+
+#define TPM_TIMEOUT msecs_to_jiffies(5)
+
+struct tpm_chip;
+
+struct tpm_chip_ops {
+	u8 req_complete_mask;
+	u8 req_complete_val;
+
+	int (*recv) (struct tpm_chip *, u8 *, size_t);
+	int (*send) (struct tpm_chip *, u8 *, size_t);
+	void (*cancel) (struct tpm_chip *);
+	struct miscdevice miscdev;
+};
+
+struct tpm_chip {
+	struct pci_dev *pci_dev;	/* PCI device stuff */
+	int dev_num;
+	u16 base;		/* TPM base address */
+
+	u8 *tpm_result_buffer;
+	atomic_t data_pending;
+	int num_opens;
+	struct timer_list user_read_timer;
+	struct timer_list tpm_timer;
+	struct semaphore user_mutex;
+	struct semaphore timer_mutex;
+	struct semaphore sync_mutex;
+
+	int tpm_time_expired;
+	struct list_head list;
+
+	struct tpm_chip_ops *ops;
+};
+
+extern void tpm_time_expired(unsigned long);
+extern int rdx(int);
+extern void wrx(int, int);
+extern int lpc_bus_init(struct pci_dev *, u16);
+
+extern int register_tpm_driver(struct pci_driver *);
+extern void unregister_tpm_driver(struct pci_driver *);
+extern int register_tpm_hardware(struct pci_dev *, struct tpm_chip_ops *,
+				 u16);
diff -uprN linux-2.6.9/drivers/char/tpm_nsc.c linux-2.6.9-tpm/drivers/char/tpm_nsc.c
--- linux-2.6.9/drivers/char/tpm_nsc.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm_nsc.c	2004-12-16 17:14:31.000000000 -0600
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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"
+
+/* National definitions */
+#define	TPM_NSC_BASE			0x360
+#define	TPM_NSC_IRQ			0x07
+
+#define	NSC_LDN_INDEX			0x07
+#define	NSC_SID_INDEX			0x20
+#define	NSC_LDC_INDEX			0x30
+#define	NSC_DIO_INDEX			0x60
+#define	NSC_CIO_INDEX			0x62
+#define	NSC_IRQ_INDEX			0x70
+#define	NSC_ITS_INDEX			0x71
+
+#define	NSC_STATUS			0x01
+#define	NSC_COMMAND			0x01
+#define	NSC_DATA			0x00
+
+/* status bits */
+#define	NSC_STATUS_OBF			0x01	/* output buffer full */
+#define	NSC_STATUS_IBF			0x02	/* input buffer full */
+#define	NSC_STATUS_F0			0x04	/* F0 */
+#define	NSC_STATUS_A2			0x08	/* A2 */
+#define	NSC_STATUS_RDY			0x10	/* ready to receive command */
+#define	NSC_STATUS_IBR			0x20	/* ready to receive data */
+
+/* command bits */
+#define	NSC_COMMAND_NORMAL		0x01	/* normal mode */
+#define	NSC_COMMAND_BURST		0x81	/* burst mode */
+#define	NSC_COMMAND_EOC			0x03
+#define	NSC_COMMAND_CANCEL		0x22
+
+/*
+ * Wait for a certain status to appear
+ */
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
+{
+	int expired = 0;
+	struct timer_list status_timer =
+	    TIMER_INITIALIZER(tpm_time_expired, jiffies + 10 * HZ,
+			      (unsigned long) &expired);
+
+	/* status immediately available check */
+	*data = inb(chip->base + 1);
+	if ((*data & mask) == val)
+		return 0;
+
+	/* wait for status */
+	add_timer(&status_timer);
+	do {
+		schedule_timeout(TPM_TIMEOUT);
+		*data = inb(chip->base + 1);
+		if ((*data & mask) == val) {
+			del_singleshot_timer_sync(&status_timer);
+			return 0;
+		}
+	}
+	while (!expired);
+
+	return -EBUSY;
+}
+
+static int nsc_wait_for_ready(struct tpm_chip *chip)
+{
+	int status;
+	int expired = 0;
+	struct timer_list status_timer =
+	    TIMER_INITIALIZER(tpm_time_expired, jiffies + 100,
+			      (unsigned long) &expired);
+
+	/* status immediately available check */
+	status = inb(chip->base + NSC_STATUS);
+	if (status & NSC_STATUS_OBF)
+		status = inb(chip->base + NSC_DATA);
+	if (status & NSC_STATUS_RDY)
+		return 0;
+
+	/* wait for status */
+	add_timer(&status_timer);
+	do {
+		schedule_timeout(TPM_TIMEOUT);
+		status = inb(chip->base + NSC_STATUS);
+		if (status & NSC_STATUS_OBF)
+			status = inb(chip->base + NSC_DATA);
+		if (status & NSC_STATUS_RDY) {
+			del_singleshot_timer_sync(&status_timer);
+			return 0;
+		}
+	}
+	while (!expired);
+
+	dev_info(&chip->pci_dev->dev, "wait for ready failed\n");
+	return -EBUSY;
+}
+
+
+static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 *buffer = buf;
+	u8 data, *p;
+	u32 size;
+	__be32 *native_size;
+
+	if (count < 6)
+		return -EIO;
+
+	if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "F0 timeout\n");
+		return -EIO;
+	}
+	if ((data = inb(chip->base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
+		dev_err(&chip->pci_dev->dev, "not in normal mode (0x%x)\n",
+			data);
+		return -EIO;
+	}
+
+	/* read the whole packet */
+	for (p = buffer; p < &buffer[count]; p++) {
+		if (wait_for_stat
+		    (chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) {
+			dev_err(&chip->pci_dev->dev,
+				"OBF timeout (while reading data)\n");
+			return -EIO;
+		}
+		if (data & NSC_STATUS_F0)
+			break;
+		*p = inb(chip->base + NSC_DATA);
+	}
+
+	if ((data & NSC_STATUS_F0) == 0) {
+		dev_err(&chip->pci_dev->dev, "F0 not set\n");
+		return -EIO;
+	}
+	if ((data = inb(chip->base + NSC_DATA)) != NSC_COMMAND_EOC) {
+		dev_err(&chip->pci_dev->dev,
+			"expected end of command(0x%x)\n", data);
+		return -EIO;
+	}
+
+	native_size = (__force __be32 *) (buf + 2);
+	size = be32_to_cpu(*native_size);
+
+	if (count < size)
+		return -EIO;
+
+	return size;
+}
+
+static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 data;
+	int i;
+
+	/*
+	 * If we hit the chip with back to back commands it locks up
+	 * and never set IBF. Hitting it with this "hammer" seems to
+	 * fix it. Not sure why this is needed, we followed the flow
+	 * chart in the manual to the letter.
+	 */
+	outb(NSC_COMMAND_CANCEL, chip->base + NSC_COMMAND);
+
+	if (nsc_wait_for_ready(chip) != 0)
+		return -EIO;
+
+	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+		return -EIO;
+	}
+
+	outb(NSC_COMMAND_NORMAL, chip->base + NSC_COMMAND);
+	if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBR timeout\n");
+		return -EIO;
+	}
+
+	for (i = 0; i < count; i++) {
+		if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+			dev_err(&chip->pci_dev->dev,
+				"IBF timeout (while writing data)\n");
+			return -EIO;
+		}
+		outb(buf[i], chip->base + NSC_DATA);
+	}
+
+	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+		return -EIO;
+	}
+	outb(NSC_COMMAND_EOC, chip->base + NSC_COMMAND);
+
+	return count;
+}
+
+static void tpm_nsc_cancel(struct tpm_chip *chip)
+{
+	outb(NSC_COMMAND_CANCEL, chip->base + NSC_COMMAND);
+}
+
+static struct file_operations nsc_ops = {
+	.owner = THIS_MODULE,
+};
+
+static struct tpm_chip_ops tpm_nsc = {
+	.recv = tpm_nsc_recv,
+	.send = tpm_nsc_send,
+	.cancel = tpm_nsc_cancel,
+	.req_complete_mask = NSC_STATUS_OBF,
+	.req_complete_val = NSC_STATUS_OBF,
+	.miscdev.fops = &nsc_ops,
+};
+
+static int __devinit tpm_nsc_init(struct pci_dev *pci_dev,
+				  const struct pci_device_id *pci_id)
+{
+	int rc = 0;
+
+	if (pci_enable_device(pci_dev))
+		return -EIO;
+
+	if (lpc_bus_init(pci_dev, TPM_NSC_BASE)) {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	/* verify that it is a National part (SID) */
+	if (rdx(NSC_SID_INDEX) != 0xEF) {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	dev_dbg(&pci_dev->dev, "NSC TPM detected\n");
+	dev_dbg(&pci_dev->dev,
+		"NSC LDN 0x%x, SID 0x%x, SRID 0x%x\n", rdx(0x07),
+		rdx(0x20), rdx(0x27));
+	dev_dbg(&pci_dev->dev,
+		"NSC SIOCF1 0x%x SIOCF5 0x%x SIOCF6 0x%x SIOCF8 0x%x\n",
+		rdx(0x21), rdx(0x25), rdx(0x26), rdx(0x28));
+	dev_dbg(&pci_dev->dev, "NSC IO Base0 0x%x\n",
+		(rdx(0x60) << 8) | rdx(0x61));
+	dev_dbg(&pci_dev->dev, "NSC IO Base1 0x%x\n",
+		(rdx(0x62) << 8) | rdx(0x63));
+	dev_dbg(&pci_dev->dev,
+		"NSC Interrupt number and wakeup 0x%x\n", rdx(0x70));
+	dev_dbg(&pci_dev->dev, "NSC IRQ type select 0x%x\n", rdx(0x71));
+	dev_dbg(&pci_dev->dev,
+		"NSC DMA channel select0 0x%x, select1 0x%x\n", rdx(0x74),
+		rdx(0x75));
+	dev_dbg(&pci_dev->dev,
+		"NSC Config "
+		"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+		rdx(0xF0), rdx(0xF1), rdx(0xF2), rdx(0xF3), rdx(0xF4),
+		rdx(0xF5), rdx(0xF6), rdx(0xF7), rdx(0xF8), rdx(0xF9));
+
+	dev_info(&pci_dev->dev,
+		 "NSC PC21100 TPM revision %d\n", rdx(0x27) & 0x1F);
+
+	if (rdx(NSC_LDC_INDEX) == 0)
+		dev_info(&pci_dev->dev, ": NSC TPM not active\n");
+
+	/* select PM channel 1 */
+	wrx(NSC_LDN_INDEX, 0x12);
+	rdx(NSC_LDN_INDEX);
+
+	/* disable the DPM module */
+	wrx(NSC_LDC_INDEX, 0);
+	rdx(NSC_LDC_INDEX);
+
+	/* set the data register base addresses */
+	wrx(NSC_DIO_INDEX, TPM_NSC_BASE >> 8);
+	wrx(NSC_DIO_INDEX + 1, TPM_NSC_BASE);
+	rdx(NSC_DIO_INDEX);
+	rdx(NSC_DIO_INDEX + 1);
+
+	/* set the command register base addresses */
+	wrx(NSC_CIO_INDEX, (TPM_NSC_BASE + 1) >> 8);
+	wrx(NSC_CIO_INDEX + 1, (TPM_NSC_BASE + 1));
+	rdx(NSC_DIO_INDEX);
+	rdx(NSC_DIO_INDEX + 1);
+
+	/* set the interrupt number to be used for the host interface */
+	wrx(NSC_IRQ_INDEX, TPM_NSC_IRQ);
+	wrx(NSC_ITS_INDEX, 0x00);
+	rdx(NSC_IRQ_INDEX);
+
+	/* enable the DPM module */
+	wrx(NSC_LDC_INDEX, 0x01);
+	rdx(NSC_LDC_INDEX);
+
+	if ((rc =
+	     register_tpm_hardware(pci_dev, &tpm_nsc, TPM_NSC_BASE)) < 0)
+		goto out_err;
+
+	return 0;
+
+out_err:
+	pci_disable_device(pci_dev);
+	return rc;
+}
+
+static struct pci_driver nsc_pci_driver = {
+	.name = "tpm_nsc",
+	.probe = tpm_nsc_init,
+};
+
+static int __init init_nsc(void)
+{
+	return register_tpm_driver(&nsc_pci_driver);
+
+}
+
+static void __exit cleanup_nsc(void)
+{
+	unregister_tpm_driver(&nsc_pci_driver);
+}
+
+module_init(init_nsc);
+module_exit(cleanup_nsc);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.9/include/linux/pci_ids.h linux-2.6.9-tpm/include/linux/pci_ids.h
--- linux-2.6.9/include/linux/pci_ids.h	2004-12-06 16:53:35.000000000 -0600
+++ linux-2.6.9-tpm/include/linux/pci_ids.h	2004-12-06 14:27:05.000000000 -0600
@@ -494,6 +494,7 @@
 #define PCI_DEVICE_ID_AMD_OPUS_7449	0x7449
 #	define PCI_DEVICE_ID_AMD_VIPER_7449	PCI_DEVICE_ID_AMD_OPUS_7449
 #define PCI_DEVICE_ID_AMD_8111_LAN	0x7462
+#define PCI_DEVICE_ID_AMD_8111_LPC	0x7468
 #define PCI_DEVICE_ID_AMD_8111_IDE	0x7469
 #define PCI_DEVICE_ID_AMD_8111_AUDIO	0x746d
 #define PCI_DEVICE_ID_AMD_8151_0	0x7454
diff -uprN linux-2.6.9/MAINTAINERS linux-2.6.9-tpm/MAINTAINERS
--- linux-2.6.9/MAINTAINERS	2004-10-18 16:54:37.000000000 -0500
+++ linux-2.6.9-tpm/MAINTAINERS	2004-12-07 12:39:10.000000000 -0600
@@ -2144,6 +2144,12 @@ L:	tlinux-users@tce.toshiba-dme.co.jp
 W:	http://www.buzzard.org.uk/toshiba/
 S:	Maintained
 
+TPM DRIVER
+P:	Kylene Hall
+M:	tpmdd-devel@lists.sourceforge.net
+L:	tpmdd-devel@lists.sourceforge.net
+S:	Maintained
+
 TRIDENT 4DWAVE/SIS 7018 PCI AUDIO CORE
 P:	Muli Ben-Yehuda
 M:	mulix@mulix.org

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement --updated version
  2004-12-16 22:37 ` [PATCH 1/1] driver: Tpm hardware enablement --updated version Kylene Hall
@ 2004-12-16 22:48   ` Greg KH
  2004-12-17 22:47     ` [tpmdd-devel] " Kylene Hall
  2004-12-17  0:53   ` Chris Wright
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 43+ messages in thread
From: Greg KH @ 2004-12-16 22:48 UTC (permalink / raw)
  To: Kylene Hall; +Cc: linux-kernel, sailer, leendert, emilyr, toml, tpmdd-devel

On Thu, Dec 16, 2004 at 04:37:34PM -0600, Kylene Hall wrote:
> +config TCG_TPM
> +	tristate "TPM Hardware Support"
> +	depends on EXPERIMENTAL
> +	---help---
> +	  If you have a TPM security chip in your system, which
> +	  implements the Trusted Computing Group's specification,
> +	  say Yes and it will be accessible from within Linux. To 
> +	  compile this driver as a module, choose M here; the module 
> +	  will be called tpm. For more information see 
> +	  www.trustedcomputinggroup.org. A implementation of the 
> +	  Trusted Software Stack (TSS), the userspace enablement piece 
> +	  of the specification, can be obtained at 
> +	  http://sourceforge.net/projects/trousers
> +	  If unsure, say N.

What happened to the "if built as a module..." text?

> +
> +config TCG_NSC
> +	tristate "National Semiconductor TPM Interface"
> +	depends on TCG_TPM
> +
> +config TCG_ATMEL
> +	tristate "Atmel TPM Interface"
> +	depends on TCG_TPM

Please provide help text for these options.

> +/*
> + * Vendor specific TPMs will have a unique name and probe function.
> + * Those fields should be populated prior to calling this function in
> + * tpm_<specific>.c's module init function.
> + */
> +int register_tpm_driver(struct pci_driver *drv)
> +{
> +	drv->id_table = tpm_pci_tbl;
> +	drv->remove = __devexit_p(tpm_remove);
> +	drv->suspend = tpm_pm_suspend;
> +	drv->resume = tpm_pm_resume;
> +
> +	return pci_register_driver(drv);
> +}
> +
> +EXPORT_SYMBOL(register_tpm_driver);

Why not EXPORT_SYMBOL_GPL()?  Based on the content of these drivers, I'd
feel better if they all were that way, but that's just me :)

Actually, why even have this function at all?  It's not needed, just
export the suspend, resume, and remove functions, and you are set.

Also, don't say that other drivers really support the other pci devices,
when they do not.  The MODULE_DEVICE_TABLE() stuff needs to be in the
driver that actually supports that hardware.  Otherwise all of the
hotplug functionality will not work properly.

> +
> +void unregister_tpm_driver(struct pci_driver *drv)
> +{
> +	pci_unregister_driver(drv);
> +}
> +
> +EXPORT_SYMBOL(unregister_tpm_driver);

Um, why even have such a function?


> +EXPORT_SYMBOL(register_tpm_hardware);

EXPORT_SYMBOL_GPL() (same goes for all of these exported symbols...)

> diff -uprN linux-2.6.9/drivers/char/tpm.h linux-2.6.9-tpm/drivers/char/tpm.h
> --- linux-2.6.9/drivers/char/tpm.h	1969-12-31 18:00:00.000000000 -0600
> +++ linux-2.6.9-tpm/drivers/char/tpm.h	2004-12-16 17:16:50.000000000 -0600
> +extern void tpm_time_expired(unsigned long);
> +extern int rdx(int);
> +extern void wrx(int, int);

Please use better names for these functions.  That's very cryptic for a
global symbol.

> +extern int lpc_bus_init(struct pci_dev *, u16);

No "tpm"?

> +extern int register_tpm_driver(struct pci_driver *);
> +extern void unregister_tpm_driver(struct pci_driver *);
> +extern int register_tpm_hardware(struct pci_dev *, struct tpm_chip_ops *,
> +				 u16);

Try putting "tpm" first here, for these functions, so the namespace is sane.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement --updated version
  2004-12-16 22:37 ` [PATCH 1/1] driver: Tpm hardware enablement --updated version Kylene Hall
  2004-12-16 22:48   ` Greg KH
@ 2004-12-17  0:53   ` Chris Wright
  2004-12-17 22:47     ` [tpmdd-devel] " Kylene Hall
  2004-12-17 22:47   ` Kylene Hall
  2004-12-19 19:48   ` [PATCH 1/1] driver: Tpm hardware enablement --updated version Pavel Machek
  3 siblings, 1 reply; 43+ messages in thread
From: Chris Wright @ 2004-12-17  0:53 UTC (permalink / raw)
  To: Kylene Hall
  Cc: linux-kernel, greg, sailer, leendert, emilyr, toml, tpmdd-devel

Is there no support for the crypto/key/rng/etc features, or am I
missing something?  I guess this is just to bring the hardware up?

* Kylene Hall (kjhall@us.ibm.com) wrote:
> diff -uprN linux-2.6.9/drivers/char/Makefile linux-2.6.9-tpm/drivers/char/Makefile
> --- linux-2.6.9/drivers/char/Makefile	2004-10-18 16:55:28.000000000 -0500
> +++ linux-2.6.9-tpm/drivers/char/Makefile	2004-12-16 13:35:57.000000000 -0600
> @@ -88,6 +88,9 @@ obj-$(CONFIG_PCMCIA) += pcmcia/
>  obj-$(CONFIG_IPMI_HANDLER) += ipmi/
>  
>  obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
> +obj-$(CONFIG_TCG_TPM) += tpm.o
> +obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
> +obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o

Any reason not to have a tpm/ driver dir?  Aren't there likely to be more tpm
chips?

> diff -uprN linux-2.6.9/drivers/char/tpm_atmel.c linux-2.6.9-tpm/drivers/char/tpm_atmel.c
> --- linux-2.6.9/drivers/char/tpm_atmel.c	1969-12-31 18:00:00.000000000 -0600
> +++ linux-2.6.9-tpm/drivers/char/tpm_atmel.c	2004-12-16 17:14:31.000000000 -0600
> @@ -0,0 +1,187 @@
> +/*
> + * Copyright (C) 2004 IBM Corporation
> + *
> + * Authors:
> + * Leendert van Doorn <leendert@watson.ibm.com>
> + * Reiner Sailer <sailer@watson.ibm.com>
> + * Dave Safford <safford@watson.ibm.com>
> + * Kylene Hall <kjhall@us.ibm.com>
> + *
> + * Maintained by: <tpmdd_devel@lists.sourceforge.net>
> + *
> + * Device driver for TCG/TCPA TPM (trusted platform module).
> + * Specifications at www.trustedcomputinggroup.org	 
> + *
> + * 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"
> +
> +/* Atmel definitions */
> +#define	TPM_ATML_BASE			0x400
> +
> +/* write status bits */
> +#define	ATML_STATUS_ABORT		0x01
> +#define	ATML_STATUS_LASTBYTE		0x04
> +
> +/* read status bits */
> +#define	ATML_STATUS_BUSY		0x01
> +#define	ATML_STATUS_DATA_AVAIL		0x02
> +#define	ATML_STATUS_REWRITE		0x04
> +
> +
> +static int tpm_atml_recv(struct tpm_chip *chip, u8 * buf, size_t count)
> +{
> +	u8 status, *hdr = buf;
> +	u32 size;
> +	int i;
> +	__be32 *native_size;
> +
> +	/* start reading header */
> +	if (count < 6)
> +		return -EIO;
> +
> +	for (i = 0; i < 6; i++) {
> +		status = inb(chip->base + 1);
> +		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
> +			dev_err(&chip->pci_dev->dev,
> +				"error reading header\n");
> +			return -EIO;
> +		}
> +		*buf++ = inb(chip->base);
> +	}
> +
> +	/* size of the data received */
> +	native_size = (__force __be32 *) (hdr + 2);
> +	size = be32_to_cpu(*native_size);
> +
> +	if (count < size)
> +		return -EIO;
> +
> +	/* read all the data available */
> +	for (; i < size; i++) {
> +		status = inb(chip->base + 1);
> +		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
> +			dev_err(&chip->pci_dev->dev,
> +				"error reading data\n");
> +			return -EIO;
> +		}
> +		*buf++ = inb(chip->base);
> +	}
> +
> +	/* make sure data available is gone */
> +	status = inb(chip->base + 1);
> +	if (status & ATML_STATUS_DATA_AVAIL) {
> +		dev_err(&chip->pci_dev->dev, "data available is stuck\n");
> +		return -EIO;
> +	}
> +
> +	return size;
> +}
> +
> +static int tpm_atml_send(struct tpm_chip *chip, u8 * buf, size_t count)
> +{
> +	int i;
> +
> +	dev_dbg(&chip->pci_dev->dev, "tpm_atml_send: ");
> +	for (i = 0; i < count; i++)
> +		dev_dbg(&chip->pci_dev->dev, "0x%x(%d) ", buf[i], buf[i]);
> +
> +	for (i = 0; i < count; i++)
> +		outb(buf[i], chip->base);

This could be one loop.  And this too is unbounded.  So a write with a
large buffer will blowout base...erp, nm, I imagined base + i. 

> +
> +	return count;
> +}
> +
> +static void tpm_atml_cancel(struct tpm_chip *chip)
> +{
> +	outb(ATML_STATUS_ABORT, chip->base + 1);
> +}
> +
> +static struct file_operations atmel_ops = {
> +	.owner = THIS_MODULE,
> +};

This can be tpm_open, etc, right here.

> +static struct tpm_chip_ops tpm_atmel = {
> +	.recv = tpm_atml_recv,
> +	.send = tpm_atml_send,
> +	.cancel = tpm_atml_cancel,
> +	.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
> +	.req_complete_val = ATML_STATUS_DATA_AVAIL,
> +	.miscdev.fops = &atmel_ops,
> +};
> +
> +static int __devinit tpm_atml_init(struct pci_dev *pci_dev,
> +				   const struct pci_device_id *pci_id)
> +{
> +	u8 version[4];
> +	int rc = 0;
> +
> +	if (pci_enable_device(pci_dev))
> +		return -EIO;
> +
> +	if (lpc_bus_init(pci_dev, TPM_ATML_BASE)) {
> +		rc = -ENODEV;
> +		goto out_err;
> +	}
> +
> +	/* verify that it is an Atmel part */
> +	if (rdx(4) != 'A' || rdx(5) != 'T' || rdx(6) != 'M'
> +	    || rdx(7) != 'L') {
> +		rc = -ENODEV;
> +		goto out_err;
> +	}
> +
> +
> +	/* query chip for its version number */
> +	if ((version[0] = rdx(0x00)) != 0xFF) {
> +		version[1] = rdx(0x01);
> +		version[2] = rdx(0x02);
> +		version[3] = rdx(0x03);
> +	} else {
> +		dev_info(&pci_dev->dev, "version query failed\n");
> +		rc = -ENODEV;
> +		goto out_err;
> +	}
> +
> +	if ((rc =
> +	     register_tpm_hardware(pci_dev, &tpm_atmel,
> +				   TPM_ATML_BASE)) < 0)
> +		goto out_err;
> +
> +	dev_info(&pci_dev->dev,
> +		 "Atmel TPM version %d.%d.%d.%d\n", version[0], version[1],
> +		 version[2], version[3]);
> +
> +	return 0;
> +out_err:
> +	pci_disable_device(pci_dev);
> +	return rc;
> +}
> +
> +static struct pci_driver atmel_pci_driver = {
> +	.name = "tpm_atmel",
> +	.probe = tpm_atml_init,
> +};
> +
> +static int __init init_atmel(void)
> +{
> +	return register_tpm_driver(&atmel_pci_driver);
> +}
> +
> +static void __exit cleanup_atmel(void)
> +{
> +	unregister_tpm_driver(&atmel_pci_driver);
> +}
> +
> +module_init(init_atmel);
> +module_exit(cleanup_atmel);
> +
> +MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
> +MODULE_DESCRIPTION("TPM Driver");
> +MODULE_VERSION("2.0");
> +MODULE_LICENSE("GPL");
> diff -uprN linux-2.6.9/drivers/char/tpm.c linux-2.6.9-tpm/drivers/char/tpm.c
> --- linux-2.6.9/drivers/char/tpm.c	1969-12-31 18:00:00.000000000 -0600
> +++ linux-2.6.9-tpm/drivers/char/tpm.c	2004-12-16 17:24:55.000000000 -0600
> @@ -0,0 +1,581 @@
> +/*
> + * Copyright (C) 2004 IBM Corporation
> + *
> + * Authors:
> + * Leendert van Doorn <leendert@watson.ibm.com>
> + * Reiner Sailer <sailer@watson.ibm.com>
> + * Dave Safford <safford@watson.ibm.com>
> + * Kylene Hall <kjhall@us.ibm.com>
> + *
> + * Maintained by: <tpmdd_devel@lists.sourceforge.net>
> + *
> + * Device driver for TCG/TCPA TPM (trusted platform module).
> + * Specifications at www.trustedcomputinggroup.org	 
> + *
> + * 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.
> + * 
> + * Note, the TPM chip is not interrupt driven (only polling)
> + * and can have very long timeouts (minutes!). Hence the unusual
> + * calls to schedule_timeout.
> + *
> + */
> +
> +#include <linux/sched.h>
> +#include <linux/poll.h>
> +#include <linux/spinlock.h>
> +#include "tpm.h"
> +
> +#define	TPM_MINOR			224	/* officially assigned */
> +
> +#define	TPM_BUFSIZE			2048
> +
> +/* PCI configuration addresses */
> +#define	PCI_GEN_PMCON_1			0xA0
> +#define	PCI_GEN1_DEC			0xE4
> +#define	PCI_LPC_EN			0xE6
> +#define	PCI_GEN2_DEC			0xEC
> +
> +/* TPM addresses */
> +#define	TPM_ADDR			0x4E
> +#define	TPM_DATA			0x4F
> +
> +static struct list_head tpm_chip_list;
> +static spinlock_t driver_lock;
> +static int dev_mask[32];
> +
> +static void user_reader_timeout(unsigned long ptr)
> +{
> +	struct tpm_chip *chip = (struct tpm_chip *) ptr;
> +
> +	if (down_trylock(&chip->timer_mutex) == 0) {
> +		atomic_set(&chip->data_pending, 0);
> +		memset(chip->tpm_result_buffer, 0, TPM_BUFSIZE);
> +		up(&chip->user_mutex);
> +		up(&chip->timer_mutex);
> +	}
> +}
> +
> +void tpm_time_expired(unsigned long ptr)
> +{
> +	int *exp = (int *) ptr;
> +	*exp = 1;
> +}
> +
> +EXPORT_SYMBOL(tpm_time_expired);
> +
> +int rdx(int index)
> +{
> +	outb(index, TPM_ADDR);
> +	return inb(TPM_DATA) & 0xFF;
> +}
> +
> +EXPORT_SYMBOL(rdx);
> +
> +void wrx(int index, int value)
> +{
> +	outb(index, TPM_ADDR);
> +	outb(value & 0xFF, TPM_DATA);
> +}
> +
> +EXPORT_SYMBOL(wrx);

These (rdx/wrx) are not appropriate names for global namespace.  Must
they even be exported?  Could they not be made static inline in tpm.h?

> +/*
> + * Initialize the LPC bus and enable the TPM ports
> + */
> +int lpc_bus_init(struct pci_dev *pci_dev, u16 base)
> +{
> +	u32 lpcenable, tmp;
> +	int is_lpcm = 0;
> +
> +	switch (pci_dev->vendor) {
> +	case PCI_VENDOR_ID_INTEL:

This doesn't look quite right to have device specific logic in the
core.  Shouldn't this go in the device specific driver logic?

> +		switch (pci_dev->device) {
> +		case PCI_DEVICE_ID_INTEL_82801CA_12:
> +		case PCI_DEVICE_ID_INTEL_82801DB_12:
> +			is_lpcm = 1;
> +			break;
> +		}
> +		/* init ICH (enable LPC) */
> +		pci_read_config_dword(pci_dev, PCI_GEN1_DEC, &lpcenable);
> +		lpcenable |= 0x20000000;
> +		pci_write_config_dword(pci_dev, PCI_GEN1_DEC, lpcenable);
> +
> +		if (is_lpcm) {
> +			pci_read_config_dword(pci_dev, PCI_GEN1_DEC,
> +					      &lpcenable);
> +			if ((lpcenable & 0x20000000) == 0) {
> +				dev_err(&pci_dev->dev,
> +					"cannot enable LPC\n");
> +				return -ENODEV;
> +			}
> +		}
> +
> +		/* initialize TPM registers */
> +		pci_read_config_dword(pci_dev, PCI_GEN2_DEC, &tmp);
> +
> +		if (!is_lpcm)
> +			tmp = (tmp & 0xFFFF0000) | (base & 0xFFF0);
> +		else
> +			tmp =
> +			    (tmp & 0xFFFF0000) | (base & 0xFFF0) |
> +			    0x00000001;
> +
> +		pci_write_config_dword(pci_dev, PCI_GEN2_DEC, tmp);
> +
> +		if (is_lpcm) {
> +			pci_read_config_dword(pci_dev, PCI_GEN_PMCON_1,
> +					      &tmp);
> +			tmp |= 0x00000004;	/* enable CLKRUN */
> +			pci_write_config_dword(pci_dev, PCI_GEN_PMCON_1,
> +					       tmp);
> +		}
> +		outb(0x0D, TPM_ADDR);	/* unlock 4F */
> +		outb(0x55, TPM_DATA);
> +		outb(0x0A, TPM_ADDR);	/* int disable */
> +		outb(0x00, TPM_DATA);
> +		outb(0x08, TPM_ADDR);	/* base addr lo */
> +		outb(base & 0xFF, TPM_DATA);
> +		outb(0x09, TPM_ADDR);	/* base addr hi */
> +		outb((base & 0xFF00) >> 8, TPM_DATA);
> +		outb(0x0D, TPM_ADDR);	/* lock 4F */
> +		outb(0xAA, TPM_DATA);

Hey, aren't these a bunch of those wdx's? ;-)

> +		break;
> +	case PCI_VENDOR_ID_AMD:
> +		/* nothing yet */
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +EXPORT_SYMBOL(lpc_bus_init);
> +
> +/*
> + * Internal kernel interface to transmit TPM commands
> + */
> +static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
> +			    size_t bufsiz)
> +{
> +	ssize_t len;
> +	u32 count;
> +	__be32 *native_size;
> +
> +	native_size = (__force __be32 *) (buf + 2);
> +	count = be32_to_cpu(*native_size);
> +
> +	if (count == 0)
> +		return -ENODATA;
> +	if (count > bufsiz) {
> +		dev_err(&chip->pci_dev->dev,
> +			"invalid count value %x %x \n", count, bufsiz);
> +		return -E2BIG;
> +	}
> +
> +	if ((len = chip->ops->send(chip, (u8 *) buf, count)) < 0) {
> +		dev_err(&chip->pci_dev->dev,
> +			"tpm_transmit: tpm_send: error %d\n", len);
> +		return len;
> +	}
> +
> +	down(&chip->sync_mutex);
> +	chip->tpm_time_expired = 0;
> +	init_timer(&chip->tpm_timer);
> +	chip->tpm_timer.function = tpm_time_expired;
> +	chip->tpm_timer.expires = jiffies + 2 * 60 * HZ;
> +	chip->tpm_timer.data = (unsigned long) &chip->tpm_time_expired;
> +	add_timer(&chip->tpm_timer);
> +	up(&chip->sync_mutex);
> +
> +	do {
> +		u8 status = inb(chip->base + 1);

Is this guaranteed to be status location on all chips?  Perhaps a
status() callback is better.

> +		if ((status & chip->ops->req_complete_mask) ==
> +		    chip->ops->req_complete_val) {
> +			down(&chip->sync_mutex);
> +			del_singleshot_timer_sync(&chip->tpm_timer);
> +			up(&chip->sync_mutex);
> +			goto out_recv;
> +		}
> +		schedule_timeout(TPM_TIMEOUT);
> +		rmb();
> +	} while (!chip->tpm_time_expired);
> +
> +
> +	chip->ops->cancel(chip);
> +	dev_err(&chip->pci_dev->dev, "Time expired\n");
> +	return -EIO;
> +
> +out_recv:
> +	len = chip->ops->recv(chip, (u8 *) buf, bufsiz);
> +	if (len < 0)
> +		dev_err(&chip->pci_dev->dev,
> +			"tpm_transmit: tpm_recv: error %d\n", len);
> +	return len;
> +}
> +
> +/*
> + * Device file system interface to the TPM
> + */
> +static int tpm_open(struct inode *inode, struct file *file)
> +{
> +	int rc = 0, minor = iminor(inode);
> +	struct tpm_chip *chip = NULL, *pos;
> +
> +	spin_lock(&driver_lock);
> +
> +	list_for_each_entry(pos, &tpm_chip_list, list) {
> +		if (pos->ops->miscdev.minor == minor) {
> +			chip = pos;
> +			break;
> +		}
> +	}
> +	if (chip == NULL) {
> +		rc = -ENODEV;
> +		goto err_out;
> +	}
> +
> +	if (chip->num_opens) {
> +		dev_dbg(&chip->pci_dev->dev,
> +			"Another process owns this TPM\n");
> +		rc = -EBUSY;
> +		goto err_out;
> +	}
> +
> +	chip->num_opens++;

Hmm, looks a bit like it's just a mutex.

> +	pci_dev_get(chip->pci_dev);
> +
> +	spin_unlock(&driver_lock);
> +
> +	chip->tpm_result_buffer =
> +	    kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
> +	if (chip->tpm_result_buffer == NULL) {
> +		chip->num_opens--;
> +		pci_dev_put(chip->pci_dev);
> +		return -ENOMEM;
> +	}
> +
> +	atomic_set(&chip->data_pending, 0);
> +
> +	file->private_data = chip;
> +	return 0;
> +
> +err_out:
> +	spin_unlock(&driver_lock);
> +	return rc;
> +}
> +
> +static int tpm_release(struct inode *inode, struct file *file)
> +{
> +	struct tpm_chip *chip = file->private_data;
> +
> +	if (chip == NULL)
> +		return -ENODEV;

Don't think that'll ever happen?

> +
> +	spin_lock(&driver_lock);
> +	chip->num_opens--;
> +	if (chip->num_opens == 0) {

Is there a case where num_opens-- != 0?  I thought you were making sure
there was only one open?

> +		down(&chip->sync_mutex);
> +		if (timer_pending(&chip->user_read_timer))
> +			del_singleshot_timer_sync(&chip->user_read_timer);
> +		else if (timer_pending(&chip->tpm_timer))
> +			del_singleshot_timer_sync(&chip->tpm_timer);
> +		up(&chip->sync_mutex);
> +		kfree(chip->tpm_result_buffer);
> +		atomic_set(&chip->data_pending, 0);
> +	}
> +
> +	pci_dev_put(chip->pci_dev);
> +	file->private_data = NULL;
> +	spin_unlock(&driver_lock);
> +	return 0;
> +}
> +
> +static ssize_t tpm_write(struct file *file, const char __user * buf,
> +			 size_t size, loff_t * off)
> +{
> +	struct tpm_chip *chip = file->private_data;
> +	int out_size;
> +
> +	if (chip == NULL)
> +		return -ENODEV;

Don't think that'll ever happen?

> +
> +	down(&chip->user_mutex);

What is this protecting?

> +
> +	if (copy_from_user
> +	    (chip->tpm_result_buffer, (void __user *) buf, size)) {
> +		up(&chip->user_mutex);
> +		return -EFAULT;
> +	}

This is a buffer overflow waiting to happen.

> +	out_size =
> +	    tpm_transmit(chip, chip->tpm_result_buffer, TPM_BUFSIZE);

How does device distinguish from leftover garbage in buffer when size <
TPM_BUFSIZE?  Wonder if you could read this back from tpm (and leak
kernel memory to userspace that way)?

> +	down(&chip->sync_mutex);
> +	init_timer(&chip->user_read_timer);
> +	chip->user_read_timer.function = user_reader_timeout;
> +	chip->user_read_timer.data = (unsigned long) chip;
> +	chip->user_read_timer.expires = jiffies + (60 * HZ);
> +	add_timer(&chip->user_read_timer);
> +	up(&chip->sync_mutex);
> +
> +	atomic_set(&chip->data_pending, out_size);
> +
> +	return size;
> +}
> +
> +static ssize_t tpm_read(struct file *file, char __user * buf,
> +			size_t size, loff_t * off)
> +{
> +	struct tpm_chip *chip = file->private_data;
> +	int write_size;
> +
> +	if (chip == NULL)
> +		return -ENODEV;

Don't think that'll happen?

> +	if (down_trylock(&chip->timer_mutex) != 0) {
> +		dev_err(&chip->pci_dev->dev, "Timeout occured\n");
> +		return -ETIME;
> +	}
> +
> +	write_size = atomic_read(&chip->data_pending);
> +	atomic_set(&chip->data_pending, 0);
> +
> +	if (write_size == 0) {
> +		dev_err(&chip->pci_dev->dev, "No data pending\n");
> +		up(&chip->timer_mutex);
> +		return -ENODATA;
> +	}
> +
> +	down(&chip->sync_mutex);
> +	del_singleshot_timer_sync(&chip->user_read_timer);
> +	up(&chip->sync_mutex);
> +
> +	up(&chip->timer_mutex);
> +
> +	if (write_size < 0)
> +		goto out;
> +
> +	if (size < write_size)
> +		write_size = size;
> +
> +	if (copy_to_user
> +	    ((void __user *) buf, chip->tpm_result_buffer, write_size)) {
> +		write_size = -EFAULT;
> +		goto out;
> +	}
> +
> +out:
> +	up(&chip->user_mutex);
> +	return write_size;
> +}
> +
> +static void __devexit tpm_remove(struct pci_dev *pci_dev)
> +{
> +	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
> +
> +	if (chip == NULL) {
> +		dev_err(&pci_dev->dev, "No device data found\n");
> +		return;
> +	}
> +
> +	spin_lock(&driver_lock);
> +
> +	if (chip->num_opens != 0) {

Won't module refcount care for this?

> +		dev_err(&chip->pci_dev->dev,
> +			"Device still open (%d times) in userspace\n",
> +			chip->num_opens);
> +		spin_unlock(&driver_lock);
> +		return;
> +	}
> +
> +	list_del(&chip->list);
> +
> +	pci_set_drvdata(pci_dev, NULL);
> +	misc_deregister(&chip->ops->miscdev);
> +	spin_unlock(&driver_lock);
> +
> +	pci_disable_device(pci_dev);
> +
> +	dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
> +
> +	kfree(chip);
> +
> +	pci_dev_put(pci_dev);
> +}
> +
> +static u8 savestate[] = {
> +	0, 193,			/* TPM_TAG_RQU_COMMAND */
> +	0, 0, 0, 10,		/* blob length (in bytes) */
> +	0, 0, 0, 152		/* TPM_ORD_SaveState */
> +};
> +
> +/*
> + * We are about to suspend. Save the TPM state
> + * so that it can be restored.
> + */
> +static int tpm_pm_suspend(struct pci_dev *pci_dev, u32 pm_state)
> +{
> +	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
> +	if (chip == NULL)
> +		return -ENODEV;
> +
> +	tpm_transmit(chip, savestate, sizeof(savestate));
> +	return 0;
> +}
> +
> +/*
> + * Resume from a power safe. The BIOS already restored
> + * the TPM state.
> + */
> +static int tpm_pm_resume(struct pci_dev *pci_dev)
> +{
> +	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
> +	if (chip == NULL)
> +		return -ENODEV;
> +
> +	spin_lock(&driver_lock);
> +	lpc_bus_init(pci_dev, chip->base);
> +	spin_unlock(&driver_lock);
> +
> +	return 0;
> +}
> +
> +static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
> +	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
> +	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
> +	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
> +	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
> +	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
> +	{PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
> +	{0,}
> +};
> +
> +MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
> +
> +/*
> + * Vendor specific TPMs will have a unique name and probe function.
> + * Those fields should be populated prior to calling this function in
> + * tpm_<specific>.c's module init function.
> + */
> +int register_tpm_driver(struct pci_driver *drv)
> +{
> +	drv->id_table = tpm_pci_tbl;
> +	drv->remove = __devexit_p(tpm_remove);
> +	drv->suspend = tpm_pm_suspend;
> +	drv->resume = tpm_pm_resume;
> +
> +	return pci_register_driver(drv);
> +}
> +
> +EXPORT_SYMBOL(register_tpm_driver);
> +
> +void unregister_tpm_driver(struct pci_driver *drv)
> +{
> +	pci_unregister_driver(drv);
> +}
> +
> +EXPORT_SYMBOL(unregister_tpm_driver);
> +
> +/*
> + * Called from tpm_<specific>.c probe function only for devices 
> + * the driver has determined it should claim.  Prior to calling
> + * this function the specific probe function has called pci_enable_device
> + * upon errant exit from this function specific probe function should call
> + * pci_disable_device
> + */
> +int register_tpm_hardware(struct pci_dev *pci_dev,
> +			  struct tpm_chip_ops *entry, u16 base)
> +{
> +	char devname[7];
> +	struct tpm_chip *chip;
> +	int i, j;
> +
> +	/* Driver specific per-device data */
> +	chip = kmalloc(sizeof(*chip), GFP_KERNEL);
> +	if (chip == NULL)
> +		return -ENOMEM;
> +
> +	memset(chip, 0, sizeof(struct tpm_chip));
> +
> +	init_MUTEX(&chip->user_mutex);
> +	init_MUTEX(&chip->timer_mutex);
> +	init_MUTEX(&chip->sync_mutex);
> +	INIT_LIST_HEAD(&chip->list);
> +	chip->base = base;
> +
> +	chip->ops = entry;
> +
> +	chip->dev_num = -1;
> +
> +	for (i = 0; i < 32; i++)
> +		for (j = 0; j < 8; j++)
> +			if ((dev_mask[i] & (1 << j)) == 0) {
> +				chip->dev_num = i * 32 + j;
> +				dev_mask[i] |= 1 << j;
> +				goto dev_num_search_complete;
> +			}
> +
> +dev_num_search_complete:
> +	if (chip->dev_num < 0) {
> +		dev_err(&pci_dev->dev, "No available tpm device numbers\n");
> +		kfree(chip);
> +		return -ENODEV;
> +	} else if (chip->dev_num == 0)
> +		chip->ops->miscdev.minor = TPM_MINOR;
> +	else
> +		chip->ops->miscdev.minor = MISC_DYNAMIC_MINOR;
> +
> +	snprintf(devname, sizeof(devname), "%s%d", "tpm", chip->dev_num);
> +	chip->ops->miscdev.name = devname;
> +
> +	chip->ops->miscdev.fops->llseek = no_llseek;
> +	chip->ops->miscdev.fops->open = tpm_open;
> +	chip->ops->miscdev.fops->read = tpm_read;
> +	chip->ops->miscdev.fops->write = tpm_write;
> +	chip->ops->miscdev.fops->release = tpm_release;

This is usually done statically, entry is passed in after all.

> +	chip->ops->miscdev.dev = &(pci_dev->dev);
> +	chip->pci_dev = pci_dev_get(pci_dev);
> +
> +	spin_lock(&driver_lock);
> +
> +	if (misc_register(&chip->ops->miscdev)) {
> +		dev_err(&chip->pci_dev->dev,
> +			"unable to misc_register %s, minor %d\n",
> +			chip->ops->miscdev.name, chip->ops->miscdev.minor);
> +		pci_dev_put(pci_dev);
> +		spin_unlock(&driver_lock);
> +		kfree(chip);
> +		dev_mask[i] &= !(1 << j);
> +		return -ENODEV;
> +	}
> +
> +	pci_set_drvdata(pci_dev, chip);
> +
> +	list_add(&chip->list, &tpm_chip_list);
> +	spin_unlock(&driver_lock);
> +	return 0;
> +}
> +
> +EXPORT_SYMBOL(register_tpm_hardware);
> +
> +static int __init init_tpm(void)
> +{
> +	INIT_LIST_HEAD(&tpm_chip_list);
> +	spin_lock_init(&driver_lock);

These can be done statically.

> +	return 0;
> +}
> +
> +static void __exit cleanup_tpm(void)
> +{
> +
> +}
> +
> +module_init(init_tpm);
> +module_exit(cleanup_tpm);
> +
> +MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
> +MODULE_DESCRIPTION("TPM Driver");
> +MODULE_VERSION("2.0");
> +MODULE_LICENSE("GPL");
> diff -uprN linux-2.6.9/drivers/char/tpm.h linux-2.6.9-tpm/drivers/char/tpm.h
> --- linux-2.6.9/drivers/char/tpm.h	1969-12-31 18:00:00.000000000 -0600
> +++ linux-2.6.9-tpm/drivers/char/tpm.h	2004-12-16 17:16:50.000000000 -0600
> @@ -0,0 +1,69 @@
> +/*
> + * Copyright (C) 2004 IBM Corporation
> + *
> + * Authors:
> + * Leendert van Doorn <leendert@watson.ibm.com>
> + * Reiner Sailer <sailer@watson.ibm.com>
> + * Dave Safford <safford@watson.ibm.com>
> + * Kylene Hall <kjhall@us.ibm.com>
> + *
> + * Maintained by: <tpmdd_devel@lists.sourceforge.net>
> + *
> + * Device driver for TCG/TCPA TPM (trusted platform module).
> + * Specifications at www.trustedcomputinggroup.org	 
> + *
> + * 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/module.h>
> +#include <linux/version.h>
> +#include <linux/pci.h>
> +#include <linux/delay.h>
> +#include <linux/miscdevice.h>
> +
> +#define TPM_TIMEOUT msecs_to_jiffies(5)
> +
> +struct tpm_chip;
> +
> +struct tpm_chip_ops {
> +	u8 req_complete_mask;
> +	u8 req_complete_val;

ops are usually ops only.

> +
> +	int (*recv) (struct tpm_chip *, u8 *, size_t);
> +	int (*send) (struct tpm_chip *, u8 *, size_t);
> +	void (*cancel) (struct tpm_chip *);
> +	struct miscdevice miscdev;
> +};
> +
> +struct tpm_chip {
> +	struct pci_dev *pci_dev;	/* PCI device stuff */
> +	int dev_num;
> +	u16 base;		/* TPM base address */
> +
> +	u8 *tpm_result_buffer;
> +	atomic_t data_pending;
> +	int num_opens;
> +	struct timer_list user_read_timer;
> +	struct timer_list tpm_timer;
> +	struct semaphore user_mutex;
> +	struct semaphore timer_mutex;
> +	struct semaphore sync_mutex;

Wow, three mutexes for this little data strucutre?

> +	int tpm_time_expired;
> +	struct list_head list;
> +
> +	struct tpm_chip_ops *ops;
> +};
> +
> +extern void tpm_time_expired(unsigned long);
> +extern int rdx(int);
> +extern void wrx(int, int);
> +extern int lpc_bus_init(struct pci_dev *, u16);
> +
> +extern int register_tpm_driver(struct pci_driver *);
> +extern void unregister_tpm_driver(struct pci_driver *);
> +extern int register_tpm_hardware(struct pci_dev *, struct tpm_chip_ops *,
> +				 u16);
> diff -uprN linux-2.6.9/drivers/char/tpm_nsc.c linux-2.6.9-tpm/drivers/char/tpm_nsc.c
> --- linux-2.6.9/drivers/char/tpm_nsc.c	1969-12-31 18:00:00.000000000 -0600
> +++ linux-2.6.9-tpm/drivers/char/tpm_nsc.c	2004-12-16 17:14:31.000000000 -0600
> @@ -0,0 +1,343 @@
> +/*
> + * Copyright (C) 2004 IBM Corporation
> + *
> + * Authors:
> + * Leendert van Doorn <leendert@watson.ibm.com>
> + * Reiner Sailer <sailer@watson.ibm.com>
> + * Dave Safford <safford@watson.ibm.com>
> + * Kylene Hall <kjhall@us.ibm.com>
> + *
> + * Maintained by: <tpmdd_devel@lists.sourceforge.net>
> + *
> + * Device driver for TCG/TCPA TPM (trusted platform module).
> + * Specifications at www.trustedcomputinggroup.org	 
> + *
> + * 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"
> +
> +/* National definitions */
> +#define	TPM_NSC_BASE			0x360
> +#define	TPM_NSC_IRQ			0x07
> +
> +#define	NSC_LDN_INDEX			0x07
> +#define	NSC_SID_INDEX			0x20
> +#define	NSC_LDC_INDEX			0x30
> +#define	NSC_DIO_INDEX			0x60
> +#define	NSC_CIO_INDEX			0x62
> +#define	NSC_IRQ_INDEX			0x70
> +#define	NSC_ITS_INDEX			0x71
> +
> +#define	NSC_STATUS			0x01
> +#define	NSC_COMMAND			0x01
> +#define	NSC_DATA			0x00
> +
> +/* status bits */
> +#define	NSC_STATUS_OBF			0x01	/* output buffer full */
> +#define	NSC_STATUS_IBF			0x02	/* input buffer full */
> +#define	NSC_STATUS_F0			0x04	/* F0 */
> +#define	NSC_STATUS_A2			0x08	/* A2 */
> +#define	NSC_STATUS_RDY			0x10	/* ready to receive command */
> +#define	NSC_STATUS_IBR			0x20	/* ready to receive data */
> +
> +/* command bits */
> +#define	NSC_COMMAND_NORMAL		0x01	/* normal mode */
> +#define	NSC_COMMAND_BURST		0x81	/* burst mode */

Hmm, unused.  Is it for a dma type interface?

> +#define	NSC_COMMAND_EOC			0x03
> +#define	NSC_COMMAND_CANCEL		0x22
> +
> +/*
> + * Wait for a certain status to appear
> + */
> +static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
> +{
> +	int expired = 0;
> +	struct timer_list status_timer =
> +	    TIMER_INITIALIZER(tpm_time_expired, jiffies + 10 * HZ,
> +			      (unsigned long) &expired);
> +
> +	/* status immediately available check */
> +	*data = inb(chip->base + 1);

Isn't this base + NSC_STATUS?  Nice to use constants where possible.

> +	if ((*data & mask) == val)
> +		return 0;
> +
> +	/* wait for status */
> +	add_timer(&status_timer);
> +	do {
> +		schedule_timeout(TPM_TIMEOUT);
> +		*data = inb(chip->base + 1);
> +		if ((*data & mask) == val) {
> +			del_singleshot_timer_sync(&status_timer);
> +			return 0;
> +		}
> +	}
> +	while (!expired);
> +
> +	return -EBUSY;
> +}
> +
> +static int nsc_wait_for_ready(struct tpm_chip *chip)
> +{
> +	int status;
> +	int expired = 0;
> +	struct timer_list status_timer =
> +	    TIMER_INITIALIZER(tpm_time_expired, jiffies + 100,
> +			      (unsigned long) &expired);
> +
> +	/* status immediately available check */
> +	status = inb(chip->base + NSC_STATUS);
> +	if (status & NSC_STATUS_OBF)
> +		status = inb(chip->base + NSC_DATA);
> +	if (status & NSC_STATUS_RDY)
> +		return 0;
> +
> +	/* wait for status */
> +	add_timer(&status_timer);
> +	do {
> +		schedule_timeout(TPM_TIMEOUT);
> +		status = inb(chip->base + NSC_STATUS);
> +		if (status & NSC_STATUS_OBF)
> +			status = inb(chip->base + NSC_DATA);
> +		if (status & NSC_STATUS_RDY) {
> +			del_singleshot_timer_sync(&status_timer);
> +			return 0;
> +		}
> +	}
> +	while (!expired);
> +
> +	dev_info(&chip->pci_dev->dev, "wait for ready failed\n");
> +	return -EBUSY;
> +}
> +
> +
> +static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
> +{
> +	u8 *buffer = buf;
> +	u8 data, *p;
> +	u32 size;
> +	__be32 *native_size;
> +
> +	if (count < 6)
> +		return -EIO;
> +
> +	if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
> +		dev_err(&chip->pci_dev->dev, "F0 timeout\n");
> +		return -EIO;
> +	}
> +	if ((data = inb(chip->base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
> +		dev_err(&chip->pci_dev->dev, "not in normal mode (0x%x)\n",
> +			data);
> +		return -EIO;
> +	}
> +
> +	/* read the whole packet */
> +	for (p = buffer; p < &buffer[count]; p++) {
> +		if (wait_for_stat
> +		    (chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) {
> +			dev_err(&chip->pci_dev->dev,
> +				"OBF timeout (while reading data)\n");
> +			return -EIO;
> +		}
> +		if (data & NSC_STATUS_F0)
> +			break;
> +		*p = inb(chip->base + NSC_DATA);
> +	}
> +
> +	if ((data & NSC_STATUS_F0) == 0) {
> +		dev_err(&chip->pci_dev->dev, "F0 not set\n");
> +		return -EIO;
> +	}
> +	if ((data = inb(chip->base + NSC_DATA)) != NSC_COMMAND_EOC) {
> +		dev_err(&chip->pci_dev->dev,
> +			"expected end of command(0x%x)\n", data);
> +		return -EIO;
> +	}
> +
> +	native_size = (__force __be32 *) (buf + 2);
> +	size = be32_to_cpu(*native_size);
> +
> +	if (count < size)
> +		return -EIO;
> +
> +	return size;
> +}
> +
> +static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
> +{
> +	u8 data;
> +	int i;
> +
> +	/*
> +	 * If we hit the chip with back to back commands it locks up
> +	 * and never set IBF. Hitting it with this "hammer" seems to
> +	 * fix it. Not sure why this is needed, we followed the flow
> +	 * chart in the manual to the letter.
> +	 */
> +	outb(NSC_COMMAND_CANCEL, chip->base + NSC_COMMAND);
> +
> +	if (nsc_wait_for_ready(chip) != 0)
> +		return -EIO;
> +
> +	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
> +		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
> +		return -EIO;
> +	}
> +
> +	outb(NSC_COMMAND_NORMAL, chip->base + NSC_COMMAND);
> +	if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
> +		dev_err(&chip->pci_dev->dev, "IBR timeout\n");
> +		return -EIO;
> +	}
> +
> +	for (i = 0; i < count; i++) {
> +		if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
> +			dev_err(&chip->pci_dev->dev,
> +				"IBF timeout (while writing data)\n");
> +			return -EIO;
> +		}
> +		outb(buf[i], chip->base + NSC_DATA);
> +	}
> +
> +	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
> +		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
> +		return -EIO;
> +	}
> +	outb(NSC_COMMAND_EOC, chip->base + NSC_COMMAND);
> +
> +	return count;
> +}
> +
> +static void tpm_nsc_cancel(struct tpm_chip *chip)
> +{
> +	outb(NSC_COMMAND_CANCEL, chip->base + NSC_COMMAND);
> +}
> +
> +static struct file_operations nsc_ops = {
> +	.owner = THIS_MODULE,
> +};
> +
> +static struct tpm_chip_ops tpm_nsc = {
> +	.recv = tpm_nsc_recv,
> +	.send = tpm_nsc_send,
> +	.cancel = tpm_nsc_cancel,
> +	.req_complete_mask = NSC_STATUS_OBF,
> +	.req_complete_val = NSC_STATUS_OBF,
> +	.miscdev.fops = &nsc_ops,
> +};
> +
> +static int __devinit tpm_nsc_init(struct pci_dev *pci_dev,
> +				  const struct pci_device_id *pci_id)
> +{
> +	int rc = 0;
> +
> +	if (pci_enable_device(pci_dev))
> +		return -EIO;
> +
> +	if (lpc_bus_init(pci_dev, TPM_NSC_BASE)) {
> +		rc = -ENODEV;
> +		goto out_err;
> +	}
> +
> +	/* verify that it is a National part (SID) */
> +	if (rdx(NSC_SID_INDEX) != 0xEF) {
> +		rc = -ENODEV;
> +		goto out_err;
> +	}
> +
> +	dev_dbg(&pci_dev->dev, "NSC TPM detected\n");
> +	dev_dbg(&pci_dev->dev,
> +		"NSC LDN 0x%x, SID 0x%x, SRID 0x%x\n", rdx(0x07),
> +		rdx(0x20), rdx(0x27));
> +	dev_dbg(&pci_dev->dev,
> +		"NSC SIOCF1 0x%x SIOCF5 0x%x SIOCF6 0x%x SIOCF8 0x%x\n",
> +		rdx(0x21), rdx(0x25), rdx(0x26), rdx(0x28));
> +	dev_dbg(&pci_dev->dev, "NSC IO Base0 0x%x\n",
> +		(rdx(0x60) << 8) | rdx(0x61));
> +	dev_dbg(&pci_dev->dev, "NSC IO Base1 0x%x\n",
> +		(rdx(0x62) << 8) | rdx(0x63));
> +	dev_dbg(&pci_dev->dev,
> +		"NSC Interrupt number and wakeup 0x%x\n", rdx(0x70));
> +	dev_dbg(&pci_dev->dev, "NSC IRQ type select 0x%x\n", rdx(0x71));
> +	dev_dbg(&pci_dev->dev,
> +		"NSC DMA channel select0 0x%x, select1 0x%x\n", rdx(0x74),
> +		rdx(0x75));
> +	dev_dbg(&pci_dev->dev,
> +		"NSC Config "
> +		"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
> +		rdx(0xF0), rdx(0xF1), rdx(0xF2), rdx(0xF3), rdx(0xF4),
> +		rdx(0xF5), rdx(0xF6), rdx(0xF7), rdx(0xF8), rdx(0xF9));
> +
> +	dev_info(&pci_dev->dev,
> +		 "NSC PC21100 TPM revision %d\n", rdx(0x27) & 0x1F);
> +
> +	if (rdx(NSC_LDC_INDEX) == 0)
> +		dev_info(&pci_dev->dev, ": NSC TPM not active\n");
> +
> +	/* select PM channel 1 */
> +	wrx(NSC_LDN_INDEX, 0x12);
> +	rdx(NSC_LDN_INDEX);
> +
> +	/* disable the DPM module */
> +	wrx(NSC_LDC_INDEX, 0);
> +	rdx(NSC_LDC_INDEX);
> +
> +	/* set the data register base addresses */
> +	wrx(NSC_DIO_INDEX, TPM_NSC_BASE >> 8);
> +	wrx(NSC_DIO_INDEX + 1, TPM_NSC_BASE);
> +	rdx(NSC_DIO_INDEX);
> +	rdx(NSC_DIO_INDEX + 1);
> +
> +	/* set the command register base addresses */
> +	wrx(NSC_CIO_INDEX, (TPM_NSC_BASE + 1) >> 8);
> +	wrx(NSC_CIO_INDEX + 1, (TPM_NSC_BASE + 1));
> +	rdx(NSC_DIO_INDEX);
> +	rdx(NSC_DIO_INDEX + 1);
> +
> +	/* set the interrupt number to be used for the host interface */
> +	wrx(NSC_IRQ_INDEX, TPM_NSC_IRQ);
> +	wrx(NSC_ITS_INDEX, 0x00);
> +	rdx(NSC_IRQ_INDEX);
> +
> +	/* enable the DPM module */
> +	wrx(NSC_LDC_INDEX, 0x01);
> +	rdx(NSC_LDC_INDEX);
> +
> +	if ((rc =
> +	     register_tpm_hardware(pci_dev, &tpm_nsc, TPM_NSC_BASE)) < 0)
> +		goto out_err;
> +
> +	return 0;
> +
> +out_err:
> +	pci_disable_device(pci_dev);
> +	return rc;
> +}
> +
> +static struct pci_driver nsc_pci_driver = {
> +	.name = "tpm_nsc",
> +	.probe = tpm_nsc_init,
> +};
> +
> +static int __init init_nsc(void)
> +{
> +	return register_tpm_driver(&nsc_pci_driver);
> +
> +}
> +
> +static void __exit cleanup_nsc(void)
> +{
> +	unregister_tpm_driver(&nsc_pci_driver);
> +}
> +
> +module_init(init_nsc);
> +module_exit(cleanup_nsc);
> +
> +MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
> +MODULE_DESCRIPTION("TPM Driver");
> +MODULE_VERSION("2.0");
> +MODULE_LICENSE("GPL");
> diff -uprN linux-2.6.9/include/linux/pci_ids.h linux-2.6.9-tpm/include/linux/pci_ids.h
> --- linux-2.6.9/include/linux/pci_ids.h	2004-12-06 16:53:35.000000000 -0600
> +++ linux-2.6.9-tpm/include/linux/pci_ids.h	2004-12-06 14:27:05.000000000 -0600
> @@ -494,6 +494,7 @@
>  #define PCI_DEVICE_ID_AMD_OPUS_7449	0x7449
>  #	define PCI_DEVICE_ID_AMD_VIPER_7449	PCI_DEVICE_ID_AMD_OPUS_7449
>  #define PCI_DEVICE_ID_AMD_8111_LAN	0x7462
> +#define PCI_DEVICE_ID_AMD_8111_LPC	0x7468
>  #define PCI_DEVICE_ID_AMD_8111_IDE	0x7469
>  #define PCI_DEVICE_ID_AMD_8111_AUDIO	0x746d
>  #define PCI_DEVICE_ID_AMD_8151_0	0x7454
> diff -uprN linux-2.6.9/MAINTAINERS linux-2.6.9-tpm/MAINTAINERS
> --- linux-2.6.9/MAINTAINERS	2004-10-18 16:54:37.000000000 -0500
> +++ linux-2.6.9-tpm/MAINTAINERS	2004-12-07 12:39:10.000000000 -0600
> @@ -2144,6 +2144,12 @@ L:	tlinux-users@tce.toshiba-dme.co.jp
>  W:	http://www.buzzard.org.uk/toshiba/
>  S:	Maintained
>  
> +TPM DRIVER
> +P:	Kylene Hall
> +M:	tpmdd-devel@lists.sourceforge.net
> +L:	tpmdd-devel@lists.sourceforge.net
> +S:	Maintained
> +
>  TRIDENT 4DWAVE/SIS 7018 PCI AUDIO CORE
>  P:	Muli Ben-Yehuda
>  M:	mulix@mulix.org
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

-- 
Linux Security Modules     http://lsm.immunix.org     http://lsm.bkbits.net

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [tpmdd-devel] Re: [PATCH 1/1] driver: Tpm hardware enablement --updated version
  2004-12-17  0:53   ` Chris Wright
@ 2004-12-17 22:47     ` Kylene Hall
  0 siblings, 0 replies; 43+ messages in thread
From: Kylene Hall @ 2004-12-17 22:47 UTC (permalink / raw)
  To: Chris Wright
  Cc: linux-kernel, greg, sailer, leendert, Emily Ratliff,
	Tom Lendacky, tpmdd-devel

On Thu, 2004-12-16 at 18:53, Chris Wright wrote:
Thanks for your help.  Addressing your questions here, new patch to
follow.

> Is there no support for the crypto/key/rng/etc features, or am I
> missing something?  I guess this is just to bring the hardware up?
Yes the TPM chip supports all those functions.  Those functions are
managed/exported by the TSS (Trusted Software Stack) a library that
implements another part of the TCG specification.  A linux
implementation of the TSS called trousers is available at
http://sourceforge.net/projects/trousers.  Does that answer your
question?  This is just the driver to interface between that library and
the device.

> 
> * Kylene Hall (kjhall@us.ibm.com) wrote:
> > diff -uprN linux-2.6.9/drivers/char/Makefile linux-2.6.9-tpm/drivers/char/Makefile
> > --- linux-2.6.9/drivers/char/Makefile	2004-10-18 16:55:28.000000000 -0500
> > +++ linux-2.6.9-tpm/drivers/char/Makefile	2004-12-16 13:35:57.000000000 -0600
> > @@ -88,6 +88,9 @@ obj-$(CONFIG_PCMCIA) += pcmcia/
> >  obj-$(CONFIG_IPMI_HANDLER) += ipmi/
> >  
> >  obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
> > +obj-$(CONFIG_TCG_TPM) += tpm.o
> > +obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
> > +obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
> 
> Any reason not to have a tpm/ driver dir?  Aren't there likely to be more tpm
> chips?
No reason, a tpm driver directory will be created in the next version.


> > +	dev_dbg(&chip->pci_dev->dev, "tpm_atml_send: ");
> > +	for (i = 0; i < count; i++)
> > +		dev_dbg(&chip->pci_dev->dev, "0x%x(%d) ", buf[i], buf[i]);
> > +
> > +	for (i = 0; i < count; i++)
> > +		outb(buf[i], chip->base);
> 
> This could be one loop.  And this too is unbounded.  So a write with a
> large buffer will blowout base...erp, nm, I imagined base + i. 
> 
Agreed.

> > +static struct file_operations atmel_ops = {
> > +	.owner = THIS_MODULE,
> > +};
> 
> This can be tpm_open, etc, right here.
OK
> > +	outb(index, TPM_ADDR);
> > +	return inb(TPM_DATA) & 0xFF;
> > +}
> > +
> > +EXPORT_SYMBOL(rdx);
> > +
> > +void wrx(int index, int value)
> > +{
> > +	outb(index, TPM_ADDR);
> > +	outb(value & 0xFF, TPM_DATA);
> > +}
> > +
> > +EXPORT_SYMBOL(wrx);
> 
> These (rdx/wrx) are not appropriate names for global namespace.  Must
> they even be exported?  Could they not be made static inline in tpm.h?
> 
Taken care of by making them inline in the header.

> > +/*
> > + * Initialize the LPC bus and enable the TPM ports
> > + */
> > +int lpc_bus_init(struct pci_dev *pci_dev, u16 base)
> > +{
> > +	u32 lpcenable, tmp;
> > +	int is_lpcm = 0;
> > +
> > +	switch (pci_dev->vendor) {
> > +	case PCI_VENDOR_ID_INTEL:
> 
> This doesn't look quite right to have device specific logic in the
> core.  Shouldn't this go in the device specific driver logic?
> 
Currently we are relying on bus ids because the chip doesn't have a
unique id so this is needed by both specific drivers.

> 			       tmp);
> > +		}
> > +		outb(0x0D, TPM_ADDR);	/* unlock 4F */
> > +		outb(0x55, TPM_DATA);
> > +		outb(0x0A, TPM_ADDR);	/* int disable */
> > +		outb(0x00, TPM_DATA);
> > +		outb(0x08, TPM_ADDR);	/* base addr lo */
> > +		outb(base & 0xFF, TPM_DATA);
> > +		outb(0x09, TPM_ADDR);	/* base addr hi */
> > +		outb((base & 0xFF00) >> 8, TPM_DATA);
> > +		outb(0x0D, TPM_ADDR);	/* lock 4F */
> > +		outb(0xAA, TPM_DATA);
> 
> Hey, aren't these a bunch of those wdx's? ;-)
> 
Fixed in subsequent version.

> > +		u8 status = inb(chip->base + 1);
> 
> Is this guaranteed to be status location on all chips?  Perhaps a
> status() callback is better.
> 
As far as I know this is where status will always be found.  If it
changes I think the easiest thing to do would be to add the status
address to the tpm_vendor_specific struct like the base address is
currently.


> > +	if (chip->num_opens) {
> > +		dev_dbg(&chip->pci_dev->dev,
> > +			"Another process owns this TPM\n");
> > +		rc = -EBUSY;
> > +		goto err_out;
> > +	}
> > +
> > +	chip->num_opens++;
> 
> Hmm, looks a bit like it's just a mutex.
Yes we are only allowing one open at a time as the specification states
that only the TSS can access the device.  Would it be better to use a
mutex rather than a count in this case?

> > +
> > +	if (chip == NULL)
> > +		return -ENODEV;
> 
> Don't think that'll ever happen?
> 
Removed.

> > +
> > +	spin_lock(&driver_lock);
> > +	chip->num_opens--;
> > +	if (chip->num_opens == 0) {
> 
> Is there a case where num_opens-- != 0?  I thought you were making sure
> there was only one open?
> 
Removed.

> > +
> > +	if (chip == NULL)
> > +		return -ENODEV;

> Don't think that'll ever happen?
> 
Removed.

> > +
> > +	down(&chip->user_mutex);
> 
> What is this protecting?
The writes and reads have to be serialized so this was sort of
protecting the tpm_result_buffer.  I have fixed this to be clearer and
reduced the number of mutexes.

> 
> > +
> > +	if (copy_from_user
> > +	    (chip->tpm_result_buffer, (void __user *) buf, size)) {
> > +		up(&chip->user_mutex);
> > +		return -EFAULT;
> > +	}
> 
> This is a buffer overflow waiting to happen.
Fixed
> 
> > +	out_size =
> > +	    tpm_transmit(chip, chip->tpm_result_buffer, TPM_BUFSIZE);
> 
> How does device distinguish from leftover garbage in buffer when size <
> TPM_BUFSIZE?  Wonder if you could read this back from tpm (and leak
> kernel memory to userspace that way)?
> 
The transmit uses the fact that it knows the layout of the data packet
passed into (set by the spec) and looks at the length field.  That is
all that is transmited to the TPM and also back to the user on the read.

> > +	if (chip == NULL)
> > +		return -ENODEV;
> 
> Don't think that'll happen?
> 
Removed.

> > +
> > +	if (chip->num_opens != 0) {
> 
> Won't module refcount care for this?
Removed.


> > +	chip->ops->miscdev.name = devname;
> > +
> > +	chip->ops->miscdev.fops->llseek = no_llseek;
> > +	chip->ops->miscdev.fops->open = tpm_open;
> > +	chip->ops->miscdev.fops->read = tpm_read;
> > +	chip->ops->miscdev.fops->write = tpm_write;
> > +	chip->ops->miscdev.fops->release = tpm_release;
> 
> This is usually done statically, entry is passed in after all.
> 
Fixed


> > +static int __init init_tpm(void)
> > +{
> > +	INIT_LIST_HEAD(&tpm_chip_list);
> > +	spin_lock_init(&driver_lock);
> 
> These can be done statically.
> 
Fixed

> > +	struct semaphore user_mutex;
> > +	struct semaphore timer_mutex;
> > +	struct semaphore sync_mutex;

> Wow, three mutexes for this little data strucutre?
> 
For the user_mutex and timer_mutex I was able to collapse these into one
(and give it a better name).  The sync_mutex was added so I could call
timer_pending in the release function to decide if timers were pending
and needed to be canceled when the device is being closed.  I notice
that del_timer can be called on an inactive timer, I could maybe use
this and drop this extra mutex?  However, someone had suggested I use
del_singleshot_timer_sync which doesn't look like is safe to call on an
inactive timer.  Do you know if del_singleshot_time_sync is necessary in
this case.



> > +/* command bits */
> > +#define	NSC_COMMAND_NORMAL		0x01	/* normal mode */
> > +#define	NSC_COMMAND_BURST		0x81	/* burst mode */
> 
> Hmm, unused.  Is it for a dma type interface?
Dropped.

> > +
> > +	/* status immediately available check */
> > +	*data = inb(chip->base + 1);
> 
> Isn't this base + NSC_STATUS?  Nice to use constants where possible.
> 
Fixed


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [tpmdd-devel] Re: [PATCH 1/1] driver: Tpm hardware enablement --updated version
  2004-12-16 22:48   ` Greg KH
@ 2004-12-17 22:47     ` Kylene Hall
  0 siblings, 0 replies; 43+ messages in thread
From: Kylene Hall @ 2004-12-17 22:47 UTC (permalink / raw)
  To: Greg KH
  Cc: linux-kernel, sailer, leendert, Emily Ratliff, Tom Lendacky, tpmdd-devel

On Thu, 2004-12-16 at 16:48, Greg KH wrote:
Thanks for your help.  Comments and more questions inline.  

> On Thu, Dec 16, 2004 at 04:37:34PM -0600, Kylene Hall wrote:
> > +config TCG_TPM
> > +	tristate "TPM Hardware Support"
> > +	depends on EXPERIMENTAL
> > +	---help---
> > +	  If you have a TPM security chip in your system, which
> > +	  implements the Trusted Computing Group's specification,
> > +	  say Yes and it will be accessible from within Linux. To 
> > +	  compile this driver as a module, choose M here; the module 
> > +	  will be called tpm. For more information see 
> > +	  www.trustedcomputinggroup.org. A implementation of the 
> > +	  Trusted Software Stack (TSS), the userspace enablement piece 
> > +	  of the specification, can be obtained at 
> > +	  http://sourceforge.net/projects/trousers
> > +	  If unsure, say N.
> 
> What happened to the "if built as a module..
> ." text?
It is there in the middle of the paragraph.  I moved it to the end of
the paragraph to make it easier to find in the future.

> 
> > +
> > +config TCG_NSC
> > +	tristate "National Semiconductor TPM Interface"
> > +	depends on TCG_TPM
> > +
> > +config TCG_ATMEL
> > +	tristate "Atmel TPM Interface"
> > +	depends on TCG_TPM
> 
> Please provide help text for these options.
Added.
> 
> > +/*
> > + * Vendor specific TPMs will have a unique name and probe function.
> > + * Those fields should be populated prior to calling this function in
> > + * tpm_<specific>.c's module init function.
> > + */
> > +int register_tpm_driver(struct pci_driver *drv)
> > +{
> > +	drv->id_table = tpm_pci_tbl;
> > +	drv->remove = __devexit_p(tpm_remove);
> > +	drv->suspend = tpm_pm_suspend;
> > +	drv->resume = tpm_pm_resume;
> > +
> > +	return pci_register_driver(drv);
> > +}
> > +
> > +EXPORT_SYMBOL(register_tpm_driver);
> 
> Why not EXPORT_SYMBOL_GPL()?  Based on the content of these drivers, I'd
> feel better if they all were that way, but that's just me :)

> Actually, why even have this function at all?  It's not needed, just
> export the suspend, resume, and remove functions, and you are set.
> 
All fixed.

> Also, don't say that other drivers really support the other pci devices,
> when they do not.  The MODULE_DEVICE_TABLE() stuff needs to be in the
> driver that actually supports that hardware.  Otherwise all of the
> hotplug functionality will not work properly.
> 
So the problem we have is that the chip does not have a unique id and we
are just having to rely on the id of the chipset that the lpc bus is on
therefore either chip (NSC or Atmel, etc.) could claim any of these ids.
Do you have a better suggestion so we can get away from maintaining this
list?  Also, in my latest version this table has been moved to the
header inorder to move to static initialization of the struct pci_driver
as Chris suggested.

> > +
> > +void unregister_tpm_driver(struct pci_driver *drv)
> > +{
> > +	pci_unregister_driver(drv);
> > +}
> > +
> > +EXPORT_SYMBOL(unregister_tpm_driver);
> 
> Um, why even have such a function?
> 
Fixed.
> 
> > +EXPORT_SYMBOL(register_tpm_hardware);
> 
> EXPORT_SYMBOL_GPL() (same goes for all of these exported symbols...)
> 
Fixed.
> > diff -uprN linux-2.6.9/drivers/char/tpm.h linux-2.6.9-tpm/drivers/char/tpm.h
> > --- linux-2.6.9/drivers/char/tpm.h	1969-12-31 18:00:00.000000000 -0600
> > +++ linux-2.6.9-tpm/drivers/char/tpm.h	2004-12-16 17:16:50.000000000 -0600
> > +extern void tpm_time_expired(unsigned long);
> > +extern int rdx(int);
> > +extern void wrx(int, int);
> 
> Please use better names for these functions.  That's very cryptic for a
> global symbol.
Fixed.
> 
> > +extern int lpc_bus_init(struct pci_dev *, u16);
> 
> No "tpm"?
> 
Fixed.
> > +extern int register_tpm_driver(struct pci_driver *);
> > +extern void unregister_tpm_driver(struct pci_driver *);
> > +extern int register_tpm_hardware(struct pci_dev *, struct tpm_chip_ops *,
> > +				 u16);
> 
> Try putting "tpm" first here, for these functions, so the namespace is sane.
> 
Fixed.
> thanks,
> 
> greg k-h
> 
Thanks,
Kylene

> 
> -------------------------------------------------------
> SF email is sponsored by - The IT Product Guide
> Read honest & candid reviews on hundreds of IT Products from real users.
> Discover which products truly live up to the hype. Start reading now. 
> http://productguide.itmanagersjournal.com/
> _______________________________________________
> tpmdd-devel mailing list
> tpmdd-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/tpmdd-devel
> 


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement --updated version
  2004-12-16 22:37 ` [PATCH 1/1] driver: Tpm hardware enablement --updated version Kylene Hall
  2004-12-16 22:48   ` Greg KH
  2004-12-17  0:53   ` Chris Wright
@ 2004-12-17 22:47   ` Kylene Hall
  2004-12-17 22:59     ` Greg KH
  2004-12-20 17:50     ` Kylene Hall
  2004-12-19 19:48   ` [PATCH 1/1] driver: Tpm hardware enablement --updated version Pavel Machek
  3 siblings, 2 replies; 43+ messages in thread
From: Kylene Hall @ 2004-12-17 22:47 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, sailer, leendert, emilyr, toml, tpmdd-devel

This patch is a device driver to enable new hardware.  The new hardware is
the TPM chip as described by specifications at trustedcomputinggroup.org.
The TPM chip will enable you to use hardware to securely store and protect
your keys and personal data.  To use the chip according to the 
 specification, you will need the Trusted Software Stack (TSS) of which an 
 implementation for Linux is available at: 
 http://sourceforge.net/projects/trousers.
 
> Updates include: splitting out the vendor specific code into separated 
> drivers from the base TPM functionality, fixed busy waiting loops, 
> concerns about timer handling and rmmod, buffer name.

Updates include a consolodated buffer mutex, numerous naming improvements, 
better placement of pci_driver and file_operations structure 
initializations, change to use of EXPORT_SYMBOL_GPL, improvement of 
Kconfig help texts and creation of a tpm directory
 
Thanks,
Kylene
 
Signed-off-by: Leendert van Doorn <leendert@watson.ibm.com>
Signed-off-by: Reiner Sailer <sailer@watson.ibm.com>
Signed-off-by: Dave Safford <safford@watson.ibm.com>
Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---
diff -uprN linux-2.6.9/drivers/char/Kconfig linux-2.6.9-tpm/drivers/char/Kconfig
--- linux-2.6.9/drivers/char/Kconfig	2004-10-18 16:53:07.000000000 -0500
+++ linux-2.6.9-tpm/drivers/char/Kconfig	2004-12-17 17:41:00.714774536 -0600
@@ -989,5 +989,7 @@ config MMTIMER
 	  The mmtimer device allows direct userspace access to the
 	  Altix system timer.
 
+source "drivers/char/tpm/Kconfig"
+
 endmenu
 
diff -uprN linux-2.6.9/drivers/char/Makefile linux-2.6.9-tpm/drivers/char/Makefile
--- linux-2.6.9/drivers/char/Makefile	2004-10-18 16:55:28.000000000 -0500
+++ linux-2.6.9-tpm/drivers/char/Makefile	2004-12-17 17:41:06.494895824 -0600
@@ -88,7 +88,7 @@ obj-$(CONFIG_PCMCIA) += pcmcia/
 obj-$(CONFIG_IPMI_HANDLER) += ipmi/
 
 obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
-
+obj-$(CONFIG_TCG_TPM) += tpm/
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c qtronixmap.c
 
diff -uprN linux-2.6.9/drivers/char/tpm/Kconfig linux-2.6.9-tpm/drivers/char/tpm/Kconfig
--- linux-2.6.9/drivers/char/tpm/Kconfig	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/Kconfig	2004-12-17 17:40:51.603159712 -0600
@@ -0,0 +1,39 @@
+#
+# TPM device configuration
+#
+
+menu "TPM devices"
+
+config TCG_TPM
+	tristate "TPM Hardware Support"
+	depends on EXPERIMENTAL
+	---help---
+	  If you have a TPM security chip in your system, which
+	  implements the Trusted Computing Group's specification,
+	  say Yes and it will be accessible from within Linux.  For
+	  more information see <http://www.trustedcomputinggroup.org>. 
+	  An implementation of the Trusted Software Stack (TSS), the 
+	  userspace enablement piece of the specification, can be 
+	  obtained at: <http://sourceforge.net/projects/trousers>.  To 
+	  compile this driver as a module, choose M here; the module 
+	  will be called tpm. If unsure, say N.
+
+config TCG_NSC
+	tristate "National Semiconductor TPM Interface"
+	depends on TCG_TPM
+	---help---
+	  If you have a TPM security chip from National Semicondutor 
+	  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_nsc.
+
+config TCG_ATMEL
+	tristate "Atmel TPM Interface"
+	depends on TCG_TPM
+	---help---
+	  If you have a TPM security chip from Atmel 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_atmel.
+
+endmenu
+
diff -uprN linux-2.6.9/drivers/char/tpm/Makefile linux-2.6.9-tpm/drivers/char/tpm/Makefile
--- linux-2.6.9/drivers/char/tpm/Makefile	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/Makefile	2004-12-17 17:40:46.358956952 -0600
@@ -0,0 +1,7 @@
+#
+# Makefile for the kernel tpm device drivers.
+#
+obj-$(CONFIG_TCG_TPM) += tpm.o
+obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
+obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
+
diff -uprN linux-2.6.9/drivers/char/tpm/tpm_atmel.c linux-2.6.9-tpm/drivers/char/tpm/tpm_atmel.c
--- linux-2.6.9/drivers/char/tpm/tpm_atmel.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/tpm_atmel.c	2004-12-17 17:40:38.899091024 -0600
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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"
+
+/* Atmel definitions */
+#define	TPM_ATML_BASE			0x400
+
+/* write status bits */
+#define	ATML_STATUS_ABORT		0x01
+#define	ATML_STATUS_LASTBYTE		0x04
+
+/* read status bits */
+#define	ATML_STATUS_BUSY		0x01
+#define	ATML_STATUS_DATA_AVAIL		0x02
+#define	ATML_STATUS_REWRITE		0x04
+
+
+static int tpm_atml_recv(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 status, *hdr = buf;
+	u32 size;
+	int i;
+	__be32 *native_size;
+
+	/* start reading header */
+	if (count < 6)
+		return -EIO;
+
+	for (i = 0; i < 6; i++) {
+		status = inb(chip->vendor->base + 1);
+		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+			dev_err(&chip->pci_dev->dev,
+				"error reading header\n");
+			return -EIO;
+		}
+		*buf++ = inb(chip->vendor->base);
+	}
+
+	/* size of the data received */
+	native_size = (__force __be32 *) (hdr + 2);
+	size = be32_to_cpu(*native_size);
+
+	if (count < size)
+		return -EIO;
+
+	/* read all the data available */
+	for (; i < size; i++) {
+		status = inb(chip->vendor->base + 1);
+		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+			dev_err(&chip->pci_dev->dev,
+				"error reading data\n");
+			return -EIO;
+		}
+		*buf++ = inb(chip->vendor->base);
+	}
+
+	/* make sure data available is gone */
+	status = inb(chip->vendor->base + 1);
+	if (status & ATML_STATUS_DATA_AVAIL) {
+		dev_err(&chip->pci_dev->dev, "data available is stuck\n");
+		return -EIO;
+	}
+
+	return size;
+}
+
+static int tpm_atml_send(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	int i;
+
+	dev_dbg(&chip->pci_dev->dev, "tpm_atml_send: ");
+	for (i = 0; i < count; i++) {
+		dev_dbg(&chip->pci_dev->dev, "0x%x(%d) ", buf[i], buf[i]);
+		outb(buf[i], chip->vendor->base);
+	}
+
+	return count;
+}
+
+static void tpm_atml_cancel(struct tpm_chip *chip)
+{
+	outb(ATML_STATUS_ABORT, chip->vendor->base + 1);
+}
+
+static struct file_operations atmel_ops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.open = tpm_open,
+	.read = tpm_read,
+	.write = tpm_write,
+	.release = tpm_release,
+};
+
+static struct tpm_vendor_specific tpm_atmel = {
+	.recv = tpm_atml_recv,
+	.send = tpm_atml_send,
+	.cancel = tpm_atml_cancel,
+	.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
+	.req_complete_val = ATML_STATUS_DATA_AVAIL,
+	.base = TPM_ATML_BASE,
+	.miscdev.fops = &atmel_ops,
+};
+
+static int __devinit tpm_atml_init(struct pci_dev *pci_dev,
+				   const struct pci_device_id *pci_id)
+{
+	u8 version[4];
+	int rc = 0;
+
+	if (pci_enable_device(pci_dev))
+		return -EIO;
+
+	if (tpm_lpc_bus_init(pci_dev, TPM_ATML_BASE)) {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	/* verify that it is an Atmel part */
+	if (tpm_read_index(4) != 'A' || tpm_read_index(5) != 'T'
+	    || tpm_read_index(6) != 'M' || tpm_read_index(7) != 'L') {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+
+	/* query chip for its version number */
+	if ((version[0] = tpm_read_index(0x00)) != 0xFF) {
+		version[1] = tpm_read_index(0x01);
+		version[2] = tpm_read_index(0x02);
+		version[3] = tpm_read_index(0x03);
+	} else {
+		dev_info(&pci_dev->dev, "version query failed\n");
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	if ((rc = tpm_register_hardware(pci_dev, &tpm_atmel)) < 0)
+		goto out_err;
+
+	dev_info(&pci_dev->dev,
+		 "Atmel TPM version %d.%d.%d.%d\n", version[0], version[1],
+		 version[2], version[3]);
+
+	return 0;
+out_err:
+	pci_disable_device(pci_dev);
+	return rc;
+}
+
+static struct pci_driver atmel_pci_driver = {
+	.name = "tpm_atmel",
+	.id_table = tpm_pci_tbl,
+	.probe = tpm_atml_init,
+	.remove = __devexit_p(tpm_remove),
+	.suspend = tpm_pm_suspend,
+	.resume = tpm_pm_resume,
+};
+
+static int __init init_atmel(void)
+{
+	return pci_register_driver(&atmel_pci_driver);
+}
+
+static void __exit cleanup_atmel(void)
+{
+	pci_unregister_driver(&atmel_pci_driver);
+}
+
+module_init(init_atmel);
+module_exit(cleanup_atmel);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.9/drivers/char/tpm/tpm.c linux-2.6.9-tpm/drivers/char/tpm/tpm.c
--- linux-2.6.9/drivers/char/tpm/tpm.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/tpm.c	2004-12-17 17:40:21.108795560 -0600
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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.
+ * 
+ * Note, the TPM chip is not interrupt driven (only polling)
+ * and can have very long timeouts (minutes!). Hence the unusual
+ * calls to schedule_timeout.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include "tpm.h"
+
+#define	TPM_MINOR			224	/* officially assigned */
+
+#define	TPM_BUFSIZE			2048
+
+/* PCI configuration addresses */
+#define	PCI_GEN_PMCON_1			0xA0
+#define	PCI_GEN1_DEC			0xE4
+#define	PCI_LPC_EN			0xE6
+#define	PCI_GEN2_DEC			0xEC
+
+static LIST_HEAD(tpm_chip_list);
+static spinlock_t driver_lock = SPIN_LOCK_UNLOCKED;
+static int dev_mask[32];
+
+static void user_reader_timeout(unsigned long ptr)
+{
+	struct tpm_chip *chip = (struct tpm_chip *) ptr;
+
+	down(&chip->buffer_mutex);
+	atomic_set(&chip->data_pending, 0);
+	memset(chip->data_buffer, 0, TPM_BUFSIZE);
+	up(&chip->buffer_mutex);
+
+}
+
+void tpm_time_expired(unsigned long ptr)
+{
+	int *exp = (int *) ptr;
+	*exp = 1;
+}
+
+EXPORT_SYMBOL_GPL(tpm_time_expired);
+
+/*
+ * Initialize the LPC bus and enable the TPM ports
+ */
+int tpm_lpc_bus_init(struct pci_dev *pci_dev, u16 base)
+{
+	u32 lpcenable, tmp;
+	int is_lpcm = 0;
+
+	switch (pci_dev->vendor) {
+	case PCI_VENDOR_ID_INTEL:
+		switch (pci_dev->device) {
+		case PCI_DEVICE_ID_INTEL_82801CA_12:
+		case PCI_DEVICE_ID_INTEL_82801DB_12:
+			is_lpcm = 1;
+			break;
+		}
+		/* init ICH (enable LPC) */
+		pci_read_config_dword(pci_dev, PCI_GEN1_DEC, &lpcenable);
+		lpcenable |= 0x20000000;
+		pci_write_config_dword(pci_dev, PCI_GEN1_DEC, lpcenable);
+
+		if (is_lpcm) {
+			pci_read_config_dword(pci_dev, PCI_GEN1_DEC,
+					      &lpcenable);
+			if ((lpcenable & 0x20000000) == 0) {
+				dev_err(&pci_dev->dev,
+					"cannot enable LPC\n");
+				return -ENODEV;
+			}
+		}
+
+		/* initialize TPM registers */
+		pci_read_config_dword(pci_dev, PCI_GEN2_DEC, &tmp);
+
+		if (!is_lpcm)
+			tmp = (tmp & 0xFFFF0000) | (base & 0xFFF0);
+		else
+			tmp =
+			    (tmp & 0xFFFF0000) | (base & 0xFFF0) |
+			    0x00000001;
+
+		pci_write_config_dword(pci_dev, PCI_GEN2_DEC, tmp);
+
+		if (is_lpcm) {
+			pci_read_config_dword(pci_dev, PCI_GEN_PMCON_1,
+					      &tmp);
+			tmp |= 0x00000004;	/* enable CLKRUN */
+			pci_write_config_dword(pci_dev, PCI_GEN_PMCON_1,
+					       tmp);
+		}
+		tpm_write_index(0x0D, 0x55);	/* unlock 4F */
+		tpm_write_index(0x0A, 0x00);	/* int disable */
+		tpm_write_index(0x08, base);	/* base addr lo */
+		tpm_write_index(0x09, (base & 0xFF00) >> 8);	/* base addr hi */
+		tpm_write_index(0x0D, 0xAA);	/* lock 4F */
+		break;
+	case PCI_VENDOR_ID_AMD:
+		/* nothing yet */
+		break;
+	}
+
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_lpc_bus_init);
+
+/*
+ * Internal kernel interface to transmit TPM commands
+ */
+static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
+			    size_t bufsiz)
+{
+	ssize_t len;
+	u32 count;
+	__be32 *native_size;
+
+	native_size = (__force __be32 *) (buf + 2);
+	count = be32_to_cpu(*native_size);
+
+	if (count == 0)
+		return -ENODATA;
+	if (count > bufsiz) {
+		dev_err(&chip->pci_dev->dev,
+			"invalid count value %x %x \n", count, bufsiz);
+		return -E2BIG;
+	}
+
+	if ((len = chip->vendor->send(chip, (u8 *) buf, count)) < 0) {
+		dev_err(&chip->pci_dev->dev,
+			"tpm_transmit: tpm_send: error %d\n", len);
+		return len;
+	}
+
+	down(&chip->timer_manipulation_mutex);
+	chip->time_expired = 0;
+	init_timer(&chip->device_timer);
+	chip->device_timer.function = tpm_time_expired;
+	chip->device_timer.expires = jiffies + 2 * 60 * HZ;
+	chip->device_timer.data = (unsigned long) &chip->time_expired;
+	add_timer(&chip->device_timer);
+	up(&chip->timer_manipulation_mutex);
+
+	do {
+		u8 status = inb(chip->vendor->base + 1);
+		if ((status & chip->vendor->req_complete_mask) ==
+		    chip->vendor->req_complete_val) {
+			down(&chip->timer_manipulation_mutex);
+			del_singleshot_timer_sync(&chip->device_timer);
+			up(&chip->timer_manipulation_mutex);
+			goto out_recv;
+		}
+		schedule_timeout(TPM_TIMEOUT);
+		rmb();
+	} while (!chip->time_expired);
+
+
+	chip->vendor->cancel(chip);
+	dev_err(&chip->pci_dev->dev, "Time expired\n");
+	return -EIO;
+
+out_recv:
+	len = chip->vendor->recv(chip, (u8 *) buf, bufsiz);
+	if (len < 0)
+		dev_err(&chip->pci_dev->dev,
+			"tpm_transmit: tpm_recv: error %d\n", len);
+	return len;
+}
+
+/*
+ * Device file system interface to the TPM
+ */
+int tpm_open(struct inode *inode, struct file *file)
+{
+	int rc = 0, minor = iminor(inode);
+	struct tpm_chip *chip = NULL, *pos;
+
+	spin_lock(&driver_lock);
+
+	list_for_each_entry(pos, &tpm_chip_list, list) {
+		if (pos->vendor->miscdev.minor == minor) {
+			chip = pos;
+			break;
+		}
+	}
+
+	if (chip == NULL) {
+		rc = -ENODEV;
+		goto err_out;
+	}
+
+	if (chip->num_opens) {
+		dev_dbg(&chip->pci_dev->dev,
+			"Another process owns this TPM\n");
+		rc = -EBUSY;
+		goto err_out;
+	}
+
+	chip->num_opens++;
+	pci_dev_get(chip->pci_dev);
+
+	spin_unlock(&driver_lock);
+
+	chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
+	if (chip->data_buffer == NULL) {
+		chip->num_opens--;
+		pci_dev_put(chip->pci_dev);
+		return -ENOMEM;
+	}
+
+	atomic_set(&chip->data_pending, 0);
+
+	file->private_data = chip;
+	return 0;
+
+err_out:
+	spin_unlock(&driver_lock);
+	return rc;
+}
+
+EXPORT_SYMBOL_GPL(tpm_open);
+
+int tpm_release(struct inode *inode, struct file *file)
+{
+	struct tpm_chip *chip = file->private_data;
+
+	spin_lock(&driver_lock);
+	chip->num_opens--;
+	down(&chip->timer_manipulation_mutex);
+	if (timer_pending(&chip->user_read_timer))
+		del_singleshot_timer_sync(&chip->user_read_timer);
+	else if (timer_pending(&chip->device_timer))
+		del_singleshot_timer_sync(&chip->device_timer);
+	up(&chip->timer_manipulation_mutex);
+	kfree(chip->data_buffer);
+	atomic_set(&chip->data_pending, 0);
+
+	pci_dev_put(chip->pci_dev);
+	file->private_data = NULL;
+	spin_unlock(&driver_lock);
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_release);
+
+ssize_t tpm_write(struct file * file, const char __user * buf,
+		  size_t size, loff_t * off)
+{
+	struct tpm_chip *chip = file->private_data;
+	int in_size = size, out_size;
+
+	/* cannot perform a write until the read has cleared
+	   either via tpm_read or a user_read_timer timeout */
+	while (atomic_read(&chip->data_pending) != 0) {
+		schedule_timeout(TPM_TIMEOUT);
+	}
+
+	down(&chip->buffer_mutex);
+
+	if (in_size > TPM_BUFSIZE)
+		in_size = TPM_BUFSIZE;
+
+	if (copy_from_user
+	    (chip->data_buffer, (void __user *) buf, in_size)) {
+		up(&chip->buffer_mutex);
+		return -EFAULT;
+	}
+
+	/* atomic tpm command send and result receive */
+	out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE);
+
+	atomic_set(&chip->data_pending, out_size);
+	up(&chip->buffer_mutex);
+
+	/* Set a timeout by which the reader must come claim the result */
+	down(&chip->timer_manipulation_mutex);
+	init_timer(&chip->user_read_timer);
+	chip->user_read_timer.function = user_reader_timeout;
+	chip->user_read_timer.data = (unsigned long) chip;
+	chip->user_read_timer.expires = jiffies + (60 * HZ);
+	add_timer(&chip->user_read_timer);
+	up(&chip->timer_manipulation_mutex);
+
+	return in_size;
+}
+
+EXPORT_SYMBOL_GPL(tpm_write);
+
+ssize_t tpm_read(struct file * file, char __user * buf,
+		 size_t size, loff_t * off)
+{
+	struct tpm_chip *chip = file->private_data;
+	int ret_size = -ENODATA;
+
+	if (atomic_read(&chip->data_pending) != 0) {	/* Result available */
+		down(&chip->timer_manipulation_mutex);
+		del_singleshot_timer_sync(&chip->user_read_timer);
+		up(&chip->timer_manipulation_mutex);
+
+		down(&chip->buffer_mutex);
+
+		ret_size = atomic_read(&chip->data_pending);
+		atomic_set(&chip->data_pending, 0);
+
+		if (ret_size == 0)	/* timeout just occurred */
+			ret_size = -ETIME;
+		else if (ret_size > 0) {	/* relay data */
+			if (size < ret_size)
+				ret_size = size;
+
+			if (copy_to_user((void __user *) buf,
+					 chip->data_buffer, ret_size)) {
+				ret_size = -EFAULT;
+			}
+		}
+		up(&chip->buffer_mutex);
+	}
+
+	return ret_size;
+}
+
+EXPORT_SYMBOL_GPL(tpm_read);
+
+void __devexit tpm_remove(struct pci_dev *pci_dev)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+
+	if (chip == NULL) {
+		dev_err(&pci_dev->dev, "No device data found\n");
+		return;
+	}
+
+	spin_lock(&driver_lock);
+
+	list_del(&chip->list);
+
+	pci_set_drvdata(pci_dev, NULL);
+	misc_deregister(&chip->vendor->miscdev);
+	spin_unlock(&driver_lock);
+
+	pci_disable_device(pci_dev);
+
+	dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
+
+	kfree(chip);
+
+	pci_dev_put(pci_dev);
+}
+
+EXPORT_SYMBOL_GPL(tpm_remove);
+
+static u8 savestate[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 10,		/* blob length (in bytes) */
+	0, 0, 0, 152		/* TPM_ORD_SaveState */
+};
+
+/*
+ * We are about to suspend. Save the TPM state
+ * so that it can be restored.
+ */
+int tpm_pm_suspend(struct pci_dev *pci_dev, u32 pm_state)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+	if (chip == NULL)
+		return -ENODEV;
+
+	tpm_transmit(chip, savestate, sizeof(savestate));
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_pm_suspend);
+
+/*
+ * Resume from a power safe. The BIOS already restored
+ * the TPM state.
+ */
+int tpm_pm_resume(struct pci_dev *pci_dev)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+	if (chip == NULL)
+		return -ENODEV;
+
+	spin_lock(&driver_lock);
+	tpm_lpc_bus_init(pci_dev, chip->vendor->base);
+	spin_unlock(&driver_lock);
+
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_pm_resume);
+
+/*
+ * Called from tpm_<specific>.c probe function only for devices 
+ * the driver has determined it should claim.  Prior to calling
+ * this function the specific probe function has called pci_enable_device
+ * upon errant exit from this function specific probe function should call
+ * pci_disable_device
+ */
+int tpm_register_hardware(struct pci_dev *pci_dev,
+			  struct tpm_vendor_specific *entry)
+{
+	char devname[7];
+	struct tpm_chip *chip;
+	int i, j;
+
+	/* Driver specific per-device data */
+	chip = kmalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	memset(chip, 0, sizeof(struct tpm_chip));
+
+	init_MUTEX(&chip->buffer_mutex);
+	init_MUTEX(&chip->timer_manipulation_mutex);
+	INIT_LIST_HEAD(&chip->list);
+
+	chip->vendor = entry;
+
+	chip->dev_num = -1;
+
+	for (i = 0; i < 32; i++)
+		for (j = 0; j < 8; j++)
+			if ((dev_mask[i] & (1 << j)) == 0) {
+				chip->dev_num = i * 32 + j;
+				dev_mask[i] |= 1 << j;
+				goto dev_num_search_complete;
+			}
+
+dev_num_search_complete:
+	if (chip->dev_num < 0) {
+		dev_err(&pci_dev->dev,
+			"No available tpm device numbers\n");
+		kfree(chip);
+		return -ENODEV;
+	} else if (chip->dev_num == 0)
+		chip->vendor->miscdev.minor = TPM_MINOR;
+	else
+		chip->vendor->miscdev.minor = MISC_DYNAMIC_MINOR;
+
+	snprintf(devname, sizeof(devname), "%s%d", "tpm", chip->dev_num);
+	chip->vendor->miscdev.name = devname;
+
+	chip->vendor->miscdev.dev = &(pci_dev->dev);
+	chip->pci_dev = pci_dev_get(pci_dev);
+
+	spin_lock(&driver_lock);
+
+	if (misc_register(&chip->vendor->miscdev)) {
+		dev_err(&chip->pci_dev->dev,
+			"unable to misc_register %s, minor %d\n",
+			chip->vendor->miscdev.name,
+			chip->vendor->miscdev.minor);
+		pci_dev_put(pci_dev);
+		spin_unlock(&driver_lock);
+		kfree(chip);
+		dev_mask[i] &= !(1 << j);
+		return -ENODEV;
+	}
+
+	pci_set_drvdata(pci_dev, chip);
+
+	list_add(&chip->list, &tpm_chip_list);
+	spin_unlock(&driver_lock);
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_register_hardware);
+
+static int __init init_tpm(void)
+{
+	return 0;
+}
+
+static void __exit cleanup_tpm(void)
+{
+
+}
+
+module_init(init_tpm);
+module_exit(cleanup_tpm);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.9/drivers/char/tpm/tpm.h linux-2.6.9-tpm/drivers/char/tpm/tpm.h
--- linux-2.6.9/drivers/char/tpm/tpm.h	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/tpm.h	2004-12-17 17:40:24.476283624 -0600
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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/module.h>
+#include <linux/version.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/miscdevice.h>
+
+#define TPM_TIMEOUT msecs_to_jiffies(5)
+
+/* TPM addresses */
+#define	TPM_ADDR			0x4E
+#define	TPM_DATA			0x4F
+
+struct tpm_chip;
+
+struct tpm_vendor_specific {
+	u8 req_complete_mask;
+	u8 req_complete_val;
+	u16 base;		/* TPM base address */
+
+	int (*recv) (struct tpm_chip *, u8 *, size_t);
+	int (*send) (struct tpm_chip *, u8 *, size_t);
+	void (*cancel) (struct tpm_chip *);
+	struct miscdevice miscdev;
+};
+
+struct tpm_chip {
+	struct pci_dev *pci_dev;	/* PCI device stuff */
+
+	int dev_num;		/* /dev/tpm# */
+	int num_opens;		/* only one allowed */
+	int time_expired;
+
+	/* Data passed to and from the tpm via the read/write calls */
+	u8 *data_buffer;
+	atomic_t data_pending;
+	struct semaphore buffer_mutex;
+
+	struct timer_list user_read_timer;	/* user needs to claim result */
+	struct timer_list device_timer;	/* tpm is processing */
+	struct semaphore timer_manipulation_mutex;
+
+	struct tpm_vendor_specific *vendor;
+
+	struct list_head list;
+};
+
+static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
+
+static inline int tpm_read_index(int index)
+{
+	outb(index, TPM_ADDR);
+	return inb(TPM_DATA) & 0xFF;
+}
+
+static inline void tpm_write_index(int index, int value)
+{
+	outb(index, TPM_ADDR);
+	outb(value & 0xFF, TPM_DATA);
+}
+
+extern void tpm_time_expired(unsigned long);
+extern int tpm_lpc_bus_init(struct pci_dev *, u16);
+
+extern int tpm_register_hardware(struct pci_dev *,
+				 struct tpm_vendor_specific *);
+extern int tpm_open(struct inode *, struct file *);
+extern int tpm_release(struct inode *, struct file *);
+extern ssize_t tpm_write(struct file *, const char __user *, size_t,
+			 loff_t *);
+extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *);
+extern void __devexit tpm_remove(struct pci_dev *);
+extern int tpm_pm_suspend(struct pci_dev *, u32);
+extern int tpm_pm_resume(struct pci_dev *);
diff -uprN linux-2.6.9/drivers/char/tpm/tpm_nsc.c linux-2.6.9-tpm/drivers/char/tpm/tpm_nsc.c
--- linux-2.6.9/drivers/char/tpm/tpm_nsc.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/tpm_nsc.c	2004-12-17 17:40:34.772718328 -0600
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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"
+
+/* National definitions */
+#define	TPM_NSC_BASE			0x360
+#define	TPM_NSC_IRQ			0x07
+
+#define	NSC_LDN_INDEX			0x07
+#define	NSC_SID_INDEX			0x20
+#define	NSC_LDC_INDEX			0x30
+#define	NSC_DIO_INDEX			0x60
+#define	NSC_CIO_INDEX			0x62
+#define	NSC_IRQ_INDEX			0x70
+#define	NSC_ITS_INDEX			0x71
+
+#define	NSC_STATUS			0x01
+#define	NSC_COMMAND			0x01
+#define	NSC_DATA			0x00
+
+/* status bits */
+#define	NSC_STATUS_OBF			0x01	/* output buffer full */
+#define	NSC_STATUS_IBF			0x02	/* input buffer full */
+#define	NSC_STATUS_F0			0x04	/* F0 */
+#define	NSC_STATUS_A2			0x08	/* A2 */
+#define	NSC_STATUS_RDY			0x10	/* ready to receive command */
+#define	NSC_STATUS_IBR			0x20	/* ready to receive data */
+
+/* command bits */
+#define	NSC_COMMAND_NORMAL		0x01	/* normal mode */
+#define	NSC_COMMAND_EOC			0x03
+#define	NSC_COMMAND_CANCEL		0x22
+
+/*
+ * Wait for a certain status to appear
+ */
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
+{
+	int expired = 0;
+	struct timer_list status_timer =
+	    TIMER_INITIALIZER(tpm_time_expired, jiffies + 10 * HZ,
+			      (unsigned long) &expired);
+
+	/* status immediately available check */
+	*data = inb(chip->vendor->base + NSC_STATUS);
+	if ((*data & mask) == val)
+		return 0;
+
+	/* wait for status */
+	add_timer(&status_timer);
+	do {
+		schedule_timeout(TPM_TIMEOUT);
+		*data = inb(chip->vendor->base + 1);
+		if ((*data & mask) == val) {
+			del_singleshot_timer_sync(&status_timer);
+			return 0;
+		}
+	}
+	while (!expired);
+
+	return -EBUSY;
+}
+
+static int nsc_wait_for_ready(struct tpm_chip *chip)
+{
+	int status;
+	int expired = 0;
+	struct timer_list status_timer =
+	    TIMER_INITIALIZER(tpm_time_expired, jiffies + 100,
+			      (unsigned long) &expired);
+
+	/* status immediately available check */
+	status = inb(chip->vendor->base + NSC_STATUS);
+	if (status & NSC_STATUS_OBF)
+		status = inb(chip->vendor->base + NSC_DATA);
+	if (status & NSC_STATUS_RDY)
+		return 0;
+
+	/* wait for status */
+	add_timer(&status_timer);
+	do {
+		schedule_timeout(TPM_TIMEOUT);
+		status = inb(chip->vendor->base + NSC_STATUS);
+		if (status & NSC_STATUS_OBF)
+			status = inb(chip->vendor->base + NSC_DATA);
+		if (status & NSC_STATUS_RDY) {
+			del_singleshot_timer_sync(&status_timer);
+			return 0;
+		}
+	}
+	while (!expired);
+
+	dev_info(&chip->pci_dev->dev, "wait for ready failed\n");
+	return -EBUSY;
+}
+
+
+static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 *buffer = buf;
+	u8 data, *p;
+	u32 size;
+	__be32 *native_size;
+
+	if (count < 6)
+		return -EIO;
+
+	if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "F0 timeout\n");
+		return -EIO;
+	}
+	if ((data =
+	     inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
+		dev_err(&chip->pci_dev->dev, "not in normal mode (0x%x)\n",
+			data);
+		return -EIO;
+	}
+
+	/* read the whole packet */
+	for (p = buffer; p < &buffer[count]; p++) {
+		if (wait_for_stat
+		    (chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) {
+			dev_err(&chip->pci_dev->dev,
+				"OBF timeout (while reading data)\n");
+			return -EIO;
+		}
+		if (data & NSC_STATUS_F0)
+			break;
+		*p = inb(chip->vendor->base + NSC_DATA);
+	}
+
+	if ((data & NSC_STATUS_F0) == 0) {
+		dev_err(&chip->pci_dev->dev, "F0 not set\n");
+		return -EIO;
+	}
+	if ((data = inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_EOC) {
+		dev_err(&chip->pci_dev->dev,
+			"expected end of command(0x%x)\n", data);
+		return -EIO;
+	}
+
+	native_size = (__force __be32 *) (buf + 2);
+	size = be32_to_cpu(*native_size);
+
+	if (count < size)
+		return -EIO;
+
+	return size;
+}
+
+static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 data;
+	int i;
+
+	/*
+	 * If we hit the chip with back to back commands it locks up
+	 * and never set IBF. Hitting it with this "hammer" seems to
+	 * fix it. Not sure why this is needed, we followed the flow
+	 * chart in the manual to the letter.
+	 */
+	outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND);
+
+	if (nsc_wait_for_ready(chip) != 0)
+		return -EIO;
+
+	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+		return -EIO;
+	}
+
+	outb(NSC_COMMAND_NORMAL, chip->vendor->base + NSC_COMMAND);
+	if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBR timeout\n");
+		return -EIO;
+	}
+
+	for (i = 0; i < count; i++) {
+		if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+			dev_err(&chip->pci_dev->dev,
+				"IBF timeout (while writing data)\n");
+			return -EIO;
+		}
+		outb(buf[i], chip->vendor->base + NSC_DATA);
+	}
+
+	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+		return -EIO;
+	}
+	outb(NSC_COMMAND_EOC, chip->vendor->base + NSC_COMMAND);
+
+	return count;
+}
+
+static void tpm_nsc_cancel(struct tpm_chip *chip)
+{
+	outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND);
+}
+
+static struct file_operations nsc_ops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.open = tpm_open,
+	.read = tpm_read,
+	.write = tpm_write,
+	.release = tpm_release,
+};
+
+static struct tpm_vendor_specific tpm_nsc = {
+	.recv = tpm_nsc_recv,
+	.send = tpm_nsc_send,
+	.cancel = tpm_nsc_cancel,
+	.req_complete_mask = NSC_STATUS_OBF,
+	.req_complete_val = NSC_STATUS_OBF,
+	.base = TPM_NSC_BASE,
+	.miscdev.fops = &nsc_ops,
+};
+
+static int __devinit tpm_nsc_init(struct pci_dev *pci_dev,
+				  const struct pci_device_id *pci_id)
+{
+	int rc = 0;
+
+	if (pci_enable_device(pci_dev))
+		return -EIO;
+
+	if (tpm_lpc_bus_init(pci_dev, TPM_NSC_BASE)) {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	/* verify that it is a National part (SID) */
+	if (tpm_read_index(NSC_SID_INDEX) != 0xEF) {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	dev_dbg(&pci_dev->dev, "NSC TPM detected\n");
+	dev_dbg(&pci_dev->dev,
+		"NSC LDN 0x%x, SID 0x%x, SRID 0x%x\n",
+		tpm_read_index(0x07), tpm_read_index(0x20),
+		tpm_read_index(0x27));
+	dev_dbg(&pci_dev->dev,
+		"NSC SIOCF1 0x%x SIOCF5 0x%x SIOCF6 0x%x SIOCF8 0x%x\n",
+		tpm_read_index(0x21), tpm_read_index(0x25),
+		tpm_read_index(0x26), tpm_read_index(0x28));
+	dev_dbg(&pci_dev->dev, "NSC IO Base0 0x%x\n",
+		(tpm_read_index(0x60) << 8) | tpm_read_index(0x61));
+	dev_dbg(&pci_dev->dev, "NSC IO Base1 0x%x\n",
+		(tpm_read_index(0x62) << 8) | tpm_read_index(0x63));
+	dev_dbg(&pci_dev->dev, "NSC Interrupt number and wakeup 0x%x\n",
+		tpm_read_index(0x70));
+	dev_dbg(&pci_dev->dev, "NSC IRQ type select 0x%x\n",
+		tpm_read_index(0x71));
+	dev_dbg(&pci_dev->dev,
+		"NSC DMA channel select0 0x%x, select1 0x%x\n",
+		tpm_read_index(0x74), tpm_read_index(0x75));
+	dev_dbg(&pci_dev->dev,
+		"NSC Config "
+		"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+		tpm_read_index(0xF0), tpm_read_index(0xF1),
+		tpm_read_index(0xF2), tpm_read_index(0xF3),
+		tpm_read_index(0xF4), tpm_read_index(0xF5),
+		tpm_read_index(0xF6), tpm_read_index(0xF7),
+		tpm_read_index(0xF8), tpm_read_index(0xF9));
+
+	dev_info(&pci_dev->dev,
+		 "NSC PC21100 TPM revision %d\n",
+		 tpm_read_index(0x27) & 0x1F);
+
+	if (tpm_read_index(NSC_LDC_INDEX) == 0)
+		dev_info(&pci_dev->dev, ": NSC TPM not active\n");
+
+	/* select PM channel 1 */
+	tpm_write_index(NSC_LDN_INDEX, 0x12);
+	tpm_read_index(NSC_LDN_INDEX);
+
+	/* disable the DPM module */
+	tpm_write_index(NSC_LDC_INDEX, 0);
+	tpm_read_index(NSC_LDC_INDEX);
+
+	/* set the data register base addresses */
+	tpm_write_index(NSC_DIO_INDEX, TPM_NSC_BASE >> 8);
+	tpm_write_index(NSC_DIO_INDEX + 1, TPM_NSC_BASE);
+	tpm_read_index(NSC_DIO_INDEX);
+	tpm_read_index(NSC_DIO_INDEX + 1);
+
+	/* set the command register base addresses */
+	tpm_write_index(NSC_CIO_INDEX, (TPM_NSC_BASE + 1) >> 8);
+	tpm_write_index(NSC_CIO_INDEX + 1, (TPM_NSC_BASE + 1));
+	tpm_read_index(NSC_DIO_INDEX);
+	tpm_read_index(NSC_DIO_INDEX + 1);
+
+	/* set the interrupt number to be used for the host interface */
+	tpm_write_index(NSC_IRQ_INDEX, TPM_NSC_IRQ);
+	tpm_write_index(NSC_ITS_INDEX, 0x00);
+	tpm_read_index(NSC_IRQ_INDEX);
+
+	/* enable the DPM module */
+	tpm_write_index(NSC_LDC_INDEX, 0x01);
+	tpm_read_index(NSC_LDC_INDEX);
+
+	if ((rc = tpm_register_hardware(pci_dev, &tpm_nsc)) < 0)
+		goto out_err;
+
+	return 0;
+
+out_err:
+	pci_disable_device(pci_dev);
+	return rc;
+}
+
+static struct pci_driver nsc_pci_driver = {
+	.name = "tpm_nsc",
+	.id_table = tpm_pci_tbl,
+	.probe = tpm_nsc_init,
+	.remove = __devexit_p(tpm_remove),
+	.suspend = tpm_pm_suspend,
+	.resume = tpm_pm_resume,
+};
+
+static int __init init_nsc(void)
+{
+	return pci_register_driver(&nsc_pci_driver);
+}
+
+static void __exit cleanup_nsc(void)
+{
+	pci_unregister_driver(&nsc_pci_driver);
+}
+
+module_init(init_nsc);
+module_exit(cleanup_nsc);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.9/include/linux/pci_ids.h linux-2.6.9-tpm/include/linux/pci_ids.h
--- linux-2.6.9/include/linux/pci_ids.h	2004-12-06 16:53:35.000000000 -0600
+++ linux-2.6.9-tpm/include/linux/pci_ids.h	2004-12-06 14:27:05.000000000 -0600
@@ -494,6 +494,7 @@
 #define PCI_DEVICE_ID_AMD_OPUS_7449	0x7449
 #	define PCI_DEVICE_ID_AMD_VIPER_7449	PCI_DEVICE_ID_AMD_OPUS_7449
 #define PCI_DEVICE_ID_AMD_8111_LAN	0x7462
+#define PCI_DEVICE_ID_AMD_8111_LPC	0x7468
 #define PCI_DEVICE_ID_AMD_8111_IDE	0x7469
 #define PCI_DEVICE_ID_AMD_8111_AUDIO	0x746d
 #define PCI_DEVICE_ID_AMD_8151_0	0x7454
diff -uprN linux-2.6.9/MAINTAINERS linux-2.6.9-tpm/MAINTAINERS
--- linux-2.6.9/MAINTAINERS	2004-10-18 16:54:37.000000000 -0500
+++ linux-2.6.9-tpm/MAINTAINERS	2004-12-07 12:39:10.000000000 -0600
@@ -2144,6 +2144,12 @@ L:	tlinux-users@tce.toshiba-dme.co.jp
 W:	http://www.buzzard.org.uk/toshiba/
 S:	Maintained
 
+TPM DRIVER
+P:	Kylene Hall
+M:	tpmdd-devel@lists.sourceforge.net
+L:	tpmdd-devel@lists.sourceforge.net
+S:	Maintained
+
 TRIDENT 4DWAVE/SIS 7018 PCI AUDIO CORE
 P:	Muli Ben-Yehuda
 M:	mulix@mulix.org

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement --updated version
  2004-12-17 22:47   ` Kylene Hall
@ 2004-12-17 22:59     ` Greg KH
  2004-12-20 17:50     ` Kylene Hall
  1 sibling, 0 replies; 43+ messages in thread
From: Greg KH @ 2004-12-17 22:59 UTC (permalink / raw)
  To: Kylene Hall; +Cc: linux-kernel, sailer, leendert, emilyr, toml, tpmdd-devel

On Fri, Dec 17, 2004 at 04:47:18PM -0600, Kylene Hall wrote:
> +static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
> +	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
> +	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
> +	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
> +	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
> +	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
> +	{PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
> +	{0,}
> +};
> +
> +MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);

Please do not put this in a .h file.  It belongs in the .c files.  If
both tpm drivers could be on all of the above pci devices, then that's
fine.  But odds are, eventually the table will start to differenciate.

This is also not a good idea, as the main tpm core module will end up
with this pci device table, and it should not, as it does not support
any pci devices itself.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement --updated version
  2004-12-16 22:37 ` [PATCH 1/1] driver: Tpm hardware enablement --updated version Kylene Hall
                     ` (2 preceding siblings ...)
  2004-12-17 22:47   ` Kylene Hall
@ 2004-12-19 19:48   ` Pavel Machek
  3 siblings, 0 replies; 43+ messages in thread
From: Pavel Machek @ 2004-12-19 19:48 UTC (permalink / raw)
  To: Kylene Hall
  Cc: linux-kernel, greg, sailer, leendert, emilyr, toml, tpmdd-devel

Hi!

> This patch is a device driver to enable new hardware.  The new hardware is
> the TPM chip as described by specifications at trustedcomputinggroup.org.
> The TPM chip will enable you to use hardware to securely store and protect
> your keys and personal data.  To use the chip according to the 
> specification, you will need the Trusted Software Stack (TSS) of which an 
> implementation for Linux is available at: 
> http://sourceforge.net/projects/trousers.

Could we get some [short] description of what this chip does and
userland intereface into Documentation/?

								Pavel
-- 
People were complaining that M$ turns users into beta-testers...
...jr ghea gurz vagb qrirybcref, naq gurl frrz gb yvxr vg gung jnl!

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement --updated version
  2004-12-17 22:47   ` Kylene Hall
  2004-12-17 22:59     ` Greg KH
@ 2004-12-20 17:50     ` Kylene Hall
  2004-12-21 16:51       ` Nish Aravamudan
  1 sibling, 1 reply; 43+ messages in thread
From: Kylene Hall @ 2004-12-20 17:50 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, sailer, leendert, emilyr, toml, tpmdd-devel

This patch is a device driver to enable new hardware.  The new hardware is
the TPM chip as described by specifications at 
<http://www.trustedcomputinggroup.org>.  The TPM chip will enable you to 
use hardware to securely store and protect your keys and personal data.  
To use the chip according to the specification, you will need the Trusted 
Software Stack (TSS) of which an implementation for Linux is available at: 
<http://sourceforge.net/projects/trousers>.
  
> > Updates include: splitting out the vendor specific code into separated 
> > drivers from the base TPM functionality, fixed busy waiting loops, 
> > concerns about timer handling and rmmod, buffer name.
 
> Updates include a consolodated buffer mutex, numerous naming improvements, 
> better placement of pci_driver and file_operations structure 
> initializations, change to use of EXPORT_SYMBOL_GPL, improvement of 
> Kconfig help texts and creation of a tpm directory

Updates include moving the pci_ids table into the individual vendor 
specific .c files and including a tpm description file in the 
Documentation directory.
  
Thanks,
Kylene
  
Signed-off-by: Leendert van Doorn <leendert@watson.ibm.com>
Signed-off-by: Reiner Sailer <sailer@watson.ibm.com>
Signed-off-by: Dave Safford <safford@watson.ibm.com>
Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---
diff -uprN linux-2.6.9/Documentation/tpm.txt linux-2.6.9-tpm/Documentation/tpm.txt
--- linux-2.6.9/Documentation/tpm.txt	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/Documentation/tpm.txt	2004-12-20 12:35:27.000000000 -0600
@@ -0,0 +1,12 @@
+			TPM Support
+
+	The Trusted Platform Module (TPM) is a hardware device described by 
+specifications at <http://www.trustedcomputinggroup.org>.  Some of the 
+capabilities of the device include a random number generator as well as 
+protection of keys, digital certificates, passwords and data securely by the 
+hardware.  Besides the specification for this piece of hardware the Trusted 
+Computing Group (TCG) provides a specification for an library interface to the 
+hardware that is to be used by applications.  This interface is known as the 
+Trusted Software Stack (TSS).  The TSS specification is also available at the 
+TCG website and an implementation of the interface is available for Linux 
+at <http://sourceforge.net/projects/trousers>.
diff -uprN linux-2.6.9/drivers/char/Kconfig linux-2.6.9-tpm/drivers/char/Kconfig
--- linux-2.6.9/drivers/char/Kconfig	2004-10-18 16:53:07.000000000 -0500
+++ linux-2.6.9-tpm/drivers/char/Kconfig	2004-12-17 17:41:00.000000000 -0600
@@ -989,5 +989,7 @@ config MMTIMER
 	  The mmtimer device allows direct userspace access to the
 	  Altix system timer.
 
+source "drivers/char/tpm/Kconfig"
+
 endmenu
 
diff -uprN linux-2.6.9/drivers/char/Makefile linux-2.6.9-tpm/drivers/char/Makefile
--- linux-2.6.9/drivers/char/Makefile	2004-10-18 16:55:28.000000000 -0500
+++ linux-2.6.9-tpm/drivers/char/Makefile	2004-12-17 17:41:06.000000000 -0600
@@ -88,7 +88,7 @@ obj-$(CONFIG_PCMCIA) += pcmcia/
 obj-$(CONFIG_IPMI_HANDLER) += ipmi/
 
 obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
-
+obj-$(CONFIG_TCG_TPM) += tpm/
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c qtronixmap.c
 
diff -uprN linux-2.6.9/drivers/char/tpm/Kconfig linux-2.6.9-tpm/drivers/char/tpm/Kconfig
--- linux-2.6.9/drivers/char/tpm/Kconfig	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/Kconfig	2004-12-17 17:40:51.000000000 -0600
@@ -0,0 +1,39 @@
+#
+# TPM device configuration
+#
+
+menu "TPM devices"
+
+config TCG_TPM
+	tristate "TPM Hardware Support"
+	depends on EXPERIMENTAL
+	---help---
+	  If you have a TPM security chip in your system, which
+	  implements the Trusted Computing Group's specification,
+	  say Yes and it will be accessible from within Linux.  For
+	  more information see <http://www.trustedcomputinggroup.org>. 
+	  An implementation of the Trusted Software Stack (TSS), the 
+	  userspace enablement piece of the specification, can be 
+	  obtained at: <http://sourceforge.net/projects/trousers>.  To 
+	  compile this driver as a module, choose M here; the module 
+	  will be called tpm. If unsure, say N.
+
+config TCG_NSC
+	tristate "National Semiconductor TPM Interface"
+	depends on TCG_TPM
+	---help---
+	  If you have a TPM security chip from National Semicondutor 
+	  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_nsc.
+
+config TCG_ATMEL
+	tristate "Atmel TPM Interface"
+	depends on TCG_TPM
+	---help---
+	  If you have a TPM security chip from Atmel 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_atmel.
+
+endmenu
+
diff -uprN linux-2.6.9/drivers/char/tpm/Makefile linux-2.6.9-tpm/drivers/char/tpm/Makefile
--- linux-2.6.9/drivers/char/tpm/Makefile	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/Makefile	2004-12-17 17:40:46.000000000 -0600
@@ -0,0 +1,7 @@
+#
+# Makefile for the kernel tpm device drivers.
+#
+obj-$(CONFIG_TCG_TPM) += tpm.o
+obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
+obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
+
diff -uprN linux-2.6.9/drivers/char/tpm/tpm_atmel.c linux-2.6.9-tpm/drivers/char/tpm/tpm_atmel.c
--- linux-2.6.9/drivers/char/tpm/tpm_atmel.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/tpm_atmel.c	2004-12-20 10:34:57.000000000 -0600
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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"
+
+/* Atmel definitions */
+#define	TPM_ATML_BASE			0x400
+
+/* write status bits */
+#define	ATML_STATUS_ABORT		0x01
+#define	ATML_STATUS_LASTBYTE		0x04
+
+/* read status bits */
+#define	ATML_STATUS_BUSY		0x01
+#define	ATML_STATUS_DATA_AVAIL		0x02
+#define	ATML_STATUS_REWRITE		0x04
+
+
+static int tpm_atml_recv(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 status, *hdr = buf;
+	u32 size;
+	int i;
+	__be32 *native_size;
+
+	/* start reading header */
+	if (count < 6)
+		return -EIO;
+
+	for (i = 0; i < 6; i++) {
+		status = inb(chip->vendor->base + 1);
+		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+			dev_err(&chip->pci_dev->dev,
+				"error reading header\n");
+			return -EIO;
+		}
+		*buf++ = inb(chip->vendor->base);
+	}
+
+	/* size of the data received */
+	native_size = (__force __be32 *) (hdr + 2);
+	size = be32_to_cpu(*native_size);
+
+	if (count < size)
+		return -EIO;
+
+	/* read all the data available */
+	for (; i < size; i++) {
+		status = inb(chip->vendor->base + 1);
+		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+			dev_err(&chip->pci_dev->dev,
+				"error reading data\n");
+			return -EIO;
+		}
+		*buf++ = inb(chip->vendor->base);
+	}
+
+	/* make sure data available is gone */
+	status = inb(chip->vendor->base + 1);
+	if (status & ATML_STATUS_DATA_AVAIL) {
+		dev_err(&chip->pci_dev->dev, "data available is stuck\n");
+		return -EIO;
+	}
+
+	return size;
+}
+
+static int tpm_atml_send(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	int i;
+
+	dev_dbg(&chip->pci_dev->dev, "tpm_atml_send: ");
+	for (i = 0; i < count; i++) {
+		dev_dbg(&chip->pci_dev->dev, "0x%x(%d) ", buf[i], buf[i]);
+		outb(buf[i], chip->vendor->base);
+	}
+
+	return count;
+}
+
+static void tpm_atml_cancel(struct tpm_chip *chip)
+{
+	outb(ATML_STATUS_ABORT, chip->vendor->base + 1);
+}
+
+static struct file_operations atmel_ops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.open = tpm_open,
+	.read = tpm_read,
+	.write = tpm_write,
+	.release = tpm_release,
+};
+
+static struct tpm_vendor_specific tpm_atmel = {
+	.recv = tpm_atml_recv,
+	.send = tpm_atml_send,
+	.cancel = tpm_atml_cancel,
+	.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
+	.req_complete_val = ATML_STATUS_DATA_AVAIL,
+	.base = TPM_ATML_BASE,
+	.miscdev.fops = &atmel_ops,
+};
+
+static int __devinit tpm_atml_init(struct pci_dev *pci_dev,
+				   const struct pci_device_id *pci_id)
+{
+	u8 version[4];
+	int rc = 0;
+
+	if (pci_enable_device(pci_dev))
+		return -EIO;
+
+	if (tpm_lpc_bus_init(pci_dev, TPM_ATML_BASE)) {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	/* verify that it is an Atmel part */
+	if (tpm_read_index(4) != 'A' || tpm_read_index(5) != 'T'
+	    || tpm_read_index(6) != 'M' || tpm_read_index(7) != 'L') {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	/* query chip for its version number */
+	if ((version[0] = tpm_read_index(0x00)) != 0xFF) {
+		version[1] = tpm_read_index(0x01);
+		version[2] = tpm_read_index(0x02);
+		version[3] = tpm_read_index(0x03);
+	} else {
+		dev_info(&pci_dev->dev, "version query failed\n");
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	if ((rc = tpm_register_hardware(pci_dev, &tpm_atmel)) < 0)
+		goto out_err;
+
+	dev_info(&pci_dev->dev,
+		 "Atmel TPM version %d.%d.%d.%d\n", version[0], version[1],
+		 version[2], version[3]);
+
+	return 0;
+out_err:
+	pci_disable_device(pci_dev);
+	return rc;
+}
+
+static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
+
+static struct pci_driver atmel_pci_driver = {
+	.name = "tpm_atmel",
+	.id_table = tpm_pci_tbl,
+	.probe = tpm_atml_init,
+	.remove = __devexit_p(tpm_remove),
+	.suspend = tpm_pm_suspend,
+	.resume = tpm_pm_resume,
+};
+
+static int __init init_atmel(void)
+{
+	return pci_register_driver(&atmel_pci_driver);
+}
+
+static void __exit cleanup_atmel(void)
+{
+	pci_unregister_driver(&atmel_pci_driver);
+}
+
+module_init(init_atmel);
+module_exit(cleanup_atmel);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.9/drivers/char/tpm/tpm.c linux-2.6.9-tpm/drivers/char/tpm/tpm.c
--- linux-2.6.9/drivers/char/tpm/tpm.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/tpm.c	2004-12-17 17:40:21.000000000 -0600
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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.
+ * 
+ * Note, the TPM chip is not interrupt driven (only polling)
+ * and can have very long timeouts (minutes!). Hence the unusual
+ * calls to schedule_timeout.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include "tpm.h"
+
+#define	TPM_MINOR			224	/* officially assigned */
+
+#define	TPM_BUFSIZE			2048
+
+/* PCI configuration addresses */
+#define	PCI_GEN_PMCON_1			0xA0
+#define	PCI_GEN1_DEC			0xE4
+#define	PCI_LPC_EN			0xE6
+#define	PCI_GEN2_DEC			0xEC
+
+static LIST_HEAD(tpm_chip_list);
+static spinlock_t driver_lock = SPIN_LOCK_UNLOCKED;
+static int dev_mask[32];
+
+static void user_reader_timeout(unsigned long ptr)
+{
+	struct tpm_chip *chip = (struct tpm_chip *) ptr;
+
+	down(&chip->buffer_mutex);
+	atomic_set(&chip->data_pending, 0);
+	memset(chip->data_buffer, 0, TPM_BUFSIZE);
+	up(&chip->buffer_mutex);
+
+}
+
+void tpm_time_expired(unsigned long ptr)
+{
+	int *exp = (int *) ptr;
+	*exp = 1;
+}
+
+EXPORT_SYMBOL_GPL(tpm_time_expired);
+
+/*
+ * Initialize the LPC bus and enable the TPM ports
+ */
+int tpm_lpc_bus_init(struct pci_dev *pci_dev, u16 base)
+{
+	u32 lpcenable, tmp;
+	int is_lpcm = 0;
+
+	switch (pci_dev->vendor) {
+	case PCI_VENDOR_ID_INTEL:
+		switch (pci_dev->device) {
+		case PCI_DEVICE_ID_INTEL_82801CA_12:
+		case PCI_DEVICE_ID_INTEL_82801DB_12:
+			is_lpcm = 1;
+			break;
+		}
+		/* init ICH (enable LPC) */
+		pci_read_config_dword(pci_dev, PCI_GEN1_DEC, &lpcenable);
+		lpcenable |= 0x20000000;
+		pci_write_config_dword(pci_dev, PCI_GEN1_DEC, lpcenable);
+
+		if (is_lpcm) {
+			pci_read_config_dword(pci_dev, PCI_GEN1_DEC,
+					      &lpcenable);
+			if ((lpcenable & 0x20000000) == 0) {
+				dev_err(&pci_dev->dev,
+					"cannot enable LPC\n");
+				return -ENODEV;
+			}
+		}
+
+		/* initialize TPM registers */
+		pci_read_config_dword(pci_dev, PCI_GEN2_DEC, &tmp);
+
+		if (!is_lpcm)
+			tmp = (tmp & 0xFFFF0000) | (base & 0xFFF0);
+		else
+			tmp =
+			    (tmp & 0xFFFF0000) | (base & 0xFFF0) |
+			    0x00000001;
+
+		pci_write_config_dword(pci_dev, PCI_GEN2_DEC, tmp);
+
+		if (is_lpcm) {
+			pci_read_config_dword(pci_dev, PCI_GEN_PMCON_1,
+					      &tmp);
+			tmp |= 0x00000004;	/* enable CLKRUN */
+			pci_write_config_dword(pci_dev, PCI_GEN_PMCON_1,
+					       tmp);
+		}
+		tpm_write_index(0x0D, 0x55);	/* unlock 4F */
+		tpm_write_index(0x0A, 0x00);	/* int disable */
+		tpm_write_index(0x08, base);	/* base addr lo */
+		tpm_write_index(0x09, (base & 0xFF00) >> 8);	/* base addr hi */
+		tpm_write_index(0x0D, 0xAA);	/* lock 4F */
+		break;
+	case PCI_VENDOR_ID_AMD:
+		/* nothing yet */
+		break;
+	}
+
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_lpc_bus_init);
+
+/*
+ * Internal kernel interface to transmit TPM commands
+ */
+static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
+			    size_t bufsiz)
+{
+	ssize_t len;
+	u32 count;
+	__be32 *native_size;
+
+	native_size = (__force __be32 *) (buf + 2);
+	count = be32_to_cpu(*native_size);
+
+	if (count == 0)
+		return -ENODATA;
+	if (count > bufsiz) {
+		dev_err(&chip->pci_dev->dev,
+			"invalid count value %x %x \n", count, bufsiz);
+		return -E2BIG;
+	}
+
+	if ((len = chip->vendor->send(chip, (u8 *) buf, count)) < 0) {
+		dev_err(&chip->pci_dev->dev,
+			"tpm_transmit: tpm_send: error %d\n", len);
+		return len;
+	}
+
+	down(&chip->timer_manipulation_mutex);
+	chip->time_expired = 0;
+	init_timer(&chip->device_timer);
+	chip->device_timer.function = tpm_time_expired;
+	chip->device_timer.expires = jiffies + 2 * 60 * HZ;
+	chip->device_timer.data = (unsigned long) &chip->time_expired;
+	add_timer(&chip->device_timer);
+	up(&chip->timer_manipulation_mutex);
+
+	do {
+		u8 status = inb(chip->vendor->base + 1);
+		if ((status & chip->vendor->req_complete_mask) ==
+		    chip->vendor->req_complete_val) {
+			down(&chip->timer_manipulation_mutex);
+			del_singleshot_timer_sync(&chip->device_timer);
+			up(&chip->timer_manipulation_mutex);
+			goto out_recv;
+		}
+		schedule_timeout(TPM_TIMEOUT);
+		rmb();
+	} while (!chip->time_expired);
+
+
+	chip->vendor->cancel(chip);
+	dev_err(&chip->pci_dev->dev, "Time expired\n");
+	return -EIO;
+
+out_recv:
+	len = chip->vendor->recv(chip, (u8 *) buf, bufsiz);
+	if (len < 0)
+		dev_err(&chip->pci_dev->dev,
+			"tpm_transmit: tpm_recv: error %d\n", len);
+	return len;
+}
+
+/*
+ * Device file system interface to the TPM
+ */
+int tpm_open(struct inode *inode, struct file *file)
+{
+	int rc = 0, minor = iminor(inode);
+	struct tpm_chip *chip = NULL, *pos;
+
+	spin_lock(&driver_lock);
+
+	list_for_each_entry(pos, &tpm_chip_list, list) {
+		if (pos->vendor->miscdev.minor == minor) {
+			chip = pos;
+			break;
+		}
+	}
+
+	if (chip == NULL) {
+		rc = -ENODEV;
+		goto err_out;
+	}
+
+	if (chip->num_opens) {
+		dev_dbg(&chip->pci_dev->dev,
+			"Another process owns this TPM\n");
+		rc = -EBUSY;
+		goto err_out;
+	}
+
+	chip->num_opens++;
+	pci_dev_get(chip->pci_dev);
+
+	spin_unlock(&driver_lock);
+
+	chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
+	if (chip->data_buffer == NULL) {
+		chip->num_opens--;
+		pci_dev_put(chip->pci_dev);
+		return -ENOMEM;
+	}
+
+	atomic_set(&chip->data_pending, 0);
+
+	file->private_data = chip;
+	return 0;
+
+err_out:
+	spin_unlock(&driver_lock);
+	return rc;
+}
+
+EXPORT_SYMBOL_GPL(tpm_open);
+
+int tpm_release(struct inode *inode, struct file *file)
+{
+	struct tpm_chip *chip = file->private_data;
+
+	spin_lock(&driver_lock);
+	chip->num_opens--;
+	down(&chip->timer_manipulation_mutex);
+	if (timer_pending(&chip->user_read_timer))
+		del_singleshot_timer_sync(&chip->user_read_timer);
+	else if (timer_pending(&chip->device_timer))
+		del_singleshot_timer_sync(&chip->device_timer);
+	up(&chip->timer_manipulation_mutex);
+	kfree(chip->data_buffer);
+	atomic_set(&chip->data_pending, 0);
+
+	pci_dev_put(chip->pci_dev);
+	file->private_data = NULL;
+	spin_unlock(&driver_lock);
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_release);
+
+ssize_t tpm_write(struct file * file, const char __user * buf,
+		  size_t size, loff_t * off)
+{
+	struct tpm_chip *chip = file->private_data;
+	int in_size = size, out_size;
+
+	/* cannot perform a write until the read has cleared
+	   either via tpm_read or a user_read_timer timeout */
+	while (atomic_read(&chip->data_pending) != 0) {
+		schedule_timeout(TPM_TIMEOUT);
+	}
+
+	down(&chip->buffer_mutex);
+
+	if (in_size > TPM_BUFSIZE)
+		in_size = TPM_BUFSIZE;
+
+	if (copy_from_user
+	    (chip->data_buffer, (void __user *) buf, in_size)) {
+		up(&chip->buffer_mutex);
+		return -EFAULT;
+	}
+
+	/* atomic tpm command send and result receive */
+	out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE);
+
+	atomic_set(&chip->data_pending, out_size);
+	up(&chip->buffer_mutex);
+
+	/* Set a timeout by which the reader must come claim the result */
+	down(&chip->timer_manipulation_mutex);
+	init_timer(&chip->user_read_timer);
+	chip->user_read_timer.function = user_reader_timeout;
+	chip->user_read_timer.data = (unsigned long) chip;
+	chip->user_read_timer.expires = jiffies + (60 * HZ);
+	add_timer(&chip->user_read_timer);
+	up(&chip->timer_manipulation_mutex);
+
+	return in_size;
+}
+
+EXPORT_SYMBOL_GPL(tpm_write);
+
+ssize_t tpm_read(struct file * file, char __user * buf,
+		 size_t size, loff_t * off)
+{
+	struct tpm_chip *chip = file->private_data;
+	int ret_size = -ENODATA;
+
+	if (atomic_read(&chip->data_pending) != 0) {	/* Result available */
+		down(&chip->timer_manipulation_mutex);
+		del_singleshot_timer_sync(&chip->user_read_timer);
+		up(&chip->timer_manipulation_mutex);
+
+		down(&chip->buffer_mutex);
+
+		ret_size = atomic_read(&chip->data_pending);
+		atomic_set(&chip->data_pending, 0);
+
+		if (ret_size == 0)	/* timeout just occurred */
+			ret_size = -ETIME;
+		else if (ret_size > 0) {	/* relay data */
+			if (size < ret_size)
+				ret_size = size;
+
+			if (copy_to_user((void __user *) buf,
+					 chip->data_buffer, ret_size)) {
+				ret_size = -EFAULT;
+			}
+		}
+		up(&chip->buffer_mutex);
+	}
+
+	return ret_size;
+}
+
+EXPORT_SYMBOL_GPL(tpm_read);
+
+void __devexit tpm_remove(struct pci_dev *pci_dev)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+
+	if (chip == NULL) {
+		dev_err(&pci_dev->dev, "No device data found\n");
+		return;
+	}
+
+	spin_lock(&driver_lock);
+
+	list_del(&chip->list);
+
+	pci_set_drvdata(pci_dev, NULL);
+	misc_deregister(&chip->vendor->miscdev);
+	spin_unlock(&driver_lock);
+
+	pci_disable_device(pci_dev);
+
+	dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
+
+	kfree(chip);
+
+	pci_dev_put(pci_dev);
+}
+
+EXPORT_SYMBOL_GPL(tpm_remove);
+
+static u8 savestate[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 10,		/* blob length (in bytes) */
+	0, 0, 0, 152		/* TPM_ORD_SaveState */
+};
+
+/*
+ * We are about to suspend. Save the TPM state
+ * so that it can be restored.
+ */
+int tpm_pm_suspend(struct pci_dev *pci_dev, u32 pm_state)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+	if (chip == NULL)
+		return -ENODEV;
+
+	tpm_transmit(chip, savestate, sizeof(savestate));
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_pm_suspend);
+
+/*
+ * Resume from a power safe. The BIOS already restored
+ * the TPM state.
+ */
+int tpm_pm_resume(struct pci_dev *pci_dev)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+	if (chip == NULL)
+		return -ENODEV;
+
+	spin_lock(&driver_lock);
+	tpm_lpc_bus_init(pci_dev, chip->vendor->base);
+	spin_unlock(&driver_lock);
+
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_pm_resume);
+
+/*
+ * Called from tpm_<specific>.c probe function only for devices 
+ * the driver has determined it should claim.  Prior to calling
+ * this function the specific probe function has called pci_enable_device
+ * upon errant exit from this function specific probe function should call
+ * pci_disable_device
+ */
+int tpm_register_hardware(struct pci_dev *pci_dev,
+			  struct tpm_vendor_specific *entry)
+{
+	char devname[7];
+	struct tpm_chip *chip;
+	int i, j;
+
+	/* Driver specific per-device data */
+	chip = kmalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	memset(chip, 0, sizeof(struct tpm_chip));
+
+	init_MUTEX(&chip->buffer_mutex);
+	init_MUTEX(&chip->timer_manipulation_mutex);
+	INIT_LIST_HEAD(&chip->list);
+
+	chip->vendor = entry;
+
+	chip->dev_num = -1;
+
+	for (i = 0; i < 32; i++)
+		for (j = 0; j < 8; j++)
+			if ((dev_mask[i] & (1 << j)) == 0) {
+				chip->dev_num = i * 32 + j;
+				dev_mask[i] |= 1 << j;
+				goto dev_num_search_complete;
+			}
+
+dev_num_search_complete:
+	if (chip->dev_num < 0) {
+		dev_err(&pci_dev->dev,
+			"No available tpm device numbers\n");
+		kfree(chip);
+		return -ENODEV;
+	} else if (chip->dev_num == 0)
+		chip->vendor->miscdev.minor = TPM_MINOR;
+	else
+		chip->vendor->miscdev.minor = MISC_DYNAMIC_MINOR;
+
+	snprintf(devname, sizeof(devname), "%s%d", "tpm", chip->dev_num);
+	chip->vendor->miscdev.name = devname;
+
+	chip->vendor->miscdev.dev = &(pci_dev->dev);
+	chip->pci_dev = pci_dev_get(pci_dev);
+
+	spin_lock(&driver_lock);
+
+	if (misc_register(&chip->vendor->miscdev)) {
+		dev_err(&chip->pci_dev->dev,
+			"unable to misc_register %s, minor %d\n",
+			chip->vendor->miscdev.name,
+			chip->vendor->miscdev.minor);
+		pci_dev_put(pci_dev);
+		spin_unlock(&driver_lock);
+		kfree(chip);
+		dev_mask[i] &= !(1 << j);
+		return -ENODEV;
+	}
+
+	pci_set_drvdata(pci_dev, chip);
+
+	list_add(&chip->list, &tpm_chip_list);
+	spin_unlock(&driver_lock);
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_register_hardware);
+
+static int __init init_tpm(void)
+{
+	return 0;
+}
+
+static void __exit cleanup_tpm(void)
+{
+
+}
+
+module_init(init_tpm);
+module_exit(cleanup_tpm);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.9/drivers/char/tpm/tpm.h linux-2.6.9-tpm/drivers/char/tpm/tpm.h
--- linux-2.6.9/drivers/char/tpm/tpm.h	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/tpm.h	2004-12-20 10:26:46.000000000 -0600
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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/module.h>
+#include <linux/version.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/miscdevice.h>
+
+#define TPM_TIMEOUT msecs_to_jiffies(5)
+
+/* TPM addresses */
+#define	TPM_ADDR			0x4E
+#define	TPM_DATA			0x4F
+
+struct tpm_chip;
+
+struct tpm_vendor_specific {
+	u8 req_complete_mask;
+	u8 req_complete_val;
+	u16 base;		/* TPM base address */
+
+	int (*recv) (struct tpm_chip *, u8 *, size_t);
+	int (*send) (struct tpm_chip *, u8 *, size_t);
+	void (*cancel) (struct tpm_chip *);
+	struct miscdevice miscdev;
+};
+
+struct tpm_chip {
+	struct pci_dev *pci_dev;	/* PCI device stuff */
+
+	int dev_num;		/* /dev/tpm# */
+	int num_opens;		/* only one allowed */
+	int time_expired;
+
+	/* Data passed to and from the tpm via the read/write calls */
+	u8 *data_buffer;
+	atomic_t data_pending;
+	struct semaphore buffer_mutex;
+
+	struct timer_list user_read_timer;	/* user needs to claim result */
+	struct timer_list device_timer;	/* tpm is processing */
+	struct semaphore timer_manipulation_mutex;
+
+	struct tpm_vendor_specific *vendor;
+
+	struct list_head list;
+};
+
+static inline int tpm_read_index(int index)
+{
+	outb(index, TPM_ADDR);
+	return inb(TPM_DATA) & 0xFF;
+}
+
+static inline void tpm_write_index(int index, int value)
+{
+	outb(index, TPM_ADDR);
+	outb(value & 0xFF, TPM_DATA);
+}
+
+extern void tpm_time_expired(unsigned long);
+extern int tpm_lpc_bus_init(struct pci_dev *, u16);
+
+extern int tpm_register_hardware(struct pci_dev *,
+				 struct tpm_vendor_specific *);
+extern int tpm_open(struct inode *, struct file *);
+extern int tpm_release(struct inode *, struct file *);
+extern ssize_t tpm_write(struct file *, const char __user *, size_t,
+			 loff_t *);
+extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *);
+extern void __devexit tpm_remove(struct pci_dev *);
+extern int tpm_pm_suspend(struct pci_dev *, u32);
+extern int tpm_pm_resume(struct pci_dev *);
diff -uprN linux-2.6.9/drivers/char/tpm/tpm_nsc.c linux-2.6.9-tpm/drivers/char/tpm/tpm_nsc.c
--- linux-2.6.9/drivers/char/tpm/tpm_nsc.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/tpm_nsc.c	2004-12-20 10:35:50.000000000 -0600
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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"
+
+/* National definitions */
+#define	TPM_NSC_BASE			0x360
+#define	TPM_NSC_IRQ			0x07
+
+#define	NSC_LDN_INDEX			0x07
+#define	NSC_SID_INDEX			0x20
+#define	NSC_LDC_INDEX			0x30
+#define	NSC_DIO_INDEX			0x60
+#define	NSC_CIO_INDEX			0x62
+#define	NSC_IRQ_INDEX			0x70
+#define	NSC_ITS_INDEX			0x71
+
+#define	NSC_STATUS			0x01
+#define	NSC_COMMAND			0x01
+#define	NSC_DATA			0x00
+
+/* status bits */
+#define	NSC_STATUS_OBF			0x01	/* output buffer full */
+#define	NSC_STATUS_IBF			0x02	/* input buffer full */
+#define	NSC_STATUS_F0			0x04	/* F0 */
+#define	NSC_STATUS_A2			0x08	/* A2 */
+#define	NSC_STATUS_RDY			0x10	/* ready to receive command */
+#define	NSC_STATUS_IBR			0x20	/* ready to receive data */
+
+/* command bits */
+#define	NSC_COMMAND_NORMAL		0x01	/* normal mode */
+#define	NSC_COMMAND_EOC			0x03
+#define	NSC_COMMAND_CANCEL		0x22
+
+/*
+ * Wait for a certain status to appear
+ */
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
+{
+	int expired = 0;
+	struct timer_list status_timer =
+	    TIMER_INITIALIZER(tpm_time_expired, jiffies + 10 * HZ,
+			      (unsigned long) &expired);
+
+	/* status immediately available check */
+	*data = inb(chip->vendor->base + NSC_STATUS);
+	if ((*data & mask) == val)
+		return 0;
+
+	/* wait for status */
+	add_timer(&status_timer);
+	do {
+		schedule_timeout(TPM_TIMEOUT);
+		*data = inb(chip->vendor->base + 1);
+		if ((*data & mask) == val) {
+			del_singleshot_timer_sync(&status_timer);
+			return 0;
+		}
+	}
+	while (!expired);
+
+	return -EBUSY;
+}
+
+static int nsc_wait_for_ready(struct tpm_chip *chip)
+{
+	int status;
+	int expired = 0;
+	struct timer_list status_timer =
+	    TIMER_INITIALIZER(tpm_time_expired, jiffies + 100,
+			      (unsigned long) &expired);
+
+	/* status immediately available check */
+	status = inb(chip->vendor->base + NSC_STATUS);
+	if (status & NSC_STATUS_OBF)
+		status = inb(chip->vendor->base + NSC_DATA);
+	if (status & NSC_STATUS_RDY)
+		return 0;
+
+	/* wait for status */
+	add_timer(&status_timer);
+	do {
+		schedule_timeout(TPM_TIMEOUT);
+		status = inb(chip->vendor->base + NSC_STATUS);
+		if (status & NSC_STATUS_OBF)
+			status = inb(chip->vendor->base + NSC_DATA);
+		if (status & NSC_STATUS_RDY) {
+			del_singleshot_timer_sync(&status_timer);
+			return 0;
+		}
+	}
+	while (!expired);
+
+	dev_info(&chip->pci_dev->dev, "wait for ready failed\n");
+	return -EBUSY;
+}
+
+
+static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 *buffer = buf;
+	u8 data, *p;
+	u32 size;
+	__be32 *native_size;
+
+	if (count < 6)
+		return -EIO;
+
+	if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "F0 timeout\n");
+		return -EIO;
+	}
+	if ((data =
+	     inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
+		dev_err(&chip->pci_dev->dev, "not in normal mode (0x%x)\n",
+			data);
+		return -EIO;
+	}
+
+	/* read the whole packet */
+	for (p = buffer; p < &buffer[count]; p++) {
+		if (wait_for_stat
+		    (chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) {
+			dev_err(&chip->pci_dev->dev,
+				"OBF timeout (while reading data)\n");
+			return -EIO;
+		}
+		if (data & NSC_STATUS_F0)
+			break;
+		*p = inb(chip->vendor->base + NSC_DATA);
+	}
+
+	if ((data & NSC_STATUS_F0) == 0) {
+		dev_err(&chip->pci_dev->dev, "F0 not set\n");
+		return -EIO;
+	}
+	if ((data = inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_EOC) {
+		dev_err(&chip->pci_dev->dev,
+			"expected end of command(0x%x)\n", data);
+		return -EIO;
+	}
+
+	native_size = (__force __be32 *) (buf + 2);
+	size = be32_to_cpu(*native_size);
+
+	if (count < size)
+		return -EIO;
+
+	return size;
+}
+
+static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 data;
+	int i;
+
+	/*
+	 * If we hit the chip with back to back commands it locks up
+	 * and never set IBF. Hitting it with this "hammer" seems to
+	 * fix it. Not sure why this is needed, we followed the flow
+	 * chart in the manual to the letter.
+	 */
+	outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND);
+
+	if (nsc_wait_for_ready(chip) != 0)
+		return -EIO;
+
+	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+		return -EIO;
+	}
+
+	outb(NSC_COMMAND_NORMAL, chip->vendor->base + NSC_COMMAND);
+	if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBR timeout\n");
+		return -EIO;
+	}
+
+	for (i = 0; i < count; i++) {
+		if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+			dev_err(&chip->pci_dev->dev,
+				"IBF timeout (while writing data)\n");
+			return -EIO;
+		}
+		outb(buf[i], chip->vendor->base + NSC_DATA);
+	}
+
+	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+		return -EIO;
+	}
+	outb(NSC_COMMAND_EOC, chip->vendor->base + NSC_COMMAND);
+
+	return count;
+}
+
+static void tpm_nsc_cancel(struct tpm_chip *chip)
+{
+	outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND);
+}
+
+static struct file_operations nsc_ops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.open = tpm_open,
+	.read = tpm_read,
+	.write = tpm_write,
+	.release = tpm_release,
+};
+
+static struct tpm_vendor_specific tpm_nsc = {
+	.recv = tpm_nsc_recv,
+	.send = tpm_nsc_send,
+	.cancel = tpm_nsc_cancel,
+	.req_complete_mask = NSC_STATUS_OBF,
+	.req_complete_val = NSC_STATUS_OBF,
+	.base = TPM_NSC_BASE,
+	.miscdev.fops = &nsc_ops,
+};
+
+static int __devinit tpm_nsc_init(struct pci_dev *pci_dev,
+				  const struct pci_device_id *pci_id)
+{
+	int rc = 0;
+
+	if (pci_enable_device(pci_dev))
+		return -EIO;
+
+	if (tpm_lpc_bus_init(pci_dev, TPM_NSC_BASE)) {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	/* verify that it is a National part (SID) */
+	if (tpm_read_index(NSC_SID_INDEX) != 0xEF) {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	dev_dbg(&pci_dev->dev, "NSC TPM detected\n");
+	dev_dbg(&pci_dev->dev,
+		"NSC LDN 0x%x, SID 0x%x, SRID 0x%x\n",
+		tpm_read_index(0x07), tpm_read_index(0x20),
+		tpm_read_index(0x27));
+	dev_dbg(&pci_dev->dev,
+		"NSC SIOCF1 0x%x SIOCF5 0x%x SIOCF6 0x%x SIOCF8 0x%x\n",
+		tpm_read_index(0x21), tpm_read_index(0x25),
+		tpm_read_index(0x26), tpm_read_index(0x28));
+	dev_dbg(&pci_dev->dev, "NSC IO Base0 0x%x\n",
+		(tpm_read_index(0x60) << 8) | tpm_read_index(0x61));
+	dev_dbg(&pci_dev->dev, "NSC IO Base1 0x%x\n",
+		(tpm_read_index(0x62) << 8) | tpm_read_index(0x63));
+	dev_dbg(&pci_dev->dev, "NSC Interrupt number and wakeup 0x%x\n",
+		tpm_read_index(0x70));
+	dev_dbg(&pci_dev->dev, "NSC IRQ type select 0x%x\n",
+		tpm_read_index(0x71));
+	dev_dbg(&pci_dev->dev,
+		"NSC DMA channel select0 0x%x, select1 0x%x\n",
+		tpm_read_index(0x74), tpm_read_index(0x75));
+	dev_dbg(&pci_dev->dev,
+		"NSC Config "
+		"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+		tpm_read_index(0xF0), tpm_read_index(0xF1),
+		tpm_read_index(0xF2), tpm_read_index(0xF3),
+		tpm_read_index(0xF4), tpm_read_index(0xF5),
+		tpm_read_index(0xF6), tpm_read_index(0xF7),
+		tpm_read_index(0xF8), tpm_read_index(0xF9));
+
+	dev_info(&pci_dev->dev,
+		 "NSC PC21100 TPM revision %d\n",
+		 tpm_read_index(0x27) & 0x1F);
+
+	if (tpm_read_index(NSC_LDC_INDEX) == 0)
+		dev_info(&pci_dev->dev, ": NSC TPM not active\n");
+
+	/* select PM channel 1 */
+	tpm_write_index(NSC_LDN_INDEX, 0x12);
+	tpm_read_index(NSC_LDN_INDEX);
+
+	/* disable the DPM module */
+	tpm_write_index(NSC_LDC_INDEX, 0);
+	tpm_read_index(NSC_LDC_INDEX);
+
+	/* set the data register base addresses */
+	tpm_write_index(NSC_DIO_INDEX, TPM_NSC_BASE >> 8);
+	tpm_write_index(NSC_DIO_INDEX + 1, TPM_NSC_BASE);
+	tpm_read_index(NSC_DIO_INDEX);
+	tpm_read_index(NSC_DIO_INDEX + 1);
+
+	/* set the command register base addresses */
+	tpm_write_index(NSC_CIO_INDEX, (TPM_NSC_BASE + 1) >> 8);
+	tpm_write_index(NSC_CIO_INDEX + 1, (TPM_NSC_BASE + 1));
+	tpm_read_index(NSC_DIO_INDEX);
+	tpm_read_index(NSC_DIO_INDEX + 1);
+
+	/* set the interrupt number to be used for the host interface */
+	tpm_write_index(NSC_IRQ_INDEX, TPM_NSC_IRQ);
+	tpm_write_index(NSC_ITS_INDEX, 0x00);
+	tpm_read_index(NSC_IRQ_INDEX);
+
+	/* enable the DPM module */
+	tpm_write_index(NSC_LDC_INDEX, 0x01);
+	tpm_read_index(NSC_LDC_INDEX);
+
+	if ((rc = tpm_register_hardware(pci_dev, &tpm_nsc)) < 0)
+		goto out_err;
+
+	return 0;
+
+out_err:
+	pci_disable_device(pci_dev);
+	return rc;
+}
+
+static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
+
+static struct pci_driver nsc_pci_driver = {
+	.name = "tpm_nsc",
+	.id_table = tpm_pci_tbl,
+	.probe = tpm_nsc_init,
+	.remove = __devexit_p(tpm_remove),
+	.suspend = tpm_pm_suspend,
+	.resume = tpm_pm_resume,
+};
+
+static int __init init_nsc(void)
+{
+	return pci_register_driver(&nsc_pci_driver);
+}
+
+static void __exit cleanup_nsc(void)
+{
+	pci_unregister_driver(&nsc_pci_driver);
+}
+
+module_init(init_nsc);
+module_exit(cleanup_nsc);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.9/include/linux/pci_ids.h linux-2.6.9-tpm/include/linux/pci_ids.h
--- linux-2.6.9/include/linux/pci_ids.h	2004-12-06 16:53:35.000000000 -0600
+++ linux-2.6.9-tpm/include/linux/pci_ids.h	2004-12-06 14:27:05.000000000 -0600
@@ -494,6 +494,7 @@
 #define PCI_DEVICE_ID_AMD_OPUS_7449	0x7449
 #	define PCI_DEVICE_ID_AMD_VIPER_7449	PCI_DEVICE_ID_AMD_OPUS_7449
 #define PCI_DEVICE_ID_AMD_8111_LAN	0x7462
+#define PCI_DEVICE_ID_AMD_8111_LPC	0x7468
 #define PCI_DEVICE_ID_AMD_8111_IDE	0x7469
 #define PCI_DEVICE_ID_AMD_8111_AUDIO	0x746d
 #define PCI_DEVICE_ID_AMD_8151_0	0x7454
diff -uprN linux-2.6.9/MAINTAINERS linux-2.6.9-tpm/MAINTAINERS
--- linux-2.6.9/MAINTAINERS	2004-10-18 16:54:37.000000000 -0500
+++ linux-2.6.9-tpm/MAINTAINERS	2004-12-07 12:39:10.000000000 -0600
@@ -2144,6 +2144,12 @@ L:	tlinux-users@tce.toshiba-dme.co.jp
 W:	http://www.buzzard.org.uk/toshiba/
 S:	Maintained
 
+TPM DRIVER
+P:	Kylene Hall
+M:	tpmdd-devel@lists.sourceforge.net
+L:	tpmdd-devel@lists.sourceforge.net
+S:	Maintained
+
 TRIDENT 4DWAVE/SIS 7018 PCI AUDIO CORE
 P:	Muli Ben-Yehuda
 M:	mulix@mulix.org

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement --updated version
  2004-12-20 17:50     ` Kylene Hall
@ 2004-12-21 16:51       ` Nish Aravamudan
  2004-12-21 18:19         ` Kylene Hall
  0 siblings, 1 reply; 43+ messages in thread
From: Nish Aravamudan @ 2004-12-21 16:51 UTC (permalink / raw)
  To: Kylene Hall
  Cc: linux-kernel, greg, sailer, leendert, emilyr, toml, tpmdd-devel

On Mon, 20 Dec 2004 11:50:00 -0600 (CST), Kylene Hall <kjhall@us.ibm.com> wrote:
> This patch is a device driver to enable new hardware.  The new hardware is
> the TPM chip as described by specifications at 
> <http://www.trustedcomputinggroup.org>.  The TPM chip will enable you to
> use hardware to securely store and protect your keys and personal data.
> To use the chip according to the specification, you will need the Trusted
> Software Stack (TSS) of which an implementation for Linux is available at:
> <http://sourceforge.net/projects/trousers>.
> 
> > > Updates include: splitting out the vendor specific code into separated
> > > drivers from the base TPM functionality, fixed busy waiting loops,
> > > concerns about timer handling and rmmod, buffer name.
> 
> > Updates include a consolodated buffer mutex, numerous naming improvements,
> > better placement of pci_driver and file_operations structure
> > initializations, change to use of EXPORT_SYMBOL_GPL, improvement of
> > Kconfig help texts and creation of a tpm directory
> 
> Updates include moving the pci_ids table into the individual vendor
> specific .c files and including a tpm description file in the
> Documentation directory.
> 
> Thanks,
> Kylene
> 
> Signed-off-by: Leendert van Doorn <leendert@watson.ibm.com>
> Signed-off-by: Reiner Sailer <sailer@watson.ibm.com>
> Signed-off-by: Dave Safford <safford@watson.ibm.com>
> Signed-off-by: Kylene Hall <kjhall@us.ibm.com>

<snip>

> diff -uprN linux-2.6.9/drivers/char/tpm/tpm.c linux-2.6.9-tpm/drivers/char/tpm/tpm.c
> --- linux-2.6.9/drivers/char/tpm/tpm.c  1969-12-31 18:00:00.000000000 -0600
> +++ linux-2.6.9-tpm/drivers/char/tpm/tpm.c      2004-12-17 17:40:21.000000000 -0600
> @@ -0,0 +1,508 @@
> +/*
> + * Copyright (C) 2004 IBM Corporation
> + *
> + * Authors:
> + * Leendert van Doorn <leendert@watson.ibm.com>
> + * Reiner Sailer <sailer@watson.ibm.com>
> + * Dave Safford <safford@watson.ibm.com>
> + * Kylene Hall <kjhall@us.ibm.com>
> + *
> + * Maintained by: <tpmdd_devel@lists.sourceforge.net>
> + *
> + * Device driver for TCG/TCPA TPM (trusted platform module).
> + * Specifications at www.trustedcomputinggroup.org
> + *
> + * 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.
> + *
> + * Note, the TPM chip is not interrupt driven (only polling)
> + * and can have very long timeouts (minutes!). Hence the unusual
> + * calls to schedule_timeout.

<snip>

> +       do {
> +               u8 status = inb(chip->vendor->base + 1);
> +               if ((status & chip->vendor->req_complete_mask) ==
> +                   chip->vendor->req_complete_val) {
> +                       down(&chip->timer_manipulation_mutex);
> +                       del_singleshot_timer_sync(&chip->device_timer);
> +                       up(&chip->timer_manipulation_mutex);
> +                       goto out_recv;
> +               }
> +               schedule_timeout(TPM_TIMEOUT);
> +               rmb();
> +       } while (!chip->time_expired);

All of these calls to schedule_timeout() are broken. You must set the
state appropriately before calling schedule_timeout() or it will
return immediately. For your reference here is the difference between
the two states with respects to calling schedule_timeout() directly:

TASK_INTERRUPTIBLE: sleep, waking up early on both signals and waitqueue events
TASK_UNINTERRUPTIBLE: sleep, waking up early on watiqueue events

Keep in mind that if TASK_INTERRUPTIBLE is used, you *must* be
prepared to handle signals (check return value, indicating an early
return, and signals_pending(current), indicating whether the cause of
early return is either a signal or waitqueue event).

Also, as a final consideration, because of the way the loadavg is
calculated, a TASK_UNINTERRUPTIBLE sleep will factor in as a 1 to the
load.

-Nish

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement --updated version
  2004-12-21 16:51       ` Nish Aravamudan
@ 2004-12-21 18:19         ` Kylene Hall
  2005-01-12 18:45           ` Kylene Hall
  0 siblings, 1 reply; 43+ messages in thread
From: Kylene Hall @ 2004-12-21 18:19 UTC (permalink / raw)
  To: Nish Aravamudan
  Cc: linux-kernel, greg, sailer, leendert, emilyr, toml, tpmdd-devel

This patch is a device driver to enable new hardware.  The new hardware is
the TPM chip as described by specifications at 
<http://www.trustedcomputinggroup.org>.  The TPM chip will enable you to
use hardware to securely store and protect your keys and personal data.
To use the chip according to the specification, you will need the Trusted
Software Stack (TSS) of which an implementation for Linux is available at:
<http://sourceforge.net/projects/trousers>.
 
> > > > Updates include: splitting out the vendor specific code into separated
> > > > drivers from the base TPM functionality, fixed busy waiting loops,
> > > > concerns about timer handling and rmmod, buffer name.
> > 
> > > Updates include a consolodated buffer mutex, numerous naming improvements,
> > > better placement of pci_driver and file_operations structure
> > > initializations, change to use of EXPORT_SYMBOL_GPL, improvement of
> > > Kconfig help texts and creation of a tpm directory
> > 
> > Updates include moving the pci_ids table into the individual vendor
> > specific .c files and including a tpm description file in the
> > Documentation directory.

> All of these calls to schedule_timeout() are broken. You must set the
> state appropriately before calling schedule_timeout() or it will
> return immediately. For your reference here is the difference between
> the two states with respects to calling schedule_timeout() directly:
> 
> TASK_INTERRUPTIBLE: sleep, waking up early on both signals and waitqueue events
> TASK_UNINTERRUPTIBLE: sleep, waking up early on watiqueue events
> 
> Keep in mind that if TASK_INTERRUPTIBLE is used, you *must* be
> prepared to handle signals (check return value, indicating an early
> return, and signals_pending(current), indicating whether the cause of
> early return is either a signal or waitqueue event).
> 
> Also, as a final consideration, because of the way the loadavg is
> calculated, a TASK_UNINTERRUPTIBLE sleep will factor in as a 1 to the
> load.
> 
> -Nish

Fixed in the patch below.

 
Thanks,
Kylene

Signed-off-by: Leendert van Doorn <leendert@watson.ibm.com>
Signed-off-by: Reiner Sailer <sailer@watson.ibm.com>
Signed-off-by: Dave Safford <safford@watson.ibm.com>
Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---
diff -uprN linux-2.6.9/Documentation/tpm.txt linux-2.6.9-tpm/Documentation/tpm.txt
--- linux-2.6.9/Documentation/tpm.txt	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/Documentation/tpm.txt	2004-12-20 12:35:27.000000000 -0600
@@ -0,0 +1,12 @@
+			TPM Support
+
+	The Trusted Platform Module (TPM) is a hardware device described by 
+specifications at <http://www.trustedcomputinggroup.org>.  Some of the 
+capabilities of the device include a random number generator as well as 
+protection of keys, digital certificates, passwords and data securely by the 
+hardware.  Besides the specification for this piece of hardware the Trusted 
+Computing Group (TCG) provides a specification for an library interface to the 
+hardware that is to be used by applications.  This interface is known as the 
+Trusted Software Stack (TSS).  The TSS specification is also available at the 
+TCG website and an implementation of the interface is available for Linux 
+at <http://sourceforge.net/projects/trousers>.
diff -uprN linux-2.6.9/drivers/char/Kconfig linux-2.6.9-tpm/drivers/char/Kconfig
--- linux-2.6.9/drivers/char/Kconfig	2004-10-18 16:53:07.000000000 -0500
+++ linux-2.6.9-tpm/drivers/char/Kconfig	2004-12-17 17:41:00.000000000 -0600
@@ -989,5 +989,7 @@ config MMTIMER
 	  The mmtimer device allows direct userspace access to the
 	  Altix system timer.
 
+source "drivers/char/tpm/Kconfig"
+
 endmenu
 
diff -uprN linux-2.6.9/drivers/char/Makefile linux-2.6.9-tpm/drivers/char/Makefile
--- linux-2.6.9/drivers/char/Makefile	2004-10-18 16:55:28.000000000 -0500
+++ linux-2.6.9-tpm/drivers/char/Makefile	2004-12-17 17:41:06.000000000 -0600
@@ -88,7 +88,7 @@ obj-$(CONFIG_PCMCIA) += pcmcia/
 obj-$(CONFIG_IPMI_HANDLER) += ipmi/
 
 obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
-
+obj-$(CONFIG_TCG_TPM) += tpm/
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c qtronixmap.c
 
diff -uprN linux-2.6.9/drivers/char/tpm/Kconfig linux-2.6.9-tpm/drivers/char/tpm/Kconfig
--- linux-2.6.9/drivers/char/tpm/Kconfig	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/Kconfig	2004-12-17 17:40:51.000000000 -0600
@@ -0,0 +1,39 @@
+#
+# TPM device configuration
+#
+
+menu "TPM devices"
+
+config TCG_TPM
+	tristate "TPM Hardware Support"
+	depends on EXPERIMENTAL
+	---help---
+	  If you have a TPM security chip in your system, which
+	  implements the Trusted Computing Group's specification,
+	  say Yes and it will be accessible from within Linux.  For
+	  more information see <http://www.trustedcomputinggroup.org>. 
+	  An implementation of the Trusted Software Stack (TSS), the 
+	  userspace enablement piece of the specification, can be 
+	  obtained at: <http://sourceforge.net/projects/trousers>.  To 
+	  compile this driver as a module, choose M here; the module 
+	  will be called tpm. If unsure, say N.
+
+config TCG_NSC
+	tristate "National Semiconductor TPM Interface"
+	depends on TCG_TPM
+	---help---
+	  If you have a TPM security chip from National Semicondutor 
+	  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_nsc.
+
+config TCG_ATMEL
+	tristate "Atmel TPM Interface"
+	depends on TCG_TPM
+	---help---
+	  If you have a TPM security chip from Atmel 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_atmel.
+
+endmenu
+
diff -uprN linux-2.6.9/drivers/char/tpm/Makefile linux-2.6.9-tpm/drivers/char/tpm/Makefile
--- linux-2.6.9/drivers/char/tpm/Makefile	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/Makefile	2004-12-17 17:40:46.000000000 -0600
@@ -0,0 +1,7 @@
+#
+# Makefile for the kernel tpm device drivers.
+#
+obj-$(CONFIG_TCG_TPM) += tpm.o
+obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
+obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
+
diff -uprN linux-2.6.9/drivers/char/tpm/tpm_atmel.c linux-2.6.9-tpm/drivers/char/tpm/tpm_atmel.c
--- linux-2.6.9/drivers/char/tpm/tpm_atmel.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/tpm_atmel.c	2004-12-21 12:32:34.000000000 -0600
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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"
+
+/* Atmel definitions */
+#define	TPM_ATML_BASE			0x400
+
+/* write status bits */
+#define	ATML_STATUS_ABORT		0x01
+#define	ATML_STATUS_LASTBYTE		0x04
+
+/* read status bits */
+#define	ATML_STATUS_BUSY		0x01
+#define	ATML_STATUS_DATA_AVAIL		0x02
+#define	ATML_STATUS_REWRITE		0x04
+
+
+static int tpm_atml_recv(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 status, *hdr = buf;
+	u32 size;
+	int i;
+	__be32 *native_size;
+
+	/* start reading header */
+	if (count < 6)
+		return -EIO;
+
+	for (i = 0; i < 6; i++) {
+		status = inb(chip->vendor->base + 1);
+		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+			dev_err(&chip->pci_dev->dev,
+				"error reading header\n");
+			return -EIO;
+		}
+		*buf++ = inb(chip->vendor->base);
+	}
+
+	/* size of the data received */
+	native_size = (__force __be32 *) (hdr + 2);
+	size = be32_to_cpu(*native_size);
+
+	if (count < size) {
+		dev_err(&chip->pci_dev->dev,
+			"Recv size(%d) less than available space\n", size);
+		for (; i < size; i++) {	/* clear the waiting data anyway */
+			status = inb(chip->vendor->base + 1);
+			if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+				dev_err(&chip->pci_dev->dev,
+					"error reading data\n");
+				return -EIO;
+			}
+		}
+		return -EIO;
+	}
+
+	/* read all the data available */
+	for (; i < size; i++) {
+		status = inb(chip->vendor->base + 1);
+		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+			dev_err(&chip->pci_dev->dev,
+				"error reading data\n");
+			return -EIO;
+		}
+		*buf++ = inb(chip->vendor->base);
+	}
+
+	/* make sure data available is gone */
+	status = inb(chip->vendor->base + 1);
+	if (status & ATML_STATUS_DATA_AVAIL) {
+		dev_err(&chip->pci_dev->dev, "data available is stuck\n");
+		return -EIO;
+	}
+
+	return size;
+}
+
+static int tpm_atml_send(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	int i;
+
+	dev_dbg(&chip->pci_dev->dev, "tpm_atml_send: ");
+	for (i = 0; i < count; i++) {
+		dev_dbg(&chip->pci_dev->dev, "0x%x(%d) ", buf[i], buf[i]);
+		outb(buf[i], chip->vendor->base);
+	}
+
+	return count;
+}
+
+static void tpm_atml_cancel(struct tpm_chip *chip)
+{
+	outb(ATML_STATUS_ABORT, chip->vendor->base + 1);
+}
+
+static struct file_operations atmel_ops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.open = tpm_open,
+	.read = tpm_read,
+	.write = tpm_write,
+	.release = tpm_release,
+};
+
+static struct tpm_vendor_specific tpm_atmel = {
+	.recv = tpm_atml_recv,
+	.send = tpm_atml_send,
+	.cancel = tpm_atml_cancel,
+	.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
+	.req_complete_val = ATML_STATUS_DATA_AVAIL,
+	.base = TPM_ATML_BASE,
+	.miscdev.fops = &atmel_ops,
+};
+
+static int __devinit tpm_atml_init(struct pci_dev *pci_dev,
+				   const struct pci_device_id *pci_id)
+{
+	u8 version[4];
+	int rc = 0;
+
+	if (pci_enable_device(pci_dev))
+		return -EIO;
+
+	if (tpm_lpc_bus_init(pci_dev, TPM_ATML_BASE)) {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	/* verify that it is an Atmel part */
+	if (tpm_read_index(4) != 'A' || tpm_read_index(5) != 'T'
+	    || tpm_read_index(6) != 'M' || tpm_read_index(7) != 'L') {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	/* query chip for its version number */
+	if ((version[0] = tpm_read_index(0x00)) != 0xFF) {
+		version[1] = tpm_read_index(0x01);
+		version[2] = tpm_read_index(0x02);
+		version[3] = tpm_read_index(0x03);
+	} else {
+		dev_info(&pci_dev->dev, "version query failed\n");
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	if ((rc = tpm_register_hardware(pci_dev, &tpm_atmel)) < 0)
+		goto out_err;
+
+	dev_info(&pci_dev->dev,
+		 "Atmel TPM version %d.%d.%d.%d\n", version[0], version[1],
+		 version[2], version[3]);
+
+	return 0;
+out_err:
+	pci_disable_device(pci_dev);
+	return rc;
+}
+
+static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
+
+static struct pci_driver atmel_pci_driver = {
+	.name = "tpm_atmel",
+	.id_table = tpm_pci_tbl,
+	.probe = tpm_atml_init,
+	.remove = __devexit_p(tpm_remove),
+	.suspend = tpm_pm_suspend,
+	.resume = tpm_pm_resume,
+};
+
+static int __init init_atmel(void)
+{
+	return pci_register_driver(&atmel_pci_driver);
+}
+
+static void __exit cleanup_atmel(void)
+{
+	pci_unregister_driver(&atmel_pci_driver);
+}
+
+module_init(init_atmel);
+module_exit(cleanup_atmel);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.9/drivers/char/tpm/tpm.c linux-2.6.9-tpm/drivers/char/tpm/tpm.c
--- linux-2.6.9/drivers/char/tpm/tpm.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/tpm.c	2004-12-21 13:17:28.220533736 -0600
@@ -0,0 +1,512 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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.
+ * 
+ * Note, the TPM chip is not interrupt driven (only polling)
+ * and can have very long timeouts (minutes!). Hence the unusual
+ * calls to schedule_timeout.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include "tpm.h"
+
+#define	TPM_MINOR			224	/* officially assigned */
+
+#define	TPM_BUFSIZE			2048
+
+/* PCI configuration addresses */
+#define	PCI_GEN_PMCON_1			0xA0
+#define	PCI_GEN1_DEC			0xE4
+#define	PCI_LPC_EN			0xE6
+#define	PCI_GEN2_DEC			0xEC
+
+static LIST_HEAD(tpm_chip_list);
+static spinlock_t driver_lock = SPIN_LOCK_UNLOCKED;
+static int dev_mask[32];
+
+static void user_reader_timeout(unsigned long ptr)
+{
+	struct tpm_chip *chip = (struct tpm_chip *) ptr;
+
+	down(&chip->buffer_mutex);
+	atomic_set(&chip->data_pending, 0);
+	memset(chip->data_buffer, 0, TPM_BUFSIZE);
+	up(&chip->buffer_mutex);
+}
+
+void tpm_time_expired(unsigned long ptr)
+{
+	int *exp = (int *) ptr;
+	*exp = 1;
+}
+
+EXPORT_SYMBOL_GPL(tpm_time_expired);
+
+/*
+ * Initialize the LPC bus and enable the TPM ports
+ */
+int tpm_lpc_bus_init(struct pci_dev *pci_dev, u16 base)
+{
+	u32 lpcenable, tmp;
+	int is_lpcm = 0;
+
+	switch (pci_dev->vendor) {
+	case PCI_VENDOR_ID_INTEL:
+		switch (pci_dev->device) {
+		case PCI_DEVICE_ID_INTEL_82801CA_12:
+		case PCI_DEVICE_ID_INTEL_82801DB_12:
+			is_lpcm = 1;
+			break;
+		}
+		/* init ICH (enable LPC) */
+		pci_read_config_dword(pci_dev, PCI_GEN1_DEC, &lpcenable);
+		lpcenable |= 0x20000000;
+		pci_write_config_dword(pci_dev, PCI_GEN1_DEC, lpcenable);
+
+		if (is_lpcm) {
+			pci_read_config_dword(pci_dev, PCI_GEN1_DEC,
+					      &lpcenable);
+			if ((lpcenable & 0x20000000) == 0) {
+				dev_err(&pci_dev->dev,
+					"cannot enable LPC\n");
+				return -ENODEV;
+			}
+		}
+
+		/* initialize TPM registers */
+		pci_read_config_dword(pci_dev, PCI_GEN2_DEC, &tmp);
+
+		if (!is_lpcm)
+			tmp = (tmp & 0xFFFF0000) | (base & 0xFFF0);
+		else
+			tmp =
+			    (tmp & 0xFFFF0000) | (base & 0xFFF0) |
+			    0x00000001;
+
+		pci_write_config_dword(pci_dev, PCI_GEN2_DEC, tmp);
+
+		if (is_lpcm) {
+			pci_read_config_dword(pci_dev, PCI_GEN_PMCON_1,
+					      &tmp);
+			tmp |= 0x00000004;	/* enable CLKRUN */
+			pci_write_config_dword(pci_dev, PCI_GEN_PMCON_1,
+					       tmp);
+		}
+		tpm_write_index(0x0D, 0x55);	/* unlock 4F */
+		tpm_write_index(0x0A, 0x00);	/* int disable */
+		tpm_write_index(0x08, base);	/* base addr lo */
+		tpm_write_index(0x09, (base & 0xFF00) >> 8);	/* base addr hi */
+		tpm_write_index(0x0D, 0xAA);	/* lock 4F */
+		break;
+	case PCI_VENDOR_ID_AMD:
+		/* nothing yet */
+		break;
+	}
+
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_lpc_bus_init);
+
+/*
+ * Internal kernel interface to transmit TPM commands
+ */
+static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
+			    size_t bufsiz)
+{
+	ssize_t len;
+	u32 count;
+	__be32 *native_size;
+
+	native_size = (__force __be32 *) (buf + 2);
+	count = be32_to_cpu(*native_size);
+
+	if (count == 0)
+		return -ENODATA;
+	if (count > bufsiz) {
+		dev_err(&chip->pci_dev->dev,
+			"invalid count value %x %x \n", count, bufsiz);
+		return -E2BIG;
+	}
+
+	if ((len = chip->vendor->send(chip, (u8 *) buf, count)) < 0) {
+		dev_err(&chip->pci_dev->dev,
+			"tpm_transmit: tpm_send: error %d\n", len);
+		return len;
+	}
+
+	down(&chip->timer_manipulation_mutex);
+	chip->time_expired = 0;
+	init_timer(&chip->device_timer);
+	chip->device_timer.function = tpm_time_expired;
+	chip->device_timer.expires = jiffies + 2 * 60 * HZ;
+	chip->device_timer.data = (unsigned long) &chip->time_expired;
+	add_timer(&chip->device_timer);
+	up(&chip->timer_manipulation_mutex);
+
+	do {
+		u8 status = inb(chip->vendor->base + 1);
+		if ((status & chip->vendor->req_complete_mask) ==
+		    chip->vendor->req_complete_val) {
+			down(&chip->timer_manipulation_mutex);
+			del_singleshot_timer_sync(&chip->device_timer);
+			up(&chip->timer_manipulation_mutex);
+			goto out_recv;
+		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(TPM_TIMEOUT);
+		rmb();
+	} while (!chip->time_expired);
+
+
+	chip->vendor->cancel(chip);
+	dev_err(&chip->pci_dev->dev, "Time expired\n");
+	return -EIO;
+
+out_recv:
+	len = chip->vendor->recv(chip, (u8 *) buf, bufsiz);
+	if (len < 0)
+		dev_err(&chip->pci_dev->dev,
+			"tpm_transmit: tpm_recv: error %d\n", len);
+	return len;
+}
+
+/*
+ * Device file system interface to the TPM
+ */
+int tpm_open(struct inode *inode, struct file *file)
+{
+	int rc = 0, minor = iminor(inode);
+	struct tpm_chip *chip = NULL, *pos;
+
+	spin_lock(&driver_lock);
+
+	list_for_each_entry(pos, &tpm_chip_list, list) {
+		if (pos->vendor->miscdev.minor == minor) {
+			chip = pos;
+			break;
+		}
+	}
+
+	if (chip == NULL) {
+		rc = -ENODEV;
+		goto err_out;
+	}
+
+	if (chip->num_opens) {
+		dev_dbg(&chip->pci_dev->dev,
+			"Another process owns this TPM\n");
+		rc = -EBUSY;
+		goto err_out;
+	}
+
+	chip->num_opens++;
+	pci_dev_get(chip->pci_dev);
+
+	spin_unlock(&driver_lock);
+
+	chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
+	if (chip->data_buffer == NULL) {
+		chip->num_opens--;
+		pci_dev_put(chip->pci_dev);
+		return -ENOMEM;
+	}
+
+	atomic_set(&chip->data_pending, 0);
+
+	file->private_data = chip;
+	return 0;
+
+err_out:
+	spin_unlock(&driver_lock);
+	return rc;
+}
+
+EXPORT_SYMBOL_GPL(tpm_open);
+
+int tpm_release(struct inode *inode, struct file *file)
+{
+	struct tpm_chip *chip = file->private_data;
+
+	spin_lock(&driver_lock);
+	chip->num_opens--;
+	down(&chip->timer_manipulation_mutex);
+	if (timer_pending(&chip->user_read_timer))
+		del_singleshot_timer_sync(&chip->user_read_timer);
+	else if (timer_pending(&chip->device_timer))
+		del_singleshot_timer_sync(&chip->device_timer);
+	up(&chip->timer_manipulation_mutex);
+	kfree(chip->data_buffer);
+	atomic_set(&chip->data_pending, 0);
+
+	pci_dev_put(chip->pci_dev);
+	file->private_data = NULL;
+	spin_unlock(&driver_lock);
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_release);
+
+ssize_t tpm_write(struct file * file, const char __user * buf,
+		  size_t size, loff_t * off)
+{
+	struct tpm_chip *chip = file->private_data;
+	int in_size = size, out_size;
+
+	/* cannot perform a write until the read has cleared
+	   either via tpm_read or a user_read_timer timeout */
+	while (atomic_read(&chip->data_pending) != 0) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(TPM_TIMEOUT);
+	}
+
+	down(&chip->buffer_mutex);
+
+	if (in_size > TPM_BUFSIZE)
+		in_size = TPM_BUFSIZE;
+
+	if (copy_from_user
+	    (chip->data_buffer, (void __user *) buf, in_size)) {
+		up(&chip->buffer_mutex);
+		return -EFAULT;
+	}
+
+	/* atomic tpm command send and result receive */
+	out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE);
+
+	atomic_set(&chip->data_pending, out_size);
+	up(&chip->buffer_mutex);
+
+	/* Set a timeout by which the reader must come claim the result */
+	down(&chip->timer_manipulation_mutex);
+	init_timer(&chip->user_read_timer);
+	chip->user_read_timer.function = user_reader_timeout;
+	chip->user_read_timer.data = (unsigned long) chip;
+	chip->user_read_timer.expires = jiffies + (60 * HZ);
+	add_timer(&chip->user_read_timer);
+	up(&chip->timer_manipulation_mutex);
+
+	return in_size;
+}
+
+EXPORT_SYMBOL_GPL(tpm_write);
+
+ssize_t tpm_read(struct file * file, char __user * buf, size_t size,
+		 loff_t * off)
+{
+	struct tpm_chip *chip = file->private_data;
+	int ret_size = -ENODATA;
+
+	if (atomic_read(&chip->data_pending) != 0) {	/* Result available */
+		down(&chip->timer_manipulation_mutex);
+		del_singleshot_timer_sync(&chip->user_read_timer);
+		up(&chip->timer_manipulation_mutex);
+
+		down(&chip->buffer_mutex);
+
+		ret_size = atomic_read(&chip->data_pending);
+		atomic_set(&chip->data_pending, 0);
+
+		if (ret_size == 0)	/* timeout just occurred */
+			ret_size = -ETIME;
+		else if (ret_size > 0) {	/* relay data */
+			if (size < ret_size)
+				ret_size = size;
+
+			if (copy_to_user
+			    ((void __user *) buf, chip->data_buffer,
+			     ret_size)) {
+				ret_size = -EFAULT;
+			}
+		}
+		up(&chip->buffer_mutex);
+	}
+
+	return ret_size;
+}
+
+EXPORT_SYMBOL_GPL(tpm_read);
+
+void __devexit tpm_remove(struct pci_dev *pci_dev)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+
+	if (chip == NULL) {
+		dev_err(&pci_dev->dev, "No device data found\n");
+		return;
+	}
+
+	spin_lock(&driver_lock);
+
+	list_del(&chip->list);
+
+	pci_set_drvdata(pci_dev, NULL);
+	misc_deregister(&chip->vendor->miscdev);
+
+	spin_unlock(&driver_lock);
+
+	pci_disable_device(pci_dev);
+
+	dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
+
+	kfree(chip);
+
+	pci_dev_put(pci_dev);
+}
+
+EXPORT_SYMBOL_GPL(tpm_remove);
+
+static u8 savestate[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 10,		/* blob length (in bytes) */
+	0, 0, 0, 152		/* TPM_ORD_SaveState */
+};
+
+/*
+ * We are about to suspend. Save the TPM state
+ * so that it can be restored.
+ */
+int tpm_pm_suspend(struct pci_dev *pci_dev, u32 pm_state)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+	if (chip == NULL)
+		return -ENODEV;
+
+	tpm_transmit(chip, savestate, sizeof(savestate));
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_pm_suspend);
+
+/*
+ * Resume from a power safe. The BIOS already restored
+ * the TPM state.
+ */
+int tpm_pm_resume(struct pci_dev *pci_dev)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+	if (chip == NULL)
+		return -ENODEV;
+
+	spin_lock(&driver_lock);
+	tpm_lpc_bus_init(pci_dev, chip->vendor->base);
+	spin_unlock(&driver_lock);
+
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_pm_resume);
+
+/*
+ * Called from tpm_<specific>.c probe function only for devices 
+ * the driver has determined it should claim.  Prior to calling
+ * this function the specific probe function has called pci_enable_device
+ * upon errant exit from this function specific probe function should call
+ * pci_disable_device
+ */
+int tpm_register_hardware(struct pci_dev *pci_dev,
+			  struct tpm_vendor_specific *entry)
+{
+	char devname[7];
+	struct tpm_chip *chip;
+	int i, j;
+
+	/* Driver specific per-device data */
+	chip = kmalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	memset(chip, 0, sizeof(struct tpm_chip));
+
+	init_MUTEX(&chip->buffer_mutex);
+	init_MUTEX(&chip->timer_manipulation_mutex);
+	INIT_LIST_HEAD(&chip->list);
+
+	chip->vendor = entry;
+
+	chip->dev_num = -1;
+
+	for (i = 0; i < 32; i++)
+		for (j = 0; j < 8; j++)
+			if ((dev_mask[i] & (1 << j)) == 0) {
+				chip->dev_num = i * 32 + j;
+				dev_mask[i] |= 1 << j;
+				goto dev_num_search_complete;
+			}
+
+dev_num_search_complete:
+	if (chip->dev_num < 0) {
+		dev_err(&pci_dev->dev,
+			"No available tpm device numbers\n");
+		kfree(chip);
+		return -ENODEV;
+	} else if (chip->dev_num == 0)
+		chip->vendor->miscdev.minor = TPM_MINOR;
+	else
+		chip->vendor->miscdev.minor = MISC_DYNAMIC_MINOR;
+
+	snprintf(devname, sizeof(devname), "%s%d", "tpm", chip->dev_num);
+	chip->vendor->miscdev.name = devname;
+
+	chip->vendor->miscdev.dev = &(pci_dev->dev);
+	chip->pci_dev = pci_dev_get(pci_dev);
+
+	spin_lock(&driver_lock);
+
+	if (misc_register(&chip->vendor->miscdev)) {
+		dev_err(&chip->pci_dev->dev,
+			"unable to misc_register %s, minor %d\n",
+			chip->vendor->miscdev.name,
+			chip->vendor->miscdev.minor);
+		pci_dev_put(pci_dev);
+		spin_unlock(&driver_lock);
+		kfree(chip);
+		dev_mask[i] &= !(1 << j);
+		return -ENODEV;
+	}
+
+	pci_set_drvdata(pci_dev, chip);
+
+	list_add(&chip->list, &tpm_chip_list);
+
+	spin_unlock(&driver_lock);
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_register_hardware);
+
+static int __init init_tpm(void)
+{
+	return 0;
+}
+
+static void __exit cleanup_tpm(void)
+{
+
+}
+
+module_init(init_tpm);
+module_exit(cleanup_tpm);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.9/drivers/char/tpm/tpm.h linux-2.6.9-tpm/drivers/char/tpm/tpm.h
--- linux-2.6.9/drivers/char/tpm/tpm.h	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/tpm.h	2004-12-21 12:32:44.000000000 -0600
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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/module.h>
+#include <linux/version.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/miscdevice.h>
+
+#define TPM_TIMEOUT msecs_to_jiffies(5)
+
+/* TPM addresses */
+#define	TPM_ADDR			0x4E
+#define	TPM_DATA			0x4F
+
+struct tpm_chip;
+
+struct tpm_vendor_specific {
+	u8 req_complete_mask;
+	u8 req_complete_val;
+	u16 base;		/* TPM base address */
+
+	int (*recv) (struct tpm_chip *, u8 *, size_t);
+	int (*send) (struct tpm_chip *, u8 *, size_t);
+	void (*cancel) (struct tpm_chip *);
+	struct miscdevice miscdev;
+};
+
+struct tpm_chip {
+	struct pci_dev *pci_dev;	/* PCI device stuff */
+
+	int dev_num;		/* /dev/tpm# */
+	int num_opens;		/* only one allowed */
+	int time_expired;
+
+	/* Data passed to and from the tpm via the read/write calls */
+	u8 *data_buffer;
+	atomic_t data_pending;
+	struct semaphore buffer_mutex;
+
+	struct timer_list user_read_timer;	/* user needs to claim result */
+	struct timer_list device_timer;	/* tpm is processing */
+	struct semaphore timer_manipulation_mutex;
+
+	struct tpm_vendor_specific *vendor;
+
+	struct list_head list;
+};
+
+static inline int tpm_read_index(int index)
+{
+	outb(index, TPM_ADDR);
+	return inb(TPM_DATA) & 0xFF;
+}
+
+static inline void tpm_write_index(int index, int value)
+{
+	outb(index, TPM_ADDR);
+	outb(value & 0xFF, TPM_DATA);
+}
+
+extern void tpm_time_expired(unsigned long);
+extern int tpm_lpc_bus_init(struct pci_dev *, u16);
+
+extern int tpm_register_hardware(struct pci_dev *,
+				 struct tpm_vendor_specific *);
+extern int tpm_open(struct inode *, struct file *);
+extern int tpm_release(struct inode *, struct file *);
+extern ssize_t tpm_write(struct file *, const char __user *, size_t,
+			 loff_t *);
+extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *);
+extern void __devexit tpm_remove(struct pci_dev *);
+extern int tpm_pm_suspend(struct pci_dev *, u32);
+extern int tpm_pm_resume(struct pci_dev *);
diff -uprN linux-2.6.9/drivers/char/tpm/tpm_nsc.c linux-2.6.9-tpm/drivers/char/tpm/tpm_nsc.c
--- linux-2.6.9/drivers/char/tpm/tpm_nsc.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9-tpm/drivers/char/tpm/tpm_nsc.c	2004-12-21 13:11:50.000000000 -0600
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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"
+
+/* National definitions */
+#define	TPM_NSC_BASE			0x360
+#define	TPM_NSC_IRQ			0x07
+
+#define	NSC_LDN_INDEX			0x07
+#define	NSC_SID_INDEX			0x20
+#define	NSC_LDC_INDEX			0x30
+#define	NSC_DIO_INDEX			0x60
+#define	NSC_CIO_INDEX			0x62
+#define	NSC_IRQ_INDEX			0x70
+#define	NSC_ITS_INDEX			0x71
+
+#define	NSC_STATUS			0x01
+#define	NSC_COMMAND			0x01
+#define	NSC_DATA			0x00
+
+/* status bits */
+#define	NSC_STATUS_OBF			0x01	/* output buffer full */
+#define	NSC_STATUS_IBF			0x02	/* input buffer full */
+#define	NSC_STATUS_F0			0x04	/* F0 */
+#define	NSC_STATUS_A2			0x08	/* A2 */
+#define	NSC_STATUS_RDY			0x10	/* ready to receive command */
+#define	NSC_STATUS_IBR			0x20	/* ready to receive data */
+
+/* command bits */
+#define	NSC_COMMAND_NORMAL		0x01	/* normal mode */
+#define	NSC_COMMAND_EOC			0x03
+#define	NSC_COMMAND_CANCEL		0x22
+
+/*
+ * Wait for a certain status to appear
+ */
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
+{
+	int expired = 0;
+	struct timer_list status_timer =
+	    TIMER_INITIALIZER(tpm_time_expired, jiffies + 10 * HZ,
+			      (unsigned long) &expired);
+
+	/* status immediately available check */
+	*data = inb(chip->vendor->base + NSC_STATUS);
+	if ((*data & mask) == val)
+		return 0;
+
+	/* wait for status */
+	add_timer(&status_timer);
+	do {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(TPM_TIMEOUT);
+		*data = inb(chip->vendor->base + 1);
+		if ((*data & mask) == val) {
+			del_singleshot_timer_sync(&status_timer);
+			return 0;
+		}
+	}
+	while (!expired);
+
+	return -EBUSY;
+}
+
+static int nsc_wait_for_ready(struct tpm_chip *chip)
+{
+	int status;
+	int expired = 0;
+	struct timer_list status_timer =
+	    TIMER_INITIALIZER(tpm_time_expired, jiffies + 100,
+			      (unsigned long) &expired);
+
+	/* status immediately available check */
+	status = inb(chip->vendor->base + NSC_STATUS);
+	if (status & NSC_STATUS_OBF)
+		status = inb(chip->vendor->base + NSC_DATA);
+	if (status & NSC_STATUS_RDY)
+		return 0;
+
+	/* wait for status */
+	add_timer(&status_timer);
+	do {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(TPM_TIMEOUT);
+		status = inb(chip->vendor->base + NSC_STATUS);
+		if (status & NSC_STATUS_OBF)
+			status = inb(chip->vendor->base + NSC_DATA);
+		if (status & NSC_STATUS_RDY) {
+			del_singleshot_timer_sync(&status_timer);
+			return 0;
+		}
+	}
+	while (!expired);
+
+	dev_info(&chip->pci_dev->dev, "wait for ready failed\n");
+	return -EBUSY;
+}
+
+
+static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 *buffer = buf;
+	u8 data, *p;
+	u32 size;
+	__be32 *native_size;
+
+	if (count < 6)
+		return -EIO;
+
+	if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "F0 timeout\n");
+		return -EIO;
+	}
+	if ((data =
+	     inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
+		dev_err(&chip->pci_dev->dev, "not in normal mode (0x%x)\n",
+			data);
+		return -EIO;
+	}
+
+	/* read the whole packet */
+	for (p = buffer; p < &buffer[count]; p++) {
+		if (wait_for_stat
+		    (chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) {
+			dev_err(&chip->pci_dev->dev,
+				"OBF timeout (while reading data)\n");
+			return -EIO;
+		}
+		if (data & NSC_STATUS_F0)
+			break;
+		*p = inb(chip->vendor->base + NSC_DATA);
+	}
+
+	if ((data & NSC_STATUS_F0) == 0) {
+		dev_err(&chip->pci_dev->dev, "F0 not set\n");
+		return -EIO;
+	}
+	if ((data = inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_EOC) {
+		dev_err(&chip->pci_dev->dev,
+			"expected end of command(0x%x)\n", data);
+		return -EIO;
+	}
+
+	native_size = (__force __be32 *) (buf + 2);
+	size = be32_to_cpu(*native_size);
+
+	if (count < size)
+		return -EIO;
+
+	return size;
+}
+
+static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 data;
+	int i;
+
+	/*
+	 * If we hit the chip with back to back commands it locks up
+	 * and never set IBF. Hitting it with this "hammer" seems to
+	 * fix it. Not sure why this is needed, we followed the flow
+	 * chart in the manual to the letter.
+	 */
+	outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND);
+
+	if (nsc_wait_for_ready(chip) != 0)
+		return -EIO;
+
+	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+		return -EIO;
+	}
+
+	outb(NSC_COMMAND_NORMAL, chip->vendor->base + NSC_COMMAND);
+	if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBR timeout\n");
+		return -EIO;
+	}
+
+	for (i = 0; i < count; i++) {
+		if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+			dev_err(&chip->pci_dev->dev,
+				"IBF timeout (while writing data)\n");
+			return -EIO;
+		}
+		outb(buf[i], chip->vendor->base + NSC_DATA);
+	}
+
+	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+		return -EIO;
+	}
+	outb(NSC_COMMAND_EOC, chip->vendor->base + NSC_COMMAND);
+
+	return count;
+}
+
+static void tpm_nsc_cancel(struct tpm_chip *chip)
+{
+	outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND);
+}
+
+static struct file_operations nsc_ops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.open = tpm_open,
+	.read = tpm_read,
+	.write = tpm_write,
+	.release = tpm_release,
+};
+
+static struct tpm_vendor_specific tpm_nsc = {
+	.recv = tpm_nsc_recv,
+	.send = tpm_nsc_send,
+	.cancel = tpm_nsc_cancel,
+	.req_complete_mask = NSC_STATUS_OBF,
+	.req_complete_val = NSC_STATUS_OBF,
+	.base = TPM_NSC_BASE,
+	.miscdev.fops = &nsc_ops,
+};
+
+static int __devinit tpm_nsc_init(struct pci_dev *pci_dev,
+				  const struct pci_device_id *pci_id)
+{
+	int rc = 0;
+
+	if (pci_enable_device(pci_dev))
+		return -EIO;
+
+	if (tpm_lpc_bus_init(pci_dev, TPM_NSC_BASE)) {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	/* verify that it is a National part (SID) */
+	if (tpm_read_index(NSC_SID_INDEX) != 0xEF) {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	dev_dbg(&pci_dev->dev, "NSC TPM detected\n");
+	dev_dbg(&pci_dev->dev,
+		"NSC LDN 0x%x, SID 0x%x, SRID 0x%x\n",
+		tpm_read_index(0x07), tpm_read_index(0x20),
+		tpm_read_index(0x27));
+	dev_dbg(&pci_dev->dev,
+		"NSC SIOCF1 0x%x SIOCF5 0x%x SIOCF6 0x%x SIOCF8 0x%x\n",
+		tpm_read_index(0x21), tpm_read_index(0x25),
+		tpm_read_index(0x26), tpm_read_index(0x28));
+	dev_dbg(&pci_dev->dev, "NSC IO Base0 0x%x\n",
+		(tpm_read_index(0x60) << 8) | tpm_read_index(0x61));
+	dev_dbg(&pci_dev->dev, "NSC IO Base1 0x%x\n",
+		(tpm_read_index(0x62) << 8) | tpm_read_index(0x63));
+	dev_dbg(&pci_dev->dev, "NSC Interrupt number and wakeup 0x%x\n",
+		tpm_read_index(0x70));
+	dev_dbg(&pci_dev->dev, "NSC IRQ type select 0x%x\n",
+		tpm_read_index(0x71));
+	dev_dbg(&pci_dev->dev,
+		"NSC DMA channel select0 0x%x, select1 0x%x\n",
+		tpm_read_index(0x74), tpm_read_index(0x75));
+	dev_dbg(&pci_dev->dev,
+		"NSC Config "
+		"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+		tpm_read_index(0xF0), tpm_read_index(0xF1),
+		tpm_read_index(0xF2), tpm_read_index(0xF3),
+		tpm_read_index(0xF4), tpm_read_index(0xF5),
+		tpm_read_index(0xF6), tpm_read_index(0xF7),
+		tpm_read_index(0xF8), tpm_read_index(0xF9));
+
+	dev_info(&pci_dev->dev,
+		 "NSC PC21100 TPM revision %d\n",
+		 tpm_read_index(0x27) & 0x1F);
+
+	if (tpm_read_index(NSC_LDC_INDEX) == 0)
+		dev_info(&pci_dev->dev, ": NSC TPM not active\n");
+
+	/* select PM channel 1 */
+	tpm_write_index(NSC_LDN_INDEX, 0x12);
+	tpm_read_index(NSC_LDN_INDEX);
+
+	/* disable the DPM module */
+	tpm_write_index(NSC_LDC_INDEX, 0);
+	tpm_read_index(NSC_LDC_INDEX);
+
+	/* set the data register base addresses */
+	tpm_write_index(NSC_DIO_INDEX, TPM_NSC_BASE >> 8);
+	tpm_write_index(NSC_DIO_INDEX + 1, TPM_NSC_BASE);
+	tpm_read_index(NSC_DIO_INDEX);
+	tpm_read_index(NSC_DIO_INDEX + 1);
+
+	/* set the command register base addresses */
+	tpm_write_index(NSC_CIO_INDEX, (TPM_NSC_BASE + 1) >> 8);
+	tpm_write_index(NSC_CIO_INDEX + 1, (TPM_NSC_BASE + 1));
+	tpm_read_index(NSC_DIO_INDEX);
+	tpm_read_index(NSC_DIO_INDEX + 1);
+
+	/* set the interrupt number to be used for the host interface */
+	tpm_write_index(NSC_IRQ_INDEX, TPM_NSC_IRQ);
+	tpm_write_index(NSC_ITS_INDEX, 0x00);
+	tpm_read_index(NSC_IRQ_INDEX);
+
+	/* enable the DPM module */
+	tpm_write_index(NSC_LDC_INDEX, 0x01);
+	tpm_read_index(NSC_LDC_INDEX);
+
+	if ((rc = tpm_register_hardware(pci_dev, &tpm_nsc)) < 0)
+		goto out_err;
+
+	return 0;
+
+out_err:
+	pci_disable_device(pci_dev);
+	return rc;
+}
+
+static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
+
+static struct pci_driver nsc_pci_driver = {
+	.name = "tpm_nsc",
+	.id_table = tpm_pci_tbl,
+	.probe = tpm_nsc_init,
+	.remove = __devexit_p(tpm_remove),
+	.suspend = tpm_pm_suspend,
+	.resume = tpm_pm_resume,
+};
+
+static int __init init_nsc(void)
+{
+	return pci_register_driver(&nsc_pci_driver);
+}
+
+static void __exit cleanup_nsc(void)
+{
+	pci_unregister_driver(&nsc_pci_driver);
+}
+
+module_init(init_nsc);
+module_exit(cleanup_nsc);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.9/include/linux/pci_ids.h linux-2.6.9-tpm/include/linux/pci_ids.h
--- linux-2.6.9/include/linux/pci_ids.h	2004-12-06 16:53:35.000000000 -0600
+++ linux-2.6.9-tpm/include/linux/pci_ids.h	2004-12-06 14:27:05.000000000 -0600
@@ -494,6 +494,7 @@
 #define PCI_DEVICE_ID_AMD_OPUS_7449	0x7449
 #	define PCI_DEVICE_ID_AMD_VIPER_7449	PCI_DEVICE_ID_AMD_OPUS_7449
 #define PCI_DEVICE_ID_AMD_8111_LAN	0x7462
+#define PCI_DEVICE_ID_AMD_8111_LPC	0x7468
 #define PCI_DEVICE_ID_AMD_8111_IDE	0x7469
 #define PCI_DEVICE_ID_AMD_8111_AUDIO	0x746d
 #define PCI_DEVICE_ID_AMD_8151_0	0x7454
diff -uprN linux-2.6.9/MAINTAINERS linux-2.6.9-tpm/MAINTAINERS
--- linux-2.6.9/MAINTAINERS	2004-10-18 16:54:37.000000000 -0500
+++ linux-2.6.9-tpm/MAINTAINERS	2004-12-07 12:39:10.000000000 -0600
@@ -2144,6 +2144,12 @@ L:	tlinux-users@tce.toshiba-dme.co.jp
 W:	http://www.buzzard.org.uk/toshiba/
 S:	Maintained
 
+TPM DRIVER
+P:	Kylene Hall
+M:	tpmdd-devel@lists.sourceforge.net
+L:	tpmdd-devel@lists.sourceforge.net
+S:	Maintained
+
 TRIDENT 4DWAVE/SIS 7018 PCI AUDIO CORE
 P:	Muli Ben-Yehuda
 M:	mulix@mulix.org

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement --updated version
  2004-12-21 18:19         ` Kylene Hall
@ 2005-01-12 18:45           ` Kylene Hall
  2005-01-12 23:28             ` Greg KH
  2005-01-18 22:29             ` [PATCH 1/1] tpm: fix cause of SMP stack traces Kylene Hall
  0 siblings, 2 replies; 43+ messages in thread
From: Kylene Hall @ 2005-01-12 18:45 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, sailer, leendert, emilyr, toml, tpmdd-devel

This patch is a device driver to enable new hardware.  The new hardware is
the TPM chip as described by specifications at 
<http://www.trustedcomputinggroup.org>.  The TPM chip will enable you to
use hardware to securely store and protect your keys and personal data.
To use the chip according to the specification, you will need the Trusted
Software Stack (TSS) of which an implementation for Linux is available at:
<http://sourceforge.net/projects/trousers>.
  
> > > > > Updates include: splitting out the vendor specific code into separated
> > > > > drivers from the base TPM functionality, fixed busy waiting loops,
> > > > > concerns about timer handling and rmmod, buffer name.
> > > 
> > > > Updates include a consolodated buffer mutex, numerous naming improvements,
> > > > better placement of pci_driver and file_operations structure
> > > > initializations, change to use of EXPORT_SYMBOL_GPL, improvement of
> > > > Kconfig help texts and creation of a tpm directory
> > > 
> > > Updates include moving the pci_ids table into the individual vendor
> > > specific .c files and including a tpm description file in the
> > > Documentation directory.
> 
> > All of these calls to schedule_timeout() are broken. You must set the
> > state appropriately before calling schedule_timeout() or it will
> > return immediately. For your reference here is the difference between
> > the two states with respects to calling schedule_timeout() directly:
> Fixed in the patch below.

Updates include fixing an uninitialized mutex and the addition of sysfs 
files for retrieving important device information.  Patch generated 
against 2.6.10. 

Thanks,
Kylene
 
Signed-off-by: Leendert van Doorn <leendert@watson.ibm.com>
Signed-off-by: Reiner Sailer <sailer@watson.ibm.com>
Signed-off-by: Dave Safford <safford@watson.ibm.com>
Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---
diff -uprN linux-2.6.10/drivers/char/Kconfig linux-2.6.10-tpm/drivers/char/Kconfig
--- linux-2.6.10/drivers/char/Kconfig	2004-12-24 15:33:49.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/Kconfig	2005-01-07 13:23:08.000000000 -0600
@@ -1007,5 +1007,7 @@ config MMTIMER
 	  The mmtimer device allows direct userspace access to the
 	  Altix system timer.
 
+source "drivers/char/tpm/Kconfig"
+
 endmenu
 
diff -uprN linux-2.6.10/drivers/char/Makefile linux-2.6.10-tpm/drivers/char/Makefile
--- linux-2.6.10/drivers/char/Makefile	2004-12-24 15:35:29.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/Makefile	2005-01-07 13:20:43.000000000 -0600
@@ -90,7 +90,7 @@ obj-$(CONFIG_PCMCIA) += pcmcia/
 obj-$(CONFIG_IPMI_HANDLER) += ipmi/
 
 obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
-
+obj-$(CONFIG_TCG_TPM) += tpm/
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c qtronixmap.c
 
diff -uprN linux-2.6.10/drivers/char/tpm/Kconfig linux-2.6.10-tpm/drivers/char/tpm/Kconfig
--- linux-2.6.10/drivers/char/tpm/Kconfig	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/Kconfig	2004-12-20 11:06:10.000000000 -0600
@@ -0,0 +1,39 @@
+#
+# TPM device configuration
+#
+
+menu "TPM devices"
+
+config TCG_TPM
+	tristate "TPM Hardware Support"
+	depends on EXPERIMENTAL
+	---help---
+	  If you have a TPM security chip in your system, which
+	  implements the Trusted Computing Group's specification,
+	  say Yes and it will be accessible from within Linux.  For
+	  more information see <http://www.trustedcomputinggroup.org>. 
+	  An implementation of the Trusted Software Stack (TSS), the 
+	  userspace enablement piece of the specification, can be 
+	  obtained at: <http://sourceforge.net/projects/trousers>.  To 
+	  compile this driver as a module, choose M here; the module 
+	  will be called tpm. If unsure, say N.
+
+config TCG_NSC
+	tristate "National Semiconductor TPM Interface"
+	depends on TCG_TPM
+	---help---
+	  If you have a TPM security chip from National Semicondutor 
+	  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_nsc.
+
+config TCG_ATMEL
+	tristate "Atmel TPM Interface"
+	depends on TCG_TPM
+	---help---
+	  If you have a TPM security chip from Atmel 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_atmel.
+
+endmenu
+
diff -uprN linux-2.6.10/drivers/char/tpm/Makefile linux-2.6.10-tpm/drivers/char/tpm/Makefile
--- linux-2.6.10/drivers/char/tpm/Makefile	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/Makefile	2004-12-20 11:06:10.000000000 -0600
@@ -0,0 +1,7 @@
+#
+# Makefile for the kernel tpm device drivers.
+#
+obj-$(CONFIG_TCG_TPM) += tpm.o
+obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
+obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
+
diff -uprN linux-2.6.10/drivers/char/tpm/tpm_atmel.c linux-2.6.10-tpm/drivers/char/tpm/tpm_atmel.c
--- linux-2.6.10/drivers/char/tpm/tpm_atmel.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm_atmel.c	2004-12-21 12:33:08.000000000 -0600
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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"
+
+/* Atmel definitions */
+#define	TPM_ATML_BASE			0x400
+
+/* write status bits */
+#define	ATML_STATUS_ABORT		0x01
+#define	ATML_STATUS_LASTBYTE		0x04
+
+/* read status bits */
+#define	ATML_STATUS_BUSY		0x01
+#define	ATML_STATUS_DATA_AVAIL		0x02
+#define	ATML_STATUS_REWRITE		0x04
+
+
+static int tpm_atml_recv(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 status, *hdr = buf;
+	u32 size;
+	int i;
+	__be32 *native_size;
+
+	/* start reading header */
+	if (count < 6)
+		return -EIO;
+
+	for (i = 0; i < 6; i++) {
+		status = inb(chip->vendor->base + 1);
+		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+			dev_err(&chip->pci_dev->dev,
+				"error reading header\n");
+			return -EIO;
+		}
+		*buf++ = inb(chip->vendor->base);
+	}
+
+	/* size of the data received */
+	native_size = (__force __be32 *) (hdr + 2);
+	size = be32_to_cpu(*native_size);
+
+	if (count < size) {
+		dev_err(&chip->pci_dev->dev,
+			"Recv size(%d) less than available space\n", size);
+		for (; i < size; i++) {	/* clear the waiting data anyway */
+			status = inb(chip->vendor->base + 1);
+			if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+				dev_err(&chip->pci_dev->dev,
+					"error reading data\n");
+				return -EIO;
+			}
+		}
+		return -EIO;
+	}
+
+	/* read all the data available */
+	for (; i < size; i++) {
+		status = inb(chip->vendor->base + 1);
+		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+			dev_err(&chip->pci_dev->dev,
+				"error reading data\n");
+			return -EIO;
+		}
+		*buf++ = inb(chip->vendor->base);
+	}
+
+	/* make sure data available is gone */
+	status = inb(chip->vendor->base + 1);
+	if (status & ATML_STATUS_DATA_AVAIL) {
+		dev_err(&chip->pci_dev->dev, "data available is stuck\n");
+		return -EIO;
+	}
+
+	return size;
+}
+
+static int tpm_atml_send(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	int i;
+
+	dev_dbg(&chip->pci_dev->dev, "tpm_atml_send: ");
+	for (i = 0; i < count; i++) {
+		dev_dbg(&chip->pci_dev->dev, "0x%x(%d) ", buf[i], buf[i]);
+		outb(buf[i], chip->vendor->base);
+	}
+
+	return count;
+}
+
+static void tpm_atml_cancel(struct tpm_chip *chip)
+{
+	outb(ATML_STATUS_ABORT, chip->vendor->base + 1);
+}
+
+static struct file_operations atmel_ops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.open = tpm_open,
+	.read = tpm_read,
+	.write = tpm_write,
+	.release = tpm_release,
+};
+
+static struct tpm_vendor_specific tpm_atmel = {
+	.recv = tpm_atml_recv,
+	.send = tpm_atml_send,
+	.cancel = tpm_atml_cancel,
+	.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
+	.req_complete_val = ATML_STATUS_DATA_AVAIL,
+	.base = TPM_ATML_BASE,
+	.miscdev.fops = &atmel_ops,
+};
+
+static int __devinit tpm_atml_init(struct pci_dev *pci_dev,
+				   const struct pci_device_id *pci_id)
+{
+	u8 version[4];
+	int rc = 0;
+
+	if (pci_enable_device(pci_dev))
+		return -EIO;
+
+	if (tpm_lpc_bus_init(pci_dev, TPM_ATML_BASE)) {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	/* verify that it is an Atmel part */
+	if (tpm_read_index(4) != 'A' || tpm_read_index(5) != 'T'
+	    || tpm_read_index(6) != 'M' || tpm_read_index(7) != 'L') {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	/* query chip for its version number */
+	if ((version[0] = tpm_read_index(0x00)) != 0xFF) {
+		version[1] = tpm_read_index(0x01);
+		version[2] = tpm_read_index(0x02);
+		version[3] = tpm_read_index(0x03);
+	} else {
+		dev_info(&pci_dev->dev, "version query failed\n");
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	if ((rc = tpm_register_hardware(pci_dev, &tpm_atmel)) < 0)
+		goto out_err;
+
+	dev_info(&pci_dev->dev,
+		 "Atmel TPM version %d.%d.%d.%d\n", version[0], version[1],
+		 version[2], version[3]);
+
+	return 0;
+out_err:
+	pci_disable_device(pci_dev);
+	return rc;
+}
+
+static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
+
+static struct pci_driver atmel_pci_driver = {
+	.name = "tpm_atmel",
+	.id_table = tpm_pci_tbl,
+	.probe = tpm_atml_init,
+	.remove = __devexit_p(tpm_remove),
+	.suspend = tpm_pm_suspend,
+	.resume = tpm_pm_resume,
+};
+
+static int __init init_atmel(void)
+{
+	return pci_register_driver(&atmel_pci_driver);
+}
+
+static void __exit cleanup_atmel(void)
+{
+	pci_unregister_driver(&atmel_pci_driver);
+}
+
+module_init(init_atmel);
+module_exit(cleanup_atmel);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.c linux-2.6.10-tpm/drivers/char/tpm/tpm.c
--- linux-2.6.10/drivers/char/tpm/tpm.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.c	2005-01-12 12:52:56.000000000 -0600
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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.
+ * 
+ * Note, the TPM chip is not interrupt driven (only polling)
+ * and can have very long timeouts (minutes!). Hence the unusual
+ * calls to schedule_timeout.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include "tpm.h"
+
+#define	TPM_MINOR			224	/* officially assigned */
+
+#define	TPM_BUFSIZE			2048
+
+/* PCI configuration addresses */
+#define	PCI_GEN_PMCON_1			0xA0
+#define	PCI_GEN1_DEC			0xE4
+#define	PCI_LPC_EN			0xE6
+#define	PCI_GEN2_DEC			0xEC
+
+static LIST_HEAD(tpm_chip_list);
+static spinlock_t driver_lock = SPIN_LOCK_UNLOCKED;
+static int dev_mask[32];
+
+static void user_reader_timeout(unsigned long ptr)
+{
+	struct tpm_chip *chip = (struct tpm_chip *) ptr;
+
+	down(&chip->buffer_mutex);
+	atomic_set(&chip->data_pending, 0);
+	memset(chip->data_buffer, 0, TPM_BUFSIZE);
+	up(&chip->buffer_mutex);
+}
+
+void tpm_time_expired(unsigned long ptr)
+{
+	int *exp = (int *) ptr;
+	*exp = 1;
+}
+
+EXPORT_SYMBOL_GPL(tpm_time_expired);
+
+/*
+ * Initialize the LPC bus and enable the TPM ports
+ */
+int tpm_lpc_bus_init(struct pci_dev *pci_dev, u16 base)
+{
+	u32 lpcenable, tmp;
+	int is_lpcm = 0;
+
+	switch (pci_dev->vendor) {
+	case PCI_VENDOR_ID_INTEL:
+		switch (pci_dev->device) {
+		case PCI_DEVICE_ID_INTEL_82801CA_12:
+		case PCI_DEVICE_ID_INTEL_82801DB_12:
+			is_lpcm = 1;
+			break;
+		}
+		/* init ICH (enable LPC) */
+		pci_read_config_dword(pci_dev, PCI_GEN1_DEC, &lpcenable);
+		lpcenable |= 0x20000000;
+		pci_write_config_dword(pci_dev, PCI_GEN1_DEC, lpcenable);
+
+		if (is_lpcm) {
+			pci_read_config_dword(pci_dev, PCI_GEN1_DEC,
+					      &lpcenable);
+			if ((lpcenable & 0x20000000) == 0) {
+				dev_err(&pci_dev->dev,
+					"cannot enable LPC\n");
+				return -ENODEV;
+			}
+		}
+
+		/* initialize TPM registers */
+		pci_read_config_dword(pci_dev, PCI_GEN2_DEC, &tmp);
+
+		if (!is_lpcm)
+			tmp = (tmp & 0xFFFF0000) | (base & 0xFFF0);
+		else
+			tmp =
+			    (tmp & 0xFFFF0000) | (base & 0xFFF0) |
+			    0x00000001;
+
+		pci_write_config_dword(pci_dev, PCI_GEN2_DEC, tmp);
+
+		if (is_lpcm) {
+			pci_read_config_dword(pci_dev, PCI_GEN_PMCON_1,
+					      &tmp);
+			tmp |= 0x00000004;	/* enable CLKRUN */
+			pci_write_config_dword(pci_dev, PCI_GEN_PMCON_1,
+					       tmp);
+		}
+		tpm_write_index(0x0D, 0x55);	/* unlock 4F */
+		tpm_write_index(0x0A, 0x00);	/* int disable */
+		tpm_write_index(0x08, base);	/* base addr lo */
+		tpm_write_index(0x09, (base & 0xFF00) >> 8);	/* base addr hi */
+		tpm_write_index(0x0D, 0xAA);	/* lock 4F */
+		break;
+	case PCI_VENDOR_ID_AMD:
+		/* nothing yet */
+		break;
+	}
+
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_lpc_bus_init);
+
+/*
+ * Internal kernel interface to transmit TPM commands
+ */
+static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
+			    size_t bufsiz)
+{
+	ssize_t len;
+	u32 count;
+	__be32 *native_size;
+
+	native_size = (__force __be32 *) (buf + 2);
+	count = be32_to_cpu(*native_size);
+
+	if (count == 0)
+		return -ENODATA;
+	if (count > bufsiz) {
+		dev_err(&chip->pci_dev->dev,
+			"invalid count value %x %x \n", count, bufsiz);
+		return -E2BIG;
+	}
+
+	down(&chip->tpm_mutex);
+
+	if ((len = chip->vendor->send(chip, (u8 *) buf, count)) < 0) {
+		dev_err(&chip->pci_dev->dev,
+			"tpm_transmit: tpm_send: error %d\n", len);
+		return len;
+	}
+
+	down(&chip->timer_manipulation_mutex);
+	chip->time_expired = 0;
+	init_timer(&chip->device_timer);
+	chip->device_timer.function = tpm_time_expired;
+	chip->device_timer.expires = jiffies + 2 * 60 * HZ;
+	chip->device_timer.data = (unsigned long) &chip->time_expired;
+	add_timer(&chip->device_timer);
+	up(&chip->timer_manipulation_mutex);
+
+	do {
+		u8 status = inb(chip->vendor->base + 1);
+		if ((status & chip->vendor->req_complete_mask) ==
+		    chip->vendor->req_complete_val) {
+			down(&chip->timer_manipulation_mutex);
+			del_singleshot_timer_sync(&chip->device_timer);
+			up(&chip->timer_manipulation_mutex);
+			goto out_recv;
+		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(TPM_TIMEOUT);
+		rmb();
+	} while (!chip->time_expired);
+
+
+	chip->vendor->cancel(chip);
+	dev_err(&chip->pci_dev->dev, "Time expired\n");
+	up(&chip->tpm_mutex);
+	return -EIO;
+
+out_recv:
+	len = chip->vendor->recv(chip, (u8 *) buf, bufsiz);
+	if (len < 0)
+		dev_err(&chip->pci_dev->dev,
+			"tpm_transmit: tpm_recv: error %d\n", len);
+	up(&chip->tpm_mutex);
+	return len;
+}
+
+#define TPM_DIGEST_SIZE 20
+#define CAP_PCR_RESULT_SIZE 18
+static u8 cap_pcr[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 22,		/* length */
+	0, 0, 0, 101,		/* TPM_ORD_GetCapability */
+	0, 0, 0, 5,
+	0, 0, 0, 4,
+	0, 0, 1, 1
+};
+
+#define READ_PCR_RESULT_SIZE 30
+static u8 pcrread[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 14,		/* length */
+	0, 0, 0, 21,		/* TPM_ORD_PcrRead */
+	0, 0, 0, 0		/* PCR index */
+};
+
+static ssize_t show_pcrs(struct device *dev, char *buf)
+{
+	u8 data[READ_PCR_RESULT_SIZE];
+	ssize_t len;
+	int i, j, index, num_pcrs;
+	char *str = buf;
+
+	struct tpm_chp *chip =
+	    pci_get_drvdata(container_of(dev, struct pci_dev, dev));
+	if (chip == NULL)
+		return -ENODEV;
+
+	memcpy(data, cap_pcr, sizeof(cap_pcr));
+	if ((len = tpm_transmit(chip, data, sizeof(data)))
+	    < CAP_PCR_RESULT_SIZE)
+		return len;
+
+	num_pcrs = be32_to_cpu(*((__force __be32 *) (data + 14)));
+
+	for (i = 0; i < num_pcrs; i++) {
+		memcpy(data, pcrread, sizeof(pcrread));
+		index = cpu_to_be32(i);
+		memcpy(data + 10, &index, 4);
+		if ((len = tpm_transmit(chip, data, sizeof(data)))
+		    < READ_PCR_RESULT_SIZE)
+			return len;
+		str += sprintf(str, "PCR-%02d: ", i);
+		for (j = 0; j < TPM_DIGEST_SIZE; j++)
+			str += sprintf(str, "%02X ", *(data + 10 + j));
+		str += sprintf(str, "\n");
+	}
+	return str - buf;
+}
+
+static DEVICE_ATTR(pcrs, S_IRUGO, show_pcrs, NULL);
+
+#define  READ_PUBEK_RESULT_SIZE 314
+static u8 readpubek[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 30,		/* length */
+	0, 0, 0, 124,		/* TPM_ORD_ReadPubek */
+};
+
+static ssize_t show_pubek(struct device *dev, char *buf)
+{
+	u8 data[READ_PUBEK_RESULT_SIZE];
+	ssize_t len;
+	__be32 *native_val;
+	int i;
+	char *str = buf;
+
+	struct tpm_chip *chip =
+	    pci_get_drvdata(container_of(dev, struct pci_dev, dev));
+	if (chip == NULL)
+		return -ENODEV;
+
+	memcpy(data, readpubek, sizeof(readpubek));
+	memset(data + sizeof(readpubek), 0, 20);	/* zero nonce */
+
+	if ((len = tpm_transmit(chip, data, sizeof(data))) <
+	    READ_PUBEK_RESULT_SIZE)
+		return len;
+
+	/* 
+	   ignore header 10 bytes
+	   algorithm 32 bits (1 == RSA )
+	   encscheme 16 bits
+	   sigscheme 16 bits
+	   parameters (RSA 12->bytes: keybit, #primes, expbit)  
+	   keylenbytes 32 bits
+	   256 byte modulus
+	   ignore checksum 20 bytes
+	 */
+
+	native_val = (__force __be32 *) (data + 34);
+
+	str +=
+	    sprintf(str,
+		    "Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n"
+		    "Sigscheme: %02X %02X\nParameters: %02X %02X %02X %02X"
+		    " %02X %02X %02X %02X %02X %02X %02X %02X\n"
+		    "Modulus length: %d\nModulus: \n",
+		    data[10], data[11], data[12], data[13], data[14],
+		    data[15], data[16], data[17], data[22], data[23],
+		    data[24], data[25], data[26], data[27], data[28],
+		    data[29], data[30], data[31], data[32], data[33],
+		    be32_to_cpu(*native_val)
+	    );
+
+	for (i = 0; i < 256; i++) {
+		str += sprintf(str, "%02X ", data[i + 39]);
+		if ((i + 1) % 16 == 0)
+			str += sprintf(str, "\n");
+	}
+	return str - buf;
+}
+
+static DEVICE_ATTR(pubek, S_IRUGO, show_pubek, NULL);
+
+#define CAP_VER_RESULT_SIZE 18
+static u8 cap_version[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 18,		/* length */
+	0, 0, 0, 101,		/* TPM_ORD_GetCapability */
+	0, 0, 0, 6,
+	0, 0, 0, 0
+};
+
+#define CAP_MANUFACTURER_RESULT_SIZE 18
+static u8 cap_manufacturer[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 22,		/* length */
+	0, 0, 0, 101,		/* TPM_ORD_GetCapability */
+	0, 0, 0, 5,
+	0, 0, 0, 4,
+	0, 0, 1, 3
+};
+
+static ssize_t show_caps(struct device *dev, char *buf)
+{
+	u8 data[READ_PUBEK_RESULT_SIZE];
+	ssize_t len;
+	char *str = buf;
+
+	struct tpm_chip *chip =
+	    pci_get_drvdata(container_of(dev, struct pci_dev, dev));
+	if (chip == NULL)
+		return -ENODEV;
+
+	memcpy(data, cap_manufacturer, sizeof(cap_manufacturer));
+
+	if ((len = tpm_transmit(chip, data, sizeof(data))) <
+	    CAP_MANUFACTURER_RESULT_SIZE)
+		return len;
+
+	str += sprintf(str, "Manufacturer: 0x%x\n",
+		       be32_to_cpu(*(data + 14)));
+
+	memcpy(data, cap_version, sizeof(cap_version));
+
+	if ((len = tpm_transmit(chip, data, sizeof(data))) <
+	    CAP_VER_RESULT_SIZE)
+		return len;
+
+	str +=
+	    sprintf(str, "TCG version: %d.%d\nFirmware version: %d.%d\n",
+		    (int) data[14], (int) data[15], (int) data[16],
+		    (int) data[17]);
+
+	return str - buf;
+}
+
+static DEVICE_ATTR(caps, S_IRUGO, show_caps, NULL);
+
+/*
+ * Device file system interface to the TPM
+ */
+int tpm_open(struct inode *inode, struct file *file)
+{
+	int rc = 0, minor = iminor(inode);
+	struct tpm_chip *chip = NULL, *pos;
+
+	spin_lock(&driver_lock);
+
+	list_for_each_entry(pos, &tpm_chip_list, list) {
+		if (pos->vendor->miscdev.minor == minor) {
+			chip = pos;
+			break;
+		}
+	}
+
+	if (chip == NULL) {
+		rc = -ENODEV;
+		goto err_out;
+	}
+
+	if (chip->num_opens) {
+		dev_dbg(&chip->pci_dev->dev,
+			"Another process owns this TPM\n");
+		rc = -EBUSY;
+		goto err_out;
+	}
+
+	chip->num_opens++;
+	pci_dev_get(chip->pci_dev);
+
+	spin_unlock(&driver_lock);
+
+	chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
+	if (chip->data_buffer == NULL) {
+		chip->num_opens--;
+		pci_dev_put(chip->pci_dev);
+		return -ENOMEM;
+	}
+
+	atomic_set(&chip->data_pending, 0);
+
+	file->private_data = chip;
+	return 0;
+
+err_out:
+	spin_unlock(&driver_lock);
+	return rc;
+}
+
+EXPORT_SYMBOL_GPL(tpm_open);
+
+int tpm_release(struct inode *inode, struct file *file)
+{
+	struct tpm_chip *chip = file->private_data;
+
+	spin_lock(&driver_lock);
+	chip->num_opens--;
+	down(&chip->timer_manipulation_mutex);
+	if (timer_pending(&chip->user_read_timer))
+		del_singleshot_timer_sync(&chip->user_read_timer);
+	else if (timer_pending(&chip->device_timer))
+		del_singleshot_timer_sync(&chip->device_timer);
+	up(&chip->timer_manipulation_mutex);
+	kfree(chip->data_buffer);
+	atomic_set(&chip->data_pending, 0);
+
+	pci_dev_put(chip->pci_dev);
+	file->private_data = NULL;
+	spin_unlock(&driver_lock);
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_release);
+
+ssize_t tpm_write(struct file * file, const char __user * buf,
+		  size_t size, loff_t * off)
+{
+	struct tpm_chip *chip = file->private_data;
+	int in_size = size, out_size;
+
+	/* cannot perform a write until the read has cleared
+	   either via tpm_read or a user_read_timer timeout */
+	while (atomic_read(&chip->data_pending) != 0) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(TPM_TIMEOUT);
+	}
+
+	down(&chip->buffer_mutex);
+
+	if (in_size > TPM_BUFSIZE)
+		in_size = TPM_BUFSIZE;
+
+	if (copy_from_user
+	    (chip->data_buffer, (void __user *) buf, in_size)) {
+		up(&chip->buffer_mutex);
+		return -EFAULT;
+	}
+
+	/* atomic tpm command send and result receive */
+	out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE);
+
+	atomic_set(&chip->data_pending, out_size);
+	up(&chip->buffer_mutex);
+
+	/* Set a timeout by which the reader must come claim the result */
+	down(&chip->timer_manipulation_mutex);
+	init_timer(&chip->user_read_timer);
+	chip->user_read_timer.function = user_reader_timeout;
+	chip->user_read_timer.data = (unsigned long) chip;
+	chip->user_read_timer.expires = jiffies + (60 * HZ);
+	add_timer(&chip->user_read_timer);
+	up(&chip->timer_manipulation_mutex);
+
+	return in_size;
+}
+
+EXPORT_SYMBOL_GPL(tpm_write);
+
+ssize_t tpm_read(struct file * file, char __user * buf,
+		 size_t size, loff_t * off)
+{
+	struct tpm_chip *chip = file->private_data;
+	int ret_size = -ENODATA;
+
+	if (atomic_read(&chip->data_pending) != 0) {	/* Result available */
+		down(&chip->timer_manipulation_mutex);
+		del_singleshot_timer_sync(&chip->user_read_timer);
+		up(&chip->timer_manipulation_mutex);
+
+		down(&chip->buffer_mutex);
+
+		ret_size = atomic_read(&chip->data_pending);
+		atomic_set(&chip->data_pending, 0);
+
+		if (ret_size == 0)	/* timeout just occurred */
+			ret_size = -ETIME;
+		else if (ret_size > 0) {	/* relay data */
+			if (size < ret_size)
+				ret_size = size;
+
+			if (copy_to_user((void __user *) buf,
+					 chip->data_buffer, ret_size)) {
+				ret_size = -EFAULT;
+			}
+		}
+		up(&chip->buffer_mutex);
+	}
+
+	return ret_size;
+}
+
+EXPORT_SYMBOL_GPL(tpm_read);
+
+void __devexit tpm_remove(struct pci_dev *pci_dev)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+
+	if (chip == NULL) {
+		dev_err(&pci_dev->dev, "No device data found\n");
+		return;
+	}
+
+	spin_lock(&driver_lock);
+
+	list_del(&chip->list);
+
+	pci_set_drvdata(pci_dev, NULL);
+	misc_deregister(&chip->vendor->miscdev);
+
+	device_remove_file(&pci_dev->dev, &dev_attr_pubek);
+	device_remove_file(&pci_dev->dev, &dev_attr_pcrs);
+	device_remove_file(&pci_dev->dev, &dev_attr_caps);
+
+	spin_unlock(&driver_lock);
+
+	pci_disable_device(pci_dev);
+
+	dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
+
+	kfree(chip);
+
+	pci_dev_put(pci_dev);
+}
+
+EXPORT_SYMBOL_GPL(tpm_remove);
+
+static u8 savestate[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 10,		/* blob length (in bytes) */
+	0, 0, 0, 152		/* TPM_ORD_SaveState */
+};
+
+/*
+ * We are about to suspend. Save the TPM state
+ * so that it can be restored.
+ */
+int tpm_pm_suspend(struct pci_dev *pci_dev, u32 pm_state)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+	if (chip == NULL)
+		return -ENODEV;
+
+	tpm_transmit(chip, savestate, sizeof(savestate));
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_pm_suspend);
+
+/*
+ * Resume from a power safe. The BIOS already restored
+ * the TPM state.
+ */
+int tpm_pm_resume(struct pci_dev *pci_dev)
+{
+	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+	if (chip == NULL)
+		return -ENODEV;
+
+	spin_lock(&driver_lock);
+	tpm_lpc_bus_init(pci_dev, chip->vendor->base);
+	spin_unlock(&driver_lock);
+
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_pm_resume);
+
+/*
+ * Called from tpm_<specific>.c probe function only for devices 
+ * the driver has determined it should claim.  Prior to calling
+ * this function the specific probe function has called pci_enable_device
+ * upon errant exit from this function specific probe function should call
+ * pci_disable_device
+ */
+int tpm_register_hardware(struct pci_dev *pci_dev,
+			  struct tpm_vendor_specific *entry)
+{
+	char devname[7];
+	struct tpm_chip *chip;
+	int i, j;
+
+	/* Driver specific per-device data */
+	chip = kmalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	memset(chip, 0, sizeof(struct tpm_chip));
+
+	init_MUTEX(&chip->buffer_mutex);
+	init_MUTEX(&chip->tpm_mutex);
+	init_MUTEX(&chip->timer_manipulation_mutex);
+	INIT_LIST_HEAD(&chip->list);
+
+	chip->vendor = entry;
+
+	chip->dev_num = -1;
+
+	for (i = 0; i < 32; i++)
+		for (j = 0; j < 8; j++)
+			if ((dev_mask[i] & (1 << j)) == 0) {
+				chip->dev_num = i * 32 + j;
+				dev_mask[i] |= 1 << j;
+				goto dev_num_search_complete;
+			}
+
+dev_num_search_complete:
+	if (chip->dev_num < 0) {
+		dev_err(&pci_dev->dev,
+			"No available tpm device numbers\n");
+		kfree(chip);
+		return -ENODEV;
+	} else if (chip->dev_num == 0)
+		chip->vendor->miscdev.minor = TPM_MINOR;
+	else
+		chip->vendor->miscdev.minor = MISC_DYNAMIC_MINOR;
+
+	snprintf(devname, sizeof(devname), "%s%d", "tpm", chip->dev_num);
+	chip->vendor->miscdev.name = devname;
+
+	chip->vendor->miscdev.dev = &(pci_dev->dev);
+	chip->pci_dev = pci_dev_get(pci_dev);
+
+	spin_lock(&driver_lock);
+
+	if (misc_register(&chip->vendor->miscdev)) {
+		dev_err(&chip->pci_dev->dev,
+			"unable to misc_register %s, minor %d\n",
+			chip->vendor->miscdev.name,
+			chip->vendor->miscdev.minor);
+		pci_dev_put(pci_dev);
+		spin_unlock(&driver_lock);
+		kfree(chip);
+		dev_mask[i] &= !(1 << j);
+		return -ENODEV;
+	}
+
+	pci_set_drvdata(pci_dev, chip);
+
+	list_add(&chip->list, &tpm_chip_list);
+
+	device_create_file(&pci_dev->dev, &dev_attr_pubek);
+	device_create_file(&pci_dev->dev, &dev_attr_pcrs);
+	device_create_file(&pci_dev->dev, &dev_attr_caps);
+
+	spin_unlock(&driver_lock);
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_register_hardware);
+
+static int __init init_tpm(void)
+{
+	return 0;
+}
+
+static void __exit cleanup_tpm(void)
+{
+
+}
+
+module_init(init_tpm);
+module_exit(cleanup_tpm);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.h linux-2.6.10-tpm/drivers/char/tpm/tpm.h
--- linux-2.6.10/drivers/char/tpm/tpm.h	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.h	2004-12-21 13:35:36.000000000 -0600
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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/module.h>
+#include <linux/version.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/miscdevice.h>
+
+#define TPM_TIMEOUT msecs_to_jiffies(5)
+
+/* TPM addresses */
+#define	TPM_ADDR			0x4E
+#define	TPM_DATA			0x4F
+
+struct tpm_chip;
+
+struct tpm_vendor_specific {
+	u8 req_complete_mask;
+	u8 req_complete_val;
+	u16 base;		/* TPM base address */
+
+	int (*recv) (struct tpm_chip *, u8 *, size_t);
+	int (*send) (struct tpm_chip *, u8 *, size_t);
+	void (*cancel) (struct tpm_chip *);
+	struct miscdevice miscdev;
+};
+
+struct tpm_chip {
+	struct pci_dev *pci_dev;	/* PCI device stuff */
+
+	int dev_num;		/* /dev/tpm# */
+	int num_opens;		/* only one allowed */
+	int time_expired;
+
+	/* Data passed to and from the tpm via the read/write calls */
+	u8 *data_buffer;
+	atomic_t data_pending;
+	struct semaphore buffer_mutex;
+
+	struct timer_list user_read_timer;	/* user needs to claim result */
+	struct semaphore tpm_mutex;	/* tpm is processing */
+	struct timer_list device_timer;	/* tpm is processing */
+	struct semaphore timer_manipulation_mutex;
+
+	struct tpm_vendor_specific *vendor;
+
+	struct list_head list;
+};
+
+static inline int tpm_read_index(int index)
+{
+	outb(index, TPM_ADDR);
+	return inb(TPM_DATA) & 0xFF;
+}
+
+static inline void tpm_write_index(int index, int value)
+{
+	outb(index, TPM_ADDR);
+	outb(value & 0xFF, TPM_DATA);
+}
+
+extern void tpm_time_expired(unsigned long);
+extern int tpm_lpc_bus_init(struct pci_dev *, u16);
+
+extern int tpm_register_hardware(struct pci_dev *,
+				 struct tpm_vendor_specific *);
+extern int tpm_open(struct inode *, struct file *);
+extern int tpm_release(struct inode *, struct file *);
+extern ssize_t tpm_write(struct file *, const char __user *, size_t,
+			 loff_t *);
+extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *);
+extern void __devexit tpm_remove(struct pci_dev *);
+extern int tpm_pm_suspend(struct pci_dev *, u32);
+extern int tpm_pm_resume(struct pci_dev *);
diff -uprN linux-2.6.10/drivers/char/tpm/tpm_nsc.c linux-2.6.10-tpm/drivers/char/tpm/tpm_nsc.c
--- linux-2.6.10/drivers/char/tpm/tpm_nsc.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm_nsc.c	2004-12-21 13:35:46.000000000 -0600
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org	 
+ *
+ * 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"
+
+/* National definitions */
+#define	TPM_NSC_BASE			0x360
+#define	TPM_NSC_IRQ			0x07
+
+#define	NSC_LDN_INDEX			0x07
+#define	NSC_SID_INDEX			0x20
+#define	NSC_LDC_INDEX			0x30
+#define	NSC_DIO_INDEX			0x60
+#define	NSC_CIO_INDEX			0x62
+#define	NSC_IRQ_INDEX			0x70
+#define	NSC_ITS_INDEX			0x71
+
+#define	NSC_STATUS			0x01
+#define	NSC_COMMAND			0x01
+#define	NSC_DATA			0x00
+
+/* status bits */
+#define	NSC_STATUS_OBF			0x01	/* output buffer full */
+#define	NSC_STATUS_IBF			0x02	/* input buffer full */
+#define	NSC_STATUS_F0			0x04	/* F0 */
+#define	NSC_STATUS_A2			0x08	/* A2 */
+#define	NSC_STATUS_RDY			0x10	/* ready to receive command */
+#define	NSC_STATUS_IBR			0x20	/* ready to receive data */
+
+/* command bits */
+#define	NSC_COMMAND_NORMAL		0x01	/* normal mode */
+#define	NSC_COMMAND_EOC			0x03
+#define	NSC_COMMAND_CANCEL		0x22
+
+/*
+ * Wait for a certain status to appear
+ */
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
+{
+	int expired = 0;
+	struct timer_list status_timer =
+	    TIMER_INITIALIZER(tpm_time_expired, jiffies + 10 * HZ,
+			      (unsigned long) &expired);
+
+	/* status immediately available check */
+	*data = inb(chip->vendor->base + NSC_STATUS);
+	if ((*data & mask) == val)
+		return 0;
+
+	/* wait for status */
+	add_timer(&status_timer);
+	do {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(TPM_TIMEOUT);
+		*data = inb(chip->vendor->base + 1);
+		if ((*data & mask) == val) {
+			del_singleshot_timer_sync(&status_timer);
+			return 0;
+		}
+	}
+	while (!expired);
+
+	return -EBUSY;
+}
+
+static int nsc_wait_for_ready(struct tpm_chip *chip)
+{
+	int status;
+	int expired = 0;
+	struct timer_list status_timer =
+	    TIMER_INITIALIZER(tpm_time_expired, jiffies + 100,
+			      (unsigned long) &expired);
+
+	/* status immediately available check */
+	status = inb(chip->vendor->base + NSC_STATUS);
+	if (status & NSC_STATUS_OBF)
+		status = inb(chip->vendor->base + NSC_DATA);
+	if (status & NSC_STATUS_RDY)
+		return 0;
+
+	/* wait for status */
+	add_timer(&status_timer);
+	do {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(TPM_TIMEOUT);
+		status = inb(chip->vendor->base + NSC_STATUS);
+		if (status & NSC_STATUS_OBF)
+			status = inb(chip->vendor->base + NSC_DATA);
+		if (status & NSC_STATUS_RDY) {
+			del_singleshot_timer_sync(&status_timer);
+			return 0;
+		}
+	}
+	while (!expired);
+
+	dev_info(&chip->pci_dev->dev, "wait for ready failed\n");
+	return -EBUSY;
+}
+
+
+static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 *buffer = buf;
+	u8 data, *p;
+	u32 size;
+	__be32 *native_size;
+
+	if (count < 6)
+		return -EIO;
+
+	if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "F0 timeout\n");
+		return -EIO;
+	}
+	if ((data =
+	     inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
+		dev_err(&chip->pci_dev->dev, "not in normal mode (0x%x)\n",
+			data);
+		return -EIO;
+	}
+
+	/* read the whole packet */
+	for (p = buffer; p < &buffer[count]; p++) {
+		if (wait_for_stat
+		    (chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) {
+			dev_err(&chip->pci_dev->dev,
+				"OBF timeout (while reading data)\n");
+			return -EIO;
+		}
+		if (data & NSC_STATUS_F0)
+			break;
+		*p = inb(chip->vendor->base + NSC_DATA);
+	}
+
+	if ((data & NSC_STATUS_F0) == 0) {
+		dev_err(&chip->pci_dev->dev, "F0 not set\n");
+		return -EIO;
+	}
+	if ((data = inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_EOC) {
+		dev_err(&chip->pci_dev->dev,
+			"expected end of command(0x%x)\n", data);
+		return -EIO;
+	}
+
+	native_size = (__force __be32 *) (buf + 2);
+	size = be32_to_cpu(*native_size);
+
+	if (count < size)
+		return -EIO;
+
+	return size;
+}
+
+static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+	u8 data;
+	int i;
+
+	/*
+	 * If we hit the chip with back to back commands it locks up
+	 * and never set IBF. Hitting it with this "hammer" seems to
+	 * fix it. Not sure why this is needed, we followed the flow
+	 * chart in the manual to the letter.
+	 */
+	outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND);
+
+	if (nsc_wait_for_ready(chip) != 0)
+		return -EIO;
+
+	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+		return -EIO;
+	}
+
+	outb(NSC_COMMAND_NORMAL, chip->vendor->base + NSC_COMMAND);
+	if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBR timeout\n");
+		return -EIO;
+	}
+
+	for (i = 0; i < count; i++) {
+		if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+			dev_err(&chip->pci_dev->dev,
+				"IBF timeout (while writing data)\n");
+			return -EIO;
+		}
+		outb(buf[i], chip->vendor->base + NSC_DATA);
+	}
+
+	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+		return -EIO;
+	}
+	outb(NSC_COMMAND_EOC, chip->vendor->base + NSC_COMMAND);
+
+	return count;
+}
+
+static void tpm_nsc_cancel(struct tpm_chip *chip)
+{
+	outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND);
+}
+
+static struct file_operations nsc_ops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.open = tpm_open,
+	.read = tpm_read,
+	.write = tpm_write,
+	.release = tpm_release,
+};
+
+static struct tpm_vendor_specific tpm_nsc = {
+	.recv = tpm_nsc_recv,
+	.send = tpm_nsc_send,
+	.cancel = tpm_nsc_cancel,
+	.req_complete_mask = NSC_STATUS_OBF,
+	.req_complete_val = NSC_STATUS_OBF,
+	.base = TPM_NSC_BASE,
+	.miscdev.fops = &nsc_ops,
+};
+
+static int __devinit tpm_nsc_init(struct pci_dev *pci_dev,
+				  const struct pci_device_id *pci_id)
+{
+	int rc = 0;
+
+	if (pci_enable_device(pci_dev))
+		return -EIO;
+
+	if (tpm_lpc_bus_init(pci_dev, TPM_NSC_BASE)) {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	/* verify that it is a National part (SID) */
+	if (tpm_read_index(NSC_SID_INDEX) != 0xEF) {
+		rc = -ENODEV;
+		goto out_err;
+	}
+
+	dev_dbg(&pci_dev->dev, "NSC TPM detected\n");
+	dev_dbg(&pci_dev->dev,
+		"NSC LDN 0x%x, SID 0x%x, SRID 0x%x\n",
+		tpm_read_index(0x07), tpm_read_index(0x20),
+		tpm_read_index(0x27));
+	dev_dbg(&pci_dev->dev,
+		"NSC SIOCF1 0x%x SIOCF5 0x%x SIOCF6 0x%x SIOCF8 0x%x\n",
+		tpm_read_index(0x21), tpm_read_index(0x25),
+		tpm_read_index(0x26), tpm_read_index(0x28));
+	dev_dbg(&pci_dev->dev, "NSC IO Base0 0x%x\n",
+		(tpm_read_index(0x60) << 8) | tpm_read_index(0x61));
+	dev_dbg(&pci_dev->dev, "NSC IO Base1 0x%x\n",
+		(tpm_read_index(0x62) << 8) | tpm_read_index(0x63));
+	dev_dbg(&pci_dev->dev, "NSC Interrupt number and wakeup 0x%x\n",
+		tpm_read_index(0x70));
+	dev_dbg(&pci_dev->dev, "NSC IRQ type select 0x%x\n",
+		tpm_read_index(0x71));
+	dev_dbg(&pci_dev->dev,
+		"NSC DMA channel select0 0x%x, select1 0x%x\n",
+		tpm_read_index(0x74), tpm_read_index(0x75));
+	dev_dbg(&pci_dev->dev,
+		"NSC Config "
+		"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+		tpm_read_index(0xF0), tpm_read_index(0xF1),
+		tpm_read_index(0xF2), tpm_read_index(0xF3),
+		tpm_read_index(0xF4), tpm_read_index(0xF5),
+		tpm_read_index(0xF6), tpm_read_index(0xF7),
+		tpm_read_index(0xF8), tpm_read_index(0xF9));
+
+	dev_info(&pci_dev->dev,
+		 "NSC PC21100 TPM revision %d\n",
+		 tpm_read_index(0x27) & 0x1F);
+
+	if (tpm_read_index(NSC_LDC_INDEX) == 0)
+		dev_info(&pci_dev->dev, ": NSC TPM not active\n");
+
+	/* select PM channel 1 */
+	tpm_write_index(NSC_LDN_INDEX, 0x12);
+	tpm_read_index(NSC_LDN_INDEX);
+
+	/* disable the DPM module */
+	tpm_write_index(NSC_LDC_INDEX, 0);
+	tpm_read_index(NSC_LDC_INDEX);
+
+	/* set the data register base addresses */
+	tpm_write_index(NSC_DIO_INDEX, TPM_NSC_BASE >> 8);
+	tpm_write_index(NSC_DIO_INDEX + 1, TPM_NSC_BASE);
+	tpm_read_index(NSC_DIO_INDEX);
+	tpm_read_index(NSC_DIO_INDEX + 1);
+
+	/* set the command register base addresses */
+	tpm_write_index(NSC_CIO_INDEX, (TPM_NSC_BASE + 1) >> 8);
+	tpm_write_index(NSC_CIO_INDEX + 1, (TPM_NSC_BASE + 1));
+	tpm_read_index(NSC_DIO_INDEX);
+	tpm_read_index(NSC_DIO_INDEX + 1);
+
+	/* set the interrupt number to be used for the host interface */
+	tpm_write_index(NSC_IRQ_INDEX, TPM_NSC_IRQ);
+	tpm_write_index(NSC_ITS_INDEX, 0x00);
+	tpm_read_index(NSC_IRQ_INDEX);
+
+	/* enable the DPM module */
+	tpm_write_index(NSC_LDC_INDEX, 0x01);
+	tpm_read_index(NSC_LDC_INDEX);
+
+	if ((rc = tpm_register_hardware(pci_dev, &tpm_nsc)) < 0)
+		goto out_err;
+
+	return 0;
+
+out_err:
+	pci_disable_device(pci_dev);
+	return rc;
+}
+
+static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
+	{PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
+
+static struct pci_driver nsc_pci_driver = {
+	.name = "tpm_nsc",
+	.id_table = tpm_pci_tbl,
+	.probe = tpm_nsc_init,
+	.remove = __devexit_p(tpm_remove),
+	.suspend = tpm_pm_suspend,
+	.resume = tpm_pm_resume,
+};
+
+static int __init init_nsc(void)
+{
+	return pci_register_driver(&nsc_pci_driver);
+}
+
+static void __exit cleanup_nsc(void)
+{
+	pci_unregister_driver(&nsc_pci_driver);
+}
+
+module_init(init_nsc);
+module_exit(cleanup_nsc);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff -uprN linux-2.6.10/include/linux/pci_ids.h linux-2.6.10-tpm/include/linux/pci_ids.h
--- linux-2.6.10/include/linux/pci_ids.h	2004-12-24 15:35:50.000000000 -0600
+++ linux-2.6.10-tpm/include/linux/pci_ids.h	2005-01-07 13:34:28.000000000 -0600
@@ -495,6 +495,7 @@
 #define PCI_DEVICE_ID_AMD_OPUS_7449	0x7449
 #	define PCI_DEVICE_ID_AMD_VIPER_7449	PCI_DEVICE_ID_AMD_OPUS_7449
 #define PCI_DEVICE_ID_AMD_8111_LAN	0x7462
+#define PCI_DEVICE_ID_AMD_8111_LPC	0x7468
 #define PCI_DEVICE_ID_AMD_8111_IDE	0x7469
 #define PCI_DEVICE_ID_AMD_8111_AUDIO	0x746d
 #define PCI_DEVICE_ID_AMD_8151_0	0x7454

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] driver: Tpm hardware enablement --updated version
  2005-01-12 18:45           ` Kylene Hall
@ 2005-01-12 23:28             ` Greg KH
  2005-01-18 22:29             ` [PATCH 1/1] tpm: fix cause of SMP stack traces Kylene Hall
  1 sibling, 0 replies; 43+ messages in thread
From: Greg KH @ 2005-01-12 23:28 UTC (permalink / raw)
  To: Kylene Hall; +Cc: linux-kernel, sailer, leendert, emilyr, toml, tpmdd-devel

On Wed, Jan 12, 2005 at 12:45:23PM -0600, Kylene Hall wrote:
> This patch is a device driver to enable new hardware.  The new hardware is
> the TPM chip as described by specifications at 
> <http://www.trustedcomputinggroup.org>.  The TPM chip will enable you to
> use hardware to securely store and protect your keys and personal data.
> To use the chip according to the specification, you will need the Trusted
> Software Stack (TSS) of which an implementation for Linux is available at:
> <http://sourceforge.net/projects/trousers>.

I've added this to my bk trees, and it should show up in the next -mm
release.  Lets see how that works out.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH 1/1] tpm: fix cause of SMP stack traces
  2005-01-12 18:45           ` Kylene Hall
  2005-01-12 23:28             ` Greg KH
@ 2005-01-18 22:29             ` Kylene Hall
  2005-01-18 22:37               ` Chris Wright
                                 ` (2 more replies)
  1 sibling, 3 replies; 43+ messages in thread
From: Kylene Hall @ 2005-01-18 22:29 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, emilyr, toml, tpmdd-devel

There were misplaced spinlock acquires and releases in the probe, open, 
close and release paths which were causing might_sleep and schedule while 
atomic error messages accompanied by stack traces when the kernel was 
compiled with SMP support. Bug reported by Reben Jenster 
<ruben@hotheads.de>

Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.c linux-2.6.10-tpm/drivers/char/tpm/tpm.c
--- linux-2.6.10/drivers/char/tpm/tpm.c	2005-01-18 16:42:17.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.c	2005-01-18 12:52:53.000000000 -0600
@@ -373,8 +372,9 @@ int tpm_open(struct inode *inode, struct
 {
 	int rc = 0, minor = iminor(inode);
 	struct tpm_chip *chip = NULL, *pos;
+	unsigned long flags;
 
-	spin_lock(&driver_lock);
+	spin_lock_irqsave(&driver_lock, flags);
 
 	list_for_each_entry(pos, &tpm_chip_list, list) {
 		if (pos->vendor->miscdev.minor == minor) {
@@ -398,7 +398,7 @@ int tpm_open(struct inode *inode, struct
 	chip->num_opens++;
 	pci_dev_get(chip->pci_dev);
 
-	spin_unlock(&driver_lock);
+	spin_unlock_irqrestore(&driver_lock, flags);
 
 	chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
 	if (chip->data_buffer == NULL) {
@@ -413,7 +413,7 @@ int tpm_open(struct inode *inode, struct
 	return 0;
 
 err_out:
-	spin_unlock(&driver_lock);
+	spin_unlock_irqrestore(&driver_lock, flags);
 	return rc;
 }
 
@@ -422,21 +422,25 @@ EXPORT_SYMBOL_GPL(tpm_open);
 int tpm_release(struct inode *inode, struct file *file)
 {
 	struct tpm_chip *chip = file->private_data;
+	unsigned long flags;
 
-	spin_lock(&driver_lock);
+	file->private_data = NULL;
+
+	spin_lock_irqsave(&driver_lock, flags);
 	chip->num_opens--;
+	spin_unlock_irqrestore(&driver_lock, flags);
+
 	down(&chip->timer_manipulation_mutex);
 	if (timer_pending(&chip->user_read_timer))
 		del_singleshot_timer_sync(&chip->user_read_timer);
 	else if (timer_pending(&chip->device_timer))
 		del_singleshot_timer_sync(&chip->device_timer);
 	up(&chip->timer_manipulation_mutex);
+
 	kfree(chip->data_buffer);
 	atomic_set(&chip->data_pending, 0);
 
 	pci_dev_put(chip->pci_dev);
-	file->private_data = NULL;
-	spin_unlock(&driver_lock);
 	return 0;
 }
 
@@ -524,16 +528,19 @@ EXPORT_SYMBOL_GPL(tpm_read);
 void __devexit tpm_remove(struct pci_dev *pci_dev)
 {
 	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+	unsigned long flags;
 
 	if (chip == NULL) {
 		dev_err(&pci_dev->dev, "No device data found\n");
 		return;
 	}
 
-	spin_lock(&driver_lock);
+	spin_lock_irqsave(&driver_lock, flags);
 
 	list_del(&chip->list);
 
+	spin_unlock_irqrestore(&driver_lock, flags);
+
 	pci_set_drvdata(pci_dev, NULL);
 	misc_deregister(&chip->vendor->miscdev);
 
@@ -541,8 +548,6 @@ void __devexit tpm_remove(struct pci_dev
 	device_remove_file(&pci_dev->dev, &dev_attr_pcrs);
 	device_remove_file(&pci_dev->dev, &dev_attr_caps);
 
-	spin_unlock(&driver_lock);
-
 	pci_disable_device(pci_dev);
 
 	dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
@@ -583,12 +588,14 @@ EXPORT_SYMBOL_GPL(tpm_pm_suspend);
 int tpm_pm_resume(struct pci_dev *pci_dev)
 {
 	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+	unsigned long flags;
+
 	if (chip == NULL)
 		return -ENODEV;
 
-	spin_lock(&driver_lock);
+	spin_lock_irqsave(&driver_lock, flags);
 	tpm_lpc_bus_init(pci_dev, chip->vendor->base);
-	spin_unlock(&driver_lock);
+	spin_unlock_irqrestore(&driver_lock, flags);
 
 	return 0;
 }
@@ -650,15 +657,12 @@ dev_num_search_complete:
 	chip->vendor->miscdev.dev = &(pci_dev->dev);
 	chip->pci_dev = pci_dev_get(pci_dev);
 
-	spin_lock(&driver_lock);
-
 	if (misc_register(&chip->vendor->miscdev)) {
 		dev_err(&chip->pci_dev->dev,
 			"unable to misc_register %s, minor %d\n",
 			chip->vendor->miscdev.name,
 			chip->vendor->miscdev.minor);
 		pci_dev_put(pci_dev);
-		spin_unlock(&driver_lock);
 		kfree(chip);
 		dev_mask[i] &= !(1 << j);
 		return -ENODEV;
@@ -672,7 +676,6 @@ dev_num_search_complete:
 	device_create_file(&pci_dev->dev, &dev_attr_pcrs);
 	device_create_file(&pci_dev->dev, &dev_attr_caps);
 
-	spin_unlock(&driver_lock);
 	return 0;
 }
 

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] tpm: fix cause of SMP stack traces
  2005-01-18 22:29             ` [PATCH 1/1] tpm: fix cause of SMP stack traces Kylene Hall
@ 2005-01-18 22:37               ` Chris Wright
  2005-01-18 22:44                 ` Kylene Hall
  2005-01-18 22:47               ` Greg KH
  2005-01-18 23:39               ` [PATCH 1/1] tpm: fix cause of SMP stack traces -- updated version Kylene Hall
  2 siblings, 1 reply; 43+ messages in thread
From: Chris Wright @ 2005-01-18 22:37 UTC (permalink / raw)
  To: Kylene Hall; +Cc: linux-kernel, greg, emilyr, toml, tpmdd-devel

* Kylene Hall (kjhall@us.ibm.com) wrote:
> There were misplaced spinlock acquires and releases in the probe, open, 
> close and release paths which were causing might_sleep and schedule while 
> atomic error messages accompanied by stack traces when the kernel was 
> compiled with SMP support. Bug reported by Reben Jenster 
> <ruben@hotheads.de>
> 
> Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
> ---
> diff -uprN linux-2.6.10/drivers/char/tpm/tpm.c linux-2.6.10-tpm/drivers/char/tpm/tpm.c
> --- linux-2.6.10/drivers/char/tpm/tpm.c	2005-01-18 16:42:17.000000000 -0600
> +++ linux-2.6.10-tpm/drivers/char/tpm/tpm.c	2005-01-18 12:52:53.000000000 -0600
> @@ -373,8 +372,9 @@ int tpm_open(struct inode *inode, struct
>  {
>  	int rc = 0, minor = iminor(inode);
>  	struct tpm_chip *chip = NULL, *pos;
> +	unsigned long flags;
>  
> -	spin_lock(&driver_lock);
> +	spin_lock_irqsave(&driver_lock, flags);

Hmm, unless I'm missing something, this is only worse (for might sleep
warnings).  Now you've disabled irq's too.

thanks,
-chris
-- 
Linux Security Modules     http://lsm.immunix.org     http://lsm.bkbits.net

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] tpm: fix cause of SMP stack traces
  2005-01-18 22:37               ` Chris Wright
@ 2005-01-18 22:44                 ` Kylene Hall
  2005-01-18 22:47                   ` Chris Wright
  0 siblings, 1 reply; 43+ messages in thread
From: Kylene Hall @ 2005-01-18 22:44 UTC (permalink / raw)
  To: Chris Wright; +Cc: linux-kernel, greg, Emily Ratliff, Tom Lendacky, tpmdd-devel

On Tue, 2005-01-18 at 16:37, Chris Wright wrote:
> * Kylene Hall (kjhall@us.ibm.com) wrote:
> > There were misplaced spinlock acquires and releases in the probe, open, 
> > close and release paths which were causing might_sleep and schedule while 
> > atomic error messages accompanied by stack traces when the kernel was 
> > compiled with SMP support. Bug reported by Reben Jenster 
> > <ruben@hotheads.de>
> > 
> > Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
> > ---
> > diff -uprN linux-2.6.10/drivers/char/tpm/tpm.c linux-2.6.10-tpm/drivers/char/tpm/tpm.c
> > --- linux-2.6.10/drivers/char/tpm/tpm.c	2005-01-18 16:42:17.000000000 -0600
> > +++ linux-2.6.10-tpm/drivers/char/tpm/tpm.c	2005-01-18 12:52:53.000000000 -0600
> > @@ -373,8 +372,9 @@ int tpm_open(struct inode *inode, struct
> >  {
> >  	int rc = 0, minor = iminor(inode);
> >  	struct tpm_chip *chip = NULL, *pos;
> > +	unsigned long flags;
> >  
> > -	spin_lock(&driver_lock);
> > +	spin_lock_irqsave(&driver_lock, flags);
> 
> Hmm, unless I'm missing something, this is only worse (for might sleep
> warnings).  Now you've disabled irq's too.

I actually had to move the location of some of the locks to remove the
might sleep warnings.  Since I didn't know much about the might sleep
warnings before, my first course of action was to try using the disable
irq mechanism and I went ahead and just left them in once it was working
with the new lock placements.  I assume you believe they shouldn't be
necessary at all?

Thanks,
Kylie   

> 
> thanks,
> -chris


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] tpm: fix cause of SMP stack traces
  2005-01-18 22:44                 ` Kylene Hall
@ 2005-01-18 22:47                   ` Chris Wright
  0 siblings, 0 replies; 43+ messages in thread
From: Chris Wright @ 2005-01-18 22:47 UTC (permalink / raw)
  To: Kylene Hall
  Cc: Chris Wright, linux-kernel, greg, Emily Ratliff, Tom Lendacky,
	tpmdd-devel

* Kylene Hall (kjhall@us.ibm.com) wrote:
> I actually had to move the location of some of the locks to remove the
> might sleep warnings.

Ah, that sounds like the proper fix.

> Since I didn't know much about the might sleep
> warnings before, my first course of action was to try using the disable
> irq mechanism and I went ahead and just left them in once it was working
> with the new lock placements.  I assume you believe they shouldn't be
> necessary at all?

Right.  Unless you're taking same spin_lock in irq context (which I
didn't recall you were doing).

thanks,
-chris
-- 
Linux Security Modules     http://lsm.immunix.org     http://lsm.bkbits.net

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] tpm: fix cause of SMP stack traces
  2005-01-18 22:29             ` [PATCH 1/1] tpm: fix cause of SMP stack traces Kylene Hall
  2005-01-18 22:37               ` Chris Wright
@ 2005-01-18 22:47               ` Greg KH
  2005-01-18 23:07                 ` Kylene Hall
  2005-01-18 23:39               ` [PATCH 1/1] tpm: fix cause of SMP stack traces -- updated version Kylene Hall
  2 siblings, 1 reply; 43+ messages in thread
From: Greg KH @ 2005-01-18 22:47 UTC (permalink / raw)
  To: Kylene Hall; +Cc: linux-kernel, emilyr, toml, tpmdd-devel

On Tue, Jan 18, 2005 at 04:29:23PM -0600, Kylene Hall wrote:
> There were misplaced spinlock acquires and releases in the probe, open, 
> close and release paths which were causing might_sleep and schedule while 
> atomic error messages accompanied by stack traces when the kernel was 
> compiled with SMP support. Bug reported by Reben Jenster 
> <ruben@hotheads.de>

Where exactly where the trace errors coming from?  I don't see anything
in the open path that might have caused it.

Anyway, Chris is right, just changing this to _irqsave will not fix the
issue.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] tpm: fix cause of SMP stack traces
  2005-01-18 22:47               ` Greg KH
@ 2005-01-18 23:07                 ` Kylene Hall
  0 siblings, 0 replies; 43+ messages in thread
From: Kylene Hall @ 2005-01-18 23:07 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, Emily Ratliff, Tom Lendacky, tpmdd-devel

On Tue, 2005-01-18 at 16:47, Greg KH wrote:
> On Tue, Jan 18, 2005 at 04:29:23PM -0600, Kylene Hall wrote:
> > There were misplaced spinlock acquires and releases in the probe, open, 
> > close and release paths which were causing might_sleep and schedule while 
> > atomic error messages accompanied by stack traces when the kernel was 
> > compiled with SMP support. Bug reported by Reben Jenster 
> > <ruben@hotheads.de>
> 
> Where exactly where the trace errors coming from?  I don't see anything
> in the open path that might have caused it.
> 
True the open path was not affected.
> Anyway, Chris is right, just changing this to _irqsave will not fix the
> issue.
Fixing will reissue the patch momentarily.
> 
> thanks,
> 
> greg k-h
> 


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] tpm: fix cause of SMP stack traces -- updated version
  2005-01-18 22:29             ` [PATCH 1/1] tpm: fix cause of SMP stack traces Kylene Hall
  2005-01-18 22:37               ` Chris Wright
  2005-01-18 22:47               ` Greg KH
@ 2005-01-18 23:39               ` Kylene Hall
  2005-01-28 21:45                 ` [PATCH 1/1] tpm: insert missing up mutex in an error path Kylene Hall
  2005-02-01  8:28                 ` [PATCH 1/1] tpm: fix cause of SMP stack traces -- " Greg KH
  2 siblings, 2 replies; 43+ messages in thread
From: Kylene Hall @ 2005-01-18 23:39 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, emilyr, toml, tpmdd-devel

There were misplaced spinlock acquires and releases in the probe, close and release 
paths which were causing might_sleep and schedule while atomic error messages accompanied 
by stack traces when the kernel was compiled with SMP support. Bug reported by Reben Jenster 
<ruben@hotheads.de>

Thanks,
Kylie
 
Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.c linux-2.6.10-tpm/drivers/char/tpm/tpm.c
--- linux-2.6.10/drivers/char/tpm/tpm.c	2005-01-18 18:10:16.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.c	2005-01-18 18:13:59.000000000 -0600
@@ -422,21 +421,24 @@ EXPORT_SYMBOL_GPL(tpm_open);
 int tpm_release(struct inode *inode, struct file *file)
 {
 	struct tpm_chip *chip = file->private_data;
+	
+	file->private_data = NULL;
 
 	spin_lock(&driver_lock);
 	chip->num_opens--;
+	spin_unlock(&driver_lock);
+
 	down(&chip->timer_manipulation_mutex);
 	if (timer_pending(&chip->user_read_timer))
 		del_singleshot_timer_sync(&chip->user_read_timer);
 	else if (timer_pending(&chip->device_timer))
 		del_singleshot_timer_sync(&chip->device_timer);
 	up(&chip->timer_manipulation_mutex);
+
 	kfree(chip->data_buffer);
 	atomic_set(&chip->data_pending, 0);
 
 	pci_dev_put(chip->pci_dev);
-	file->private_data = NULL;
-	spin_unlock(&driver_lock);
 	return 0;
 }
 
@@ -534,6 +536,8 @@ void __devexit tpm_remove(struct pci_dev
 
 	list_del(&chip->list);
 
+	spin_unlock(&driver_lock);
+
 	pci_set_drvdata(pci_dev, NULL);
 	misc_deregister(&chip->vendor->miscdev);
 
@@ -541,8 +545,6 @@ void __devexit tpm_remove(struct pci_dev
 	device_remove_file(&pci_dev->dev, &dev_attr_pcrs);
 	device_remove_file(&pci_dev->dev, &dev_attr_caps);
 
-	spin_unlock(&driver_lock);
-
 	pci_disable_device(pci_dev);
 
 	dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
@@ -583,6 +585,7 @@ EXPORT_SYMBOL_GPL(tpm_pm_suspend);
 int tpm_pm_resume(struct pci_dev *pci_dev)
 {
 	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+
 	if (chip == NULL)
 		return -ENODEV;
 
@@ -650,15 +653,12 @@ dev_num_search_complete:
 	chip->vendor->miscdev.dev = &(pci_dev->dev);
 	chip->pci_dev = pci_dev_get(pci_dev);
 
-	spin_lock(&driver_lock);
-
 	if (misc_register(&chip->vendor->miscdev)) {
 		dev_err(&chip->pci_dev->dev,
 			"unable to misc_register %s, minor %d\n",
 			chip->vendor->miscdev.name,
 			chip->vendor->miscdev.minor);
 		pci_dev_put(pci_dev);
-		spin_unlock(&driver_lock);
 		kfree(chip);
 		dev_mask[i] &= !(1 << j);
 		return -ENODEV;
@@ -672,7 +672,6 @@ dev_num_search_complete:
 	device_create_file(&pci_dev->dev, &dev_attr_pcrs);
 	device_create_file(&pci_dev->dev, &dev_attr_caps);
 
-	spin_unlock(&driver_lock);
 	return 0;
 }
 

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH 1/1] tpm: insert missing up mutex in an error path
  2005-01-18 23:39               ` [PATCH 1/1] tpm: fix cause of SMP stack traces -- updated version Kylene Hall
@ 2005-01-28 21:45                 ` Kylene Hall
  2005-01-31 19:27                   ` [PATCH 1/1] tpm: insert missing up mutex in an error path, typo build fix -- updated version Kylene Hall
  2005-02-01  8:28                 ` [PATCH 1/1] tpm: fix cause of SMP stack traces -- " Greg KH
  1 sibling, 1 reply; 43+ messages in thread
From: Kylene Hall @ 2005-01-28 21:45 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, emilyr, toml, tpmdd-devel

This patch puts in the missing up call on the tpm_mutex on an 
error condition in the tpm_transmit function.  Bug reported by Stefan 
Berger <stefanb@us.ibm.com>.  This patch also implements a new status 
function to handle future chip configurations which may generate status 
differntly. 

Thanks,
Kylie
  
Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---
diff -uprN linux-2.6.10/drivers/char/tpm/tpm_atmel.c linux-2.6.10-tpm/drivers/char/tpm/tpm_atmel.c
--- linux-2.6.10/drivers/char/tpm/tpm_atmel.c	2005-01-18 16:42:17.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm_atmel.c	2005-01-21 13:11:11.000000000 -0600
@@ -112,6 +112,11 @@ static void tpm_atml_cancel(struct tpm_c
 	outb(ATML_STATUS_ABORT, chip->vendor->base + 1);
 }
 
+static u8 tpm_atml_status(struct tpm_chip *chip)
+{
+	return inb( chip->vendor->base + 1);
+}
+
 static struct file_operations atmel_ops = {
 	.owner = THIS_MODULE,
 	.llseek = no_llseek,
@@ -125,6 +130,7 @@ static struct tpm_vendor_specific tpm_at
 	.recv = tpm_atml_recv,
 	.send = tpm_atml_send,
 	.cancel = tpm_atml_cancel,
+	.status = tpm_atml_status,
 	.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
 	.req_complete_val = ATML_STATUS_DATA_AVAIL,
 	.base = TPM_ATML_BASE,
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.c linux-2.6.10-tpm/drivers/char/tpm/tpm.c
--- linux-2.6.10/drivers/char/tpm/tpm.c	2005-01-21 12:53:26.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.c	2005-01-28 16:28:45.578493680 -0600
@@ -152,6 +151,7 @@ static ssize_t tpm_transmit(struct tpm_c
 	if ((len = chip->vendor->send(chip, (u8 *) buf, count)) < 0) {
 		dev_err(&chip->pci_dev->dev,
 			"tpm_transmit: tpm_send: error %d\n", len);
+		up(&chip->tpm_mutex);
 		return len;
 	}
 
@@ -165,7 +165,7 @@ static ssize_t tpm_transmit(struct tpm_c
 	up(&chip->timer_manipulation_mutex);
 
 	do {
-		u8 status = inb(chip->vendor->base + 1);
+		u8 status = chip->vendor->status(chip);
 		if ((status & chip->vendor->req_complete_mask) ==
 		    chip->vendor->req_complete_val) {
 			down(&chip->timer_manipulation_mutex);
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.h linux-2.6.10-tpm/drivers/char/tpm/tpm.h
--- linux-2.6.10/drivers/char/tpm/tpm.h	2005-01-18 16:42:17.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.h	2005-01-21 13:10:20.000000000 -0600
@@ -40,6 +40,7 @@ struct tpm_vendor_specific {
 	int (*recv) (struct tpm_chip *, u8 *, size_t);
 	int (*send) (struct tpm_chip *, u8 *, size_t);
 	void (*cancel) (struct tpm_chip *);
+	u8 (*status) (struct tpm_chip *);
 	struct miscdevice miscdev;
 };
 
diff -uprN linux-2.6.10/drivers/char/tpm/tpm_nsc.c linux-2.6.10-tpm/drivers/char/tpm/tpm_nsc.c
--- linux-2.6.10/drivers/char/tpm/tpm_nsc.c	2005-01-18 16:42:17.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm_nsc.c	2005-01-21 13:12:27.000000000 -0600
@@ -219,6 +219,12 @@ static void tpm_nsc_cancel(struct tpm_ch
 	outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND);
 }
 
+
+static u8 tpm_nsc_status(struct tpm_chip *chip)
+{
+	return inb(chip->vendor->base + NSC_STATUS);
+}
+
 static struct file_operations nsc_ops = {
 	.owner = THIS_MODULE,
 	.llseek = no_llseek,
@@ -232,6 +238,7 @@ static struct tpm_vendor_specific tpm_ns
 	.recv = tpm_nsc_recv,
 	.send = tpm_nsc_send,
 	.cancel = tpm_nsc_cancel,
+	.status = tpm_nsc_status,
 	.req_complete_mask = NSC_STATUS_OBF,
 	.req_complete_val = NSC_STATUS_OBF,
 	.base = TPM_NSC_BASE,

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] tpm: insert missing up mutex in an error path, typo build fix -- updated version
  2005-01-28 21:45                 ` [PATCH 1/1] tpm: insert missing up mutex in an error path Kylene Hall
@ 2005-01-31 19:27                   ` Kylene Hall
  2005-02-03 16:40                     ` [PATCH 1/1] tpm: remove pci specific stuff from the underlying generic driver Kylene Hall
  0 siblings, 1 reply; 43+ messages in thread
From: Kylene Hall @ 2005-01-31 19:27 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, emilyr, toml, tpmdd-devel

There is also a typo in the driver version in the 2.6.11-rc2-mm2 tree that 
is not fixed by the previous patch.  Please apply this upated version to 
fix the previously acknowledged problems and this typo causing a build 
error.

Thanks,
Kylie

On Fri, 28 Jan 2005, Kylene Hall wrote:

> This patch puts in the missing up call on the tpm_mutex on an 
> error condition in the tpm_transmit function.  Bug reported by Stefan 
> Berger <stefanb@us.ibm.com>.  This patch also implements a new status 
> function to handle future chip configurations which may generate status 
> differntly. 
> 
> Thanks,
> Kylie
>   

Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---
diff -uprN linux-2.6.10/drivers/char/tpm/tpm_atmel.c linux-2.6.10-tpm/drivers/char/tpm/tpm_atmel.c
--- linux-2.6.10/drivers/char/tpm/tpm_atmel.c	2005-01-18 16:42:17.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm_atmel.c	2005-01-21 13:11:11.000000000 -0600
@@ -112,6 +112,11 @@ static void tpm_atml_cancel(struct tpm_c
 	outb(ATML_STATUS_ABORT, chip->vendor->base + 1);
 }
 
+static u8 tpm_atml_status(struct tpm_chip *chip)
+{
+	return inb( chip->vendor->base + 1);
+}
+
 static struct file_operations atmel_ops = {
 	.owner = THIS_MODULE,
 	.llseek = no_llseek,
@@ -125,6 +130,7 @@ static struct tpm_vendor_specific tpm_at
 	.recv = tpm_atml_recv,
 	.send = tpm_atml_send,
 	.cancel = tpm_atml_cancel,
+	.status = tpm_atml_status,
 	.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
 	.req_complete_val = ATML_STATUS_DATA_AVAIL,
 	.base = TPM_ATML_BASE,
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.c linux-2.6.10-tpm/drivers/char/tpm/tpm.c
--- linux-2.6.10/drivers/char/tpm/tpm.c	2005-01-31 13:59:38.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.c	2005-01-28 16:28:45.000000000 -0600
@@ -152,6 +151,7 @@ static ssize_t tpm_transmit(struct tpm_c
 	if ((len = chip->vendor->send(chip, (u8 *) buf, count)) < 0) {
 		dev_err(&chip->pci_dev->dev,
 			"tpm_transmit: tpm_send: error %d\n", len);
+		up(&chip->tpm_mutex);
 		return len;
 	}
 
@@ -165,7 +165,7 @@ static ssize_t tpm_transmit(struct tpm_c
 	up(&chip->timer_manipulation_mutex);
 
 	do {
-		u8 status = inb(chip->vendor->base + 1);
+		u8 status = chip->vendor->status(chip);
 		if ((status & chip->vendor->req_complete_mask) ==
 		    chip->vendor->req_complete_val) {
 			down(&chip->timer_manipulation_mutex);
@@ -219,7 +219,7 @@ static ssize_t show_pcrs(struct device *
 	int i, j, index, num_pcrs;
 	char *str = buf;
 
-	struct tpm_chip *chp =
+	struct tpm_chip *chip =
 	    pci_get_drvdata(container_of(dev, struct pci_dev, dev));
 	if (chip == NULL)
 		return -ENODEV;
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.h linux-2.6.10-tpm/drivers/char/tpm/tpm.h
--- linux-2.6.10/drivers/char/tpm/tpm.h	2005-01-18 16:42:17.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.h	2005-01-21 13:10:20.000000000 -0600
@@ -40,6 +40,7 @@ struct tpm_vendor_specific {
 	int (*recv) (struct tpm_chip *, u8 *, size_t);
 	int (*send) (struct tpm_chip *, u8 *, size_t);
 	void (*cancel) (struct tpm_chip *);
+	u8 (*status) (struct tpm_chip *);
 	struct miscdevice miscdev;
 };
 
diff -uprN linux-2.6.10/drivers/char/tpm/tpm_nsc.c linux-2.6.10-tpm/drivers/char/tpm/tpm_nsc.c
--- linux-2.6.10/drivers/char/tpm/tpm_nsc.c	2005-01-18 16:42:17.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm_nsc.c	2005-01-21 13:12:27.000000000 -0600
@@ -219,6 +219,12 @@ static void tpm_nsc_cancel(struct tpm_ch
 	outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND);
 }
 
+
+static u8 tpm_nsc_status(struct tpm_chip *chip)
+{
+	return inb(chip->vendor->base + NSC_STATUS);
+}
+
 static struct file_operations nsc_ops = {
 	.owner = THIS_MODULE,
 	.llseek = no_llseek,
@@ -232,6 +238,7 @@ static struct tpm_vendor_specific tpm_ns
 	.recv = tpm_nsc_recv,
 	.send = tpm_nsc_send,
 	.cancel = tpm_nsc_cancel,
+	.status = tpm_nsc_status,
 	.req_complete_mask = NSC_STATUS_OBF,
 	.req_complete_val = NSC_STATUS_OBF,
 	.base = TPM_NSC_BASE,

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] tpm: fix cause of SMP stack traces -- updated version
  2005-01-18 23:39               ` [PATCH 1/1] tpm: fix cause of SMP stack traces -- updated version Kylene Hall
  2005-01-28 21:45                 ` [PATCH 1/1] tpm: insert missing up mutex in an error path Kylene Hall
@ 2005-02-01  8:28                 ` Greg KH
  1 sibling, 0 replies; 43+ messages in thread
From: Greg KH @ 2005-02-01  8:28 UTC (permalink / raw)
  To: Kylene Hall; +Cc: linux-kernel, emilyr, toml, tpmdd-devel

On Tue, Jan 18, 2005 at 05:39:53PM -0600, Kylene Hall wrote:
> There were misplaced spinlock acquires and releases in the probe, close and release 
> paths which were causing might_sleep and schedule while atomic error messages accompanied 
> by stack traces when the kernel was compiled with SMP support. Bug reported by Reben Jenster 
> <ruben@hotheads.de>
> 
> Thanks,
> Kylie
>  
> Signed-off-by: Kylene Hall <kjhall@us.ibm.com>

Applied, thanks.

greg k-h


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH 1/1] tpm: remove pci specific stuff from the underlying generic driver
  2005-01-31 19:27                   ` [PATCH 1/1] tpm: insert missing up mutex in an error path, typo build fix -- updated version Kylene Hall
@ 2005-02-03 16:40                     ` Kylene Hall
  2005-02-04 20:12                       ` [PATCH 1/1] tpm: implement use of sysfs classes Kylene Hall
  0 siblings, 1 reply; 43+ messages in thread
From: Kylene Hall @ 2005-02-03 16:40 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, emilyr, toml, tpmdd-devel

Since future versions of this chip might not be pci devices and the 
generic tpm driver does not need access to the pci related fields, 
I updated the structures and functions to use struct device and related functions 
rather than the pci equivalents.  This simplifies many things including 
dev_dbg and dev_err calls which were pulling the dev structure out of the 
pci_dev everytime.  Also pci calls to get and set driver data were merely 
pulling this structure out and passing it on as well.  Now only the vendor 
specific drivers (atmel and national at this time) will know about the pci 
device stuff and future non-pci chips can easily be connected to the 
architecture.

Thanks,
Kylie

Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---
diff -uprN linux-2.6.10/drivers/char/tpm/tpm_atmel.c linux-2.6.10-tpm/drivers/char/tpm/tpm_atmel.c
--- linux-2.6.10/drivers/char/tpm/tpm_atmel.c	2005-02-01 11:08:44.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm_atmel.c	2005-02-01 11:27:58.000000000 -0600
@@ -48,8 +48,7 @@ static int tpm_atml_recv(struct tpm_chip
 	for (i = 0; i < 6; i++) {
 		status = inb(chip->vendor->base + 1);
 		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
-			dev_err(&chip->pci_dev->dev,
-				"error reading header\n");
+			dev_err(chip->dev, "error reading header\n");
 			return -EIO;
 		}
 		*buf++ = inb(chip->vendor->base);
@@ -60,13 +59,12 @@ static int tpm_atml_recv(struct tpm_chip
 	size = be32_to_cpu(*native_size);
 
 	if (count < size) {
-		dev_err(&chip->pci_dev->dev,
+		dev_err(chip->dev,
 			"Recv size(%d) less than available space\n", size);
 		for (; i < size; i++) {	/* clear the waiting data anyway */
 			status = inb(chip->vendor->base + 1);
 			if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
-				dev_err(&chip->pci_dev->dev,
-					"error reading data\n");
+				dev_err(chip->dev, "error reading data\n");
 				return -EIO;
 			}
 		}
@@ -77,8 +75,7 @@ static int tpm_atml_recv(struct tpm_chip
 	for (; i < size; i++) {
 		status = inb(chip->vendor->base + 1);
 		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
-			dev_err(&chip->pci_dev->dev,
-				"error reading data\n");
+			dev_err(chip->dev, "error reading data\n");
 			return -EIO;
 		}
 		*buf++ = inb(chip->vendor->base);
@@ -87,7 +84,7 @@ static int tpm_atml_recv(struct tpm_chip
 	/* make sure data available is gone */
 	status = inb(chip->vendor->base + 1);
 	if (status & ATML_STATUS_DATA_AVAIL) {
-		dev_err(&chip->pci_dev->dev, "data available is stuck\n");
+		dev_err(chip->dev, "data available is stuck\n");
 		return -EIO;
 	}
 
@@ -98,9 +95,9 @@ static int tpm_atml_send(struct tpm_chip
 {
 	int i;
 
-	dev_dbg(&chip->pci_dev->dev, "tpm_atml_send: ");
+	dev_dbg(chip->dev, "tpm_atml_send: ");
 	for (i = 0; i < count; i++) {
-		dev_dbg(&chip->pci_dev->dev, "0x%x(%d) ", buf[i], buf[i]);
+		dev_dbg(chip->dev, "0x%x(%d) ", buf[i], buf[i]);
 		outb(buf[i], chip->vendor->base);
 	}
 
@@ -114,7 +111,7 @@ static void tpm_atml_cancel(struct tpm_c
 
 static u8 tpm_atml_status(struct tpm_chip *chip)
 {
-	return inb( chip->vendor->base + 1);
+	return inb(chip->vendor->base + 1);
 }
 
 static struct file_operations atmel_ops = {
@@ -169,7 +166,7 @@ static int __devinit tpm_atml_init(struc
 		goto out_err;
 	}
 
-	if ((rc = tpm_register_hardware(pci_dev, &tpm_atmel)) < 0)
+	if ((rc = tpm_register_hardware(&pci_dev->dev, &tpm_atmel)) < 0)
 		goto out_err;
 
 	dev_info(&pci_dev->dev,
@@ -182,6 +179,12 @@ out_err:
 	return rc;
 }
 
+static void __devexit tpm_atml_remove(struct pci_dev *pci_dev)
+{
+	tpm_remove_hardware(&pci_dev->dev);
+	pci_disable_device(pci_dev);
+}
+
 static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
@@ -198,7 +201,7 @@ static struct pci_driver atmel_pci_drive
 	.name = "tpm_atmel",
 	.id_table = tpm_pci_tbl,
 	.probe = tpm_atml_init,
-	.remove = __devexit_p(tpm_remove),
+	.remove = __devexit_p(tpm_atml_remove),
 	.suspend = tpm_pm_suspend,
 	.resume = tpm_pm_resume,
 };
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.c linux-2.6.10-tpm/drivers/char/tpm/tpm.c
--- linux-2.6.10/drivers/char/tpm/tpm.c	2005-02-01 11:08:44.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.c	2005-02-01 11:27:40.000000000 -0600
@@ -142,7 +141,7 @@ static ssize_t tpm_transmit(struct tpm_c
 	if (count == 0)
 		return -ENODATA;
 	if (count > bufsiz) {
-		dev_err(&chip->pci_dev->dev,
+		dev_err(chip->dev,
 			"invalid count value %x %x \n", count, bufsiz);
 		return -E2BIG;
 	}
@@ -150,7 +149,7 @@ static ssize_t tpm_transmit(struct tpm_c
 	down(&chip->tpm_mutex);
 
 	if ((len = chip->vendor->send(chip, (u8 *) buf, count)) < 0) {
-		dev_err(&chip->pci_dev->dev,
+		dev_err(chip->dev,
 			"tpm_transmit: tpm_send: error %d\n", len);
 		up(&chip->tpm_mutex);
 		return len;
@@ -181,14 +180,14 @@ static ssize_t tpm_transmit(struct tpm_c
 
 
 	chip->vendor->cancel(chip);
-	dev_err(&chip->pci_dev->dev, "Time expired\n");
+	dev_err(chip->dev, "Time expired\n");
 	up(&chip->tpm_mutex);
 	return -EIO;
 
 out_recv:
 	len = chip->vendor->recv(chip, (u8 *) buf, bufsiz);
 	if (len < 0)
-		dev_err(&chip->pci_dev->dev,
+		dev_err(chip->dev,
 			"tpm_transmit: tpm_recv: error %d\n", len);
 	up(&chip->tpm_mutex);
 	return len;
@@ -220,8 +219,7 @@ static ssize_t show_pcrs(struct device *
 	int i, j, index, num_pcrs;
 	char *str = buf;
 
-	struct tpm_chip *chip =
-	    pci_get_drvdata(container_of(dev, struct pci_dev, dev));
+	struct tpm_chip *chip = dev_get_drvdata(dev);
 	if (chip == NULL)
 		return -ENODEV;
 
@@ -264,8 +262,7 @@ static ssize_t show_pubek(struct device 
 	int i;
 	char *str = buf;
 
-	struct tpm_chip *chip =
-	    pci_get_drvdata(container_of(dev, struct pci_dev, dev));
+	struct tpm_chip *chip = dev_get_drvdata(dev);
 	if (chip == NULL)
 		return -ENODEV;
 
@@ -337,8 +334,7 @@ static ssize_t show_caps(struct device *
 	ssize_t len;
 	char *str = buf;
 
-	struct tpm_chip *chip =
-	    pci_get_drvdata(container_of(dev, struct pci_dev, dev));
+	struct tpm_chip *chip = dev_get_drvdata(dev);
 	if (chip == NULL)
 		return -ENODEV;
 
@@ -390,21 +386,20 @@ int tpm_open(struct inode *inode, struct
 	}
 
 	if (chip->num_opens) {
-		dev_dbg(&chip->pci_dev->dev,
-			"Another process owns this TPM\n");
+		dev_dbg(chip->dev, "Another process owns this TPM\n");
 		rc = -EBUSY;
 		goto err_out;
 	}
 
 	chip->num_opens++;
-	pci_dev_get(chip->pci_dev);
+	get_device(chip->dev);
 
 	spin_unlock(&driver_lock);
 
 	chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
 	if (chip->data_buffer == NULL) {
 		chip->num_opens--;
-		pci_dev_put(chip->pci_dev);
+		put_device(chip->dev);
 		return -ENOMEM;
 	}
 
@@ -423,7 +418,7 @@ EXPORT_SYMBOL_GPL(tpm_open);
 int tpm_release(struct inode *inode, struct file *file)
 {
 	struct tpm_chip *chip = file->private_data;
-	
+
 	file->private_data = NULL;
 
 	spin_lock(&driver_lock);
@@ -440,7 +435,7 @@ int tpm_release(struct inode *inode, str
 	kfree(chip->data_buffer);
 	atomic_set(&chip->data_pending, 0);
 
-	pci_dev_put(chip->pci_dev);
+	put_device(chip->dev);
 	return 0;
 }
 
@@ -525,12 +520,12 @@ ssize_t tpm_read(struct file * file, cha
 
 EXPORT_SYMBOL_GPL(tpm_read);
 
-void __devexit tpm_remove(struct pci_dev *pci_dev)
+void tpm_remove_hardware(struct device *dev)
 {
-	struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+	struct tpm_chip *chip = dev_get_drvdata(dev);
 
 	if (chip == NULL) {
-		dev_err(&pci_dev->dev, "No device data found\n");
+		dev_err(dev, "No device data found\n");
 		return;
 	}
 
@@ -540,23 +535,21 @@ void __devexit tpm_remove(struct pci_dev
 
 	spin_unlock(&driver_lock);
 
-	pci_set_drvdata(pci_dev, NULL);
+	dev_set_drvdata(dev, NULL);
 	misc_deregister(&chip->vendor->miscdev);
 
-	device_remove_file(&pci_dev->dev, &dev_attr_pubek);
-	device_remove_file(&pci_dev->dev, &dev_attr_pcrs);
-	device_remove_file(&pci_dev->dev, &dev_attr_caps);
-
-	pci_disable_device(pci_dev);
+	device_remove_file(dev, &dev_attr_pubek);
+	device_remove_file(dev, &dev_attr_pcrs);
+	device_remove_file(dev, &dev_attr_caps);
 
 	dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
 
 	kfree(chip);
 
-	pci_dev_put(pci_dev);
+	put_device(dev);
 }
 
-EXPORT_SYMBOL_GPL(tpm_remove);
+EXPORT_SYMBOL_GPL(tpm_remove_hardware);
 
 static u8 savestate[] = {
 	0, 193,			/* TPM_TAG_RQU_COMMAND */
@@ -607,7 +600,7 @@ EXPORT_SYMBOL_GPL(tpm_pm_resume);
  * upon errant exit from this function specific probe function should call
  * pci_disable_device
  */
-int tpm_register_hardware(struct pci_dev *pci_dev,
+int tpm_register_hardware(struct device *dev,
 			  struct tpm_vendor_specific *entry)
 {
 	char devname[7];
@@ -640,8 +633,7 @@ int tpm_register_hardware(struct pci_dev
 
 dev_num_search_complete:
 	if (chip->dev_num < 0) {
-		dev_err(&pci_dev->dev,
-			"No available tpm device numbers\n");
+		dev_err(dev, "No available tpm device numbers\n");
 		kfree(chip);
 		return -ENODEV;
 	} else if (chip->dev_num == 0)
@@ -652,27 +644,27 @@ dev_num_search_complete:
 	snprintf(devname, sizeof(devname), "%s%d", "tpm", chip->dev_num);
 	chip->vendor->miscdev.name = devname;
 
-	chip->vendor->miscdev.dev = &(pci_dev->dev);
-	chip->pci_dev = pci_dev_get(pci_dev);
+	chip->vendor->miscdev.dev = dev;
+	chip->dev = get_device(dev);
 
 	if (misc_register(&chip->vendor->miscdev)) {
-		dev_err(&chip->pci_dev->dev,
+		dev_err(chip->dev,
 			"unable to misc_register %s, minor %d\n",
 			chip->vendor->miscdev.name,
 			chip->vendor->miscdev.minor);
-		pci_dev_put(pci_dev);
+		put_device(dev);
 		kfree(chip);
 		dev_mask[i] &= !(1 << j);
 		return -ENODEV;
 	}
 
-	pci_set_drvdata(pci_dev, chip);
+	dev_set_drvdata(dev, chip);
 
 	list_add(&chip->list, &tpm_chip_list);
 
-	device_create_file(&pci_dev->dev, &dev_attr_pubek);
-	device_create_file(&pci_dev->dev, &dev_attr_pcrs);
-	device_create_file(&pci_dev->dev, &dev_attr_caps);
+	device_create_file(dev, &dev_attr_pubek);
+	device_create_file(dev, &dev_attr_pcrs);
+	device_create_file(dev, &dev_attr_caps);
 
 	return 0;
 }
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.h linux-2.6.10-tpm/drivers/char/tpm/tpm.h
--- linux-2.6.10/drivers/char/tpm/tpm.h	2005-02-01 11:08:44.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.h	2005-02-01 11:28:30.000000000 -0600
@@ -45,7 +45,7 @@ struct tpm_vendor_specific {
 };
 
 struct tpm_chip {
-	struct pci_dev *pci_dev;	/* PCI device stuff */
+	struct device *dev;	/* PCI device stuff */
 
 	int dev_num;		/* /dev/tpm# */
 	int num_opens;		/* only one allowed */
@@ -81,13 +81,13 @@ static inline void tpm_write_index(int i
 extern void tpm_time_expired(unsigned long);
 extern int tpm_lpc_bus_init(struct pci_dev *, u16);
 
-extern int tpm_register_hardware(struct pci_dev *,
+extern int tpm_register_hardware(struct device *,
 				 struct tpm_vendor_specific *);
+extern void tpm_remove_hardware(struct device *);
 extern int tpm_open(struct inode *, struct file *);
 extern int tpm_release(struct inode *, struct file *);
 extern ssize_t tpm_write(struct file *, const char __user *, size_t,
 			 loff_t *);
 extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *);
-extern void __devexit tpm_remove(struct pci_dev *);
 extern int tpm_pm_suspend(struct pci_dev *, u32);
 extern int tpm_pm_resume(struct pci_dev *);
diff -uprN linux-2.6.10/drivers/char/tpm/tpm_nsc.c linux-2.6.10-tpm/drivers/char/tpm/tpm_nsc.c
--- linux-2.6.10/drivers/char/tpm/tpm_nsc.c	2005-02-01 11:08:44.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm_nsc.c	2005-02-01 11:28:14.000000000 -0600
@@ -111,7 +111,7 @@ static int nsc_wait_for_ready(struct tpm
 	}
 	while (!expired);
 
-	dev_info(&chip->pci_dev->dev, "wait for ready failed\n");
+	dev_info(chip->dev, "wait for ready failed\n");
 	return -EBUSY;
 }
 
@@ -127,13 +127,12 @@ static int tpm_nsc_recv(struct tpm_chip 
 		return -EIO;
 
 	if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
-		dev_err(&chip->pci_dev->dev, "F0 timeout\n");
+		dev_err(chip->dev, "F0 timeout\n");
 		return -EIO;
 	}
 	if ((data =
 	     inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
-		dev_err(&chip->pci_dev->dev, "not in normal mode (0x%x)\n",
-			data);
+		dev_err(chip->dev, "not in normal mode (0x%x)\n", data);
 		return -EIO;
 	}
 
@@ -141,7 +140,7 @@ static int tpm_nsc_recv(struct tpm_chip 
 	for (p = buffer; p < &buffer[count]; p++) {
 		if (wait_for_stat
 		    (chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) {
-			dev_err(&chip->pci_dev->dev,
+			dev_err(chip->dev,
 				"OBF timeout (while reading data)\n");
 			return -EIO;
 		}
@@ -151,11 +150,11 @@ static int tpm_nsc_recv(struct tpm_chip 
 	}
 
 	if ((data & NSC_STATUS_F0) == 0) {
-		dev_err(&chip->pci_dev->dev, "F0 not set\n");
+		dev_err(chip->dev, "F0 not set\n");
 		return -EIO;
 	}
 	if ((data = inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_EOC) {
-		dev_err(&chip->pci_dev->dev,
+		dev_err(chip->dev,
 			"expected end of command(0x%x)\n", data);
 		return -EIO;
 	}
@@ -186,19 +185,19 @@ static int tpm_nsc_send(struct tpm_chip 
 		return -EIO;
 
 	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
-		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+		dev_err(chip->dev, "IBF timeout\n");
 		return -EIO;
 	}
 
 	outb(NSC_COMMAND_NORMAL, chip->vendor->base + NSC_COMMAND);
 	if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
-		dev_err(&chip->pci_dev->dev, "IBR timeout\n");
+		dev_err(chip->dev, "IBR timeout\n");
 		return -EIO;
 	}
 
 	for (i = 0; i < count; i++) {
 		if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
-			dev_err(&chip->pci_dev->dev,
+			dev_err(chip->dev,
 				"IBF timeout (while writing data)\n");
 			return -EIO;
 		}
@@ -206,7 +205,7 @@ static int tpm_nsc_send(struct tpm_chip 
 	}
 
 	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
-		dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+		dev_err(chip->dev, "IBF timeout\n");
 		return -EIO;
 	}
 	outb(NSC_COMMAND_EOC, chip->vendor->base + NSC_COMMAND);
@@ -329,7 +328,7 @@ static int __devinit tpm_nsc_init(struct
 	tpm_write_index(NSC_LDC_INDEX, 0x01);
 	tpm_read_index(NSC_LDC_INDEX);
 
-	if ((rc = tpm_register_hardware(pci_dev, &tpm_nsc)) < 0)
+	if ((rc = tpm_register_hardware(&pci_dev->dev, &tpm_nsc)) < 0)
 		goto out_err;
 
 	return 0;
@@ -339,6 +338,12 @@ out_err:
 	return rc;
 }
 
+static void __devexit tpm_nsc_remove(struct pci_dev *pci_dev)
+{
+	tpm_remove_hardware(&pci_dev->dev);
+	pci_disable_device(pci_dev);
+}
+
 static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
@@ -355,7 +360,7 @@ static struct pci_driver nsc_pci_driver 
 	.name = "tpm_nsc",
 	.id_table = tpm_pci_tbl,
 	.probe = tpm_nsc_init,
-	.remove = __devexit_p(tpm_remove),
+	.remove = __devexit_p(tpm_nsc_remove),
 	.suspend = tpm_pm_suspend,
 	.resume = tpm_pm_resume,
 };

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH 1/1] tpm: implement use of sysfs classes
  2005-02-03 16:40                     ` [PATCH 1/1] tpm: remove pci specific stuff from the underlying generic driver Kylene Hall
@ 2005-02-04 20:12                       ` Kylene Hall
  2005-02-04 20:52                         ` Greg KH
  0 siblings, 1 reply; 43+ messages in thread
From: Kylene Hall @ 2005-02-04 20:12 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, emilyr, toml, tpmdd-devel

With the enablement of being able to support chips on differnt buses  
(see patch sent 2/3/2005), the userspace library (TSS) needs to be able to 
find the corresponding sysfs files.  Added a new sysfs class "tpm" where 
the devices are now registered (tpm0, tpm1, ...) and links to the 
driver, device and tpm specific files (pcrs, etc) now live.  This patch 
also includes a new sysfs file for each tpm that is needed to support 
canceling an operation.

Thanks,
Kylie
 
Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.c linux-2.6.10-tpm/drivers/char/tpm/tpm.c
--- linux-2.6.10/drivers/char/tpm/tpm.c	2005-02-04 15:03:03.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.c	2005-02-04 15:02:14.000000000 -0600
@@ -213,14 +212,14 @@ static u8 pcrread[] = {
 	0, 0, 0, 0		/* PCR index */
 };
 
-static ssize_t show_pcrs(struct device *dev, char *buf)
+static ssize_t show_pcrs(struct class_device *cdev, char *buf)
 {
 	u8 data[READ_PCR_RESULT_SIZE];
 	ssize_t len;
 	int i, j, index, num_pcrs;
 	char *str = buf;
 
-	struct tpm_chip *chip = dev_get_drvdata(dev);
+	struct tpm_chip *chip = dev_get_drvdata(cdev->dev);
 	if (chip == NULL)
 		return -ENODEV;
 
@@ -246,8 +245,6 @@ static ssize_t show_pcrs(struct device *
 	return str - buf;
 }
 
-static DEVICE_ATTR(pcrs, S_IRUGO, show_pcrs, NULL);
-
 #define  READ_PUBEK_RESULT_SIZE 314
 static u8 readpubek[] = {
 	0, 193,			/* TPM_TAG_RQU_COMMAND */
@@ -255,7 +252,7 @@ static u8 readpubek[] = {
 	0, 0, 0, 124,		/* TPM_ORD_ReadPubek */
 };
 
-static ssize_t show_pubek(struct device *dev, char *buf)
+static ssize_t show_pubek(struct class_device *cdev, char *buf)
 {
 	u8 data[READ_PUBEK_RESULT_SIZE];
 	ssize_t len;
@@ -263,7 +260,7 @@ static ssize_t show_pubek(struct device 
 	int i;
 	char *str = buf;
 
-	struct tpm_chip *chip = dev_get_drvdata(dev);
+	struct tpm_chip *chip = dev_get_drvdata(cdev->dev);
 	if (chip == NULL)
 		return -ENODEV;
 
@@ -308,8 +305,6 @@ static ssize_t show_pubek(struct device 
 	return str - buf;
 }
 
-static DEVICE_ATTR(pubek, S_IRUGO, show_pubek, NULL);
-
 #define CAP_VER_RESULT_SIZE 18
 static u8 cap_version[] = {
 	0, 193,			/* TPM_TAG_RQU_COMMAND */
@@ -329,13 +324,13 @@ static u8 cap_manufacturer[] = {
 	0, 0, 1, 3
 };
 
-static ssize_t show_caps(struct device *dev, char *buf)
+static ssize_t show_caps(struct class_device *cdev, char *buf)
 {
 	u8 data[READ_PUBEK_RESULT_SIZE];
 	ssize_t len;
 	char *str = buf;
 
-	struct tpm_chip *chip = dev_get_drvdata(dev);
+	struct tpm_chip *chip = dev_get_drvdata(cdev->dev);
 	if (chip == NULL)
 		return -ENODEV;
 
@@ -362,7 +357,22 @@ static ssize_t show_caps(struct device *
 	return str - buf;
 }
 
-static DEVICE_ATTR(caps, S_IRUGO, show_caps, NULL);
+static ssize_t store_cancel(struct class_device *cdev, const char *buf,
+			    size_t count)
+{
+	struct tpm_chip *chip = dev_get_drvdata(cdev->dev);
+	if (chip == NULL)
+		return 0;
+
+	chip->vendor->cancel(chip);
+
+	down(&chip->timer_manipulation_mutex);
+	if (timer_pending(&chip->device_timer))
+		mod_timer(&chip->device_timer, jiffies);
+	up(&chip->timer_manipulation_mutex);
+
+	return count;
+}
 
 /*
  * Device file system interface to the TPM
@@ -539,9 +549,7 @@ void tpm_remove_hardware(struct device *
 	dev_set_drvdata(dev, NULL);
 	misc_deregister(&chip->vendor->miscdev);
 
-	device_remove_file(dev, &dev_attr_pubek);
-	device_remove_file(dev, &dev_attr_pcrs);
-	device_remove_file(dev, &dev_attr_caps);
+	class_device_unregister(&chip->cdev);
 
 	dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
 
@@ -594,6 +602,19 @@ int tpm_pm_resume(struct pci_dev *pci_de
 
 EXPORT_SYMBOL_GPL(tpm_pm_resume);
 
+static struct class_device_attribute tpm_attrs[] = {
+	__ATTR(pubek, S_IRUGO, show_pubek, NULL),
+	__ATTR(pcrs, S_IRUGO, show_pcrs, NULL),
+	__ATTR(caps, S_IRUGO, show_caps, NULL),
+	__ATTR(cancel, S_IWUSR | S_IWGRP, NULL, store_cancel),
+	__ATTR_NULL
+};
+
+static struct class tpm_class = {
+	.name = "tpm",
+	.class_dev_attrs = tpm_attrs,
+};
+
 /*
  * Called from tpm_<specific>.c probe function only for devices 
  * the driver has determined it should claim.  Prior to calling
@@ -663,9 +684,10 @@ dev_num_search_complete:
 
 	list_add(&chip->list, &tpm_chip_list);
 
-	device_create_file(dev, &dev_attr_pubek);
-	device_create_file(dev, &dev_attr_pcrs);
-	device_create_file(dev, &dev_attr_caps);
+	chip->cdev.dev = dev;
+	chip->cdev.class = &tpm_class;
+	sprintf(chip->cdev.class_id, "%s%d", "tpm", chip->dev_num);
+	class_device_register(&chip->cdev);
 
 	return 0;
 }
@@ -674,12 +696,14 @@ EXPORT_SYMBOL_GPL(tpm_register_hardware)
 
 static int __init init_tpm(void)
 {
+	class_register(&tpm_class);
+
 	return 0;
 }
 
 static void __exit cleanup_tpm(void)
 {
-
+	class_unregister(&tpm_class);
 }
 
 module_init(init_tpm);
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.h linux-2.6.10-tpm/drivers/char/tpm/tpm.h
--- linux-2.6.10/drivers/char/tpm/tpm.h	2005-02-04 15:03:03.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.h	2005-02-04 15:09:24.898152744 -0600
@@ -46,6 +46,7 @@ struct tpm_vendor_specific {
 
 struct tpm_chip {
 	struct device *dev;	/* PCI device stuff */
+	struct class_device cdev;	/* TPM class device */
 
 	int dev_num;		/* /dev/tpm# */
 	int num_opens;		/* only one allowed */

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] tpm: implement use of sysfs classes
  2005-02-04 20:12                       ` [PATCH 1/1] tpm: implement use of sysfs classes Kylene Hall
@ 2005-02-04 20:52                         ` Greg KH
  2005-02-04 21:37                           ` Kylene Hall
  0 siblings, 1 reply; 43+ messages in thread
From: Greg KH @ 2005-02-04 20:52 UTC (permalink / raw)
  To: Kylene Hall; +Cc: linux-kernel, emilyr, toml, tpmdd-devel

On Fri, Feb 04, 2005 at 02:12:50PM -0600, Kylene Hall wrote:
> +static struct class tpm_class = {
> +	.name = "tpm",
> +	.class_dev_attrs = tpm_attrs,
> +};

Where is your release function?  Did you see any warnings from the
kernel when you removed any of these class devices?  Why did you ignore
it?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] tpm: implement use of sysfs classes
  2005-02-04 20:52                         ` Greg KH
@ 2005-02-04 21:37                           ` Kylene Hall
  2005-02-04 21:51                             ` Greg KH
  0 siblings, 1 reply; 43+ messages in thread
From: Kylene Hall @ 2005-02-04 21:37 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, Emily Ratliff, Tom Lendacky, tpmdd-devel

On Fri, 2005-02-04 at 14:52, Greg KH wrote:
> On Fri, Feb 04, 2005 at 02:12:50PM -0600, Kylene Hall wrote:
> > +static struct class tpm_class = {
> > +	.name = "tpm",
> > +	.class_dev_attrs = tpm_attrs,
> > +};
> 
> Where is your release function?  Did you see any warnings from the
> kernel when you removed any of these class devices?  Why did you ignore
> it?
> 
Sorry, I missed the warning message.  I have looked at some other
instances for what I might need to put in that function and I'm
stumped.  I didn't kmalloc my class_device structure so I don't need to
kfree it.  I am using this mechanism so that my sysfs stuff is in a
predictable place.  It is also very convient how the driver and device
links as well as all my class specific files get created for me in the
register and likewise removed in the unregister.  I call the register
from the pci probe path and the unregister from the pci remove path. 
What might I need to put in this function?

Thanks,
Kylie

> thanks,
> 
> greg k-h
> 


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] tpm: implement use of sysfs classes
  2005-02-04 21:37                           ` Kylene Hall
@ 2005-02-04 21:51                             ` Greg KH
  2005-02-09 18:05                               ` [PATCH 1/1] tpm: update tpm sysfs file ownership Kylene Hall
  0 siblings, 1 reply; 43+ messages in thread
From: Greg KH @ 2005-02-04 21:51 UTC (permalink / raw)
  To: Kylene Hall; +Cc: linux-kernel, Emily Ratliff, Tom Lendacky, tpmdd-devel

On Fri, Feb 04, 2005 at 03:37:20PM -0600, Kylene Hall wrote:
> On Fri, 2005-02-04 at 14:52, Greg KH wrote:
> > On Fri, Feb 04, 2005 at 02:12:50PM -0600, Kylene Hall wrote:
> > > +static struct class tpm_class = {
> > > +	.name = "tpm",
> > > +	.class_dev_attrs = tpm_attrs,
> > > +};
> > 
> > Where is your release function?  Did you see any warnings from the
> > kernel when you removed any of these class devices?  Why did you ignore
> > it?
> > 
> Sorry, I missed the warning message.  I have looked at some other
> instances for what I might need to put in that function and I'm
> stumped.  I didn't kmalloc my class_device structure so I don't need to
> kfree it.

How do you create the structure that contains the class device?  What
will happen if a user holds a reference to the class device and your
structure holding that class device goes away?

Ick, I think I agree more and more with Al Viro that exposing this kind
of stuff to developers was a big mistake...

Anyway, why not try using the class_simple interface instead?  If you do
that you don't have to worry (as much) in the reference counting logic.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] tpm: update tpm sysfs file ownership
  2005-02-04 21:51                             ` Greg KH
@ 2005-02-09 18:05                               ` Kylene Hall
  2005-02-09 18:17                                 ` Greg KH
  0 siblings, 1 reply; 43+ messages in thread
From: Kylene Hall @ 2005-02-09 18:05 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, Emily Ratliff, Tom Lendacky, tpmdd-devel

On Fri, 4 Feb 2005, Greg KH wrote:
> On Fri, Feb 04, 2005 at 03:37:20PM -0600, Kylene Hall wrote:
> > On Fri, 2005-02-04 at 14:52, Greg KH wrote:
> > > On Fri, Feb 04, 2005 at 02:12:50PM -0600, Kylene Hall wrote:
> > > > +static struct class tpm_class = {
> > > > +	.name = "tpm",
> > > > +	.class_dev_attrs = tpm_attrs,
> > > > +};
> > > 
> > > Where is your release function?  Did you see any warnings from the
> > > kernel when you removed any of these class devices?  Why did you ignore
> > > it?
> > > 
> > Sorry, I missed the warning message.  I have looked at some other
> > instances for what I might need to put in that function and I'm
> > stumped.  I didn't kmalloc my class_device structure so I don't need to
> > kfree it.
> 
> Anyway, why not try using the class_simple interface instead?  If you do
> that you don't have to worry (as much) in the reference counting logic.

Thanks for pointing me to the class in the miscdevice.  I was able to use 
that for my needs.  I do need this small patch however to get the sysfs 
file ownership correct on my sysfs files.  The patch also adds on more 
sysfs file we need.

Thanks,
Kylie

> 
> thanks,
> 
> greg k-h
> 
> 


Signed off by: Kylene Hall <kjhall@us.ibm.com>
---
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.c linux-2.6.10-tpm/drivers/char/tpm/tpm.c
--- linux-2.6.10/drivers/char/tpm/tpm.c	2005-02-04 15:03:03.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.c	2005-02-09 12:23:23.137004424 -0600
@@ -246,8 +246,6 @@ static ssize_t show_pcrs(struct device *
 	return str - buf;
 }
 
-static DEVICE_ATTR(pcrs, S_IRUGO, show_pcrs, NULL);
-
 #define  READ_PUBEK_RESULT_SIZE 314
 static u8 readpubek[] = {
 	0, 193,			/* TPM_TAG_RQU_COMMAND */
@@ -308,8 +306,6 @@ static ssize_t show_pubek(struct device 
 	return str - buf;
 }
 
-static DEVICE_ATTR(pubek, S_IRUGO, show_pubek, NULL);
-
 #define CAP_VER_RESULT_SIZE 18
 static u8 cap_version[] = {
 	0, 193,			/* TPM_TAG_RQU_COMMAND */
@@ -362,7 +358,22 @@ static ssize_t show_caps(struct device *
 	return str - buf;
 }
 
-static DEVICE_ATTR(caps, S_IRUGO, show_caps, NULL);
+static ssize_t store_cancel(struct device *dev, const char *buf,
+			    size_t count)
+{
+	struct tpm_chip *chip = dev_get_drvdata(dev);
+	if (chip == NULL)
+		return 0;
+
+	chip->vendor->cancel(chip);
+
+	down(&chip->timer_manipulation_mutex);
+	if (timer_pending(&chip->device_timer))
+		mod_timer(&chip->device_timer, jiffies);
+	up(&chip->timer_manipulation_mutex);
+
+	return count;
+}
 
 /*
  * Device file system interface to the TPM
@@ -524,6 +535,7 @@ EXPORT_SYMBOL_GPL(tpm_read);
 void tpm_remove_hardware(struct device *dev)
 {
 	struct tpm_chip *chip = dev_get_drvdata(dev);
+	int i;
 
 	if (chip == NULL) {
 		dev_err(dev, "No device data found\n");
@@ -539,9 +551,8 @@ void tpm_remove_hardware(struct device *
 	dev_set_drvdata(dev, NULL);
 	misc_deregister(&chip->vendor->miscdev);
 
-	device_remove_file(dev, &dev_attr_pubek);
-	device_remove_file(dev, &dev_attr_pcrs);
-	device_remove_file(dev, &dev_attr_caps);
+	for ( i = 0; i < TPM_ATTRS; i++ ) 
+		device_remove_file(dev, &chip->attr[i]);
 
 	dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
 
@@ -608,6 +619,11 @@ int tpm_register_hardware(struct device 
 	struct tpm_chip *chip;
 	int i, j;
 
+	DEVICE_ATTR(pcrs, S_IRUGO, show_pcrs, NULL);
+	DEVICE_ATTR(pubek, S_IRUGO, show_pubek, NULL);
+	DEVICE_ATTR(caps, S_IRUGO, show_caps, NULL);
+	DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, store_cancel);
+
 	/* Driver specific per-device data */
 	chip = kmalloc(sizeof(*chip), GFP_KERNEL);
 	if (chip == NULL)
@@ -663,10 +679,16 @@ dev_num_search_complete:
 
 	list_add(&chip->list, &tpm_chip_list);
 
-	device_create_file(dev, &dev_attr_pubek);
-	device_create_file(dev, &dev_attr_pcrs);
-	device_create_file(dev, &dev_attr_caps);
-
+	chip->attr[0] = dev_attr_pubek;
+	chip->attr[1] = dev_attr_pcrs;
+	chip->attr[2] = dev_attr_caps;
+	chip->attr[3] = dev_attr_cancel;
+
+	for ( i = 0; i < TPM_ATTRS; i++ ) {
+		chip->attr[i].attr.owner = chip->vendor->miscdev.fops->owner;
+		device_create_file(dev, &chip->attr[i]);		
+	}
+	
 	return 0;
 }
 
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.h linux-2.6.10-tpm/drivers/char/tpm/tpm.h
--- linux-2.6.10/drivers/char/tpm/tpm.h	2005-02-04 15:03:03.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.h	2005-02-09 12:23:18.244748160 -0600
@@ -25,6 +25,7 @@
 #include <linux/miscdevice.h>
 
 #define TPM_TIMEOUT msecs_to_jiffies(5)
+#define TPM_ATTRS 4
 
 /* TPM addresses */
 #define	TPM_ADDR			0x4E
@@ -46,6 +47,7 @@ struct tpm_vendor_specific {
 
 struct tpm_chip {
 	struct device *dev;	/* PCI device stuff */
+	struct device_attribute attr[4];
 
 	int dev_num;		/* /dev/tpm# */
 	int num_opens;		/* only one allowed */

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/1] tpm: update tpm sysfs file ownership
  2005-02-09 18:05                               ` [PATCH 1/1] tpm: update tpm sysfs file ownership Kylene Hall
@ 2005-02-09 18:17                                 ` Greg KH
  2005-02-09 20:35                                   ` [tpmdd-devel] Re: [PATCH 1/1] tpm: update tpm sysfs file ownership - updated version Kylene Hall
  0 siblings, 1 reply; 43+ messages in thread
From: Greg KH @ 2005-02-09 18:17 UTC (permalink / raw)
  To: Kylene Hall; +Cc: linux-kernel, Emily Ratliff, Tom Lendacky, tpmdd-devel

On Wed, Feb 09, 2005 at 12:05:42PM -0600, Kylene Hall wrote:
> @@ -539,9 +551,8 @@ void tpm_remove_hardware(struct device *
>  	dev_set_drvdata(dev, NULL);
>  	misc_deregister(&chip->vendor->miscdev);
>  
> -	device_remove_file(dev, &dev_attr_pubek);
> -	device_remove_file(dev, &dev_attr_pcrs);
> -	device_remove_file(dev, &dev_attr_caps);
> +	for ( i = 0; i < TPM_ATTRS; i++ ) 
> +		device_remove_file(dev, &chip->attr[i]);
>  
>  	dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
>  

This code works?

> @@ -608,6 +619,11 @@ int tpm_register_hardware(struct device 
>  	struct tpm_chip *chip;
>  	int i, j;
>  
> +	DEVICE_ATTR(pcrs, S_IRUGO, show_pcrs, NULL);
> +	DEVICE_ATTR(pubek, S_IRUGO, show_pubek, NULL);
> +	DEVICE_ATTR(caps, S_IRUGO, show_caps, NULL);
> +	DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, store_cancel);
> +
>  	/* Driver specific per-device data */
>  	chip = kmalloc(sizeof(*chip), GFP_KERNEL);
>  	if (chip == NULL)

You do realize you just created those attributes on the stack?  And then
you try to remove them from within a different scope above?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [tpmdd-devel] Re: [PATCH 1/1] tpm: update tpm sysfs file ownership - updated version
  2005-02-09 18:17                                 ` Greg KH
@ 2005-02-09 20:35                                   ` Kylene Hall
  2005-02-09 22:04                                     ` Chris Wright
  0 siblings, 1 reply; 43+ messages in thread
From: Kylene Hall @ 2005-02-09 20:35 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, Emily Ratliff, Tom Lendacky, tpmdd-devel

On Wed, 9 Feb 2005, Greg KH wrote:
> On Wed, Feb 09, 2005 at 12:05:42PM -0600, Kylene Hall wrote:
> > @@ -539,9 +551,8 @@ void tpm_remove_hardware(struct device *
> >  	dev_set_drvdata(dev, NULL);
> >  	misc_deregister(&chip->vendor->miscdev);
> >  
> > -	device_remove_file(dev, &dev_attr_pubek);
> > -	device_remove_file(dev, &dev_attr_pcrs);
> > -	device_remove_file(dev, &dev_attr_caps);
> > +	for ( i = 0; i < TPM_ATTRS; i++ ) 
> > +		device_remove_file(dev, &chip->attr[i]);
> >  
> >  	dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
> >  
> 
> This code works?
> 
> > @@ -608,6 +619,11 @@ int tpm_register_hardware(struct device 
> >  	struct tpm_chip *chip;
> >  	int i, j;
> >  
> > +	DEVICE_ATTR(pcrs, S_IRUGO, show_pcrs, NULL);
> > +	DEVICE_ATTR(pubek, S_IRUGO, show_pubek, NULL);
> > +	DEVICE_ATTR(caps, S_IRUGO, show_caps, NULL);
> > +	DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, store_cancel);
> > +
> >  	/* Driver specific per-device data */
> >  	chip = kmalloc(sizeof(*chip), GFP_KERNEL);
> >  	if (chip == NULL)
> 
> You do realize you just created those attributes on the stack?  And then
> you try to remove them from within a different scope above?
>
I didn't realize their was a string in the structure that wouldn't get 
copied when I made the copy to the kmalloc'd struct.  Here is an alternate 
implementation that pushes the initialization into the respective specific 
drivers via a MACRO which has the added benefit of getting the owner field 
right from the start.
 
> thanks,
> 
> greg k-h

Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---

diff -uprN linux-2.6.10/drivers/char/tpm/tpm_atmel.c linux-2.6.10-tpm/drivers/char/tpm/tpm_atmel.c
--- linux-2.6.10/drivers/char/tpm/tpm_atmel.c	2005-02-04 15:03:03.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm_atmel.c	2005-02-09 14:12:30.711621784 -0600
@@ -131,6 +131,7 @@ static struct tpm_vendor_specific tpm_at
 	.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
 	.req_complete_val = ATML_STATUS_DATA_AVAIL,
 	.base = TPM_ATML_BASE,
+	.attr = TPM_DEVICE_ATTRS,
 	.miscdev.fops = &atmel_ops,
 };
 
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.c linux-2.6.10-tpm/drivers/char/tpm/tpm.c
--- linux-2.6.10/drivers/char/tpm/tpm.c	2005-02-04 15:03:03.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.c	2005-02-09 14:12:30.695624216 -0600
@@ -213,7 +213,7 @@ static u8 pcrread[] = {
 	0, 0, 0, 0		/* PCR index */
 };
 
-static ssize_t show_pcrs(struct device *dev, char *buf)
+ssize_t show_pcrs(struct device *dev, char *buf)
 {
 	u8 data[READ_PCR_RESULT_SIZE];
 	ssize_t len;
@@ -245,8 +245,7 @@ static ssize_t show_pcrs(struct device *
 	}
 	return str - buf;
 }
-
-static DEVICE_ATTR(pcrs, S_IRUGO, show_pcrs, NULL);
+EXPORT_SYMBOL_GPL(show_pcrs);
 
 #define  READ_PUBEK_RESULT_SIZE 314
 static u8 readpubek[] = {
@@ -255,7 +254,7 @@ static u8 readpubek[] = {
 	0, 0, 0, 124,		/* TPM_ORD_ReadPubek */
 };
 
-static ssize_t show_pubek(struct device *dev, char *buf)
+ssize_t show_pubek(struct device *dev, char *buf)
 {
 	u8 data[READ_PUBEK_RESULT_SIZE];
 	ssize_t len;
@@ -308,7 +307,7 @@ static ssize_t show_pubek(struct device 
 	return str - buf;
 }
 
-static DEVICE_ATTR(pubek, S_IRUGO, show_pubek, NULL);
+EXPORT_SYMBOL_GPL(show_pubek);
 
 #define CAP_VER_RESULT_SIZE 18
 static u8 cap_version[] = {
@@ -329,7 +328,7 @@ static u8 cap_manufacturer[] = {
 	0, 0, 1, 3
 };
 
-static ssize_t show_caps(struct device *dev, char *buf)
+ssize_t show_caps(struct device *dev, char *buf)
 {
 	u8 data[READ_PUBEK_RESULT_SIZE];
 	ssize_t len;
@@ -362,7 +361,26 @@ static ssize_t show_caps(struct device *
 	return str - buf;
 }
 
-static DEVICE_ATTR(caps, S_IRUGO, show_caps, NULL);
+EXPORT_SYMBOL_GPL(show_caps);
+
+ssize_t store_cancel(struct device *dev, const char *buf,
+			    size_t count)
+{
+	struct tpm_chip *chip = dev_get_drvdata(dev);
+	if (chip == NULL)
+		return 0;
+
+	chip->vendor->cancel(chip);
+
+	down(&chip->timer_manipulation_mutex);
+	if (timer_pending(&chip->device_timer))
+		mod_timer(&chip->device_timer, jiffies);
+	up(&chip->timer_manipulation_mutex);
+
+	return count;
+}
+
+EXPORT_SYMBOL_GPL(store_cancel);
 
 /*
  * Device file system interface to the TPM
@@ -524,6 +542,7 @@ EXPORT_SYMBOL_GPL(tpm_read);
 void tpm_remove_hardware(struct device *dev)
 {
 	struct tpm_chip *chip = dev_get_drvdata(dev);
+	int i;
 
 	if (chip == NULL) {
 		dev_err(dev, "No device data found\n");
@@ -539,9 +558,8 @@ void tpm_remove_hardware(struct device *
 	dev_set_drvdata(dev, NULL);
 	misc_deregister(&chip->vendor->miscdev);
 
-	device_remove_file(dev, &dev_attr_pubek);
-	device_remove_file(dev, &dev_attr_pcrs);
-	device_remove_file(dev, &dev_attr_caps);
+	for ( i = 0; i < TPM_NUM_ATTR; i++ ) 
+		device_remove_file(dev, &chip->vendor->attr[i]);
 
 	dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
 
@@ -663,10 +681,9 @@ dev_num_search_complete:
 
 	list_add(&chip->list, &tpm_chip_list);
 
-	device_create_file(dev, &dev_attr_pubek);
-	device_create_file(dev, &dev_attr_pcrs);
-	device_create_file(dev, &dev_attr_caps);
-
+	for ( i = 0; i < TPM_NUM_ATTR; i++ ) 
+		device_create_file(dev, &chip->vendor->attr[i]);		
+		
 	return 0;
 }
 
diff -uprN linux-2.6.10/drivers/char/tpm/tpm.h linux-2.6.10-tpm/drivers/char/tpm/tpm.h
--- linux-2.6.10/drivers/char/tpm/tpm.h	2005-02-04 15:03:03.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm.h	2005-02-09 14:12:30.702623152 -0600
@@ -25,11 +25,23 @@
 #include <linux/miscdevice.h>
 
 #define TPM_TIMEOUT msecs_to_jiffies(5)
+#define TPM_NUM_ATTR 4
 
 /* TPM addresses */
 #define	TPM_ADDR			0x4E
 #define	TPM_DATA			0x4F
 
+extern ssize_t show_pubek(struct device *, char *);
+extern ssize_t show_pcrs(struct device *, char *);
+extern ssize_t show_caps(struct device *, char *);
+extern ssize_t store_cancel(struct device *, const char *, size_t);
+
+#define TPM_DEVICE_ATTRS { \
+	__ATTR(pubek, S_IRUGO, show_pubek, NULL), \
+	__ATTR(pcrs, S_IRUGO, show_pcrs, NULL), \
+	__ATTR(caps, S_IRUGO, show_caps, NULL), \
+	__ATTR(cancel, S_IWUSR | S_IWGRP, NULL, store_cancel) }
+
 struct tpm_chip;
 
 struct tpm_vendor_specific {
@@ -42,6 +54,7 @@ struct tpm_vendor_specific {
 	void (*cancel) (struct tpm_chip *);
 	u8 (*status) (struct tpm_chip *);
 	struct miscdevice miscdev;
+	struct device_attribute attr[TPM_NUM_ATTR];
 };
 
 struct tpm_chip {
diff -uprN linux-2.6.10/drivers/char/tpm/tpm_nsc.c linux-2.6.10-tpm/drivers/char/tpm/tpm_nsc.c
--- linux-2.6.10/drivers/char/tpm/tpm_nsc.c	2005-02-04 15:03:03.000000000 -0600
+++ linux-2.6.10-tpm/drivers/char/tpm/tpm_nsc.c	2005-02-09 14:12:30.728619200 -0600
@@ -241,6 +241,7 @@ static struct tpm_vendor_specific tpm_ns
 	.req_complete_mask = NSC_STATUS_OBF,
 	.req_complete_val = NSC_STATUS_OBF,
 	.base = TPM_NSC_BASE,
+	.attr = TPM_DEVICE_ATTRS,
 	.miscdev.fops = &nsc_ops,
 };
 

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [tpmdd-devel] Re: [PATCH 1/1] tpm: update tpm sysfs file ownership - updated version
  2005-02-09 20:35                                   ` [tpmdd-devel] Re: [PATCH 1/1] tpm: update tpm sysfs file ownership - updated version Kylene Hall
@ 2005-02-09 22:04                                     ` Chris Wright
  2005-02-10 15:40                                       ` Kylene Hall
  0 siblings, 1 reply; 43+ messages in thread
From: Chris Wright @ 2005-02-09 22:04 UTC (permalink / raw)
  To: Kylene Hall
  Cc: Greg KH, linux-kernel, Emily Ratliff, Tom Lendacky, tpmdd-devel

* Kylene Hall (kjhall@us.ibm.com) wrote:
> diff -uprN linux-2.6.10/drivers/char/tpm/tpm_atmel.c linux-2.6.10-tpm/drivers/char/tpm/tpm_atmel.c
> --- linux-2.6.10/drivers/char/tpm/tpm_atmel.c	2005-02-04 15:03:03.000000000 -0600
> +++ linux-2.6.10-tpm/drivers/char/tpm/tpm_atmel.c	2005-02-09 14:12:30.711621784 -0600
> @@ -131,6 +131,7 @@ static struct tpm_vendor_specific tpm_at
>  	.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
>  	.req_complete_val = ATML_STATUS_DATA_AVAIL,
>  	.base = TPM_ATML_BASE,
> +	.attr = TPM_DEVICE_ATTRS,
>  	.miscdev.fops = &atmel_ops,
>  };
>  
> diff -uprN linux-2.6.10/drivers/char/tpm/tpm.c linux-2.6.10-tpm/drivers/char/tpm/tpm.c
> --- linux-2.6.10/drivers/char/tpm/tpm.c	2005-02-04 15:03:03.000000000 -0600
> +++ linux-2.6.10-tpm/drivers/char/tpm/tpm.c	2005-02-09 14:12:30.695624216 -0600
> @@ -213,7 +213,7 @@ static u8 pcrread[] = {
>  	0, 0, 0, 0		/* PCR index */
>  };
>  
> -static ssize_t show_pcrs(struct device *dev, char *buf)
> +ssize_t show_pcrs(struct device *dev, char *buf)

This is too generic a name for global namespace.

>  {
>  	u8 data[READ_PCR_RESULT_SIZE];
>  	ssize_t len;
> @@ -245,8 +245,7 @@ static ssize_t show_pcrs(struct device *
>  	}
>  	return str - buf;
>  }
> -
> -static DEVICE_ATTR(pcrs, S_IRUGO, show_pcrs, NULL);
> +EXPORT_SYMBOL_GPL(show_pcrs);
>  
>  #define  READ_PUBEK_RESULT_SIZE 314
>  static u8 readpubek[] = {
> @@ -255,7 +254,7 @@ static u8 readpubek[] = {
>  	0, 0, 0, 124,		/* TPM_ORD_ReadPubek */
>  };
>  
> -static ssize_t show_pubek(struct device *dev, char *buf)
> +ssize_t show_pubek(struct device *dev, char *buf)

same here

>  {
>  	u8 data[READ_PUBEK_RESULT_SIZE];
>  	ssize_t len;
> @@ -308,7 +307,7 @@ static ssize_t show_pubek(struct device 
>  	return str - buf;
>  }
>  
> -static DEVICE_ATTR(pubek, S_IRUGO, show_pubek, NULL);
> +EXPORT_SYMBOL_GPL(show_pubek);
>  
>  #define CAP_VER_RESULT_SIZE 18
>  static u8 cap_version[] = {
> @@ -329,7 +328,7 @@ static u8 cap_manufacturer[] = {
>  	0, 0, 1, 3
>  };
>  
> -static ssize_t show_caps(struct device *dev, char *buf)
> +ssize_t show_caps(struct device *dev, char *buf)

and here.

>  {
>  	u8 data[READ_PUBEK_RESULT_SIZE];
>  	ssize_t len;
> @@ -362,7 +361,26 @@ static ssize_t show_caps(struct device *
>  	return str - buf;
>  }
>  
> -static DEVICE_ATTR(caps, S_IRUGO, show_caps, NULL);
> +EXPORT_SYMBOL_GPL(show_caps);
> +
> +ssize_t store_cancel(struct device *dev, const char *buf,
> +			    size_t count)

and here

> +{
> +	struct tpm_chip *chip = dev_get_drvdata(dev);
> +	if (chip == NULL)
> +		return 0;
> +

Do you want any extra protection besides mode bits (S_IWUSR | S_IWGRP)?
How privileged should this operation be?

> +	chip->vendor->cancel(chip);
> +
> +	down(&chip->timer_manipulation_mutex);
> +	if (timer_pending(&chip->device_timer))
> +		mod_timer(&chip->device_timer, jiffies);
> +	up(&chip->timer_manipulation_mutex);
> +
> +	return count;
> +}
> +
> +EXPORT_SYMBOL_GPL(store_cancel);
>  
>  /*
>   * Device file system interface to the TPM
> @@ -524,6 +542,7 @@ EXPORT_SYMBOL_GPL(tpm_read);
>  void tpm_remove_hardware(struct device *dev)
>  {
>  	struct tpm_chip *chip = dev_get_drvdata(dev);
> +	int i;
>  
>  	if (chip == NULL) {
>  		dev_err(dev, "No device data found\n");
> @@ -539,9 +558,8 @@ void tpm_remove_hardware(struct device *
>  	dev_set_drvdata(dev, NULL);
>  	misc_deregister(&chip->vendor->miscdev);
>  
> -	device_remove_file(dev, &dev_attr_pubek);
> -	device_remove_file(dev, &dev_attr_pcrs);
> -	device_remove_file(dev, &dev_attr_caps);
> +	for ( i = 0; i < TPM_NUM_ATTR; i++ ) 
> +		device_remove_file(dev, &chip->vendor->attr[i]);
>  
>  	dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
>  
> @@ -663,10 +681,9 @@ dev_num_search_complete:
>  
>  	list_add(&chip->list, &tpm_chip_list);
>  
> -	device_create_file(dev, &dev_attr_pubek);
> -	device_create_file(dev, &dev_attr_pcrs);
> -	device_create_file(dev, &dev_attr_caps);
> -
> +	for ( i = 0; i < TPM_NUM_ATTR; i++ ) 
> +		device_create_file(dev, &chip->vendor->attr[i]);		
> +		
>  	return 0;
>  }
>  
> diff -uprN linux-2.6.10/drivers/char/tpm/tpm.h linux-2.6.10-tpm/drivers/char/tpm/tpm.h
> --- linux-2.6.10/drivers/char/tpm/tpm.h	2005-02-04 15:03:03.000000000 -0600
> +++ linux-2.6.10-tpm/drivers/char/tpm/tpm.h	2005-02-09 14:12:30.702623152 -0600
> @@ -25,11 +25,23 @@
>  #include <linux/miscdevice.h>
>  
>  #define TPM_TIMEOUT msecs_to_jiffies(5)
> +#define TPM_NUM_ATTR 4
>  
>  /* TPM addresses */
>  #define	TPM_ADDR			0x4E
>  #define	TPM_DATA			0x4F
>  
> +extern ssize_t show_pubek(struct device *, char *);
> +extern ssize_t show_pcrs(struct device *, char *);
> +extern ssize_t show_caps(struct device *, char *);
> +extern ssize_t store_cancel(struct device *, const char *, size_t);
> +
> +#define TPM_DEVICE_ATTRS { \
> +	__ATTR(pubek, S_IRUGO, show_pubek, NULL), \
> +	__ATTR(pcrs, S_IRUGO, show_pcrs, NULL), \
> +	__ATTR(caps, S_IRUGO, show_caps, NULL), \
> +	__ATTR(cancel, S_IWUSR | S_IWGRP, NULL, store_cancel) }

This doesn't look like the right way to go.  

> +
>  struct tpm_chip;
>  
>  struct tpm_vendor_specific {
> @@ -42,6 +54,7 @@ struct tpm_vendor_specific {
>  	void (*cancel) (struct tpm_chip *);
>  	u8 (*status) (struct tpm_chip *);
>  	struct miscdevice miscdev;
> +	struct device_attribute attr[TPM_NUM_ATTR];

So every device will have the same attrs?  If so, make that whole struct
exported (not the individual show/store methods) and reference that in
each driver.

thanks,
-chris
-- 
Linux Security Modules     http://lsm.immunix.org     http://lsm.bkbits.net

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [tpmdd-devel] Re: [PATCH 1/1] tpm: update tpm sysfs file ownership - updated version
  2005-02-09 22:04                                     ` Chris Wright
@ 2005-02-10 15:40                                       ` Kylene Hall
  0 siblings, 0 replies; 43+ messages in thread
From: Kylene Hall @ 2005-02-10 15:40 UTC (permalink / raw)
  To: Chris Wright
  Cc: Greg KH, linux-kernel, Emily Ratliff, Tom Lendacky, tpmdd-devel

On Wed, 2005-02-09 at 16:04, Chris Wright wrote:
> > +#define TPM_DEVICE_ATTRS { \
> > +	__ATTR(pubek, S_IRUGO, show_pubek, NULL), \
> > +	__ATTR(pcrs, S_IRUGO, show_pcrs, NULL), \
> > +	__ATTR(caps, S_IRUGO, show_caps, NULL), \
> > +	__ATTR(cancel, S_IWUSR | S_IWGRP, NULL, store_cancel) }
> 
> This doesn't look like the right way to go.  
> 
> > +
> >  struct tpm_chip;
> >  
> >  struct tpm_vendor_specific {
> > @@ -42,6 +54,7 @@ struct tpm_vendor_specific {
> >  	void (*cancel) (struct tpm_chip *);
> >  	u8 (*status) (struct tpm_chip *);
> >  	struct miscdevice miscdev;
> > +	struct device_attribute attr[TPM_NUM_ATTR];
> 
> So every device will have the same attrs?  If so, make that whole struct
> exported (not the individual show/store methods) and reference that in
> each driver.
> 
They are all the same except they have a different owner.  What I was
trying to do was have them initialized in the tpm specific drivers so
that the attribute ownership would be correct but then create them in
the generic driver since that was common to all the specific drivers.  I
could have one copy of the attributes like the one in the mm tree now
and change the owner when ever I want to add or remove the attribute. 
Would that be correct?

Thanks,
Kylie

> thanks,
> -chris


^ permalink raw reply	[flat|nested] 43+ messages in thread

end of thread, other threads:[~2005-02-10 15:40 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-12-09 15:25 [PATCH 1/1] driver: Tpm hardware enablement Kylene Hall
2004-12-09 15:48 ` Arjan van de Ven
2004-12-09 17:06   ` Kylie Hall
2004-12-11  8:31     ` Nish Aravamudan
2004-12-10 20:45   ` Alan Cox
2004-12-10 10:56 ` Ian Campbell
2004-12-10 15:28   ` Kylene Hall
2004-12-10 15:41     ` Ian Campbell
2004-12-10 18:39       ` [tpmdd-devel] " Kylene Hall
2004-12-14  9:59         ` Ian Campbell
2004-12-16 22:37 ` [PATCH 1/1] driver: Tpm hardware enablement --updated version Kylene Hall
2004-12-16 22:48   ` Greg KH
2004-12-17 22:47     ` [tpmdd-devel] " Kylene Hall
2004-12-17  0:53   ` Chris Wright
2004-12-17 22:47     ` [tpmdd-devel] " Kylene Hall
2004-12-17 22:47   ` Kylene Hall
2004-12-17 22:59     ` Greg KH
2004-12-20 17:50     ` Kylene Hall
2004-12-21 16:51       ` Nish Aravamudan
2004-12-21 18:19         ` Kylene Hall
2005-01-12 18:45           ` Kylene Hall
2005-01-12 23:28             ` Greg KH
2005-01-18 22:29             ` [PATCH 1/1] tpm: fix cause of SMP stack traces Kylene Hall
2005-01-18 22:37               ` Chris Wright
2005-01-18 22:44                 ` Kylene Hall
2005-01-18 22:47                   ` Chris Wright
2005-01-18 22:47               ` Greg KH
2005-01-18 23:07                 ` Kylene Hall
2005-01-18 23:39               ` [PATCH 1/1] tpm: fix cause of SMP stack traces -- updated version Kylene Hall
2005-01-28 21:45                 ` [PATCH 1/1] tpm: insert missing up mutex in an error path Kylene Hall
2005-01-31 19:27                   ` [PATCH 1/1] tpm: insert missing up mutex in an error path, typo build fix -- updated version Kylene Hall
2005-02-03 16:40                     ` [PATCH 1/1] tpm: remove pci specific stuff from the underlying generic driver Kylene Hall
2005-02-04 20:12                       ` [PATCH 1/1] tpm: implement use of sysfs classes Kylene Hall
2005-02-04 20:52                         ` Greg KH
2005-02-04 21:37                           ` Kylene Hall
2005-02-04 21:51                             ` Greg KH
2005-02-09 18:05                               ` [PATCH 1/1] tpm: update tpm sysfs file ownership Kylene Hall
2005-02-09 18:17                                 ` Greg KH
2005-02-09 20:35                                   ` [tpmdd-devel] Re: [PATCH 1/1] tpm: update tpm sysfs file ownership - updated version Kylene Hall
2005-02-09 22:04                                     ` Chris Wright
2005-02-10 15:40                                       ` Kylene Hall
2005-02-01  8:28                 ` [PATCH 1/1] tpm: fix cause of SMP stack traces -- " Greg KH
2004-12-19 19:48   ` [PATCH 1/1] driver: Tpm hardware enablement --updated version Pavel Machek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).