This is preparatory patch to integrate tsm measurement registers with IMA.

To integrate tsm measurement registers, introcude ima_mr structure
which abstract measurements register and ima_mr_operation structure
which defines below operations to communicate with them:

  - mr_init(): find and initialise to communicate measurement registers
  - mr_get_bank_info: get information of bank of measurement registers.
  - mr_calc_boot_aggregate: generate boot aggregate hash with
                            measurement registers.
  - mr_extend: extend measurement registers.

Also, this patch adds ima_mr using TPM device using PCR as
measurement registers.

Signed-off-by: Yeoreum Yun <[email protected]>
---
 security/integrity/ima/Makefile           |   2 +-
 security/integrity/ima/ima.h              |   7 +-
 security/integrity/ima/ima_api.c          |   4 +-
 security/integrity/ima/ima_crypto.c       | 137 +++++++++-----------------
 security/integrity/ima/ima_fs.c           |  16 ++-
 security/integrity/ima/ima_init.c         |   7 +-
 security/integrity/ima/ima_mr.c           |  47 +++++++++
 security/integrity/ima/ima_mr.h           |  75 +++++++++++++++
 security/integrity/ima/ima_mr_tpm.c       | 155 ++++++++++++++++++++++++++++++
 security/integrity/ima/ima_queue.c        |  39 ++++----
 security/integrity/ima/ima_template.c     |   4 +-
 security/integrity/ima/ima_template_lib.c |   2 +-
 12 files changed, 365 insertions(+), 130 deletions(-)

diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index b376d38b4ee6..f2c46b405a00 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -7,7 +7,7 @@
 obj-$(CONFIG_IMA) += ima.o ima_iint.o
 
 ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
-        ima_policy.o ima_template.o ima_template_lib.o
+        ima_policy.o ima_template.o ima_template_lib.o ima_mr.o ima_mr_tpm.o
 ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
 ima-$(CONFIG_IMA_APPRAISE_MODSIG) += ima_modsig.o
 ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 10214f73ca1e..5e43d3140357 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -22,11 +22,11 @@
 #include <linux/audit.h>
 #include <crypto/hash_info.h>
 
+#include "ima_mr.h"
 #include "../integrity.h"
 
 enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_BINARY_NO_FIELD_LEN,
                     IMA_SHOW_BINARY_OLD_STRING_FMT, IMA_SHOW_ASCII };
-enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 };
 
 /*
  * BINARY: current binary measurements list
@@ -50,8 +50,6 @@ enum binary_lists {
 #define IMA_TEMPLATE_IMA_NAME "ima"
 #define IMA_TEMPLATE_IMA_FMT "d|n"
 
-#define NR_BANKS(chip) ((chip != NULL) ? chip->nr_allocated_banks : 0)
-
 /* current content of the policy */
 extern int ima_policy_flag;
 
@@ -75,7 +73,6 @@ extern int ima_extra_slots __ro_after_init;
 extern struct ima_algo_desc *ima_algo_array __ro_after_init;
 
 extern int ima_appraise;
-extern struct tpm_chip *ima_tpm_chip;
 extern const char boot_aggregate_name[];
 extern const char boot_aggregate_late_name[];
 
