diff options
Diffstat (limited to 'drivers/char/tpm/tpm.c')
-rw-r--r-- | drivers/char/tpm/tpm.c | 137 |
1 files changed, 117 insertions, 20 deletions
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 361a1dff8f77..6a8771f47a55 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
28 | #include <linux/mutex.h> | 28 | #include <linux/mutex.h> |
29 | #include <linux/spinlock.h> | 29 | #include <linux/spinlock.h> |
30 | #include <linux/freezer.h> | ||
30 | 31 | ||
31 | #include "tpm.h" | 32 | #include "tpm.h" |
32 | 33 | ||
@@ -440,7 +441,6 @@ out: | |||
440 | } | 441 | } |
441 | 442 | ||
442 | #define TPM_DIGEST_SIZE 20 | 443 | #define TPM_DIGEST_SIZE 20 |
443 | #define TPM_ERROR_SIZE 10 | ||
444 | #define TPM_RET_CODE_IDX 6 | 444 | #define TPM_RET_CODE_IDX 6 |
445 | 445 | ||
446 | enum tpm_capabilities { | 446 | enum tpm_capabilities { |
@@ -469,12 +469,14 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd, | |||
469 | len = tpm_transmit(chip,(u8 *) cmd, len); | 469 | len = tpm_transmit(chip,(u8 *) cmd, len); |
470 | if (len < 0) | 470 | if (len < 0) |
471 | return len; | 471 | return len; |
472 | if (len == TPM_ERROR_SIZE) { | 472 | else if (len < TPM_HEADER_SIZE) |
473 | err = be32_to_cpu(cmd->header.out.return_code); | 473 | return -EFAULT; |
474 | dev_dbg(chip->dev, "A TPM error (%d) occurred %s\n", err, desc); | 474 | |
475 | return err; | 475 | err = be32_to_cpu(cmd->header.out.return_code); |
476 | } | 476 | if (err != 0) |
477 | return 0; | 477 | dev_err(chip->dev, "A TPM error (%d) occurred %s\n", err, desc); |
478 | |||
479 | return err; | ||
478 | } | 480 | } |
479 | 481 | ||
480 | #define TPM_INTERNAL_RESULT_SIZE 200 | 482 | #define TPM_INTERNAL_RESULT_SIZE 200 |
@@ -530,7 +532,7 @@ void tpm_gen_interrupt(struct tpm_chip *chip) | |||
530 | } | 532 | } |
531 | EXPORT_SYMBOL_GPL(tpm_gen_interrupt); | 533 | EXPORT_SYMBOL_GPL(tpm_gen_interrupt); |
532 | 534 | ||
533 | void tpm_get_timeouts(struct tpm_chip *chip) | 535 | int tpm_get_timeouts(struct tpm_chip *chip) |
534 | { | 536 | { |
535 | struct tpm_cmd_t tpm_cmd; | 537 | struct tpm_cmd_t tpm_cmd; |
536 | struct timeout_t *timeout_cap; | 538 | struct timeout_t *timeout_cap; |
@@ -552,7 +554,7 @@ void tpm_get_timeouts(struct tpm_chip *chip) | |||
552 | if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 || | 554 | if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 || |
553 | be32_to_cpu(tpm_cmd.header.out.length) | 555 | be32_to_cpu(tpm_cmd.header.out.length) |
554 | != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32)) | 556 | != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32)) |
555 | return; | 557 | return -EINVAL; |
556 | 558 | ||
557 | timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout; | 559 | timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout; |
558 | /* Don't overwrite default if value is 0 */ | 560 | /* Don't overwrite default if value is 0 */ |
@@ -583,12 +585,12 @@ duration: | |||
583 | rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, | 585 | rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, |
584 | "attempting to determine the durations"); | 586 | "attempting to determine the durations"); |
585 | if (rc) | 587 | if (rc) |
586 | return; | 588 | return rc; |
587 | 589 | ||
588 | if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 || | 590 | if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 || |
589 | be32_to_cpu(tpm_cmd.header.out.length) | 591 | be32_to_cpu(tpm_cmd.header.out.length) |
590 | != sizeof(tpm_cmd.header.out) + sizeof(u32) + 3 * sizeof(u32)) | 592 | != sizeof(tpm_cmd.header.out) + sizeof(u32) + 3 * sizeof(u32)) |
591 | return; | 593 | return -EINVAL; |
592 | 594 | ||
593 | duration_cap = &tpm_cmd.params.getcap_out.cap.duration; | 595 | duration_cap = &tpm_cmd.params.getcap_out.cap.duration; |
594 | chip->vendor.duration[TPM_SHORT] = | 596 | chip->vendor.duration[TPM_SHORT] = |
@@ -610,20 +612,36 @@ duration: | |||
610 | chip->vendor.duration_adjusted = true; | 612 | chip->vendor.duration_adjusted = true; |
611 | dev_info(chip->dev, "Adjusting TPM timeout parameters."); | 613 | dev_info(chip->dev, "Adjusting TPM timeout parameters."); |
612 | } | 614 | } |
615 | return 0; | ||
613 | } | 616 | } |
614 | EXPORT_SYMBOL_GPL(tpm_get_timeouts); | 617 | EXPORT_SYMBOL_GPL(tpm_get_timeouts); |
615 | 618 | ||
616 | void tpm_continue_selftest(struct tpm_chip *chip) | 619 | #define TPM_ORD_CONTINUE_SELFTEST 83 |
620 | #define CONTINUE_SELFTEST_RESULT_SIZE 10 | ||
621 | |||
622 | static struct tpm_input_header continue_selftest_header = { | ||
623 | .tag = TPM_TAG_RQU_COMMAND, | ||
624 | .length = cpu_to_be32(10), | ||
625 | .ordinal = cpu_to_be32(TPM_ORD_CONTINUE_SELFTEST), | ||
626 | }; | ||
627 | |||
628 | /** | ||
629 | * tpm_continue_selftest -- run TPM's selftest | ||
630 | * @chip: TPM chip to use | ||
631 | * | ||
632 | * Returns 0 on success, < 0 in case of fatal error or a value > 0 representing | ||
633 | * a TPM error code. | ||
634 | */ | ||
635 | static int tpm_continue_selftest(struct tpm_chip *chip) | ||
617 | { | 636 | { |
618 | u8 data[] = { | 637 | int rc; |
619 | 0, 193, /* TPM_TAG_RQU_COMMAND */ | 638 | struct tpm_cmd_t cmd; |
620 | 0, 0, 0, 10, /* length */ | ||
621 | 0, 0, 0, 83, /* TPM_ORD_ContinueSelfTest */ | ||
622 | }; | ||
623 | 639 | ||
624 | tpm_transmit(chip, data, sizeof(data)); | 640 | cmd.header.in = continue_selftest_header; |
641 | rc = transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE, | ||
642 | "continue selftest"); | ||
643 | return rc; | ||
625 | } | 644 | } |
626 | EXPORT_SYMBOL_GPL(tpm_continue_selftest); | ||
627 | 645 | ||
628 | ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr, | 646 | ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr, |
629 | char *buf) | 647 | char *buf) |
@@ -718,7 +736,7 @@ static struct tpm_input_header pcrread_header = { | |||
718 | .ordinal = TPM_ORDINAL_PCRREAD | 736 | .ordinal = TPM_ORDINAL_PCRREAD |
719 | }; | 737 | }; |
720 | 738 | ||
721 | int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) | 739 | static int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) |
722 | { | 740 | { |
723 | int rc; | 741 | int rc; |
724 | struct tpm_cmd_t cmd; | 742 | struct tpm_cmd_t cmd; |
@@ -798,6 +816,45 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) | |||
798 | } | 816 | } |
799 | EXPORT_SYMBOL_GPL(tpm_pcr_extend); | 817 | EXPORT_SYMBOL_GPL(tpm_pcr_extend); |
800 | 818 | ||
819 | /** | ||
820 | * tpm_do_selftest - have the TPM continue its selftest and wait until it | ||
821 | * can receive further commands | ||
822 | * @chip: TPM chip to use | ||
823 | * | ||
824 | * Returns 0 on success, < 0 in case of fatal error or a value > 0 representing | ||
825 | * a TPM error code. | ||
826 | */ | ||
827 | int tpm_do_selftest(struct tpm_chip *chip) | ||
828 | { | ||
829 | int rc; | ||
830 | u8 digest[TPM_DIGEST_SIZE]; | ||
831 | unsigned int loops; | ||
832 | unsigned int delay_msec = 1000; | ||
833 | unsigned long duration; | ||
834 | |||
835 | duration = tpm_calc_ordinal_duration(chip, | ||
836 | TPM_ORD_CONTINUE_SELFTEST); | ||
837 | |||
838 | loops = jiffies_to_msecs(duration) / delay_msec; | ||
839 | |||
840 | rc = tpm_continue_selftest(chip); | ||
841 | /* This may fail if there was no TPM driver during a suspend/resume | ||
842 | * cycle; some may return 10 (BAD_ORDINAL), others 28 (FAILEDSELFTEST) | ||
843 | */ | ||
844 | if (rc) | ||
845 | return rc; | ||
846 | |||
847 | do { | ||
848 | rc = __tpm_pcr_read(chip, 0, digest); | ||
849 | if (rc != TPM_WARN_DOING_SELFTEST) | ||
850 | return rc; | ||
851 | msleep(delay_msec); | ||
852 | } while (--loops > 0); | ||
853 | |||
854 | return rc; | ||
855 | } | ||
856 | EXPORT_SYMBOL_GPL(tpm_do_selftest); | ||
857 | |||
801 | int tpm_send(u32 chip_num, void *cmd, size_t buflen) | 858 | int tpm_send(u32 chip_num, void *cmd, size_t buflen) |
802 | { | 859 | { |
803 | struct tpm_chip *chip; | 860 | struct tpm_chip *chip; |
@@ -1005,6 +1062,46 @@ ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr, | |||
1005 | } | 1062 | } |
1006 | EXPORT_SYMBOL_GPL(tpm_store_cancel); | 1063 | EXPORT_SYMBOL_GPL(tpm_store_cancel); |
1007 | 1064 | ||
1065 | int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, | ||
1066 | wait_queue_head_t *queue) | ||
1067 | { | ||
1068 | unsigned long stop; | ||
1069 | long rc; | ||
1070 | u8 status; | ||
1071 | |||
1072 | /* check current status */ | ||
1073 | status = chip->vendor.status(chip); | ||
1074 | if ((status & mask) == mask) | ||
1075 | return 0; | ||
1076 | |||
1077 | stop = jiffies + timeout; | ||
1078 | |||
1079 | if (chip->vendor.irq) { | ||
1080 | again: | ||
1081 | timeout = stop - jiffies; | ||
1082 | if ((long)timeout <= 0) | ||
1083 | return -ETIME; | ||
1084 | rc = wait_event_interruptible_timeout(*queue, | ||
1085 | ((chip->vendor.status(chip) | ||
1086 | & mask) == mask), | ||
1087 | timeout); | ||
1088 | if (rc > 0) | ||
1089 | return 0; | ||
1090 | if (rc == -ERESTARTSYS && freezing(current)) { | ||
1091 | clear_thread_flag(TIF_SIGPENDING); | ||
1092 | goto again; | ||
1093 | } | ||
1094 | } else { | ||
1095 | do { | ||
1096 | msleep(TPM_TIMEOUT); | ||
1097 | status = chip->vendor.status(chip); | ||
1098 | if ((status & mask) == mask) | ||
1099 | return 0; | ||
1100 | } while (time_before(jiffies, stop)); | ||
1101 | } | ||
1102 | return -ETIME; | ||
1103 | } | ||
1104 | EXPORT_SYMBOL_GPL(wait_for_tpm_stat); | ||
1008 | /* | 1105 | /* |
1009 | * Device file system interface to the TPM | 1106 | * Device file system interface to the TPM |
1010 | * | 1107 | * |