diff options
Diffstat (limited to 'drivers/char/tpm/tpm.c')
-rw-r--r-- | drivers/char/tpm/tpm.c | 235 |
1 files changed, 233 insertions, 2 deletions
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index e54bcb4e4e3e..24c4423d4851 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c | |||
@@ -430,17 +430,27 @@ out: | |||
430 | #define TPM_GET_CAP_RET_UINT32_2_IDX 18 | 430 | #define TPM_GET_CAP_RET_UINT32_2_IDX 18 |
431 | #define TPM_GET_CAP_RET_UINT32_3_IDX 22 | 431 | #define TPM_GET_CAP_RET_UINT32_3_IDX 22 |
432 | #define TPM_GET_CAP_RET_UINT32_4_IDX 26 | 432 | #define TPM_GET_CAP_RET_UINT32_4_IDX 26 |
433 | #define TPM_GET_CAP_PERM_DISABLE_IDX 16 | ||
434 | #define TPM_GET_CAP_PERM_INACTIVE_IDX 18 | ||
435 | #define TPM_GET_CAP_RET_BOOL_1_IDX 14 | ||
436 | #define TPM_GET_CAP_TEMP_INACTIVE_IDX 16 | ||
433 | 437 | ||
434 | #define TPM_CAP_IDX 13 | 438 | #define TPM_CAP_IDX 13 |
435 | #define TPM_CAP_SUBCAP_IDX 21 | 439 | #define TPM_CAP_SUBCAP_IDX 21 |
436 | 440 | ||
437 | enum tpm_capabilities { | 441 | enum tpm_capabilities { |
442 | TPM_CAP_FLAG = 4, | ||
438 | TPM_CAP_PROP = 5, | 443 | TPM_CAP_PROP = 5, |
439 | }; | 444 | }; |
440 | 445 | ||
441 | enum tpm_sub_capabilities { | 446 | enum tpm_sub_capabilities { |
442 | TPM_CAP_PROP_PCR = 0x1, | 447 | TPM_CAP_PROP_PCR = 0x1, |
443 | TPM_CAP_PROP_MANUFACTURER = 0x3, | 448 | TPM_CAP_PROP_MANUFACTURER = 0x3, |
449 | TPM_CAP_FLAG_PERM = 0x8, | ||
450 | TPM_CAP_FLAG_VOL = 0x9, | ||
451 | TPM_CAP_PROP_OWNER = 0x11, | ||
452 | TPM_CAP_PROP_TIS_TIMEOUT = 0x15, | ||
453 | TPM_CAP_PROP_TIS_DURATION = 0x20, | ||
444 | }; | 454 | }; |
445 | 455 | ||
446 | /* | 456 | /* |
@@ -474,6 +484,180 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, u8 *data, int len, | |||
474 | return 0; | 484 | return 0; |
475 | } | 485 | } |
476 | 486 | ||
487 | void tpm_gen_interrupt(struct tpm_chip *chip) | ||
488 | { | ||
489 | u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)]; | ||
490 | ssize_t rc; | ||
491 | |||
492 | memcpy(data, tpm_cap, sizeof(tpm_cap)); | ||
493 | data[TPM_CAP_IDX] = TPM_CAP_PROP; | ||
494 | data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT; | ||
495 | |||
496 | rc = transmit_cmd(chip, data, sizeof(data), | ||
497 | "attempting to determine the timeouts"); | ||
498 | } | ||
499 | EXPORT_SYMBOL_GPL(tpm_gen_interrupt); | ||
500 | |||
501 | void tpm_get_timeouts(struct tpm_chip *chip) | ||
502 | { | ||
503 | u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)]; | ||
504 | ssize_t rc; | ||
505 | u32 timeout; | ||
506 | |||
507 | memcpy(data, tpm_cap, sizeof(tpm_cap)); | ||
508 | data[TPM_CAP_IDX] = TPM_CAP_PROP; | ||
509 | data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT; | ||
510 | |||
511 | rc = transmit_cmd(chip, data, sizeof(data), | ||
512 | "attempting to determine the timeouts"); | ||
513 | if (rc) | ||
514 | goto duration; | ||
515 | |||
516 | if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX))) | ||
517 | != 4 * sizeof(u32)) | ||
518 | goto duration; | ||
519 | |||
520 | /* Don't overwrite default if value is 0 */ | ||
521 | timeout = | ||
522 | be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))); | ||
523 | if (timeout) | ||
524 | chip->vendor.timeout_a = timeout; | ||
525 | timeout = | ||
526 | be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX))); | ||
527 | if (timeout) | ||
528 | chip->vendor.timeout_b = timeout; | ||
529 | timeout = | ||
530 | be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX))); | ||
531 | if (timeout) | ||
532 | chip->vendor.timeout_c = timeout; | ||
533 | timeout = | ||
534 | be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_4_IDX))); | ||
535 | if (timeout) | ||
536 | chip->vendor.timeout_d = timeout; | ||
537 | |||
538 | duration: | ||
539 | memcpy(data, tpm_cap, sizeof(tpm_cap)); | ||
540 | data[TPM_CAP_IDX] = TPM_CAP_PROP; | ||
541 | data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_DURATION; | ||
542 | |||
543 | rc = transmit_cmd(chip, data, sizeof(data), | ||
544 | "attempting to determine the durations"); | ||
545 | if (rc) | ||
546 | return; | ||
547 | |||
548 | if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX))) | ||
549 | != 3 * sizeof(u32)) | ||
550 | return; | ||
551 | |||
552 | chip->vendor.duration[TPM_SHORT] = | ||
553 | be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))); | ||
554 | chip->vendor.duration[TPM_MEDIUM] = | ||
555 | be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX))); | ||
556 | chip->vendor.duration[TPM_LONG] = | ||
557 | be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX))); | ||
558 | } | ||
559 | EXPORT_SYMBOL_GPL(tpm_get_timeouts); | ||
560 | |||
561 | void tpm_continue_selftest(struct tpm_chip *chip) | ||
562 | { | ||
563 | u8 data[] = { | ||
564 | 0, 193, /* TPM_TAG_RQU_COMMAND */ | ||
565 | 0, 0, 0, 10, /* length */ | ||
566 | 0, 0, 0, 83, /* TPM_ORD_GetCapability */ | ||
567 | }; | ||
568 | |||
569 | tpm_transmit(chip, data, sizeof(data)); | ||
570 | } | ||
571 | EXPORT_SYMBOL_GPL(tpm_continue_selftest); | ||
572 | |||
573 | ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr, | ||
574 | char *buf) | ||
575 | { | ||
576 | u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 35)]; | ||
577 | ssize_t rc; | ||
578 | |||
579 | struct tpm_chip *chip = dev_get_drvdata(dev); | ||
580 | if (chip == NULL) | ||
581 | return -ENODEV; | ||
582 | |||
583 | memcpy(data, tpm_cap, sizeof(tpm_cap)); | ||
584 | data[TPM_CAP_IDX] = TPM_CAP_FLAG; | ||
585 | data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM; | ||
586 | |||
587 | rc = transmit_cmd(chip, data, sizeof(data), | ||
588 | "attemtping to determine the permanent state"); | ||
589 | if (rc) | ||
590 | return 0; | ||
591 | return sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_DISABLE_IDX]); | ||
592 | } | ||
593 | EXPORT_SYMBOL_GPL(tpm_show_enabled); | ||
594 | |||
595 | ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr, | ||
596 | char *buf) | ||
597 | { | ||
598 | u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 35)]; | ||
599 | ssize_t rc; | ||
600 | |||
601 | struct tpm_chip *chip = dev_get_drvdata(dev); | ||
602 | if (chip == NULL) | ||
603 | return -ENODEV; | ||
604 | |||
605 | memcpy(data, tpm_cap, sizeof(tpm_cap)); | ||
606 | data[TPM_CAP_IDX] = TPM_CAP_FLAG; | ||
607 | data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM; | ||
608 | |||
609 | rc = transmit_cmd(chip, data, sizeof(data), | ||
610 | "attemtping to determine the permanent state"); | ||
611 | if (rc) | ||
612 | return 0; | ||
613 | return sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_INACTIVE_IDX]); | ||
614 | } | ||
615 | EXPORT_SYMBOL_GPL(tpm_show_active); | ||
616 | |||
617 | ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr, | ||
618 | char *buf) | ||
619 | { | ||
620 | u8 data[sizeof(tpm_cap)]; | ||
621 | ssize_t rc; | ||
622 | |||
623 | struct tpm_chip *chip = dev_get_drvdata(dev); | ||
624 | if (chip == NULL) | ||
625 | return -ENODEV; | ||
626 | |||
627 | memcpy(data, tpm_cap, sizeof(tpm_cap)); | ||
628 | data[TPM_CAP_IDX] = TPM_CAP_PROP; | ||
629 | data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_OWNER; | ||
630 | |||
631 | rc = transmit_cmd(chip, data, sizeof(data), | ||
632 | "attempting to determine the owner state"); | ||
633 | if (rc) | ||
634 | return 0; | ||
635 | return sprintf(buf, "%d\n", data[TPM_GET_CAP_RET_BOOL_1_IDX]); | ||
636 | } | ||
637 | EXPORT_SYMBOL_GPL(tpm_show_owned); | ||
638 | |||
639 | ssize_t tpm_show_temp_deactivated(struct device * dev, | ||
640 | struct device_attribute * attr, char *buf) | ||
641 | { | ||
642 | u8 data[sizeof(tpm_cap)]; | ||
643 | ssize_t rc; | ||
644 | |||
645 | struct tpm_chip *chip = dev_get_drvdata(dev); | ||
646 | if (chip == NULL) | ||
647 | return -ENODEV; | ||
648 | |||
649 | memcpy(data, tpm_cap, sizeof(tpm_cap)); | ||
650 | data[TPM_CAP_IDX] = TPM_CAP_FLAG; | ||
651 | data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_VOL; | ||
652 | |||
653 | rc = transmit_cmd(chip, data, sizeof(data), | ||
654 | "attempting to determine the temporary state"); | ||
655 | if (rc) | ||
656 | return 0; | ||
657 | return sprintf(buf, "%d\n", data[TPM_GET_CAP_TEMP_INACTIVE_IDX]); | ||
658 | } | ||
659 | EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated); | ||
660 | |||
477 | static const u8 pcrread[] = { | 661 | static const u8 pcrread[] = { |
478 | 0, 193, /* TPM_TAG_RQU_COMMAND */ | 662 | 0, 193, /* TPM_TAG_RQU_COMMAND */ |
479 | 0, 0, 0, 14, /* length */ | 663 | 0, 0, 0, 14, /* length */ |
@@ -484,7 +668,7 @@ static const u8 pcrread[] = { | |||
484 | ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr, | 668 | ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr, |
485 | char *buf) | 669 | char *buf) |
486 | { | 670 | { |
487 | u8 data[30]; | 671 | u8 data[max_t(int, max(ARRAY_SIZE(tpm_cap), ARRAY_SIZE(pcrread)), 30)]; |
488 | ssize_t rc; | 672 | ssize_t rc; |
489 | int i, j, num_pcrs; | 673 | int i, j, num_pcrs; |
490 | __be32 index; | 674 | __be32 index; |
@@ -588,6 +772,7 @@ out: | |||
588 | EXPORT_SYMBOL_GPL(tpm_show_pubek); | 772 | EXPORT_SYMBOL_GPL(tpm_show_pubek); |
589 | 773 | ||
590 | #define CAP_VERSION_1_1 6 | 774 | #define CAP_VERSION_1_1 6 |
775 | #define CAP_VERSION_1_2 0x1A | ||
591 | #define CAP_VERSION_IDX 13 | 776 | #define CAP_VERSION_IDX 13 |
592 | static const u8 cap_version[] = { | 777 | static const u8 cap_version[] = { |
593 | 0, 193, /* TPM_TAG_RQU_COMMAND */ | 778 | 0, 193, /* TPM_TAG_RQU_COMMAND */ |
@@ -600,7 +785,7 @@ static const u8 cap_version[] = { | |||
600 | ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr, | 785 | ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr, |
601 | char *buf) | 786 | char *buf) |
602 | { | 787 | { |
603 | u8 data[30]; | 788 | u8 data[max_t(int, max(ARRAY_SIZE(tpm_cap), ARRAY_SIZE(cap_version)), 30)]; |
604 | ssize_t rc; | 789 | ssize_t rc; |
605 | char *str = buf; | 790 | char *str = buf; |
606 | 791 | ||
@@ -637,6 +822,52 @@ out: | |||
637 | } | 822 | } |
638 | EXPORT_SYMBOL_GPL(tpm_show_caps); | 823 | EXPORT_SYMBOL_GPL(tpm_show_caps); |
639 | 824 | ||
825 | ssize_t tpm_show_caps_1_2(struct device * dev, | ||
826 | struct device_attribute * attr, char *buf) | ||
827 | { | ||
828 | u8 data[max_t(int, max(ARRAY_SIZE(tpm_cap), ARRAY_SIZE(cap_version)), 30)]; | ||
829 | ssize_t len; | ||
830 | char *str = buf; | ||
831 | |||
832 | struct tpm_chip *chip = dev_get_drvdata(dev); | ||
833 | if (chip == NULL) | ||
834 | return -ENODEV; | ||
835 | |||
836 | memcpy(data, tpm_cap, sizeof(tpm_cap)); | ||
837 | data[TPM_CAP_IDX] = TPM_CAP_PROP; | ||
838 | data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER; | ||
839 | |||
840 | if ((len = tpm_transmit(chip, data, sizeof(data))) <= | ||
841 | TPM_ERROR_SIZE) { | ||
842 | dev_dbg(chip->dev, "A TPM error (%d) occurred " | ||
843 | "attempting to determine the manufacturer\n", | ||
844 | be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX)))); | ||
845 | return 0; | ||
846 | } | ||
847 | |||
848 | str += sprintf(str, "Manufacturer: 0x%x\n", | ||
849 | be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX)))); | ||
850 | |||
851 | memcpy(data, cap_version, sizeof(cap_version)); | ||
852 | data[CAP_VERSION_IDX] = CAP_VERSION_1_2; | ||
853 | |||
854 | if ((len = tpm_transmit(chip, data, sizeof(data))) <= | ||
855 | TPM_ERROR_SIZE) { | ||
856 | dev_err(chip->dev, "A TPM error (%d) occurred " | ||
857 | "attempting to determine the 1.2 version\n", | ||
858 | be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX)))); | ||
859 | goto out; | ||
860 | } | ||
861 | str += sprintf(str, | ||
862 | "TCG version: %d.%d\nFirmware version: %d.%d\n", | ||
863 | (int) data[16], (int) data[17], (int) data[18], | ||
864 | (int) data[19]); | ||
865 | |||
866 | out: | ||
867 | return str - buf; | ||
868 | } | ||
869 | EXPORT_SYMBOL_GPL(tpm_show_caps_1_2); | ||
870 | |||
640 | ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr, | 871 | ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr, |
641 | const char *buf, size_t count) | 872 | const char *buf, size_t count) |
642 | { | 873 | { |