aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/tpm/tpm_infineon.c
diff options
context:
space:
mode:
authorMarcel Selhorst <selhorst@crypto.rub.de>2005-07-27 14:45:12 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2005-07-27 19:26:06 -0400
commitebb81fdb3dd0be7514b84197c4f8388a17130f04 (patch)
tree8dd41338a44bb5194134828a8a14a36311193dac /drivers/char/tpm/tpm_infineon.c
parente77e17161ccb8bd877bf83b3611cd318e451c605 (diff)
[PATCH] tpm: Support for Infineon TPM
This patch provides a new device driver for the Infineon SLD 9630 TT Trusted Platform Module (TPM 1.1b) [1] which is embedded on Intel- mainboards or in HP/ Fujitsu-Siemens / Toshiba-Notebooks. A nearly complete list where this module is integrated in can be found in [2]. This kernel module acts as a communication gateway between the linux kernel and the hardware chip and fits the TPM-specific interfaces created by IBM in drivers/char/tpm/tpm.h Further information about this module and a list of succesfully tested and therefore supported hardware can be found at our project page [3]. [1] http://www.infineon.com/cgi/ecrm.dll/ecrm/scripts/public_download.jsp?oid=114135&parent_oid=29049 [2] http://www.tonymcfadden.net/tpmvendors.htm [3] http://www.prosec.rub.de/tpm Signed-off-by: Marcel Selhorst <selhorst@crypto.rub.de> Acked-by: Kylene Jo Hall <kjhall@us.ibm.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char/tpm/tpm_infineon.c')
-rw-r--r--drivers/char/tpm/tpm_infineon.c467
1 files changed, 467 insertions, 0 deletions
diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c
new file mode 100644
index 000000000000..07542f9e7644
--- /dev/null
+++ b/drivers/char/tpm/tpm_infineon.c
@@ -0,0 +1,467 @@
1/*
2 * Description:
3 * Device Driver for the Infineon Technologies
4 * SLD 9630 TT Trusted Platform Module
5 * Specifications at www.trustedcomputinggroup.org
6 *
7 * Copyright (C) 2005, Marcel Selhorst <selhorst@crypto.rub.de>
8 * Applied Data Security Group, Ruhr-University Bochum, Germany
9 * Project-Homepage: http://www.prosec.rub.de/tpm
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License as
13 * published by the Free Software Foundation, version 2 of the
14 * License.
15 *
16 */
17
18#include "tpm.h"
19
20/* Infineon specific definitions */
21/* maximum number of WTX-packages */
22#define TPM_MAX_WTX_PACKAGES 50
23/* msleep-Time for WTX-packages */
24#define TPM_WTX_MSLEEP_TIME 20
25/* msleep-Time --> Interval to check status register */
26#define TPM_MSLEEP_TIME 3
27/* gives number of max. msleep()-calls before throwing timeout */
28#define TPM_MAX_TRIES 5000
29#define TCPA_INFINEON_DEV_VEN_VALUE 0x15D1
30#define TPM_DATA (TPM_ADDR + 1) & 0xff
31
32/* TPM header definitions */
33enum infineon_tpm_header {
34 TPM_VL_VER = 0x01,
35 TPM_VL_CHANNEL_CONTROL = 0x07,
36 TPM_VL_CHANNEL_PERSONALISATION = 0x0A,
37 TPM_VL_CHANNEL_TPM = 0x0B,
38 TPM_VL_CONTROL = 0x00,
39 TPM_INF_NAK = 0x15,
40 TPM_CTRL_WTX = 0x10,
41 TPM_CTRL_WTX_ABORT = 0x18,
42 TPM_CTRL_WTX_ABORT_ACK = 0x18,
43 TPM_CTRL_ERROR = 0x20,
44 TPM_CTRL_CHAININGACK = 0x40,
45 TPM_CTRL_CHAINING = 0x80,
46 TPM_CTRL_DATA = 0x04,
47 TPM_CTRL_DATA_CHA = 0x84,
48 TPM_CTRL_DATA_CHA_ACK = 0xC4
49};
50
51enum infineon_tpm_register {
52 WRFIFO = 0x00,
53 RDFIFO = 0x01,
54 STAT = 0x02,
55 CMD = 0x03
56};
57
58enum infineon_tpm_command_bits {
59 CMD_DIS = 0x00,
60 CMD_LP = 0x01,
61 CMD_RES = 0x02,
62 CMD_IRQC = 0x06
63};
64
65enum infineon_tpm_status_bits {
66 STAT_XFE = 0x00,
67 STAT_LPA = 0x01,
68 STAT_FOK = 0x02,
69 STAT_TOK = 0x03,
70 STAT_IRQA = 0x06,
71 STAT_RDA = 0x07
72};
73
74/* some outgoing values */
75enum infineon_tpm_values {
76 CHIP_ID1 = 0x20,
77 CHIP_ID2 = 0x21,
78 DAR = 0x30,
79 RESET_LP_IRQC_DISABLE = 0x41,
80 ENABLE_REGISTER_PAIR = 0x55,
81 IOLIMH = 0x60,
82 IOLIML = 0x61,
83 DISABLE_REGISTER_PAIR = 0xAA,
84 IDVENL = 0xF1,
85 IDVENH = 0xF2,
86 IDPDL = 0xF3,
87 IDPDH = 0xF4
88};
89
90static int number_of_wtx;
91
92static int empty_fifo(struct tpm_chip *chip, int clear_wrfifo)
93{
94 int status;
95 int check = 0;
96 int i;
97
98 if (clear_wrfifo) {
99 for (i = 0; i < 4096; i++) {
100 status = inb(chip->vendor->base + WRFIFO);
101 if (status == 0xff) {
102 if (check == 5)
103 break;
104 else
105 check++;
106 }
107 }
108 }
109 /* Note: The values which are currently in the FIFO of the TPM
110 are thrown away since there is no usage for them. Usually,
111 this has nothing to say, since the TPM will give its answer
112 immediately or will be aborted anyway, so the data here is
113 usually garbage and useless.
114 We have to clean this, because the next communication with
115 the TPM would be rubbish, if there is still some old data
116 in the Read FIFO.
117 */
118 i = 0;
119 do {
120 status = inb(chip->vendor->base + RDFIFO);
121 status = inb(chip->vendor->base + STAT);
122 i++;
123 if (i == TPM_MAX_TRIES)
124 return -EIO;
125 } while ((status & (1 << STAT_RDA)) != 0);
126 return 0;
127}
128
129static int wait(struct tpm_chip *chip, int wait_for_bit)
130{
131 int status;
132 int i;
133 for (i = 0; i < TPM_MAX_TRIES; i++) {
134 status = inb(chip->vendor->base + STAT);
135 /* check the status-register if wait_for_bit is set */
136 if (status & 1 << wait_for_bit)
137 break;
138 msleep(TPM_MSLEEP_TIME);
139 }
140 if (i == TPM_MAX_TRIES) { /* timeout occurs */
141 if (wait_for_bit == STAT_XFE)
142 dev_err(&chip->pci_dev->dev,
143 "Timeout in wait(STAT_XFE)\n");
144 if (wait_for_bit == STAT_RDA)
145 dev_err(&chip->pci_dev->dev,
146 "Timeout in wait(STAT_RDA)\n");
147 return -EIO;
148 }
149 return 0;
150};
151
152static void wait_and_send(struct tpm_chip *chip, u8 sendbyte)
153{
154 wait(chip, STAT_XFE);
155 outb(sendbyte, chip->vendor->base + WRFIFO);
156}
157
158 /* Note: WTX means Waiting-Time-Extension. Whenever the TPM needs more
159 calculation time, it sends a WTX-package, which has to be acknowledged
160 or aborted. This usually occurs if you are hammering the TPM with key
161 creation. Set the maximum number of WTX-packages in the definitions
162 above, if the number is reached, the waiting-time will be denied
163 and the TPM command has to be resend.
164 */
165
166static void tpm_wtx(struct tpm_chip *chip)
167{
168 number_of_wtx++;
169 dev_info(&chip->pci_dev->dev, "Granting WTX (%02d / %02d)\n",
170 number_of_wtx, TPM_MAX_WTX_PACKAGES);
171 wait_and_send(chip, TPM_VL_VER);
172 wait_and_send(chip, TPM_CTRL_WTX);
173 wait_and_send(chip, 0x00);
174 wait_and_send(chip, 0x00);
175 msleep(TPM_WTX_MSLEEP_TIME);
176}
177
178static void tpm_wtx_abort(struct tpm_chip *chip)
179{
180 dev_info(&chip->pci_dev->dev, "Aborting WTX\n");
181 wait_and_send(chip, TPM_VL_VER);
182 wait_and_send(chip, TPM_CTRL_WTX_ABORT);
183 wait_and_send(chip, 0x00);
184 wait_and_send(chip, 0x00);
185 number_of_wtx = 0;
186 msleep(TPM_WTX_MSLEEP_TIME);
187}
188
189static int tpm_inf_recv(struct tpm_chip *chip, u8 * buf, size_t count)
190{
191 int i;
192 int ret;
193 u32 size = 0;
194
195recv_begin:
196 /* start receiving header */
197 for (i = 0; i < 4; i++) {
198 ret = wait(chip, STAT_RDA);
199 if (ret)
200 return -EIO;
201 buf[i] = inb(chip->vendor->base + RDFIFO);
202 }
203
204 if (buf[0] != TPM_VL_VER) {
205 dev_err(&chip->pci_dev->dev,
206 "Wrong transport protocol implementation!\n");
207 return -EIO;
208 }
209
210 if (buf[1] == TPM_CTRL_DATA) {
211 /* size of the data received */
212 size = ((buf[2] << 8) | buf[3]);
213
214 for (i = 0; i < size; i++) {
215 wait(chip, STAT_RDA);
216 buf[i] = inb(chip->vendor->base + RDFIFO);
217 }
218
219 if ((size == 0x6D00) && (buf[1] == 0x80)) {
220 dev_err(&chip->pci_dev->dev,
221 "Error handling on vendor layer!\n");
222 return -EIO;
223 }
224
225 for (i = 0; i < size; i++)
226 buf[i] = buf[i + 6];
227
228 size = size - 6;
229 return size;
230 }
231
232 if (buf[1] == TPM_CTRL_WTX) {
233 dev_info(&chip->pci_dev->dev, "WTX-package received\n");
234 if (number_of_wtx < TPM_MAX_WTX_PACKAGES) {
235 tpm_wtx(chip);
236 goto recv_begin;
237 } else {
238 tpm_wtx_abort(chip);
239 goto recv_begin;
240 }
241 }
242
243 if (buf[1] == TPM_CTRL_WTX_ABORT_ACK) {
244 dev_info(&chip->pci_dev->dev, "WTX-abort acknowledged\n");
245 return size;
246 }
247
248 if (buf[1] == TPM_CTRL_ERROR) {
249 dev_err(&chip->pci_dev->dev, "ERROR-package received:\n");
250 if (buf[4] == TPM_INF_NAK)
251 dev_err(&chip->pci_dev->dev,
252 "-> Negative acknowledgement"
253 " - retransmit command!\n");
254 return -EIO;
255 }
256 return -EIO;
257}
258
259static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count)
260{
261 int i;
262 int ret;
263 u8 count_high, count_low, count_4, count_3, count_2, count_1;
264
265 /* Disabling Reset, LP and IRQC */
266 outb(RESET_LP_IRQC_DISABLE, chip->vendor->base + CMD);
267
268 ret = empty_fifo(chip, 1);
269 if (ret) {
270 dev_err(&chip->pci_dev->dev, "Timeout while clearing FIFO\n");
271 return -EIO;
272 }
273
274 ret = wait(chip, STAT_XFE);
275 if (ret)
276 return -EIO;
277
278 count_4 = (count & 0xff000000) >> 24;
279 count_3 = (count & 0x00ff0000) >> 16;
280 count_2 = (count & 0x0000ff00) >> 8;
281 count_1 = (count & 0x000000ff);
282 count_high = ((count + 6) & 0xffffff00) >> 8;
283 count_low = ((count + 6) & 0x000000ff);
284
285 /* Sending Header */
286 wait_and_send(chip, TPM_VL_VER);
287 wait_and_send(chip, TPM_CTRL_DATA);
288 wait_and_send(chip, count_high);
289 wait_and_send(chip, count_low);
290
291 /* Sending Data Header */
292 wait_and_send(chip, TPM_VL_VER);
293 wait_and_send(chip, TPM_VL_CHANNEL_TPM);
294 wait_and_send(chip, count_4);
295 wait_and_send(chip, count_3);
296 wait_and_send(chip, count_2);
297 wait_and_send(chip, count_1);
298
299 /* Sending Data */
300 for (i = 0; i < count; i++) {
301 wait_and_send(chip, buf[i]);
302 }
303 return count;
304}
305
306static void tpm_inf_cancel(struct tpm_chip *chip)
307{
308 /* Nothing yet!
309 This has something to do with the internal functions
310 of the TPM. Abort isn't really necessary...
311 */
312}
313
314static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
315static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
316static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
317static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
318
319static struct attribute *inf_attrs[] = {
320 &dev_attr_pubek.attr,
321 &dev_attr_pcrs.attr,
322 &dev_attr_caps.attr,
323 &dev_attr_cancel.attr,
324 NULL,
325};
326
327static struct attribute_group inf_attr_grp = {.attrs = inf_attrs };
328
329static struct file_operations inf_ops = {
330 .owner = THIS_MODULE,
331 .llseek = no_llseek,
332 .open = tpm_open,
333 .read = tpm_read,
334 .write = tpm_write,
335 .release = tpm_release,
336};
337
338static struct tpm_vendor_specific tpm_inf = {
339 .recv = tpm_inf_recv,
340 .send = tpm_inf_send,
341 .cancel = tpm_inf_cancel,
342 .req_complete_mask = 0,
343 .req_complete_val = 0,
344 .attr_group = &inf_attr_grp,
345 .miscdev = {.fops = &inf_ops,},
346};
347
348static int __devinit tpm_inf_probe(struct pci_dev *pci_dev,
349 const struct pci_device_id *pci_id)
350{
351 int rc = 0;
352 u8 iol, ioh;
353 int vendorid[2];
354 int version[2];
355 int productid[2];
356
357 if (pci_enable_device(pci_dev))
358 return -EIO;
359
360 dev_info(&pci_dev->dev, "LPC-bus found at 0x%x\n", pci_id->device);
361
362 /* query chip for its vendor, its version number a.s.o. */
363 outb(ENABLE_REGISTER_PAIR, TPM_ADDR);
364 outb(IDVENL, TPM_ADDR);
365 vendorid[1] = inb(TPM_DATA);
366 outb(IDVENH, TPM_ADDR);
367 vendorid[0] = inb(TPM_DATA);
368 outb(IDPDL, TPM_ADDR);
369 productid[1] = inb(TPM_DATA);
370 outb(IDPDH, TPM_ADDR);
371 productid[0] = inb(TPM_DATA);
372 outb(CHIP_ID1, TPM_ADDR);
373 version[1] = inb(TPM_DATA);
374 outb(CHIP_ID2, TPM_ADDR);
375 version[0] = inb(TPM_DATA);
376
377 if ((vendorid[0] << 8 | vendorid[1]) == (TCPA_INFINEON_DEV_VEN_VALUE)) {
378
379 /* read IO-ports from TPM */
380 outb(IOLIMH, TPM_ADDR);
381 ioh = inb(TPM_DATA);
382 outb(IOLIML, TPM_ADDR);
383 iol = inb(TPM_DATA);
384 tpm_inf.base = (ioh << 8) | iol;
385
386 if (tpm_inf.base == 0) {
387 dev_err(&pci_dev->dev, "No IO-ports set!\n");
388 pci_disable_device(pci_dev);
389 return -ENODEV;
390 }
391
392 /* activate register */
393 outb(DAR, TPM_ADDR);
394 outb(0x01, TPM_DATA);
395 outb(DISABLE_REGISTER_PAIR, TPM_ADDR);
396
397 /* disable RESET, LP and IRQC */
398 outb(RESET_LP_IRQC_DISABLE, tpm_inf.base + CMD);
399
400 /* Finally, we're done, print some infos */
401 dev_info(&pci_dev->dev, "TPM found: "
402 "io base 0x%x, "
403 "chip version %02x%02x, "
404 "vendor id %x%x (Infineon), "
405 "product id %02x%02x"
406 "%s\n",
407 tpm_inf.base,
408 version[0], version[1],
409 vendorid[0], vendorid[1],
410 productid[0], productid[1], ((productid[0] == 0)
411 && (productid[1] ==
412 6)) ?
413 " (SLD 9630 TT 1.1)" : "");
414
415 rc = tpm_register_hardware(pci_dev, &tpm_inf);
416 if (rc < 0) {
417 pci_disable_device(pci_dev);
418 return -ENODEV;
419 }
420 return 0;
421 } else {
422 dev_info(&pci_dev->dev, "No Infineon TPM found!\n");
423 pci_disable_device(pci_dev);
424 return -ENODEV;
425 }
426}
427
428static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
429 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
430 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
431 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
432 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
433 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
434 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_0)},
435 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1)},
436 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_2)},
437 {0,}
438};
439
440MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
441
442static struct pci_driver inf_pci_driver = {
443 .name = "tpm_inf",
444 .id_table = tpm_pci_tbl,
445 .probe = tpm_inf_probe,
446 .remove = __devexit_p(tpm_remove),
447 .suspend = tpm_pm_suspend,
448 .resume = tpm_pm_resume,
449};
450
451static int __init init_inf(void)
452{
453 return pci_register_driver(&inf_pci_driver);
454}
455
456static void __exit cleanup_inf(void)
457{
458 pci_unregister_driver(&inf_pci_driver);
459}
460
461module_init(init_inf);
462module_exit(cleanup_inf);
463
464MODULE_AUTHOR("Marcel Selhorst <selhorst@crypto.rub.de>");
465MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT");
466MODULE_VERSION("1.4");
467MODULE_LICENSE("GPL");