Skip to content

Commit b9e22b3

Browse files
binxingdjbw
authored andcommitted
tsm-mr: Add TVM Measurement Register support
Introduce new TSM Measurement helper library (tsm-mr) for TVM guest drivers to expose MRs (Measurement Registers) as sysfs attributes, with Crypto Agility support. Add the following new APIs (see include/linux/tsm-mr.h for details): - tsm_mr_create_attribute_group(): Take on input a `struct tsm_measurements` instance, which includes one `struct tsm_measurement_register` per MR with properties like `TSM_MR_F_READABLE` and `TSM_MR_F_WRITABLE`, to determine the supported operations and create the sysfs attributes accordingly. On success, return a `struct attribute_group` instance that will typically be included by the guest driver into `miscdevice.groups` before calling misc_register(). - tsm_mr_free_attribute_group(): Free the memory allocated to the attrubute group returned by tsm_mr_create_attribute_group(). tsm_mr_create_attribute_group() creates one attribute for each MR, with names following this pattern: MRNAME[:HASH] - MRNAME - Placeholder for the MR name, as specified by `tsm_measurement_register.mr_name`. - :HASH - Optional suffix indicating the hash algorithm associated with this MR, as specified by `tsm_measurement_register.mr_hash`. Support Crypto Agility by allowing multiple definitions of the same MR (i.e., with the same `mr_name`) with distinct HASH algorithms. NOTE: Crypto Agility, introduced in TPM 2.0, allows new hash algorithms to be introduced without breaking compatibility with applications using older algorithms. CC architectures may face the same challenge in the future, needing new hashes for security while retaining compatibility with older hashes, hence the need for Crypto Agility. Signed-off-by: Cedric Xing <[email protected]> Reviewed-by: Dan Williams <[email protected]> Acked-by: Dionna Amalie Glaze <[email protected]> [djbw: fixup bin_attr const conflict] Link: https://patch.msgid.link/[email protected] Signed-off-by: Dan Williams <[email protected]>
1 parent 92a09c4 commit b9e22b3

File tree

9 files changed

+454
-2
lines changed

9 files changed

+454
-2
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.. SPDX-License-Identifier: GPL-2.0
2+
3+
======================
4+
Confidential Computing
5+
======================
6+
7+
.. toctree::
8+
:maxdepth: 1
9+
10+
measurement-registers
11+
12+
.. only:: subproject and html
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.. SPDX-License-Identifier: GPL-2.0
2+
.. include:: <isonum.txt>
3+
4+
=====================
5+
Measurement Registers
6+
=====================
7+
8+
.. kernel-doc:: include/linux/tsm-mr.h
9+
:internal:
10+
11+
.. kernel-doc:: drivers/virt/coco/tsm-mr.c
12+
:export:

Documentation/driver-api/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ Subsystem-specific APIs
8181
acpi/index
8282
backlight/lp855x-driver.rst
8383
clk
84+
coco/index
8485
console
8586
crypto/index
8687
dmaengine/index

MAINTAINERS

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24648,8 +24648,9 @@ M: Dan Williams <[email protected]>
2464824648
2464924649
S: Maintained
2465024650
F: Documentation/ABI/testing/configfs-tsm
24651-
F: drivers/virt/coco/tsm.c
24652-
F: include/linux/tsm.h
24651+
F: Documentation/driver-api/coco/
24652+
F: drivers/virt/coco/tsm*.c
24653+
F: include/linux/tsm*.h
2465324654

2465424655
TRUSTED SERVICES TEE DRIVER
2465524656
M: Balint Dobszay <[email protected]>

drivers/virt/coco/Kconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ config TSM_REPORTS
77
select CONFIGFS_FS
88
tristate
99

10+
config TSM_MEASUREMENTS
11+
select CRYPTO_HASH_INFO
12+
select CRYPTO
13+
bool
14+
1015
source "drivers/virt/coco/efi_secret/Kconfig"
1116

1217
source "drivers/virt/coco/pkvm-guest/Kconfig"

drivers/virt/coco/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Confidential computing related collateral
44
#
55
obj-$(CONFIG_TSM_REPORTS) += tsm.o
6+
obj-$(CONFIG_TSM_MEASUREMENTS) += tsm-mr.o
67
obj-$(CONFIG_EFI_SECRET) += efi_secret/
78
obj-$(CONFIG_ARM_PKVM_GUEST) += pkvm-guest/
89
obj-$(CONFIG_SEV_GUEST) += sev-guest/

