[PATCH RFC 2/3] security: IMA: introduce ima_mr structure

Yeoreum Yun yeoreum.yun at arm.com
Wed Sep 30 13:44:00 UTC 2026


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 <yeoreum.yun at arm.com>
---
 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 <yeoreum.yun at arm.com>
+ */
+
+#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 <yeoreum.yun at arm.com>
+ */
+
+#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 <yeoreum.yun at arm.com>
+ */
+
+#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




More information about the Linux-security-module-archive mailing list