diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-12-14 13:57:44 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-12-14 13:57:44 -0800 |
commit | 683b96f4d1d132fcefa4a0bd11916649800d7361 (patch) | |
tree | 95ba7e1c1edc15639be080773b4c32d2be60b0a4 /drivers | |
parent | 0f1d6dfe03ca4e36132221b918499c6f0b0f048d (diff) | |
parent | 50523a29d900d5a403e0352d3d7aeda6a33df25c (diff) |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris:
"Generally pretty quiet for this release. Highlights:
Yama:
- allow ptrace access for original parent after re-parenting
TPM:
- add documentation
- many bugfixes & cleanups
- define a generic open() method for ascii & bios measurements
Integrity:
- Harden against malformed xattrs
SELinux:
- bugfixes & cleanups
Smack:
- Remove unnecessary smack_known_invalid label
- Do not apply star label in smack_setprocattr hook
- parse mnt opts after privileges check (fixes unpriv DoS vuln)"
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (56 commits)
Yama: allow access for the current ptrace parent
tpm: adjust return value of tpm_read_log
tpm: vtpm_proxy: conditionally call tpm_chip_unregister
tpm: Fix handling of missing event log
tpm: Check the bios_dir entry for NULL before accessing it
tpm: return -ENODEV if np is not set
tpm: cleanup of printk error messages
tpm: replace of_find_node_by_name() with dev of_node property
tpm: redefine read_log() to handle ACPI/OF at runtime
tpm: fix the missing .owner in tpm_bios_measurements_ops
tpm: have event log use the tpm_chip
tpm: drop tpm1_chip_register(/unregister)
tpm: replace dynamically allocated bios_dir with a static array
tpm: replace symbolic permission with octal for securityfs files
char: tpm: fix kerneldoc tpm2_unseal_trusted name typo
tpm_tis: Allow tpm_tis to be bound using DT
tpm, tpm_vtpm_proxy: add kdoc comments for VTPM_PROXY_IOC_NEW_DEV
tpm: Only call pm_runtime_get_sync if device has a parent
tpm: define a generic open() method for ascii & bios measurements
Documentation: tpm: add the Physical TPM device tree binding documentation
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/tpm/Kconfig | 2 | ||||
-rw-r--r-- | drivers/char/tpm/Makefile | 14 | ||||
-rw-r--r-- | drivers/char/tpm/tpm-chip.c | 38 | ||||
-rw-r--r-- | drivers/char/tpm/tpm-interface.c | 110 | ||||
-rw-r--r-- | drivers/char/tpm/tpm-sysfs.c | 7 | ||||
-rw-r--r-- | drivers/char/tpm/tpm.h | 41 | ||||
-rw-r--r-- | drivers/char/tpm/tpm2-cmd.c | 2 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_acpi.c | 46 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_crb.c | 173 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_eventlog.c | 230 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_eventlog.h | 22 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_of.c | 48 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis.c | 11 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis_core.c | 64 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_vtpm_proxy.c | 85 | ||||
-rw-r--r-- | drivers/char/tpm/xen-tpmfront.c | 1 |
16 files changed, 519 insertions, 375 deletions
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 9faa0b1e7766..277186d3b668 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -32,7 +32,7 @@ config TCG_TIS_CORE config TCG_TIS tristate "TPM Interface Specification 1.2 Interface / TPM 2.0 FIFO Interface" - depends on X86 + depends on X86 || OF select TCG_TIS_CORE ---help--- If you have a TPM security chip that is compliant with the diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index a385fb8c17de..a05b1ebd0b26 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -2,16 +2,10 @@ # Makefile for the kernel tpm device drivers. # obj-$(CONFIG_TCG_TPM) += tpm.o -tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o -tpm-$(CONFIG_ACPI) += tpm_ppi.o - -ifdef CONFIG_ACPI - tpm-y += tpm_eventlog.o tpm_acpi.o -else -ifdef CONFIG_TCG_IBMVTPM - tpm-y += tpm_eventlog.o tpm_of.o -endif -endif +tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o \ + tpm_eventlog.o +tpm-$(CONFIG_ACPI) += tpm_ppi.o tpm_acpi.o +tpm-$(CONFIG_OF) += tpm_of.o obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o obj-$(CONFIG_TCG_TIS) += tpm_tis.o obj-$(CONFIG_TCG_TIS_SPI) += tpm_tis_spi.o diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index e5950131bd90..7a4869151d3b 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -127,6 +127,7 @@ static void tpm_dev_release(struct device *dev) idr_remove(&dev_nums_idr, chip->dev_num); mutex_unlock(&idr_lock); + kfree(chip->log.bios_event_log); kfree(chip); } @@ -276,27 +277,6 @@ static void tpm_del_char_device(struct tpm_chip *chip) up_write(&chip->ops_sem); } -static int tpm1_chip_register(struct tpm_chip *chip) -{ - if (chip->flags & TPM_CHIP_FLAG_TPM2) - return 0; - - tpm_sysfs_add_device(chip); - - chip->bios_dir = tpm_bios_log_setup(dev_name(&chip->dev)); - - return 0; -} - -static void tpm1_chip_unregister(struct tpm_chip *chip) -{ - if (chip->flags & TPM_CHIP_FLAG_TPM2) - return; - - if (chip->bios_dir) - tpm_bios_log_teardown(chip->bios_dir); -} - static void tpm_del_legacy_sysfs(struct tpm_chip *chip) { struct attribute **i; @@ -363,20 +343,20 @@ int tpm_chip_register(struct tpm_chip *chip) return rc; } - rc = tpm1_chip_register(chip); - if (rc) + tpm_sysfs_add_device(chip); + + rc = tpm_bios_log_setup(chip); + if (rc != 0 && rc != -ENODEV) return rc; tpm_add_ppi(chip); rc = tpm_add_char_device(chip); if (rc) { - tpm1_chip_unregister(chip); + tpm_bios_log_teardown(chip); return rc; } - chip->flags |= TPM_CHIP_FLAG_REGISTERED; - rc = tpm_add_legacy_sysfs(chip); if (rc) { tpm_chip_unregister(chip); @@ -402,12 +382,8 @@ EXPORT_SYMBOL_GPL(tpm_chip_register); */ void tpm_chip_unregister(struct tpm_chip *chip) { - if (!(chip->flags & TPM_CHIP_FLAG_REGISTERED)) - return; - tpm_del_legacy_sysfs(chip); - - tpm1_chip_unregister(chip); + tpm_bios_log_teardown(chip); tpm_del_char_device(chip); } EXPORT_SYMBOL_GPL(tpm_chip_unregister); diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 3a9149cf0110..a2688ac2b48f 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -29,6 +29,7 @@ #include <linux/mutex.h> #include <linux/spinlock.h> #include <linux/freezer.h> +#include <linux/pm_runtime.h> #include "tpm.h" #include "tpm_eventlog.h" @@ -356,6 +357,9 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, if (!(flags & TPM_TRANSMIT_UNLOCKED)) mutex_lock(&chip->tpm_mutex); + if (chip->dev.parent) + pm_runtime_get_sync(chip->dev.parent); + rc = chip->ops->send(chip, (u8 *) buf, count); if (rc < 0) { dev_err(&chip->dev, @@ -397,6 +401,9 @@ out_recv: dev_err(&chip->dev, "tpm_transmit: tpm_recv: error %zd\n", rc); out: + if (chip->dev.parent) + pm_runtime_put_sync(chip->dev.parent); + if (!(flags & TPM_TRANSMIT_UNLOCKED)) mutex_unlock(&chip->tpm_mutex); return rc; @@ -437,26 +444,29 @@ static const struct tpm_input_header tpm_getcap_header = { .ordinal = TPM_ORD_GET_CAP }; -ssize_t tpm_getcap(struct tpm_chip *chip, __be32 subcap_id, cap_t *cap, +ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, const char *desc) { struct tpm_cmd_t tpm_cmd; int rc; tpm_cmd.header.in = tpm_getcap_header; - if (subcap_id == CAP_VERSION_1_1 || subcap_id == CAP_VERSION_1_2) { - tpm_cmd.params.getcap_in.cap = subcap_id; + if (subcap_id == TPM_CAP_VERSION_1_1 || + subcap_id == TPM_CAP_VERSION_1_2) { + tpm_cmd.params.getcap_in.cap = cpu_to_be32(subcap_id); /*subcap field not necessary */ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(0); tpm_cmd.header.in.length -= cpu_to_be32(sizeof(__be32)); } else { if (subcap_id == TPM_CAP_FLAG_PERM || subcap_id == TPM_CAP_FLAG_VOL) - tpm_cmd.params.getcap_in.cap = TPM_CAP_FLAG; + tpm_cmd.params.getcap_in.cap = + cpu_to_be32(TPM_CAP_FLAG); else - tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; + tpm_cmd.params.getcap_in.cap = + cpu_to_be32(TPM_CAP_PROP); tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); - tpm_cmd.params.getcap_in.subcap = subcap_id; + tpm_cmd.params.getcap_in.subcap = cpu_to_be32(subcap_id); } rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0, desc); @@ -488,12 +498,14 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type) int tpm_get_timeouts(struct tpm_chip *chip) { - struct tpm_cmd_t tpm_cmd; + cap_t cap; unsigned long new_timeout[4]; unsigned long old_timeout[4]; - struct duration_t *duration_cap; ssize_t rc; + if (chip->flags & TPM_CHIP_FLAG_HAVE_TIMEOUTS) + return 0; + if (chip->flags & TPM_CHIP_FLAG_TPM2) { /* Fixed timeouts for TPM2 */ chip->timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A); @@ -506,46 +518,30 @@ int tpm_get_timeouts(struct tpm_chip *chip) msecs_to_jiffies(TPM2_DURATION_MEDIUM); chip->duration[TPM_LONG] = msecs_to_jiffies(TPM2_DURATION_LONG); + + chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS; return 0; } - tpm_cmd.header.in = tpm_getcap_header; - tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; - tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); - tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT; - rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0, - NULL); - + rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, + "attempting to determine the timeouts"); if (rc == TPM_ERR_INVALID_POSTINIT) { /* The TPM is not started, we are the first to talk to it. Execute a startup command. */ - dev_info(&chip->dev, "Issuing TPM_STARTUP"); + dev_info(&chip->dev, "Issuing TPM_STARTUP\n"); if (tpm_startup(chip, TPM_ST_CLEAR)) return rc; - tpm_cmd.header.in = tpm_getcap_header; - tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; - tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); - tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT; - rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, - 0, NULL); + rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, + "attempting to determine the timeouts"); } - if (rc) { - dev_err(&chip->dev, - "A TPM error (%zd) occurred attempting to determine the timeouts\n", - rc); - goto duration; - } - - if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 || - be32_to_cpu(tpm_cmd.header.out.length) - != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32)) - return -EINVAL; + if (rc) + return rc; - old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a); - old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b); - old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c); - old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d); + old_timeout[0] = be32_to_cpu(cap.timeout.a); + old_timeout[1] = be32_to_cpu(cap.timeout.b); + old_timeout[2] = be32_to_cpu(cap.timeout.c); + old_timeout[3] = be32_to_cpu(cap.timeout.d); memcpy(new_timeout, old_timeout, sizeof(new_timeout)); /* @@ -583,29 +579,17 @@ int tpm_get_timeouts(struct tpm_chip *chip) chip->timeout_c = usecs_to_jiffies(new_timeout[2]); chip->timeout_d = usecs_to_jiffies(new_timeout[3]); -duration: - tpm_cmd.header.in = tpm_getcap_header; - tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; - tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); - tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION; - - rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0, - "attempting to determine the durations"); + rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_DURATION, &cap, + "attempting to determine the durations"); if (rc) return rc; - if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 || - be32_to_cpu(tpm_cmd.header.out.length) - != sizeof(tpm_cmd.header.out) + sizeof(u32) + 3 * sizeof(u32)) - return -EINVAL; - - duration_cap = &tpm_cmd.params.getcap_out.cap.duration; chip->duration[TPM_SHORT] = - usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short)); + usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_short)); chip->duration[TPM_MEDIUM] = - usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium)); + usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium)); chip->duration[TPM_LONG] = - usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long)); + usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long)); /* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above * value wrong and apparently reports msecs rather than usecs. So we @@ -619,6 +603,8 @@ duration: chip->duration_adjusted = true; dev_info(&chip->dev, "Adjusting TPM timeout parameters."); } + + chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS; return 0; } EXPORT_SYMBOL_GPL(tpm_get_timeouts); @@ -726,6 +712,14 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) } EXPORT_SYMBOL_GPL(tpm_pcr_read); +#define TPM_ORD_PCR_EXTEND cpu_to_be32(20) +#define EXTEND_PCR_RESULT_SIZE 34 +static const struct tpm_input_header pcrextend_header = { + .tag = TPM_TAG_RQU_COMMAND, + .length = cpu_to_be32(34), + .ordinal = TPM_ORD_PCR_EXTEND +}; + /** * tpm_pcr_extend - extend pcr value with hash * @chip_num: tpm idx # or AN& @@ -736,14 +730,6 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read); * isn't, protect against the chip disappearing, by incrementing * the module usage count. */ -#define TPM_ORD_PCR_EXTEND cpu_to_be32(20) -#define EXTEND_PCR_RESULT_SIZE 34 -static const struct tpm_input_header pcrextend_header = { - .tag = TPM_TAG_RQU_COMMAND, - .length = cpu_to_be32(34), - .ordinal = TPM_ORD_PCR_EXTEND -}; - int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) { struct tpm_cmd_t cmd; diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index a76ab4af9fb2..848ad6580b46 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -193,7 +193,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, be32_to_cpu(cap.manufacturer_id)); /* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */ - rc = tpm_getcap(chip, CAP_VERSION_1_2, &cap, + rc = tpm_getcap(chip, TPM_CAP_VERSION_1_2, &cap, "attempting to determine the 1.2 version"); if (!rc) { str += sprintf(str, @@ -204,7 +204,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, cap.tpm_version_1_2.revMinor); } else { /* Otherwise just use TPM_STRUCT_VER */ - rc = tpm_getcap(chip, CAP_VERSION_1_1, &cap, + rc = tpm_getcap(chip, TPM_CAP_VERSION_1_1, &cap, "attempting to determine the 1.1 version"); if (rc) return 0; @@ -284,6 +284,9 @@ static const struct attribute_group tpm_dev_group = { void tpm_sysfs_add_device(struct tpm_chip *chip) { + if (chip->flags & TPM_CHIP_FLAG_TPM2) + return; + /* The sysfs routines rely on an implicit tpm_try_get_ops, device_del * is called before ops is null'd and the sysfs core synchronizes this * removal so that no callbacks are running or can run again diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 4d183c97f6a6..1ae976894257 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -35,11 +35,14 @@ #include <linux/cdev.h> #include <linux/highmem.h> +#include "tpm_eventlog.h" + enum tpm_const { TPM_MINOR = 224, /* officially assigned */ TPM_BUFSIZE = 4096, TPM_NUM_DEVICES = 65536, TPM_RETRY = 50, /* 5 seconds */ + TPM_NUM_EVENT_LOG_FILES = 3, }; enum tpm_timeout { @@ -139,10 +142,15 @@ enum tpm2_startup_types { #define TPM_PPI_VERSION_LEN 3 enum tpm_chip_flags { - TPM_CHIP_FLAG_REGISTERED = BIT(0), TPM_CHIP_FLAG_TPM2 = BIT(1), TPM_CHIP_FLAG_IRQ = BIT(2), TPM_CHIP_FLAG_VIRTUAL = BIT(3), + TPM_CHIP_FLAG_HAVE_TIMEOUTS = BIT(4), +}; + +struct tpm_chip_seqops { + struct tpm_chip *chip; + const struct seq_operations *seqops; }; struct tpm_chip { @@ -156,6 +164,10 @@ struct tpm_chip { struct rw_semaphore ops_sem; const struct tpm_class_ops *ops; + struct tpm_bios_log log; + struct tpm_chip_seqops bin_log_seqops; + struct tpm_chip_seqops ascii_log_seqops; + unsigned int flags; int dev_num; /* /dev/tpm# */ @@ -171,7 +183,7 @@ struct tpm_chip { unsigned long duration[3]; /* jiffies */ bool duration_adjusted; - struct dentry **bios_dir; + struct dentry *bios_dir[TPM_NUM_EVENT_LOG_FILES]; const struct attribute_group *groups[3]; unsigned int groups_cnt; @@ -282,21 +294,20 @@ typedef union { } cap_t; enum tpm_capabilities { - TPM_CAP_FLAG = cpu_to_be32(4), - TPM_CAP_PROP = cpu_to_be32(5), - CAP_VERSION_1_1 = cpu_to_be32(0x06), - CAP_VERSION_1_2 = cpu_to_be32(0x1A) + TPM_CAP_FLAG = 4, + TPM_CAP_PROP = 5, + TPM_CAP_VERSION_1_1 = 0x06, + TPM_CAP_VERSION_1_2 = 0x1A, }; enum tpm_sub_capabilities { - TPM_CAP_PROP_PCR = cpu_to_be32(0x101), - TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103), - TPM_CAP_FLAG_PERM = cpu_to_be32(0x108), - TPM_CAP_FLAG_VOL = cpu_to_be32(0x109), - TPM_CAP_PROP_OWNER = cpu_to_be32(0x111), - TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115), - TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120), - + TPM_CAP_PROP_PCR = 0x101, + TPM_CAP_PROP_MANUFACTURER = 0x103, + TPM_CAP_FLAG_PERM = 0x108, + TPM_CAP_FLAG_VOL = 0x109, + TPM_CAP_PROP_OWNER = 0x111, + TPM_CAP_PROP_TIS_TIMEOUT = 0x115, + TPM_CAP_PROP_TIS_DURATION = 0x120, }; struct tpm_getcap_params_in { @@ -484,7 +495,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, unsigned int flags); ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *cmd, int len, unsigned int flags, const char *desc); -ssize_t tpm_getcap(struct tpm_chip *chip, __be32 subcap_id, cap_t *cap, +ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, const char *desc); int tpm_get_timeouts(struct tpm_chip *); int tpm1_auto_startup(struct tpm_chip *chip); diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 7df55d58c939..da5b782a9731 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -680,7 +680,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, } /** - * tpm_unseal_trusted() - unseal the payload of a trusted key + * tpm2_unseal_trusted() - unseal the payload of a trusted key * @chip_num: TPM chip to use * @payload: the key data in clear and encrypted form * @options: authentication values and other options diff --git a/drivers/char/tpm/tpm_acpi.c b/drivers/char/tpm/tpm_acpi.c index 565a9478cb94..b7718c95fd0b 100644 --- a/drivers/char/tpm/tpm_acpi.c +++ b/drivers/char/tpm/tpm_acpi.c @@ -6,10 +6,11 @@ * Stefan Berger <stefanb@us.ibm.com> * Reiner Sailer <sailer@watson.ibm.com> * Kylene Hall <kjhall@us.ibm.com> + * Nayna Jain <nayna@linux.vnet.ibm.com> * * Maintained by: <tpmdd-devel@lists.sourceforge.net> * - * Access to the eventlog extended by the TCG BIOS of PC platform + * Access to the event log extended by the TCG BIOS of PC platform * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -45,29 +46,28 @@ struct acpi_tcpa { }; /* read binary bios log */ -int read_log(struct tpm_bios_log *log) +int tpm_read_log_acpi(struct tpm_chip *chip) { struct acpi_tcpa *buff; acpi_status status; void __iomem *virt; u64 len, start; + struct tpm_bios_log *log; - if (log->bios_event_log != NULL) { - printk(KERN_ERR - "%s: ERROR - Eventlog already initialized\n", - __func__); - return -EFAULT; - } + log = &chip->log; + + /* Unfortuntely ACPI does not associate the event log with a specific + * TPM, like PPI. Thus all ACPI TPMs will read the same log. + */ + if (!chip->acpi_dev_handle) + return -ENODEV; /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */ status = acpi_get_table(ACPI_SIG_TCPA, 1, (struct acpi_table_header **)&buff); - if (ACPI_FAILURE(status)) { - printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n", - __func__); - return -EIO; - } + if (ACPI_FAILURE(status)) + return -ENODEV; switch(buff->platform_class) { case BIOS_SERVER: @@ -81,29 +81,29 @@ int read_log(struct tpm_bios_log *log) break; } if (!len) { - printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__); + dev_warn(&chip->dev, "%s: TCPA log area empty\n", __func__); return -EIO; } /* malloc EventLog space */ log->bios_event_log = kmalloc(len, GFP_KERNEL); - if (!log->bios_event_log) { - printk("%s: ERROR - Not enough Memory for BIOS measurements\n", - __func__); + if (!log->bios_event_log) return -ENOMEM; - } log->bios_event_log_end = log->bios_event_log + len; virt = acpi_os_map_iomem(start, len); - if (!virt) { - kfree(log->bios_event_log); - printk("%s: ERROR - Unable to map memory\n", __func__); - return -EIO; - } + if (!virt) + goto err; memcpy_fromio(log->bios_event_log, virt, len); acpi_os_unmap_iomem(virt, len); return 0; + +err: + kfree(log->bios_event_log); + log->bios_event_log = NULL; + return -EIO; + } diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index a7c870af916c..717b6b47c042 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -19,6 +19,7 @@ #include <linux/highmem.h> #include <linux/rculist.h> #include <linux/module.h> +#include <linux/pm_runtime.h> #include "tpm.h" #define ACPI_SIG_TPM2 "TPM2" @@ -83,7 +84,71 @@ struct crb_priv { u32 cmd_size; }; -static SIMPLE_DEV_PM_OPS(crb_pm, tpm_pm_suspend, tpm_pm_resume); +/** + * crb_go_idle - request tpm crb device to go the idle state + * + * @dev: crb device + * @priv: crb private data + * + * Write CRB_CTRL_REQ_GO_IDLE to TPM_CRB_CTRL_REQ + * The device should respond within TIMEOUT_C by clearing the bit. + * Anyhow, we do not wait here as a consequent CMD_READY request + * will be handled correctly even if idle was not completed. + * + * The function does nothing for devices with ACPI-start method. + * + * Return: 0 always + */ +static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv) +{ + if (priv->flags & CRB_FL_ACPI_START) + return 0; + + iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->cca->req); + /* we don't really care when this settles */ + + return 0; +} + +/** + * crb_cmd_ready - request tpm crb device to enter ready state + * + * @dev: crb device + * @priv: crb private data + * + * Write CRB_CTRL_REQ_CMD_READY to TPM_CRB_CTRL_REQ + * and poll till the device acknowledge it by clearing the bit. + * The device should respond within TIMEOUT_C. + * + * The function does nothing for devices with ACPI-start method + * + * Return: 0 on success -ETIME on timeout; + */ +static int __maybe_unused crb_cmd_ready(struct device *dev, + struct crb_priv *priv) +{ + ktime_t stop, start; + + if (priv->flags & CRB_FL_ACPI_START) + return 0; + + iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->cca->req); + + start = ktime_get(); + stop = ktime_add(start, ms_to_ktime(TPM2_TIMEOUT_C)); + do { + if (!(ioread32(&priv->cca->req) & CRB_CTRL_REQ_CMD_READY)) + return 0; + usleep_range(50, 100); + } while (ktime_before(ktime_get(), stop)); + + if (ioread32(&priv->cca->req) & CRB_CTRL_REQ_CMD_READY) { + dev_warn(dev, "cmdReady timed out\n"); + return -ETIME; + } + + return 0; +} static u8 crb_status(struct tpm_chip *chip) { @@ -196,21 +261,6 @@ static const struct tpm_class_ops tpm_crb = { .req_complete_val = CRB_DRV_STS_COMPLETE, }; -static int crb_init(struct acpi_device *device, struct crb_priv *priv) -{ - struct tpm_chip *chip; - - chip = tpmm_chip_alloc(&device->dev, &tpm_crb); - if (IS_ERR(chip)) - return PTR_ERR(chip); - - dev_set_drvdata(&chip->dev, priv); - chip->acpi_dev_handle = device->handle; - chip->flags = TPM_CHIP_FLAG_TPM2; - - return tpm_chip_register(chip); -} - static int crb_check_resource(struct acpi_resource *ares, void *data) { struct resource *io_res = data; @@ -249,6 +299,7 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, struct list_head resources; struct resource io_res; struct device *dev = &device->dev; + u32 pa_high, pa_low; u64 cmd_pa; u32 cmd_size; u64 rsp_pa; @@ -276,12 +327,27 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, if (IS_ERR(priv->cca)) return PTR_ERR(priv->cca); - cmd_pa = ((u64) ioread32(&priv->cca->cmd_pa_high) << 32) | - (u64) ioread32(&priv->cca->cmd_pa_low); + /* + * PTT HW bug w/a: wake up the device to access + * possibly not retained registers. + */ + ret = crb_cmd_ready(dev, priv); + if (ret) + return ret; + + pa_high = ioread32(&priv->cca->cmd_pa_high); + pa_low = ioread32(&priv->cca->cmd_pa_low); + cmd_pa = ((u64)pa_high << 32) | pa_low; cmd_size = ioread32(&priv->cca->cmd_size); + + dev_dbg(dev, "cmd_hi = %X cmd_low = %X cmd_size %X\n", + pa_high, pa_low, cmd_size); + priv->cmd = crb_map_res(dev, priv, &io_res, cmd_pa, cmd_size); - if (IS_ERR(priv->cmd)) - return PTR_ERR(priv->cmd); + if (IS_ERR(priv->cmd)) { + ret = PTR_ERR(priv->cmd); + goto out; + } memcpy_fromio(&rsp_pa, &priv->cca->rsp_pa, 8); rsp_pa = le64_to_cpu(rsp_pa); @@ -289,7 +355,8 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, if (cmd_pa != rsp_pa) { priv->rsp = crb_map_res(dev, priv, &io_res, rsp_pa, rsp_size); - return PTR_ERR_OR_ZERO(priv->rsp); + ret = PTR_ERR_OR_ZERO(priv->rsp); + goto out; } /* According to the PTP specification, overlapping command and response @@ -297,18 +364,25 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, */ if (cmd_size != rsp_size) { dev_err(dev, FW_BUG "overlapping command and response buffer sizes are not identical"); - return -EINVAL; + ret = -EINVAL; + goto out; } + priv->cmd_size = cmd_size; priv->rsp = priv->cmd; - return 0; + +out: + crb_go_idle(dev, priv); + + return ret; } static int crb_acpi_add(struct acpi_device *device) { struct acpi_table_tpm2 *buf; struct crb_priv *priv; + struct tpm_chip *chip; struct device *dev = &device->dev; acpi_status status; u32 sm; @@ -346,7 +420,33 @@ static int crb_acpi_add(struct acpi_device *device) if (rc) return rc; - return crb_init(device, priv); + chip = tpmm_chip_alloc(dev, &tpm_crb); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + dev_set_drvdata(&chip->dev, priv); + chip->acpi_dev_handle = device->handle; + chip->flags = TPM_CHIP_FLAG_TPM2; + + rc = crb_cmd_ready(dev, priv); + if (rc) + return rc; + + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + rc = tpm_chip_register(chip); + if (rc) { + crb_go_idle(dev, priv); + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + return rc; + } + + pm_runtime_put(dev); + + return 0; } static int crb_acpi_remove(struct acpi_device *device) @@ -356,9 +456,34 @@ static int crb_acpi_remove(struct acpi_device *device) tpm_chip_unregister(chip); + pm_runtime_disable(dev); + return 0; } +#ifdef CONFIG_PM +static int crb_pm_runtime_suspend(struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct crb_priv *priv = dev_get_drvdata(&chip->dev); + + return crb_go_idle(dev, priv); +} + +static int crb_pm_runtime_resume(struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct crb_priv *priv = dev_get_drvdata(&chip->dev); + + return crb_cmd_ready(dev, priv); +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops crb_pm = { + SET_SYSTEM_SLEEP_PM_OPS(tpm_pm_suspend, tpm_pm_resume) + SET_RUNTIME_PM_OPS(crb_pm_runtime_suspend, crb_pm_runtime_resume, NULL) +}; + static struct acpi_device_id crb_device_ids[] = { {"MSFT0101", 0}, {"", 0}, diff --git a/drivers/char/tpm/tpm_eventlog.c b/drivers/char/tpm/tpm_eventlog.c index e7228863290e..11bb1138a828 100644 --- a/drivers/char/tpm/tpm_eventlog.c +++ b/drivers/char/tpm/tpm_eventlog.c @@ -7,10 +7,11 @@ * Stefan Berger <stefanb@us.ibm.com> * Reiner Sailer <sailer@watson.ibm.com> * Kylene Hall <kjhall@us.ibm.com> + * Nayna Jain <nayna@linux.vnet.ibm.com> * * Maintained by: <tpmdd-devel@lists.sourceforge.net> * - * Access to the eventlog created by a system's firmware / BIOS + * Access to the event log created by a system's firmware / BIOS * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -72,7 +73,8 @@ static const char* tcpa_pc_event_id_strings[] = { static void *tpm_bios_measurements_start(struct seq_file *m, loff_t *pos) { loff_t i; - struct tpm_bios_log *log = m->private; + struct tpm_chip *chip = m->private; + struct tpm_bios_log *log = &chip->log; void *addr = log->bios_event_log; void *limit = log->bios_event_log_end; struct tcpa_event *event; @@ -119,7 +121,8 @@ static void *tpm_bios_measurements_next(struct seq_file *m, void *v, loff_t *pos) { struct tcpa_event *event = v; - struct tpm_bios_log *log = m->private; + struct tpm_chip *chip = m->private; + struct tpm_bios_log *log = &chip->log; void *limit = log->bios_event_log_end; u32 converted_event_size; u32 converted_event_type; @@ -260,13 +263,10 @@ static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v) static int tpm_bios_measurements_release(struct inode *inode, struct file *file) { - struct seq_file *seq = file->private_data; - struct tpm_bios_log *log = seq->private; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct tpm_chip *chip = (struct tpm_chip *)seq->private; - if (log) { - kfree(log->bios_event_log); - kfree(log); - } + put_device(&chip->dev); return seq_release(inode, file); } @@ -304,151 +304,159 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) return 0; } -static const struct seq_operations tpm_ascii_b_measurments_seqops = { +static const struct seq_operations tpm_ascii_b_measurements_seqops = { .start = tpm_bios_measurements_start, .next = tpm_bios_measurements_next, .stop = tpm_bios_measurements_stop, .show = tpm_ascii_bios_measurements_show, }; -static const struct seq_operations tpm_binary_b_measurments_seqops = { +static const struct seq_operations tpm_binary_b_measurements_seqops = { .start = tpm_bios_measurements_start, .next = tpm_bios_measurements_next, .stop = tpm_bios_measurements_stop, .show = tpm_binary_bios_measurements_show, }; -static int tpm_ascii_bios_measurements_open(struct inode *inode, +static int tpm_bios_measurements_open(struct inode *inode, struct file *file) { int err; - struct tpm_bios_log *log; struct seq_file *seq; - - log = kzalloc(sizeof(struct tpm_bios_log), GFP_KERNEL); - if (!log) - return -ENOMEM; - - if ((err = read_log(log))) - goto out_free; + struct tpm_chip_seqops *chip_seqops; + const struct seq_operations *seqops; + struct tpm_chip *chip; + + inode_lock(inode); + if (!inode->i_private) { + inode_unlock(inode); + return -ENODEV; + } + chip_seqops = (struct tpm_chip_seqops *)inode->i_private; + seqops = chip_seqops->seqops; + chip = chip_seqops->chip; + get_device(&chip->dev); + inod |