Skip to content

Commit 4d2a7bf

Browse files
binxingdjbw
authored andcommitted
virt: tdx-guest: Expose TDX MRs as sysfs attributes
Expose the most commonly used TDX MRs (Measurement Registers) as sysfs attributes. Use the ioctl() interface of /dev/tdx_guest to request a full TDREPORT for access to other TD measurements. Directory structure of TDX MRs inside a TDVM is as follows: /sys/class/misc/tdx_guest └── measurements ├── mrconfigid ├── mrowner ├── mrownerconfig ├── mrtd:sha384 ├── rtmr0:sha384 ├── rtmr1:sha384 ├── rtmr2:sha384 └── rtmr3:sha384 Read the file/attribute to retrieve the current value of an MR. Write to the file/attribute (if writable) to extend the corresponding RTMR. Refer to Documentation/ABI/testing/sysfs-devices-virtual-misc-tdx_guest for more information. Signed-off-by: Cedric Xing <cedric.xing@intel.com> Acked-by: Dionna Amalie Glaze <dionnaglaze@google.com> [djbw: fixup exit order] Link: https://patch.msgid.link/20250508010606.4129953-1-dan.j.williams@intel.com Signed-off-by: Dan Williams <dan.j.williams@intel.com>
1 parent 2748566 commit 4d2a7bf

File tree

4 files changed

+214
-2
lines changed

4 files changed

+214
-2
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
What: /sys/devices/virtual/misc/tdx_guest/measurements/MRNAME[:HASH]
2+
Date: April, 2025
3+
KernelVersion: v6.16
4+
Contact: linux-coco@lists.linux.dev
5+
Description:
6+
Value of a TDX measurement register (MR). MRNAME and HASH above
7+
are placeholders. The optional suffix :HASH is used for MRs
8+
that have associated hash algorithms. See below for a complete
9+
list of TDX MRs exposed via sysfs. Refer to Intel TDX Module
10+
ABI Specification for the definition of TDREPORT and the full
11+
list of TDX measurements.
12+
13+
Intel TDX Module ABI Specification can be found at:
14+
https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/documentation.html#architecture
15+
16+
See also:
17+
https://docs.kernel.org/driver-api/coco/measurement-registers.html
18+
19+
What: /sys/devices/virtual/misc/tdx_guest/measurements/mrconfigid
20+
Date: April, 2025
21+
KernelVersion: v6.16
22+
Contact: linux-coco@lists.linux.dev
23+
Description:
24+
(RO) MRCONFIGID - 48-byte immutable storage typically used for
25+
software-defined ID for non-owner-defined configuration of the
26+
guest TD – e.g., run-time or OS configuration.
27+
28+
What: /sys/devices/virtual/misc/tdx_guest/measurements/mrowner
29+
Date: April, 2025
30+
KernelVersion: v6.16
31+
Contact: linux-coco@lists.linux.dev
32+
Description:
33+
(RO) MROWNER - 48-byte immutable storage typically used for
34+
software-defined ID for the guest TD’s owner.
35+
36+
What: /sys/devices/virtual/misc/tdx_guest/measurements/mrownerconfig
37+
Date: April, 2025
38+
KernelVersion: v6.16
39+
Contact: linux-coco@lists.linux.dev
40+
Description:
41+
(RO) MROWNERCONFIG - 48-byte immutable storage typically used
42+
for software-defined ID for owner-defined configuration of the
43+
guest TD – e.g., specific to the workload rather than the
44+
run-time or OS.
45+
46+
What: /sys/devices/virtual/misc/tdx_guest/measurements/mrtd:sha384
47+
Date: April, 2025
48+
KernelVersion: v6.16
49+
Contact: linux-coco@lists.linux.dev
50+
Description:
51+
(RO) MRTD - Measurement of the initial contents of the TD.
52+
53+
What: /sys/devices/virtual/misc/tdx_guest/measurements/rtmr[0123]:sha384
54+
Date: April, 2025
55+
KernelVersion: v6.16
56+
Contact: linux-coco@lists.linux.dev
57+
Description:
58+
(RW) RTMR[0123] - 4 Run-Time extendable Measurement Registers.
59+
Read from any of these returns the current value of the
60+
corresponding RTMR. Write extends the written buffer to the
61+
RTMR. All writes must start at offset 0 and be 48 bytes in
62+
size. Partial writes will result in EINVAL returned by the
63+
write() syscall.

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26321,6 +26321,7 @@ L: x86@kernel.org
2632126321
L: linux-coco@lists.linux.dev
2632226322
S: Supported
2632326323
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/tdx
26324+
F: Documentation/ABI/testing/sysfs-devices-virtual-misc-tdx_guest
2632426325
F: arch/x86/boot/compressed/tdx*
2632526326
F: arch/x86/coco/tdx/
2632626327
F: arch/x86/include/asm/shared/tdx.h

