Linux Security Modules development
 help / color / mirror / Atom feed
From: Yeoreum Yun <yeoreum.yun@arm.com>
To: linux-coco@lists.linux.dev, linux-kernel@vger.kernel.org,
	 linux-arm-kernel@lists.infradead.org,
	 Eric Snowberg <eric.snowberg@oracle.com>,
	linux-integrity@vger.kernel.org,
	 linux-security-module@vger.kernel.org
Cc: Dan Williams <djbw@kernel.org>, Mimi Zohar <zohar@linux.ibm.com>,
	 Roberto Sassu <roberto.sassu@huawei.com>,
	 Dmitry Kasatkin <dmitry.kasatkin@gmail.com>,
	 Paul Moore <paul@paul-moore.com>,
	James Morris <jmorris@namei.org>,
	 "Serge E. Hallyn" <serge@hallyn.com>,
	 Catalin Marinas <catalin.marinas@arm.com>,
	Jason Gunthorpe <jgg@ziepe.ca>,
	 Suzuki Poulose <suzuki.poulose@arm.com>,
	 Steven Price <steven.price@arm.com>,
	Sami Mujawar <sami.mujawar@arm.com>,
	 "Aneesh Kumar K.V" <aneesh.kumar@kernel.org>,
	Jiri Pirko <jiri@resnulli.us>,  Yeoreum Yun <yeoreum.yun@arm.com>
Subject: [PATCH RFC 3/3] security: IMA: use TSM measurement registers
Date: Wed, 30 Sep 2026 14:44:01 +0100	[thread overview]
Message-ID: <20260930-ima_tgx_integration_v2-v1-3-722c35370548@arm.com> (raw)
In-Reply-To: <20260930-ima_tgx_integration_v2-v1-0-722c35370548@arm.com>

IMA uses TPM PCRs to record measurement digests. When no TPM device is
available, TSM measurement registers can serve as an alternative for guest.

The following mappings are defined for Intel TDX [0] and proposed for
Arm CCA [1]:

  TPM PCR index | Intel TDX register | Arm CCA register
  --------------+--------------------+-----------------
  0             | MRTD               | RIM
  1, 7          | RTMR[0]            | REM[0]
  2-6           | RTMR[1]            | REM[1]
  8-15          | RTMR[2]            | REM[2]

Add support for extending IMA measurement digests into the corresponding
TSM measurement register when no TPM device is available.

Link: [0] https://uefi.org/specs/UEFI/2.11/38_Confidential_Computing.html#intel-trust-domain-extension
Link: [1] https://github.com/tianocore/edk2/issues/11383
Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
---
 security/integrity/ima/Makefile     |   3 +-
 security/integrity/ima/ima_mr.c     |   1 +
 security/integrity/ima/ima_mr.h     |   1 +
 security/integrity/ima/ima_mr_tsm.c | 290 ++++++++++++++++++++++++++++++++++++
 4 files changed, 294 insertions(+), 1 deletion(-)

diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index f2c46b405a00..f0a22e3a5320 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -7,7 +7,8 @@
 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_mr.o ima_mr_tpm.o
+	 ima_policy.o ima_template.o ima_template_lib.o ima_mr.o ima_mr_tpm.o \
+	 ima_mr_tsm.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_mr.c b/security/integrity/ima/ima_mr.c
index fe58eb968954..85a66e616f64 100644
--- a/security/integrity/ima/ima_mr.c
+++ b/security/integrity/ima/ima_mr.c
@@ -15,6 +15,7 @@ struct ima_mr *ima_mr;
 
 static struct ima_mr_operations *ima_mr_ops[] = {
 	&ima_mr_tpm_operations,
+	&ima_mr_tsm_operations,
 };
 
 void __init ima_init_mr(void)
