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

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'tsm-for-6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/devsec/tsm

Pull trusted security manager (TSM) updates from Dan Williams:

- Add a general sysfs scheme for publishing "Measurement" values
provided by the architecture's TEE Security Manager. Use it to
publish TDX "Runtime Measurement Registers" ("RTMRs") that either
maintain a hash of stored values (similar to a TPM PCR) or provide
statically provisioned data. These measurements are validated by a
relying party.

- Reorganize the drivers/virt/coco/ directory for "host" and "guest"
shared infrastructure.

- Fix a configfs-tsm-report unregister bug

- With CONFIG_TSM_MEASUREMENTS joining CONFIG_TSM_REPORTS and in
anticipation of more shared "TSM" infrastructure arriving, rename the
maintainer entry to "TRUSTED SECURITY MODULE (TSM) INFRASTRUCTURE".

* tag 'tsm-for-6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/devsec/tsm:
tsm-mr: Fix init breakage after bin_attrs constification by scoping non-const pointers to init phase
sample/tsm-mr: Fix missing static for sample_report
virt: tdx-guest: Transition to scoped_cond_guard for mutex operations
virt: tdx-guest: Refactor and streamline TDREPORT generation
virt: tdx-guest: Expose TDX MRs as sysfs attributes
x86/tdx: tdx_mcall_get_report0: Return -EBUSY on TDCALL_OPERAND_BUSY error
x86/tdx: Add tdx_mcall_extend_rtmr() interface
tsm-mr: Add tsm-mr sample code
tsm-mr: Add TVM Measurement Register support
configfs-tsm-report: Fix NULL dereference of tsm_ops
coco/guest: Move shared guest CC infrastructure to drivers/virt/coco/guest/
configfs-tsm: Namespace TSM report symbols

