diff options
Diffstat (limited to 'drivers/char/tpm/tpm_tis.c')
-rw-r--r-- | drivers/char/tpm/tpm_tis.c | 182 |
1 files changed, 160 insertions, 22 deletions
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index dd21df55689d..7fc2f108f490 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/interrupt.h> | 26 | #include <linux/interrupt.h> |
27 | #include <linux/wait.h> | 27 | #include <linux/wait.h> |
28 | #include <linux/acpi.h> | 28 | #include <linux/acpi.h> |
29 | #include <linux/freezer.h> | ||
29 | #include "tpm.h" | 30 | #include "tpm.h" |
30 | 31 | ||
31 | #define TPM_HEADER_SIZE 10 | 32 | #define TPM_HEADER_SIZE 10 |
@@ -79,7 +80,7 @@ enum tis_defaults { | |||
79 | static LIST_HEAD(tis_chips); | 80 | static LIST_HEAD(tis_chips); |
80 | static DEFINE_SPINLOCK(tis_lock); | 81 | static DEFINE_SPINLOCK(tis_lock); |
81 | 82 | ||
82 | #ifdef CONFIG_ACPI | 83 | #ifdef CONFIG_PNP |
83 | static int is_itpm(struct pnp_dev *dev) | 84 | static int is_itpm(struct pnp_dev *dev) |
84 | { | 85 | { |
85 | struct acpi_device *acpi = pnp_acpi_device(dev); | 86 | struct acpi_device *acpi = pnp_acpi_device(dev); |
@@ -92,11 +93,6 @@ static int is_itpm(struct pnp_dev *dev) | |||
92 | 93 | ||
93 | return 0; | 94 | return 0; |
94 | } | 95 | } |
95 | #else | ||
96 | static int is_itpm(struct pnp_dev *dev) | ||
97 | { | ||
98 | return 0; | ||
99 | } | ||
100 | #endif | 96 | #endif |
101 | 97 | ||
102 | static int check_locality(struct tpm_chip *chip, int l) | 98 | static int check_locality(struct tpm_chip *chip, int l) |
@@ -120,7 +116,7 @@ static void release_locality(struct tpm_chip *chip, int l, int force) | |||
120 | 116 | ||
121 | static int request_locality(struct tpm_chip *chip, int l) | 117 | static int request_locality(struct tpm_chip *chip, int l) |
122 | { | 118 | { |
123 | unsigned long stop; | 119 | unsigned long stop, timeout; |
124 | long rc; | 120 | long rc; |
125 | 121 | ||
126 | if (check_locality(chip, l) >= 0) | 122 | if (check_locality(chip, l) >= 0) |
@@ -129,17 +125,25 @@ static int request_locality(struct tpm_chip *chip, int l) | |||
129 | iowrite8(TPM_ACCESS_REQUEST_USE, | 125 | iowrite8(TPM_ACCESS_REQUEST_USE, |
130 | chip->vendor.iobase + TPM_ACCESS(l)); | 126 | chip->vendor.iobase + TPM_ACCESS(l)); |
131 | 127 | ||
128 | stop = jiffies + chip->vendor.timeout_a; | ||
129 | |||
132 | if (chip->vendor.irq) { | 130 | if (chip->vendor.irq) { |
131 | again: | ||
132 | timeout = stop - jiffies; | ||
133 | if ((long)timeout <= 0) | ||
134 | return -1; | ||
133 | rc = wait_event_interruptible_timeout(chip->vendor.int_queue, | 135 | rc = wait_event_interruptible_timeout(chip->vendor.int_queue, |
134 | (check_locality | 136 | (check_locality |
135 | (chip, l) >= 0), | 137 | (chip, l) >= 0), |
136 | chip->vendor.timeout_a); | 138 | timeout); |
137 | if (rc > 0) | 139 | if (rc > 0) |
138 | return l; | 140 | return l; |
139 | 141 | if (rc == -ERESTARTSYS && freezing(current)) { | |
142 | clear_thread_flag(TIF_SIGPENDING); | ||
143 | goto again; | ||
144 | } | ||
140 | } else { | 145 | } else { |
141 | /* wait for burstcount */ | 146 | /* wait for burstcount */ |
142 | stop = jiffies + chip->vendor.timeout_a; | ||
143 | do { | 147 | do { |
144 | if (check_locality(chip, l) >= 0) | 148 | if (check_locality(chip, l) >= 0) |
145 | return l; | 149 | return l; |
@@ -196,15 +200,24 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, | |||
196 | if ((status & mask) == mask) | 200 | if ((status & mask) == mask) |
197 | return 0; | 201 | return 0; |
198 | 202 | ||
203 | stop = jiffies + timeout; | ||
204 | |||
199 | if (chip->vendor.irq) { | 205 | if (chip->vendor.irq) { |
206 | again: | ||
207 | timeout = stop - jiffies; | ||
208 | if ((long)timeout <= 0) | ||
209 | return -ETIME; | ||
200 | rc = wait_event_interruptible_timeout(*queue, | 210 | rc = wait_event_interruptible_timeout(*queue, |
201 | ((tpm_tis_status | 211 | ((tpm_tis_status |
202 | (chip) & mask) == | 212 | (chip) & mask) == |
203 | mask), timeout); | 213 | mask), timeout); |
204 | if (rc > 0) | 214 | if (rc > 0) |
205 | return 0; | 215 | return 0; |
216 | if (rc == -ERESTARTSYS && freezing(current)) { | ||
217 | clear_thread_flag(TIF_SIGPENDING); | ||
218 | goto again; | ||
219 | } | ||
206 | } else { | 220 | } else { |
207 | stop = jiffies + timeout; | ||
208 | do { | 221 | do { |
209 | msleep(TPM_TIMEOUT); | 222 | msleep(TPM_TIMEOUT); |
210 | status = tpm_tis_status(chip); | 223 | status = tpm_tis_status(chip); |
@@ -288,11 +301,10 @@ MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)"); | |||
288 | * tpm.c can skip polling for the data to be available as the interrupt is | 301 | * tpm.c can skip polling for the data to be available as the interrupt is |
289 | * waited for here | 302 | * waited for here |
290 | */ | 303 | */ |
291 | static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) | 304 | static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) |
292 | { | 305 | { |
293 | int rc, status, burstcnt; | 306 | int rc, status, burstcnt; |
294 | size_t count = 0; | 307 | size_t count = 0; |
295 | u32 ordinal; | ||
296 | 308 | ||
297 | if (request_locality(chip, 0) < 0) | 309 | if (request_locality(chip, 0) < 0) |
298 | return -EBUSY; | 310 | return -EBUSY; |
@@ -327,8 +339,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) | |||
327 | 339 | ||
328 | /* write last byte */ | 340 | /* write last byte */ |
329 | iowrite8(buf[count], | 341 | iowrite8(buf[count], |
330 | chip->vendor.iobase + | 342 | chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality)); |
331 | TPM_DATA_FIFO(chip->vendor.locality)); | ||
332 | wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, | 343 | wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, |
333 | &chip->vendor.int_queue); | 344 | &chip->vendor.int_queue); |
334 | status = tpm_tis_status(chip); | 345 | status = tpm_tis_status(chip); |
@@ -337,6 +348,28 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) | |||
337 | goto out_err; | 348 | goto out_err; |
338 | } | 349 | } |
339 | 350 | ||
351 | return 0; | ||
352 | |||
353 | out_err: | ||
354 | tpm_tis_ready(chip); | ||
355 | release_locality(chip, chip->vendor.locality, 0); | ||
356 | return rc; | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * If interrupts are used (signaled by an irq set in the vendor structure) | ||
361 | * tpm.c can skip polling for the data to be available as the interrupt is | ||
362 | * waited for here | ||
363 | */ | ||
364 | static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) | ||
365 | { | ||
366 | int rc; | ||
367 | u32 ordinal; | ||
368 | |||
369 | rc = tpm_tis_send_data(chip, buf, len); | ||
370 | if (rc < 0) | ||
371 | return rc; | ||
372 | |||
340 | /* go and do it */ | 373 | /* go and do it */ |
341 | iowrite8(TPM_STS_GO, | 374 | iowrite8(TPM_STS_GO, |
342 | chip->vendor.iobase + TPM_STS(chip->vendor.locality)); | 375 | chip->vendor.iobase + TPM_STS(chip->vendor.locality)); |
@@ -358,6 +391,47 @@ out_err: | |||
358 | return rc; | 391 | return rc; |
359 | } | 392 | } |
360 | 393 | ||
394 | /* | ||
395 | * Early probing for iTPM with STS_DATA_EXPECT flaw. | ||
396 | * Try sending command without itpm flag set and if that | ||
397 | * fails, repeat with itpm flag set. | ||
398 | */ | ||
399 | static int probe_itpm(struct tpm_chip *chip) | ||
400 | { | ||
401 | int rc = 0; | ||
402 | u8 cmd_getticks[] = { | ||
403 | 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a, | ||
404 | 0x00, 0x00, 0x00, 0xf1 | ||
405 | }; | ||
406 | size_t len = sizeof(cmd_getticks); | ||
407 | int rem_itpm = itpm; | ||
408 | |||
409 | itpm = 0; | ||
410 | |||
411 | rc = tpm_tis_send_data(chip, cmd_getticks, len); | ||
412 | if (rc == 0) | ||
413 | goto out; | ||
414 | |||
415 | tpm_tis_ready(chip); | ||
416 | release_locality(chip, chip->vendor.locality, 0); | ||
417 | |||
418 | itpm = 1; | ||
419 | |||
420 | rc = tpm_tis_send_data(chip, cmd_getticks, len); | ||
421 | if (rc == 0) { | ||
422 | dev_info(chip->dev, "Detected an iTPM.\n"); | ||
423 | rc = 1; | ||
424 | } else | ||
425 | rc = -EFAULT; | ||
426 | |||
427 | out: | ||
428 | itpm = rem_itpm; | ||
429 | tpm_tis_ready(chip); | ||
430 | release_locality(chip, chip->vendor.locality, 0); | ||
431 | |||
432 | return rc; | ||
433 | } | ||
434 | |||
361 | static const struct file_operations tis_ops = { | 435 | static const struct file_operations tis_ops = { |
362 | .owner = THIS_MODULE, | 436 | .owner = THIS_MODULE, |
363 | .llseek = no_llseek, | 437 | .llseek = no_llseek, |
@@ -376,6 +450,8 @@ static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, | |||
376 | NULL); | 450 | NULL); |
377 | static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL); | 451 | static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL); |
378 | static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); | 452 | static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); |
453 | static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL); | ||
454 | static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL); | ||
379 | 455 | ||
380 | static struct attribute *tis_attrs[] = { | 456 | static struct attribute *tis_attrs[] = { |
381 | &dev_attr_pubek.attr, | 457 | &dev_attr_pubek.attr, |
@@ -385,7 +461,9 @@ static struct attribute *tis_attrs[] = { | |||
385 | &dev_attr_owned.attr, | 461 | &dev_attr_owned.attr, |
386 | &dev_attr_temp_deactivated.attr, | 462 | &dev_attr_temp_deactivated.attr, |
387 | &dev_attr_caps.attr, | 463 | &dev_attr_caps.attr, |
388 | &dev_attr_cancel.attr, NULL, | 464 | &dev_attr_cancel.attr, |
465 | &dev_attr_durations.attr, | ||
466 | &dev_attr_timeouts.attr, NULL, | ||
389 | }; | 467 | }; |
390 | 468 | ||
391 | static struct attribute_group tis_attr_grp = { | 469 | static struct attribute_group tis_attr_grp = { |
@@ -416,7 +494,7 @@ static irqreturn_t tis_int_probe(int irq, void *dev_id) | |||
416 | if (interrupt == 0) | 494 | if (interrupt == 0) |
417 | return IRQ_NONE; | 495 | return IRQ_NONE; |
418 | 496 | ||
419 | chip->vendor.irq = irq; | 497 | chip->vendor.probed_irq = irq; |
420 | 498 | ||
421 | /* Clear interrupts handled with TPM_EOI */ | 499 | /* Clear interrupts handled with TPM_EOI */ |
422 | iowrite32(interrupt, | 500 | iowrite32(interrupt, |
@@ -464,7 +542,7 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, | |||
464 | resource_size_t len, unsigned int irq) | 542 | resource_size_t len, unsigned int irq) |
465 | { | 543 | { |
466 | u32 vendor, intfcaps, intmask; | 544 | u32 vendor, intfcaps, intmask; |
467 | int rc, i; | 545 | int rc, i, irq_s, irq_e; |
468 | struct tpm_chip *chip; | 546 | struct tpm_chip *chip; |
469 | 547 | ||
470 | if (!(chip = tpm_register_hardware(dev, &tpm_tis))) | 548 | if (!(chip = tpm_register_hardware(dev, &tpm_tis))) |
@@ -493,6 +571,14 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, | |||
493 | "1.2 TPM (device-id 0x%X, rev-id %d)\n", | 571 | "1.2 TPM (device-id 0x%X, rev-id %d)\n", |
494 | vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0))); | 572 | vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0))); |
495 | 573 | ||
574 | if (!itpm) { | ||
575 | itpm = probe_itpm(chip); | ||
576 | if (itpm < 0) { | ||
577 | rc = -ENODEV; | ||
578 | goto out_err; | ||
579 | } | ||
580 | } | ||
581 | |||
496 | if (itpm) | 582 | if (itpm) |
497 | dev_info(dev, "Intel iTPM workaround enabled\n"); | 583 | dev_info(dev, "Intel iTPM workaround enabled\n"); |
498 | 584 | ||
@@ -522,6 +608,9 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, | |||
522 | if (intfcaps & TPM_INTF_DATA_AVAIL_INT) | 608 | if (intfcaps & TPM_INTF_DATA_AVAIL_INT) |
523 | dev_dbg(dev, "\tData Avail Int Support\n"); | 609 | dev_dbg(dev, "\tData Avail Int Support\n"); |
524 | 610 | ||
611 | /* get the timeouts before testing for irqs */ | ||
612 | tpm_get_timeouts(chip); | ||
613 | |||
525 | /* INTERRUPT Setup */ | 614 | /* INTERRUPT Setup */ |
526 | init_waitqueue_head(&chip->vendor.read_queue); | 615 | init_waitqueue_head(&chip->vendor.read_queue); |
527 | init_waitqueue_head(&chip->vendor.int_queue); | 616 | init_waitqueue_head(&chip->vendor.int_queue); |
@@ -540,13 +629,19 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, | |||
540 | if (interrupts) | 629 | if (interrupts) |
541 | chip->vendor.irq = irq; | 630 | chip->vendor.irq = irq; |
542 | if (interrupts && !chip->vendor.irq) { | 631 | if (interrupts && !chip->vendor.irq) { |
543 | chip->vendor.irq = | 632 | irq_s = |
544 | ioread8(chip->vendor.iobase + | 633 | ioread8(chip->vendor.iobase + |
545 | TPM_INT_VECTOR(chip->vendor.locality)); | 634 | TPM_INT_VECTOR(chip->vendor.locality)); |
635 | if (irq_s) { | ||
636 | irq_e = irq_s; | ||
637 | } else { | ||
638 | irq_s = 3; | ||
639 | irq_e = 15; | ||
640 | } | ||
546 | 641 | ||
547 | for (i = 3; i < 16 && chip->vendor.irq == 0; i++) { | 642 | for (i = irq_s; i <= irq_e && chip->vendor.irq == 0; i++) { |
548 | iowrite8(i, chip->vendor.iobase + | 643 | iowrite8(i, chip->vendor.iobase + |
549 | TPM_INT_VECTOR(chip->vendor.locality)); | 644 | TPM_INT_VECTOR(chip->vendor.locality)); |
550 | if (request_irq | 645 | if (request_irq |
551 | (i, tis_int_probe, IRQF_SHARED, | 646 | (i, tis_int_probe, IRQF_SHARED, |
552 | chip->vendor.miscdev.name, chip) != 0) { | 647 | chip->vendor.miscdev.name, chip) != 0) { |
@@ -568,9 +663,22 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, | |||
568 | chip->vendor.iobase + | 663 | chip->vendor.iobase + |
569 | TPM_INT_ENABLE(chip->vendor.locality)); | 664 | TPM_INT_ENABLE(chip->vendor.locality)); |
570 | 665 | ||
666 | chip->vendor.probed_irq = 0; | ||
667 | |||
571 | /* Generate Interrupts */ | 668 | /* Generate Interrupts */ |
572 | tpm_gen_interrupt(chip); | 669 | tpm_gen_interrupt(chip); |
573 | 670 | ||
671 | chip->vendor.irq = chip->vendor.probed_irq; | ||
672 | |||
673 | /* free_irq will call into tis_int_probe; | ||
674 | clear all irqs we haven't seen while doing | ||
675 | tpm_gen_interrupt */ | ||
676 | iowrite32(ioread32 | ||
677 | (chip->vendor.iobase + | ||
678 | TPM_INT_STATUS(chip->vendor.locality)), | ||
679 | chip->vendor.iobase + | ||
680 | TPM_INT_STATUS(chip->vendor.locality)); | ||
681 | |||
574 | /* Turn off */ | 682 | /* Turn off */ |
575 | iowrite32(intmask, | 683 | iowrite32(intmask, |
576 | chip->vendor.iobase + | 684 | chip->vendor.iobase + |
@@ -609,7 +717,6 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, | |||
609 | list_add(&chip->vendor.list, &tis_chips); | 717 | list_add(&chip->vendor.list, &tis_chips); |
610 | spin_unlock(&tis_lock); | 718 | spin_unlock(&tis_lock); |
611 | 719 | ||
612 | tpm_get_timeouts(chip); | ||
613 | tpm_continue_selftest(chip); | 720 | tpm_continue_selftest(chip); |
614 | 721 | ||
615 | return 0; | 722 | return 0; |
@@ -619,6 +726,29 @@ out_err: | |||
619 | tpm_remove_hardware(chip->dev); | 726 | tpm_remove_hardware(chip->dev); |
620 | return rc; | 727 | return rc; |
621 | } | 728 | } |
729 | |||
730 | static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) | ||
731 | { | ||
732 | u32 intmask; | ||
733 | |||
734 | /* reenable interrupts that device may have lost or | ||
735 | BIOS/firmware may have disabled */ | ||
736 | iowrite8(chip->vendor.irq, chip->vendor.iobase + | ||
737 | TPM_INT_VECTOR(chip->vendor.locality)); | ||
738 | |||
739 | intmask = | ||
740 | ioread32(chip->vendor.iobase + | ||
741 | TPM_INT_ENABLE(chip->vendor.locality)); | ||
742 | |||
743 | intmask |= TPM_INTF_CMD_READY_INT | ||
744 | | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT | ||
745 | | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE; | ||
746 | |||
747 | iowrite32(intmask, | ||
748 | chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); | ||
749 | } | ||
750 | |||
751 | |||
622 | #ifdef CONFIG_PNP | 752 | #ifdef CONFIG_PNP |
623 | static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, | 753 | static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, |
624 | const struct pnp_device_id *pnp_id) | 754 | const struct pnp_device_id *pnp_id) |
@@ -650,6 +780,9 @@ static int tpm_tis_pnp_resume(struct pnp_dev *dev) | |||
650 | struct tpm_chip *chip = pnp_get_drvdata(dev); | 780 | struct tpm_chip *chip = pnp_get_drvdata(dev); |
651 | int ret; | 781 | int ret; |
652 | 782 | ||
783 | if (chip->vendor.irq) | ||
784 | tpm_tis_reenable_interrupts(chip); | ||
785 | |||
653 | ret = tpm_pm_resume(&dev->dev); | 786 | ret = tpm_pm_resume(&dev->dev); |
654 | if (!ret) | 787 | if (!ret) |
655 | tpm_continue_selftest(chip); | 788 | tpm_continue_selftest(chip); |
@@ -702,6 +835,11 @@ static int tpm_tis_suspend(struct platform_device *dev, pm_message_t msg) | |||
702 | 835 | ||
703 | static int tpm_tis_resume(struct platform_device *dev) | 836 | static int tpm_tis_resume(struct platform_device *dev) |
704 | { | 837 | { |
838 | struct tpm_chip *chip = dev_get_drvdata(&dev->dev); | ||
839 | |||
840 | if (chip->vendor.irq) | ||
841 | tpm_tis_reenable_interrupts(chip); | ||
842 | |||
705 | return tpm_pm_resume(&dev->dev); | 843 | return tpm_pm_resume(&dev->dev); |
706 | } | 844 | } |
707 | static struct platform_driver tis_drv = { | 845 | static struct platform_driver tis_drv = { |