You can subscribe to this list here.
2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
(2) |
Dec
(20) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2005 |
Jan
(11) |
Feb
(11) |
Mar
|
Apr
|
May
(3) |
Jun
(19) |
Jul
(5) |
Aug
(13) |
Sep
(18) |
Oct
(8) |
Nov
(25) |
Dec
(3) |
2006 |
Jan
(16) |
Feb
|
Mar
|
Apr
(55) |
May
(23) |
Jun
(5) |
Jul
(19) |
Aug
(12) |
Sep
(12) |
Oct
(24) |
Nov
(3) |
Dec
(7) |
2007 |
Jan
|
Feb
(13) |
Mar
(12) |
Apr
(4) |
May
|
Jun
|
Jul
(6) |
Aug
(12) |
Sep
(3) |
Oct
(1) |
Nov
(16) |
Dec
(2) |
2008 |
Jan
(20) |
Feb
(6) |
Mar
(2) |
Apr
(3) |
May
(29) |
Jun
(11) |
Jul
(16) |
Aug
(7) |
Sep
(2) |
Oct
|
Nov
(9) |
Dec
(24) |
2009 |
Jan
(4) |
Feb
(1) |
Mar
|
Apr
(2) |
May
(5) |
Jun
(13) |
Jul
(25) |
Aug
|
Sep
(47) |
Oct
(5) |
Nov
(13) |
Dec
|
2010 |
Jan
(2) |
Feb
(4) |
Mar
(7) |
Apr
(6) |
May
(2) |
Jun
(14) |
Jul
(10) |
Aug
(6) |
Sep
(12) |
Oct
(6) |
Nov
(16) |
Dec
|
2011 |
Jan
(8) |
Feb
(10) |
Mar
(41) |
Apr
|
May
(5) |
Jun
(1) |
Jul
(1) |
Aug
(6) |
Sep
(8) |
Oct
(11) |
Nov
|
Dec
(15) |
2012 |
Jan
(15) |
Feb
(5) |
Mar
(7) |
Apr
(4) |
May
(19) |
Jun
(27) |
Jul
(20) |
Aug
(40) |
Sep
(19) |
Oct
(45) |
Nov
(102) |
Dec
(26) |
2013 |
Jan
(18) |
Feb
(21) |
Mar
(36) |
Apr
(46) |
May
(46) |
Jun
(10) |
Jul
(20) |
Aug
(8) |
Sep
(53) |
Oct
(109) |
Nov
(58) |
Dec
(37) |
2014 |
Jan
(37) |
Feb
(19) |
Mar
(5) |
Apr
(16) |
May
(23) |
Jun
(3) |
Jul
(30) |
Aug
(40) |
Sep
(110) |
Oct
(207) |
Nov
(84) |
Dec
(147) |
2015 |
Jan
(88) |
Feb
(40) |
Mar
(57) |
Apr
(61) |
May
(48) |
Jun
(65) |
Jul
(23) |
Aug
(15) |
Sep
(43) |
Oct
(130) |
Nov
(210) |
Dec
(177) |
2016 |
Jan
(135) |
Feb
(302) |
Mar
(288) |
Apr
(201) |
May
(65) |
Jun
(166) |
Jul
(193) |
Aug
(139) |
Sep
(211) |
Oct
(281) |
Nov
(259) |
Dec
(86) |
2017 |
Jan
(571) |
Feb
(257) |
Mar
(276) |
Apr
(138) |
May
(257) |
Jun
(145) |
Jul
(68) |
Aug
(176) |
Sep
(152) |
Oct
(72) |
Nov
(18) |
Dec
(9) |
2018 |
Jan
(5) |
Feb
|
Mar
(30) |
Apr
(1) |
May
(18) |
Jun
(2) |
Jul
|
Aug
(43) |
Sep
|
Oct
|
Nov
|
Dec
|
2019 |
Jan
|
Feb
|
Mar
|
Apr
(1) |
May
|
Jun
(5) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2020 |
Jan
|
Feb
(1) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2021 |
Jan
|
Feb
(1) |
Mar
|
Apr
|
May
(1) |
Jun
(1) |
Jul
|
Aug
|
Sep
(1) |
Oct
|
Nov
|
Dec
|
2022 |
Jan
|
Feb
(1) |
Mar
(1) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2023 |
Jan
(1) |
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: Greg KH <gr...@kr...> - 2004-12-17 23:00:18
|
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 |
From: Kylene H. <kj...@us...> - 2004-12-17 22:47:56
|
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 (kj...@us...) 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 |
From: Kylene H. <kj...@us...> - 2004-12-17 22:47:49
|
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 > tpm...@li... > https://lists.sourceforge.net/lists/listinfo/tpmdd-devel > |
From: Kylene H. <kj...@us...> - 2004-12-17 22:47:32
|
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 <lee...@wa...> Signed-off-by: Reiner Sailer <sa...@wa...> Signed-off-by: Dave Safford <sa...@wa...> Signed-off-by: Kylene Hall <kj...@us...> --- 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 <lee...@wa...> + * Reiner Sailer <sa...@wa...> + * Dave Safford <sa...@wa...> + * Kylene Hall <kj...@us...> + * + * Maintained by: <tpm...@li...> + * + * 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 (lee...@wa...)"); +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 <lee...@wa...> + * Reiner Sailer <sa...@wa...> + * Dave Safford <sa...@wa...> + * Kylene Hall <kj...@us...> + * + * Maintained by: <tpm...@li...> + * + * 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 (lee...@wa...)"); +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 <lee...@wa...> + * Reiner Sailer <sa...@wa...> + * Dave Safford <sa...@wa...> + * Kylene Hall <kj...@us...> + * + * Maintained by: <tpm...@li...> + * + * 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 <lee...@wa...> + * Reiner Sailer <sa...@wa...> + * Dave Safford <sa...@wa...> + * Kylene Hall <kj...@us...> + * + * Maintained by: <tpm...@li...> + * + * 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 (lee...@wa...)"); +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: tli...@tc... W: http://www.buzzard.org.uk/toshiba/ S: Maintained +TPM DRIVER +P: Kylene Hall +M: tpm...@li... +L: tpm...@li... +S: Maintained + TRIDENT 4DWAVE/SIS 7018 PCI AUDIO CORE P: Muli Ben-Yehuda M: mu...@mu... |
From: Chris W. <ch...@os...> - 2004-12-17 00:53:39
|
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 (kj...@us...) 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 <lee...@wa...> > + * Reiner Sailer <sa...@wa...> > + * Dave Safford <sa...@wa...> > + * Kylene Hall <kj...@us...> > + * > + * Maintained by: <tpm...@li...> > + * > + * 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 (lee...@wa...)"); > +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 <lee...@wa...> > + * Reiner Sailer <sa...@wa...> > + * Dave Safford <sa...@wa...> > + * Kylene Hall <kj...@us...> > + * > + * Maintained by: <tpm...@li...> > + * > + * 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 (lee...@wa...)"); > +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 <lee...@wa...> > + * Reiner Sailer <sa...@wa...> > + * Dave Safford <sa...@wa...> > + * Kylene Hall <kj...@us...> > + * > + * Maintained by: <tpm...@li...> > + * > + * 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 <lee...@wa...> > + * Reiner Sailer <sa...@wa...> > + * Dave Safford <sa...@wa...> > + * Kylene Hall <kj...@us...> > + * > + * Maintained by: <tpm...@li...> > + * > + * 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 (lee...@wa...)"); > +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: tli...@tc... > W: http://www.buzzard.org.uk/toshiba/ > S: Maintained > > +TPM DRIVER > +P: Kylene Hall > +M: tpm...@li... > +L: tpm...@li... > +S: Maintained > + > TRIDENT 4DWAVE/SIS 7018 PCI AUDIO CORE > P: Muli Ben-Yehuda > M: mu...@mu... > - > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to maj...@vg... > 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 |
From: Greg KH <gr...@kr...> - 2004-12-16 22:48:24
|
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 |
From: Kylene H. <kj...@us...> - 2004-12-16 22:38:29
|
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 <lee...@wa...> Signed-off-by: Reiner Sailer <sa...@wa...> Signed-off-by: Dave Safford <sa...@wa...> Signed-off-by: Kylene Hall <kj...@us...> --- 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 <lee...@wa...> + * Reiner Sailer <sa...@wa...> + * Dave Safford <sa...@wa...> + * Kylene Hall <kj...@us...> + * + * Maintained by: <tpm...@li...> + * + * 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 (lee...@wa...)"); +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 <lee...@wa...> + * Reiner Sailer <sa...@wa...> + * Dave Safford <sa...@wa...> + * Kylene Hall <kj...@us...> + * + * Maintained by: <tpm...@li...> + * + * 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 (lee...@wa...)"); +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 <lee...@wa...> + * Reiner Sailer <sa...@wa...> + * Dave Safford <sa...@wa...> + * Kylene Hall <kj...@us...> + * + * Maintained by: <tpm...@li...> + * + * 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 <lee...@wa...> + * Reiner Sailer <sa...@wa...> + * Dave Safford <sa...@wa...> + * Kylene Hall <kj...@us...> + * + * Maintained by: <tpm...@li...> + * + * 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 (lee...@wa...)"); +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: tli...@tc... W: http://www.buzzard.org.uk/toshiba/ S: Maintained +TPM DRIVER +P: Kylene Hall +M: tpm...@li... +L: tpm...@li... +S: Maintained + TRIDENT 4DWAVE/SIS 7018 PCI AUDIO CORE P: Muli Ben-Yehuda M: mu...@mu... |
From: Ian C. <ica...@ar...> - 2004-12-14 10:00:04
|
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 |
From: Nish A. <nis...@gm...> - 2004-12-11 08:32:00
|
On Thu, 09 Dec 2004 11:06:27 -0600, Kylie Hall <kj...@us...> 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 |
From: Alan C. <al...@lx...> - 2004-12-10 21:49:48
|
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 |
From: Kylene H. <kj...@us...> - 2004-12-10 18:39:54
|
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. |
From: Ian C. <ica...@ar...> - 2004-12-10 15:41:46
|
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 |
From: Kylene H. <kj...@us...> - 2004-12-10 15:29:10
|
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. |
From: Ian C. <ica...@ar...> - 2004-12-10 10:56:20
|
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 |
From: Kylie H. <kj...@us...> - 2004-12-09 17:06:39
|
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 |
From: Kylene H. <kj...@us...> - 2004-12-09 15:25:17
|
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 <lee...@wa...> Signed-off-by: Reiner Sailer <sa...@wa...> Signed-off-by: Dave Safford <sa...@wa...> Signed-off-by: Kylene Hall <kj...@us...> --- 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 <lee...@wa...> + * Reiner Sailer <sa...@wa...> + * Dave Safford <sa...@wa...> + * Kylene Hall <kj...@us...> + * + * Maintained by: <tpm...@li...> + * + * 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 (lee...@wa...)"); +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: tli...@tc... W: http://www.buzzard.org.uk/toshiba/ S: Maintained +TPM DRIVER +P: Kylene Hall +M: tpm...@li... +L: tpm...@li... +S: Maintained + TRIDENT 4DWAVE/SIS 7018 PCI AUDIO CORE P: Muli Ben-Yehuda M: mu...@mu... |
From: Kylie H. <kj...@us...> - 2004-11-29 21:01:28
|
From: Kylie H. <kj...@us...> - 2004-11-29 19:52:05
|
What happens to this? |