diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/tpm/tpm.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/tpm/tpm.c')
-rw-r--r-- | drivers/char/tpm/tpm.c | 697 |
1 files changed, 697 insertions, 0 deletions
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c new file mode 100644 index 000000000000..8318268169d6 --- /dev/null +++ b/drivers/char/tpm/tpm.c | |||
@@ -0,0 +1,697 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004 IBM Corporation | ||
3 | * | ||
4 | * Authors: | ||
5 | * Leendert van Doorn <leendert@watson.ibm.com> | ||
6 | * Dave Safford <safford@watson.ibm.com> | ||
7 | * Reiner Sailer <sailer@watson.ibm.com> | ||
8 | * Kylene Hall <kjhall@us.ibm.com> | ||
9 | * | ||
10 | * Maintained by: <tpmdd_devel@lists.sourceforge.net> | ||
11 | * | ||
12 | * Device driver for TCG/TCPA TPM (trusted platform module). | ||
13 | * Specifications at www.trustedcomputinggroup.org | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or | ||
16 | * modify it under the terms of the GNU General Public License as | ||
17 | * published by the Free Software Foundation, version 2 of the | ||
18 | * License. | ||
19 | * | ||
20 | * Note, the TPM chip is not interrupt driven (only polling) | ||
21 | * and can have very long timeouts (minutes!). Hence the unusual | ||
22 | * calls to schedule_timeout. | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #include <linux/sched.h> | ||
27 | #include <linux/poll.h> | ||
28 | #include <linux/spinlock.h> | ||
29 | #include "tpm.h" | ||
30 | |||
31 | #define TPM_MINOR 224 /* officially assigned */ | ||
32 | |||
33 | #define TPM_BUFSIZE 2048 | ||
34 | |||
35 | /* PCI configuration addresses */ | ||
36 | #define PCI_GEN_PMCON_1 0xA0 | ||
37 | #define PCI_GEN1_DEC 0xE4 | ||
38 | #define PCI_LPC_EN 0xE6 | ||
39 | #define PCI_GEN2_DEC 0xEC | ||
40 | |||
41 | static LIST_HEAD(tpm_chip_list); | ||
42 | static DEFINE_SPINLOCK(driver_lock); | ||
43 | static int dev_mask[32]; | ||
44 | |||
45 | static void user_reader_timeout(unsigned long ptr) | ||
46 | { | ||
47 | struct tpm_chip *chip = (struct tpm_chip *) ptr; | ||
48 | |||
49 | down(&chip->buffer_mutex); | ||
50 | atomic_set(&chip->data_pending, 0); | ||
51 | memset(chip->data_buffer, 0, TPM_BUFSIZE); | ||
52 | up(&chip->buffer_mutex); | ||
53 | } | ||
54 | |||
55 | void tpm_time_expired(unsigned long ptr) | ||
56 | { | ||
57 | int *exp = (int *) ptr; | ||
58 | *exp = 1; | ||
59 | } | ||
60 | |||
61 | EXPORT_SYMBOL_GPL(tpm_time_expired); | ||
62 | |||
63 | /* | ||
64 | * Initialize the LPC bus and enable the TPM ports | ||
65 | */ | ||
66 | int tpm_lpc_bus_init(struct pci_dev *pci_dev, u16 base) | ||
67 | { | ||
68 | u32 lpcenable, tmp; | ||
69 | int is_lpcm = 0; | ||
70 | |||
71 | switch (pci_dev->vendor) { | ||
72 | case PCI_VENDOR_ID_INTEL: | ||
73 | switch (pci_dev->device) { | ||
74 | case PCI_DEVICE_ID_INTEL_82801CA_12: | ||
75 | case PCI_DEVICE_ID_INTEL_82801DB_12: | ||
76 | is_lpcm = 1; | ||
77 | break; | ||
78 | } | ||
79 | /* init ICH (enable LPC) */ | ||
80 | pci_read_config_dword(pci_dev, PCI_GEN1_DEC, &lpcenable); | ||
81 | lpcenable |= 0x20000000; | ||
82 | pci_write_config_dword(pci_dev, PCI_GEN1_DEC, lpcenable); | ||
83 | |||
84 | if (is_lpcm) { | ||
85 | pci_read_config_dword(pci_dev, PCI_GEN1_DEC, | ||
86 | &lpcenable); | ||
87 | if ((lpcenable & 0x20000000) == 0) { | ||
88 | dev_err(&pci_dev->dev, | ||
89 | "cannot enable LPC\n"); | ||
90 | return -ENODEV; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | /* initialize TPM registers */ | ||
95 | pci_read_config_dword(pci_dev, PCI_GEN2_DEC, &tmp); | ||
96 | |||
97 | if (!is_lpcm) | ||
98 | tmp = (tmp & 0xFFFF0000) | (base & 0xFFF0); | ||
99 | else | ||
100 | tmp = | ||
101 | (tmp & 0xFFFF0000) | (base & 0xFFF0) | | ||
102 | 0x00000001; | ||
103 | |||
104 | pci_write_config_dword(pci_dev, PCI_GEN2_DEC, tmp); | ||
105 | |||
106 | if (is_lpcm) { | ||
107 | pci_read_config_dword(pci_dev, PCI_GEN_PMCON_1, | ||
108 | &tmp); | ||
109 | tmp |= 0x00000004; /* enable CLKRUN */ | ||
110 | pci_write_config_dword(pci_dev, PCI_GEN_PMCON_1, | ||
111 | tmp); | ||
112 | } | ||
113 | tpm_write_index(0x0D, 0x55); /* unlock 4F */ | ||
114 | tpm_write_index(0x0A, 0x00); /* int disable */ | ||
115 | tpm_write_index(0x08, base); /* base addr lo */ | ||
116 | tpm_write_index(0x09, (base & 0xFF00) >> 8); /* base addr hi */ | ||
117 | tpm_write_index(0x0D, 0xAA); /* lock 4F */ | ||
118 | break; | ||
119 | case PCI_VENDOR_ID_AMD: | ||
120 | /* nothing yet */ | ||
121 | break; | ||
122 | } | ||
123 | |||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | EXPORT_SYMBOL_GPL(tpm_lpc_bus_init); | ||
128 | |||
129 | /* | ||
130 | * Internal kernel interface to transmit TPM commands | ||
131 | */ | ||
132 | static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, | ||
133 | size_t bufsiz) | ||
134 | { | ||
135 | ssize_t len; | ||
136 | u32 count; | ||
137 | __be32 *native_size; | ||
138 | |||
139 | native_size = (__force __be32 *) (buf + 2); | ||
140 | count = be32_to_cpu(*native_size); | ||
141 | |||
142 | if (count == 0) | ||
143 | return -ENODATA; | ||
144 | if (count > bufsiz) { | ||
145 | dev_err(&chip->pci_dev->dev, | ||
146 | "invalid count value %x %x \n", count, bufsiz); | ||
147 | return -E2BIG; | ||
148 | } | ||
149 | |||
150 | down(&chip->tpm_mutex); | ||
151 | |||
152 | if ((len = chip->vendor->send(chip, (u8 *) buf, count)) < 0) { | ||
153 | dev_err(&chip->pci_dev->dev, | ||
154 | "tpm_transmit: tpm_send: error %d\n", len); | ||
155 | return len; | ||
156 | } | ||
157 | |||
158 | down(&chip->timer_manipulation_mutex); | ||
159 | chip->time_expired = 0; | ||
160 | init_timer(&chip->device_timer); | ||
161 | chip->device_timer.function = tpm_time_expired; | ||
162 | chip->device_timer.expires = jiffies + 2 * 60 * HZ; | ||
163 | chip->device_timer.data = (unsigned long) &chip->time_expired; | ||
164 | add_timer(&chip->device_timer); | ||
165 | up(&chip->timer_manipulation_mutex); | ||
166 | |||
167 | do { | ||
168 | u8 status = inb(chip->vendor->base + 1); | ||
169 | if ((status & chip->vendor->req_complete_mask) == | ||
170 | chip->vendor->req_complete_val) { | ||
171 | down(&chip->timer_manipulation_mutex); | ||
172 | del_singleshot_timer_sync(&chip->device_timer); | ||
173 | up(&chip->timer_manipulation_mutex); | ||
174 | goto out_recv; | ||
175 | } | ||
176 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
177 | schedule_timeout(TPM_TIMEOUT); | ||
178 | rmb(); | ||
179 | } while (!chip->time_expired); | ||
180 | |||
181 | |||
182 | chip->vendor->cancel(chip); | ||
183 | dev_err(&chip->pci_dev->dev, "Time expired\n"); | ||
184 | up(&chip->tpm_mutex); | ||
185 | return -EIO; | ||
186 | |||
187 | out_recv: | ||
188 | len = chip->vendor->recv(chip, (u8 *) buf, bufsiz); | ||
189 | if (len < 0) | ||
190 | dev_err(&chip->pci_dev->dev, | ||
191 | "tpm_transmit: tpm_recv: error %d\n", len); | ||
192 | up(&chip->tpm_mutex); | ||
193 | return len; | ||
194 | } | ||
195 | |||
196 | #define TPM_DIGEST_SIZE 20 | ||
197 | #define CAP_PCR_RESULT_SIZE 18 | ||
198 | static u8 cap_pcr[] = { | ||
199 | 0, 193, /* TPM_TAG_RQU_COMMAND */ | ||
200 | 0, 0, 0, 22, /* length */ | ||
201 | 0, 0, 0, 101, /* TPM_ORD_GetCapability */ | ||
202 | 0, 0, 0, 5, | ||
203 | 0, 0, 0, 4, | ||
204 | 0, 0, 1, 1 | ||
205 | }; | ||
206 | |||
207 | #define READ_PCR_RESULT_SIZE 30 | ||
208 | static u8 pcrread[] = { | ||
209 | 0, 193, /* TPM_TAG_RQU_COMMAND */ | ||
210 | 0, 0, 0, 14, /* length */ | ||
211 | 0, 0, 0, 21, /* TPM_ORD_PcrRead */ | ||
212 | 0, 0, 0, 0 /* PCR index */ | ||
213 | }; | ||
214 | |||
215 | static ssize_t show_pcrs(struct device *dev, char *buf) | ||
216 | { | ||
217 | u8 data[READ_PCR_RESULT_SIZE]; | ||
218 | ssize_t len; | ||
219 | int i, j, index, num_pcrs; | ||
220 | char *str = buf; | ||
221 | |||
222 | struct tpm_chip *chip = | ||
223 | pci_get_drvdata(container_of(dev, struct pci_dev, dev)); | ||
224 | if (chip == NULL) | ||
225 | return -ENODEV; | ||
226 | |||
227 | memcpy(data, cap_pcr, sizeof(cap_pcr)); | ||
228 | if ((len = tpm_transmit(chip, data, sizeof(data))) | ||
229 | < CAP_PCR_RESULT_SIZE) | ||
230 | return len; | ||
231 | |||
232 | num_pcrs = be32_to_cpu(*((__force __be32 *) (data + 14))); | ||
233 | |||
234 | for (i = 0; i < num_pcrs; i++) { | ||
235 | memcpy(data, pcrread, sizeof(pcrread)); | ||
236 | index = cpu_to_be32(i); | ||
237 | memcpy(data + 10, &index, 4); | ||
238 | if ((len = tpm_transmit(chip, data, sizeof(data))) | ||
239 | < READ_PCR_RESULT_SIZE) | ||
240 | return len; | ||
241 | str += sprintf(str, "PCR-%02d: ", i); | ||
242 | for (j = 0; j < TPM_DIGEST_SIZE; j++) | ||
243 | str += sprintf(str, "%02X ", *(data + 10 + j)); | ||
244 | str += sprintf(str, "\n"); | ||
245 | } | ||
246 | return str - buf; | ||
247 | } | ||
248 | |||
249 | static DEVICE_ATTR(pcrs, S_IRUGO, show_pcrs, NULL); | ||
250 | |||
251 | #define READ_PUBEK_RESULT_SIZE 314 | ||
252 | static u8 readpubek[] = { | ||
253 | 0, 193, /* TPM_TAG_RQU_COMMAND */ | ||
254 | 0, 0, 0, 30, /* length */ | ||
255 | 0, 0, 0, 124, /* TPM_ORD_ReadPubek */ | ||
256 | }; | ||
257 | |||
258 | static ssize_t show_pubek(struct device *dev, char *buf) | ||
259 | { | ||
260 | u8 data[READ_PUBEK_RESULT_SIZE]; | ||
261 | ssize_t len; | ||
262 | __be32 *native_val; | ||
263 | int i; | ||
264 | char *str = buf; | ||
265 | |||
266 | struct tpm_chip *chip = | ||
267 | pci_get_drvdata(container_of(dev, struct pci_dev, dev)); | ||
268 | if (chip == NULL) | ||
269 | return -ENODEV; | ||
270 | |||
271 | memcpy(data, readpubek, sizeof(readpubek)); | ||
272 | memset(data + sizeof(readpubek), 0, 20); /* zero nonce */ | ||
273 | |||
274 | if ((len = tpm_transmit(chip, data, sizeof(data))) < | ||
275 | READ_PUBEK_RESULT_SIZE) | ||
276 | return len; | ||
277 | |||
278 | /* | ||
279 | ignore header 10 bytes | ||
280 | algorithm 32 bits (1 == RSA ) | ||
281 | encscheme 16 bits | ||
282 | sigscheme 16 bits | ||
283 | parameters (RSA 12->bytes: keybit, #primes, expbit) | ||
284 | keylenbytes 32 bits | ||
285 | 256 byte modulus | ||
286 | ignore checksum 20 bytes | ||
287 | */ | ||
288 | |||
289 | native_val = (__force __be32 *) (data + 34); | ||
290 | |||
291 | str += | ||
292 | sprintf(str, | ||
293 | "Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n" | ||
294 | "Sigscheme: %02X %02X\nParameters: %02X %02X %02X %02X" | ||
295 | " %02X %02X %02X %02X %02X %02X %02X %02X\n" | ||
296 | "Modulus length: %d\nModulus: \n", | ||
297 | data[10], data[11], data[12], data[13], data[14], | ||
298 | data[15], data[16], data[17], data[22], data[23], | ||
299 | data[24], data[25], data[26], data[27], data[28], | ||
300 | data[29], data[30], data[31], data[32], data[33], | ||
301 | be32_to_cpu(*native_val) | ||
302 | ); | ||
303 | |||
304 | for (i = 0; i < 256; i++) { | ||
305 | str += sprintf(str, "%02X ", data[i + 39]); | ||
306 | if ((i + 1) % 16 == 0) | ||
307 | str += sprintf(str, "\n"); | ||
308 | } | ||
309 | return str - buf; | ||
310 | } | ||
311 | |||
312 | static DEVICE_ATTR(pubek, S_IRUGO, show_pubek, NULL); | ||
313 | |||
314 | #define CAP_VER_RESULT_SIZE 18 | ||
315 | static u8 cap_version[] = { | ||
316 | 0, 193, /* TPM_TAG_RQU_COMMAND */ | ||
317 | 0, 0, 0, 18, /* length */ | ||
318 | 0, 0, 0, 101, /* TPM_ORD_GetCapability */ | ||
319 | 0, 0, 0, 6, | ||
320 | 0, 0, 0, 0 | ||
321 | }; | ||
322 | |||
323 | #define CAP_MANUFACTURER_RESULT_SIZE 18 | ||
324 | static u8 cap_manufacturer[] = { | ||
325 | 0, 193, /* TPM_TAG_RQU_COMMAND */ | ||
326 | 0, 0, 0, 22, /* length */ | ||
327 | 0, 0, 0, 101, /* TPM_ORD_GetCapability */ | ||
328 | 0, 0, 0, 5, | ||
329 | 0, 0, 0, 4, | ||
330 | 0, 0, 1, 3 | ||
331 | }; | ||
332 | |||
333 | static ssize_t show_caps(struct device *dev, char *buf) | ||
334 | { | ||
335 | u8 data[READ_PUBEK_RESULT_SIZE]; | ||
336 | ssize_t len; | ||
337 | char *str = buf; | ||
338 | |||
339 | struct tpm_chip *chip = | ||
340 | pci_get_drvdata(container_of(dev, struct pci_dev, dev)); | ||
341 | if (chip == NULL) | ||
342 | return -ENODEV; | ||
343 | |||
344 | memcpy(data, cap_manufacturer, sizeof(cap_manufacturer)); | ||
345 | |||
346 | if ((len = tpm_transmit(chip, data, sizeof(data))) < | ||
347 | CAP_MANUFACTURER_RESULT_SIZE) | ||
348 | return len; | ||
349 | |||
350 | str += sprintf(str, "Manufacturer: 0x%x\n", | ||
351 | be32_to_cpu(*(data + 14))); | ||
352 | |||
353 | memcpy(data, cap_version, sizeof(cap_version)); | ||
354 | |||
355 | if ((len = tpm_transmit(chip, data, sizeof(data))) < | ||
356 | CAP_VER_RESULT_SIZE) | ||
357 | return len; | ||
358 | |||
359 | str += | ||
360 | sprintf(str, "TCG version: %d.%d\nFirmware version: %d.%d\n", | ||
361 | (int) data[14], (int) data[15], (int) data[16], | ||
362 | (int) data[17]); | ||
363 | |||
364 | return str - buf; | ||
365 | } | ||
366 | |||
367 | static DEVICE_ATTR(caps, S_IRUGO, show_caps, NULL); | ||
368 | |||
369 | /* | ||
370 | * Device file system interface to the TPM | ||
371 | */ | ||
372 | int tpm_open(struct inode *inode, struct file *file) | ||
373 | { | ||
374 | int rc = 0, minor = iminor(inode); | ||
375 | struct tpm_chip *chip = NULL, *pos; | ||
376 | |||
377 | spin_lock(&driver_lock); | ||
378 | |||
379 | list_for_each_entry(pos, &tpm_chip_list, list) { | ||
380 | if (pos->vendor->miscdev.minor == minor) { | ||
381 | chip = pos; | ||
382 | break; | ||
383 | } | ||
384 | } | ||
385 | |||
386 | if (chip == NULL) { | ||
387 | rc = -ENODEV; | ||
388 | goto err_out; | ||
389 | } | ||
390 | |||
391 | if (chip->num_opens) { | ||
392 | dev_dbg(&chip->pci_dev->dev, | ||
393 | "Another process owns this TPM\n"); | ||
394 | rc = -EBUSY; | ||
395 | goto err_out; | ||
396 | } | ||
397 | |||
398 | chip->num_opens++; | ||
399 | pci_dev_get(chip->pci_dev); | ||
400 | |||
401 | spin_unlock(&driver_lock); | ||
402 | |||
403 | chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); | ||
404 | if (chip->data_buffer == NULL) { | ||
405 | chip->num_opens--; | ||
406 | pci_dev_put(chip->pci_dev); | ||
407 | return -ENOMEM; | ||
408 | } | ||
409 | |||
410 | atomic_set(&chip->data_pending, 0); | ||
411 | |||
412 | file->private_data = chip; | ||
413 | return 0; | ||
414 | |||
415 | err_out: | ||
416 | spin_unlock(&driver_lock); | ||
417 | return rc; | ||
418 | } | ||
419 | |||
420 | EXPORT_SYMBOL_GPL(tpm_open); | ||
421 | |||
422 | int tpm_release(struct inode *inode, struct file *file) | ||
423 | { | ||
424 | struct tpm_chip *chip = file->private_data; | ||
425 | |||
426 | file->private_data = NULL; | ||
427 | |||
428 | spin_lock(&driver_lock); | ||
429 | chip->num_opens--; | ||
430 | spin_unlock(&driver_lock); | ||
431 | |||
432 | down(&chip->timer_manipulation_mutex); | ||
433 | if (timer_pending(&chip->user_read_timer)) | ||
434 | del_singleshot_timer_sync(&chip->user_read_timer); | ||
435 | else if (timer_pending(&chip->device_timer)) | ||
436 | del_singleshot_timer_sync(&chip->device_timer); | ||
437 | up(&chip->timer_manipulation_mutex); | ||
438 | |||
439 | kfree(chip->data_buffer); | ||
440 | atomic_set(&chip->data_pending, 0); | ||
441 | |||
442 | pci_dev_put(chip->pci_dev); | ||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | EXPORT_SYMBOL_GPL(tpm_release); | ||
447 | |||
448 | ssize_t tpm_write(struct file * file, const char __user * buf, | ||
449 | size_t size, loff_t * off) | ||
450 | { | ||
451 | struct tpm_chip *chip = file->private_data; | ||
452 | int in_size = size, out_size; | ||
453 | |||
454 | /* cannot perform a write until the read has cleared | ||
455 | either via tpm_read or a user_read_timer timeout */ | ||
456 | while (atomic_read(&chip->data_pending) != 0) { | ||
457 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
458 | schedule_timeout(TPM_TIMEOUT); | ||
459 | } | ||
460 | |||
461 | down(&chip->buffer_mutex); | ||
462 | |||
463 | if (in_size > TPM_BUFSIZE) | ||
464 | in_size = TPM_BUFSIZE; | ||
465 | |||
466 | if (copy_from_user | ||
467 | (chip->data_buffer, (void __user *) buf, in_size)) { | ||
468 | up(&chip->buffer_mutex); | ||
469 | return -EFAULT; | ||
470 | } | ||
471 | |||
472 | /* atomic tpm command send and result receive */ | ||
473 | out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE); | ||
474 | |||
475 | atomic_set(&chip->data_pending, out_size); | ||
476 | up(&chip->buffer_mutex); | ||
477 | |||
478 | /* Set a timeout by which the reader must come claim the result */ | ||
479 | down(&chip->timer_manipulation_mutex); | ||
480 | init_timer(&chip->user_read_timer); | ||
481 | chip->user_read_timer.function = user_reader_timeout; | ||
482 | chip->user_read_timer.data = (unsigned long) chip; | ||
483 | chip->user_read_timer.expires = jiffies + (60 * HZ); | ||
484 | add_timer(&chip->user_read_timer); | ||
485 | up(&chip->timer_manipulation_mutex); | ||
486 | |||
487 | return in_size; | ||
488 | } | ||
489 | |||
490 | EXPORT_SYMBOL_GPL(tpm_write); | ||
491 | |||
492 | ssize_t tpm_read(struct file * file, char __user * buf, | ||
493 | size_t size, loff_t * off) | ||
494 | { | ||
495 | struct tpm_chip *chip = file->private_data; | ||
496 | int ret_size = -ENODATA; | ||
497 | |||
498 | if (atomic_read(&chip->data_pending) != 0) { /* Result available */ | ||
499 | down(&chip->timer_manipulation_mutex); | ||
500 | del_singleshot_timer_sync(&chip->user_read_timer); | ||
501 | up(&chip->timer_manipulation_mutex); | ||
502 | |||
503 | down(&chip->buffer_mutex); | ||
504 | |||
505 | ret_size = atomic_read(&chip->data_pending); | ||
506 | atomic_set(&chip->data_pending, 0); | ||
507 | |||
508 | if (ret_size == 0) /* timeout just occurred */ | ||
509 | ret_size = -ETIME; | ||
510 | else if (ret_size > 0) { /* relay data */ | ||
511 | if (size < ret_size) | ||
512 | ret_size = size; | ||
513 | |||
514 | if (copy_to_user((void __user *) buf, | ||
515 | chip->data_buffer, ret_size)) { | ||
516 | ret_size = -EFAULT; | ||
517 | } | ||
518 | } | ||
519 | up(&chip->buffer_mutex); | ||
520 | } | ||
521 | |||
522 | return ret_size; | ||
523 | } | ||
524 | |||
525 | EXPORT_SYMBOL_GPL(tpm_read); | ||
526 | |||
527 | void __devexit tpm_remove(struct pci_dev *pci_dev) | ||
528 | { | ||
529 | struct tpm_chip *chip = pci_get_drvdata(pci_dev); | ||
530 | |||
531 | if (chip == NULL) { | ||
532 | dev_err(&pci_dev->dev, "No device data found\n"); | ||
533 | return; | ||
534 | } | ||
535 | |||
536 | spin_lock(&driver_lock); | ||
537 | |||
538 | list_del(&chip->list); | ||
539 | |||
540 | spin_unlock(&driver_lock); | ||
541 | |||
542 | pci_set_drvdata(pci_dev, NULL); | ||
543 | misc_deregister(&chip->vendor->miscdev); | ||
544 | |||
545 | device_remove_file(&pci_dev->dev, &dev_attr_pubek); | ||
546 | device_remove_file(&pci_dev->dev, &dev_attr_pcrs); | ||
547 | device_remove_file(&pci_dev->dev, &dev_attr_caps); | ||
548 | |||
549 | pci_disable_device(pci_dev); | ||
550 | |||
551 | dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32)); | ||
552 | |||
553 | kfree(chip); | ||
554 | |||
555 | pci_dev_put(pci_dev); | ||
556 | } | ||
557 | |||
558 | EXPORT_SYMBOL_GPL(tpm_remove); | ||
559 | |||
560 | static u8 savestate[] = { | ||
561 | 0, 193, /* TPM_TAG_RQU_COMMAND */ | ||
562 | 0, 0, 0, 10, /* blob length (in bytes) */ | ||
563 | 0, 0, 0, 152 /* TPM_ORD_SaveState */ | ||
564 | }; | ||
565 | |||
566 | /* | ||
567 | * We are about to suspend. Save the TPM state | ||
568 | * so that it can be restored. | ||
569 | */ | ||
570 | int tpm_pm_suspend(struct pci_dev *pci_dev, u32 pm_state) | ||
571 | { | ||
572 | struct tpm_chip *chip = pci_get_drvdata(pci_dev); | ||
573 | if (chip == NULL) | ||
574 | return -ENODEV; | ||
575 | |||
576 | tpm_transmit(chip, savestate, sizeof(savestate)); | ||
577 | return 0; | ||
578 | } | ||
579 | |||
580 | EXPORT_SYMBOL_GPL(tpm_pm_suspend); | ||
581 | |||
582 | /* | ||
583 | * Resume from a power safe. The BIOS already restored | ||
584 | * the TPM state. | ||
585 | */ | ||
586 | int tpm_pm_resume(struct pci_dev *pci_dev) | ||
587 | { | ||
588 | struct tpm_chip *chip = pci_get_drvdata(pci_dev); | ||
589 | |||
590 | if (chip == NULL) | ||
591 | return -ENODEV; | ||
592 | |||
593 | spin_lock(&driver_lock); | ||
594 | tpm_lpc_bus_init(pci_dev, chip->vendor->base); | ||
595 | spin_unlock(&driver_lock); | ||
596 | |||
597 | return 0; | ||
598 | } | ||
599 | |||
600 | EXPORT_SYMBOL_GPL(tpm_pm_resume); | ||
601 | |||
602 | /* | ||
603 | * Called from tpm_<specific>.c probe function only for devices | ||
604 | * the driver has determined it should claim. Prior to calling | ||
605 | * this function the specific probe function has called pci_enable_device | ||
606 | * upon errant exit from this function specific probe function should call | ||
607 | * pci_disable_device | ||
608 | */ | ||
609 | int tpm_register_hardware(struct pci_dev *pci_dev, | ||
610 | struct tpm_vendor_specific *entry) | ||
611 | { | ||
612 | char devname[7]; | ||
613 | struct tpm_chip *chip; | ||
614 | int i, j; | ||
615 | |||
616 | /* Driver specific per-device data */ | ||
617 | chip = kmalloc(sizeof(*chip), GFP_KERNEL); | ||
618 | if (chip == NULL) | ||
619 | return -ENOMEM; | ||
620 | |||
621 | memset(chip, 0, sizeof(struct tpm_chip)); | ||
622 | |||
623 | init_MUTEX(&chip->buffer_mutex); | ||
624 | init_MUTEX(&chip->tpm_mutex); | ||
625 | init_MUTEX(&chip->timer_manipulation_mutex); | ||
626 | INIT_LIST_HEAD(&chip->list); | ||
627 | |||
628 | chip->vendor = entry; | ||
629 | |||
630 | chip->dev_num = -1; | ||
631 | |||
632 | for (i = 0; i < 32; i++) | ||
633 | for (j = 0; j < 8; j++) | ||
634 | if ((dev_mask[i] & (1 << j)) == 0) { | ||
635 | chip->dev_num = i * 32 + j; | ||
636 | dev_mask[i] |= 1 << j; | ||
637 | goto dev_num_search_complete; | ||
638 | } | ||
639 | |||
640 | dev_num_search_complete: | ||
641 | if (chip->dev_num < 0) { | ||
642 | dev_err(&pci_dev->dev, | ||
643 | "No available tpm device numbers\n"); | ||
644 | kfree(chip); | ||
645 | return -ENODEV; | ||
646 | } else if (chip->dev_num == 0) | ||
647 | chip->vendor->miscdev.minor = TPM_MINOR; | ||
648 | else | ||
649 | chip->vendor->miscdev.minor = MISC_DYNAMIC_MINOR; | ||
650 | |||
651 | snprintf(devname, sizeof(devname), "%s%d", "tpm", chip->dev_num); | ||
652 | chip->vendor->miscdev.name = devname; | ||
653 | |||
654 | chip->vendor->miscdev.dev = &(pci_dev->dev); | ||
655 | chip->pci_dev = pci_dev_get(pci_dev); | ||
656 | |||
657 | if (misc_register(&chip->vendor->miscdev)) { | ||
658 | dev_err(&chip->pci_dev->dev, | ||
659 | "unable to misc_register %s, minor %d\n", | ||
660 | chip->vendor->miscdev.name, | ||
661 | chip->vendor->miscdev.minor); | ||
662 | pci_dev_put(pci_dev); | ||
663 | kfree(chip); | ||
664 | dev_mask[i] &= !(1 << j); | ||
665 | return -ENODEV; | ||
666 | } | ||
667 | |||
668 | pci_set_drvdata(pci_dev, chip); | ||
669 | |||
670 | list_add(&chip->list, &tpm_chip_list); | ||
671 | |||
672 | device_create_file(&pci_dev->dev, &dev_attr_pubek); | ||
673 | device_create_file(&pci_dev->dev, &dev_attr_pcrs); | ||
674 | device_create_file(&pci_dev->dev, &dev_attr_caps); | ||
675 | |||
676 | return 0; | ||
677 | } | ||
678 | |||
679 | EXPORT_SYMBOL_GPL(tpm_register_hardware); | ||
680 | |||
681 | static int __init init_tpm(void) | ||
682 | { | ||
683 | return 0; | ||
684 | } | ||
685 | |||
686 | static void __exit cleanup_tpm(void) | ||
687 | { | ||
688 | |||
689 | } | ||
690 | |||
691 | module_init(init_tpm); | ||
692 | module_exit(cleanup_tpm); | ||
693 | |||
694 | MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)"); | ||
695 | MODULE_DESCRIPTION("TPM Driver"); | ||
696 | MODULE_VERSION("2.0"); | ||
697 | MODULE_LICENSE("GPL"); | ||