From: Dan Williams Date: Tue, 13 May 2025 05:12:44 +0000 (-0700) Subject: Merge branch 'for-6.16/tsm-mr' into tsm-next X-Git-Tag: ceph-for-6.17-rc6~732^2~1 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=15ff5d0e90bb044b94d9a4ac57276829861a954d;p=ceph-client.git Merge branch 'for-6.16/tsm-mr' into tsm-next Merge measurement-register infrastructure for v6.16. Resolve conflicts with the establishment of drivers/virt/coco/guest/ for cross-vendor common TSM functionality. Address a mis-merge with a fixup from Lukas: Link: http://lore.kernel.org/20250509134031.70559-1-lukas.bulwahn@redhat.com --- 15ff5d0e90bb044b94d9a4ac57276829861a954d diff --cc Documentation/driver-api/coco/measurement-registers.rst index 0000000000000,cef85945a9a74..962a44efa2c02 mode 000000,100644..100644 --- a/Documentation/driver-api/coco/measurement-registers.rst +++ b/Documentation/driver-api/coco/measurement-registers.rst @@@ -1,0 -1,12 +1,12 @@@ + .. SPDX-License-Identifier: GPL-2.0 + .. include:: + + ===================== + Measurement Registers + ===================== + + .. kernel-doc:: include/linux/tsm-mr.h + :internal: + -.. kernel-doc:: drivers/virt/coco/tsm-mr.c ++.. kernel-doc:: drivers/virt/coco/guest/tsm-mr.c + :export: diff --cc MAINTAINERS index 0a1ca9233ccf8,912e16ace0b4a..10d6d99b8dc7e --- a/MAINTAINERS +++ b/MAINTAINERS @@@ -24555,13 -24643,15 +24643,15 @@@ M: David Lechner L: linux-coco@lists.linux.dev S: Maintained -F: Documentation/ABI/testing/configfs-tsm +F: Documentation/ABI/testing/configfs-tsm-report + F: Documentation/driver-api/coco/ -F: drivers/virt/coco/tsm*.c +F: drivers/virt/coco/guest/ - F: include/linux/tsm.h + F: include/linux/tsm*.h -F: samples/tsm/ ++F: samples/tsm-mr/ TRUSTED SERVICES TEE DRIVER M: Balint Dobszay diff --cc drivers/virt/coco/Makefile index 885c9ef4e9fc9,eb6ec5c1d2e17..f918bbb617375 --- a/drivers/virt/coco/Makefile +++ b/drivers/virt/coco/Makefile @@@ -7,4 -9,3 +7,4 @@@ obj-$(CONFIG_ARM_PKVM_GUEST) += pkvm-gu obj-$(CONFIG_SEV_GUEST) += sev-guest/ obj-$(CONFIG_INTEL_TDX_GUEST) += tdx-guest/ obj-$(CONFIG_ARM_CCA_GUEST) += arm-cca-guest/ - obj-$(CONFIG_TSM_REPORTS) += guest/ ++obj-$(CONFIG_TSM_GUEST) += guest/ diff --cc drivers/virt/coco/guest/Kconfig index ed9bafbdd8548,0000000000000..3d5e1d05bf343 mode 100644,000000..100644 --- a/drivers/virt/coco/guest/Kconfig +++ b/drivers/virt/coco/guest/Kconfig @@@ -1,7 -1,0 +1,17 @@@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Confidential computing shared guest collateral +# ++config TSM_GUEST ++ bool ++ +config TSM_REPORTS ++ select TSM_GUEST + select CONFIGFS_FS + tristate ++ ++config TSM_MEASUREMENTS ++ select TSM_GUEST ++ select CRYPTO_HASH_INFO ++ select CRYPTO ++ bool diff --cc drivers/virt/coco/guest/Makefile index b3b217af77cf5,0000000000000..9ec4860bd213a mode 100644,000000..100644 --- a/drivers/virt/coco/guest/Makefile +++ b/drivers/virt/coco/guest/Makefile @@@ -1,3 -1,0 +1,4 @@@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_TSM_REPORTS) += tsm_report.o +tsm_report-y := report.o ++obj-$(CONFIG_TSM_MEASUREMENTS) += tsm-mr.o diff --cc drivers/virt/coco/guest/tsm-mr.c index 0000000000000,0000000000000..1f0c43a516fb8 new file mode 100644 --- /dev/null +++ b/drivers/virt/coco/guest/tsm-mr.c @@@ -1,0 -1,0 +1,251 @@@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* Copyright(c) 2024-2025 Intel Corporation. All rights reserved. */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++ ++#define CREATE_TRACE_POINTS ++#include ++ ++/* ++ * 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 * const *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) { ++ /* break const for init */ ++ struct bin_attribute **bas = (struct bin_attribute **)attrs; ++ ++ bas[i] = &ctx->mrs[i]; ++ sysfs_bin_attr_init(bas[i]); ++ ++ if (tm->mrs[i].mr_flags & TSM_MR_F_NOHASH) ++ bas[i]->attr.name = tm->mrs[i].mr_name; ++ else if (name < end) { ++ bas[i]->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(bas[i]->attr.name, bas[j]->attr.name)) ++ return ERR_PTR(-EINVAL); ++ ++ if (tm->mrs[i].mr_flags & TSM_MR_F_READABLE) { ++ bas[i]->attr.mode |= 0444; ++ bas[i]->read_new = tm_digest_read; ++ } ++ ++ if (tm->mrs[i].mr_flags & TSM_MR_F_WRITABLE) { ++ bas[i]->attr.mode |= 0200; ++ bas[i]->write_new = tm_digest_write; ++ } ++ ++ bas[i]->size = tm->mrs[i].mr_size; ++ bas[i]->private = ctx; ++ } ++ ++ if (name != end) ++ return ERR_PTR(-EINVAL); ++ ++ init_rwsem(&ctx->rwsem); ++ ctx->agrp.name = "measurements"; ++ ctx->agrp.bin_attrs_new = 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); diff --cc drivers/virt/coco/tdx-guest/tdx-guest.c index bd043838ab2e1,60466c439a4bc..4e239ec960c9b --- a/drivers/virt/coco/tdx-guest/tdx-guest.c +++ b/drivers/virt/coco/tdx-guest/tdx-guest.c @@@ -157,11 -262,11 +262,11 @@@ static int wait_for_quote_completion(st return (i == timeout) ? -ETIMEDOUT : 0; } - static int tdx_report_new(struct tsm_report *report, void *data) + static int tdx_report_new_locked(struct tsm_report *report, void *data) { - u8 *buf, *reportdata = NULL, *tdreport = NULL; + u8 *buf; struct tdx_quote_buf *quote_buf = quote_data; - struct tsm_desc *desc = &report->desc; + struct tsm_report_desc *desc = &report->desc; int ret; u64 err; @@@ -339,9 -429,10 +429,10 @@@ module_init(tdx_guest_init) static void __exit tdx_guest_exit(void) { - tsm_unregister(&tdx_tsm_ops); + tsm_report_unregister(&tdx_tsm_ops); free_quote_buf(quote_data); misc_deregister(&tdx_misc_dev); + tdx_mr_deinit(tdx_attr_groups[0]); } module_exit(tdx_guest_exit);