From: Jeff Garzik <jgarzik@pobox.com>
To: Greg K-H <greg@kroah.com>
Cc: linux-kernel@vger.kernel.org, kjhall@us.ibm.com,
safford@watson.ibm.com, leendert@watson.ibm.com
Subject: Re: [PATCH] Add TPM hardware enablement driver
Date: Wed, 09 Mar 2005 22:51:07 -0500 [thread overview]
Message-ID: <422FC42B.7@pobox.com> (raw)
In-Reply-To: <1110415321526@kroah.com>
Greg KH wrote:
> diff -Nru a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
> --- /dev/null Wed Dec 31 16:00:00 196900
> +++ b/drivers/char/tpm/tpm.c 2005-03-09 16:40:26 -08:00
> @@ -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
enums preferred to #defines, as these provide more type information, and
are more visible in a debugger.
> +static LIST_HEAD(tpm_chip_list);
> +static spinlock_t driver_lock = SPIN_LOCK_UNLOCKED;
> +static int dev_mask[32];
don't use '32', create a constant and use that constant instead.
> +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 */
please define symbol names for the TPM registers
> + 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);
very wrong. you init_timer() when you initialize 'chip'... once. then
during the device lifetime you add/mod/del the timer.
calling init_timer() could lead to corruption of state.
> + 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
> +};
const
> +#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 */
> +};
const
> +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));
use to_pci_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];
massive obj on stack
> + 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));
to_pci_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
> +};
const
> +#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
> +};
const
> +static ssize_t show_caps(struct device *dev, char *buf)
> +{
> + u8 data[READ_PUBEK_RESULT_SIZE];
massive obj on stack
> + ssize_t len;
> + char *str = buf;
> +
> + struct tpm_chip *chip =
> + pci_get_drvdata(container_of(dev, struct pci_dev, dev));
to_pci_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;
> + }
what is the purpose of this pci_dev_get/put? attempting to prevent
hotplug or something?
> + 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);
use msleep()
> + }
> +
> + 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);
major bug? in_size may be smaller than 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);
again, don't repeatedly init_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;
POSIX violation -- when there is no data available, returning a
non-standard error is silly
> +}
> +
> +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);
> +}
similar comment as before... I don't see the need for pci_dev_put?
> +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 */
> +};
const
> +/*
> + * 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);
timer init should be here
> + 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);
why? just delete these, I would say.
> +MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
> +MODULE_DESCRIPTION("TPM Driver");
> +MODULE_VERSION("2.0");
> +MODULE_LICENSE("GPL");
> diff -Nru a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
> --- /dev/null Wed Dec 31 16:00:00 196900
> +++ b/drivers/char/tpm/tpm.h 2005-03-09 16:40:26 -08:00
> @@ -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
enum preferred to #define
> +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 -Nru a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c
> --- /dev/null Wed Dec 31 16:00:00 196900
> +++ b/drivers/char/tpm/tpm_atmel.c 2005-03-09 16:40:26 -08:00
> @@ -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
enum preferred
> +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;
> + }
are you REALLY sure you want to eat data?
> + /* 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 -Nru a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c
> --- /dev/null Wed Dec 31 16:00:00 196900
> +++ b/drivers/char/tpm/tpm_nsc.c 2005-03-09 16:40:26 -08:00
> @@ -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
enum
> +/*
> + * 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);
use msleep()
> + *data = inb(chip->vendor->base + 1);
> + if ((*data & mask) == val) {
> + del_singleshot_timer_sync(&status_timer);
> + return 0;
> + }
> + }
> + while (!expired);
infinite loop: expired never cleared
was this code ever tested?
> + 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);
msleep()
> + 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);
another infinite loop?
next prev parent reply other threads:[~2005-03-10 4:08 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-03-10 0:41 [BK PATCH] Add TPM driver support for 2.6.11 Greg KH
2005-03-10 0:42 ` [PATCH] Add TPM hardware enablement driver Greg KH
2005-03-10 0:42 ` [PATCH] tpm: fix cause of SMP stack traces Greg KH
2005-03-10 0:42 ` [PATCH] tpm_msc-build-fix Greg KH
2005-03-10 0:42 ` [PATCH] tpm_atmel build fix Greg KH
2005-03-10 0:42 ` [PATCH] tpm-build-fix Greg KH
2005-03-10 3:51 ` Jeff Garzik [this message]
2005-03-15 23:59 ` [PATCH] Add TPM hardware enablement driver Kylene Jo Hall
2005-03-17 0:32 ` Kylene Hall
2005-03-23 2:02 ` Jeff Garzik
2005-03-24 6:39 ` Greg KH
2005-03-24 21:04 ` Jeff Garzik
2005-03-24 21:33 ` Greg KH
2005-04-05 16:14 ` Kylene Jo Hall
2005-04-08 20:07 ` Kylene Jo Hall
2005-04-09 8:31 ` Ian Campbell
2005-04-27 22:15 ` [PATCH: 1 of 12] Fix concerns with TPM driver -- use enums Kylene Hall
2005-04-27 22:23 ` Greg KH
2005-04-27 22:15 ` [PATCH: 2 of 12 ] Fix TPM driver -- address missing const defs Kylene Hall
2005-04-27 22:16 ` [PATCH: 3 of 12] Fix TPM driver --remove unnecessary module stuff Kylene Hall
2005-04-27 22:16 ` [PATCH 4 of 12] Fix TPM driver -- read return code issue Kylene Hall
2005-04-27 22:16 ` [PATCH 5 of 12] Fix TPM driver -- large stack objects Kylene Hall
2005-04-27 22:18 ` [PATCH 6 of 12] Fix TPM driver -- how timer is initialized Kylene Hall
2005-04-27 22:18 ` [PATCH 7 of 12] Fix TPM driver -- use to_pci_dev Kylene Hall
2005-03-10 17:35 ` [PATCH] Add TPM hardware enablement driver Nish Aravamudan
2005-03-10 18:19 ` Nish Aravamudan
2005-03-10 19:09 ` [PATCH] char/tpm: use msleep(), clean-up timers, fix typo Nishanth Aravamudan
2005-03-10 21:04 ` [PATCH] Add TPM hardware enablement driver Alexey Dobriyan
2005-04-27 22:18 ` [PATCH 9 of 12] Fix TPM driver -- remove unnecessary __force Kylene Hall
2005-03-11 18:18 ` [PATCH] char/tpm: use msleep(), clean-up timers, fix typo Nishanth Aravamudan
2005-04-15 20:23 ` Kylene Hall
2005-04-15 20:44 ` Nish Aravamudan
2005-04-15 21:04 ` Greg KH
2005-04-15 21:47 ` Nish Aravamudan
2005-04-15 21:47 ` Nish Aravamudan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=422FC42B.7@pobox.com \
--to=jgarzik@pobox.com \
--cc=greg@kroah.com \
--cc=kjhall@us.ibm.com \
--cc=leendert@watson.ibm.com \
--cc=linux-kernel@vger.kernel.org \
--cc=safford@watson.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.