From 96785d635bc2b9e6f819cc606424d342a37872b4 Mon Sep 17 00:00:00 2001 From: Mark Hasemeyer Date: Tue, 14 Mar 2023 13:54:04 -0600 Subject: [PATCH 01/29] tpm: cr50: i2c: use jiffies to wait for tpm ready irq When waiting for a tpm ready completion, the cr50 i2c driver incorrectly assumes that the value of timeout_a is represented in milliseconds instead of jiffies. Remove the msecs_to_jiffies conversion. Signed-off-by: Mark Hasemeyer Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_i2c_cr50.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_i2c_cr50.c b/drivers/char/tpm/tpm_tis_i2c_cr50.c index 77cea5b31c6e..376ae18a04eb 100644 --- a/drivers/char/tpm/tpm_tis_i2c_cr50.c +++ b/drivers/char/tpm/tpm_tis_i2c_cr50.c @@ -100,8 +100,7 @@ static int tpm_cr50_i2c_wait_tpm_ready(struct tpm_chip *chip) } /* Wait for interrupt to indicate TPM is ready to respond */ - if (!wait_for_completion_timeout(&priv->tpm_ready, - msecs_to_jiffies(chip->timeout_a))) { + if (!wait_for_completion_timeout(&priv->tpm_ready, chip->timeout_a)) { dev_warn(&chip->dev, "Timeout waiting for TPM ready\n"); return -ETIMEDOUT; } From eff33245595d7ade7b821c9ed0f688272b16b07b Mon Sep 17 00:00:00 2001 From: Yu Zhe Date: Thu, 16 Mar 2023 16:50:37 +0800 Subject: [PATCH 02/29] tpm: remove unnecessary (void*) conversions Pointer variables of void * type do not require type cast. Signed-off-by: Yu Zhe Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/eventlog/common.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/char/tpm/eventlog/common.c b/drivers/char/tpm/eventlog/common.c index 8512ec76d526..639c3f395a5a 100644 --- a/drivers/char/tpm/eventlog/common.c +++ b/drivers/char/tpm/eventlog/common.c @@ -36,7 +36,7 @@ static int tpm_bios_measurements_open(struct inode *inode, inode_unlock(inode); return -ENODEV; } - chip_seqops = (struct tpm_chip_seqops *)inode->i_private; + chip_seqops = inode->i_private; seqops = chip_seqops->seqops; chip = chip_seqops->chip; get_device(&chip->dev); @@ -55,8 +55,8 @@ static int tpm_bios_measurements_open(struct inode *inode, static int tpm_bios_measurements_release(struct inode *inode, struct file *file) { - struct seq_file *seq = (struct seq_file *)file->private_data; - struct tpm_chip *chip = (struct tpm_chip *)seq->private; + struct seq_file *seq = file->private_data; + struct tpm_chip *chip = seq->private; put_device(&chip->dev); From 7f8da9915fcc6386edf86471bf31e162845930a4 Mon Sep 17 00:00:00 2001 From: Eric Snowberg Date: Thu, 2 Mar 2023 11:46:47 -0500 Subject: [PATCH 03/29] KEYS: Create static version of public_key_verify_signature The kernel test robot reports undefined reference to public_key_verify_signature when CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE is not defined. Create a static version in this case and return -EINVAL. Fixes: db6c43bd2132 ("crypto: KEYS: convert public key and digsig asym to the akcipher api") Reported-by: kernel test robot Signed-off-by: Eric Snowberg Reviewed-by: Mimi Zohar Reviewed-by: Petr Vorel Reviewed-by: Jarkko Sakkinen Tested-by: Mimi Zohar Signed-off-by: Jarkko Sakkinen --- include/crypto/public_key.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 68f7aa2a7e55..6d61695e1cde 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -80,7 +80,16 @@ extern int create_signature(struct kernel_pkey_params *, const void *, void *); extern int verify_signature(const struct key *, const struct public_key_signature *); +#if IS_REACHABLE(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) int public_key_verify_signature(const struct public_key *pkey, const struct public_key_signature *sig); +#else +static inline +int public_key_verify_signature(const struct public_key *pkey, + const struct public_key_signature *sig) +{ + return -EINVAL; +} +#endif #endif /* _LINUX_PUBLIC_KEY_H */ From ef97e774713fcd34c45f7a7426c7d8845394f7be Mon Sep 17 00:00:00 2001 From: Eric Snowberg Date: Thu, 2 Mar 2023 11:46:48 -0500 Subject: [PATCH 04/29] KEYS: Add missing function documentation Compiling with 'W=1' results in warnings that 'Function parameter or member not described' Add the missing parameters for restrict_link_by_builtin_and_secondary_trusted and restrict_link_to_builtin_trusted. Use /* instead of /** for get_builtin_and_secondary_restriction, since it is a static function. Fix wrong function name restrict_link_to_builtin_trusted. Fixes: d3bfe84129f6 ("certs: Add a secondary system keyring that can be added to dynamically") Signed-off-by: Eric Snowberg Reviewed-by: Petr Vorel Reviewed-by: Mimi Zohar Reviewed-by: Jarkko Sakkinen Tested-by: Mimi Zohar Signed-off-by: Jarkko Sakkinen --- certs/system_keyring.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 5042cc54fa5e..a7a49b17ceb1 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -33,7 +33,11 @@ extern __initconst const unsigned long system_certificate_list_size; extern __initconst const unsigned long module_cert_size; /** - * restrict_link_to_builtin_trusted - Restrict keyring addition by built in CA + * restrict_link_by_builtin_trusted - Restrict keyring addition by built-in CA + * @dest_keyring: Keyring being linked to. + * @type: The type of key being added. + * @payload: The payload of the new key. + * @restriction_key: A ring of keys that can be used to vouch for the new cert. * * Restrict the addition of keys into a keyring based on the key-to-be-added * being vouched for by a key in the built in system keyring. @@ -50,7 +54,11 @@ int restrict_link_by_builtin_trusted(struct key *dest_keyring, #ifdef CONFIG_SECONDARY_TRUSTED_KEYRING /** * restrict_link_by_builtin_and_secondary_trusted - Restrict keyring - * addition by both builtin and secondary keyrings + * addition by both built-in and secondary keyrings. + * @dest_keyring: Keyring being linked to. + * @type: The type of key being added. + * @payload: The payload of the new key. + * @restrict_key: A ring of keys that can be used to vouch for the new cert. * * Restrict the addition of keys into a keyring based on the key-to-be-added * being vouched for by a key in either the built-in or the secondary system @@ -75,7 +83,7 @@ int restrict_link_by_builtin_and_secondary_trusted( secondary_trusted_keys); } -/** +/* * Allocate a struct key_restriction for the "builtin and secondary trust" * keyring. Only for use in system_trusted_keyring_init(). */ From 30eae2b037af54b24109dcaea21db46f6285c69b Mon Sep 17 00:00:00 2001 From: Eric Snowberg Date: Thu, 2 Mar 2023 11:46:49 -0500 Subject: [PATCH 05/29] KEYS: X.509: Parse Basic Constraints for CA Parse the X.509 Basic Constraints. The basic constraints extension identifies whether the subject of the certificate is a CA. BasicConstraints ::= SEQUENCE { cA BOOLEAN DEFAULT FALSE, pathLenConstraint INTEGER (0..MAX) OPTIONAL } If the CA is true, store it in the public_key. This will be used in a follow on patch that requires knowing if the public key is a CA. Link: https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.9 Signed-off-by: Eric Snowberg Reviewed-by: Mimi Zohar Reviewed-by: Jarkko Sakkinen Tested-by: Mimi Zohar Signed-off-by: Jarkko Sakkinen --- crypto/asymmetric_keys/x509_cert_parser.c | 22 ++++++++++++++++++++++ include/crypto/public_key.h | 2 ++ 2 files changed, 24 insertions(+) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 7a9b084e2043..77547d4bd94d 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -586,6 +586,28 @@ int x509_process_extension(void *context, size_t hdrlen, return 0; } + if (ctx->last_oid == OID_basicConstraints) { + /* + * Get hold of the basicConstraints + * v[1] is the encoding size + * (Expect 0x2 or greater, making it 1 or more bytes) + * v[2] is the encoding type + * (Expect an ASN1_BOOL for the CA) + * v[3] is the contents of the ASN1_BOOL + * (Expect 1 if the CA is TRUE) + * vlen should match the entire extension size + */ + if (v[0] != (ASN1_CONS_BIT | ASN1_SEQ)) + return -EBADMSG; + if (vlen < 2) + return -EBADMSG; + if (v[1] != vlen - 2) + return -EBADMSG; + if (vlen >= 4 && v[1] != 0 && v[2] == ASN1_BOOL && v[3] == 1) + ctx->cert->pub->key_eflags |= 1 << KEY_EFLAG_CA; + return 0; + } + return 0; } diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 6d61695e1cde..c401762850f2 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -28,6 +28,8 @@ struct public_key { bool key_is_private; const char *id_type; const char *pkey_algo; + unsigned long key_eflags; /* key extension flags */ +#define KEY_EFLAG_CA 0 /* set if the CA basic constraints is set */ }; extern void public_key_free(struct public_key *key); From 567671281a751b80918a4531c4ba84b90a2a42c0 Mon Sep 17 00:00:00 2001 From: Eric Snowberg Date: Thu, 2 Mar 2023 11:46:50 -0500 Subject: [PATCH 06/29] KEYS: X.509: Parse Key Usage Parse the X.509 Key Usage. The key usage extension defines the purpose of the key contained in the certificate. id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } KeyUsage ::= BIT STRING { digitalSignature (0), contentCommitment (1), keyEncipherment (2), dataEncipherment (3), keyAgreement (4), keyCertSign (5), cRLSign (6), encipherOnly (7), decipherOnly (8) } If the keyCertSign or digitalSignature is set, store it in the public_key structure. Having the purpose of the key being stored during parsing, allows enforcement on the usage field in the future. This will be used in a follow on patch that requires knowing the certificate key usage type. Link: https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.3 Signed-off-by: Eric Snowberg Reviewed-by: Mimi Zohar Reviewed-by: Jarkko Sakkinen Tested-by: Mimi Zohar Signed-off-by: Jarkko Sakkinen --- crypto/asymmetric_keys/x509_cert_parser.c | 28 +++++++++++++++++++++++ include/crypto/public_key.h | 2 ++ 2 files changed, 30 insertions(+) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 77547d4bd94d..0a7049b470c1 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -579,6 +579,34 @@ int x509_process_extension(void *context, size_t hdrlen, return 0; } + if (ctx->last_oid == OID_keyUsage) { + /* + * Get hold of the keyUsage bit string + * v[1] is the encoding size + * (Expect either 0x02 or 0x03, making it 1 or 2 bytes) + * v[2] is the number of unused bits in the bit string + * (If >= 3 keyCertSign is missing when v[1] = 0x02) + * v[3] and possibly v[4] contain the bit string + * + * From RFC 5280 4.2.1.3: + * 0x04 is where keyCertSign lands in this bit string + * 0x80 is where digitalSignature lands in this bit string + */ + if (v[0] != ASN1_BTS) + return -EBADMSG; + if (vlen < 4) + return -EBADMSG; + if (v[2] >= 8) + return -EBADMSG; + if (v[3] & 0x80) + ctx->cert->pub->key_eflags |= 1 << KEY_EFLAG_DIGITALSIG; + if (v[1] == 0x02 && v[2] <= 2 && (v[3] & 0x04)) + ctx->cert->pub->key_eflags |= 1 << KEY_EFLAG_KEYCERTSIGN; + else if (vlen > 4 && v[1] == 0x03 && (v[3] & 0x04)) + ctx->cert->pub->key_eflags |= 1 << KEY_EFLAG_KEYCERTSIGN; + return 0; + } + if (ctx->last_oid == OID_authorityKeyIdentifier) { /* Get hold of the CA key fingerprint */ ctx->raw_akid = v; diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index c401762850f2..03c3fb990d59 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -30,6 +30,8 @@ struct public_key { const char *pkey_algo; unsigned long key_eflags; /* key extension flags */ #define KEY_EFLAG_CA 0 /* set if the CA basic constraints is set */ +#define KEY_EFLAG_DIGITALSIG 1 /* set if the digitalSignature usage is set */ +#define KEY_EFLAG_KEYCERTSIGN 2 /* set if the keyCertSign usage is set */ }; extern void public_key_free(struct public_key *key); From 76adb2fbc69a13c80b39042aab4d34e99309c8d4 Mon Sep 17 00:00:00 2001 From: Eric Snowberg Date: Thu, 2 Mar 2023 11:46:51 -0500 Subject: [PATCH 07/29] KEYS: CA link restriction Add a new link restriction. Restrict the addition of keys in a keyring based on the key to be added being a CA. Signed-off-by: Eric Snowberg Reviewed-by: Mimi Zohar Reviewed-by: Jarkko Sakkinen Tested-by: Mimi Zohar Signed-off-by: Jarkko Sakkinen --- crypto/asymmetric_keys/restrict.c | 38 +++++++++++++++++++++++++++++++ include/crypto/public_key.h | 15 ++++++++++++ 2 files changed, 53 insertions(+) diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c index 6b1ac5f5896a..48457c6f33f9 100644 --- a/crypto/asymmetric_keys/restrict.c +++ b/crypto/asymmetric_keys/restrict.c @@ -108,6 +108,44 @@ int restrict_link_by_signature(struct key *dest_keyring, return ret; } +/** + * restrict_link_by_ca - Restrict additions to a ring of CA keys + * @dest_keyring: Keyring being linked to. + * @type: The type of key being added. + * @payload: The payload of the new key. + * @trust_keyring: Unused. + * + * Check if the new certificate is a CA. If it is a CA, then mark the new + * certificate as being ok to link. + * + * Returns 0 if the new certificate was accepted, -ENOKEY if the + * certificate is not a CA. -ENOPKG if the signature uses unsupported + * crypto, or some other error if there is a matching certificate but + * the signature check cannot be performed. + */ +int restrict_link_by_ca(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trust_keyring) +{ + const struct public_key *pkey; + + if (type != &key_type_asymmetric) + return -EOPNOTSUPP; + + pkey = payload->data[asym_crypto]; + if (!pkey) + return -ENOPKG; + if (!test_bit(KEY_EFLAG_CA, &pkey->key_eflags)) + return -ENOKEY; + if (!test_bit(KEY_EFLAG_KEYCERTSIGN, &pkey->key_eflags)) + return -ENOKEY; + if (test_bit(KEY_EFLAG_DIGITALSIG, &pkey->key_eflags)) + return -ENOKEY; + + return 0; +} + static bool match_either_id(const struct asymmetric_key_id **pair, const struct asymmetric_key_id *single) { diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 03c3fb990d59..653992a6e941 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -75,6 +75,21 @@ extern int restrict_link_by_key_or_keyring_chain(struct key *trust_keyring, const union key_payload *payload, struct key *trusted); +#if IS_REACHABLE(CONFIG_ASYMMETRIC_KEY_TYPE) +extern int restrict_link_by_ca(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trust_keyring); +#else +static inline int restrict_link_by_ca(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trust_keyring) +{ + return 0; +} +#endif + extern int query_asymmetric_key(const struct kernel_pkey_params *, struct kernel_pkey_query *); From 099f26f22f5834ad744aee093ed4d11de13cac15 Mon Sep 17 00:00:00 2001 From: Eric Snowberg Date: Thu, 2 Mar 2023 11:46:52 -0500 Subject: [PATCH 08/29] integrity: machine keyring CA configuration Add machine keyring CA restriction options to control the type of keys that may be added to it. The motivation is separation of certificate signing from code signing keys. Subsquent work will limit certificates being loaded into the IMA keyring to code signing keys used for signature verification. When no restrictions are selected, all Machine Owner Keys (MOK) are added to the machine keyring. When CONFIG_INTEGRITY_CA_MACHINE_KEYRING is selected, the CA bit must be true. Also the key usage must contain keyCertSign, any other usage field may be set as well. When CONFIG_INTEGRITY_CA_MACHINE_KEYRING_MAX is selected, the CA bit must be true. Also the key usage must contain keyCertSign and the digitialSignature usage may not be set. Signed-off-by: Eric Snowberg Acked-by: Mimi Zohar Reviewed-by: Jarkko Sakkinen Tested-by: Mimi Zohar Signed-off-by: Jarkko Sakkinen --- crypto/asymmetric_keys/restrict.c | 2 ++ security/integrity/Kconfig | 23 ++++++++++++++++++++++- security/integrity/digsig.c | 8 ++++++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c index 48457c6f33f9..276bdb627498 100644 --- a/crypto/asymmetric_keys/restrict.c +++ b/crypto/asymmetric_keys/restrict.c @@ -140,6 +140,8 @@ int restrict_link_by_ca(struct key *dest_keyring, return -ENOKEY; if (!test_bit(KEY_EFLAG_KEYCERTSIGN, &pkey->key_eflags)) return -ENOKEY; + if (!IS_ENABLED(CONFIG_INTEGRITY_CA_MACHINE_KEYRING_MAX)) + return 0; if (test_bit(KEY_EFLAG_DIGITALSIG, &pkey->key_eflags)) return -ENOKEY; diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 599429f99f99..ec6e0d789da1 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -68,13 +68,34 @@ config INTEGRITY_MACHINE_KEYRING depends on INTEGRITY_ASYMMETRIC_KEYS depends on SYSTEM_BLACKLIST_KEYRING depends on LOAD_UEFI_KEYS - depends on !IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY help If set, provide a keyring to which Machine Owner Keys (MOK) may be added. This keyring shall contain just MOK keys. Unlike keys in the platform keyring, keys contained in the .machine keyring will be trusted within the kernel. +config INTEGRITY_CA_MACHINE_KEYRING + bool "Enforce Machine Keyring CA Restrictions" + depends on INTEGRITY_MACHINE_KEYRING + default n + help + The .machine keyring can be configured to enforce CA restriction + on any key added to it. By default no restrictions are in place + and all Machine Owner Keys (MOK) are added to the machine keyring. + If enabled only CA keys are added to the machine keyring, all + other MOK keys load into the platform keyring. + +config INTEGRITY_CA_MACHINE_KEYRING_MAX + bool "Only CA keys without DigitialSignature usage set" + depends on INTEGRITY_CA_MACHINE_KEYRING + default n + help + When selected, only load CA keys are loaded into the machine + keyring that contain the CA bit set along with the keyCertSign + Usage field. Keys containing the digitialSignature Usage field + will not be loaded. The remaining MOK keys are loaded into the + .platform keyring. + config LOAD_UEFI_KEYS depends on INTEGRITY_PLATFORM_KEYRING depends on EFI diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index f2193c531f4a..6f31ffe23c48 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -132,7 +132,8 @@ int __init integrity_init_keyring(const unsigned int id) | KEY_USR_READ | KEY_USR_SEARCH; if (id == INTEGRITY_KEYRING_PLATFORM || - id == INTEGRITY_KEYRING_MACHINE) { + (id == INTEGRITY_KEYRING_MACHINE && + !IS_ENABLED(CONFIG_INTEGRITY_CA_MACHINE_KEYRING))) { restriction = NULL; goto out; } @@ -144,7 +145,10 @@ int __init integrity_init_keyring(const unsigned int id) if (!restriction) return -ENOMEM; - restriction->check = restrict_link_to_ima; + if (id == INTEGRITY_KEYRING_MACHINE) + restriction->check = restrict_link_by_ca; + else + restriction->check = restrict_link_to_ima; /* * MOK keys can only be added through a read-only runtime services From 858e8b792d06f45c427897bd90205a1d90bf430f Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:25 +0100 Subject: [PATCH 09/29] tpm, tpm_tis: Avoid cache incoherency in test for interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The interrupt handler that sets the boolean variable irq_tested may run on another CPU as the thread that checks irq_tested as part of the irq test in tpm_tis_send(). Since nothing guarantees cache coherency between CPUs for unsynchronized accesses to boolean variables the testing thread might not perceive the value change done in the interrupt handler. Avoid this issue by setting the bit TPM_TIS_IRQ_TESTED in the flags field of the tpm_tis_data struct and by accessing this field with the bit manipulating functions that provide cache coherency. Also convert all other existing sites to use the proper macros when accessing this bitfield. Signed-off-by: Lino Sanfilippo Tested-by: Michael Niewöhner Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis.c | 2 +- drivers/char/tpm/tpm_tis_core.c | 21 +++++++++++---------- drivers/char/tpm/tpm_tis_core.h | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index ed5dabd3c72d..914abc58a328 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -227,7 +227,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info) irq = tpm_info->irq; if (itpm || is_itpm(ACPI_COMPANION(dev))) - phy->priv.flags |= TPM_TIS_ITPM_WORKAROUND; + set_bit(TPM_TIS_ITPM_WORKAROUND, &phy->priv.flags); return tpm_tis_core_init(dev, &phy->priv, irq, &tpm_tcg, ACPI_HANDLE(dev)); diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 3f98e587b3e8..57fd315a1d50 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -351,7 +351,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len) struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); int rc, status, burstcnt; size_t count = 0; - bool itpm = priv->flags & TPM_TIS_ITPM_WORKAROUND; + bool itpm = test_bit(TPM_TIS_ITPM_WORKAROUND, &priv->flags); status = tpm_tis_status(chip); if ((status & TPM_STS_COMMAND_READY) == 0) { @@ -484,7 +484,8 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) int rc, irq; struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - if (!(chip->flags & TPM_CHIP_FLAG_IRQ) || priv->irq_tested) + if (!(chip->flags & TPM_CHIP_FLAG_IRQ) || + test_bit(TPM_TIS_IRQ_TESTED, &priv->flags)) return tpm_tis_send_main(chip, buf, len); /* Verify receipt of the expected IRQ */ @@ -494,11 +495,11 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) rc = tpm_tis_send_main(chip, buf, len); priv->irq = irq; chip->flags |= TPM_CHIP_FLAG_IRQ; - if (!priv->irq_tested) + if (!test_bit(TPM_TIS_IRQ_TESTED, &priv->flags)) tpm_msleep(1); - if (!priv->irq_tested) + if (!test_bit(TPM_TIS_IRQ_TESTED, &priv->flags)) disable_interrupts(chip); - priv->irq_tested = true; + set_bit(TPM_TIS_IRQ_TESTED, &priv->flags); return rc; } @@ -641,7 +642,7 @@ static int probe_itpm(struct tpm_chip *chip) size_t len = sizeof(cmd_getticks); u16 vendor; - if (priv->flags & TPM_TIS_ITPM_WORKAROUND) + if (test_bit(TPM_TIS_ITPM_WORKAROUND, &priv->flags)) return 0; rc = tpm_tis_read16(priv, TPM_DID_VID(0), &vendor); @@ -661,13 +662,13 @@ static int probe_itpm(struct tpm_chip *chip) tpm_tis_ready(chip); - priv->flags |= TPM_TIS_ITPM_WORKAROUND; + set_bit(TPM_TIS_ITPM_WORKAROUND, &priv->flags); rc = tpm_tis_send_data(chip, cmd_getticks, len); if (rc == 0) dev_info(&chip->dev, "Detected an iTPM.\n"); else { - priv->flags &= ~TPM_TIS_ITPM_WORKAROUND; + clear_bit(TPM_TIS_ITPM_WORKAROUND, &priv->flags); rc = -EFAULT; } @@ -711,7 +712,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) if (interrupt == 0) return IRQ_NONE; - priv->irq_tested = true; + set_bit(TPM_TIS_IRQ_TESTED, &priv->flags); if (interrupt & TPM_INTF_DATA_AVAIL_INT) wake_up_interruptible(&priv->read_queue); if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT) @@ -797,7 +798,7 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, if (rc < 0) return rc; - priv->irq_tested = false; + clear_bit(TPM_TIS_IRQ_TESTED, &priv->flags); /* Generate an interrupt by having the core call through to * tpm_tis_send diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index b68479e0de10..57718f43cf64 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -87,13 +87,13 @@ enum tpm_tis_flags { TPM_TIS_ITPM_WORKAROUND = BIT(0), TPM_TIS_INVALID_STATUS = BIT(1), TPM_TIS_DEFAULT_CANCELLATION = BIT(2), + TPM_TIS_IRQ_TESTED = BIT(3), }; struct tpm_tis_data { u16 manufacturer_id; int locality; int irq; - bool irq_tested; unsigned long flags; void __iomem *ilb_base_addr; u16 clkrun_enabled; From 282657a8bd7fddcf511b834f43705001668b33a7 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:26 +0100 Subject: [PATCH 10/29] tpm, tpm_tis: Claim locality before writing TPM_INT_ENABLE register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In disable_interrupts() the TPM_GLOBAL_INT_ENABLE bit is unset in the TPM_INT_ENABLE register to shut the interrupts off. However modifying the register is only possible with a held locality. So claim the locality before disable_interrupts() is called. Signed-off-by: Lino Sanfilippo Tested-by: Michael Niewöhner Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 57fd315a1d50..3371304df334 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -1102,7 +1102,11 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, dev_err(&chip->dev, FW_BUG "TPM interrupt not working, polling instead\n"); + rc = request_locality(chip, 0); + if (rc < 0) + goto out_err; disable_interrupts(chip); + release_locality(chip, 0); } } else { tpm_tis_probe_irq(chip, intmask); From 6d789ad726950e612a7f31044260337237c5b490 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:27 +0100 Subject: [PATCH 11/29] tpm, tpm_tis: Disable interrupts if tpm_tis_probe_irq() failed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both functions tpm_tis_probe_irq_single() and tpm_tis_probe_irq() may setup the interrupts and then return with an error. This case is indicated by a missing TPM_CHIP_FLAG_IRQ flag in chip->flags. Currently the interrupt setup is only undone if tpm_tis_probe_irq_single() fails. Undo the setup also if tpm_tis_probe_irq() fails. Signed-off-by: Lino Sanfilippo Tested-by: Michael Niewöhner Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 3371304df334..347af7010396 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -1095,21 +1095,21 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, goto out_err; } - if (irq) { + if (irq) tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED, irq); - if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) { - dev_err(&chip->dev, FW_BUG + else + tpm_tis_probe_irq(chip, intmask); + + if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) { + dev_err(&chip->dev, FW_BUG "TPM interrupt not working, polling instead\n"); - rc = request_locality(chip, 0); - if (rc < 0) - goto out_err; - disable_interrupts(chip); - release_locality(chip, 0); - } - } else { - tpm_tis_probe_irq(chip, intmask); + rc = request_locality(chip, 0); + if (rc < 0) + goto out_err; + disable_interrupts(chip); + release_locality(chip, 0); } } From ed9be0e6c892a783800d77a41ca4c7255c6af8c5 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:28 +0100 Subject: [PATCH 12/29] tpm, tpm_tis: Do not skip reset of original interrupt vector If in tpm_tis_probe_irq_single() an error occurs after the original interrupt vector has been read, restore the interrupts before the error is returned. Since the caller does not check the error value, return -1 in any case that the TPM_CHIP_FLAG_IRQ flag is not set. Since the return value of function tpm_tis_gen_interrupt() is not longer used, make it a void function. Fixes: 1107d065fdf1 ("tpm_tis: Introduce intermediate layer for TPM access") Signed-off-by: Lino Sanfilippo Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 347af7010396..b5338e522a45 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -733,7 +733,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) return IRQ_HANDLED; } -static int tpm_tis_gen_interrupt(struct tpm_chip *chip) +static void tpm_tis_gen_interrupt(struct tpm_chip *chip) { const char *desc = "attempting to generate an interrupt"; u32 cap2; @@ -742,7 +742,7 @@ static int tpm_tis_gen_interrupt(struct tpm_chip *chip) ret = request_locality(chip, 0); if (ret < 0) - return ret; + return; if (chip->flags & TPM_CHIP_FLAG_TPM2) ret = tpm2_get_tpm_pt(chip, 0x100, &cap2, desc); @@ -750,8 +750,6 @@ static int tpm_tis_gen_interrupt(struct tpm_chip *chip) ret = tpm1_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, desc, 0); release_locality(chip, 0); - - return ret; } /* Register the IRQ and issue a command that will cause an interrupt. If an @@ -781,42 +779,37 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), irq); if (rc < 0) - return rc; + goto restore_irqs; rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &int_status); if (rc < 0) - return rc; + goto restore_irqs; /* Clear all existing */ rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), int_status); if (rc < 0) - return rc; - + goto restore_irqs; /* Turn on */ rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask | TPM_GLOBAL_INT_ENABLE); if (rc < 0) - return rc; + goto restore_irqs; clear_bit(TPM_TIS_IRQ_TESTED, &priv->flags); /* Generate an interrupt by having the core call through to * tpm_tis_send */ - rc = tpm_tis_gen_interrupt(chip); - if (rc < 0) - return rc; + tpm_tis_gen_interrupt(chip); +restore_irqs: /* tpm_tis_send will either confirm the interrupt is working or it * will call disable_irq which undoes all of the above. */ if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) { - rc = tpm_tis_write8(priv, original_int_vec, - TPM_INT_VECTOR(priv->locality)); - if (rc < 0) - return rc; - - return 1; + tpm_tis_write8(priv, original_int_vec, + TPM_INT_VECTOR(priv->locality)); + return -1; } return 0; From 15d7aa4e46eba87242a320f39773aa16faddadee Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:29 +0100 Subject: [PATCH 13/29] tpm, tpm_tis: Claim locality before writing interrupt registers In tpm_tis_probe_single_irq() interrupt registers TPM_INT_VECTOR, TPM_INT_STATUS and TPM_INT_ENABLE are modified to setup the interrupts. Currently these modifications are done without holding a locality thus they have no effect. Fix this by claiming the (default) locality before the registers are written. Since now tpm_tis_gen_interrupt() is called with the locality already claimed remove locality request and release from this function. Signed-off-by: Lino Sanfilippo Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index b5338e522a45..f70c1d95bc03 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -740,16 +740,10 @@ static void tpm_tis_gen_interrupt(struct tpm_chip *chip) cap_t cap; int ret; - ret = request_locality(chip, 0); - if (ret < 0) - return; - if (chip->flags & TPM_CHIP_FLAG_TPM2) ret = tpm2_get_tpm_pt(chip, 0x100, &cap2, desc); else ret = tpm1_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, desc, 0); - - release_locality(chip, 0); } /* Register the IRQ and issue a command that will cause an interrupt. If an @@ -772,11 +766,17 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, } priv->irq = irq; - rc = tpm_tis_read8(priv, TPM_INT_VECTOR(priv->locality), - &original_int_vec); + rc = request_locality(chip, 0); if (rc < 0) return rc; + rc = tpm_tis_read8(priv, TPM_INT_VECTOR(priv->locality), + &original_int_vec); + if (rc < 0) { + release_locality(chip, priv->locality); + return rc; + } + rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), irq); if (rc < 0) goto restore_irqs; @@ -809,10 +809,12 @@ restore_irqs: if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) { tpm_tis_write8(priv, original_int_vec, TPM_INT_VECTOR(priv->locality)); - return -1; + rc = -1; } - return 0; + release_locality(chip, priv->locality); + + return rc; } /* Try to find the IRQ the TPM is using. This is for legacy x86 systems that From e87fcf0dc2b47fac5b4824f00f74dfbcd4acd363 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:30 +0100 Subject: [PATCH 14/29] tpm, tpm_tis: Only handle supported interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the TPM Interface Specification (TIS) support for "stsValid" and "commandReady" interrupts is only optional. This has to be taken into account when handling the interrupts in functions like wait_for_tpm_stat(). To determine the supported interrupts use the capability query. Also adjust wait_for_tpm_stat() to only wait for interrupt reported status changes. After that process all the remaining status changes by polling the status register. Signed-off-by: Lino Sanfilippo Tested-by: Michael Niewöhner Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 120 +++++++++++++++++++------------- drivers/char/tpm/tpm_tis_core.h | 1 + 2 files changed, 73 insertions(+), 48 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index f70c1d95bc03..e4ba4d5090be 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -53,41 +53,63 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, long rc; u8 status; bool canceled = false; + u8 sts_mask = 0; + int ret = 0; /* check current status */ status = chip->ops->status(chip); if ((status & mask) == mask) return 0; - stop = jiffies + timeout; + /* check what status changes can be handled by irqs */ + if (priv->int_mask & TPM_INTF_STS_VALID_INT) + sts_mask |= TPM_STS_VALID; - if (chip->flags & TPM_CHIP_FLAG_IRQ) { + if (priv->int_mask & TPM_INTF_DATA_AVAIL_INT) + sts_mask |= TPM_STS_DATA_AVAIL; + + if (priv->int_mask & TPM_INTF_CMD_READY_INT) + sts_mask |= TPM_STS_COMMAND_READY; + + sts_mask &= mask; + + stop = jiffies + timeout; + /* process status changes with irq support */ + if (sts_mask) { + ret = -ETIME; again: timeout = stop - jiffies; if ((long)timeout <= 0) return -ETIME; rc = wait_event_interruptible_timeout(*queue, - wait_for_tpm_stat_cond(chip, mask, check_cancel, + wait_for_tpm_stat_cond(chip, sts_mask, check_cancel, &canceled), timeout); if (rc > 0) { if (canceled) return -ECANCELED; - return 0; + ret = 0; } if (rc == -ERESTARTSYS && freezing(current)) { clear_thread_flag(TIF_SIGPENDING); goto again; } - } else { - do { - usleep_range(priv->timeout_min, - priv->timeout_max); - status = chip->ops->status(chip); - if ((status & mask) == mask) - return 0; - } while (time_before(jiffies, stop)); } + + if (ret) + return ret; + + mask &= ~sts_mask; + if (!mask) /* all done */ + return 0; + /* process status changes without irq support */ + do { + status = chip->ops->status(chip); + if ((status & mask) == mask) + return 0; + usleep_range(priv->timeout_min, + priv->timeout_max); + } while (time_before(jiffies, stop)); return -ETIME; } @@ -1005,8 +1027,40 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, if (rc < 0) goto out_err; - intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | - TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT; + /* Figure out the capabilities */ + rc = tpm_tis_read32(priv, TPM_INTF_CAPS(priv->locality), &intfcaps); + if (rc < 0) + goto out_err; + + dev_dbg(dev, "TPM interface capabilities (0x%x):\n", + intfcaps); + if (intfcaps & TPM_INTF_BURST_COUNT_STATIC) + dev_dbg(dev, "\tBurst Count Static\n"); + if (intfcaps & TPM_INTF_CMD_READY_INT) { + intmask |= TPM_INTF_CMD_READY_INT; + dev_dbg(dev, "\tCommand Ready Int Support\n"); + } + if (intfcaps & TPM_INTF_INT_EDGE_FALLING) + dev_dbg(dev, "\tInterrupt Edge Falling\n"); + if (intfcaps & TPM_INTF_INT_EDGE_RISING) + dev_dbg(dev, "\tInterrupt Edge Rising\n"); + if (intfcaps & TPM_INTF_INT_LEVEL_LOW) + dev_dbg(dev, "\tInterrupt Level Low\n"); + if (intfcaps & TPM_INTF_INT_LEVEL_HIGH) + dev_dbg(dev, "\tInterrupt Level High\n"); + if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT) { + intmask |= TPM_INTF_LOCALITY_CHANGE_INT; + dev_dbg(dev, "\tLocality Change Int Support\n"); + } + if (intfcaps & TPM_INTF_STS_VALID_INT) { + intmask |= TPM_INTF_STS_VALID_INT; + dev_dbg(dev, "\tSts Valid Int Support\n"); + } + if (intfcaps & TPM_INTF_DATA_AVAIL_INT) { + intmask |= TPM_INTF_DATA_AVAIL_INT; + dev_dbg(dev, "\tData Avail Int Support\n"); + } + intmask &= ~TPM_GLOBAL_INT_ENABLE; rc = request_locality(chip, 0); @@ -1040,32 +1094,6 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, goto out_err; } - /* Figure out the capabilities */ - rc = tpm_tis_read32(priv, TPM_INTF_CAPS(priv->locality), &intfcaps); - if (rc < 0) - goto out_err; - - dev_dbg(dev, "TPM interface capabilities (0x%x):\n", - intfcaps); - if (intfcaps & TPM_INTF_BURST_COUNT_STATIC) - dev_dbg(dev, "\tBurst Count Static\n"); - if (intfcaps & TPM_INTF_CMD_READY_INT) - dev_dbg(dev, "\tCommand Ready Int Support\n"); - if (intfcaps & TPM_INTF_INT_EDGE_FALLING) - dev_dbg(dev, "\tInterrupt Edge Falling\n"); - if (intfcaps & TPM_INTF_INT_EDGE_RISING) - dev_dbg(dev, "\tInterrupt Edge Rising\n"); - if (intfcaps & TPM_INTF_INT_LEVEL_LOW) - dev_dbg(dev, "\tInterrupt Level Low\n"); - if (intfcaps & TPM_INTF_INT_LEVEL_HIGH) - dev_dbg(dev, "\tInterrupt Level High\n"); - if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT) - dev_dbg(dev, "\tLocality Change Int Support\n"); - if (intfcaps & TPM_INTF_STS_VALID_INT) - dev_dbg(dev, "\tSts Valid Int Support\n"); - if (intfcaps & TPM_INTF_DATA_AVAIL_INT) - dev_dbg(dev, "\tData Avail Int Support\n"); - /* INTERRUPT Setup */ init_waitqueue_head(&priv->read_queue); init_waitqueue_head(&priv->int_queue); @@ -1096,7 +1124,9 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, else tpm_tis_probe_irq(chip, intmask); - if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) { + if (chip->flags & TPM_CHIP_FLAG_IRQ) { + priv->int_mask = intmask; + } else { dev_err(&chip->dev, FW_BUG "TPM interrupt not working, polling instead\n"); @@ -1143,13 +1173,7 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) if (rc < 0) goto out; - rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask); - if (rc < 0) - goto out; - - intmask |= TPM_INTF_CMD_READY_INT - | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT - | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE; + intmask = priv->int_mask | TPM_GLOBAL_INT_ENABLE; tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index 57718f43cf64..19ef8c1a4142 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -94,6 +94,7 @@ struct tpm_tis_data { u16 manufacturer_id; int locality; int irq; + unsigned int int_mask; unsigned long flags; void __iomem *ilb_base_addr; u16 clkrun_enabled; From 4303553bced7a1d84583dada552393f5ebd31e54 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:31 +0100 Subject: [PATCH 15/29] tpm, tpm_tis: Move interrupt mask checks into own function Clean up wait_for_tpm_stat() by moving multiple similar interrupt mask checks into an own function. Signed-off-by: Lino Sanfilippo Suggested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index e4ba4d5090be..8981433abb0f 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -44,6 +44,20 @@ static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask, return false; } +static u8 tpm_tis_filter_sts_mask(u8 int_mask, u8 sts_mask) +{ + if (!(int_mask & TPM_INTF_STS_VALID_INT)) + sts_mask &= ~TPM_STS_VALID; + + if (!(int_mask & TPM_INTF_DATA_AVAIL_INT)) + sts_mask &= ~TPM_STS_DATA_AVAIL; + + if (!(int_mask & TPM_INTF_CMD_READY_INT)) + sts_mask &= ~TPM_STS_COMMAND_READY; + + return sts_mask; +} + static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, wait_queue_head_t *queue, bool check_cancel) @@ -53,7 +67,7 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, long rc; u8 status; bool canceled = false; - u8 sts_mask = 0; + u8 sts_mask; int ret = 0; /* check current status */ @@ -61,17 +75,10 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, if ((status & mask) == mask) return 0; + sts_mask = mask & (TPM_STS_VALID | TPM_STS_DATA_AVAIL | + TPM_STS_COMMAND_READY); /* check what status changes can be handled by irqs */ - if (priv->int_mask & TPM_INTF_STS_VALID_INT) - sts_mask |= TPM_STS_VALID; - - if (priv->int_mask & TPM_INTF_DATA_AVAIL_INT) - sts_mask |= TPM_STS_DATA_AVAIL; - - if (priv->int_mask & TPM_INTF_CMD_READY_INT) - sts_mask |= TPM_STS_COMMAND_READY; - - sts_mask &= mask; + sts_mask = tpm_tis_filter_sts_mask(priv->int_mask, sts_mask); stop = jiffies + timeout; /* process status changes with irq support */ From 35f621287ead686b66aa17aa605ed5d7c95883dc Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:32 +0100 Subject: [PATCH 16/29] tpm, tpm_tis: do not check for the active locality in interrupt handler After driver initialization tpm_tis_data->locality may only be modified in case of a LOCALITY CHANGE interrupt. In this case the interrupt handler iterates over all localities only to assign the active one to tpm_tis_data->locality. However this information is never used any more, so the assignment is not needed. Furthermore without the assignment tpm_tis_data->locality cannot change any more at driver runtime, and thus no protection against concurrent modification is required when the variable is read at other places. So remove this iteration entirely. Signed-off-by: Lino Sanfilippo Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 8981433abb0f..717b81435cbd 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -732,7 +732,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) struct tpm_chip *chip = dev_id; struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); u32 interrupt; - int i, rc; + int rc; rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt); if (rc < 0) @@ -744,10 +744,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) set_bit(TPM_TIS_IRQ_TESTED, &priv->flags); if (interrupt & TPM_INTF_DATA_AVAIL_INT) wake_up_interruptible(&priv->read_queue); - if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT) - for (i = 0; i < 5; i++) - if (check_locality(chip, i)) - break; + if (interrupt & (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT | TPM_INTF_CMD_READY_INT)) From 7a2f55d0be296c4e81fd782f3d6c43ed4ec7e265 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:33 +0100 Subject: [PATCH 17/29] tpm, tpm: Implement usage counter for locality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement a usage counter for the (default) locality used by the TPM TIS driver: Request the locality from the TPM if it has not been claimed yet, otherwise only increment the counter. Also release the locality if the counter is 0 otherwise only decrement the counter. Since in case of SPI the register accesses are locked by means of the SPI bus mutex use a sleepable lock (i.e. also a mutex) to ensure thread-safety of the counter which may be accessed by both a userspace thread and the interrupt handler. By doing this refactor the names of the amended functions to use a more appropriate prefix. Signed-off-by: Lino Sanfilippo Tested-by: Michael Niewöhner Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 65 +++++++++++++++++++++++---------- drivers/char/tpm/tpm_tis_core.h | 2 + 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 717b81435cbd..b05c1ed51dd3 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -165,16 +165,27 @@ static bool check_locality(struct tpm_chip *chip, int l) return false; } -static int release_locality(struct tpm_chip *chip, int l) +static int __tpm_tis_relinquish_locality(struct tpm_tis_data *priv, int l) { - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY); return 0; } -static int request_locality(struct tpm_chip *chip, int l) +static int tpm_tis_relinquish_locality(struct tpm_chip *chip, int l) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + + mutex_lock(&priv->locality_count_mutex); + priv->locality_count--; + if (priv->locality_count == 0) + __tpm_tis_relinquish_locality(priv, l); + mutex_unlock(&priv->locality_count_mutex); + + return 0; +} + +static int __tpm_tis_request_locality(struct tpm_chip *chip, int l) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); unsigned long stop, timeout; @@ -215,6 +226,20 @@ again: return -1; } +static int tpm_tis_request_locality(struct tpm_chip *chip, int l) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + int ret = 0; + + mutex_lock(&priv->locality_count_mutex); + if (priv->locality_count == 0) + ret = __tpm_tis_request_locality(chip, l); + if (!ret) + priv->locality_count++; + mutex_unlock(&priv->locality_count_mutex); + return ret; +} + static u8 tpm_tis_status(struct tpm_chip *chip) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); @@ -682,7 +707,7 @@ static int probe_itpm(struct tpm_chip *chip) if (vendor != TPM_VID_INTEL) return 0; - if (request_locality(chip, 0) != 0) + if (tpm_tis_request_locality(chip, 0) != 0) return -EBUSY; rc = tpm_tis_send_data(chip, cmd_getticks, len); @@ -703,7 +728,7 @@ static int probe_itpm(struct tpm_chip *chip) out: tpm_tis_ready(chip); - release_locality(chip, priv->locality); + tpm_tis_relinquish_locality(chip, priv->locality); return rc; } @@ -792,14 +817,14 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, } priv->irq = irq; - rc = request_locality(chip, 0); + rc = tpm_tis_request_locality(chip, 0); if (rc < 0) return rc; rc = tpm_tis_read8(priv, TPM_INT_VECTOR(priv->locality), &original_int_vec); if (rc < 0) { - release_locality(chip, priv->locality); + tpm_tis_relinquish_locality(chip, priv->locality); return rc; } @@ -838,7 +863,7 @@ restore_irqs: rc = -1; } - release_locality(chip, priv->locality); + tpm_tis_relinquish_locality(chip, priv->locality); return rc; } @@ -954,8 +979,8 @@ static const struct tpm_class_ops tpm_tis = { .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_canceled = tpm_tis_req_canceled, - .request_locality = request_locality, - .relinquish_locality = release_locality, + .request_locality = tpm_tis_request_locality, + .relinquish_locality = tpm_tis_relinquish_locality, .clk_enable = tpm_tis_clkrun_enable, }; @@ -989,6 +1014,8 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, priv->timeout_min = TPM_TIMEOUT_USECS_MIN; priv->timeout_max = TPM_TIMEOUT_USECS_MAX; priv->phy_ops = phy_ops; + priv->locality_count = 0; + mutex_init(&priv->locality_count_mutex); dev_set_drvdata(&chip->dev, priv); @@ -1067,14 +1094,14 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, intmask &= ~TPM_GLOBAL_INT_ENABLE; - rc = request_locality(chip, 0); + rc = tpm_tis_request_locality(chip, 0); if (rc < 0) { rc = -ENODEV; goto out_err; } tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); - release_locality(chip, 0); + tpm_tis_relinquish_locality(chip, 0); rc = tpm_chip_start(chip); if (rc) @@ -1108,13 +1135,13 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, * proper timeouts for the driver. */ - rc = request_locality(chip, 0); + rc = tpm_tis_request_locality(chip, 0); if (rc < 0) goto out_err; rc = tpm_get_timeouts(chip); - release_locality(chip, 0); + tpm_tis_relinquish_locality(chip, 0); if (rc) { dev_err(dev, "Could not get TPM timeouts and durations\n"); @@ -1134,11 +1161,11 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, dev_err(&chip->dev, FW_BUG "TPM interrupt not working, polling instead\n"); - rc = request_locality(chip, 0); + rc = tpm_tis_request_locality(chip, 0); if (rc < 0) goto out_err; disable_interrupts(chip); - release_locality(chip, 0); + tpm_tis_relinquish_locality(chip, 0); } } @@ -1205,13 +1232,13 @@ int tpm_tis_resume(struct device *dev) * an error code but for unknown reason it isn't handled. */ if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { - ret = request_locality(chip, 0); + ret = tpm_tis_request_locality(chip, 0); if (ret < 0) return ret; tpm1_do_selftest(chip); - release_locality(chip, 0); + tpm_tis_relinquish_locality(chip, 0); } return 0; diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index 19ef8c1a4142..e978f457fd4d 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -92,6 +92,8 @@ enum tpm_tis_flags { struct tpm_tis_data { u16 manufacturer_id; + struct mutex locality_count_mutex; + unsigned int locality_count; int locality; int irq; unsigned int int_mask; From 0c7e66e5fd69bf21034c9a9b081d7de7c3eb2cea Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:34 +0100 Subject: [PATCH 18/29] tpm, tpm_tis: Request threaded interrupt handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TIS interrupt handler at least has to read and write the interrupt status register. In case of SPI both operations result in a call to tpm_tis_spi_transfer() which uses the bus_lock_mutex of the spi device and thus must only be called from a sleepable context. To ensure this request a threaded interrupt handler. Signed-off-by: Lino Sanfilippo Tested-by: Michael Niewöhner Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index b05c1ed51dd3..bd443cf5a9f0 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -809,8 +809,11 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, int rc; u32 int_status; - if (devm_request_irq(chip->dev.parent, irq, tis_int_handler, flags, - dev_name(&chip->dev), chip) != 0) { + + rc = devm_request_threaded_irq(chip->dev.parent, irq, NULL, + tis_int_handler, IRQF_ONESHOT | flags, + dev_name(&chip->dev), chip); + if (rc) { dev_info(&chip->dev, "Unable to request irq: %d for probe\n", irq); return -1; From 0e069265bce5a40c4eee52e2364bbbd4dabee94a Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:35 +0100 Subject: [PATCH 19/29] tpm, tpm_tis: Claim locality in interrupt handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Writing the TPM_INT_STATUS register in the interrupt handler to clear the interrupts only has effect if a locality is held. Since this is not guaranteed at the time the interrupt is fired, claim the locality explicitly in the handler. Signed-off-by: Lino Sanfilippo Tested-by: Michael Niewöhner Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index bd443cf5a9f0..fda4ed61d0a4 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -776,7 +776,9 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) wake_up_interruptible(&priv->int_queue); /* Clear interrupts handled with TPM_EOI */ + tpm_tis_request_locality(chip, 0); rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), interrupt); + tpm_tis_relinquish_locality(chip, 0); if (rc < 0) return IRQ_NONE; From 955df4f87760b3bb2af253d3fbb12fb712b3ffa6 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:36 +0100 Subject: [PATCH 20/29] tpm, tpm_tis: Claim locality when interrupts are reenabled on resume In tpm_tis_resume() make sure that the locality has been claimed when tpm_tis_reenable_interrupts() is called. Otherwise the writings to the register might not have any effect. Fixes: 45baa1d1fa39 ("tpm_tis: Re-enable interrupts upon (S3) resume") Signed-off-by: Lino Sanfilippo Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index fda4ed61d0a4..dfb622a1b6f7 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -1225,28 +1225,27 @@ int tpm_tis_resume(struct device *dev) struct tpm_chip *chip = dev_get_drvdata(dev); int ret; + ret = tpm_tis_request_locality(chip, 0); + if (ret < 0) + return ret; + if (chip->flags & TPM_CHIP_FLAG_IRQ) tpm_tis_reenable_interrupts(chip); ret = tpm_pm_resume(dev); if (ret) - return ret; + goto out; /* * TPM 1.2 requires self-test on resume. This function actually returns * an error code but for unknown reason it isn't handled. */ - if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { - ret = tpm_tis_request_locality(chip, 0); - if (ret < 0) - return ret; - + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) tpm1_do_selftest(chip); +out: + tpm_tis_relinquish_locality(chip, 0); - tpm_tis_relinquish_locality(chip, 0); - } - - return 0; + return ret; } EXPORT_SYMBOL_GPL(tpm_tis_resume); #endif From 548eb516ec0f7a484a23a902835899341164b8ea Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:37 +0100 Subject: [PATCH 21/29] tpm, tpm_tis: startup chip before testing for interrupts In tpm_tis_gen_interrupt() a request for a property value is sent to the TPM to test if interrupts are generated. However after a power cycle the TPM responds with TPM_RC_INITIALIZE which indicates that the TPM is not yet properly initialized. Fix this by first starting the TPM up before the request is sent. For this the startup implementation is removed from tpm_chip_register() and put into the new function tpm_chip_startup() which is called before the interrupts are tested. Signed-off-by: Lino Sanfilippo Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm-chip.c | 38 +++++++++++++++++++++------------ drivers/char/tpm/tpm.h | 1 + drivers/char/tpm/tpm_tis_core.c | 5 +++++ 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 0601e6e5e326..33319a78767f 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -605,6 +605,30 @@ static int tpm_get_pcr_allocation(struct tpm_chip *chip) return rc; } +/* + * tpm_chip_startup() - performs auto startup and allocates the PCRs + * @chip: TPM chip to use. + */ +int tpm_chip_startup(struct tpm_chip *chip) +{ + int rc; + + rc = tpm_chip_start(chip); + if (rc) + return rc; + + rc = tpm_auto_startup(chip); + if (rc) + goto stop; + + rc = tpm_get_pcr_allocation(chip); +stop: + tpm_chip_stop(chip); + + return rc; +} +EXPORT_SYMBOL_GPL(tpm_chip_startup); + /* * tpm_chip_register() - create a character device for the TPM chip * @chip: TPM chip to use. @@ -620,20 +644,6 @@ int tpm_chip_register(struct tpm_chip *chip) { int rc; - rc = tpm_chip_start(chip); - if (rc) - return rc; - rc = tpm_auto_startup(chip); - if (rc) { - tpm_chip_stop(chip); - return rc; - } - - rc = tpm_get_pcr_allocation(chip); - tpm_chip_stop(chip); - if (rc) - return rc; - tpm_sysfs_add_device(chip); tpm_bios_log_setup(chip); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 830014a26609..88d3bd76e076 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -263,6 +263,7 @@ static inline void tpm_msleep(unsigned int delay_msec) delay_msec * 1000); }; +int tpm_chip_startup(struct tpm_chip *chip); int tpm_chip_start(struct tpm_chip *chip); void tpm_chip_stop(struct tpm_chip *chip); struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip); diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index dfb622a1b6f7..5b01a3df65a6 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -1133,6 +1133,11 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, /* INTERRUPT Setup */ init_waitqueue_head(&priv->read_queue); init_waitqueue_head(&priv->int_queue); + + rc = tpm_chip_startup(chip); + if (rc) + goto out_err; + if (irq != -1) { /* * Before doing irq testing issue a command to the TPM in polling mode From e644b2f498d297a928efcb7ff6f900c27f8b788e Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:38 +0100 Subject: [PATCH 22/29] tpm, tpm_tis: Enable interrupt test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test for interrupts in tpm_tis_send() is skipped if the flag TPM_CHIP_FLAG_IRQ is not set. Since the current code never sets the flag initially the test is never executed. Fix this by setting the flag in tpm_tis_gen_interrupt() right after interrupts have been enabled and before the test is executed. Signed-off-by: Lino Sanfilippo Tested-by: Michael Niewöhner Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 5b01a3df65a6..c2421162cf34 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -793,10 +793,15 @@ static void tpm_tis_gen_interrupt(struct tpm_chip *chip) cap_t cap; int ret; + chip->flags |= TPM_CHIP_FLAG_IRQ; + if (chip->flags & TPM_CHIP_FLAG_TPM2) ret = tpm2_get_tpm_pt(chip, 0x100, &cap2, desc); else ret = tpm1_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, desc, 0); + + if (ret) + chip->flags &= ~TPM_CHIP_FLAG_IRQ; } /* Register the IRQ and issue a command that will cause an interrupt. If an From c3985d8b9c224b359851f0a521ad25a83db6bdca Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 18:35:40 +0100 Subject: [PATCH 23/29] tpm: st33zp24: Mark ACPI and OF related data as maybe unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can be compile tested with !CONFIG_OF or !CONFIG_ACPI making drivers/char/tpm/st33zp24/i2c.c:141:34: error: ‘of_st33zp24_i2c_match’ defined but not used [-Werror=unused-const-variable=] drivers/char/tpm/st33zp24/spi.c:258:34: error: ‘of_st33zp24_spi_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/st33zp24/i2c.c | 4 ++-- drivers/char/tpm/st33zp24/spi.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index c4d0b744e3cc..2d28f55ef490 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -138,13 +138,13 @@ static const struct i2c_device_id st33zp24_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, st33zp24_i2c_id); -static const struct of_device_id of_st33zp24_i2c_match[] = { +static const struct of_device_id of_st33zp24_i2c_match[] __maybe_unused = { { .compatible = "st,st33zp24-i2c", }, {} }; MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match); -static const struct acpi_device_id st33zp24_i2c_acpi_match[] = { +static const struct acpi_device_id st33zp24_i2c_acpi_match[] __maybe_unused = { {"SMO3324"}, {} }; diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index 2154059f0235..f5811b301d3b 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -255,13 +255,13 @@ static const struct spi_device_id st33zp24_spi_id[] = { }; MODULE_DEVICE_TABLE(spi, st33zp24_spi_id); -static const struct of_device_id of_st33zp24_spi_match[] = { +static const struct of_device_id of_st33zp24_spi_match[] __maybe_unused = { { .compatible = "st,st33zp24-spi", }, {} }; MODULE_DEVICE_TABLE(of, of_st33zp24_spi_match); -static const struct acpi_device_id st33zp24_spi_acpi_match[] = { +static const struct acpi_device_id st33zp24_spi_acpi_match[] __maybe_unused = { {"SMO3324"}, {} }; From 3fb29a23fcdadc9d7b60d323bbc1d9896249f6ac Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 18:35:41 +0100 Subject: [PATCH 24/29] tpm: tpm_tis_spi: Mark ACPI and OF related data as maybe unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can be compile tested with !CONFIG_OF or !CONFIG_ACPI making unused: drivers/char/tpm/tpm_tis_spi_main.c:234:34: error: ‘of_tis_spi_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_spi_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_spi_main.c b/drivers/char/tpm/tpm_tis_spi_main.c index a0963a3e92bd..1f5207974a17 100644 --- a/drivers/char/tpm/tpm_tis_spi_main.c +++ b/drivers/char/tpm/tpm_tis_spi_main.c @@ -231,7 +231,7 @@ static const struct spi_device_id tpm_tis_spi_id[] = { }; MODULE_DEVICE_TABLE(spi, tpm_tis_spi_id); -static const struct of_device_id of_tis_spi_match[] = { +static const struct of_device_id of_tis_spi_match[] __maybe_unused = { { .compatible = "st,st33htpm-spi", .data = tpm_tis_spi_probe }, { .compatible = "infineon,slb9670", .data = tpm_tis_spi_probe }, { .compatible = "tcg,tpm_tis-spi", .data = tpm_tis_spi_probe }, @@ -240,7 +240,7 @@ static const struct of_device_id of_tis_spi_match[] = { }; MODULE_DEVICE_TABLE(of, of_tis_spi_match); -static const struct acpi_device_id acpi_tis_spi_match[] = { +static const struct acpi_device_id acpi_tis_spi_match[] __maybe_unused = { {"SMO0768", 0}, {} }; From bd88328607c4650ee8e7746a287375c5a618d09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 20 Mar 2023 09:06:05 +0100 Subject: [PATCH 25/29] tpm/tpm_ftpm_tee: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. ftpm_tee_remove() returns zero unconditionally (and cannot easily converted to return void). So ignore the return value to be able to make ftpm_plat_tee_remove() return void. Signed-off-by: Uwe Kleine-König Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_ftpm_tee.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c index deff23bb54bf..528f35b14fb6 100644 --- a/drivers/char/tpm/tpm_ftpm_tee.c +++ b/drivers/char/tpm/tpm_ftpm_tee.c @@ -334,11 +334,11 @@ static int ftpm_tee_remove(struct device *dev) return 0; } -static int ftpm_plat_tee_remove(struct platform_device *pdev) +static void ftpm_plat_tee_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - return ftpm_tee_remove(dev); + ftpm_tee_remove(dev); } /** @@ -367,7 +367,7 @@ static struct platform_driver ftpm_tee_plat_driver = { }, .shutdown = ftpm_plat_tee_shutdown, .probe = ftpm_plat_tee_probe, - .remove = ftpm_plat_tee_remove, + .remove_new = ftpm_plat_tee_remove, }; /* UUID of the fTPM TA */ From c3da2c6eeb10e71cc7be33c4b17ced60024fd9b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 20 Mar 2023 09:06:06 +0100 Subject: [PATCH 26/29] tpm/tpm_tis: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 914abc58a328..ba0402202bee 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -324,14 +324,12 @@ static int tpm_tis_plat_probe(struct platform_device *pdev) return tpm_tis_init(&pdev->dev, &tpm_info); } -static int tpm_tis_plat_remove(struct platform_device *pdev) +static void tpm_tis_plat_remove(struct platform_device *pdev) { struct tpm_chip *chip = dev_get_drvdata(&pdev->dev); tpm_chip_unregister(chip); tpm_tis_remove(chip); - - return 0; } #ifdef CONFIG_OF @@ -344,7 +342,7 @@ MODULE_DEVICE_TABLE(of, tis_of_platform_match); static struct platform_driver tis_drv = { .probe = tpm_tis_plat_probe, - .remove = tpm_tis_plat_remove, + .remove_new = tpm_tis_plat_remove, .driver = { .name = "tpm_tis", .pm = &tpm_tis_pm, From 7b69ef62034492b816c65b1d15b45834df2b194e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 20 Mar 2023 09:06:07 +0100 Subject: [PATCH 27/29] tpm/tpm_tis_synquacer: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_synquacer.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_synquacer.c b/drivers/char/tpm/tpm_tis_synquacer.c index 679196c61401..49278746b0e2 100644 --- a/drivers/char/tpm/tpm_tis_synquacer.c +++ b/drivers/char/tpm/tpm_tis_synquacer.c @@ -127,14 +127,12 @@ static int tpm_tis_synquacer_probe(struct platform_device *pdev) return tpm_tis_synquacer_init(&pdev->dev, &tpm_info); } -static int tpm_tis_synquacer_remove(struct platform_device *pdev) +static void tpm_tis_synquacer_remove(struct platform_device *pdev) { struct tpm_chip *chip = dev_get_drvdata(&pdev->dev); tpm_chip_unregister(chip); tpm_tis_remove(chip); - - return 0; } #ifdef CONFIG_OF @@ -155,7 +153,7 @@ MODULE_DEVICE_TABLE(acpi, tpm_synquacer_acpi_tbl); static struct platform_driver tis_synquacer_drv = { .probe = tpm_tis_synquacer_probe, - .remove = tpm_tis_synquacer_remove, + .remove_new = tpm_tis_synquacer_remove, .driver = { .name = "tpm_tis_synquacer", .pm = &tpm_tis_synquacer_pm, From 77218e83c83c1cd4b994edfb5b162ece42e73ffe Mon Sep 17 00:00:00 2001 From: Haris Okanovic Date: Wed, 19 Apr 2023 17:41:30 +0200 Subject: [PATCH 28/29] tpm_tis: fix stall after iowrite*()s ioread8() operations to TPM MMIO addresses can stall the CPU when immediately following a sequence of iowrite*()'s to the same region. For example, cyclitest measures ~400us latency spikes when a non-RT usermode application communicates with an SPI-based TPM chip (Intel Atom E3940 system, PREEMPT_RT kernel). The spikes are caused by a stalling ioread8() operation following a sequence of 30+ iowrite8()s to the same address. I believe this happens because the write sequence is buffered (in CPU or somewhere along the bus), and gets flushed on the first LOAD instruction (ioread*()) that follows. The enclosed change appears to fix this issue: read the TPM chip's access register (status code) after every iowrite*() operation to amortize the cost of flushing data to chip across multiple instructions. Signed-off-by: Haris Okanovic Link: https://lore.kernel.org/r/20230323153436.B2SATnZV@linutronix.de Signed-off-by: Sebastian Andrzej Siewior Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis.c | 43 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index ba0402202bee..7af389806643 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -50,6 +50,45 @@ static inline struct tpm_tis_tcg_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *da return container_of(data, struct tpm_tis_tcg_phy, priv); } +#ifdef CONFIG_PREEMPT_RT +/* + * Flush previous write operations with a dummy read operation to the + * TPM MMIO base address. + */ +static inline void tpm_tis_flush(void __iomem *iobase) +{ + ioread8(iobase + TPM_ACCESS(0)); +} +#else +#define tpm_tis_flush(iobase) do { } while (0) +#endif + +/* + * Write a byte word to the TPM MMIO address, and flush the write queue. + * The flush ensures that the data is sent immediately over the bus and not + * aggregated with further requests and transferred later in a batch. The large + * write requests can lead to unwanted latency spikes by blocking the CPU until + * the complete batch has been transferred. + */ +static inline void tpm_tis_iowrite8(u8 b, void __iomem *iobase, u32 addr) +{ + iowrite8(b, iobase + addr); + tpm_tis_flush(iobase); +} + +/* + * Write a 32-bit word to the TPM MMIO address, and flush the write queue. + * The flush ensures that the data is sent immediately over the bus and not + * aggregated with further requests and transferred later in a batch. The large + * write requests can lead to unwanted latency spikes by blocking the CPU until + * the complete batch has been transferred. + */ +static inline void tpm_tis_iowrite32(u32 b, void __iomem *iobase, u32 addr) +{ + iowrite32(b, iobase + addr); + tpm_tis_flush(iobase); +} + static int interrupts = -1; module_param(interrupts, int, 0444); MODULE_PARM_DESC(interrupts, "Enable interrupts"); @@ -186,12 +225,12 @@ static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len, switch (io_mode) { case TPM_TIS_PHYS_8: while (len--) - iowrite8(*value++, phy->iobase + addr); + tpm_tis_iowrite8(*value++, phy->iobase, addr); break; case TPM_TIS_PHYS_16: return -EINVAL; case TPM_TIS_PHYS_32: - iowrite32(le32_to_cpu(*((__le32 *)value)), phy->iobase + addr); + tpm_tis_iowrite32(le32_to_cpu(*((__le32 *)value)), phy->iobase, addr); break; } From bd8621ca1510e6e802df9855bdc35a04a3cfa932 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sun, 23 Apr 2023 18:49:58 +0300 Subject: [PATCH 29/29] tpm: Add !tpm_amd_is_rng_defective() to the hwrng_unregister() call site The following crash was reported: [ 1950.279393] list_del corruption, ffff99560d485790->next is NULL [ 1950.279400] ------------[ cut here ]------------ [ 1950.279401] kernel BUG at lib/list_debug.c:49! [ 1950.279405] invalid opcode: 0000 [#1] PREEMPT SMP NOPTI [ 1950.279407] CPU: 11 PID: 5886 Comm: modprobe Tainted: G O 6.2.8_1 #1 [ 1950.279409] Hardware name: Gigabyte Technology Co., Ltd. B550M AORUS PRO-P/B550M AORUS PRO-P, BIOS F15c 05/11/2022 [ 1950.279410] RIP: 0010:__list_del_entry_valid+0x59/0xc0 [ 1950.279415] Code: 48 8b 01 48 39 f8 75 5a 48 8b 72 08 48 39 c6 75 65 b8 01 00 00 00 c3 cc cc cc cc 48 89 fe 48 c7 c7 08 a8 13 9e e8 b7 0a bc ff <0f> 0b 48 89 fe 48 c7 c7 38 a8 13 9e e8 a6 0a bc ff 0f 0b 48 89 fe [ 1950.279416] RSP: 0018:ffffa96d05647e08 EFLAGS: 00010246 [ 1950.279418] RAX: 0000000000000033 RBX: ffff99560d485750 RCX: 0000000000000000 [ 1950.279419] RDX: 0000000000000000 RSI: ffffffff9e107c59 RDI: 00000000ffffffff [ 1950.279420] RBP: ffffffffc19c5168 R08: 0000000000000000 R09: ffffa96d05647cc8 [ 1950.279421] R10: 0000000000000003 R11: ffffffff9ea2a568 R12: 0000000000000000 [ 1950.279422] R13: ffff99560140a2e0 R14: ffff99560127d2e0 R15: 0000000000000000 [ 1950.279422] FS: 00007f67da795380(0000) GS:ffff995d1f0c0000(0000) knlGS:0000000000000000 [ 1950.279424] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 1950.279424] CR2: 00007f67da7e65c0 CR3: 00000001feed2000 CR4: 0000000000750ee0 [ 1950.279426] PKRU: 55555554 [ 1950.279426] Call Trace: [ 1950.279428] [ 1950.279430] hwrng_unregister+0x28/0xe0 [rng_core] [ 1950.279436] tpm_chip_unregister+0xd5/0xf0 [tpm] Add the forgotten !tpm_amd_is_rng_defective() invariant to the hwrng_unregister() call site inside tpm_chip_unregister(). Cc: stable@vger.kernel.org Reported-by: Martin Dimov Link: https://lore.kernel.org/linux-integrity/3d1d7e9dbfb8c96125bc93b6b58b90a7@dmarto.com/ Fixes: f1324bbc4011 ("tpm: disable hwrng for fTPM on some AMD designs") Fixes: b006c439d58d ("hwrng: core - start hwrng kthread also for untrusted sources") Tested-by: Martin Dimov Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm-chip.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 33319a78767f..6fdfa65a00c3 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -692,7 +692,8 @@ EXPORT_SYMBOL_GPL(tpm_chip_register); void tpm_chip_unregister(struct tpm_chip *chip) { tpm_del_legacy_sysfs(chip); - if (IS_ENABLED(CONFIG_HW_RANDOM_TPM) && !tpm_is_firmware_upgrade(chip)) + if (IS_ENABLED(CONFIG_HW_RANDOM_TPM) && !tpm_is_firmware_upgrade(chip) && + !tpm_amd_is_rng_defective(chip)) hwrng_unregister(&chip->hwrng); tpm_bios_log_teardown(chip); if (chip->flags & TPM_CHIP_FLAG_TPM2 && !tpm_is_firmware_upgrade(chip))