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