drivers/virt/coco/tdx-guest/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ config TDX_GUEST_DRIVER
22
tristate "TDX Guest driver"
33
depends on INTEL_TDX_GUEST
44
select TSM_REPORTS
5+
select TSM_MEASUREMENTS
56
help
67
The driver provides userspace interface to communicate with
78
the TDX module to request the TDX guest details like attestation

drivers/virt/coco/tdx-guest/tdx-guest.c

Lines changed: 149 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* Copyright (C) 2022 Intel Corporation
66
*/
77

8+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9+
810
#include <linux/kernel.h>
911
#include <linux/miscdevice.h>
1012
#include <linux/mm.h>
@@ -15,14 +17,146 @@
1517
#include <linux/set_memory.h>
1618
#include <linux/io.h>
1719
#include <linux/delay.h>
20+
#include <linux/sockptr.h>
1821
#include <linux/tsm.h>
19-
#include <linux/sizes.h>
22+
#include <linux/tsm-mr.h>
2023

2124
#include <uapi/linux/tdx-guest.h>
2225

2326
#include <asm/cpu_device_id.h>
2427
#include <asm/tdx.h>
2528

29+
/* TDREPORT buffer */
30+
static u8 *tdx_report_buf;
31+
32+
/* Lock to serialize TDG.MR.REPORT and TDG.MR.RTMR.EXTEND TDCALLs */
33+
static DEFINE_MUTEX(mr_lock);
34+
35+
/* TDREPORT fields */
36+
enum {
37+
TDREPORT_reportdata = 128,
38+
TDREPORT_tee_tcb_info = 256,
39+
TDREPORT_tdinfo = TDREPORT_tee_tcb_info + 256,
40+
TDREPORT_attributes = TDREPORT_tdinfo,
41+
TDREPORT_xfam = TDREPORT_attributes + sizeof(u64),
42+
TDREPORT_mrtd = TDREPORT_xfam + sizeof(u64),
43+
TDREPORT_mrconfigid = TDREPORT_mrtd + SHA384_DIGEST_SIZE,
44+
TDREPORT_mrowner = TDREPORT_mrconfigid + SHA384_DIGEST_SIZE,
45+
TDREPORT_mrownerconfig = TDREPORT_mrowner + SHA384_DIGEST_SIZE,
46+
TDREPORT_rtmr0 = TDREPORT_mrownerconfig + SHA384_DIGEST_SIZE,
47+
TDREPORT_rtmr1 = TDREPORT_rtmr0 + SHA384_DIGEST_SIZE,
48+
TDREPORT_rtmr2 = TDREPORT_rtmr1 + SHA384_DIGEST_SIZE,
49+
TDREPORT_rtmr3 = TDREPORT_rtmr2 + SHA384_DIGEST_SIZE,
50+
TDREPORT_servtd_hash = TDREPORT_rtmr3 + SHA384_DIGEST_SIZE,
51+
};
52+
53+
static int tdx_do_report(sockptr_t data, sockptr_t tdreport)
54+
{
55+
scoped_cond_guard(mutex_intr, return -EINTR, &mr_lock) {
56+
u8 *reportdata = tdx_report_buf + TDREPORT_reportdata;
57+
int ret;
58+
59+
if (!sockptr_is_null(data) &&
60+
copy_from_sockptr(reportdata, data, TDX_REPORTDATA_LEN))
61+
return -EFAULT;
62+
63+
ret = tdx_mcall_get_report0(reportdata, tdx_report_buf);
64+
if (WARN_ONCE(ret, "tdx_mcall_get_report0() failed: %d", ret))
65+
return ret;
66+
67+
if (!sockptr_is_null(tdreport) &&
68+
copy_to_sockptr(tdreport, tdx_report_buf, TDX_REPORT_LEN))
69+
return -EFAULT;
70+
}
71+
return 0;
72+
}
73+
74+
static int tdx_do_extend(u8 mr_ind, const u8 *data)
75+
{
76+
scoped_cond_guard(mutex_intr, return -EINTR, &mr_lock) {
77+
/*
78+
* TDX requires @extend_buf to be 64-byte aligned.
79+
* It's safe to use REPORTDATA buffer for that purpose because
80+
* tdx_mr_report/extend_lock() are mutually exclusive.
81+
*/
82+
u8 *extend_buf = tdx_report_buf + TDREPORT_reportdata;
83+
int ret;
84+
85+
memcpy(extend_buf, data, SHA384_DIGEST_SIZE);
86+
87+
ret = tdx_mcall_extend_rtmr(mr_ind, extend_buf);
88+
if (WARN_ONCE(ret, "tdx_mcall_extend_rtmr(%u) failed: %d", mr_ind, ret))
89+
return ret;
90+
}
91+
return 0;
92+
}
93+
94+
#define TDX_MR_(r) .mr_value = (void *)TDREPORT_##r, TSM_MR_(r, SHA384)
95+
static struct tsm_measurement_register tdx_mrs[] = {
96+
{ TDX_MR_(rtmr0) | TSM_MR_F_RTMR },
97+
{ TDX_MR_(rtmr1) | TSM_MR_F_RTMR },
98+
{ TDX_MR_(rtmr2) | TSM_MR_F_RTMR },
99+
{ TDX_MR_(rtmr3) | TSM_MR_F_RTMR },
100+
{ TDX_MR_(mrtd) },
101+
{ TDX_MR_(mrconfigid) | TSM_MR_F_NOHASH },
102+
{ TDX_MR_(mrowner) | TSM_MR_F_NOHASH },
103+
{ TDX_MR_(mrownerconfig) | TSM_MR_F_NOHASH },
104+
};
105+
#undef TDX_MR_
106+
107+
static int tdx_mr_refresh(const struct tsm_measurements *tm)
108+
{
109+
return tdx_do_report(KERNEL_SOCKPTR(NULL), KERNEL_SOCKPTR(NULL));
110+
}
111+
112+
static int tdx_mr_extend(const struct tsm_measurements *tm,
113+
const struct tsm_measurement_register *mr,
114+
const u8 *data)
115+
{
116+
return tdx_do_extend(mr - tm->mrs, data);
117+
}
118+
119+
static struct tsm_measurements tdx_measurements = {
120+
.mrs = tdx_mrs,
121+
.nr_mrs = ARRAY_SIZE(tdx_mrs),
122+
.refresh = tdx_mr_refresh,
123+
.write = tdx_mr_extend,
124+
};
125+
126+
static const struct attribute_group *tdx_mr_init(void)
127+
{
128+
const struct attribute_group *g;
129+
int rc;
130+
131+
u8 *buf __free(kfree) = kzalloc(TDX_REPORT_LEN, GFP_KERNEL);
132+
if (!buf)
133+
return ERR_PTR(-ENOMEM);
134+
135+
tdx_report_buf = buf;
136+
rc = tdx_mr_refresh(&tdx_measurements);
137+
if (rc)
138+
return ERR_PTR(rc);
139+
140+
/*
141+
* @mr_value was initialized with the offset only, while the base
142+
* address is being added here.
143+
*/
144+
for (size_t i = 0; i < ARRAY_SIZE(tdx_mrs); ++i)
145+
*(long *)&tdx_mrs[i].mr_value += (long)buf;
146+
147+
g = tsm_mr_create_attribute_group(&tdx_measurements);
148+
if (!IS_ERR(g))
149+
tdx_report_buf = no_free_ptr(buf);
150+
151+
return g;
152+
}
153+
154+
static void tdx_mr_deinit(const struct attribute_group *mr_grp)
155+
{
156+
tsm_mr_free_attribute_group(mr_grp);
157+
kfree(tdx_report_buf);
158+
}
159+
26160
/*
27161
* Intel's SGX QE implementation generally uses Quote size less
28162
* than 8K (2K Quote data + ~5K of certificate blob).
@@ -285,10 +419,16 @@ static const struct file_operations tdx_guest_fops = {
285419
.unlocked_ioctl = tdx_guest_ioctl,
286420
};
287421

422+
static const struct attribute_group *tdx_attr_groups[] = {
423+
NULL, /* measurements */
424+
NULL
425+
};
426+
288427
static struct miscdevice tdx_misc_dev = {
289428
.name = KBUILD_MODNAME,
290429
.minor = MISC_DYNAMIC_MINOR,
291430
.fops = &tdx_guest_fops,
431+
.groups = tdx_attr_groups,
292432
};
293433