@@ -118,7 +115,7 @@ struct ima_template_desc {
 
 struct ima_template_entry {
        int pcr;
-       struct tpm_digest *digests;
+       mr_digest_t *digests;
        struct ima_template_desc *template_desc; /* template descriptor */
        u32 template_data_len;
        struct ima_field_data template_data[];  /* template related data */
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 122d127e108d..8a7194a26b81 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -40,7 +40,7 @@ int ima_alloc_init_template(struct ima_event_data *event_data,
                            struct ima_template_desc *desc)
 {
        struct ima_template_desc *template_desc;
-       struct tpm_digest *digests;
+       mr_digest_t *digests;
        int i, result = 0;
 
        if (desc)
@@ -54,7 +54,7 @@ int ima_alloc_init_template(struct ima_event_data *event_data,
                return -ENOMEM;
 
        digests = kzalloc_objs(*digests,
-                              NR_BANKS(ima_tpm_chip) + ima_extra_slots,
+                              NR_BANKS(ima_mr) + ima_extra_slots,
                               GFP_NOFS);
        if (!digests) {
                kfree(*entry);
diff --git a/security/integrity/ima/ima_crypto.c 
b/security/integrity/ima/ima_crypto.c
index 0d72b48249ee..efa27ce5f128 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -58,7 +58,7 @@ static struct crypto_shash *ima_alloc_tfm(enum hash_algo algo)
        if (algo == ima_hash_algo)
                return tfm;
 
-       for (i = 0; i < NR_BANKS(ima_tpm_chip) + ima_extra_slots; i++)
+       for (i = 0; i < NR_BANKS(ima_mr) + ima_extra_slots; i++)
                if (ima_algo_array[i].tfm && ima_algo_array[i].algo == algo)
                        return ima_algo_array[i].tfm;
 
@@ -77,6 +77,7 @@ int __init ima_init_crypto(void)
        enum hash_algo algo;
        long rc;
        int i;
+       mr_bank_info_t bank_info;
 
        rc = ima_init_ima_crypto();
        if (rc)
@@ -85,8 +86,12 @@ int __init ima_init_crypto(void)
        ima_sha1_idx = -1;
        ima_hash_algo_idx = -1;
 
-       for (i = 0; i < NR_BANKS(ima_tpm_chip); i++) {
-               algo = ima_tpm_chip->allocated_banks[i].crypto_id;
+       for (i = 0; i < NR_BANKS(ima_mr); i++) {
+               rc = ima_mr->ops->mr_get_bank_info(ima_mr, i, &bank_info);
+               if (rc)
+                       return rc;
+
+               algo = bank_info.crypto_id;
                if (algo == HASH_ALGO_SHA1)
                        ima_sha1_idx = i;
 
@@ -95,24 +100,28 @@ int __init ima_init_crypto(void)
        }
 
        if (ima_sha1_idx < 0) {
-               ima_sha1_idx = NR_BANKS(ima_tpm_chip) + ima_extra_slots++;
+               ima_sha1_idx = NR_BANKS(ima_mr) + ima_extra_slots++;
                if (ima_hash_algo == HASH_ALGO_SHA1)
                        ima_hash_algo_idx = ima_sha1_idx;
        }
 
        if (ima_hash_algo_idx < 0)
-               ima_hash_algo_idx = NR_BANKS(ima_tpm_chip) + ima_extra_slots++;
+               ima_hash_algo_idx = NR_BANKS(ima_mr) + ima_extra_slots++;
 
        ima_algo_array = kzalloc_objs(*ima_algo_array,
-                                     NR_BANKS(ima_tpm_chip) + ima_extra_slots);
+                                     NR_BANKS(ima_mr) + ima_extra_slots);
        if (!ima_algo_array) {
                rc = -ENOMEM;
                goto out;
        }
 
-       for (i = 0; i < NR_BANKS(ima_tpm_chip); i++) {
-               algo = ima_tpm_chip->allocated_banks[i].crypto_id;
-               digest_size = ima_tpm_chip->allocated_banks[i].digest_size;
+       for (i = 0; i < NR_BANKS(ima_mr); i++) {
+               rc = ima_mr->ops->mr_get_bank_info(ima_mr, i, &bank_info);
+               if (rc)
+                       return rc;
+
+               algo = bank_info.crypto_id;
+               digest_size = bank_info.digest_size;
                ima_algo_array[i].algo = algo;
                ima_algo_array[i].digest_size = digest_size;
 
@@ -137,7 +146,7 @@ int __init ima_init_crypto(void)
                }
        }
 
-       if (ima_sha1_idx >= NR_BANKS(ima_tpm_chip)) {
+       if (ima_sha1_idx >= NR_BANKS(ima_mr)) {
                if (ima_hash_algo == HASH_ALGO_SHA1) {
                        ima_algo_array[ima_sha1_idx].tfm = ima_shash_tfm;
                } else {
@@ -153,7 +162,7 @@ int __init ima_init_crypto(void)
                ima_algo_array[ima_sha1_idx].digest_size = SHA1_DIGEST_SIZE;
        }
 
-       if (ima_hash_algo_idx >= NR_BANKS(ima_tpm_chip) &&
+       if (ima_hash_algo_idx >= NR_BANKS(ima_mr) &&
            ima_hash_algo_idx != ima_sha1_idx) {
                digest_size = hash_digest_size[ima_hash_algo];
                ima_algo_array[ima_hash_algo_idx].tfm = ima_shash_tfm;
@@ -163,7 +172,7 @@ int __init ima_init_crypto(void)
 
        return 0;
 out_array:
-       for (i = 0; i < NR_BANKS(ima_tpm_chip) + ima_extra_slots; i++) {
+       for (i = 0; i < NR_BANKS(ima_mr) + ima_extra_slots; i++) {
                if (!ima_algo_array[i].tfm ||
                    ima_algo_array[i].tfm == ima_shash_tfm)
                        continue;
@@ -183,7 +192,7 @@ static void ima_free_tfm(struct crypto_shash *tfm)
        if (tfm == ima_shash_tfm)
                return;
 
-       for (i = 0; i < NR_BANKS(ima_tpm_chip) + ima_extra_slots; i++)
+       for (i = 0; i < NR_BANKS(ima_mr) + ima_extra_slots; i++)
                if (ima_algo_array[i].tfm == tfm)
                        return;
 
@@ -335,7 +344,7 @@ static int ima_calc_field_array_hash_tfm(struct 
ima_field_data *field_data,
 int ima_calc_field_array_hash(struct ima_field_data *field_data,
                              struct ima_template_entry *entry)
 {
-       u16 alg_id;
+       mr_bank_info_t bank_info;
        int rc, i;
 
        rc = ima_calc_field_array_hash_tfm(field_data, entry, ima_sha1_idx);
@@ -344,13 +353,16 @@ int ima_calc_field_array_hash(struct ima_field_data 
*field_data,
 
        entry->digests[ima_sha1_idx].alg_id = TPM_ALG_SHA1;
 
-       for (i = 0; i < NR_BANKS(ima_tpm_chip) + ima_extra_slots; i++) {
+       for (i = 0; i < NR_BANKS(ima_mr) + ima_extra_slots; i++) {
                if (i == ima_sha1_idx)
                        continue;
 
-               if (i < NR_BANKS(ima_tpm_chip)) {
-                       alg_id = ima_tpm_chip->allocated_banks[i].alg_id;
-                       entry->digests[i].alg_id = alg_id;
+               if (i < NR_BANKS(ima_mr)) {
+                       rc = ima_mr->ops->mr_get_bank_info(ima_mr, i, 
&bank_info);
+                       if (rc)
+                               return rc;
+
+                       entry->digests[i].alg_id = bank_info.alg_id;
                }
 
                /* for unmapped TPM algorithms digest is still a padded SHA1 */
@@ -414,87 +426,26 @@ int ima_calc_buffer_hash(const void *buf, loff_t len,
        return rc;
 }
 
-static void ima_pcrread(u32 idx, struct tpm_digest *d)
-{
-       if (!ima_tpm_chip)
-               return;
-
-       if (tpm_pcr_read(ima_tpm_chip, idx, d) != 0)
-               pr_err("Error Communicating to TPM chip\n");
-}
-
-/*
- * The boot_aggregate is a cumulative hash over TPM registers 0 - 7.  With
- * TPM 1.2 the boot_aggregate was based on reading the SHA1 PCRs, but with
- * TPM 2.0 hash agility, TPM chips could support multiple TPM PCR banks,
- * allowing firmware to configure and enable different banks.
- *
- * Knowing which TPM bank is read to calculate the boot_aggregate digest
- * needs to be conveyed to a verifier.  For this reason, use the same
- * hash algorithm for reading the TPM PCRs as for calculating the boot
- * aggregate digest as stored in the measurement list.
- */
-static int ima_calc_boot_aggregate_tfm(char *digest, u16 alg_id,
-                                      struct crypto_shash *tfm)
-{
-       struct tpm_digest d = { .alg_id = alg_id, .digest = {0} };
-       int rc;
-       u32 i;
-       SHASH_DESC_ON_STACK(shash, tfm);
-
-       shash->tfm = tfm;
-
-       pr_devel("calculating the boot-aggregate based on TPM bank: %04x\n",
-                d.alg_id);
-
-       rc = crypto_shash_init(shash);
-       if (rc != 0)
-               return rc;
-
-       /* cumulative digest over TPM registers 0-7 */
-       for (i = TPM_PCR0; i < TPM_PCR8; i++) {
-               ima_pcrread(i, &d);
-               /* now accumulate with current aggregate */
-               rc = crypto_shash_update(shash, d.digest,
-                                        crypto_shash_digestsize(tfm));
-               if (rc != 0)
-                       return rc;
-       }
-       /*
-        * Extend cumulative digest over TPM registers 8-9, which contain
-        * measurement for the kernel command line (reg. 8) and image (reg. 9)
-        * in a typical PCR allocation. Registers 8-9 are only included in
-        * non-SHA1 boot_aggregate digests to avoid ambiguity.
-        */
-       if (alg_id != TPM_ALG_SHA1) {
-               for (i = TPM_PCR8; i < TPM_PCR10; i++) {
-                       ima_pcrread(i, &d);
-                       rc = crypto_shash_update(shash, d.digest,
-                                               crypto_shash_digestsize(tfm));
-               }
-       }
-       if (!rc)
-               rc = crypto_shash_final(shash, digest);
-       return rc;
-}
-
 int ima_calc_boot_aggregate(struct ima_digest_data *hash)
 {
        struct crypto_shash *tfm;
-       u16 crypto_id, alg_id;
+       mr_bank_info_t bank_info;
        int rc, i, bank_idx = -1;
 
-       for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++) {
-               crypto_id = ima_tpm_chip->allocated_banks[i].crypto_id;
-               if (crypto_id == hash->algo) {
+       for (i = 0; i < NR_BANKS(ima_mr); i++) {
+               rc = ima_mr->ops->mr_get_bank_info(ima_mr, i, &bank_info);
+               if (rc)
+                       return rc;
+
+               if (bank_info.crypto_id == hash->algo) {
                        bank_idx = i;
                        break;
                }
 
-               if (crypto_id == HASH_ALGO_SHA256)
+               if (bank_info.crypto_id == HASH_ALGO_SHA256)
                        bank_idx = i;
 
-               if (bank_idx == -1 && crypto_id == HASH_ALGO_SHA1)
+               if (bank_idx == -1 && bank_info.crypto_id == HASH_ALGO_SHA1)
                        bank_idx = i;
        }
 
@@ -503,15 +454,19 @@ int ima_calc_boot_aggregate(struct ima_digest_data *hash)
                return 0;
        }
 
-       hash->algo = ima_tpm_chip->allocated_banks[bank_idx].crypto_id;
+       rc = ima_mr->ops->mr_get_bank_info(ima_mr, bank_idx, &bank_info);
+       if (rc)
+               return rc;
+
+       hash->algo = bank_info.crypto_id;
 
        tfm = ima_alloc_tfm(hash->algo);
        if (IS_ERR(tfm))
                return PTR_ERR(tfm);
 
        hash->length = crypto_shash_digestsize(tfm);
-       alg_id = ima_tpm_chip->allocated_banks[bank_idx].alg_id;
-       rc = ima_calc_boot_aggregate_tfm(hash->digest, alg_id, tfm);
+       rc = ima_mr->ops->mr_calc_boot_aggregate(ima_mr, bank_idx,
+                                                 hash->digest, tfm);
 
        ima_free_tfm(tfm);
 
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 2a0bca554316..cfe1d5227e54 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -635,7 +635,9 @@ static int __init create_securityfs_measurement_lists(bool 
staging)
        const struct file_operations *binary_ops = &ima_measurements_ops;
        umode_t permissions = (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP);
        const char *file_suffix = "";
-       int count = NR_BANKS(ima_tpm_chip);
+       int count = NR_BANKS(ima_mr);
+       int rc;
+       mr_bank_info_t bank_info;
 
        if (staging) {
                ascii_ops = &ima_ascii_measurements_staged_ops;
@@ -643,7 +645,7 @@ static int __init create_securityfs_measurement_lists(bool 
staging)
                file_suffix = "_staged";
        }
 
-       if (ima_sha1_idx >= NR_BANKS(ima_tpm_chip))
+       if (ima_sha1_idx >= NR_BANKS(ima_mr))
                count++;
 
        for (int i = 0; i < count; i++) {
@@ -651,10 +653,16 @@ static int __init 
create_securityfs_measurement_lists(bool staging)
                char file_name[NAME_MAX + 1];
                struct dentry *dentry;
 
+               if (algo == HASH_ALGO__LAST) {
+                       rc = ima_mr->ops->mr_get_bank_info(ima_mr, i, 
&bank_info);
+                       if (rc)
+                               return rc;
+               }
+
                if (algo == HASH_ALGO__LAST)
                        snprintf(file_name, sizeof(file_name),
                                 "ascii_runtime_measurements_tpm_alg_%x%s",
-                                ima_tpm_chip->allocated_banks[i].alg_id,
+                                bank_info.alg_id,
                                 file_suffix);
                else
                        snprintf(file_name, sizeof(file_name),
@@ -669,7 +677,7 @@ static int __init create_securityfs_measurement_lists(bool 
staging)
                if (algo == HASH_ALGO__LAST)
                        snprintf(file_name, sizeof(file_name),
                                 "binary_runtime_measurements_tpm_alg_%x%s",
-                                ima_tpm_chip->allocated_banks[i].alg_id,
+                                bank_info.alg_id,
                                 file_suffix);
                else
                        snprintf(file_name, sizeof(file_name),
diff --git a/security/integrity/ima/ima_init.c 
b/security/integrity/ima/ima_init.c
index d53f4d89a53e..a1290e891fa4 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -23,7 +23,6 @@
 /* name for boot aggregate entry */
 const char boot_aggregate_name[] = "boot_aggregate";
 const char boot_aggregate_late_name[] = "boot_aggregate_late";
-struct tpm_chip *ima_tpm_chip;
 
 /* Add the boot aggregate to the IMA measurement list and extend
  * the PCR register.
@@ -78,7 +77,7 @@ static int __init ima_add_boot_aggregate(void)
         * Ultimately select SHA1 also for TPM 2.0 if the SHA256 PCR bank
         * is not found.
         */
-       if (ima_tpm_chip) {
+       if (ima_mr) {
                result = ima_calc_boot_aggregate(hash_hdr);
                if (result < 0) {
                        audit_cause = "hashing_error";
@@ -126,9 +125,7 @@ int __init ima_init(void)
 {
        int rc;
 
-       ima_tpm_chip = tpm_default_chip();
-       if (!ima_tpm_chip)
-               pr_info("No TPM chip found, activating TPM-bypass!\n");
+       ima_init_mr();
 
        rc = integrity_init_keyring(INTEGRITY_KEYRING_IMA);
        if (rc)
diff --git a/security/integrity/ima/ima_mr.c b/security/integrity/ima/ima_mr.c
new file mode 100644
index 000000000000..fe58eb968954
--- /dev/null
+++ b/security/integrity/ima/ima_mr.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Arm Ltd
+ *
+ * Author:
+ * Yeoreum Yun <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "ima.h"
+
+struct ima_mr *ima_mr;
+
+static struct ima_mr_operations *ima_mr_ops[] = {
+       &ima_mr_tpm_operations,
+};
+
+void __init ima_init_mr(void)
+{
+       int rc, i;
+
+       ima_mr = kmalloc_obj(*ima_mr);
+       if (!ima_mr) {
+               pr_info("Out of memory creating MR, activating MR-bypass!\n");
+               return;
+       }
+
+       rc = -ENODEV;
+       for (i = 0; i < ARRAY_SIZE(ima_mr_ops); i++) {
+               if (!ima_mr_ops[i]->supported)
+                       continue;
+
+               rc = ima_mr_ops[i]->mr_init(ima_mr);
+               if (!rc) {
+                       pr_info("MR device found: %s\n", ima_mr_ops[i]->name);
+                       break;
+               }
+       }
+
+       if (rc) {
+               pr_info("No MR device found, activating MR-bypass!\n");
+               kfree(ima_mr);
+               ima_mr = NULL;
+       }
+}
diff --git a/security/integrity/ima/ima_mr.h b/security/integrity/ima/ima_mr.h
new file mode 100644
index 000000000000..23b85522da34
--- /dev/null
+++ b/security/integrity/ima/ima_mr.h
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Arm Ltd
+ *
+ * Author:
+ * Yeoreum Yun <[email protected]>
+ */
+
+#ifndef __LINUX_IMA_MR_H
+#define __LINUX_IMA_MR_H
+
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <linux/hash.h>
+#include <linux/tpm.h>
+
+#define NR_BANKS(mr) ((mr != NULL) ? mr->nr_banks : 0)
+
+typedef struct tpm_bank_info mr_bank_info_t;
+typedef struct tpm_digest    mr_digest_t;
+
+enum tpm_pcrs {
+       TPM_PCR0 = 0,
+       TPM_PCR1 = 1,
+       TPM_PCR2 = 2,
+       TPM_PCR7 = 7,
+       TPM_PCR8 = 8,
+       TPM_PCR10 = 10,
+       TPM_PCR16 = 16,
+};
+
+struct ima_mr_operations;
+
+struct ima_mr {
+       int nr_banks;
+       struct ima_mr_operations *ops;
+       void *data;
+};
+
+struct ima_mr_operations {
+       const char *name;
+       bool supported;
+       int (*mr_init)(struct ima_mr *mr);
+       int (*mr_get_bank_info)(struct ima_mr *mr, int bank,
+                               mr_bank_info_t *info);
+       int (*mr_calc_boot_aggregate)(struct ima_mr *mr, int bank,
+                                     char *digest, struct crypto_shash *tfm);
+       int (*mr_extend)(struct ima_mr *mr, u32 pcr_idx,
+                        mr_digest_t *digests);
+};
+
+extern struct ima_mr *ima_mr;
+extern struct ima_mr_operations ima_mr_tpm_operations;
+
+void __init ima_init_mr(void);
+
+static __always_inline u16 hash_to_alg(u16 hash_id)
+{
+       switch (hash_id) {
+       case HASH_ALGO_SHA1:
+               return TPM_ALG_SHA1;
+       case HASH_ALGO_SHA256:
+               return TPM_ALG_SHA256;
+       case HASH_ALGO_SHA384:
+               return TPM_ALG_SHA384;
+       case HASH_ALGO_SHA512:
+               return TPM_ALG_SHA512;
+       case HASH_ALGO_SM3_256:
+               return TPM_ALG_SM3_256;
+       default:
+               return TPM_ALG_ERROR;
+       }
+}
+
+#endif /* __LINUX_IMA_MR_H */
diff --git a/security/integrity/ima/ima_mr_tpm.c 
b/security/integrity/ima/ima_mr_tpm.c
new file mode 100644
index 000000000000..edee83d5a551
--- /dev/null
+++ b/security/integrity/ima/ima_mr_tpm.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Arm Ltd
+ *
+ * Author:
+ * Yeoreum Yun <[email protected]>
+ */
+
+#include <linux/kernel.h>
+
+#include "ima.h"
+
+static int tpm_mr_init(struct ima_mr *mr)
+{
+       struct tpm_chip *tpm_chip;
+
+       if (!mr)
+               return -EINVAL;
+
+       tpm_chip = tpm_default_chip();
+       if (!tpm_chip) {
+               pr_info("No TPM chip found!\n");
+               return -ENODEV;
+       }
+
+       mr->data = tpm_chip;
+       mr->nr_banks = tpm_chip->nr_allocated_banks;
+       mr->ops = &ima_mr_tpm_operations;
+
+       return 0;
+}
+
+static int tpm_mr_get_bank_info(struct ima_mr *mr, int bank,
+                               mr_bank_info_t *info)
+{
+       struct tpm_chip *tpm_chip;
+
+       if (!mr || !mr->data || !info || (bank >= mr->nr_banks))
+               return -EINVAL;
+
+       tpm_chip = mr->data;
+       info->alg_id = tpm_chip->allocated_banks[bank].alg_id;
+       info->digest_size = tpm_chip->allocated_banks[bank].digest_size;
+       info->crypto_id = tpm_chip->allocated_banks[bank].crypto_id;
+
+       if (WARN_ON_ONCE((info->crypto_id != HASH_ALGO__LAST) &&
+                        (hash_to_alg(info->crypto_id) != info->alg_id)))
+               return -ENODEV;
+
+       return 0;
+}
+
+/*
+ * The boot_aggregate is a cumulative hash over TPM registers 0 - 7.  With
+ * TPM 1.2 the boot_aggregate was based on reading the SHA1 PCRs, but with
+ * TPM 2.0 hash agility, TPM chips could support multiple TPM PCR banks,
+ * allowing firmware to configure and enable different banks.
+ *
+ * Knowing which TPM bank is read to calculate the boot_aggregate digest
+ * needs to be conveyed to a verifier.  For this reason, use the same
+ * hash algorithm for reading the TPM PCRs as for calculating the boot
+ * aggregate digest as stored in the measurement list.
+ */
+static int tpm_mr_calc_boot_aggregate(struct ima_mr *mr, int bank,
+                                     char *digest, struct crypto_shash *tfm)
+{
+       int rc;
+       struct tpm_chip *tpm_chip;
+       mr_digest_t d = { .digest = {0} };
+       u32 pcr_idx;
+       SHASH_DESC_ON_STACK(shash, tfm);
+
+       if (!mr || !mr->data || !tfm || (bank >= mr->nr_banks))
+               return -EINVAL;
+
+       tpm_chip = mr->data;
+       d.alg_id = tpm_chip->allocated_banks[bank].alg_id;
+
+       shash->tfm = tfm;
+
+       pr_devel("calculating the boot-aggregate based on TPM bank: %04x\n",
+                d.alg_id);
+
+       rc = crypto_shash_init(shash);
+       if (rc)
+               return rc;
+
+       /* cumulative digest over TPM registers 0-7 */
+       for (pcr_idx = TPM_PCR0; pcr_idx < TPM_PCR8; pcr_idx++) {
+               rc = tpm_pcr_read(tpm_chip, pcr_idx, &d);
+               rc = tpm_ret_to_err(rc);
+               if (rc) {
+                       pr_err("Error Communicating to TPM chip\n");
+                       return rc;
+               }
+
+               /* now accumulate with current aggregate */
+               rc = crypto_shash_update(shash, d.digest,
+                                        crypto_shash_digestsize(tfm));
+               if (rc)
+                       return rc;
+       }
+
+       /*
+        * Extend cumulative digest over TPM registers 8-9, which contain
+        * measurement for the kernel command line (reg. 8) and image (reg. 9)
+        * in a typical PCR allocation. Registers 8-9 are only included in
+        * non-SHA1 boot_aggregate digests to avoid ambiguity.
+        */
+       if (d.alg_id != TPM_ALG_SHA1) {
+               for (pcr_idx = TPM_PCR8; pcr_idx < TPM_PCR10; pcr_idx++) {
+                       rc = tpm_pcr_read(tpm_chip, pcr_idx, &d);
+                       rc = tpm_ret_to_err(rc);
+                       if (rc) {
+                               pr_err("Error Communicating to TPM chip\n");
+                               return rc;
+                       }
+
+                       rc = crypto_shash_update(shash, d.digest,
+                                               crypto_shash_digestsize(tfm));
+               }
+       }
+
+       if (!rc)
+               rc = crypto_shash_final(shash, digest);
+       return rc;
+}
+
+static int tpm_mr_extend(struct ima_mr *mr, u32 pcr_idx,
+                        mr_digest_t *digests)
+{
+       int rc;
+       struct tpm_chip *tpm_chip;
+
+       if (!mr || !mr->data)
+               return -EINVAL;
+
+       tpm_chip = mr->data;
+
+       rc = tpm_pcr_extend(tpm_chip, pcr_idx, digests);
+       rc = tpm_ret_to_err(rc);
+       if (rc)
+               pr_err("Error Communicating to TPM chip, result: %d\n", rc);
+
+       return rc;
+}
+
+struct ima_mr_operations ima_mr_tpm_operations = {
+       .name                    = "TPM",
+       .supported               = IS_BUILTIN(CONFIG_TCG_TPM),
+       .mr_init                 = tpm_mr_init,
+       .mr_get_bank_info        = tpm_mr_get_bank_info,
+       .mr_calc_boot_aggregate  = tpm_mr_calc_boot_aggregate,
+       .mr_extend               = tpm_mr_extend,
+};
diff --git a/security/integrity/ima/ima_queue.c 
b/security/integrity/ima/ima_queue.c
index 0f1b7e4113c4..637db7c338e2 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -217,16 +217,15 @@ unsigned long ima_get_binary_runtime_size(enum 
binary_lists binary_list)
                return val + sizeof(struct ima_kexec_hdr);
 }
 
-static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr)
+static int ima_mr_extend(struct tpm_digest *digests_arg, int pcr)
 {
        int result = 0;
 
-       if (!ima_tpm_chip)
+       if (!ima_mr)
                return result;
 
-       result = tpm_pcr_extend(ima_tpm_chip, pcr, digests_arg);
-       if (result != 0)
-               pr_err("Error Communicating to TPM chip, result: %d\n", result);
+       result = ima_mr->ops->mr_extend(ima_mr, pcr, digests_arg);
+
        return result;
 }
 
@@ -247,7 +246,7 @@ int ima_add_template_entry(struct ima_template_entry 
*entry, int violation,
        const char *audit_cause = "hash_added";
        char tpm_audit_cause[AUDIT_CAUSE_LEN_MAX];
        int audit_info = 1;
-       int result = 0, tpmresult = 0;
+       int result = 0, mresult = 0;
 
        mutex_lock(&ima_extend_list_mutex);
 
@@ -281,10 +280,10 @@ int ima_add_template_entry(struct ima_template_entry 
*entry, int violation,
        if (violation)          /* invalidate pcr */
                digests_arg = digests;
 
-       tpmresult = ima_pcr_extend(digests_arg, entry->pcr);
-       if (tpmresult != 0) {
+       mresult = ima_mr_extend(digests_arg, entry->pcr);
+       if (mresult != 0) {
                snprintf(tpm_audit_cause, AUDIT_CAUSE_LEN_MAX, "TPM_error(%d)",
-                        tpmresult);
+                        mresult);
                audit_cause = tpm_audit_cause;
                audit_info = 0;
        }
@@ -548,25 +547,27 @@ void __init ima_init_reboot_notifier(void)
 
 int __init ima_init_digests(void)
 {
+       int rc, i;
+       mr_bank_info_t bank_info;
        u16 digest_size;
-       u16 crypto_id;
-       int i;
 
-       if (!ima_tpm_chip)
+       if (!ima_mr)
                return 0;
 
-       digests = kzalloc_objs(*digests, ima_tpm_chip->nr_allocated_banks,
-                              GFP_NOFS);
+       digests = kzalloc_objs(*digests, NR_BANKS(ima_mr), GFP_NOFS);
        if (!digests)
                return -ENOMEM;
 
-       for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++) {
-               digests[i].alg_id = ima_tpm_chip->allocated_banks[i].alg_id;
-               digest_size = ima_tpm_chip->allocated_banks[i].digest_size;
-               crypto_id = ima_tpm_chip->allocated_banks[i].crypto_id;
+       for (i = 0; i < NR_BANKS(ima_mr); i++) {
+               rc = ima_mr->ops->mr_get_bank_info(ima_mr, i, &bank_info);
+               if (rc)
+                       return rc;
+
+               digests[i].alg_id = bank_info.alg_id;
+               digest_size = bank_info.digest_size;
 
                /* for unmapped TPM algorithms digest is still a padded SHA1 */
-               if (crypto_id == HASH_ALGO__LAST)
+               if (bank_info.crypto_id == HASH_ALGO__LAST)
                        digest_size = SHA1_DIGEST_SIZE;
 
                memset(digests[i].digest, 0xff, digest_size);
diff --git a/security/integrity/ima/ima_template.c 
b/security/integrity/ima/ima_template.c
index 7034573fb41e..3396e9df22a5 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -358,7 +358,7 @@ static int ima_restore_template_data(struct 
ima_template_desc *template_desc,
                                     int template_data_size,
                                     struct ima_template_entry **entry)
 {
-       struct tpm_digest *digests;
+       mr_digest_t *digests;
        int ret = 0;
        int i;
 
@@ -368,7 +368,7 @@ static int ima_restore_template_data(struct 
ima_template_desc *template_desc,
                return -ENOMEM;
 
        digests = kzalloc_objs(*digests,
-                              NR_BANKS(ima_tpm_chip) + ima_extra_slots,
+                              NR_BANKS(ima_mr) + ima_extra_slots,
                               GFP_NOFS);
        if (!digests) {
                kfree(*entry);
diff --git a/security/integrity/ima/ima_template_lib.c 
b/security/integrity/ima/ima_template_lib.c
index 8a89236f926c..12386241b126 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -365,7 +365,7 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
 
        if ((const char *)event_data->filename == boot_aggregate_name ||
            (const char *)event_data->filename == boot_aggregate_late_name) {
-               if (ima_tpm_chip) {
+               if (ima_mr) {
                        hash.hdr.algo = HASH_ALGO_SHA1;
                        result = ima_calc_boot_aggregate(hash_hdr);
 

-- 
2.43.0


Reply via email to