[PATCH RFC 1/3] virt: coco: introduce tsm_default_tm() and tsm_mr_read()/write()

Yeoreum Yun yeoreum.yun at arm.com
Wed Sep 30 13:43:59 UTC 2026


This is preparatory patch to use tsm measurement registers in IMA.
Introduce tsm_default_tm() and tsm_mr_read()/write() APIs
to read and extend the tsm measurement registers in IMA.

Since IMA is supported only when it's built as built-in,
export those symbols only tsm-mr is built as built-in.

Signed-off-by: Yeoreum Yun <yeoreum.yun at arm.com>
---
 drivers/virt/coco/guest/tsm-mr.c | 159 +++++++++++++++++++++++++++++++++------
 include/linux/tsm-mr.h           |  26 +++++++
 2 files changed, 161 insertions(+), 24 deletions(-)

diff --git a/drivers/virt/coco/guest/tsm-mr.c b/drivers/virt/coco/guest/tsm-mr.c
index 657b9c5739d0..9e721348be8d 100644
--- a/drivers/virt/coco/guest/tsm-mr.c
+++ b/drivers/virt/coco/guest/tsm-mr.c
@@ -7,9 +7,15 @@
 #include <linux/slab.h>
 #include <linux/sysfs.h>
 
+
 #define CREATE_TRACE_POINTS
 #include <trace/events/tsm_mr.h>
 
+#define TM_NUM_CTX	(64 * HASH_ALGO__LAST)
+
+DEFINE_IDR(tm_ctx_idr);
+static DEFINE_MUTEX(idr_lock);
+
 /*
  * struct tm_context - contains everything necessary to implement sysfs
  * attributes for MRs.
@@ -42,21 +48,16 @@ struct tm_context {
 	struct bin_attribute mrs[];
 };
 
-static ssize_t tm_digest_read(struct file *filp, struct kobject *kobj,
-			      const struct bin_attribute *attr, char *buffer,
-			      loff_t off, size_t count)
+static ssize_t __tsm_mr_read(struct tm_context *ctx,
+			 const struct tsm_measurement_register *mr,
+			 char *buffer, loff_t off, size_t count)
 {
-	struct tm_context *ctx;
-	const struct tsm_measurement_register *mr;
 	int rc;
 
-	ctx = attr->private;
 	rc = down_read_interruptible(&ctx->rwsem);
 	if (rc)
 		return rc;
 
-	mr = &ctx->tm->mrs[attr - ctx->mrs];
-
 	/*
 	 * @ctx->in_sync indicates if the MR cache is stale. It is a global
 	 * instead of a per-MR flag for simplicity, as most (if not all) archs
@@ -88,20 +89,11 @@ static ssize_t tm_digest_read(struct file *filp, struct kobject *kobj,
 	return rc ?: count;
 }
 
-static ssize_t tm_digest_write(struct file *filp, struct kobject *kobj,
-			       const struct bin_attribute *attr, char *buffer,
-			       loff_t off, size_t count)
+static ssize_t __tsm_mr_write(struct tm_context *ctx,
+			 const struct tsm_measurement_register *mr,
+			 char *buffer, size_t count)
 {
-	struct tm_context *ctx;
-	const struct tsm_measurement_register *mr;
-	ssize_t rc;
-
-	/* partial writes are not supported */
-	if (off != 0 || count != attr->size)
-		return -EINVAL;
-
-	ctx = attr->private;
-	mr = &ctx->tm->mrs[attr - ctx->mrs];
+	int rc;
 
 	rc = down_write_killable(&ctx->rwsem);
 	if (rc)
@@ -119,6 +111,36 @@ static ssize_t tm_digest_write(struct file *filp, struct kobject *kobj,
 	return rc ?: count;
 }
 
+static ssize_t tm_digest_read(struct file *filp, struct kobject *kobj,
+			      const struct bin_attribute *attr, char *buffer,
+			      loff_t off, size_t count)
+{
+	struct tm_context *ctx;
+	const struct tsm_measurement_register *mr;
+
+	ctx = attr->private;
+	mr = &ctx->tm->mrs[attr - ctx->mrs];
+
+	return __tsm_mr_read(ctx, mr, buffer, off, count);
+}
+
+static ssize_t tm_digest_write(struct file *filp, struct kobject *kobj,
+			       const struct bin_attribute *attr, char *buffer,
+			       loff_t off, size_t count)
+{
+	struct tm_context *ctx;
+	const struct tsm_measurement_register *mr;
+
+	/* partial writes are not supported */
+	if (off != 0 || count != attr->size)
+		return -EINVAL;
+
+	ctx = attr->private;
+	mr = &ctx->tm->mrs[attr - ctx->mrs];
+
+	return __tsm_mr_write(ctx, mr, buffer, count);
+}
+
 /**
  * tsm_mr_create_attribute_group() - creates an attribute group for measurement
  * registers (MRs)
@@ -138,8 +160,7 @@ static ssize_t tm_digest_write(struct file *filp, struct kobject *kobj,
  * * %-ENOMEM - Out of memory.
  */
 const struct attribute_group *
-tsm_mr_create_attribute_group(const struct tsm_measurements *tm)
-{
+tsm_mr_create_attribute_group(const struct tsm_measurements *tm) {
 	size_t nlen;
 
 	if (!tm || !tm->mrs)
@@ -230,6 +251,15 @@ tsm_mr_create_attribute_group(const struct tsm_measurements *tm)
 	ctx->agrp.name = "measurements";
 	ctx->agrp.bin_attrs = no_free_ptr(attrs);
 	ctx->tm = tm;
+
+	guard(mutex)(&idr_lock);
+	((struct tsm_measurements *)tm)->ctx_id = idr_alloc(&tm_ctx_idr, ctx, 0,
+							    TM_NUM_CTX, GFP_KERNEL);
+	if (tm->ctx_id < 0) {
+		kfree(ctx->agrp.bin_attrs);
+		return ERR_PTR(tm->ctx_id);
+	}
+
 	return &no_free_ptr(ctx)->agrp;
 }
 EXPORT_SYMBOL_GPL(tsm_mr_create_attribute_group);
@@ -243,9 +273,90 @@ EXPORT_SYMBOL_GPL(tsm_mr_create_attribute_group);
  */
 void tsm_mr_free_attribute_group(const struct attribute_group *attr_grp)
 {
+	struct tm_context *ctx;
+
 	if (!IS_ERR_OR_NULL(attr_grp)) {
+		ctx = container_of(attr_grp, struct tm_context, agrp);
+		scoped_guard(mutex, &idr_lock)
+			idr_remove(&tm_ctx_idr, ctx->tm->ctx_id);
 		kfree(attr_grp->bin_attrs);
-		kfree(container_of(attr_grp, struct tm_context, agrp));
+		kfree(ctx);
 	}
 }
 EXPORT_SYMBOL_GPL(tsm_mr_free_attribute_group);
+
+#if defined(CONFIG_TSM_MEASUREMENTS)
+const struct tsm_measurements *tsm_default_tm(void)
+{
+	struct tm_context *ctx;
+	int next_id = 0;
+
+	guard(mutex)(&idr_lock);
+
+	ctx = idr_get_next(&tm_ctx_idr, &next_id);
+	if (!ctx)
+		return NULL;
+
+	return ctx->tm;
+}
+EXPORT_SYMBOL_GPL(tsm_default_tm);
+
+int tsm_mr_read(const struct tsm_measurements *tm, int idx,
+		u8 *digest, u32 digest_size)
+{
+	struct tm_context *ctx;
+	const struct tsm_measurement_register *mr;
+	int rc;
+
+	scoped_guard(mutex, &idr_lock)
+		ctx = idr_find(&tm_ctx_idr, tm->ctx_id);
+
+	if (IS_ERR_OR_NULL(ctx))
+		return -ENODEV;
+
+	if (!digest || (idx >= ctx->tm->nr_mrs) ||
+	    (ctx->tm->mrs[idx].mr_size > digest_size) ||
+	    !(ctx->tm->mrs[idx].mr_flags & TSM_MR_F_READABLE))
+		return -EINVAL;
+
+	mr = &ctx->tm->mrs[idx];
+
+	rc = __tsm_mr_read(ctx, mr, (char *)digest, 0, mr->mr_size);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tsm_mr_read);
+
+int tsm_mr_write(const struct tsm_measurements *tm, int idx,
+		 u8 *digest, u32 digest_size)
+{
+	struct tm_context *ctx;
+	const struct tsm_measurement_register *mr;
+	int rc;
+
+	scoped_guard(mutex, &idr_lock)
+		ctx = idr_find(&tm_ctx_idr, tm->ctx_id);
+
+	if (IS_ERR_OR_NULL(ctx))
+		return -ENODEV;
+
+	if (!digest || (idx >= ctx->tm->nr_mrs) ||
+	    !(ctx->tm->mrs[idx].mr_flags & TSM_MR_F_WRITABLE))
+		return -EINVAL;
+
+	/* partial writes are not supported */
+	if (ctx->tm->mrs[idx].mr_size != digest_size)
+		return -EINVAL;
+
+	mr = &ctx->tm->mrs[idx];
+
+	rc = __tsm_mr_write(ctx, mr, (char *)digest, mr->mr_size);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tsm_mr_write);
+#endif
diff --git a/include/linux/tsm-mr.h b/include/linux/tsm-mr.h
index 50a521f4ac97..43a0f761cd96 100644
--- a/include/linux/tsm-mr.h
+++ b/include/linux/tsm-mr.h
@@ -80,10 +80,36 @@ struct tsm_measurements {
 	int (*refresh)(const struct tsm_measurements *tm);
 	int (*write)(const struct tsm_measurements *tm,
 		     const struct tsm_measurement_register *mr, const u8 *data);
+	int ctx_id;
 };
 
 const struct attribute_group *
 tsm_mr_create_attribute_group(const struct tsm_measurements *tm);
 void tsm_mr_free_attribute_group(const struct attribute_group *attr_grp);
 
+#if defined(CONFIG_TSM_MEASUREMENTS)
+const struct tsm_measurements *tsm_default_tm(void);
+int tsm_mr_read(const struct tsm_measurements *tm, int idx,
+		u8 *digest, u32 digest_size);
+int tsm_mr_write(const struct tsm_measurements *tm, int idx,
+		 u8 *digest, u32 digest_size);
+#else
+static inline const struct tsm_measurements *tsm_default_tm(void)
+{
+	return NULL;
+}
+
+static inline int tsm_mr_read(const struct tsm_measurements *tm, int idx,
+			      u8 *digest, u32 digest_size)
+{
+	return 0;
+}
+
+static inline int tsm_mr_write(const struct tsm_measurements *tm, int idx,
+			       u8 *digest, u32 digest_size)
+{
+	return 0;
+}
+#endif
+
 #endif

-- 
2.43.0




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