294434
static const struct x86_cpu_id tdx_guest_ids[] = {
@@ -311,9 +451,13 @@ static int __init tdx_guest_init(void)
311451
if (!x86_match_cpu(tdx_guest_ids))
312452
return -ENODEV;
313453

454+
tdx_attr_groups[0] = tdx_mr_init();
455+
if (IS_ERR(tdx_attr_groups[0]))
456+
return PTR_ERR(tdx_attr_groups[0]);
457+
314458
ret = misc_register(&tdx_misc_dev);
315459
if (ret)
316-
return ret;
460+
goto deinit_mr;
317461

318462
quote_data = alloc_quote_buf();
319463
if (!quote_data) {
@@ -332,6 +476,8 @@ static int __init tdx_guest_init(void)
332476
free_quote_buf(quote_data);
333477
free_misc:
334478
misc_deregister(&tdx_misc_dev);
479+
deinit_mr:
480+
tdx_mr_deinit(tdx_attr_groups[0]);
335481

336482
return ret;
337483
}
@@ -342,6 +488,7 @@ static void __exit tdx_guest_exit(void)
342488
tsm_unregister(&tdx_tsm_ops);
343489
free_quote_buf(quote_data);
344490
misc_deregister(&tdx_misc_dev);
491+
tdx_mr_deinit(tdx_attr_groups[0]);
345492
}
346493
module_exit(tdx_guest_exit);
347494

0 commit comments

Comments
 (0)