diff --git a/security/integrity/ima/ima_mr.h b/security/integrity/ima/ima_mr.h
index 23b85522da34..bc7b06c3adcd 100644
--- a/security/integrity/ima/ima_mr.h
+++ b/security/integrity/ima/ima_mr.h
@@ -51,6 +51,7 @@ struct ima_mr_operations {
 
 extern struct ima_mr *ima_mr;
 extern struct ima_mr_operations ima_mr_tpm_operations;
+extern struct ima_mr_operations ima_mr_tsm_operations;
 
 void __init ima_init_mr(void);
 
diff --git a/security/integrity/ima/ima_mr_tsm.c b/security/integrity/ima/ima_mr_tsm.c
new file mode 100644
index 000000000000..3f88edfb8511
--- /dev/null
+++ b/security/integrity/ima/ima_mr_tsm.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Arm Ltd
+ *
+ * Author:
+ * Yeoreum Yun <yeoreum.yun@arm.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/tsm-mr.h>
+
+#include "ima.h"
+
+#define INVALID_MR_IDX		(-1)
+
+struct tsm_context {
+	const struct tsm_measurements *tm;
+	int pcr_map[TPM2_PLATFORM_PCR];
+};
+
+static struct tsm_context tsm_ctx;
+
+static int tsm_mr_idx_by_name(const struct tsm_measurements *tm,
+			      const char *mr_name)
+{
+	int i;
+	const struct tsm_measurement_register *mr;
+
+	for (i = 0; i < tm->nr_mrs; i++) {
+		mr = &tm->mrs[i];
+		if (!strcmp(mr->mr_name, mr_name))
+			return i;
+	}
+
+	return INVALID_MR_IDX;
+}
+
+static __always_inline
+int tsm_mr_idx(const struct tsm_measurements *tm,
+	       const struct tsm_measurement_register *tmr)
+{
+	return tmr - tm->mrs;
+}
+
+static __always_inline
+void __create_tsm_pcr_map(struct tsm_context *ctx,
+			  int mr0, int mr1, int mr2, int mr3)
+{
+	int i;
+
+	ctx->pcr_map[TPM_PCR0] = mr0;
+	ctx->pcr_map[TPM_PCR1] = ctx->pcr_map[TPM_PCR7] = mr1;
+
+	for (i = TPM_PCR2; i < TPM_PCR7; i++) {
+		ctx->pcr_map[i] = mr2;
+	}
+
+	for (i = TPM_PCR8; i < TPM_PCR16; i++) {
+		ctx->pcr_map[i] = mr3;
+	}
+
+	for (i = TPM_PCR16; i < TPM2_PLATFORM_PCR; i++) {
+		ctx->pcr_map[i] = INVALID_MR_IDX;
+	}
+}
+
+static int create_tsm_arm_cca_pcr_map(struct tsm_context *ctx)
+{
+	int rim_idx, rem0_idx, rem1_idx, rem2_idx;
+
+	rim_idx = tsm_mr_idx_by_name(ctx->tm, "rim");
+	rem0_idx = tsm_mr_idx_by_name(ctx->tm, "rem0");
+	rem1_idx = tsm_mr_idx_by_name(ctx->tm, "rem1");
+	rem2_idx = tsm_mr_idx_by_name(ctx->tm, "rem2");
+
+	if ((rim_idx == INVALID_MR_IDX) || (rem0_idx == INVALID_MR_IDX) ||
+	    (rem1_idx == INVALID_MR_IDX) || (rem2_idx == INVALID_MR_IDX))
+		return -ENODEV;
+
+	__create_tsm_pcr_map(ctx, rim_idx, rem0_idx, rem1_idx, rem2_idx);
+
+	return 0;
+}
+
+static int create_tsm_tgx_pcr_map(struct tsm_context *ctx)
+{
+	int mrtd_idx, rtmr0_idx, rtmr1_idx, rtmr2_idx;
+
+	mrtd_idx = tsm_mr_idx_by_name(ctx->tm, "mrtd");
+	rtmr0_idx = tsm_mr_idx_by_name(ctx->tm, "rtmr0");
+	rtmr1_idx = tsm_mr_idx_by_name(ctx->tm, "rtmr1");
+	rtmr2_idx = tsm_mr_idx_by_name(ctx->tm, "rtmr2");
+
+	if ((mrtd_idx == INVALID_MR_IDX) || (rtmr0_idx == INVALID_MR_IDX) ||
+	    (rtmr1_idx == INVALID_MR_IDX) || (rtmr2_idx == INVALID_MR_IDX))
+		return -ENODEV;
+
+	__create_tsm_pcr_map(ctx, mrtd_idx, rtmr0_idx, rtmr1_idx, rtmr2_idx);
+
+	return 0;
+}
+
+static const struct tsm_measurement_register *
+tsm_mr_get(struct tsm_context *ctx, int pcr_idx)
+{
+	int idx;
+
+	if (pcr_idx < 0 || pcr_idx >= ARRAY_SIZE(ctx->pcr_map))
+		return NULL;
+
+	idx = ctx->pcr_map[pcr_idx];
+	if (idx == INVALID_MR_IDX)
+		return NULL;
+
+	return &ctx->tm->mrs[idx];
+}
+
+static int tsm_mr_init(struct ima_mr *mr)
+{
+	int rc;
+	const struct tsm_measurements *tm;
+
+	if (!mr)
+		return -EINVAL;
+
+	tm = tsm_default_tm();
+	if (!tm) {
+		pr_info("No TSM measurement registers found!\n");
+		return -ENODEV;
+	}
+
+	tsm_ctx.tm = tm;
+
+	if (IS_BUILTIN(CONFIG_ARM_CCA_GUEST))
+		rc = create_tsm_arm_cca_pcr_map(&tsm_ctx);
+	else
+		rc = create_tsm_tgx_pcr_map(&tsm_ctx);
+
+	if (rc) {
+		tsm_ctx.tm = NULL;
+		return rc;
+	}
+
+	mr->data = &tsm_ctx;
+	mr->nr_banks = 1;
+	mr->ops = &ima_mr_tsm_operations;
+
+	return 0;
+}
+
+static int tsm_mr_get_bank_info(struct ima_mr *mr, int bank,
+				mr_bank_info_t *info)
+{
+	struct tsm_context *ctx;
+	const struct tsm_measurement_register *tsm_mr;
+
+	if (!mr || !mr->data || !info || (bank >= mr->nr_banks))
+		return -EINVAL;
+
+	ctx = mr->data;
+	tsm_mr = tsm_mr_get(ctx, TPM_PCR0);
+	if (!tsm_mr)
+		return -ENODEV;
+
+	info->crypto_id = tsm_mr->mr_hash;
+	info->digest_size = tsm_mr->mr_size;
+	info->alg_id = hash_to_alg(info->crypto_id);
+
+	if (info->alg_id == TPM_ALG_ERROR)
+		return -ENODEV;
+
+	return 0;
+}
+
+static int tsm_mr_calc_boot_aggregate(struct ima_mr *mr, int bank,
+				      char *digest, struct crypto_shash *tfm)
+{
+	int rc;
+	struct tsm_context *ctx;
+	const struct tsm_measurement_register *tsm_mr;
+	mr_digest_t d = { .digest = {0} };
+	SHASH_DESC_ON_STACK(shash, tfm);
+	int mr_idx, pcr_idx;
+
+	if (!mr || !mr->data || !tfm || (bank >= mr->nr_banks))
+		return -EINVAL;
+
+	ctx = mr->data;
+	tsm_mr = tsm_mr_get(ctx, TPM_PCR0);
+	if (!tsm_mr)
+		return -ENODEV;
+
+	d.alg_id = hash_to_alg(tsm_mr->mr_hash);
+	if (d.alg_id == TPM_ALG_ERROR)
+		return -ENODEV;
+
+	shash->tfm = tfm;
+
+	pr_devel("calculating the boot-aggregate based on TSM bank: %04x\n",
+		 d.alg_id);
+
+	rc = crypto_shash_init(shash);
+	if (rc)
+		return rc;
+
+	/*
+	 * In TSM, PCR 0 mapped into MR 0, PCR 1,7 into MR 1 and
+	 * PCR 2-6 into MR 2. Therefore, accumulate with  MR 0-2.
+	 */
+	for (pcr_idx = TPM_PCR0; pcr_idx <= TPM_PCR2; pcr_idx++) {
+		tsm_mr = tsm_mr_get(ctx, pcr_idx);
+		if (!tsm_mr)
+			return -ENODEV;
+
+		mr_idx = tsm_mr_idx(ctx->tm, tsm_mr);
+		rc = tsm_mr_read(ctx->tm, mr_idx, d.digest, tsm_mr->mr_size);
+		if (rc) {
+			pr_err("Error Communicating to TSM(%d)\n", rc);
+			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 MR 3 which corespondant to
+	 * TPM registers 8-9, which contain measurement for
+	 * the kernel command line (TPM_PCR8) and image (TPM_PCR9)
+	 * in a typical PCR allocation. MR 3 is only included in
+	 * non-SHA1 boot_aggregate digests to avoid ambiguity.
+	 */
+	if (d.alg_id != TPM_ALG_SHA1) {
+		tsm_mr = tsm_mr_get(ctx, TPM_PCR8);
+		if (!tsm_mr)
+			return -ENODEV;
+
+		mr_idx = tsm_mr_idx(ctx->tm, tsm_mr);
+		rc = tsm_mr_read(ctx->tm, mr_idx, d.digest, tsm_mr->mr_size);
+		if (rc) {
+			pr_err("Error Communicating to TSM(%d)\n", rc);
+			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 tsm_mr_extend(struct ima_mr *mr, u32 pcr_idx,
+			 mr_digest_t *digests)
+{
+	int rc, mr_idx;
+	struct tsm_context *ctx;
+	const struct tsm_measurement_register *tsm_mr;
+
+	if (!mr || !mr->data)
+		return -EINVAL;
+
+	ctx = mr->data;
+	tsm_mr = tsm_mr_get(ctx, pcr_idx);
+	if (!tsm_mr)
+		return -ENODEV;
+
+	mr_idx = tsm_mr_idx(ctx->tm, tsm_mr);
+
+	/* TSM has only one bank. */
+	rc = tsm_mr_write(ctx->tm, mr_idx, digests[0].digest, tsm_mr->mr_size);
+	if (rc)
+		pr_err("Error Communicating to TSM, result: %d\n", rc);
+
+	return rc;
+}
+
+struct ima_mr_operations ima_mr_tsm_operations = {
+	.name                    = "TSM",
+	.supported               = (IS_BUILTIN(CONFIG_ARM_CCA_GUEST) ||
+				    IS_BUILTIN(CONFIG_TDX_GUEST_DRIVER)),
+	.mr_init                 = tsm_mr_init,
+	.mr_get_bank_info        = tsm_mr_get_bank_info,
+	.mr_calc_boot_aggregate  = tsm_mr_calc_boot_aggregate,
+	.mr_extend               = tsm_mr_extend,
+};

-- 
2.43.0


  parent reply	other threads:[~2026-09-30 13:44 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-30 13:43 [PATCH RFC 0/3] security: ima: support TSM measurement registers Yeoreum Yun
2026-09-30 13:43 ` [PATCH RFC 1/3] virt: coco: introduce tsm_default_tm() and tsm_mr_read()/write() Yeoreum Yun
2026-09-30 13:55   ` sashiko-bot
2026-09-30 13:44 ` [PATCH RFC 2/3] security: IMA: introduce ima_mr structure Yeoreum Yun
2026-09-30 13:55   ` sashiko-bot
2026-09-30 13:44 ` Yeoreum Yun [this message]
2026-09-30 13:59   ` [PATCH RFC 3/3] security: IMA: use TSM measurement registers sashiko-bot
2026-10-01 11:27 ` [PATCH RFC 0/3] security: ima: support " Roberto Sassu
2026-10-01 11:45   ` Roberto Sassu
2026-10-01 14:24     ` Yeoreum Yun
2026-10-08 11:10       ` GONG Ruiqi
2026-10-01 15:18     ` Jason Gunthorpe
2026-10-01 16:39       ` Yeoreum Yun

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260930-ima_tgx_integration_v2-v1-3-722c35370548@arm.com \
    --to=yeoreum.yun@arm.com \
    --cc=aneesh.kumar@kernel.org \
    --cc=catalin.marinas@arm.com \
    --cc=djbw@kernel.org \
    --cc=dmitry.kasatkin@gmail.com \
    --cc=eric.snowberg@oracle.com \
    --cc=jgg@ziepe.ca \
    --cc=jiri@resnulli.us \
    --cc=jmorris@namei.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-coco@lists.linux.dev \
    --cc=linux-integrity@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=paul@paul-moore.com \
    --cc=roberto.sassu@huawei.com \
    --cc=sami.mujawar@arm.com \
    --cc=serge@hallyn.com \
    --cc=steven.price@arm.com \
    --cc=suzuki.poulose@arm.com \
    --cc=zohar@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox