[PATCH RFC 1/3] virt: coco: introduce tsm_default_tm() and tsm_mr_read()/write()
From: Yeoreum Yun
Date: Wed Sep 30 2026 - 10:59:42 EST
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@xxxxxxx>
---
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