drivers/virt/coco/tsm-mr.c

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/* Copyright(c) 2024-2025 Intel Corporation. All rights reserved. */
3+
4+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
5+
6+
#include <linux/module.h>
7+
#include <linux/slab.h>
8+
#include <linux/sysfs.h>
9+
10+
#define CREATE_TRACE_POINTS
11+
#include <trace/events/tsm_mr.h>
12+
13+
/*
14+
* struct tm_context - contains everything necessary to implement sysfs
15+
* attributes for MRs.
16+
* @rwsem: protects the MR cache from concurrent access.
17+
* @agrp: contains all MR attributes created by tsm_mr_create_attribute_group().
18+
* @tm: input to tsm_mr_create_attribute_group() containing MR definitions/ops.
19+
* @in_sync: %true if MR cache is up-to-date.
20+
* @mrs: array of &struct bin_attribute, one for each MR.
21+
*
22+
* This internal structure contains everything needed to implement
23+
* tm_digest_read() and tm_digest_write().
24+
*
25+
* Given tm->refresh() is potentially expensive, tm_digest_read() caches MR
26+
* values and calls tm->refresh() only when necessary. Only live MRs (i.e., with
27+
* %TSM_MR_F_LIVE set) can trigger tm->refresh(), while others are assumed to
28+
* retain their values from the last tm->write(). @in_sync tracks if there have
29+
* been tm->write() calls since the last tm->refresh(). That is, tm->refresh()
30+
* will be called only when a live MR is being read and the cache is stale
31+
* (@in_sync is %false).
32+
*
33+
* tm_digest_write() sets @in_sync to %false and calls tm->write(), whose
34+
* semantics is arch and MR specific. Most (if not all) writable MRs support the
35+
* extension semantics (i.e., tm->write() extends the input buffer into the MR).
36+
*/
37+
struct tm_context {
38+
struct rw_semaphore rwsem;
39+
struct attribute_group agrp;
40+
const struct tsm_measurements *tm;
41+
bool in_sync;
42+
struct bin_attribute mrs[];
43+
};
44+
45+
static ssize_t tm_digest_read(struct file *filp, struct kobject *kobj,
46+
const struct bin_attribute *attr, char *buffer,
47+
loff_t off, size_t count)
48+
{
49+
struct tm_context *ctx;
50+
const struct tsm_measurement_register *mr;
51+
int rc;
52+
53+
ctx = attr->private;
54+
rc = down_read_interruptible(&ctx->rwsem);
55+
if (rc)
56+
return rc;
57+
58+
mr = &ctx->tm->mrs[attr - ctx->mrs];
59+
60+
/*
61+
* @ctx->in_sync indicates if the MR cache is stale. It is a global
62+
* instead of a per-MR flag for simplicity, as most (if not all) archs
63+
* allow reading all MRs in oneshot.
64+
*
65+
* ctx->refresh() is necessary only for LIVE MRs, while others retain
66+
* their values from their respective last ctx->write().
67+
*/
68+
if ((mr->mr_flags & TSM_MR_F_LIVE) && !ctx->in_sync) {
69+
up_read(&ctx->rwsem);
70+
71+
rc = down_write_killable(&ctx->rwsem);
72+
if (rc)
73+
return rc;
74+
75+
if (!ctx->in_sync) {
76+
rc = ctx->tm->refresh(ctx->tm);
77+
ctx->in_sync = !rc;
78+
trace_tsm_mr_refresh(mr, rc);
79+
}
80+
81+
downgrade_write(&ctx->rwsem);
82+
}
83+
84+
memcpy(buffer, mr->mr_value + off, count);
85+
trace_tsm_mr_read(mr);
86+
87+
up_read(&ctx->rwsem);
88+
return rc ?: count;
89+
}
90+
91+
static ssize_t tm_digest_write(struct file *filp, struct kobject *kobj,
92+
const struct bin_attribute *attr, char *buffer,
93+
loff_t off, size_t count)
94+
{
95+
struct tm_context *ctx;
96+
const struct tsm_measurement_register *mr;
97+
ssize_t rc;
98+
99+
/* partial writes are not supported */
100+
if (off != 0 || count != attr->size)
101+
return -EINVAL;
102+
103+
ctx = attr->private;
104+
mr = &ctx->tm->mrs[attr - ctx->mrs];
105+
106+
rc = down_write_killable(&ctx->rwsem);
107+
if (rc)
108+
return rc;
109+
110+
rc = ctx->tm->write(ctx->tm, mr, buffer);
111+
112+
/* mark MR cache stale */
113+
if (!rc) {
114+
ctx->in_sync = false;
115+
trace_tsm_mr_write(mr, buffer);
116+
}
117+
118+
up_write(&ctx->rwsem);
119+
return rc ?: count;
120+
}
121+
122+
/**
123+
* tsm_mr_create_attribute_group() - creates an attribute group for measurement
124+
* registers (MRs)
125+
* @tm: pointer to &struct tsm_measurements containing the MR definitions.
126+
*
127+
* This function creates attributes corresponding to the MR definitions
128+
* provided by @tm->mrs.
129+
*
130+
* The created attributes will reference @tm and its members. The caller must
131+
* not free @tm until after tsm_mr_free_attribute_group() is called.
132+
*
133+
* Context: Process context. May sleep due to memory allocation.
134+
*
135+
* Return:
136+
* * On success, the pointer to a an attribute group is returned; otherwise
137+
* * %-EINVAL - Invalid MR definitions.
138+
* * %-ENOMEM - Out of memory.
139+
*/
140+
const struct attribute_group *
141+
tsm_mr_create_attribute_group(const struct tsm_measurements *tm)
142+
{
143+
size_t nlen;
144+
145+
if (!tm || !tm->mrs)
146+
return ERR_PTR(-EINVAL);
147+
148+
/* aggregated length of all MR names */
149+
nlen = 0;
150+
for (size_t i = 0; i < tm->nr_mrs; ++i) {
151+
if ((tm->mrs[i].mr_flags & TSM_MR_F_LIVE) && !tm->refresh)
152+
return ERR_PTR(-EINVAL);
153+
154+
if ((tm->mrs[i].mr_flags & TSM_MR_F_WRITABLE) && !tm->write)
155+
return ERR_PTR(-EINVAL);
156+
157+
if (!tm->mrs[i].mr_name)
158+
return ERR_PTR(-EINVAL);
159+
160+
if (tm->mrs[i].mr_flags & TSM_MR_F_NOHASH)
161+
continue;
162+
163+
if (tm->mrs[i].mr_hash >= HASH_ALGO__LAST)
164+
return ERR_PTR(-EINVAL);
165+
166+
/* MR sysfs attribute names have the form of MRNAME:HASH */
167+
nlen += strlen(tm->mrs[i].mr_name) + 1 +
168+
strlen(hash_algo_name[tm->mrs[i].mr_hash]) + 1;
169+
}
170+
171+
/*
172+
* @attrs and the MR name strings are combined into a single allocation
173+
* so that we don't have to free MR names one-by-one in
174+
* tsm_mr_free_attribute_group()
175+
*/
176+
const struct bin_attribute * const *attrs __free(kfree) =
177+
kzalloc(sizeof(*attrs) * (tm->nr_mrs + 1) + nlen, GFP_KERNEL);
178+
struct tm_context *ctx __free(kfree) =
179+
kzalloc(struct_size(ctx, mrs, tm->nr_mrs), GFP_KERNEL);
180+
char *name, *end;
181+
182+
if (!ctx || !attrs)
183+
return ERR_PTR(-ENOMEM);
184+
185+
/* @attrs is followed immediately by MR name strings */
186+
name = (char *)&attrs[tm->nr_mrs + 1];
187+
end = name + nlen;
188+
189+
for (size_t i = 0; i < tm->nr_mrs; ++i) {
190+
/* break const for init */
191+
struct bin_attribute **bas = (struct bin_attribute **)attrs;
192+
193+
bas[i] = &ctx->mrs[i];
194+
sysfs_bin_attr_init(bas[i]);
195+
196+
if (tm->mrs[i].mr_flags & TSM_MR_F_NOHASH)
197+
bas[i]->attr.name = tm->mrs[i].mr_name;
198+
else if (name < end) {
199+
bas[i]->attr.name = name;
200+
name += snprintf(name, end - name, "%s:%s",
201+
tm->mrs[i].mr_name,
202+
hash_algo_name[tm->mrs[i].mr_hash]);
203+
++name;
204+
} else
205+
return ERR_PTR(-EINVAL);
206+
207+
/* check for duplicated MR definitions */
208+
for (size_t j = 0; j < i; ++j)
209+
if (!strcmp(bas[i]->attr.name, bas[j]->attr.name))
210+
return ERR_PTR(-EINVAL);
211+
212+
if (tm->mrs[i].mr_flags & TSM_MR_F_READABLE) {
213+
bas[i]->attr.mode |= 0444;
214+
bas[i]->read_new = tm_digest_read;
215+
}
216+
217+
if (tm->mrs[i].mr_flags & TSM_MR_F_WRITABLE) {
218+
bas[i]->attr.mode |= 0200;
219+
bas[i]->write_new = tm_digest_write;
220+
}
221+
222+
bas[i]->size = tm->mrs[i].mr_size;
223+
bas[i]->private = ctx;
224+
}
225+
226+
if (name != end)
227+
return ERR_PTR(-EINVAL);
228+
229+
init_rwsem(&ctx->rwsem);
230+
ctx->agrp.name = "measurements";
231+
ctx->agrp.bin_attrs_new = no_free_ptr(attrs);
232+
ctx->tm = tm;
233+
return &no_free_ptr(ctx)->agrp;
234+
}
235+
EXPORT_SYMBOL_GPL(tsm_mr_create_attribute_group);
236+
237+
/**
238+
* tsm_mr_free_attribute_group() - frees the attribute group returned by
239+
* tsm_mr_create_attribute_group()
240+
* @attr_grp: attribute group returned by tsm_mr_create_attribute_group()
241+
*
242+
* Context: Process context.
243+
*/
244+
void tsm_mr_free_attribute_group(const struct attribute_group *attr_grp)
245+
{
246+
if (!IS_ERR_OR_NULL(attr_grp)) {
247+
kfree(attr_grp->bin_attrs);
248+
kfree(container_of(attr_grp, struct tm_context, agrp));
249+
}
250+
}
251+
EXPORT_SYMBOL_GPL(tsm_mr_free_attribute_group);

0 commit comments

Comments
 (0)