// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2024-2025 Intel Corporation. All rights reserved. */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#define CREATE_TRACE_POINTS
#include <trace/events/tsm_mr.h>
/*
* struct tm_context - contains everything necessary to implement sysfs
* attributes for MRs .
* @ rwsem : protects the MR cache from concurrent access .
* @ agrp : contains all MR attributes created by tsm_mr_create_attribute_group ( ) .
* @ tm : input to tsm_mr_create_attribute_group ( ) containing MR definitions / ops .
* @ in_sync : % true if MR cache is up - to - date .
* @ mrs : array of & struct bin_attribute , one for each MR .
*
* This internal structure contains everything needed to implement
* tm_digest_read ( ) and tm_digest_write ( ) .
*
* Given tm - > refresh ( ) is potentially expensive , tm_digest_read ( ) caches MR
* values and calls tm - > refresh ( ) only when necessary . Only live MRs ( i . e . , with
* % TSM_MR_F_LIVE set ) can trigger tm - > refresh ( ) , while others are assumed to
* retain their values from the last tm - > write ( ) . @ in_sync tracks if there have
* been tm - > write ( ) calls since the last tm - > refresh ( ) . That is , tm - > refresh ( )
* will be called only when a live MR is being read and the cache is stale
* ( @ in_sync is % false ) .
*
* tm_digest_write ( ) sets @ in_sync to % false and calls tm - > write ( ) , whose
* semantics is arch and MR specific . Most ( if not all ) writable MRs support the
* extension semantics ( i . e . , tm - > write ( ) extends the input buffer into the MR ) .
*/
struct tm_context {
struct rw_semaphore rwsem;
struct attribute_group agrp;
const struct tsm_measurements *tm;
bool in_sync;
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)
{
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
* allow reading all MRs in oneshot .
*
* ctx - > refresh ( ) is necessary only for LIVE MRs , while others retain
* their values from their respective last ctx - > write ( ) .
*/
if ((mr->mr_flags & TSM_MR_F_LIVE) && !ctx->in_sync) {
up_read(&ctx->rwsem);
rc = down_write_killable(&ctx->rwsem);
if (rc)
return rc;
if (!ctx->in_sync) {
rc = ctx->tm->refresh(ctx->tm);
ctx->in_sync = !rc;
trace_tsm_mr_refresh(mr, rc);
}
downgrade_write(&ctx->rwsem);
}
memcpy(buffer, mr->mr_value + off, count);
trace_tsm_mr_read(mr);
up_read(&ctx->rwsem);
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)
{
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];
rc = down_write_killable(&ctx->rwsem);
if (rc)
return rc;
rc = ctx->tm->write(ctx->tm, mr, buffer);
/* mark MR cache stale */
if (!rc) {
ctx->in_sync = false ;
trace_tsm_mr_write(mr, buffer);
}
up_write(&ctx->rwsem);
return rc ?: count;
}
/**
* tsm_mr_create_attribute_group ( ) - creates an attribute group for measurement
* registers ( MRs )
* @ tm : pointer to & struct tsm_measurements containing the MR definitions .
*
* This function creates attributes corresponding to the MR definitions
* provided by @ tm - > mrs .
*
* The created attributes will reference @ tm and its members . The caller must
* not free @ tm until after tsm_mr_free_attribute_group ( ) is called .
*
* Context : Process context . May sleep due to memory allocation .
*
* Return :
* * On success , the pointer to a an attribute group is returned ; otherwise
* * % - EINVAL - Invalid MR definitions .
* * % - ENOMEM - Out of memory .
*/
const struct attribute_group *
tsm_mr_create_attribute_group(const struct tsm_measurements *tm)
{
size_t nlen;
if (!tm || !tm->mrs)
return ERR_PTR(-EINVAL);
/* aggregated length of all MR names */
nlen = 0 ;
for (size_t i = 0 ; i < tm->nr_mrs; ++i) {
if ((tm->mrs[i].mr_flags & TSM_MR_F_LIVE) && !tm->refresh)
return ERR_PTR(-EINVAL);
if ((tm->mrs[i].mr_flags & TSM_MR_F_WRITABLE) && !tm->write)
return ERR_PTR(-EINVAL);
if (!tm->mrs[i].mr_name)
return ERR_PTR(-EINVAL);
if (tm->mrs[i].mr_flags & TSM_MR_F_NOHASH)
continue ;
if (tm->mrs[i].mr_hash >= HASH_ALGO__LAST)
return ERR_PTR(-EINVAL);
/* MR sysfs attribute names have the form of MRNAME:HASH */
nlen += strlen(tm->mrs[i].mr_name) + 1 +
strlen(hash_algo_name[tm->mrs[i].mr_hash]) + 1 ;
}
/*
* @ attrs and the MR name strings are combined into a single allocation
* so that we don ' t have to free MR names one - by - one in
* tsm_mr_free_attribute_group ( )
*/
const struct bin_attribute **attrs __free(kfree) =
kzalloc(sizeof (*attrs) * (tm->nr_mrs + 1 ) + nlen, GFP_KERNEL);
struct tm_context *ctx __free(kfree) =
kzalloc(struct_size(ctx, mrs, tm->nr_mrs), GFP_KERNEL);
char *name, *end;
if (!ctx || !attrs)
return ERR_PTR(-ENOMEM);
/* @attrs is followed immediately by MR name strings */
name = (char *)&attrs[tm->nr_mrs + 1 ];
end = name + nlen;
for (size_t i = 0 ; i < tm->nr_mrs; ++i) {
struct bin_attribute *bap = &ctx->mrs[i];
sysfs_bin_attr_init(bap);
if (tm->mrs[i].mr_flags & TSM_MR_F_NOHASH)
bap->attr.name = tm->mrs[i].mr_name;
else if (name < end) {
bap->attr.name = name;
name += snprintf(name, end - name, "%s:%s" ,
tm->mrs[i].mr_name,
hash_algo_name[tm->mrs[i].mr_hash]);
++name;
} else
return ERR_PTR(-EINVAL);
/* check for duplicated MR definitions */
for (size_t j = 0 ; j < i; ++j)
if (!strcmp(bap->attr.name, attrs[j]->attr.name))
return ERR_PTR(-EINVAL);
if (tm->mrs[i].mr_flags & TSM_MR_F_READABLE) {
bap->attr.mode |= 0444 ;
bap->read = tm_digest_read;
}
if (tm->mrs[i].mr_flags & TSM_MR_F_WRITABLE) {
bap->attr.mode |= 0200 ;
bap->write = tm_digest_write;
}
bap->size = tm->mrs[i].mr_size;
bap->private = ctx;
attrs[i] = bap;
}
if (name != end)
return ERR_PTR(-EINVAL);
init_rwsem(&ctx->rwsem);
ctx->agrp.name = "measurements" ;
ctx->agrp.bin_attrs = no_free_ptr(attrs);
ctx->tm = tm;
return &no_free_ptr(ctx)->agrp;
}
EXPORT_SYMBOL_GPL(tsm_mr_create_attribute_group);
/**
* tsm_mr_free_attribute_group ( ) - frees the attribute group returned by
* tsm_mr_create_attribute_group ( )
* @ attr_grp : attribute group returned by tsm_mr_create_attribute_group ( )
*
* Context : Process context .
*/
void tsm_mr_free_attribute_group(const struct attribute_group *attr_grp)
{
if (!IS_ERR_OR_NULL(attr_grp)) {
kfree(attr_grp->bin_attrs);
kfree(container_of(attr_grp, struct tm_context, agrp));
}
}
EXPORT_SYMBOL_GPL(tsm_mr_free_attribute_group);
Messung V0.5 in Prozent C=97 H=95 G=95
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland