diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-11-25 10:29:42 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-11-25 10:29:42 -0800 |
commit | 54f0e54011c9e83277e84ec2f60696285066dfa9 (patch) | |
tree | 3d172cc0528ed364ab37c89416da8cf62f9a52cd /drivers | |
parent | 0be0ee71816b2b6725e2b4f32ad6726c9d729777 (diff) | |
parent | 0b40dbcbba923b5379bd1d601edd6d51e23fe72c (diff) |
Merge tag 'tpmdd-next-20191112' of git://git.infradead.org/users/jjs/linux-tpmdd
Pull tpmd updates from Jarkko Sakkinen:
- support for Cr50 fTPM
- support for fTPM on AMD Zen+ CPUs
- TPM 2.0 trusted keys code relocated from drivers/char/tpm to
security/keys
* tag 'tpmdd-next-20191112' of git://git.infradead.org/users/jjs/linux-tpmdd:
KEYS: trusted: Remove set but not used variable 'keyhndl'
tpm: Switch to platform_get_irq_optional()
tpm_crb: fix fTPM on AMD Zen+ CPUs
KEYS: trusted: Move TPM2 trusted keys code
KEYS: trusted: Create trusted keys subsystem
KEYS: Use common tpm_buf for trusted and asymmetric keys
tpm: Move tpm_buf code to include/linux/
tpm: use GFP_KERNEL instead of GFP_HIGHMEM for tpm_buf
tpm: add check after commands attribs tab allocation
tpm: tpm_tis_spi: Drop THIS_MODULE usage from driver struct
tpm: tpm_tis_spi: Cleanup includes
tpm: tpm_tis_spi: Support cr50 devices
tpm: tpm_tis_spi: Introduce a flow control callback
tpm: Add a flag to indicate TPM power is managed by firmware
dt-bindings: tpm: document properties for cr50
tpm_tis: override durations for STM tpm with firmware 1.2.8.28
tpm: provide a way to override the chip returned durations
tpm: Remove duplicate code from caps_show() in tpm-sysfs.c
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/tpm/Kconfig | 7 | ||||
-rw-r--r-- | drivers/char/tpm/Makefile | 4 | ||||
-rw-r--r-- | drivers/char/tpm/tpm-interface.c | 64 | ||||
-rw-r--r-- | drivers/char/tpm/tpm-sysfs.c | 45 | ||||
-rw-r--r-- | drivers/char/tpm/tpm.h | 248 | ||||
-rw-r--r-- | drivers/char/tpm/tpm1-cmd.c | 15 | ||||
-rw-r--r-- | drivers/char/tpm/tpm2-cmd.c | 311 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_crb.c | 123 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis.c | 2 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis_core.c | 79 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis_spi.c | 143 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis_spi.h | 53 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis_spi_cr50.c | 322 |
13 files changed, 701 insertions, 715 deletions
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 9c37047f4b56..aacdeed93320 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -67,6 +67,13 @@ config TCG_TIS_SPI within Linux. To compile this driver as a module, choose M here; the module will be called tpm_tis_spi. +config TCG_TIS_SPI_CR50 + bool "Cr50 SPI Interface" + depends on TCG_TIS_SPI + help + If you have a H1 secure module running Cr50 firmware on SPI bus, + say Yes and it will be accessible from within Linux. + config TCG_TIS_I2C_ATMEL tristate "TPM Interface Specification 1.2 Interface (I2C - Atmel)" depends on I2C diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index c354cdff9c62..5a0d99d4fec0 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -21,7 +21,9 @@ tpm-$(CONFIG_EFI) += eventlog/efi.o tpm-$(CONFIG_OF) += eventlog/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 +obj-$(CONFIG_TCG_TIS_SPI) += tpm_tis_spi_mod.o +tpm_tis_spi_mod-y := tpm_tis_spi.o +tpm_tis_spi_mod-$(CONFIG_TCG_TIS_SPI_CR50) += tpm_tis_spi_cr50.o obj-$(CONFIG_TCG_TIS_I2C_ATMEL) += tpm_i2c_atmel.o obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index d7a3888ad80f..a438b1206fcb 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -23,6 +23,7 @@ #include <linux/slab.h> #include <linux/mutex.h> #include <linux/spinlock.h> +#include <linux/suspend.h> #include <linux/freezer.h> #include <linux/tpm_eventlog.h> @@ -394,7 +395,11 @@ int tpm_pm_suspend(struct device *dev) return -ENODEV; if (chip->flags & TPM_CHIP_FLAG_ALWAYS_POWERED) - return 0; + goto suspended; + + if ((chip->flags & TPM_CHIP_FLAG_FIRMWARE_POWER_MANAGED) && + !pm_suspend_via_firmware()) + goto suspended; if (!tpm_chip_start(chip)) { if (chip->flags & TPM_CHIP_FLAG_TPM2) @@ -405,6 +410,7 @@ int tpm_pm_suspend(struct device *dev) tpm_chip_stop(chip); } +suspended: return rc; } EXPORT_SYMBOL_GPL(tpm_pm_suspend); @@ -453,62 +459,6 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max) } EXPORT_SYMBOL_GPL(tpm_get_random); -/** - * tpm_seal_trusted() - seal a trusted key payload - * @chip: a &struct tpm_chip instance, %NULL for the default chip - * @options: authentication values and other options - * @payload: the key data in clear and encrypted form - * - * Note: only TPM 2.0 chip are supported. TPM 1.x implementation is located in - * the keyring subsystem. - * - * Return: same as with tpm_transmit_cmd() - */ -int tpm_seal_trusted(struct tpm_chip *chip, struct trusted_key_payload *payload, - struct trusted_key_options *options) -{ - int rc; - - chip = tpm_find_get_ops(chip); - if (!chip || !(chip->flags & TPM_CHIP_FLAG_TPM2)) - return -ENODEV; - - rc = tpm2_seal_trusted(chip, payload, options); - - tpm_put_ops(chip); - return rc; -} -EXPORT_SYMBOL_GPL(tpm_seal_trusted); - -/** - * tpm_unseal_trusted() - unseal a trusted key - * @chip: a &struct tpm_chip instance, %NULL for the default chip - * @options: authentication values and other options - * @payload: the key data in clear and encrypted form - * - * Note: only TPM 2.0 chip are supported. TPM 1.x implementation is located in - * the keyring subsystem. - * - * Return: same as with tpm_transmit_cmd() - */ -int tpm_unseal_trusted(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options) -{ - int rc; - - chip = tpm_find_get_ops(chip); - if (!chip || !(chip->flags & TPM_CHIP_FLAG_TPM2)) - return -ENODEV; - - rc = tpm2_unseal_trusted(chip, payload, options); - - tpm_put_ops(chip); - - return rc; -} -EXPORT_SYMBOL_GPL(tpm_unseal_trusted); - static int __init tpm_init(void) { int rc; diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index edfa89160010..3b53b3e5ec3e 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -217,6 +217,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, char *buf) { struct tpm_chip *chip = to_tpm_chip(dev); + struct tpm1_version *version; ssize_t rc = 0; char *str = buf; cap_t cap; @@ -232,31 +233,31 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, str += sprintf(str, "Manufacturer: 0x%x\n", be32_to_cpu(cap.manufacturer_id)); - /* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */ - rc = tpm1_getcap(chip, TPM_CAP_VERSION_1_2, &cap, + /* TPM 1.2 */ + if (!tpm1_getcap(chip, TPM_CAP_VERSION_1_2, &cap, "attempting to determine the 1.2 version", - sizeof(cap.tpm_version_1_2)); - if (!rc) { - str += sprintf(str, - "TCG version: %d.%d\nFirmware version: %d.%d\n", - cap.tpm_version_1_2.Major, - cap.tpm_version_1_2.Minor, - cap.tpm_version_1_2.revMajor, - cap.tpm_version_1_2.revMinor); - } else { - /* Otherwise just use TPM_STRUCT_VER */ - if (tpm1_getcap(chip, TPM_CAP_VERSION_1_1, &cap, - "attempting to determine the 1.1 version", - sizeof(cap.tpm_version))) - goto out_ops; - str += sprintf(str, - "TCG version: %d.%d\nFirmware version: %d.%d\n", - cap.tpm_version.Major, - cap.tpm_version.Minor, - cap.tpm_version.revMajor, - cap.tpm_version.revMinor); + sizeof(cap.version2))) { + version = &cap.version2.version; + goto out_print; } + + /* TPM 1.1 */ + if (tpm1_getcap(chip, TPM_CAP_VERSION_1_1, &cap, + "attempting to determine the 1.1 version", + sizeof(cap.version1))) { + goto out_ops; + } + + version = &cap.version1; + +out_print: + str += sprintf(str, + "TCG version: %d.%d\nFirmware version: %d.%d\n", + version->major, version->minor, + version->rev_major, version->rev_minor); + rc = str - buf; + out_ops: tpm_put_ops(chip); return rc; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index a7fea3e0ca86..b9e1547be6b5 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -25,7 +25,6 @@ #include <linux/platform_device.h> #include <linux/io.h> #include <linux/tpm.h> -#include <linux/highmem.h> #include <linux/tpm_eventlog.h> #ifdef CONFIG_X86 @@ -58,123 +57,6 @@ enum tpm_addr { #define TPM_ERR_DISABLED 0x7 #define TPM_ERR_INVALID_POSTINIT 38 -#define TPM_HEADER_SIZE 10 - -enum tpm2_const { - TPM2_PLATFORM_PCR = 24, - TPM2_PCR_SELECT_MIN = ((TPM2_PLATFORM_PCR + 7) / 8), -}; - -enum tpm2_timeouts { - TPM2_TIMEOUT_A = 750, - TPM2_TIMEOUT_B = 2000, - TPM2_TIMEOUT_C = 200, - TPM2_TIMEOUT_D = 30, - TPM2_DURATION_SHORT = 20, - TPM2_DURATION_MEDIUM = 750, - TPM2_DURATION_LONG = 2000, - TPM2_DURATION_LONG_LONG = 300000, - TPM2_DURATION_DEFAULT = 120000, -}; - -enum tpm2_structures { - TPM2_ST_NO_SESSIONS = 0x8001, - TPM2_ST_SESSIONS = 0x8002, -}; - -/* Indicates from what layer of the software stack the error comes from */ -#define TSS2_RC_LAYER_SHIFT 16 -#define TSS2_RESMGR_TPM_RC_LAYER (11 << TSS2_RC_LAYER_SHIFT) - -enum tpm2_return_codes { - TPM2_RC_SUCCESS = 0x0000, - TPM2_RC_HASH = 0x0083, /* RC_FMT1 */ - TPM2_RC_HANDLE = 0x008B, - TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */ - TPM2_RC_FAILURE = 0x0101, - TPM2_RC_DISABLED = 0x0120, - TPM2_RC_COMMAND_CODE = 0x0143, - TPM2_RC_TESTING = 0x090A, /* RC_WARN */ - TPM2_RC_REFERENCE_H0 = 0x0910, - TPM2_RC_RETRY = 0x0922, -}; - -enum tpm2_command_codes { - TPM2_CC_FIRST = 0x011F, - TPM2_CC_HIERARCHY_CONTROL = 0x0121, - TPM2_CC_HIERARCHY_CHANGE_AUTH = 0x0129, - TPM2_CC_CREATE_PRIMARY = 0x0131, - TPM2_CC_SEQUENCE_COMPLETE = 0x013E, - TPM2_CC_SELF_TEST = 0x0143, - TPM2_CC_STARTUP = 0x0144, - TPM2_CC_SHUTDOWN = 0x0145, - TPM2_CC_NV_READ = 0x014E, - TPM2_CC_CREATE = 0x0153, - TPM2_CC_LOAD = 0x0157, - TPM2_CC_SEQUENCE_UPDATE = 0x015C, - TPM2_CC_UNSEAL = 0x015E, - TPM2_CC_CONTEXT_LOAD = 0x0161, - TPM2_CC_CONTEXT_SAVE = 0x0162, - TPM2_CC_FLUSH_CONTEXT = 0x0165, - TPM2_CC_VERIFY_SIGNATURE = 0x0177, - TPM2_CC_GET_CAPABILITY = 0x017A, - TPM2_CC_GET_RANDOM = 0x017B, - TPM2_CC_PCR_READ = 0x017E, - TPM2_CC_PCR_EXTEND = 0x0182, - TPM2_CC_EVENT_SEQUENCE_COMPLETE = 0x0185, - TPM2_CC_HASH_SEQUENCE_START = 0x0186, - TPM2_CC_CREATE_LOADED = 0x0191, - TPM2_CC_LAST = 0x0193, /* Spec 1.36 */ -}; - -enum tpm2_permanent_handles { - TPM2_RS_PW = 0x40000009, -}; - -enum tpm2_capabilities { - TPM2_CAP_HANDLES = 1, - TPM2_CAP_COMMANDS = 2, - TPM2_CAP_PCRS = 5, - TPM2_CAP_TPM_PROPERTIES = 6, -}; - -enum tpm2_properties { - TPM_PT_TOTAL_COMMANDS = 0x0129, -}; - -enum tpm2_startup_types { - TPM2_SU_CLEAR = 0x0000, - TPM2_SU_STATE = 0x0001, -}; - -enum tpm2_cc_attrs { - TPM2_CC_ATTR_CHANDLES = 25, - TPM2_CC_ATTR_RHANDLE = 28, -}; - -#define TPM_VID_INTEL 0x8086 -#define TPM_VID_WINBOND 0x1050 -#define TPM_VID_STM 0x104A - -enum tpm_chip_flags { - 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), - TPM_CHIP_FLAG_ALWAYS_POWERED = BIT(5), -}; - -#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) - -struct tpm_header { - __be16 tag; - __be32 length; - union { - __be32 ordinal; - __be32 return_code; - }; -} __packed; - #define TPM_TAG_RQU_COMMAND 193 struct stclear_flags_t { @@ -186,19 +68,16 @@ struct stclear_flags_t { u8 bGlobalLock; } __packed; -struct tpm_version_t { - u8 Major; - u8 Minor; - u8 revMajor; - u8 revMinor; +struct tpm1_version { + u8 major; + u8 minor; + u8 rev_major; + u8 rev_minor; } __packed; -struct tpm_version_1_2_t { - __be16 tag; - u8 Major; - u8 Minor; - u8 revMajor; - u8 revMinor; +struct tpm1_version2 { + __be16 tag; + struct tpm1_version version; } __packed; struct timeout_t { @@ -243,8 +122,8 @@ typedef union { struct stclear_flags_t stclear_flags; __u8 owned; __be32 num_pcrs; - struct tpm_version_t tpm_version; - struct tpm_version_1_2_t tpm_version_1_2; + struct tpm1_version version1; + struct tpm1_version2 version2; __be32 manufacturer_id; struct timeout_t timeout; struct duration_t duration; @@ -274,102 +153,6 @@ enum tpm_sub_capabilities { * compiler warnings about stack frame size. */ #define TPM_MAX_RNG_DATA 128 -/* A string buffer type for constructing TPM commands. This is based on the - * ideas of string buffer code in security/keys/trusted.h but is heap based - * in order to keep the stack usage minimal. - */ - -enum tpm_buf_flags { - TPM_BUF_OVERFLOW = BIT(0), -}; - -struct tpm_buf { - struct page *data_page; - unsigned int flags; - u8 *data; -}; - -static inline void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal) -{ - struct tpm_header *head = (struct tpm_header *)buf->data; - - head->tag = cpu_to_be16(tag); - head->length = cpu_to_be32(sizeof(*head)); - head->ordinal = cpu_to_be32(ordinal); -} - -static inline int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal) -{ - buf->data_page = alloc_page(GFP_HIGHUSER); - if (!buf->data_page) - return -ENOMEM; - - buf->flags = 0; - buf->data = kmap(buf->data_page); - tpm_buf_reset(buf, tag, ordinal); - return 0; -} - -static inline void tpm_buf_destroy(struct tpm_buf *buf) -{ - kunmap(buf->data_page); - __free_page(buf->data_page); -} - -static inline u32 tpm_buf_length(struct tpm_buf *buf) -{ - struct tpm_header *head = (struct tpm_header *)buf->data; - - return be32_to_cpu(head->length); -} - -static inline u16 tpm_buf_tag(struct tpm_buf *buf) -{ - struct tpm_header *head = (struct tpm_header *)buf->data; - - return be16_to_cpu(head->tag); -} - -static inline void tpm_buf_append(struct tpm_buf *buf, - const unsigned char *new_data, - unsigned int new_len) -{ - struct tpm_header *head = (struct tpm_header *)buf->data; - u32 len = tpm_buf_length(buf); - - /* Return silently if overflow has already happened. */ - if (buf->flags & TPM_BUF_OVERFLOW) - return; - - if ((len + new_len) > PAGE_SIZE) { - WARN(1, "tpm_buf: overflow\n"); - buf->flags |= TPM_BUF_OVERFLOW; - return; - } - - memcpy(&buf->data[len], new_data, new_len); - head->length = cpu_to_be32(len + new_len); -} - -static inline void tpm_buf_append_u8(struct tpm_buf *buf, const u8 value) -{ - tpm_buf_append(buf, &value, 1); -} - -static inline void tpm_buf_append_u16(struct tpm_buf *buf, const u16 value) -{ - __be16 value2 = cpu_to_be16(value); - - tpm_buf_append(buf, (u8 *) &value2, 2); -} - -static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value) -{ - __be32 value2 = cpu_to_be32(value); - - tpm_buf_append(buf, (u8 *) &value2, 4); -} - extern struct class *tpm_class; extern struct class *tpmrm_class; extern dev_t tpm_devt; @@ -429,11 +212,6 @@ static inline void tpm_add_ppi(struct tpm_chip *chip) } #endif -static inline u32 tpm2_rc_value(u32 rc) -{ - return (rc & BIT(7)) ? rc & 0xff : rc; -} - int tpm2_get_timeouts(struct tpm_chip *chip); int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digest, u16 *digest_size_ptr); @@ -441,12 +219,6 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digests); int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max); void tpm2_flush_context(struct tpm_chip *chip, u32 handle); -int tpm2_seal_trusted(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options); -int tpm2_unseal_trusted(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options); ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, const char *desc); diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c index 149e953ca369..ca7158fa6e6c 100644 --- a/drivers/char/tpm/tpm1-cmd.c +++ b/drivers/char/tpm/tpm1-cmd.c @@ -343,6 +343,7 @@ int tpm1_get_timeouts(struct tpm_chip *chip) { cap_t cap; unsigned long timeout_old[4], timeout_chip[4], timeout_eff[4]; + unsigned long durations[3]; ssize_t rc; rc = tpm1_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, NULL, @@ -427,6 +428,20 @@ int tpm1_get_timeouts(struct tpm_chip *chip) usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long)); chip->duration[TPM_LONG_LONG] = 0; /* not used under 1.2 */ + /* + * Provide the ability for vendor overrides of duration values in case + * of misreporting. + */ + if (chip->ops->update_durations) + chip->ops->update_durations(chip, durations); + + if (chip->duration_adjusted) { + dev_info(&chip->dev, HW_ERR "Adjusting reported durations."); + chip->duration[TPM_SHORT] = durations[0]; + chip->duration[TPM_MEDIUM] = durations[1]; + chip->duration[TPM_LONG] = durations[2]; + } + /* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above * value wrong and apparently reports msecs rather than usecs. So we * fix up the resulting too-small TPM_SHORT value to make things work. diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index ba9acae83bff..fdb457704aa7 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -13,20 +13,6 @@ #include "tpm.h" #include <crypto/hash_info.h> -#include <keys/trusted-type.h> - -enum tpm2_object_attributes { - TPM2_OA_USER_WITH_AUTH = BIT(6), -}; - -enum tpm2_session_attributes { - TPM2_SA_CONTINUE_SESSION = BIT(0), -}; - -struct tpm2_hash { - unsigned int crypto_id; - unsigned int tpm_id; -}; static struct tpm2_hash tpm2_hash_map[] = { {HASH_ALGO_SHA1, TPM_ALG_SHA1}, @@ -377,299 +363,6 @@ void tpm2_flush_context(struct tpm_chip *chip, u32 handle) tpm_buf_destroy(&buf); } -/** - * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer. - * - * @buf: an allocated tpm_buf instance - * @session_handle: session handle - * @nonce: the session nonce, may be NULL if not used - * @nonce_len: the session nonce length, may be 0 if not used - * @attributes: the session attributes - * @hmac: the session HMAC or password, may be NULL if not used - * @hmac_len: the session HMAC or password length, maybe 0 if not used - */ -static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle, - const u8 *nonce, u16 nonce_len, - u8 attributes, - const u8 *hmac, u16 hmac_len) -{ - tpm_buf_append_u32(buf, 9 + nonce_len + hmac_len); - tpm_buf_append_u32(buf, session_handle); - tpm_buf_append_u16(buf, nonce_len); - - if (nonce && nonce_len) - tpm_buf_append(buf, nonce, nonce_len); - - tpm_buf_append_u8(buf, attributes); - tpm_buf_append_u16(buf, hmac_len); - - if (hmac && hmac_len) - tpm_buf_append(buf, hmac, hmac_len); -} - -/** - * tpm2_seal_trusted() - seal the payload of a trusted key - * - * @chip: TPM chip to use - * @payload: the key data in clear and encrypted form - * @options: authentication values and other options - * - * Return: < 0 on error and 0 on success. - */ -int tpm2_seal_trusted(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options) -{ - unsigned int blob_len; - struct tpm_buf buf; - u32 hash; - int i; - int rc; - - for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) { - if (options->hash == tpm2_hash_map[i].crypto_id) { - hash = tpm2_hash_map[i].tpm_id; - break; - } - } - - if (i == ARRAY_SIZE(tpm2_hash_map)) - return -EINVAL; - - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); - if (rc) - return rc; - - tpm_buf_append_u32(&buf, options->keyhandle); - tpm2_buf_append_auth(&buf, TPM2_RS_PW, - NULL /* nonce */, 0, - 0 /* session_attributes */, - options->keyauth /* hmac */, - TPM_DIGEST_SIZE); - - /* sensitive */ - tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len + 1); - - tpm_buf_append_u16(&buf, TPM_DIGEST_SIZE); - tpm_buf_append(&buf, options->blobauth, TPM_DIGEST_SIZE); - tpm_buf_append_u16(&buf, payload->key_len + 1); - tpm_buf_append(&buf, payload->key, payload->key_len); - tpm_buf_append_u8(&buf, payload->migratable); - - /* public */ - tpm_buf_append_u16(&buf, 14 + options->policydigest_len); - tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH); - tpm_buf_append_u16(&buf, hash); - - /* policy */ - if (options->policydigest_len) { - tpm_buf_append_u32(&buf, 0); - tpm_buf_append_u16(&buf, options->policydigest_len); - tpm_buf_append(&buf, options->policydigest, - options->policydigest_len); - } else { - tpm_buf_append_u32(&buf, TPM2_OA_USER_WITH_AUTH); - tpm_buf_append_u16(&buf, 0); - } - - /* public parameters */ - tpm_buf_append_u16(&buf, TPM_ALG_NULL); - tpm_buf_append_u16(&buf, 0); - - /* outside info */ - tpm_buf_append_u16(&buf, 0); - - /* creation PCR */ - tpm_buf_append_u32(&buf, 0); - - if (buf.flags & TPM_BUF_OVERFLOW) { - rc = -E2BIG; - goto out; - } - - rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data"); - if (rc) - goto out; - - blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]); - if (blob_len > MAX_BLOB_SIZE) { - rc = -E2BIG; - goto out; - } - if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) { - rc = -EFAULT; - goto out; - } - - memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len); - payload->blob_len = blob_len; - -out: - tpm_buf_destroy(&buf); - - if (rc > 0) { - if (tpm2_rc_value(rc) == TPM2_RC_HASH) - rc = -EINVAL; - else - rc = -EPERM; - } - - return rc; -} - -/** - * tpm2_load_cmd() - execute a TPM2_Load command - * - * @chip: TPM chip to use - * @payload: the key data in clear and encrypted form - * @options: authentication values and other options - * @blob_handle: returned blob handle - * - * Return: 0 on success. - * -E2BIG on wrong payload size. - * -EPERM on tpm error status. - * < 0 error from tpm_transmit_cmd. - */ -static int tpm2_load_cmd(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options, - u32 *blob_handle) -{ - struct tpm_buf buf; - unsigned int private_len; - unsigned int public_len; - unsigned int blob_len; - int rc; - - private_len = be16_to_cpup((__be16 *) &payload->blob[0]); - if (private_len > (payload->blob_len - 2)) - return -E2BIG; - - public_len = be16_to_cpup((__be16 *) &payload->blob[2 + private_len]); - blob_len = private_len + public_len + 4; - if (blob_len > payload->blob_len) - return -E2BIG; - - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); - if (rc) - return rc; - - tpm_buf_append_u32(&buf, options->keyhandle); - tpm2_buf_append_auth(&buf, TPM2_RS_PW, - NULL /* nonce */, 0, - 0 /* session_attributes */, - options->keyauth /* hmac */, - TPM_DIGEST_SIZE); - - tpm_buf_append(&buf, payload->blob, blob_len); - - if (buf.flags & TPM_BUF_OVERFLOW) { - rc = -E2BIG; - goto out; - } - - rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob"); - if (!rc) - *blob_handle = be32_to_cpup( - (__be32 *) &buf.data[TPM_HEADER_SIZE]); - -out: - tpm_buf_destroy(&buf); - - if (rc > 0) - rc = -EPERM; - - return rc; -} - -/** - * tpm2_unseal_cmd() - execute a TPM2_Unload command - * - * @chip: TPM chip to use - * @payload: the key data in clear and encrypted form - * @options: authentication values and other options - * @blob_handle: blob handle - * - * Return: 0 on success - * -EPERM on tpm error status - * < 0 error from tpm_transmit_cmd - */ -static int tpm2_unseal_cmd(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options, - u32 blob_handle) -{ - struct tpm_buf buf; - u16 data_len; - u8 *data; - int rc; - - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL); - if (rc) - return rc; - - tpm_buf_append_u32(&buf, blob_handle); - tpm2_buf_append_auth(&buf, - options->policyhandle ? - options->policyhandle : TPM2_RS_PW, - NULL /* nonce */, 0, - TPM2_SA_CONTINUE_SESSION, - options->blobauth /* hmac */, - TPM_DIGEST_SIZE); - - rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing"); - if (rc > 0) - rc = -EPERM; - - if (!rc) { - data_len = be16_to_cpup( - (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]); - if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE + 1) { - rc = -EFAULT; - goto out; - } - - if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 6 + data_len) { - rc = -EFAULT; - goto out; - } - data = &buf.data[TPM_HEADER_SIZE + 6]; - - memcpy(payload->key, data, data_len - 1); - payload->key_len = data_len - 1; - payload->migratable = data[data_len - 1]; - } - -out: - tpm_buf_destroy(&buf); - return rc; -} - -/** - * tpm2_unseal_trusted() - unseal the payload of a trusted key - * - * @chip: TPM chip to use - * @payload: the key data in clear and encrypted form - * @options: authentication values and other options - * - * Return: Same as with tpm_transmit_cmd. - */ -int tpm2_unseal_trusted(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options) -{ - u32 blob_handle; - int rc; - - rc = tpm2_load_cmd(chip, payload, options, &blob_handle); - if (rc) - return rc; - - rc = tpm2_unseal_cmd(chip, payload, options, blob_handle); - tpm2_flush_context(chip, blob_handle); - return rc; -} - struct tpm2_get_cap_out { u8 more_data; __be32 subcap_id; @@ -939,6 +632,10 @@ static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip) chip->cc_attrs_tbl = devm_kcalloc(&chip->dev, 4, nr_commands, GFP_KERNEL); + if (!chip->cc_attrs_tbl) { + rc = -ENOMEM; + goto out; + } rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY); if (rc) diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index e59f1f91d7f3..a9dcf31eadd2 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -22,6 +22,7 @@ #include "tpm.h" #define ACPI_SIG_TPM2 "TPM2" +#define TPM_CRB_MAX_RESOURCES 3 static const guid_t crb_acpi_start_guid = GUID_INIT(0x6BBF6CAB, 0x5463, 0x4714, @@ -91,7 +92,6 @@ enum crb_status { struct crb_priv { u32 sm; const char *hid; - void __iomem *iobase; struct crb_regs_head __iomem *regs_h; struct crb_regs_tail __iomem *regs_t; u8 __iomem *cmd; @@ -434,21 +434,27 @@ static const struct tpm_class_ops tpm_crb = { static int crb_check_resource(struct acpi_resource *ares, void *data) { - struct resource *io_res = data; + struct resource *iores_array = data; struct resource_win win; struct resource *res = &(win.res); + int i; if (acpi_dev_resource_memory(ares, res) || acpi_dev_resource_address_space(ares, &win)) { - *io_res = *res; - io_res->name = NULL; + for (i = 0; i < TPM_CRB_MAX_RESOURCES + 1; ++i) { + if (resource_type(iores_array + i) != IORESOURCE_MEM) { + iores_array[i] = *res; + iores_array[i].name = NULL; + break; + } + } } return 1; } -static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv, - struct resource *io_res, u64 start, u32 size) +static void __iomem *crb_map_res(struct device *dev, struct resource *iores, + void __iomem **iobase_ptr, u64 start, u32 size) { struct resource new_res = { .start = start, @@ -460,10 +466,16 @@ static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv, if (start != new_res.start) return (void __iomem *) ERR_PTR(-EINVAL); - if (!resource_contains(io_res, &new_res)) + if (!iores) return devm_ioremap_resource(dev, &new_res); - return priv->iobase + (new_res.start - io_res->start); + if (!*iobase_ptr) { + *iobase_ptr = devm_ioremap_resource(dev, iores); + if (IS_ERR(*iobase_ptr)) + return *iobase_ptr; + } + + return *iobase_ptr + (new_res.start - iores->start); } /* @@ -490,9 +502,13 @@ static u64 crb_fixup_cmd_size(struct device *dev, struct resource *io_res, static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, struct acpi_table_tpm2 *buf) { - struct list_head resources; - struct resource io_res; + struct list_head acpi_resource_list; + struct resource iores_array[TPM_CRB_MAX_RESOURCES + 1] = { {0} }; + void __iomem *iobase_array[TPM_CRB_MAX_RESOURCES] = {NULL}; struct device *dev = &device->dev; + struct resource *iores; + void __iomem **iobase_ptr; + int i; u32 pa_high, pa_low; u64 cmd_pa; u32 cmd_size; @@ -501,21 +517,41 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, u32 rsp_size; int ret; - INIT_LIST_HEAD(&resources); - ret = acpi_dev_get_resources(device, &resources, crb_check_resource, - &io_res); + INIT_LIST_HEAD(&acpi_resource_list); + ret = acpi_dev_get_resources(device, &acpi_resource_list, + crb_check_resource, iores_array); if (ret < 0) return ret; - acpi_dev_free_resource_list(&resources); + acpi_dev_free_resource_list(&acpi_resource_list); - if (resource_type(&io_res) != IORESOURCE_MEM) { + if (resource_type(iores_array) != IORESOURCE_MEM) { dev_err(dev, FW_BUG "TPM2 ACPI table does not define a memory resource\n"); return -EINVAL; + } else if (resource_type(iores_array + TPM_CRB_MAX_RESOURCES) == + IORESOURCE_MEM) { + dev_warn(dev, "TPM2 ACPI table defines too many memory resources\n"); + memset(iores_array + TPM_CRB_MAX_RESOURCES, + 0, sizeof(*iores_array)); + iores_array[TPM_CRB_MAX_RESOURCES].flags = 0; } - priv->iobase = devm_ioremap_resource(dev, &io_res); - if (IS_ERR(priv->iobase)) - return PTR_ERR(priv->iobase); + iores = NULL; + iobase_ptr = NULL; + for (i = 0; resource_type(iores_array + i) == IORESOURCE_MEM; ++i) { + if (buf->control_address >= iores_array[i].start && + buf->control_address + sizeof(struct crb_regs_tail) - 1 <= + iores_array[i].end) { + iores = iores_array + i; + iobase_ptr = iobase_array + i; + break; + } + } + + priv->regs_t = crb_map_res(dev, iores, iobase_ptr, buf->control_address, + sizeof(struct crb_regs_tail)); + + if (IS_ERR(priv->regs_t)) + return PTR_ERR(priv->regs_t); /* The ACPI IO region starts at the head area and continues to include * the control area, as one nice sane region except for some older @@ -523,9 +559,10 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, */ if ((priv->sm == ACPI_TPM2_COMMAND_BUFFER) || (priv->sm == ACPI_TPM2_MEMORY_MAPPED)) { - if (buf->control_address == io_res.start + + if (iores && + buf->control_address == io |