+974 -137
Documentation/ABI/testing/configfs-tsm Documentation/ABI/testing/configfs-tsm-report
+63
Documentation/ABI/testing/sysfs-devices-virtual-misc-tdx_guest
··· 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.
+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/guest/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
+7 -4
MAINTAINERS
··· 24981 24981 S: Maintained 24982 24982 F: Documentation/devicetree/bindings/trigger-source/pwm-trigger.yaml 24983 24983 24984 - TRUSTED SECURITY MODULE (TSM) ATTESTATION REPORTS 24984 + TRUSTED SECURITY MODULE (TSM) INFRASTRUCTURE 24985 24985 M: Dan Williams <dan.j.williams@intel.com> 24986 24986 L: linux-coco@lists.linux.dev 24987 24987 S: Maintained 24988 - F: Documentation/ABI/testing/configfs-tsm 24989 - F: drivers/virt/coco/tsm.c 24990 - F: include/linux/tsm.h 24988 + F: Documentation/ABI/testing/configfs-tsm-report 24989 + F: Documentation/driver-api/coco/ 24990 + F: drivers/virt/coco/guest/ 24991 + F: include/linux/tsm*.h 24992 + F: samples/tsm-mr/ 24991 24993 24992 24994 TRUSTED SERVICES TEE DRIVER 24993 24995 M: Balint Dobszay <balint.dobszay@arm.com> ··· 26675 26673 L: linux-coco@lists.linux.dev 26676 26674 S: Supported 26677 26675 T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/tdx 26676 + F: Documentation/ABI/testing/sysfs-devices-virtual-misc-tdx_guest 26678 26677 F: arch/x86/boot/compressed/tdx* 26679 26678 F: arch/x86/coco/tdx/ 26680 26679 F: arch/x86/include/asm/shared/tdx.h
+45 -5
arch/x86/coco/tdx/tdx.c
··· 36 36 /* TDX Module call error codes */ 37 37 #define TDCALL_RETURN_CODE(a) ((a) >> 32) 38 38 #define TDCALL_INVALID_OPERAND 0xc0000100 39 + #define TDCALL_OPERAND_BUSY 0x80000200 39 40 40 41 #define TDREPORT_SUBTYPE_0 0 41 42 ··· 110 109 * REPORTDATA to be included into TDREPORT. 111 110 * @tdreport: Address of the output buffer to store TDREPORT. 112 111 * 113 - * Refer to section titled "TDG.MR.REPORT leaf" in the TDX Module 114 - * v1.0 specification for more information on TDG.MR.REPORT TDCALL. 112 + * Refer to section titled "TDG.MR.REPORT leaf" in the TDX Module v1.0 113 + * specification for more information on TDG.MR.REPORT TDCALL. 114 + * 115 115 * It is used in the TDX guest driver module to get the TDREPORT0. 116 116 * 117 - * Return 0 on success, -EINVAL for invalid operands, or -EIO on 118 - * other TDCALL failures. 117 + * Return 0 on success, -ENXIO for invalid operands, -EBUSY for busy operation, 118 + * or -EIO on other TDCALL failures. 119 119 */ 120 120 int tdx_mcall_get_report0(u8 *reportdata, u8 *tdreport) 121 121 { ··· 130 128 ret = __tdcall(TDG_MR_REPORT, &args); 131 129 if (ret) { 132 130 if (TDCALL_RETURN_CODE(ret) == TDCALL_INVALID_OPERAND) 133 - return -EINVAL; 131 + return -ENXIO; 132 + else if (TDCALL_RETURN_CODE(ret) == TDCALL_OPERAND_BUSY) 133 + return -EBUSY; 134 134 return -EIO; 135 135 } 136 136 137 137 return 0; 138 138 } 139 139 EXPORT_SYMBOL_GPL(tdx_mcall_get_report0); 140 + 141 + /** 142 + * tdx_mcall_extend_rtmr() - Wrapper to extend RTMR registers using 143 + * TDG.MR.RTMR.EXTEND TDCALL. 144 + * @index: Index of RTMR register to be extended. 145 + * @data: Address of the input buffer with RTMR register extend data. 146 + * 147 + * Refer to section titled "TDG.MR.RTMR.EXTEND leaf" in the TDX Module v1.0 148 + * specification for more information on TDG.MR.RTMR.EXTEND TDCALL. 149 + * 150 + * It is used in the TDX guest driver module to allow user to extend the RTMR 151 + * registers. 152 + * 153 + * Return 0 on success, -ENXIO for invalid operands, -EBUSY for busy operation, 154 + * or -EIO on other TDCALL failures. 155 + */ 156 + int tdx_mcall_extend_rtmr(u8 index, u8 *data) 157 + { 158 + struct tdx_module_args args = { 159 + .rcx = virt_to_phys(data), 160 + .rdx = index, 161 + }; 162 + u64 ret; 163 + 164 + ret = __tdcall(TDG_MR_RTMR_EXTEND, &args); 165 + if (ret) { 166 + if (TDCALL_RETURN_CODE(ret) == TDCALL_INVALID_OPERAND) 167 + return -ENXIO; 168 + if (TDCALL_RETURN_CODE(ret) == TDCALL_OPERAND_BUSY) 169 + return -EBUSY; 170 + return -EIO; 171 + } 172 + 173 + return 0; 174 + } 175 + EXPORT_SYMBOL_GPL(tdx_mcall_extend_rtmr); 140 176 141 177 /** 142 178 * tdx_hcall_get_quote() - Wrapper to request TD Quote using GetQuote
+1
arch/x86/include/asm/shared/tdx.h
··· 13 13 /* TDX module Call Leaf IDs */ 14 14 #define TDG_VP_VMCALL 0 15 15 #define TDG_VP_INFO 1 16 + #define TDG_MR_RTMR_EXTEND 2 16 17 #define TDG_VP_VEINFO_GET 3 17 18 #define TDG_MR_REPORT 4 18 19 #define TDG_MEM_PAGE_ACCEPT 6
+2
arch/x86/include/asm/tdx.h
··· 68 68 69 69 int tdx_mcall_get_report0(u8 *reportdata, u8 *tdreport); 70 70 71 + int tdx_mcall_extend_rtmr(u8 index, u8 *data); 72 + 71 73 u64 tdx_hcall_get_quote(u8 *buf, size_t size); 72 74 73 75 void __init tdx_dump_attributes(u64 td_attr);
+2 -4
drivers/virt/coco/Kconfig
··· 3 3 # Confidential computing related collateral 4 4 # 5 5 6 - config TSM_REPORTS 7 - select CONFIGFS_FS 8 - tristate 9 - 10 6 source "drivers/virt/coco/efi_secret/Kconfig" 11 7 12 8 source "drivers/virt/coco/pkvm-guest/Kconfig" ··· 12 16 source "drivers/virt/coco/tdx-guest/Kconfig" 13 17 14 18 source "drivers/virt/coco/arm-cca-guest/Kconfig" 19 + 20 + source "drivers/virt/coco/guest/Kconfig"
+1 -1
drivers/virt/coco/Makefile
··· 2 2 # 3 3 # Confidential computing related collateral 4 4 # 5 - obj-$(CONFIG_TSM_REPORTS) += tsm.o 6 5 obj-$(CONFIG_EFI_SECRET) += efi_secret/ 7 6 obj-$(CONFIG_ARM_PKVM_GUEST) += pkvm-guest/ 8 7 obj-$(CONFIG_SEV_GUEST) += sev-guest/ 9 8 obj-$(CONFIG_INTEL_TDX_GUEST) += tdx-guest/ 10 9 obj-$(CONFIG_ARM_CCA_GUEST) += arm-cca-guest/ 10 + obj-$(CONFIG_TSM_GUEST) += guest/
+4 -4
drivers/virt/coco/arm-cca-guest/arm-cca-guest.c
··· 96 96 struct arm_cca_token_info info; 97 97 void *buf; 98 98 u8 *token __free(kvfree) = NULL; 99 - struct tsm_desc *desc = &report->desc; 99 + struct tsm_report_desc *desc = &report->desc; 100 100 101 101 if (desc->inblob_len < 32 || desc->inblob_len > 64) 102 102 return -EINVAL; ··· 181 181 return ret; 182 182 } 183 183 184 - static const struct tsm_ops arm_cca_tsm_ops = { 184 + static const struct tsm_report_ops arm_cca_tsm_ops = { 185 185 .name = KBUILD_MODNAME, 186 186 .report_new = arm_cca_report_new, 187 187 }; ··· 202 202 if (!is_realm_world()) 203 203 return -ENODEV; 204 204 205 - ret = tsm_register(&arm_cca_tsm_ops, NULL); 205 + ret = tsm_report_register(&arm_cca_tsm_ops, NULL); 206 206 if (ret < 0) 207 207 pr_err("Error %d registering with TSM\n", ret); 208 208 ··· 216 216 */ 217 217 static void __exit arm_cca_guest_exit(void) 218 218 { 219 - tsm_unregister(&arm_cca_tsm_ops); 219 + tsm_report_unregister(&arm_cca_tsm_ops); 220 220 } 221 221 module_exit(arm_cca_guest_exit); 222 222
+17
drivers/virt/coco/guest/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + # 3 + # Confidential computing shared guest collateral 4 + # 5 + config TSM_GUEST 6 + bool 7 + 8 + config TSM_REPORTS 9 + select TSM_GUEST 10 + select CONFIGFS_FS 11 + tristate 12 + 13 + config TSM_MEASUREMENTS 14 + select TSM_GUEST 15 + select CRYPTO_HASH_INFO 16 + select CRYPTO 17 + bool
+4
drivers/virt/coco/guest/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + obj-$(CONFIG_TSM_REPORTS) += tsm_report.o 3 + tsm_report-y := report.o 4 + obj-$(CONFIG_TSM_MEASUREMENTS) += tsm-mr.o
+251
drivers/virt/coco/guest/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 **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 + struct bin_attribute *bap = &ctx->mrs[i]; 191 + 192 + sysfs_bin_attr_init(bap); 193 + 194 + if (tm->mrs[i].mr_flags & TSM_MR_F_NOHASH) 195 + bap->attr.name = tm->mrs[i].mr_name; 196 + else if (name < end) { 197 + bap->attr.name = name; 198 + name += snprintf(name, end - name, "%s:%s", 199 + tm->mrs[i].mr_name, 200 + hash_algo_name[tm->mrs[i].mr_hash]); 201 + ++name; 202 + } else 203 + return ERR_PTR(-EINVAL); 204 + 205 + /* check for duplicated MR definitions */ 206 + for (size_t j = 0; j < i; ++j) 207 + if (!strcmp(bap->attr.name, attrs[j]->attr.name)) 208 + return ERR_PTR(-EINVAL); 209 + 210 + if (tm->mrs[i].mr_flags & TSM_MR_F_READABLE) { 211 + bap->attr.mode |= 0444; 212 + bap->read_new = tm_digest_read; 213 + } 214 + 215 + if (tm->mrs[i].mr_flags & TSM_MR_F_WRITABLE) { 216 + bap->attr.mode |= 0200; 217 + bap->write_new = tm_digest_write; 218 + } 219 + 220 + bap->size = tm->mrs[i].mr_size; 221 + bap->private = ctx; 222 + 223 + attrs[i] = bap; 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_new); 248 + kfree(container_of(attr_grp, struct tm_context, agrp)); 249 + } 250 + } 251 + EXPORT_SYMBOL_GPL(tsm_mr_free_attribute_group);
+6 -6
drivers/virt/coco/sev-guest/sev-guest.c
··· 346 346 static int sev_svsm_report_new(struct tsm_report *report, void *data) 347 347 { 348 348 unsigned int rep_len, man_len, certs_len; 349 - struct tsm_desc *desc = &report->desc; 349 + struct tsm_report_desc *desc = &report->desc; 350 350 struct svsm_attest_call ac = {}; 351 351 unsigned int retry_count; 352 352 void *rep, *man, *certs; ··· 481 481 static int sev_report_new(struct tsm_report *report, void *data) 482 482 { 483 483 struct snp_msg_cert_entry *cert_table; 484 - struct tsm_desc *desc = &report->desc; 484 + struct tsm_report_desc *desc = &report->desc; 485 485 struct snp_guest_dev *snp_dev = data; 486 486 struct snp_msg_report_resp_hdr hdr; 487 487 const u32 report_size = SZ_4K; ··· 610 610 return false; 611 611 } 612 612 613 - static struct tsm_ops sev_tsm_ops = { 613 + static struct tsm_report_ops sev_tsm_report_ops = { 614 614 .name = KBUILD_MODNAME, 615 615 .report_new = sev_report_new, 616 616 .report_attr_visible = sev_report_attr_visible, ··· 619 619 620 620 static void unregister_sev_tsm(void *data) 621 621 { 622 - tsm_unregister(&sev_tsm_ops); 622 + tsm_report_unregister(&sev_tsm_report_ops); 623 623 } 624 624 625 625 static int __init sev_guest_probe(struct platform_device *pdev) ··· 656 656 misc->fops = &snp_guest_fops; 657 657 658 658 /* Set the privlevel_floor attribute based on the vmpck_id */ 659 - sev_tsm_ops.privlevel_floor = mdesc->vmpck_id; 659 + sev_tsm_report_ops.privlevel_floor = mdesc->vmpck_id; 660 660 661 - ret = tsm_register(&sev_tsm_ops, snp_dev); 661 + ret = tsm_report_register(&sev_tsm_report_ops, snp_dev); 662 662 if (ret) 663 663 goto e_msg_init; 664 664
+1
drivers/virt/coco/tdx-guest/Kconfig
··· 2 2 tristate "TDX Guest driver" 3 3 depends on INTEL_TDX_GUEST 4 4 select TSM_REPORTS 5 + select TSM_MEASUREMENTS 5 6 help 6 7 The driver provides userspace interface to communicate with 7 8 the TDX module to request the TDX guest details like attestation
+175 -84
drivers/virt/coco/tdx-guest/tdx-guest.c
··· 5 5 * Copyright (C) 2022 Intel Corporation 6 6 */ 7 7 8 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 + 8 10 #include <linux/kernel.h> 9 11 #include <linux/miscdevice.h> 10 12 #include <linux/mm.h> ··· 17 15 #include <linux/set_memory.h> 18 16 #include <linux/io.h> 19 17 #include <linux/delay.h> 18 + #include <linux/sockptr.h> 20 19 #include <linux/tsm.h> 21 - #include <linux/sizes.h> 20 + #include <linux/tsm-mr.h> 22 21 23 22 #include <uapi/linux/tdx-guest.h> 24 23 25 24 #include <asm/cpu_device_id.h> 26 25 #include <asm/tdx.h> 26 + 27 + /* TDREPORT buffer */ 28 + static u8 *tdx_report_buf; 29 + 30 + /* Lock to serialize TDG.MR.REPORT and TDG.MR.RTMR.EXTEND TDCALLs */ 31 + static DEFINE_MUTEX(mr_lock); 32 + 33 + /* TDREPORT fields */ 34 + enum { 35 + TDREPORT_reportdata = 128, 36 + TDREPORT_tee_tcb_info = 256, 37 + TDREPORT_tdinfo = TDREPORT_tee_tcb_info + 256, 38 + TDREPORT_attributes = TDREPORT_tdinfo, 39 + TDREPORT_xfam = TDREPORT_attributes + sizeof(u64), 40 + TDREPORT_mrtd = TDREPORT_xfam + sizeof(u64), 41 + TDREPORT_mrconfigid = TDREPORT_mrtd + SHA384_DIGEST_SIZE, 42 + TDREPORT_mrowner = TDREPORT_mrconfigid + SHA384_DIGEST_SIZE, 43 + TDREPORT_mrownerconfig = TDREPORT_mrowner + SHA384_DIGEST_SIZE, 44 + TDREPORT_rtmr0 = TDREPORT_mrownerconfig + SHA384_DIGEST_SIZE, 45 + TDREPORT_rtmr1 = TDREPORT_rtmr0 + SHA384_DIGEST_SIZE, 46 + TDREPORT_rtmr2 = TDREPORT_rtmr1 + SHA384_DIGEST_SIZE, 47 + TDREPORT_rtmr3 = TDREPORT_rtmr2 + SHA384_DIGEST_SIZE, 48 + TDREPORT_servtd_hash = TDREPORT_rtmr3 + SHA384_DIGEST_SIZE, 49 + }; 50 + 51 + static int tdx_do_report(sockptr_t data, sockptr_t tdreport) 52 + { 53 + scoped_cond_guard(mutex_intr, return -EINTR, &mr_lock) { 54 + u8 *reportdata = tdx_report_buf + TDREPORT_reportdata; 55 + int ret; 56 + 57 + if (!sockptr_is_null(data) && 58 + copy_from_sockptr(reportdata, data, TDX_REPORTDATA_LEN)) 59 + return -EFAULT; 60 + 61 + ret = tdx_mcall_get_report0(reportdata, tdx_report_buf); 62 + if (WARN_ONCE(ret, "tdx_mcall_get_report0() failed: %d", ret)) 63 + return ret; 64 + 65 + if (!sockptr_is_null(tdreport) && 66 + copy_to_sockptr(tdreport, tdx_report_buf, TDX_REPORT_LEN)) 67 + return -EFAULT; 68 + } 69 + return 0; 70 + } 71 + 72 + static int tdx_do_extend(u8 mr_ind, const u8 *data) 73 + { 74 + scoped_cond_guard(mutex_intr, return -EINTR, &mr_lock) { 75 + /* 76 + * TDX requires @extend_buf to be 64-byte aligned. 77 + * It's safe to use REPORTDATA buffer for that purpose because 78 + * tdx_mr_report/extend_lock() are mutually exclusive. 79 + */ 80 + u8 *extend_buf = tdx_report_buf + TDREPORT_reportdata; 81 + int ret; 82 + 83 + memcpy(extend_buf, data, SHA384_DIGEST_SIZE); 84 + 85 + ret = tdx_mcall_extend_rtmr(mr_ind, extend_buf); 86 + if (WARN_ONCE(ret, "tdx_mcall_extend_rtmr(%u) failed: %d", mr_ind, ret)) 87 + return ret; 88 + } 89 + return 0; 90 + } 91 + 92 + #define TDX_MR_(r) .mr_value = (void *)TDREPORT_##r, TSM_MR_(r, SHA384) 93 + static struct tsm_measurement_register tdx_mrs[] = { 94 + { TDX_MR_(rtmr0) | TSM_MR_F_RTMR }, 95 + { TDX_MR_(rtmr1) | TSM_MR_F_RTMR }, 96 + { TDX_MR_(rtmr2) | TSM_MR_F_RTMR }, 97 + { TDX_MR_(rtmr3) | TSM_MR_F_RTMR }, 98 + { TDX_MR_(mrtd) }, 99 + { TDX_MR_(mrconfigid) | TSM_MR_F_NOHASH }, 100 + { TDX_MR_(mrowner) | TSM_MR_F_NOHASH }, 101 + { TDX_MR_(mrownerconfig) | TSM_MR_F_NOHASH }, 102 + }; 103 + #undef TDX_MR_ 104 + 105 + static int tdx_mr_refresh(const struct tsm_measurements *tm) 106 + { 107 + return tdx_do_report(KERNEL_SOCKPTR(NULL), KERNEL_SOCKPTR(NULL)); 108 + } 109 + 110 + static int tdx_mr_extend(const struct tsm_measurements *tm, 111 + const struct tsm_measurement_register *mr, 112 + const u8 *data) 113 + { 114 + return tdx_do_extend(mr - tm->mrs, data); 115 + } 116 + 117 + static struct tsm_measurements tdx_measurements = { 118 + .mrs = tdx_mrs, 119 + .nr_mrs = ARRAY_SIZE(tdx_mrs), 120 + .refresh = tdx_mr_refresh, 121 + .write = tdx_mr_extend, 122 + }; 123 + 124 + static const struct attribute_group *tdx_mr_init(void) 125 + { 126 + const struct attribute_group *g; 127 + int rc; 128 + 129 + u8 *buf __free(kfree) = kzalloc(TDX_REPORT_LEN, GFP_KERNEL); 130 + if (!buf) 131 + return ERR_PTR(-ENOMEM); 132 + 133 + tdx_report_buf = buf; 134 + rc = tdx_mr_refresh(&tdx_measurements); 135 + if (rc) 136 + return ERR_PTR(rc); 137 + 138 + /* 139 + * @mr_value was initialized with the offset only, while the base 140 + * address is being added here. 141 + */ 142 + for (size_t i = 0; i < ARRAY_SIZE(tdx_mrs); ++i) 143 + *(long *)&tdx_mrs[i].mr_value += (long)buf; 144 + 145 + g = tsm_mr_create_attribute_group(&tdx_measurements); 146 + if (!IS_ERR(g)) 147 + tdx_report_buf = no_free_ptr(buf); 148 + 149 + return g; 150 + } 151 + 152 + static void tdx_mr_deinit(const struct attribute_group *mr_grp) 153 + { 154 + tsm_mr_free_attribute_group(mr_grp); 155 + kfree(tdx_report_buf); 156 + } 27 157 28 158 /* 29 159 * Intel's SGX QE implementation generally uses Quote size less ··· 202 68 203 69 static long tdx_get_report0(struct tdx_report_req __user *req) 204 70 { 205 - u8 *reportdata, *tdreport; 206 - long ret; 207 - 208 - reportdata = kmalloc(TDX_REPORTDATA_LEN, GFP_KERNEL); 209 - if (!reportdata) 210 - return -ENOMEM; 211 - 212 - tdreport = kzalloc(TDX_REPORT_LEN, GFP_KERNEL); 213 - if (!tdreport) { 214 - ret = -ENOMEM; 215 - goto out; 216 - } 217 - 218 - if (copy_from_user(reportdata, req->reportdata, TDX_REPORTDATA_LEN)) { 219 - ret = -EFAULT; 220 - goto out; 221 - } 222 - 223 - /* Generate TDREPORT0 using "TDG.MR.REPORT" TDCALL */ 224 - ret = tdx_mcall_get_report0(reportdata, tdreport); 225 - if (ret) 226 - goto out; 227 - 228 - if (copy_to_user(req->tdreport, tdreport, TDX_REPORT_LEN)) 229 - ret = -EFAULT; 230 - 231 - out: 232 - kfree(reportdata); 233 - kfree(tdreport); 234 - 235 - return ret; 71 + return tdx_do_report(USER_SOCKPTR(req->reportdata), 72 + USER_SOCKPTR(req->tdreport)); 236 73 } 237 74 238 75 static void free_quote_buf(void *buf) ··· 262 157 return (i == timeout) ? -ETIMEDOUT : 0; 263 158 } 264 159 265 - static int tdx_report_new(struct tsm_report *report, void *data) 160 + static int tdx_report_new_locked(struct tsm_report *report, void *data) 266 161 { 267 - u8 *buf, *reportdata = NULL, *tdreport = NULL; 162 + u8 *buf; 268 163 struct tdx_quote_buf *quote_buf = quote_data; 269 - struct tsm_desc *desc = &report->desc; 164 + struct tsm_report_desc *desc = &report->desc; 270 165 int ret; 271 166 u64 err; 272 - 273 - /* TODO: switch to guard(mutex_intr) */ 274 - if (mutex_lock_interruptible(&quote_lock)) 275 - return -EINTR; 276 167 277 168 /* 278 169 * If the previous request is timedout or interrupted, and the 279 170 * Quote buf status is still in GET_QUOTE_IN_FLIGHT (owned by 280 171 * VMM), don't permit any new request. 281 172 */ 282 - if (quote_buf->status == GET_QUOTE_IN_FLIGHT) { 283 - ret = -EBUSY; 284 - goto done; 285 - } 173 + if (quote_buf->status == GET_QUOTE_IN_FLIGHT) 174 + return -EBUSY; 286 175 287 - if (desc->inblob_len != TDX_REPORTDATA_LEN) { 288 - ret = -EINVAL; 289 - goto done; 290 - } 291 - 292 - reportdata = kmalloc(TDX_REPORTDATA_LEN, GFP_KERNEL); 293 - if (!reportdata) { 294 - ret = -ENOMEM; 295 - goto done; 296 - } 297 - 298 - tdreport = kzalloc(TDX_REPORT_LEN, GFP_KERNEL); 299 - if (!tdreport) { 300 - ret = -ENOMEM; 301 - goto done; 302 - } 303 - 304 - memcpy(reportdata, desc->inblob, desc->inblob_len); 305 - 306 - /* Generate TDREPORT0 using "TDG.MR.REPORT" TDCALL */ 307 - ret = tdx_mcall_get_report0(reportdata, tdreport); 308 - if (ret) { 309 - pr_err("GetReport call failed\n"); 310 - goto done; 311 - } 176 + if (desc->inblob_len != TDX_REPORTDATA_LEN) 177 + return -EINVAL; 312 178 313 179 memset(quote_data, 0, GET_QUOTE_BUF_SIZE); 314 180 ··· 287 211 quote_buf->version = GET_QUOTE_CMD_VER; 288 212 quote_buf->in_len = TDX_REPORT_LEN; 289 213 290 - memcpy(quote_buf->data, tdreport, TDX_REPORT_LEN); 214 + ret = tdx_do_report(KERNEL_SOCKPTR(desc->inblob), 215 + KERNEL_SOCKPTR(quote_buf->data)); 216 + if (ret) 217 + return ret; 291 218 292 219 err = tdx_hcall_get_quote(quote_data, GET_QUOTE_BUF_SIZE); 293 220 if (err) { 294 221 pr_err("GetQuote hypercall failed, status:%llx\n", err); 295 - ret = -EIO; 296 - goto done; 222 + return -EIO; 297 223 } 298 224 299 225 ret = wait_for_quote_completion(quote_buf, getquote_timeout); 300 226 if (ret) { 301 227 pr_err("GetQuote request timedout\n"); 302 - goto done; 228 + return ret; 303 229 } 304 230 305 231 buf = kvmemdup(quote_buf->data, quote_buf->out_len, GFP_KERNEL); 306 - if (!buf) { 307 - ret = -ENOMEM; 308 - goto done; 309 - } 232 + if (!buf) 233 + return -ENOMEM; 310 234 311 235 report->outblob = buf; 312 236 report->outblob_len = quote_buf->out_len; ··· 315 239 * TODO: parse the PEM-formatted cert chain out of the quote buffer when 316 240 * provided 317 241 */ 318 - done: 319 - mutex_unlock(&quote_lock); 320 - kfree(reportdata); 321 - kfree(tdreport); 322 242 323 243 return ret; 244 + } 245 + 246 + static int tdx_report_new(struct tsm_report *report, void *data) 247 + { 248 + scoped_cond_guard(mutex_intr, return -EINTR, &quote_lock) 249 + return tdx_report_new_locked(report, data); 324 250 } 325 251 326 252 static bool tdx_report_attr_visible(int n) ··· 363 285 .unlocked_ioctl = tdx_guest_ioctl, 364 286 }; 365 287 288 + static const struct attribute_group *tdx_attr_groups[] = { 289 + NULL, /* measurements */ 290 + NULL 291 + }; 292 + 366 293 static struct miscdevice tdx_misc_dev = { 367 294 .name = KBUILD_MODNAME, 368 295 .minor = MISC_DYNAMIC_MINOR, 369 296 .fops = &tdx_guest_fops, 297 + .groups = tdx_attr_groups, 370 298 }; 371 299 372 300 static const struct x86_cpu_id tdx_guest_ids[] = { ··· 381 297 }; 382 298 MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); 383 299 384 - static const struct tsm_ops tdx_tsm_ops = { 300 + static const struct tsm_report_ops tdx_tsm_ops = { 385 301 .name = KBUILD_MODNAME, 386 302 .report_new = tdx_report_new, 387 303 .report_attr_visible = tdx_report_attr_visible, ··· 395 311 if (!x86_match_cpu(tdx_guest_ids)) 396 312 return -ENODEV; 397 313 314 + tdx_attr_groups[0] = tdx_mr_init(); 315 + if (IS_ERR(tdx_attr_groups[0])) 316 + return PTR_ERR(tdx_attr_groups[0]); 317 + 398 318 ret = misc_register(&tdx_misc_dev); 399 319 if (ret) 400 - return ret; 320 + goto deinit_mr; 401 321 402 322 quote_data = alloc_quote_buf(); 403 323 if (!quote_data) { ··· 410 322 goto free_misc; 411 323 } 412 324 413 - ret = tsm_register(&tdx_tsm_ops, NULL); 325 + ret = tsm_report_register(&tdx_tsm_ops, NULL); 414 326 if (ret) 415 327 goto free_quote; 416 328 ··· 420 332 free_quote_buf(quote_data); 421 333 free_misc: 422 334 misc_deregister(&tdx_misc_dev); 335 + deinit_mr: 336 + tdx_mr_deinit(tdx_attr_groups[0]); 423 337 424 338 return ret; 425 339 } ··· 429 339 430 340 static void __exit tdx_guest_exit(void) 431 341 { 432 - tsm_unregister(&tdx_tsm_ops); 342 + tsm_report_unregister(&tdx_tsm_ops); 433 343 free_quote_buf(quote_data); 434 344 misc_deregister(&tdx_misc_dev); 345 + tdx_mr_deinit(tdx_attr_groups[0]); 435 346 } 436 347 module_exit(tdx_guest_exit); 437 348
+45 -18
drivers/virt/coco/tsm.c drivers/virt/coco/guest/report.c
··· 13 13 #include <linux/configfs.h> 14 14 15 15 static struct tsm_provider { 16 - const struct tsm_ops *ops; 16 + const struct tsm_report_ops *ops; 17 17 void *data; 18 + atomic_t count; 18 19 } provider; 19 20 static DECLARE_RWSEM(tsm_rwsem); 20 21 ··· 93 92 if (rc) 94 93 return rc; 95 94 95 + guard(rwsem_write)(&tsm_rwsem); 96 + if (!provider.ops) 97 + return -ENXIO; 98 + 96 99 /* 97 100 * The valid privilege levels that a TSM might accept, if it accepts a 98 101 * privilege level setting at all, are a max of TSM_PRIVLEVEL_MAX (see 99 102 * SEV-SNP GHCB) and a minimum of a TSM selected floor value no less 100 103 * than 0. 101 104 */ 102 - if (provider.ops->privlevel_floor > val || val > TSM_PRIVLEVEL_MAX) 105 + if (provider.ops->privlevel_floor > val || val > TSM_REPORT_PRIVLEVEL_MAX) 103 106 return -EINVAL; 104 107 105 - guard(rwsem_write)(&tsm_rwsem); 106 108 rc = try_advance_write_generation(report); 107 109 if (rc) 108 110 return rc; ··· 119 115 char *buf) 120 116 { 121 117 guard(rwsem_read)(&tsm_rwsem); 118 + 119 + if (!provider.ops) 120 + return -ENXIO; 121 + 122 122 return sysfs_emit(buf, "%u\n", provider.ops->privlevel_floor); 123 123 } 124 124 CONFIGFS_ATTR_RO(tsm_report_, privlevel_floor); ··· 210 202 memcpy(report->desc.inblob, buf, count); 211 203 return count; 212 204 } 213 - CONFIGFS_BIN_ATTR_WO(tsm_report_, inblob, NULL, TSM_INBLOB_MAX); 205 + CONFIGFS_BIN_ATTR_WO(tsm_report_, inblob, NULL, TSM_REPORT_INBLOB_MAX); 214 206 215 207 static ssize_t tsm_report_generation_show(struct config_item *cfg, char *buf) 216 208 { ··· 225 217 static ssize_t tsm_report_provider_show(struct config_item *cfg, char *buf) 226 218 { 227 219 guard(rwsem_read)(&tsm_rwsem); 220 + if (!provider.ops) 221 + return -ENXIO; 222 + 228 223 return sysfs_emit(buf, "%s\n", provider.ops->name); 229 224 } 230 225 CONFIGFS_ATTR_RO(tsm_report_, provider); ··· 283 272 size_t count, enum tsm_data_select select) 284 273 { 285 274 struct tsm_report_state *state = to_state(report); 286 - const struct tsm_ops *ops; 275 + const struct tsm_report_ops *ops; 287 276 ssize_t rc; 288 277 289 278 /* try to read from the existing report if present and valid... */ ··· 295 284 guard(rwsem_write)(&tsm_rwsem); 296 285 ops = provider.ops; 297 286 if (!ops) 298 - return -ENOTTY; 287 + return -ENXIO; 299 288 if (!report->desc.inblob_len) 300 289 return -EINVAL; 301 290 ··· 325 314 326 315 return tsm_report_read(report, buf, count, TSM_REPORT); 327 316 } 328 - CONFIGFS_BIN_ATTR_RO(tsm_report_, outblob, NULL, TSM_OUTBLOB_MAX); 317 + CONFIGFS_BIN_ATTR_RO(tsm_report_, outblob, NULL, TSM_REPORT_OUTBLOB_MAX); 329 318 330 319 static ssize_t tsm_report_auxblob_read(struct config_item *cfg, void *buf, 331 320 size_t count) ··· 334 323 335 324 return tsm_report_read(report, buf, count, TSM_CERTS); 336 325 } 337 - CONFIGFS_BIN_ATTR_RO(tsm_report_, auxblob, NULL, TSM_OUTBLOB_MAX); 326 + CONFIGFS_BIN_ATTR_RO(tsm_report_, auxblob, NULL, TSM_REPORT_OUTBLOB_MAX); 338 327 339 328 static ssize_t tsm_report_manifestblob_read(struct config_item *cfg, void *buf, 340 329 size_t count) ··· 343 332 344 333 return tsm_report_read(report, buf, count, TSM_MANIFEST); 345 334 } 346 - CONFIGFS_BIN_ATTR_RO(tsm_report_, manifestblob, NULL, TSM_OUTBLOB_MAX); 335 + CONFIGFS_BIN_ATTR_RO(tsm_report_, manifestblob, NULL, TSM_REPORT_OUTBLOB_MAX); 347 336 348 337 static struct configfs_attribute *tsm_report_attrs[] = { 349 338 [TSM_REPORT_GENERATION] = &tsm_report_attr_generation, ··· 432 421 if (!state) 433 422 return ERR_PTR(-ENOMEM); 434 423 424 + atomic_inc(&provider.count); 435 425 config_item_init_type_name(&state->cfg, name, &tsm_report_type); 436 426 return &state->cfg; 437 427 } 438 428 429 + static void tsm_report_drop_item(struct config_group *group, struct config_item *item) 430 + { 431 + config_item_put(item); 432 + atomic_dec(&provider.count); 433 + } 434 + 439 435 static struct configfs_group_operations tsm_report_group_ops = { 440 436 .make_item = tsm_report_make_item, 437 + .drop_item = tsm_report_drop_item, 441 438 }; 442 439 443 440 static const struct config_item_type tsm_reports_type = { ··· 467 448 .su_mutex = __MUTEX_INITIALIZER(tsm_configfs.su_mutex), 468 449 }; 469 450 470 - int tsm_register(const struct tsm_ops *ops, void *priv) 451 + int tsm_report_register(const struct tsm_report_ops *ops, void *priv) 471 452 { 472 - const struct tsm_ops *conflict; 453 + const struct tsm_report_ops *conflict; 473 454 474 455 guard(rwsem_write)(&tsm_rwsem); 475 456 conflict = provider.ops; ··· 478 459 return -EBUSY; 479 460 } 480 461 462 + if (atomic_read(&provider.count)) { 463 + pr_err("configfs/tsm/report not empty\n"); 464 + return -EBUSY; 465 + } 466 + 481 467 provider.ops = ops; 482 468 provider.data = priv; 483 469 return 0; 484 470 } 485 - EXPORT_SYMBOL_GPL(tsm_register); 471 + EXPORT_SYMBOL_GPL(tsm_report_register); 486 472 487 - int tsm_unregister(const struct tsm_ops *ops) 473 + int tsm_report_unregister(const struct tsm_report_ops *ops) 488 474 { 489 475 guard(rwsem_write)(&tsm_rwsem); 490 476 if (ops != provider.ops) 491 477 return -EBUSY; 478 + if (atomic_read(&provider.count)) 479 + pr_warn("\"%s\" unregistered with items present in configfs/tsm/report\n", 480 + provider.ops->name); 492 481 provider.ops = NULL; 493 482 provider.data = NULL; 494 483 return 0; 495 484 } 496 - EXPORT_SYMBOL_GPL(tsm_unregister); 485 + EXPORT_SYMBOL_GPL(tsm_report_unregister); 497 486 498 487 static struct config_group *tsm_report_group; 499 488 500 - static int __init tsm_init(void) 489 + static int __init tsm_report_init(void) 501 490 { 502 491 struct config_group *root = &tsm_configfs.su_group; 503 492 struct config_group *tsm; ··· 526 499 527 500 return 0; 528 501 } 529 - module_init(tsm_init); 502 + module_init(tsm_report_init); 530 503 531 - static void __exit tsm_exit(void) 504 + static void __exit tsm_report_exit(void) 532 505 { 533 506 configfs_unregister_default_group(tsm_report_group); 534 507 configfs_unregister_subsystem(&tsm_configfs); 535 508 } 536 - module_exit(tsm_exit); 509 + module_exit(tsm_report_exit); 537 510 538 511 MODULE_LICENSE("GPL"); 539 512 MODULE_DESCRIPTION("Provide Trusted Security Module attestation reports via configfs");
+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
+11 -11
include/linux/tsm.h
··· 6 6 #include <linux/types.h> 7 7 #include <linux/uuid.h> 8 8 9 - #define TSM_INBLOB_MAX 64 10 - #define TSM_OUTBLOB_MAX SZ_32K 9 + #define TSM_REPORT_INBLOB_MAX 64 10 + #define TSM_REPORT_OUTBLOB_MAX SZ_32K 11 11 12 12 /* 13 13 * Privilege level is a nested permission concept to allow confidential 14 14 * guests to partition address space, 4-levels are supported. 15 15 */ 16 - #define TSM_PRIVLEVEL_MAX 3 16 + #define TSM_REPORT_PRIVLEVEL_MAX 3 17 17 18 18 /** 19 - * struct tsm_desc - option descriptor for generating tsm report blobs 19 + * struct tsm_report_desc - option descriptor for generating tsm report blobs 20 20 * @privlevel: optional privilege level to associate with @outblob 21 21 * @inblob_len: sizeof @inblob 22 22 * @inblob: arbitrary input data ··· 24 24 * @service_guid: optional service-provider service guid to attest 25 25 * @service_manifest_version: optional service-provider service manifest version requested 26 26 */ 27 - struct tsm_desc { 27 + struct tsm_report_desc { 28 28 unsigned int privlevel; 29 29 size_t inblob_len; 30 - u8 inblob[TSM_INBLOB_MAX]; 30 + u8 inblob[TSM_REPORT_INBLOB_MAX]; 31 31 char *service_provider; 32 32 guid_t service_guid; 33 33 unsigned int service_manifest_version; ··· 44 44 * @manifestblob: (optional) manifest data associated with the report 45 45 */ 46 46 struct tsm_report { 47 - struct tsm_desc desc; 47 + struct tsm_report_desc desc; 48 48 size_t outblob_len; 49 49 u8 *outblob; 50 50 size_t auxblob_len; ··· 88 88 }; 89 89 90 90 /** 91 - * struct tsm_ops - attributes and operations for tsm instances 91 + * struct tsm_report_ops - attributes and operations for tsm_report instances 92 92 * @name: tsm id reflected in /sys/kernel/config/tsm/report/$report/provider 93 93 * @privlevel_floor: convey base privlevel for nested scenarios 94 94 * @report_new: Populate @report with the report blob and auxblob ··· 99 99 * Implementation specific ops, only one is expected to be registered at 100 100 * a time i.e. only one of "sev-guest", "tdx-guest", etc. 101 101 */ 102 - struct tsm_ops { 102 + struct tsm_report_ops { 103 103 const char *name; 104 104 unsigned int privlevel_floor; 105 105 int (*report_new)(struct tsm_report *report, void *data); ··· 107 107 bool (*report_bin_attr_visible)(int n); 108 108 }; 109 109 110 - int tsm_register(const struct tsm_ops *ops, void *priv); 111 - int tsm_unregister(const struct tsm_ops *ops); 110 + int tsm_report_register(const struct tsm_report_ops *ops, void *priv); 111 + int tsm_report_unregister(const struct tsm_report_ops *ops); 112 112 #endif /* __TSM_H */
+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>
+11
samples/Kconfig
··· 184 184 bool "Timer sample" 185 185 depends on CC_CAN_LINK && HEADERS_INSTALL 186 186 187 + config SAMPLE_TSM_MR 188 + tristate "TSM measurement sample" 189 + select TSM_MEASUREMENTS 190 + select VIRT_DRIVERS 191 + help 192 + Build a sample module that emulates MRs (Measurement Registers) and 193 + exposes them to user mode applications through the TSM sysfs 194 + interface (/sys/class/misc/tsm_mr_sample/emulated_mr/). 195 + 196 + The module name will be tsm-mr-sample when built as a module. 197 + 187 198 config SAMPLE_UHID 188 199 bool "UHID sample" 189 200 depends on CC_CAN_LINK && HEADERS_INSTALL
+1
samples/Makefile
··· 43 43 obj-$(CONFIG_SAMPLE_DAMON_WSSE) += damon/ 44 44 obj-$(CONFIG_SAMPLE_DAMON_PRCL) += damon/ 45 45 obj-$(CONFIG_SAMPLE_HUNG_TASK) += hung_task/ 46 + obj-$(CONFIG_SAMPLE_TSM_MR) += tsm-mr/
+2
samples/tsm-mr/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + obj-$(CONFIG_SAMPLE_TSM_MR) += tsm_mr_sample.o
+131
samples/tsm-mr/tsm_mr_sample.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* Copyright(c) 2024-2005 Intel Corporation. All rights reserved. */ 3 + 4 + #define pr_fmt(x) KBUILD_MODNAME ": " x 5 + 6 + #include <linux/module.h> 7 + #include <linux/tsm-mr.h> 8 + #include <linux/miscdevice.h> 9 + #include <crypto/hash.h> 10 + 11 + static struct { 12 + u8 static_mr[SHA384_DIGEST_SIZE]; 13 + u8 config_mr[SHA512_DIGEST_SIZE]; 14 + u8 rtmr0[SHA256_DIGEST_SIZE]; 15 + u8 rtmr1[SHA384_DIGEST_SIZE]; 16 + u8 report_digest[SHA512_DIGEST_SIZE]; 17 + } sample_report = { 18 + .static_mr = "static_mr", 19 + .config_mr = "config_mr", 20 + .rtmr0 = "rtmr0", 21 + .rtmr1 = "rtmr1", 22 + }; 23 + 24 + static int sample_report_refresh(const struct tsm_measurements *tm) 25 + { 26 + struct crypto_shash *tfm; 27 + int rc; 28 + 29 + tfm = crypto_alloc_shash(hash_algo_name[HASH_ALGO_SHA512], 0, 0); 30 + if (IS_ERR(tfm)) { 31 + pr_err("crypto_alloc_shash failed: %ld\n", PTR_ERR(tfm)); 32 + return PTR_ERR(tfm); 33 + } 34 + 35 + rc = crypto_shash_tfm_digest(tfm, (u8 *)&sample_report, 36 + offsetof(typeof(sample_report), 37 + report_digest), 38 + sample_report.report_digest); 39 + crypto_free_shash(tfm); 40 + if (rc) 41 + pr_err("crypto_shash_tfm_digest failed: %d\n", rc); 42 + return rc; 43 + } 44 + 45 + static int sample_report_extend_mr(const struct tsm_measurements *tm, 46 + const struct tsm_measurement_register *mr, 47 + const u8 *data) 48 + { 49 + SHASH_DESC_ON_STACK(desc, 0); 50 + int rc; 51 + 52 + desc->tfm = crypto_alloc_shash(hash_algo_name[mr->mr_hash], 0, 0); 53 + if (IS_ERR(desc->tfm)) { 54 + pr_err("crypto_alloc_shash failed: %ld\n", PTR_ERR(desc->tfm)); 55 + return PTR_ERR(desc->tfm); 56 + } 57 + 58 + rc = crypto_shash_init(desc); 59 + if (!rc) 60 + rc = crypto_shash_update(desc, mr->mr_value, mr->mr_size); 61 + if (!rc) 62 + rc = crypto_shash_finup(desc, data, mr->mr_size, mr->mr_value); 63 + crypto_free_shash(desc->tfm); 64 + if (rc) 65 + pr_err("SHA calculation failed: %d\n", rc); 66 + return rc; 67 + } 68 + 69 + #define MR_(mr, hash) .mr_value = &sample_report.mr, TSM_MR_(mr, hash) 70 + static const struct tsm_measurement_register sample_mrs[] = { 71 + /* static MR, read-only */ 72 + { MR_(static_mr, SHA384) }, 73 + /* config MR, read-only */ 74 + { MR_(config_mr, SHA512) | TSM_MR_F_NOHASH }, 75 + /* RTMR, direct extension prohibited */ 76 + { MR_(rtmr0, SHA256) | TSM_MR_F_LIVE }, 77 + /* RTMR, direct extension allowed */ 78 + { MR_(rtmr1, SHA384) | TSM_MR_F_RTMR }, 79 + /* RTMR, crypto agile, alaised to rtmr0 and rtmr1, respectively */ 80 + { .mr_value = &sample_report.rtmr0, 81 + TSM_MR_(rtmr_crypto_agile, SHA256) | TSM_MR_F_RTMR }, 82 + { .mr_value = &sample_report.rtmr1, 83 + TSM_MR_(rtmr_crypto_agile, SHA384) | TSM_MR_F_RTMR }, 84 + /* sha512 digest of the whole structure */ 85 + { MR_(report_digest, SHA512) | TSM_MR_F_LIVE }, 86 + }; 87 + #undef MR_ 88 + 89 + static struct tsm_measurements sample_tm = { 90 + .mrs = sample_mrs, 91 + .nr_mrs = ARRAY_SIZE(sample_mrs), 92 + .refresh = sample_report_refresh, 93 + .write = sample_report_extend_mr, 94 + }; 95 + 96 + static const struct attribute_group *sample_groups[] = { 97 + NULL, 98 + NULL, 99 + }; 100 + 101 + static struct miscdevice sample_misc_dev = { 102 + .name = KBUILD_MODNAME, 103 + .minor = MISC_DYNAMIC_MINOR, 104 + .groups = sample_groups, 105 + }; 106 + 107 + static int __init tsm_mr_sample_init(void) 108 + { 109 + int rc; 110 + 111 + sample_groups[0] = tsm_mr_create_attribute_group(&sample_tm); 112 + if (IS_ERR(sample_groups[0])) 113 + return PTR_ERR(sample_groups[0]); 114 + 115 + rc = misc_register(&sample_misc_dev); 116 + if (rc) 117 + tsm_mr_free_attribute_group(sample_groups[0]); 118 + return rc; 119 + } 120 + 121 + static void __exit tsm_mr_sample_exit(void) 122 + { 123 + misc_deregister(&sample_misc_dev); 124 + tsm_mr_free_attribute_group(sample_groups[0]); 125 + } 126 + 127 + module_init(tsm_mr_sample_init); 128 + module_exit(tsm_mr_sample_exit); 129 + 130 + MODULE_LICENSE("GPL"); 131 + MODULE_DESCRIPTION("Sample module using tsm-mr to expose emulated MRs");