xref: /linux/drivers/virt/coco/guest/tsm-mr.c (revision ae5ec8adb8ec9c2aa916f853737c101faa87e5ba)
115ff5d0eSDan Williams // SPDX-License-Identifier: GPL-2.0-only
215ff5d0eSDan Williams /* Copyright(c) 2024-2025 Intel Corporation. All rights reserved. */
315ff5d0eSDan Williams 
415ff5d0eSDan Williams #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
515ff5d0eSDan Williams 
615ff5d0eSDan Williams #include <linux/module.h>
715ff5d0eSDan Williams #include <linux/slab.h>
815ff5d0eSDan Williams #include <linux/sysfs.h>
915ff5d0eSDan Williams 
1015ff5d0eSDan Williams #define CREATE_TRACE_POINTS
1115ff5d0eSDan Williams #include <trace/events/tsm_mr.h>
1215ff5d0eSDan Williams 
1315ff5d0eSDan Williams /*
1415ff5d0eSDan Williams  * struct tm_context - contains everything necessary to implement sysfs
1515ff5d0eSDan Williams  * attributes for MRs.
1615ff5d0eSDan Williams  * @rwsem: protects the MR cache from concurrent access.
1715ff5d0eSDan Williams  * @agrp: contains all MR attributes created by tsm_mr_create_attribute_group().
1815ff5d0eSDan Williams  * @tm: input to tsm_mr_create_attribute_group() containing MR definitions/ops.
1915ff5d0eSDan Williams  * @in_sync: %true if MR cache is up-to-date.
2015ff5d0eSDan Williams  * @mrs: array of &struct bin_attribute, one for each MR.
2115ff5d0eSDan Williams  *
2215ff5d0eSDan Williams  * This internal structure contains everything needed to implement
2315ff5d0eSDan Williams  * tm_digest_read() and tm_digest_write().
2415ff5d0eSDan Williams  *
2515ff5d0eSDan Williams  * Given tm->refresh() is potentially expensive, tm_digest_read() caches MR
2615ff5d0eSDan Williams  * values and calls tm->refresh() only when necessary. Only live MRs (i.e., with
2715ff5d0eSDan Williams  * %TSM_MR_F_LIVE set) can trigger tm->refresh(), while others are assumed to
2815ff5d0eSDan Williams  * retain their values from the last tm->write(). @in_sync tracks if there have
2915ff5d0eSDan Williams  * been tm->write() calls since the last tm->refresh(). That is, tm->refresh()
3015ff5d0eSDan Williams  * will be called only when a live MR is being read and the cache is stale
3115ff5d0eSDan Williams  * (@in_sync is %false).
3215ff5d0eSDan Williams  *
3315ff5d0eSDan Williams  * tm_digest_write() sets @in_sync to %false and calls tm->write(), whose
3415ff5d0eSDan Williams  * semantics is arch and MR specific. Most (if not all) writable MRs support the
3515ff5d0eSDan Williams  * extension semantics (i.e., tm->write() extends the input buffer into the MR).
3615ff5d0eSDan Williams  */
3715ff5d0eSDan Williams struct tm_context {
3815ff5d0eSDan Williams 	struct rw_semaphore rwsem;
3915ff5d0eSDan Williams 	struct attribute_group agrp;
4015ff5d0eSDan Williams 	const struct tsm_measurements *tm;
4115ff5d0eSDan Williams 	bool in_sync;
4215ff5d0eSDan Williams 	struct bin_attribute mrs[];
4315ff5d0eSDan Williams };
4415ff5d0eSDan Williams 
tm_digest_read(struct file * filp,struct kobject * kobj,const struct bin_attribute * attr,char * buffer,loff_t off,size_t count)4515ff5d0eSDan Williams static ssize_t tm_digest_read(struct file *filp, struct kobject *kobj,
4615ff5d0eSDan Williams 			      const struct bin_attribute *attr, char *buffer,
4715ff5d0eSDan Williams 			      loff_t off, size_t count)
4815ff5d0eSDan Williams {
4915ff5d0eSDan Williams 	struct tm_context *ctx;
5015ff5d0eSDan Williams 	const struct tsm_measurement_register *mr;
5115ff5d0eSDan Williams 	int rc;
5215ff5d0eSDan Williams 
5315ff5d0eSDan Williams 	ctx = attr->private;
5415ff5d0eSDan Williams 	rc = down_read_interruptible(&ctx->rwsem);
5515ff5d0eSDan Williams 	if (rc)
5615ff5d0eSDan Williams 		return rc;
5715ff5d0eSDan Williams 
5815ff5d0eSDan Williams 	mr = &ctx->tm->mrs[attr - ctx->mrs];
5915ff5d0eSDan Williams 
6015ff5d0eSDan Williams 	/*
6115ff5d0eSDan Williams 	 * @ctx->in_sync indicates if the MR cache is stale. It is a global
6215ff5d0eSDan Williams 	 * instead of a per-MR flag for simplicity, as most (if not all) archs
6315ff5d0eSDan Williams 	 * allow reading all MRs in oneshot.
6415ff5d0eSDan Williams 	 *
6515ff5d0eSDan Williams 	 * ctx->refresh() is necessary only for LIVE MRs, while others retain
6615ff5d0eSDan Williams 	 * their values from their respective last ctx->write().
6715ff5d0eSDan Williams 	 */
6815ff5d0eSDan Williams 	if ((mr->mr_flags & TSM_MR_F_LIVE) && !ctx->in_sync) {
6915ff5d0eSDan Williams 		up_read(&ctx->rwsem);
7015ff5d0eSDan Williams 
7115ff5d0eSDan Williams 		rc = down_write_killable(&ctx->rwsem);
7215ff5d0eSDan Williams 		if (rc)
7315ff5d0eSDan Williams 			return rc;
7415ff5d0eSDan Williams 
7515ff5d0eSDan Williams 		if (!ctx->in_sync) {
7615ff5d0eSDan Williams 			rc = ctx->tm->refresh(ctx->tm);
7715ff5d0eSDan Williams 			ctx->in_sync = !rc;
7815ff5d0eSDan Williams 			trace_tsm_mr_refresh(mr, rc);
7915ff5d0eSDan Williams 		}
8015ff5d0eSDan Williams 
8115ff5d0eSDan Williams 		downgrade_write(&ctx->rwsem);
8215ff5d0eSDan Williams 	}
8315ff5d0eSDan Williams 
8415ff5d0eSDan Williams 	memcpy(buffer, mr->mr_value + off, count);
8515ff5d0eSDan Williams 	trace_tsm_mr_read(mr);
8615ff5d0eSDan Williams 
8715ff5d0eSDan Williams 	up_read(&ctx->rwsem);
8815ff5d0eSDan Williams 	return rc ?: count;
8915ff5d0eSDan Williams }
9015ff5d0eSDan Williams 
tm_digest_write(struct file * filp,struct kobject * kobj,const struct bin_attribute * attr,char * buffer,loff_t off,size_t count)9115ff5d0eSDan Williams static ssize_t tm_digest_write(struct file *filp, struct kobject *kobj,
9215ff5d0eSDan Williams 			       const struct bin_attribute *attr, char *buffer,
9315ff5d0eSDan Williams 			       loff_t off, size_t count)
9415ff5d0eSDan Williams {
9515ff5d0eSDan Williams 	struct tm_context *ctx;
9615ff5d0eSDan Williams 	const struct tsm_measurement_register *mr;
9715ff5d0eSDan Williams 	ssize_t rc;
9815ff5d0eSDan Williams 
9915ff5d0eSDan Williams 	/* partial writes are not supported */
10015ff5d0eSDan Williams 	if (off != 0 || count != attr->size)
10115ff5d0eSDan Williams 		return -EINVAL;
10215ff5d0eSDan Williams 
10315ff5d0eSDan Williams 	ctx = attr->private;
10415ff5d0eSDan Williams 	mr = &ctx->tm->mrs[attr - ctx->mrs];
10515ff5d0eSDan Williams 
10615ff5d0eSDan Williams 	rc = down_write_killable(&ctx->rwsem);
10715ff5d0eSDan Williams 	if (rc)
10815ff5d0eSDan Williams 		return rc;
10915ff5d0eSDan Williams 
11015ff5d0eSDan Williams 	rc = ctx->tm->write(ctx->tm, mr, buffer);
11115ff5d0eSDan Williams 
11215ff5d0eSDan Williams 	/* mark MR cache stale */
11315ff5d0eSDan Williams 	if (!rc) {
11415ff5d0eSDan Williams 		ctx->in_sync = false;
11515ff5d0eSDan Williams 		trace_tsm_mr_write(mr, buffer);
11615ff5d0eSDan Williams 	}
11715ff5d0eSDan Williams 
11815ff5d0eSDan Williams 	up_write(&ctx->rwsem);
11915ff5d0eSDan Williams 	return rc ?: count;
12015ff5d0eSDan Williams }
12115ff5d0eSDan Williams 
12215ff5d0eSDan Williams /**
12315ff5d0eSDan Williams  * tsm_mr_create_attribute_group() - creates an attribute group for measurement
12415ff5d0eSDan Williams  * registers (MRs)
12515ff5d0eSDan Williams  * @tm: pointer to &struct tsm_measurements containing the MR definitions.
12615ff5d0eSDan Williams  *
12715ff5d0eSDan Williams  * This function creates attributes corresponding to the MR definitions
12815ff5d0eSDan Williams  * provided by @tm->mrs.
12915ff5d0eSDan Williams  *
13015ff5d0eSDan Williams  * The created attributes will reference @tm and its members. The caller must
13115ff5d0eSDan Williams  * not free @tm until after tsm_mr_free_attribute_group() is called.
13215ff5d0eSDan Williams  *
13315ff5d0eSDan Williams  * Context: Process context. May sleep due to memory allocation.
13415ff5d0eSDan Williams  *
13515ff5d0eSDan Williams  * Return:
13615ff5d0eSDan Williams  * * On success, the pointer to a an attribute group is returned; otherwise
13715ff5d0eSDan Williams  * * %-EINVAL - Invalid MR definitions.
13815ff5d0eSDan Williams  * * %-ENOMEM - Out of memory.
13915ff5d0eSDan Williams  */
14015ff5d0eSDan Williams const struct attribute_group *
tsm_mr_create_attribute_group(const struct tsm_measurements * tm)14115ff5d0eSDan Williams tsm_mr_create_attribute_group(const struct tsm_measurements *tm)
14215ff5d0eSDan Williams {
14315ff5d0eSDan Williams 	size_t nlen;
14415ff5d0eSDan Williams 
14515ff5d0eSDan Williams 	if (!tm || !tm->mrs)
14615ff5d0eSDan Williams 		return ERR_PTR(-EINVAL);
14715ff5d0eSDan Williams 
14815ff5d0eSDan Williams 	/* aggregated length of all MR names */
14915ff5d0eSDan Williams 	nlen = 0;
15015ff5d0eSDan Williams 	for (size_t i = 0; i < tm->nr_mrs; ++i) {
15115ff5d0eSDan Williams 		if ((tm->mrs[i].mr_flags & TSM_MR_F_LIVE) && !tm->refresh)
15215ff5d0eSDan Williams 			return ERR_PTR(-EINVAL);
15315ff5d0eSDan Williams 
15415ff5d0eSDan Williams 		if ((tm->mrs[i].mr_flags & TSM_MR_F_WRITABLE) && !tm->write)
15515ff5d0eSDan Williams 			return ERR_PTR(-EINVAL);
15615ff5d0eSDan Williams 
15715ff5d0eSDan Williams 		if (!tm->mrs[i].mr_name)
15815ff5d0eSDan Williams 			return ERR_PTR(-EINVAL);
15915ff5d0eSDan Williams 
16015ff5d0eSDan Williams 		if (tm->mrs[i].mr_flags & TSM_MR_F_NOHASH)
16115ff5d0eSDan Williams 			continue;
16215ff5d0eSDan Williams 
16315ff5d0eSDan Williams 		if (tm->mrs[i].mr_hash >= HASH_ALGO__LAST)
16415ff5d0eSDan Williams 			return ERR_PTR(-EINVAL);
16515ff5d0eSDan Williams 
16615ff5d0eSDan Williams 		/* MR sysfs attribute names have the form of MRNAME:HASH */
16715ff5d0eSDan Williams 		nlen += strlen(tm->mrs[i].mr_name) + 1 +
16815ff5d0eSDan Williams 			strlen(hash_algo_name[tm->mrs[i].mr_hash]) + 1;
16915ff5d0eSDan Williams 	}
17015ff5d0eSDan Williams 
17115ff5d0eSDan Williams 	/*
17215ff5d0eSDan Williams 	 * @attrs and the MR name strings are combined into a single allocation
17315ff5d0eSDan Williams 	 * so that we don't have to free MR names one-by-one in
17415ff5d0eSDan Williams 	 * tsm_mr_free_attribute_group()
17515ff5d0eSDan Williams 	 */
176*9d948b88SDan Williams 	const struct bin_attribute **attrs __free(kfree) =
17715ff5d0eSDan Williams 		kzalloc(sizeof(*attrs) * (tm->nr_mrs + 1) + nlen, GFP_KERNEL);
17815ff5d0eSDan Williams 	struct tm_context *ctx __free(kfree) =
17915ff5d0eSDan Williams 		kzalloc(struct_size(ctx, mrs, tm->nr_mrs), GFP_KERNEL);
18015ff5d0eSDan Williams 	char *name, *end;
18115ff5d0eSDan Williams 
18215ff5d0eSDan Williams 	if (!ctx || !attrs)
18315ff5d0eSDan Williams 		return ERR_PTR(-ENOMEM);
18415ff5d0eSDan Williams 
18515ff5d0eSDan Williams 	/* @attrs is followed immediately by MR name strings */
18615ff5d0eSDan Williams 	name = (char *)&attrs[tm->nr_mrs + 1];
18715ff5d0eSDan Williams 	end = name + nlen;
18815ff5d0eSDan Williams 
18915ff5d0eSDan Williams 	for (size_t i = 0; i < tm->nr_mrs; ++i) {
190*9d948b88SDan Williams 		struct bin_attribute *bap = &ctx->mrs[i];
19115ff5d0eSDan Williams 
192*9d948b88SDan Williams 		sysfs_bin_attr_init(bap);
19315ff5d0eSDan Williams 
19415ff5d0eSDan Williams 		if (tm->mrs[i].mr_flags & TSM_MR_F_NOHASH)
195*9d948b88SDan Williams 			bap->attr.name = tm->mrs[i].mr_name;
19615ff5d0eSDan Williams 		else if (name < end) {
197*9d948b88SDan Williams 			bap->attr.name = name;
19815ff5d0eSDan Williams 			name += snprintf(name, end - name, "%s:%s",
19915ff5d0eSDan Williams 					 tm->mrs[i].mr_name,
20015ff5d0eSDan Williams 					 hash_algo_name[tm->mrs[i].mr_hash]);
20115ff5d0eSDan Williams 			++name;
20215ff5d0eSDan Williams 		} else
20315ff5d0eSDan Williams 			return ERR_PTR(-EINVAL);
20415ff5d0eSDan Williams 
20515ff5d0eSDan Williams 		/* check for duplicated MR definitions */
20615ff5d0eSDan Williams 		for (size_t j = 0; j < i; ++j)
207*9d948b88SDan Williams 			if (!strcmp(bap->attr.name, attrs[j]->attr.name))
20815ff5d0eSDan Williams 				return ERR_PTR(-EINVAL);
20915ff5d0eSDan Williams 
21015ff5d0eSDan Williams 		if (tm->mrs[i].mr_flags & TSM_MR_F_READABLE) {
211*9d948b88SDan Williams 			bap->attr.mode |= 0444;
212*9d948b88SDan Williams 			bap->read_new = tm_digest_read;
21315ff5d0eSDan Williams 		}
21415ff5d0eSDan Williams 
21515ff5d0eSDan Williams 		if (tm->mrs[i].mr_flags & TSM_MR_F_WRITABLE) {
216*9d948b88SDan Williams 			bap->attr.mode |= 0200;
217*9d948b88SDan Williams 			bap->write_new = tm_digest_write;
21815ff5d0eSDan Williams 		}
21915ff5d0eSDan Williams 
220*9d948b88SDan Williams 		bap->size = tm->mrs[i].mr_size;
221*9d948b88SDan Williams 		bap->private = ctx;
222*9d948b88SDan Williams 
223*9d948b88SDan Williams 		attrs[i] = bap;
22415ff5d0eSDan Williams 	}
22515ff5d0eSDan Williams 
22615ff5d0eSDan Williams 	if (name != end)
22715ff5d0eSDan Williams 		return ERR_PTR(-EINVAL);
22815ff5d0eSDan Williams 
22915ff5d0eSDan Williams 	init_rwsem(&ctx->rwsem);
23015ff5d0eSDan Williams 	ctx->agrp.name = "measurements";
23115ff5d0eSDan Williams 	ctx->agrp.bin_attrs_new = no_free_ptr(attrs);
23215ff5d0eSDan Williams 	ctx->tm = tm;
23315ff5d0eSDan Williams 	return &no_free_ptr(ctx)->agrp;
23415ff5d0eSDan Williams }
23515ff5d0eSDan Williams EXPORT_SYMBOL_GPL(tsm_mr_create_attribute_group);
23615ff5d0eSDan Williams 
23715ff5d0eSDan Williams /**
23815ff5d0eSDan Williams  * tsm_mr_free_attribute_group() - frees the attribute group returned by
23915ff5d0eSDan Williams  * tsm_mr_create_attribute_group()
24015ff5d0eSDan Williams  * @attr_grp: attribute group returned by tsm_mr_create_attribute_group()
24115ff5d0eSDan Williams  *
24215ff5d0eSDan Williams  * Context: Process context.
24315ff5d0eSDan Williams  */
tsm_mr_free_attribute_group(const struct attribute_group * attr_grp)24415ff5d0eSDan Williams void tsm_mr_free_attribute_group(const struct attribute_group *attr_grp)
24515ff5d0eSDan Williams {
24615ff5d0eSDan Williams 	if (!IS_ERR_OR_NULL(attr_grp)) {
247*9d948b88SDan Williams 		kfree(attr_grp->bin_attrs_new);
24815ff5d0eSDan Williams 		kfree(container_of(attr_grp, struct tm_context, agrp));
24915ff5d0eSDan Williams 	}
25015ff5d0eSDan Williams }
25115ff5d0eSDan Williams EXPORT_SYMBOL_GPL(tsm_mr_free_attribute_group);
252