Attestation is used to verify the TDX guest trustworthiness to other entities before provisioning secrets to the guest. For example, a key server may request for attestation before releasing the encryption keys to mount the encrypted rootfs or secondary drive.
During the TDX guest launch, the initial contents (including the firmware image) and configuration of the guest are recorded by the Intel TDX module in build time measurement register (MRTD). After TDX guest is created, run-time measurement registers (RTMRs) can be used by the guest software to extend the measurements. TDX supports 4 RTMR registers, and TDG.MR.RTMR.EXTEND TDCALL is used to update the RTMR registers securely. RTMRs are mainly used to record measurements related to sections like the kernel image, command line parameters, initrd, ACPI tables, firmware data, configuration firmware volume (CFV) of TDVF, etc. For complete details, please refer to TDX Virtual Firmware design specification, sec titled "TD Measurement".
At TDX guest runtime, the Intel TDX module reuses the Intel SGX attestation infrastructure to provide support for attesting to these measurements as described below.
The attestation process consists of two steps: TDREPORT generation and Quote generation.
TDREPORT (TDREPORT_STRUCT) is a fixed-size data structure generated by the TDX module which contains guest-specific information (such as build and boot measurements), platform security version, and the MAC to protect the integrity of the TDREPORT. The guest kernel uses TDCALL[TDG.MR.REPORT] to get the TDREPORT from the TDX module. A user-provided 64-Byte REPORTDATA is used as input and included in the TDREPORT. Typically it can be some nonce provided by attestation service so the TDREPORT can be verified uniquely. More details about the TDREPORT can be found in Intel TDX Module specification, section titled "TDG.MR.REPORT Leaf".
TDREPORT by design can only be verified on the local platform as the MAC key is bound to the platform. To support remote verification of the TDREPORT, TDX leverages Intel SGX Quote Enclave (QE) to verify the TDREPORT locally and convert it to a remote verifiable Quote.
After getting the TDREPORT, the second step of the attestation process is to send it to the QE to generate the Quote. TDX doesn't support SGX inside the guest, so the QE can be deployed in the host, or in another legacy VM with SGX support. QE checks the integrity of TDREPORT and if it is valid, a certified quote signing key is used to sign the Quote. How to send the TDREPORT to QE and receive the Quote is implementation and deployment specific.
Implement a basic guest misc driver to allow userspace to get the TDREPORT. After getting TDREPORT, the userspace attestation software can choose whatever communication channel available (i.e. vsock or hypercall) to send the TDREPORT to QE and receive the Quote.
Also note that explicit access permissions are not enforced in this driver because the quote and measurements are not a secret. However the access permissions of the device node can be used to set any desired access policy. The udev default is usually root access only.
Operations like getting TDREPORT or Quote generation involves sending a blob of data as input and getting another blob of data as output. It was considered to use a sysfs interface for this, but it doesn't fit well into the standard sysfs model for configuring values. It would be possible to do read/write on files, but it would need multiple file descriptors, which would be somewhat messy. IOCTLs seems to be the best fitting and simplest model for this use case. This is similar to AMD SEV platform, which also uses IOCTL interface to support attestation.
Any distribution enabling TDX is also expected to need attestation. So enable it by default with TDX guest support.
Reviewed-by: Tony Luck tony.luck@intel.com Reviewed-by: Andi Kleen ak@linux.intel.com Acked-by: Kirill A. Shutemov kirill.shutemov@linux.intel.com Acked-by: Kai Huang kai.huang@intel.com Acked-by: Wander Lairson Costa wander@redhat.com Signed-off-by: Kuppuswamy Sathyanarayanan sathyanarayanan.kuppuswamy@linux.intel.com ---
Changes since v10: * Replaced TD/TD Guest usage with TDX Guest or Guest. * Removed unnecessary comments. * Added more validation to user input in tdx_get_report(). * Used u64_to_user_ptr when reading user u64 pointers. * Fixed commit log as per review comments.
Changes since v9: * Dropped the cover letter. Since this patch set only adds TDREPORT support, the commit log itself has all the required details. * Dropped the Quote support and event IRQ support as per Dave's review suggestion. * Dropped attest.c and moved its contents to tdx.c * Updated commit log and comments to reflect latest changes.
Changes since v8: * Please refer to https://lore.kernel.org/all/ \ 20220728034420.648314-1-sathyanarayanan.kuppuswamy@linux.intel.com/
arch/x86/coco/tdx/tdx.c | 114 ++++++++++++++++++++++++++++++++ arch/x86/include/uapi/asm/tdx.h | 51 ++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 arch/x86/include/uapi/asm/tdx.h
diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index 928dcf7a20d9..0888bdf93a4e 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -5,16 +5,21 @@ #define pr_fmt(fmt) "tdx: " fmt
#include <linux/cpufeature.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <linux/io.h> #include <asm/coco.h> #include <asm/tdx.h> #include <asm/vmx.h> #include <asm/insn.h> #include <asm/insn-eval.h> #include <asm/pgtable.h> +#include <uapi/asm/tdx.h>
/* TDX module Call Leaf IDs */ #define TDX_GET_INFO 1 #define TDX_GET_VEINFO 3 +#define TDX_GET_REPORT 4 #define TDX_ACCEPT_PAGE 6
/* TDX hypercall Leaf IDs */ @@ -34,6 +39,10 @@ #define VE_GET_PORT_NUM(e) ((e) >> 16) #define VE_IS_IO_STRING(e) ((e) & BIT(4))
+#define DRIVER_NAME "tdx-guest" + +static struct miscdevice tdx_misc_dev; + /* * Wrapper for standard use of __tdx_hypercall with no output aside from * return code. @@ -775,3 +784,108 @@ void __init tdx_early_init(void)
pr_info("Guest detected\n"); } + +static long tdx_get_report(void __user *argp) +{ + u8 *reportdata, *tdreport; + struct tdx_report_req req; + long ret; + + if (copy_from_user(&req, argp, sizeof(req))) + return -EFAULT; + + /* + * Per TDX Module 1.0 specification, section titled + * "TDG.MR.REPORT", REPORTDATA length is fixed as + * TDX_REPORTDATA_LEN, TDREPORT length is fixed as + * TDX_REPORT_LEN, and TDREPORT subtype is fixed as + * 0. Also check for valid user pointers. + */ + if (!req.reportdata || !req.tdreport || req.subtype || + req.rpd_len != TDX_REPORTDATA_LEN || + req.tdr_len != TDX_REPORT_LEN) + return -EINVAL; + + reportdata = kmalloc(req.rpd_len, GFP_KERNEL); + if (!reportdata) + return -ENOMEM; + + tdreport = kzalloc(req.tdr_len, GFP_KERNEL); + if (!tdreport) { + kfree(reportdata); + return -ENOMEM; + } + + if (copy_from_user(reportdata, u64_to_user_ptr(req.reportdata), + req.rpd_len)) { + ret = -EFAULT; + goto out; + } + + /* + * Generate TDREPORT using "TDG.MR.REPORT" TDCALL. + * + * Get the TDREPORT using REPORTDATA as input. Refer to + * section 22.3.3 TDG.MR.REPORT leaf in the TDX Module 1.0 + * Specification for detailed information. + */ + ret = __tdx_module_call(TDX_GET_REPORT, virt_to_phys(tdreport), + virt_to_phys(reportdata), req.subtype, + 0, NULL); + if (ret) { + ret = -EIO; + goto out; + } + + if (copy_to_user(u64_to_user_ptr(req.tdreport), tdreport, req.tdr_len)) + ret = -EFAULT; + +out: + kfree(reportdata); + kfree(tdreport); + return ret; +} +static long tdx_guest_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + long ret = -EINVAL; + + switch (cmd) { + case TDX_CMD_GET_REPORT: + ret = tdx_get_report(argp); + break; + default: + pr_debug("cmd %d not supported\n", cmd); + break; + } + + return ret; +} + +static const struct file_operations tdx_guest_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = tdx_guest_ioctl, + .llseek = no_llseek, +}; + +static int __init tdx_guest_init(void) +{ + int ret; + + if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) + return -EIO; + + tdx_misc_dev.name = DRIVER_NAME; + tdx_misc_dev.minor = MISC_DYNAMIC_MINOR; + tdx_misc_dev.fops = &tdx_guest_fops; + + ret = misc_register(&tdx_misc_dev); + if (ret) { + pr_err("misc device registration failed\n"); + return ret; + } + + return 0; +} +device_initcall(tdx_guest_init) diff --git a/arch/x86/include/uapi/asm/tdx.h b/arch/x86/include/uapi/asm/tdx.h new file mode 100644 index 000000000000..c1667b20fe20 --- /dev/null +++ b/arch/x86/include/uapi/asm/tdx.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_ASM_X86_TDX_H +#define _UAPI_ASM_X86_TDX_H + +#include <linux/types.h> +#include <linux/ioctl.h> + +/* Length of the REPORTDATA used in TDG.MR.REPORT TDCALL */ +#define TDX_REPORTDATA_LEN 64 + +/* Length of TDREPORT used in TDG.MR.REPORT TDCALL */ +#define TDX_REPORT_LEN 1024 + +/** + * struct tdx_report_req: Get TDREPORT using REPORTDATA as input. + * + * @subtype : Subtype of TDREPORT (fixed as 0 by TDX Module + * specification, but added a parameter to handle + * future extension). + * @reportdata : User-defined REPORTDATA to be included into + * TDREPORT. Typically it can be some nonce + * provided by attestation service, so the + * generated TDREPORT can be uniquely verified. + * @rpd_len : Length of the REPORTDATA (fixed as 64 bytes by + * the TDX Module specification, but parameter is + * added to handle future extension). + * @tdreport : TDREPORT output from TDCALL[TDG.MR.REPORT]. + * @tdr_len : Length of the TDREPORT (fixed as 1024 bytes by + * the TDX Module specification, but a parameter + * is added to accommodate future extension). + * + * Used in TDX_CMD_GET_REPORT IOCTL request. + */ +struct tdx_report_req { + __u8 subtype; + __u64 reportdata; + __u32 rpd_len; + __u64 tdreport; + __u32 tdr_len; +}; + +/* + * TDX_CMD_GET_REPORT - Get TDREPORT using TDCALL[TDG.MR.REPORT] + * + * Return 0 on success, -EIO on TDCALL execution failure, and + * standard errno on other general error cases. + * + */ +#define TDX_CMD_GET_REPORT _IOWR('T', 0x01, __u64) + +#endif /* _UAPI_ASM_X86_TDX_H */
Attestation is used to verify the trustworthiness of a TDX guest. During the guest bring-up, Intel TDX module measures and records the initial contents and configuration of the guest, and at runtime, guest software uses runtime measurement registers (RMTRs) to measure and record details related to kernel image, command line params, ACPI tables, initrd, etc. At TDX guest runtime, Intel SGX attestation infrastructure is re-used to attest to these measurement data.
First step in the TDX attestation process is to get the TDREPORT data. It is a fixed size data structure generated by the TDX module which includes the above mentioned measurements data, a MAC to protect the integerity of the TDREPORT, and a 64-Byte of user specified data passed during TDREPORT request which can uniquely identify the TDREPORT.
Intel's TDX guest driver exposes TDX_CMD_GET_REPORT IOCTL interface to get the TDREPORT from the user space.
Add a kernel selftest module to test this ABI and verify the validity of generated TDREPORT.
Reviewed-by: Tony Luck tony.luck@intel.com Reviewed-by: Andi Kleen ak@linux.intel.com Acked-by: Kirill A. Shutemov kirill.shutemov@linux.intel.com Signed-off-by: Kuppuswamy Sathyanarayanan sathyanarayanan.kuppuswamy@linux.intel.com ---
Changes since v10: * Replaced TD/TD Guest usage with guest or TDX guest. * Reworded the subject line.
Changes since v9: * Copied arch/x86/include/uapi/asm/tdx.h to tools/arch/x86/include to decouple header dependency between kernel source and tools dir. * Fixed Makefile to adapt to above change. * Fixed commit log and comments. * Added __packed to hardware structs.
Changes since v8: * Please refer to https://lore.kernel.org/all/ \ 20220728034420.648314-1-sathyanarayanan.kuppuswamy@linux.intel.com/
tools/arch/x86/include/uapi/asm/tdx.h | 51 ++++++ tools/testing/selftests/Makefile | 1 + tools/testing/selftests/tdx/Makefile | 11 ++ tools/testing/selftests/tdx/config | 1 + tools/testing/selftests/tdx/tdx_attest_test.c | 155 ++++++++++++++++++ 5 files changed, 219 insertions(+) create mode 100644 tools/arch/x86/include/uapi/asm/tdx.h create mode 100644 tools/testing/selftests/tdx/Makefile create mode 100644 tools/testing/selftests/tdx/config create mode 100644 tools/testing/selftests/tdx/tdx_attest_test.c
diff --git a/tools/arch/x86/include/uapi/asm/tdx.h b/tools/arch/x86/include/uapi/asm/tdx.h new file mode 100644 index 000000000000..c1667b20fe20 --- /dev/null +++ b/tools/arch/x86/include/uapi/asm/tdx.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_ASM_X86_TDX_H +#define _UAPI_ASM_X86_TDX_H + +#include <linux/types.h> +#include <linux/ioctl.h> + +/* Length of the REPORTDATA used in TDG.MR.REPORT TDCALL */ +#define TDX_REPORTDATA_LEN 64 + +/* Length of TDREPORT used in TDG.MR.REPORT TDCALL */ +#define TDX_REPORT_LEN 1024 + +/** + * struct tdx_report_req: Get TDREPORT using REPORTDATA as input. + * + * @subtype : Subtype of TDREPORT (fixed as 0 by TDX Module + * specification, but added a parameter to handle + * future extension). + * @reportdata : User-defined REPORTDATA to be included into + * TDREPORT. Typically it can be some nonce + * provided by attestation service, so the + * generated TDREPORT can be uniquely verified. + * @rpd_len : Length of the REPORTDATA (fixed as 64 bytes by + * the TDX Module specification, but parameter is + * added to handle future extension). + * @tdreport : TDREPORT output from TDCALL[TDG.MR.REPORT]. + * @tdr_len : Length of the TDREPORT (fixed as 1024 bytes by + * the TDX Module specification, but a parameter + * is added to accommodate future extension). + * + * Used in TDX_CMD_GET_REPORT IOCTL request. + */ +struct tdx_report_req { + __u8 subtype; + __u64 reportdata; + __u32 rpd_len; + __u64 tdreport; + __u32 tdr_len; +}; + +/* + * TDX_CMD_GET_REPORT - Get TDREPORT using TDCALL[TDG.MR.REPORT] + * + * Return 0 on success, -EIO on TDCALL execution failure, and + * standard errno on other general error cases. + * + */ +#define TDX_CMD_GET_REPORT _IOWR('T', 0x01, __u64) + +#endif /* _UAPI_ASM_X86_TDX_H */ diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 10b34bb03bc1..22bdb3d848f5 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -70,6 +70,7 @@ TARGETS += sync TARGETS += syscall_user_dispatch TARGETS += sysctl TARGETS += tc-testing +TARGETS += tdx TARGETS += timens ifneq (1, $(quicktest)) TARGETS += timers diff --git a/tools/testing/selftests/tdx/Makefile b/tools/testing/selftests/tdx/Makefile new file mode 100644 index 000000000000..014795420184 --- /dev/null +++ b/tools/testing/selftests/tdx/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 + +top_srcdir = ../../../.. + +LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/x86/include + +CFLAGS += -O3 -Wl,-no-as-needed -Wall -static -I$(LINUX_TOOL_ARCH_INCLUDE) + +TEST_GEN_PROGS := tdx_attest_test + +include ../lib.mk diff --git a/tools/testing/selftests/tdx/config b/tools/testing/selftests/tdx/config new file mode 100644 index 000000000000..1340073a4abf --- /dev/null +++ b/tools/testing/selftests/tdx/config @@ -0,0 +1 @@ +CONFIG_INTEL_TDX_GUEST=y diff --git a/tools/testing/selftests/tdx/tdx_attest_test.c b/tools/testing/selftests/tdx/tdx_attest_test.c new file mode 100644 index 000000000000..095b8755408c --- /dev/null +++ b/tools/testing/selftests/tdx/tdx_attest_test.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test TDX attestation + * + * Copyright (C) 2022 Intel Corporation. All rights reserved. + * + * Author: Kuppuswamy Sathyanarayanan sathyanarayanan.kuppuswamy@linux.intel.com + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <uapi/asm/tdx.h> + +#include "../kselftest_harness.h" + +#define devname "/dev/tdx-guest" +#define HEX_DUMP_SIZE 8 +#define __packed __attribute__((packed)) + +/* + * Trusted Execution Environment (TEE) report (TDREPORT_STRUCT) type, + * sub type and version. More details can be found in TDX v1.0 Module + * specification, sec titled "REPORTTYPE". + */ +struct tdreport_type { + /* 0 - SGX, 81 -TDX, rest are reserved */ + __u8 type; + /* Default value is 0 */ + __u8 sub_type; + /* Default value is 0 */ + __u8 version; + __u8 reserved; +} __packed; + +/* + * struct reportmac - First field in the TRDREPORT_STRUCT. It is common + * to Intel’s TEE's e.g., SGX and TDX. It is MAC-protected and contains + * hashes of the remainder of the report structure which includes the + * TEE’s measurements, and where applicable, the measurements of additional + * TCB elements not reflected in CPUSVN – e.g., a SEAM’s measurements. + * More details can be found in TDX v1.0 Module specification, sec titled + * "REPORTMACSTRUCT" + */ +struct reportmac { + struct tdreport_type type; + __u8 reserved1[12]; + /* CPU security version */ + __u8 cpu_svn[16]; + /* SHA384 hash of TEE TCB INFO */ + __u8 tee_tcb_info_hash[48]; + /* SHA384 hash of TDINFO_STRUCT */ + __u8 tee_td_info_hash[48]; + /* User defined unique data passed in TDG.MR.REPORT request */ + __u8 reportdata[64]; + __u8 reserved2[32]; + __u8 mac[32]; +} __packed; + +/* + * struct td_info - It contains the measurements and initial configuration + * of the TDX Guest that was locked at initialization and a set of measurement + * registers that are run-time extendable. These values are copied from + * the TDCS by the TDG.MR.REPORT function. More details can be found in + * TDX v1.0 Module specification, sec titled "TDINFO_STRUCT". + */ +struct td_info { + /* TDX Guest attributes (like debug, spet_disable, etc) */ + __u8 attr[8]; + __u64 xfam; + /* Measurement registers */ + __u64 mrtd[6]; + __u64 mrconfigid[6]; + __u64 mrowner[6]; + __u64 mrownerconfig[6]; + /* Runtime measurement registers */ + __u64 rtmr[24]; + __u64 reserved[14]; +} __packed; + +struct tdreport { + /* Common to TDX/SGX of size 256 bytes */ + struct reportmac reportmac; + __u8 tee_tcb_info[239]; + __u8 reserved[17]; + /* Measurements and configuration data of size 512 byes */ + struct td_info tdinfo; +} __packed; + +#ifdef DEBUG +static void print_array_hex(const char *title, const char *prefix_str, + const void *buf, int len) +{ + const __u8 *ptr = buf; + int i, rowsize = HEX_DUMP_SIZE; + + if (!len || !buf) + return; + + printf("\t\t%s", title); + + for (i = 0; i < len; i++) { + if (!(i % rowsize)) + printf("\n%s%.8x:", prefix_str, i); + printf(" %.2x", ptr[i]); + } + + printf("\n"); +} +#endif + +TEST(verify_report) +{ + __u8 reportdata[TDX_REPORTDATA_LEN]; + struct tdreport tdreport; + struct tdx_report_req req; + int devfd, i; + + devfd = open(devname, O_RDWR | O_SYNC); + + ASSERT_LT(0, devfd); + + /* Generate sample report data */ + for (i = 0; i < TDX_REPORTDATA_LEN; i++) + reportdata[i] = i; + + /* Initialize IOCTL request */ + req.subtype = 0; + req.reportdata = (__u64)reportdata; + req.rpd_len = TDX_REPORTDATA_LEN; + req.tdreport = (__u64)&tdreport; + req.tdr_len = sizeof(tdreport); + + /* Get TDREPORT */ + ASSERT_EQ(0, ioctl(devfd, TDX_CMD_GET_REPORT, &req)); + +#ifdef DEBUG + print_array_hex("\n\t\tTDX report data\n", "", + reportdata, sizeof(reportdata)); + + print_array_hex("\n\t\tTDX tdreport data\n", "", + &tdreport, sizeof(tdreport)); +#endif + + /* Make sure TDREPORT data includes the REPORTDATA passed */ + ASSERT_EQ(0, memcmp(&tdreport.reportmac.reportdata[0], + reportdata, sizeof(reportdata))); + + ASSERT_EQ(0, close(devfd)); +} + +TEST_HARNESS_MAIN
Document details about TDX attestation process and related user API support.
Attestation details can be found in Guest-Host-Communication Interface (GHCI) for Intel Trust Domain Extensions (TDX), section titled "TD attestation".
Signed-off-by: Kuppuswamy Sathyanarayanan sathyanarayanan.kuppuswamy@linux.intel.com --- Documentation/x86/tdx.rst | 75 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+)
diff --git a/Documentation/x86/tdx.rst b/Documentation/x86/tdx.rst index b8fa4329e1a5..45db1201b624 100644 --- a/Documentation/x86/tdx.rst +++ b/Documentation/x86/tdx.rst @@ -210,6 +210,81 @@ converted to shared on boot. For coherent DMA allocation, the DMA buffer gets converted on the allocation. Check force_dma_unencrypted() for details.
+Attestation +=========== + +Attestation is used to verify the TDX guest trustworthiness to other +entities before provisioning secrets to the guest. For example, a key +server may request for attestation before releasing the encryption keys +to mount the encrypted rootfs or secondary drive. + +TDX module records the state of the TDX guest in various stages of guest +boot process using build time measurement register (MRTD) and runtime +measurement registers (RTMR). Measurements related to guest initial +configuration and firmware image is recorded in the MRTD register. +Measurements related to initial state, kernel image, firmware image, +command line options, initrd, ACPI tables, etc are recorded in RTMR +registers. For more details, please refer to TDX Virtual Firmware design +specification, sec titled "TD Measurement". + +At TDX guest runtime, the Intel TDX module reuses the Intel SGX attestation +infrastructure to provide support for attesting to these measurements as +described below. + +The attestation process consists of two steps: TDREPORT generation and +Quote generation. + +TDX guest uses TDCALL[TDG.MR.REPORT] to get the TDREPORT (TDREPORT_STRUCT) +from the TDX module. TDREPORT is a fixed-size data structure generated by +the TDX module which contains guest-specific information (such as build +and boot measurements), platform security version, and the MAC to protect +the integrity of the TDREPORT. + +After getting the TDREPORT, the second step of the attestation process +is to send it to the QE to generate the Quote. TDREPORT by design can only +be verified on local platform as the MAC key is bound to the platform. To +support remote verification of the TDREPORT, TDX leverages Intel SGX Quote +Enclave (QE) to verify the TDREPORT locally and convert it to a remote +verifiable Quote. Method of sending TDREPORT to QE is implemenentation +specific. Attestation software can choose whatever communication channel +available (i.e. vsock or hypercall) to send the TDREPORT to QE and receive +the Quote. + +To allow userspace attestation agent get the TDREPORT, TDX guest driver +exposes an IOCTL (TDX_CMD_GET_REPORT) interface via /dev/tdx-guest misc +device. + +TDX Guest driver +================ + +The TDX guest driver exposes IOCTL interfaces via /dev/tdx-guest misc +device to allow user space to get certain TDX guest specific details +(like attestation report, attestation quote or storage keys, etc). + +In this section, for each supported IOCTL, following information is +provided along with generic description. + +Input parameters: Parameters passed to the IOCTL and related details. +Output : Details about output data and return value (with details + about the non common error values). + +TDX_CMD_GET_REPORT +------------------ + +:Input parameters: struct tdx_report_req +:Output : Upon successful execution, TDREPORT data is copied to + tdx_report_req.tdreport and returns 0 or returns + -EIO on TDCALL failure and standard error number on + other common failures. + +The TDX_CMD_GET_REPORT IOCTL can be used by the attestation software to +get the TDX guest measurements data (with few other info) in the format +of TDREPORT_STRUCT. It uses TDCALL[TDG.MR.REPORT] to get the TDREPORT +from the TDX Module. + +Format of TDREPORT_STRUCT can be found in TDX 1.0 Module specification, +sec titled "TDREPORT_STRUCT". + References ==========
On Fri, Aug 26, 2022 at 08:06:38AM -0700, Kuppuswamy Sathyanarayanan wrote:
+Input parameters: Parameters passed to the IOCTL and related details. +Output : Details about output data and return value (with details
about the non common error values).
+TDX_CMD_GET_REPORT +------------------
+:Input parameters: struct tdx_report_req +:Output : Upon successful execution, TDREPORT data is copied to
tdx_report_req.tdreport and returns 0 or returns
-EIO on TDCALL failure and standard error number on
other common failures.
Hi,
The field lists above trigger htmldocs warnings:
Documentation/x86/tdx.rst:269: WARNING: Unexpected indentation. Documentation/x86/tdx.rst:275: WARNING: Field list ends without a blank line; unexpected unindent.
I have applied the fixup:
---- >8 ---- diff --git a/Documentation/x86/tdx.rst b/Documentation/x86/tdx.rst index 45db1201b62433..c9e3ecf86e0b43 100644 --- a/Documentation/x86/tdx.rst +++ b/Documentation/x86/tdx.rst @@ -264,18 +264,18 @@ device to allow user space to get certain TDX guest specific details In this section, for each supported IOCTL, following information is provided along with generic description.
-Input parameters: Parameters passed to the IOCTL and related details. -Output : Details about output data and return value (with details - about the non common error values). +:Input parameters: Parameters passed to the IOCTL and related details. +:Output: Details about output data and return value (with details + about the non common error values).
TDX_CMD_GET_REPORT ------------------
:Input parameters: struct tdx_report_req -:Output : Upon successful execution, TDREPORT data is copied to - tdx_report_req.tdreport and returns 0 or returns - -EIO on TDCALL failure and standard error number on - other common failures. +:Output: Upon successful execution, TDREPORT data is copied to + tdx_report_req.tdreport and returns 0 or returns + -EIO on TDCALL failure and standard error number on + other common failures.
The TDX_CMD_GET_REPORT IOCTL can be used by the attestation software to get the TDX guest measurements data (with few other info) in the format
Thanks.
On 8/28/22 7:50 PM, Bagas Sanjaya wrote:
On Fri, Aug 26, 2022 at 08:06:38AM -0700, Kuppuswamy Sathyanarayanan wrote:
+Input parameters: Parameters passed to the IOCTL and related details. +Output : Details about output data and return value (with details
about the non common error values).
+TDX_CMD_GET_REPORT +------------------
+:Input parameters: struct tdx_report_req +:Output : Upon successful execution, TDREPORT data is copied to
tdx_report_req.tdreport and returns 0 or returns
-EIO on TDCALL failure and standard error number on
other common failures.
Hi,
The field lists above trigger htmldocs warnings:
Documentation/x86/tdx.rst:269: WARNING: Unexpected indentation. Documentation/x86/tdx.rst:275: WARNING: Field list ends without a blank line; unexpected unindent.
I have applied the fixup:
Thanks. I will include it in next version.
---- >8 ---- diff --git a/Documentation/x86/tdx.rst b/Documentation/x86/tdx.rst index 45db1201b62433..c9e3ecf86e0b43 100644 --- a/Documentation/x86/tdx.rst +++ b/Documentation/x86/tdx.rst @@ -264,18 +264,18 @@ device to allow user space to get certain TDX guest specific details In this section, for each supported IOCTL, following information is provided along with generic description. -Input parameters: Parameters passed to the IOCTL and related details. -Output : Details about output data and return value (with details
about the non common error values).
+:Input parameters: Parameters passed to the IOCTL and related details. +:Output: Details about output data and return value (with details
about the non common error values).
TDX_CMD_GET_REPORT
:Input parameters: struct tdx_report_req -:Output : Upon successful execution, TDREPORT data is copied to
tdx_report_req.tdreport and returns 0 or returns
-EIO on TDCALL failure and standard error number on
other common failures.
+:Output: Upon successful execution, TDREPORT data is copied to
tdx_report_req.tdreport and returns 0 or returns
-EIO on TDCALL failure and standard error number on
other common failures.
The TDX_CMD_GET_REPORT IOCTL can be used by the attestation software to get the TDX guest measurements data (with few other info) in the format
Thanks.
On Fri, Aug 26, 2022 at 08:06:36AM -0700, Kuppuswamy Sathyanarayanan wrote:
Attestation is used to verify the TDX guest trustworthiness to other entities before provisioning secrets to the guest. For example, a key server may request for attestation before releasing the encryption keys to mount the encrypted rootfs or secondary drive.
During the TDX guest launch, the initial contents (including the firmware image) and configuration of the guest are recorded by the Intel TDX module in build time measurement register (MRTD). After TDX guest is created, run-time measurement registers (RTMRs) can be used by the guest software to extend the measurements. TDX supports 4 RTMR registers, and TDG.MR.RTMR.EXTEND TDCALL is used to update the RTMR registers securely. RTMRs are mainly used to record measurements related to sections like the kernel image, command line parameters, initrd, ACPI tables, firmware data, configuration firmware volume (CFV) of TDVF, etc. For complete details, please refer to TDX Virtual Firmware design specification, sec titled "TD Measurement".
At TDX guest runtime, the Intel TDX module reuses the Intel SGX attestation infrastructure to provide support for attesting to these measurements as described below.
The attestation process consists of two steps: TDREPORT generation and Quote generation.
TDREPORT (TDREPORT_STRUCT) is a fixed-size data structure generated by the TDX module which contains guest-specific information (such as build and boot measurements), platform security version, and the MAC to protect the integrity of the TDREPORT. The guest kernel uses TDCALL[TDG.MR.REPORT] to get the TDREPORT from the TDX module. A user-provided 64-Byte REPORTDATA is used as input and included in the TDREPORT. Typically it can be some nonce provided by attestation service so the TDREPORT can be verified uniquely. More details about the TDREPORT can be found in Intel TDX Module specification, section titled "TDG.MR.REPORT Leaf".
TDREPORT by design can only be verified on the local platform as the MAC key is bound to the platform. To support remote verification of the TDREPORT, TDX leverages Intel SGX Quote Enclave (QE) to verify the TDREPORT locally and convert it to a remote verifiable Quote.
After getting the TDREPORT, the second step of the attestation process is to send it to the QE to generate the Quote. TDX doesn't support SGX inside the guest, so the QE can be deployed in the host, or in another legacy VM with SGX support. QE checks the integrity of TDREPORT and if it is valid, a certified quote signing key is used to sign the Quote. How to send the TDREPORT to QE and receive the Quote is implementation and deployment specific.
Implement a basic guest misc driver to allow userspace to get the TDREPORT. After getting TDREPORT, the userspace attestation software can choose whatever communication channel available (i.e. vsock or hypercall) to send the TDREPORT to QE and receive the Quote.
Also note that explicit access permissions are not enforced in this driver because the quote and measurements are not a secret. However the access permissions of the device node can be used to set any desired access policy. The udev default is usually root access only.
Operations like getting TDREPORT or Quote generation involves sending a blob of data as input and getting another blob of data as output. It was considered to use a sysfs interface for this, but it doesn't fit well into the standard sysfs model for configuring values. It would be possible to do read/write on files, but it would need multiple file descriptors, which would be somewhat messy. IOCTLs seems to be the best fitting and simplest model for this use case. This is similar to AMD SEV platform, which also uses IOCTL interface to support attestation.
Any distribution enabling TDX is also expected to need attestation. So enable it by default with TDX guest support.
Reviewed-by: Tony Luck tony.luck@intel.com Reviewed-by: Andi Kleen ak@linux.intel.com Acked-by: Kirill A. Shutemov kirill.shutemov@linux.intel.com Acked-by: Kai Huang kai.huang@intel.com Acked-by: Wander Lairson Costa wander@redhat.com Signed-off-by: Kuppuswamy Sathyanarayanan sathyanarayanan.kuppuswamy@linux.intel.com
Changes since v10:
- Replaced TD/TD Guest usage with TDX Guest or Guest.
- Removed unnecessary comments.
- Added more validation to user input in tdx_get_report().
- Used u64_to_user_ptr when reading user u64 pointers.
- Fixed commit log as per review comments.
Changes since v9:
- Dropped the cover letter. Since this patch set only adds TDREPORT support, the commit log itself has all the required details.
- Dropped the Quote support and event IRQ support as per Dave's review suggestion.
- Dropped attest.c and moved its contents to tdx.c
- Updated commit log and comments to reflect latest changes.
Changes since v8:
- Please refer to https://lore.kernel.org/all/ \ 20220728034420.648314-1-sathyanarayanan.kuppuswamy@linux.intel.com/
arch/x86/coco/tdx/tdx.c | 114 ++++++++++++++++++++++++++++++++ arch/x86/include/uapi/asm/tdx.h | 51 ++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 arch/x86/include/uapi/asm/tdx.h
diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index 928dcf7a20d9..0888bdf93a4e 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -5,16 +5,21 @@ #define pr_fmt(fmt) "tdx: " fmt #include <linux/cpufeature.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <linux/io.h> #include <asm/coco.h> #include <asm/tdx.h> #include <asm/vmx.h> #include <asm/insn.h> #include <asm/insn-eval.h> #include <asm/pgtable.h> +#include <uapi/asm/tdx.h> /* TDX module Call Leaf IDs */ #define TDX_GET_INFO 1 #define TDX_GET_VEINFO 3 +#define TDX_GET_REPORT 4 #define TDX_ACCEPT_PAGE 6 /* TDX hypercall Leaf IDs */ @@ -34,6 +39,10 @@ #define VE_GET_PORT_NUM(e) ((e) >> 16) #define VE_IS_IO_STRING(e) ((e) & BIT(4)) +#define DRIVER_NAME "tdx-guest"
KBUILD_MODNAME?
+static struct miscdevice tdx_misc_dev;
Why not actually set the values here, instead of in the code down below?
/*
- Wrapper for standard use of __tdx_hypercall with no output aside from
- return code.
@@ -775,3 +784,108 @@ void __init tdx_early_init(void) pr_info("Guest detected\n"); }
+static long tdx_get_report(void __user *argp) +{
- u8 *reportdata, *tdreport;
- struct tdx_report_req req;
- long ret;
- if (copy_from_user(&req, argp, sizeof(req)))
return -EFAULT;
- /*
* Per TDX Module 1.0 specification, section titled
* "TDG.MR.REPORT", REPORTDATA length is fixed as
* TDX_REPORTDATA_LEN, TDREPORT length is fixed as
* TDX_REPORT_LEN, and TDREPORT subtype is fixed as
* 0. Also check for valid user pointers.
*/
- if (!req.reportdata || !req.tdreport || req.subtype ||
req.rpd_len != TDX_REPORTDATA_LEN ||
req.tdr_len != TDX_REPORT_LEN)
return -EINVAL;
- reportdata = kmalloc(req.rpd_len, GFP_KERNEL);
- if (!reportdata)
return -ENOMEM;
- tdreport = kzalloc(req.tdr_len, GFP_KERNEL);
- if (!tdreport) {
kfree(reportdata);
return -ENOMEM;
- }
- if (copy_from_user(reportdata, u64_to_user_ptr(req.reportdata),
req.rpd_len)) {
ret = -EFAULT;
goto out;
- }
- /*
* Generate TDREPORT using "TDG.MR.REPORT" TDCALL.
*
* Get the TDREPORT using REPORTDATA as input. Refer to
* section 22.3.3 TDG.MR.REPORT leaf in the TDX Module 1.0
* Specification for detailed information.
*/
- ret = __tdx_module_call(TDX_GET_REPORT, virt_to_phys(tdreport),
virt_to_phys(reportdata), req.subtype,
0, NULL);
- if (ret) {
ret = -EIO;
goto out;
- }
- if (copy_to_user(u64_to_user_ptr(req.tdreport), tdreport, req.tdr_len))
ret = -EFAULT;
+out:
- kfree(reportdata);
- kfree(tdreport);
- return ret;
+} +static long tdx_guest_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
+{
- void __user *argp = (void __user *)arg;
- long ret = -EINVAL;
- switch (cmd) {
- case TDX_CMD_GET_REPORT:
ret = tdx_get_report(argp);
break;
- default:
pr_debug("cmd %d not supported\n", cmd);
break;
Wrong error value for an invalid ioctl.
You all know this, how did this slip through?
:(
- }
- return ret;
+}
+static const struct file_operations tdx_guest_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = tdx_guest_ioctl,
- .llseek = no_llseek,
+};
+static int __init tdx_guest_init(void) +{
- int ret;
- if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
return -EIO;
- tdx_misc_dev.name = DRIVER_NAME;
- tdx_misc_dev.minor = MISC_DYNAMIC_MINOR;
- tdx_misc_dev.fops = &tdx_guest_fops;
Again, set this in the definition above, and you will be fine.
- ret = misc_register(&tdx_misc_dev);
- if (ret) {
pr_err("misc device registration failed\n");
return ret;
- }
- return 0;
+} +device_initcall(tdx_guest_init) diff --git a/arch/x86/include/uapi/asm/tdx.h b/arch/x86/include/uapi/asm/tdx.h new file mode 100644 index 000000000000..c1667b20fe20 --- /dev/null +++ b/arch/x86/include/uapi/asm/tdx.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_ASM_X86_TDX_H +#define _UAPI_ASM_X86_TDX_H
+#include <linux/types.h> +#include <linux/ioctl.h>
+/* Length of the REPORTDATA used in TDG.MR.REPORT TDCALL */ +#define TDX_REPORTDATA_LEN 64
+/* Length of TDREPORT used in TDG.MR.REPORT TDCALL */ +#define TDX_REPORT_LEN 1024
+/**
- struct tdx_report_req: Get TDREPORT using REPORTDATA as input.
- @subtype : Subtype of TDREPORT (fixed as 0 by TDX Module
specification, but added a parameter to handle
future extension).
- @reportdata : User-defined REPORTDATA to be included into
TDREPORT. Typically it can be some nonce
provided by attestation service, so the
generated TDREPORT can be uniquely verified.
- @rpd_len : Length of the REPORTDATA (fixed as 64 bytes by
the TDX Module specification, but parameter is
added to handle future extension).
- @tdreport : TDREPORT output from TDCALL[TDG.MR.REPORT].
- @tdr_len : Length of the TDREPORT (fixed as 1024 bytes by
the TDX Module specification, but a parameter
is added to accommodate future extension).
- Used in TDX_CMD_GET_REPORT IOCTL request.
- */
+struct tdx_report_req {
- __u8 subtype;
- __u64 reportdata;
Unaligned data? That's really how the chip works? That feels very odd.
Or do you require the hole here that the compiler added? If so, you can't always rely on that, properly pad things out.
- __u32 rpd_len;
- __u64 tdreport;
- __u32 tdr_len;
Again, check for holes.
And as this comes directly from hardware, what is the endian-ness of these values?
thanks,
greg k-h
Hi,
On 8/27/22 12:33 AM, Greg Kroah-Hartman wrote:
On Fri, Aug 26, 2022 at 08:06:36AM -0700, Kuppuswamy Sathyanarayanan wrote:
Attestation is used to verify the TDX guest trustworthiness to other entities before provisioning secrets to the guest. For example, a key server may request for attestation before releasing the encryption keys to mount the encrypted rootfs or secondary drive.
During the TDX guest launch, the initial contents (including the firmware image) and configuration of the guest are recorded by the Intel TDX module in build time measurement register (MRTD). After TDX guest is created, run-time measurement registers (RTMRs) can be used by the guest software to extend the measurements. TDX supports 4 RTMR registers, and TDG.MR.RTMR.EXTEND TDCALL is used to update the RTMR registers securely. RTMRs are mainly used to record measurements related to sections like the kernel image, command line parameters, initrd, ACPI tables, firmware data, configuration firmware volume (CFV) of TDVF, etc. For complete details, please refer to TDX Virtual Firmware design specification, sec titled "TD Measurement".
At TDX guest runtime, the Intel TDX module reuses the Intel SGX attestation infrastructure to provide support for attesting to these measurements as described below.
The attestation process consists of two steps: TDREPORT generation and Quote generation.
TDREPORT (TDREPORT_STRUCT) is a fixed-size data structure generated by the TDX module which contains guest-specific information (such as build and boot measurements), platform security version, and the MAC to protect the integrity of the TDREPORT. The guest kernel uses TDCALL[TDG.MR.REPORT] to get the TDREPORT from the TDX module. A user-provided 64-Byte REPORTDATA is used as input and included in the TDREPORT. Typically it can be some nonce provided by attestation service so the TDREPORT can be verified uniquely. More details about the TDREPORT can be found in Intel TDX Module specification, section titled "TDG.MR.REPORT Leaf".
TDREPORT by design can only be verified on the local platform as the MAC key is bound to the platform. To support remote verification of the TDREPORT, TDX leverages Intel SGX Quote Enclave (QE) to verify the TDREPORT locally and convert it to a remote verifiable Quote.
After getting the TDREPORT, the second step of the attestation process is to send it to the QE to generate the Quote. TDX doesn't support SGX inside the guest, so the QE can be deployed in the host, or in another legacy VM with SGX support. QE checks the integrity of TDREPORT and if it is valid, a certified quote signing key is used to sign the Quote. How to send the TDREPORT to QE and receive the Quote is implementation and deployment specific.
Implement a basic guest misc driver to allow userspace to get the TDREPORT. After getting TDREPORT, the userspace attestation software can choose whatever communication channel available (i.e. vsock or hypercall) to send the TDREPORT to QE and receive the Quote.
Also note that explicit access permissions are not enforced in this driver because the quote and measurements are not a secret. However the access permissions of the device node can be used to set any desired access policy. The udev default is usually root access only.
Operations like getting TDREPORT or Quote generation involves sending a blob of data as input and getting another blob of data as output. It was considered to use a sysfs interface for this, but it doesn't fit well into the standard sysfs model for configuring values. It would be possible to do read/write on files, but it would need multiple file descriptors, which would be somewhat messy. IOCTLs seems to be the best fitting and simplest model for this use case. This is similar to AMD SEV platform, which also uses IOCTL interface to support attestation.
Any distribution enabling TDX is also expected to need attestation. So enable it by default with TDX guest support.
Reviewed-by: Tony Luck tony.luck@intel.com Reviewed-by: Andi Kleen ak@linux.intel.com Acked-by: Kirill A. Shutemov kirill.shutemov@linux.intel.com Acked-by: Kai Huang kai.huang@intel.com Acked-by: Wander Lairson Costa wander@redhat.com Signed-off-by: Kuppuswamy Sathyanarayanan sathyanarayanan.kuppuswamy@linux.intel.com
Changes since v10:
- Replaced TD/TD Guest usage with TDX Guest or Guest.
- Removed unnecessary comments.
- Added more validation to user input in tdx_get_report().
- Used u64_to_user_ptr when reading user u64 pointers.
- Fixed commit log as per review comments.
Changes since v9:
- Dropped the cover letter. Since this patch set only adds TDREPORT support, the commit log itself has all the required details.
- Dropped the Quote support and event IRQ support as per Dave's review suggestion.
- Dropped attest.c and moved its contents to tdx.c
- Updated commit log and comments to reflect latest changes.
Changes since v8:
- Please refer to https://lore.kernel.org/all/ \ 20220728034420.648314-1-sathyanarayanan.kuppuswamy@linux.intel.com/
arch/x86/coco/tdx/tdx.c | 114 ++++++++++++++++++++++++++++++++ arch/x86/include/uapi/asm/tdx.h | 51 ++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 arch/x86/include/uapi/asm/tdx.h
diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index 928dcf7a20d9..0888bdf93a4e 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -5,16 +5,21 @@ #define pr_fmt(fmt) "tdx: " fmt #include <linux/cpufeature.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <linux/io.h> #include <asm/coco.h> #include <asm/tdx.h> #include <asm/vmx.h> #include <asm/insn.h> #include <asm/insn-eval.h> #include <asm/pgtable.h> +#include <uapi/asm/tdx.h> /* TDX module Call Leaf IDs */ #define TDX_GET_INFO 1 #define TDX_GET_VEINFO 3 +#define TDX_GET_REPORT 4 #define TDX_ACCEPT_PAGE 6 /* TDX hypercall Leaf IDs */ @@ -34,6 +39,10 @@ #define VE_GET_PORT_NUM(e) ((e) >> 16) #define VE_IS_IO_STRING(e) ((e) & BIT(4)) +#define DRIVER_NAME "tdx-guest"
KBUILD_MODNAME?
It is only used to set the tdx_misc_dev name and we want to use "tdx-guest" as the interface name. So KBUILD_MODNAME will not work. Since userspace also needs this device name, I will rename it as TDX_GUEST_DEVICE and move it to arch/x86/include/uapi/asm/tdx.h.
+static struct miscdevice tdx_misc_dev;
Why not actually set the values here, instead of in the code down below?
Fine with me. I will move this above tdx_guest_init() and initialize it directly.
+static struct miscdevice tdx_misc_dev = { + .name = TDX_GUEST_DEVICE, + .minor = MISC_DYNAMIC_MINOR, + .fops = &tdx_guest_fops, +};
/*
- Wrapper for standard use of __tdx_hypercall with no output aside from
- return code.
@@ -775,3 +784,108 @@ void __init tdx_early_init(void) pr_info("Guest detected\n"); }
+static long tdx_get_report(void __user *argp) +{
- u8 *reportdata, *tdreport;
- struct tdx_report_req req;
- long ret;
- if (copy_from_user(&req, argp, sizeof(req)))
return -EFAULT;
- /*
* Per TDX Module 1.0 specification, section titled
* "TDG.MR.REPORT", REPORTDATA length is fixed as
* TDX_REPORTDATA_LEN, TDREPORT length is fixed as
* TDX_REPORT_LEN, and TDREPORT subtype is fixed as
* 0. Also check for valid user pointers.
*/
- if (!req.reportdata || !req.tdreport || req.subtype ||
req.rpd_len != TDX_REPORTDATA_LEN ||
req.tdr_len != TDX_REPORT_LEN)
return -EINVAL;
- reportdata = kmalloc(req.rpd_len, GFP_KERNEL);
- if (!reportdata)
return -ENOMEM;
- tdreport = kzalloc(req.tdr_len, GFP_KERNEL);
- if (!tdreport) {
kfree(reportdata);
return -ENOMEM;
- }
- if (copy_from_user(reportdata, u64_to_user_ptr(req.reportdata),
req.rpd_len)) {
ret = -EFAULT;
goto out;
- }
- /*
* Generate TDREPORT using "TDG.MR.REPORT" TDCALL.
*
* Get the TDREPORT using REPORTDATA as input. Refer to
* section 22.3.3 TDG.MR.REPORT leaf in the TDX Module 1.0
* Specification for detailed information.
*/
- ret = __tdx_module_call(TDX_GET_REPORT, virt_to_phys(tdreport),
virt_to_phys(reportdata), req.subtype,
0, NULL);
- if (ret) {
ret = -EIO;
goto out;
- }
- if (copy_to_user(u64_to_user_ptr(req.tdreport), tdreport, req.tdr_len))
ret = -EFAULT;
+out:
- kfree(reportdata);
- kfree(tdreport);
- return ret;
+} +static long tdx_guest_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
+{
- void __user *argp = (void __user *)arg;
- long ret = -EINVAL;
- switch (cmd) {
- case TDX_CMD_GET_REPORT:
ret = tdx_get_report(argp);
break;
- default:
pr_debug("cmd %d not supported\n", cmd);
break;
Wrong error value for an invalid ioctl.
You all know this, how did this slip through?
:(
Sorry, I missed it. I will fix it in next version.
- }
- return ret;
+}
+static const struct file_operations tdx_guest_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = tdx_guest_ioctl,
- .llseek = no_llseek,
+};
+static int __init tdx_guest_init(void) +{
- int ret;
- if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
return -EIO;
- tdx_misc_dev.name = DRIVER_NAME;
- tdx_misc_dev.minor = MISC_DYNAMIC_MINOR;
- tdx_misc_dev.fops = &tdx_guest_fops;
Again, set this in the definition above, and you will be fine.
Agreed. I will move it.
- ret = misc_register(&tdx_misc_dev);
- if (ret) {
pr_err("misc device registration failed\n");
return ret;
- }
- return 0;
+} +device_initcall(tdx_guest_init) diff --git a/arch/x86/include/uapi/asm/tdx.h b/arch/x86/include/uapi/asm/tdx.h new file mode 100644 index 000000000000..c1667b20fe20 --- /dev/null +++ b/arch/x86/include/uapi/asm/tdx.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_ASM_X86_TDX_H +#define _UAPI_ASM_X86_TDX_H
+#include <linux/types.h> +#include <linux/ioctl.h>
+/* Length of the REPORTDATA used in TDG.MR.REPORT TDCALL */ +#define TDX_REPORTDATA_LEN 64
+/* Length of TDREPORT used in TDG.MR.REPORT TDCALL */ +#define TDX_REPORT_LEN 1024
+/**
- struct tdx_report_req: Get TDREPORT using REPORTDATA as input.
- @subtype : Subtype of TDREPORT (fixed as 0 by TDX Module
specification, but added a parameter to handle
future extension).
- @reportdata : User-defined REPORTDATA to be included into
TDREPORT. Typically it can be some nonce
provided by attestation service, so the
generated TDREPORT can be uniquely verified.
- @rpd_len : Length of the REPORTDATA (fixed as 64 bytes by
the TDX Module specification, but parameter is
added to handle future extension).
- @tdreport : TDREPORT output from TDCALL[TDG.MR.REPORT].
- @tdr_len : Length of the TDREPORT (fixed as 1024 bytes by
the TDX Module specification, but a parameter
is added to accommodate future extension).
- Used in TDX_CMD_GET_REPORT IOCTL request.
- */
+struct tdx_report_req {
- __u8 subtype;
- __u64 reportdata;
Unaligned data? That's really how the chip works? That feels very odd.
Or do you require the hole here that the compiler added? If so, you can't always rely on that, properly pad things out.
This is not related to hardware. This struct is only used in user/kernel ABI. So we don't need to care about alignment. In tdx_get_report(), we read these values and pass it in format suitable for TDX_GET_REPORT TDCALL.
- __u32 rpd_len;
- __u64 tdreport;
- __u32 tdr_len;
Again, check for holes.
And as this comes directly from hardware, what is the endian-ness of these values?
It does not come from the hardware. This ABI allows userspace to get the TDREPORT from the TDX Module.. After request completion, the kernel will copy the TDREPORT data to user buffer specified in tdx_report_req.tdreport.
thanks,
greg k-h
On 8/26/22 22:06, Kuppuswamy Sathyanarayanan wrote:
Attestation is used to verify the TDX guest trustworthiness to other entities before provisioning secrets to the guest. For example, a key server may request for attestation before releasing the encryption keys to mount the encrypted rootfs or secondary drive.
During the TDX guest launch, the initial contents (including the firmware image) and configuration of the guest are recorded by the Intel TDX module in build time measurement register (MRTD). After TDX guest is created, run-time measurement registers (RTMRs) can be used by the guest software to extend the measurements. TDX supports 4 RTMR registers, and TDG.MR.RTMR.EXTEND TDCALL is used to update the RTMR registers securely. RTMRs are mainly used to record measurements related to sections like the kernel image, command line parameters, initrd, ACPI tables, firmware data, configuration firmware volume (CFV) of TDVF, etc. For complete details, please refer to TDX Virtual Firmware design specification, sec titled "TD Measurement".
At TDX guest runtime, the Intel TDX module reuses the Intel SGX attestation infrastructure to provide support for attesting to these measurements as described below.
The attestation process consists of two steps: TDREPORT generation and Quote generation.
TDREPORT (TDREPORT_STRUCT) is a fixed-size data structure generated by the TDX module which contains guest-specific information (such as build and boot measurements), platform security version, and the MAC to protect the integrity of the TDREPORT. The guest kernel uses TDCALL[TDG.MR.REPORT] to get the TDREPORT from the TDX module. A user-provided 64-Byte REPORTDATA is used as input and included in the TDREPORT. Typically it can be some nonce provided by attestation service so the TDREPORT can be verified uniquely. More details about the TDREPORT can be found in Intel TDX Module specification, section titled "TDG.MR.REPORT Leaf".
TDREPORT by design can only be verified on the local platform as the MAC key is bound to the platform. To support remote verification of the TDREPORT, TDX leverages Intel SGX Quote Enclave (QE) to verify the TDREPORT locally and convert it to a remote verifiable Quote.
After getting the TDREPORT, the second step of the attestation process is to send it to the QE to generate the Quote. TDX doesn't support SGX inside the guest, so the QE can be deployed in the host, or in another legacy VM with SGX support. QE checks the integrity of TDREPORT and if it is valid, a certified quote signing key is used to sign the Quote. How to send the TDREPORT to QE and receive the Quote is implementation and deployment specific.
Implement a basic guest misc driver to allow userspace to get the TDREPORT. After getting TDREPORT, the userspace attestation software can choose whatever communication channel available (i.e. vsock or hypercall) to send the TDREPORT to QE and receive the Quote.
Also note that explicit access permissions are not enforced in this driver because the quote and measurements are not a secret. However the access permissions of the device node can be used to set any desired access policy. The udev default is usually root access only.
Operations like getting TDREPORT or Quote generation involves sending a blob of data as input and getting another blob of data as output. It was considered to use a sysfs interface for this, but it doesn't fit well into the standard sysfs model for configuring values. It would be possible to do read/write on files, but it would need multiple file descriptors, which would be somewhat messy. IOCTLs seems to be the best fitting and simplest model for this use case. This is similar to AMD SEV platform, which also uses IOCTL interface to support attestation.
Any distribution enabling TDX is also expected to need attestation. So enable it by default with TDX guest support.
On what tree this patch series is based on?
And as this series is multi-patch, it's customary to have cover letter (or [PATCH 0/?]).
Thanks.
On 8/27/22 12:45 AM, Bagas Sanjaya wrote:
On what tree this patch series is based on?
It is based on v6.0-rc1.
And as this series is multi-patch, it's customary to have cover letter (or [PATCH 0/?]).
Although it is multi-patch, all it does is to add user interface support to get the TDREPORT. I have split the documentation and testing parts into separate patches. Since commit log of this patch has all the required details about the TDREPORT support, I thought a cover letter would be redundant. If it is really required, I can add it from the next version.
Thanks.
On 8/26/22 08:06, Kuppuswamy Sathyanarayanan wrote:
+struct tdx_report_req {
- __u8 subtype;
- __u64 reportdata;
- __u32 rpd_len;
- __u64 tdreport;
- __u32 tdr_len;
+};
Please do take a look at how the compiler ends up building that structure.
If you sized things to "save space", the way the compiler treats that structure may surprise you. You might also want to look at how a 32-bit compile deals with it versus a 64-bit one.
Hi,
On 8/28/22 1:14 PM, Dave Hansen wrote:
On 8/26/22 08:06, Kuppuswamy Sathyanarayanan wrote:
+struct tdx_report_req {
- __u8 subtype;
- __u64 reportdata;
- __u32 rpd_len;
- __u64 tdreport;
- __u32 tdr_len;
+};
Please do take a look at how the compiler ends up building that structure.
If you sized things to "save space", the way the compiler treats that structure may surprise you. You might also want to look at how a 32-bit compile deals with it versus a 64-bit one.
Since it is only used in user/kernel ABI, I did not consider the size issue. But I understand your point. The size of this struct in a 64-bit system is 40 bytes. So we did not gain anything with using different member sizes. In a 32-bit system, size due to padding is less compared to 64-bit.
I will re-arrange the struct as below. With this change, the size will come down to 32 bytes.
struct tdx_report_req { __u64 reportdata;; __u64 tdreport; __u32 rpd_len __u32 tdr_len; __u8 subtype; };
On Sun, Aug 28, 2022 at 05:19:33PM -0700, Sathyanarayanan Kuppuswamy wrote:
Hi,
On 8/28/22 1:14 PM, Dave Hansen wrote:
On 8/26/22 08:06, Kuppuswamy Sathyanarayanan wrote:
+struct tdx_report_req {
- __u8 subtype;
- __u64 reportdata;
- __u32 rpd_len;
- __u64 tdreport;
- __u32 tdr_len;
+};
Please do take a look at how the compiler ends up building that structure.
If you sized things to "save space", the way the compiler treats that structure may surprise you. You might also want to look at how a 32-bit compile deals with it versus a 64-bit one.
Since it is only used in user/kernel ABI, I did not consider the size issue.
That is _EXACTLY_ why you need to consider the size issues here.
But I understand your point. The size of this struct in a 64-bit system is 40 bytes. So we did not gain anything with using different member sizes. In a 32-bit system, size due to padding is less compared to 64-bit.
That's not the issue here, please use a tool like pahole to see the problems with this definition.
I will re-arrange the struct as below. With this change, the size will come down to 32 bytes.
struct tdx_report_req { __u64 reportdata;; __u64 tdreport; __u32 rpd_len __u32 tdr_len; __u8 subtype; };
That's better, but again, please use pahole.
thanks,
greg k-h
Hi,
On 8/28/22 10:30 PM, Greg Kroah-Hartman wrote:
struct tdx_report_req { __u64 reportdata;; __u64 tdreport; __u32 rpd_len __u32 tdr_len; __u8 subtype; };
That's better, but again, please use pahole.
Following are the pahole dump of both formats.
With previous format, compiler adds 11 bytes in holes and 4 bytes of padding.
Total size of struct is 40 bytes.
struct tdx_report_req { __u8 subtype; /* 0 1 */
/* XXX 7 bytes hole, try to pack */
__u64 reportdata; /* 8 8 */ __u32 rpd_len; /* 16 4 */
/* XXX 4 bytes hole, try to pack */
__u64 tdreport; /* 24 8 */ __u32 tdr_len; /* 32 4 */
/* size: 40, cachelines: 1, members: 5 */ /* sum members: 25, holes: 2, sum holes: 11 */ /* padding: 4 */ /* last cacheline: 40 bytes */ };
With the changed format, we avoided the holes, but it still adds 7 bytes of padding.
Total size of struct is 32 bytes.
struct tdx_report_req { __u64 reportdata; /* 0 8 */ __u64 tdreport; /* 8 8 */ __u32 rpd_len; /* 16 4 */ __u32 tdr_len; /* 20 4 */ __u8 subtype; /* 24 1 */
/* size: 32, cachelines: 1, members: 5 */ /* padding: 7 */ /* last cacheline: 32 bytes */ };
On 8/29/22 13:09, Sathyanarayanan Kuppuswamy wrote:
With the changed format, we avoided the holes, but it still adds 7 bytes of padding.
Total size of struct is 32 bytes.
struct tdx_report_req { __u64 reportdata; /* 0 8 */ __u64 tdreport; /* 8 8 */ __u32 rpd_len; /* 16 4 */ __u32 tdr_len; /* 20 4 */ __u8 subtype; /* 24 1 */
/* size: 32, cachelines: 1, members: 5 */ /* padding: 7 */ /* last cacheline: 32 bytes */ };
Might as well make some use of the padding and add:
u8 reserved[7];
That will ensure that the entire structure is explicitly sized *AND* leave you room to add flags or other fun in the future without a new ABI.
On 8/29/22 1:28 PM, Dave Hansen wrote:
On 8/29/22 13:09, Sathyanarayanan Kuppuswamy wrote:
With the changed format, we avoided the holes, but it still adds 7 bytes of padding.
Total size of struct is 32 bytes.
struct tdx_report_req { __u64 reportdata; /* 0 8 */ __u64 tdreport; /* 8 8 */ __u32 rpd_len; /* 16 4 */ __u32 tdr_len; /* 20 4 */ __u8 subtype; /* 24 1 */
/* size: 32, cachelines: 1, members: 5 */ /* padding: 7 */ /* last cacheline: 32 bytes */ };
Might as well make some use of the padding and add:
u8 reserved[7];
That will ensure that the entire structure is explicitly sized *AND* leave you room to add flags or other fun in the future without a new ABI.
Ok. I will add it in next version.
linux-kselftest-mirror@lists.linaro.org