Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <cedric.xing@intel.com>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Acked-by: Dionna Amalie Glaze <dionnaglaze@google.com>
[djbw: fixup bin_attr const conflict]
Link: https://patch.msgid.link/20250509020739.882913-1-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>

authored by

Cedric Xing and committed by
Dan Williams
b9e22b35 92a09c47

+454 -2
+12
Documentation/driver-api/coco/index.rst
··· 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
+12
Documentation/driver-api/coco/measurement-registers.rst
··· 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:
+1
Documentation/driver-api/index.rst
··· 81 81 acpi/index 82 82 backlight/lp855x-driver.rst 83 83 clk 84 + coco/index 84 85 console 85 86 crypto/index 86 87 dmaengine/index
+3 -2
MAINTAINERS
··· 24648 24648 L: linux-coco@lists.linux.dev 24649 24649 S: Maintained 24650 24650 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 24653 24654 24654 24655 TRUSTED SERVICES TEE DRIVER 24655 24656 M: Balint Dobszay <balint.dobszay@arm.com>
+5
drivers/virt/coco/Kconfig
··· 7 7 select CONFIGFS_FS 8 8 tristate 9 9 10 + config TSM_MEASUREMENTS 11 + select CRYPTO_HASH_INFO 12 + select CRYPTO 13 + bool 14 + 10 15 source "drivers/virt/coco/efi_secret/Kconfig" 11 16 12 17 source "drivers/virt/coco/pkvm-guest/Kconfig"
+1
drivers/virt/coco/Makefile
··· 3 3 # Confidential computing related collateral 4 4 # 5 5 obj-$(CONFIG_TSM_REPORTS) += tsm.o 6 + obj-$(CONFIG_TSM_MEASUREMENTS) += tsm-mr.o 6 7 obj-$(CONFIG_EFI_SECRET) += efi_secret/ 7 8 obj-$(CONFIG_ARM_PKVM_GUEST) += pkvm-guest/ 8 9 obj-$(CONFIG_SEV_GUEST) += sev-guest/
+251
drivers/virt/coco/tsm-mr.c
··· 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);
+89
include/linux/tsm-mr.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + #ifndef __TSM_MR_H 4 + #define __TSM_MR_H 5 + 6 + #include <crypto/hash_info.h> 7 + 8 + /** 9 + * struct tsm_measurement_register - describes an architectural measurement 10 + * register (MR) 11 + * @mr_name: name of the MR 12 + * @mr_value: buffer containing the current value of the MR 13 + * @mr_size: size of the MR - typically the digest size of @mr_hash 14 + * @mr_flags: bitwise OR of one or more flags, detailed below 15 + * @mr_hash: optional hash identifier defined in include/uapi/linux/hash_info.h. 16 + * 17 + * A CC guest driver encloses an array of this structure in struct 18 + * tsm_measurements to detail the measurement facility supported by the 19 + * underlying CC hardware. 20 + * 21 + * @mr_name and @mr_value must stay valid until this structure is no longer in 22 + * use. 23 + * 24 + * @mr_flags is the bitwise-OR of zero or more of the flags below. 25 + * 26 + * * %TSM_MR_F_READABLE - the sysfs attribute corresponding to this MR is readable. 27 + * * %TSM_MR_F_WRITABLE - the sysfs attribute corresponding to this MR is writable. 28 + * The semantics is typically to extend the MR but could vary depending on the 29 + * architecture and the MR. 30 + * * %TSM_MR_F_LIVE - this MR's value may differ from the last value written, so 31 + * must be read back from the underlying CC hardware/firmware. 32 + * * %TSM_MR_F_RTMR - bitwise-OR of %TSM_MR_F_LIVE and %TSM_MR_F_WRITABLE. 33 + * * %TSM_MR_F_NOHASH - this MR does NOT have an associated hash algorithm. 34 + * @mr_hash will be ignored when this flag is set. 35 + */ 36 + struct tsm_measurement_register { 37 + const char *mr_name; 38 + void *mr_value; 39 + u32 mr_size; 40 + u32 mr_flags; 41 + enum hash_algo mr_hash; 42 + }; 43 + 44 + #define TSM_MR_F_NOHASH 1 45 + #define TSM_MR_F_WRITABLE 2 46 + #define TSM_MR_F_READABLE 4 47 + #define TSM_MR_F_LIVE 8 48 + #define TSM_MR_F_RTMR (TSM_MR_F_LIVE | TSM_MR_F_WRITABLE) 49 + 50 + #define TSM_MR_(mr, hash) \ 51 + .mr_name = #mr, .mr_size = hash##_DIGEST_SIZE, \ 52 + .mr_hash = HASH_ALGO_##hash, .mr_flags = TSM_MR_F_READABLE 53 + 54 + /** 55 + * struct tsm_measurements - defines the CC architecture specific measurement 56 + * facility and methods for updating measurement registers (MRs) 57 + * @mrs: Array of MR definitions. 58 + * @nr_mrs: Number of elements in @mrs. 59 + * @refresh: Callback function to load/sync all MRs from TVM hardware/firmware 60 + * into the kernel cache. 61 + * @write: Callback function to write to the MR specified by the parameter @mr. 62 + * Typically, writing to an MR extends the input buffer to that MR. 63 + * 64 + * The @refresh callback is invoked when an MR with %TSM_MR_F_LIVE set is being 65 + * read and the cache is stale. It must reload all MRs with %TSM_MR_F_LIVE set. 66 + * The function parameter @tm is a pointer pointing back to this structure. 67 + * 68 + * The @write callback is invoked whenever an MR is being written. It takes two 69 + * additional parameters besides @tm: 70 + * 71 + * * @mr - points to the MR (an element of @tm->mrs) being written. 72 + * * @data - contains the bytes to write and whose size is @mr->mr_size. 73 + * 74 + * Both @refresh and @write should return 0 on success and an appropriate error 75 + * code on failure. 76 + */ 77 + struct tsm_measurements { 78 + const struct tsm_measurement_register *mrs; 79 + size_t nr_mrs; 80 + int (*refresh)(const struct tsm_measurements *tm); 81 + int (*write)(const struct tsm_measurements *tm, 82 + const struct tsm_measurement_register *mr, const u8 *data); 83 + }; 84 + 85 + const struct attribute_group * 86 + tsm_mr_create_attribute_group(const struct tsm_measurements *tm); 87 + void tsm_mr_free_attribute_group(const struct attribute_group *attr_grp); 88 + 89 + #endif
+80
include/trace/events/tsm_mr.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #undef TRACE_SYSTEM 3 + #define TRACE_SYSTEM tsm_mr 4 + 5 + #if !defined(_TRACE_TSM_MR_H) || defined(TRACE_HEADER_MULTI_READ) 6 + #define _TRACE_TSM_MR_H 7 + 8 + #include <linux/tracepoint.h> 9 + #include <linux/tsm-mr.h> 10 + 11 + TRACE_EVENT(tsm_mr_read, 12 + 13 + TP_PROTO(const struct tsm_measurement_register *mr), 14 + 15 + TP_ARGS(mr), 16 + 17 + TP_STRUCT__entry( 18 + __string(mr, mr->mr_name) 19 + __string(hash, mr->mr_flags & TSM_MR_F_NOHASH ? 20 + "data" : hash_algo_name[mr->mr_hash]) 21 + __dynamic_array(u8, d, mr->mr_size) 22 + ), 23 + 24 + TP_fast_assign( 25 + __assign_str(mr); 26 + __assign_str(hash); 27 + memcpy(__get_dynamic_array(d), mr->mr_value, __get_dynamic_array_len(d)); 28 + ), 29 + 30 + TP_printk("[%s] %s:%s", __get_str(mr), __get_str(hash), 31 + __print_hex_str(__get_dynamic_array(d), __get_dynamic_array_len(d))) 32 + ); 33 + 34 + TRACE_EVENT(tsm_mr_refresh, 35 + 36 + TP_PROTO(const struct tsm_measurement_register *mr, int rc), 37 + 38 + TP_ARGS(mr, rc), 39 + 40 + TP_STRUCT__entry( 41 + __string(mr, mr->mr_name) 42 + __field(int, rc) 43 + ), 44 + 45 + TP_fast_assign( 46 + __assign_str(mr); 47 + __entry->rc = rc; 48 + ), 49 + 50 + TP_printk("[%s] %s:%d", __get_str(mr), 51 + __entry->rc ? "failed" : "succeeded", __entry->rc) 52 + ); 53 + 54 + TRACE_EVENT(tsm_mr_write, 55 + 56 + TP_PROTO(const struct tsm_measurement_register *mr, const u8 *data), 57 + 58 + TP_ARGS(mr, data), 59 + 60 + TP_STRUCT__entry( 61 + __string(mr, mr->mr_name) 62 + __string(hash, mr->mr_flags & TSM_MR_F_NOHASH ? 63 + "data" : hash_algo_name[mr->mr_hash]) 64 + __dynamic_array(u8, d, mr->mr_size) 65 + ), 66 + 67 + TP_fast_assign( 68 + __assign_str(mr); 69 + __assign_str(hash); 70 + memcpy(__get_dynamic_array(d), data, __get_dynamic_array_len(d)); 71 + ), 72 + 73 + TP_printk("[%s] %s:%s", __get_str(mr), __get_str(hash), 74 + __print_hex_str(__get_dynamic_array(d), __get_dynamic_array_len(d))) 75 + ); 76 + 77 + #endif 78 + 79 + /* This part must be outside protection */ 80 + #include <trace/define_trace.h>