Hi All,
Intel's Trust Domain Extensions (TDX) protect guest VMs from malicious hosts and some physical attacks. VM guest with TDX support is called as a TDX Guest.
In TDX guest, attestation process 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.
This patch set adds attestation support for the TDX guest. Details about the TDX attestation process and the steps involved are explained in Documentation/x86/tdx.rst (added by patch 2/3).
Following are the details of the patch set:
Patch 1/3 -> Preparatory patch for adding attestation support. Patch 2/3 -> Adds user interface driver to support attestation. Patch 3/3 -> Adds selftest support for TDREPORT feature.
Commit log history is maintained in the individual patches.
Kuppuswamy Sathyanarayanan (3): x86/tdx: Make __tdx_module_call() usable in driver module virt: Add TDX guest driver selftests: tdx: Test TDX attestation GetReport support
Documentation/virt/coco/tdx-guest.rst | 42 +++++ Documentation/virt/index.rst | 1 + Documentation/x86/tdx.rst | 43 +++++ arch/x86/coco/tdx/tdcall.S | 2 + arch/x86/coco/tdx/tdx.c | 5 - arch/x86/include/asm/tdx.h | 6 + drivers/virt/Kconfig | 2 + drivers/virt/Makefile | 1 + drivers/virt/coco/tdx-guest/Kconfig | 10 ++ drivers/virt/coco/tdx-guest/Makefile | 2 + drivers/virt/coco/tdx-guest/tdx-guest.c | 131 ++++++++++++++ include/uapi/linux/tdx-guest.h | 53 ++++++ tools/testing/selftests/Makefile | 1 + tools/testing/selftests/tdx/Makefile | 7 + tools/testing/selftests/tdx/config | 1 + tools/testing/selftests/tdx/tdx_guest_test.c | 175 +++++++++++++++++++ 16 files changed, 477 insertions(+), 5 deletions(-) create mode 100644 Documentation/virt/coco/tdx-guest.rst create mode 100644 drivers/virt/coco/tdx-guest/Kconfig create mode 100644 drivers/virt/coco/tdx-guest/Makefile create mode 100644 drivers/virt/coco/tdx-guest/tdx-guest.c create mode 100644 include/uapi/linux/tdx-guest.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_guest_test.c
To support TDX attestation, the TDX guest user interface driver must use the __tdx module_call() function in the driver to allow the user to obtain the TDREPORT.
So export the __tdx_module_call() and move the TDX Module IDs to asm/tdx.h.
This is a preparatory patch for adding attestation support.
Reviewed-by: Mika Westerberg mika.westerberg@linux.intel.com Signed-off-by: Kuppuswamy Sathyanarayanan sathyanarayanan.kuppuswamy@linux.intel.com ---
Changes since v13: * None
arch/x86/coco/tdx/tdcall.S | 2 ++ arch/x86/coco/tdx/tdx.c | 5 ----- arch/x86/include/asm/tdx.h | 5 +++++ 3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/arch/x86/coco/tdx/tdcall.S b/arch/x86/coco/tdx/tdcall.S index f9eb1134f22d..47a1534946c8 100644 --- a/arch/x86/coco/tdx/tdcall.S +++ b/arch/x86/coco/tdx/tdcall.S @@ -3,6 +3,7 @@ #include <asm/asm.h> #include <asm/frame.h> #include <asm/unwind_hints.h> +#include <asm/export.h>
#include <linux/linkage.h> #include <linux/bits.h> @@ -75,6 +76,7 @@ SYM_FUNC_START(__tdx_module_call) FRAME_END RET SYM_FUNC_END(__tdx_module_call) +EXPORT_SYMBOL_GPL(__tdx_module_call);
/* * __tdx_hypercall() - Make hypercalls to a TDX VMM using TDVMCALL leaf diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index 928dcf7a20d9..2dcc6021aa43 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -12,11 +12,6 @@ #include <asm/insn-eval.h> #include <asm/pgtable.h>
-/* TDX module Call Leaf IDs */ -#define TDX_GET_INFO 1 -#define TDX_GET_VEINFO 3 -#define TDX_ACCEPT_PAGE 6 - /* TDX hypercall Leaf IDs */ #define TDVMCALL_MAP_GPA 0x10001
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 020c81a7c729..34c00d8a5263 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -18,6 +18,11 @@ #define TDX_SW_ERROR (TDX_ERROR | GENMASK_ULL(47, 40)) #define TDX_SEAMCALL_VMFAILINVALID (TDX_SW_ERROR | _UL(0xFFFF0000))
+/* TDX module Call Leaf IDs */ +#define TDX_GET_INFO 1 +#define TDX_GET_VEINFO 3 +#define TDX_ACCEPT_PAGE 6 + #ifndef __ASSEMBLY__
/*
On Wed, Sep 28, 2022 at 02:55:33PM -0700, Kuppuswamy Sathyanarayanan wrote:
To support TDX attestation, the TDX guest user interface driver must use the __tdx module_call() function in the driver to allow the user to obtain the TDREPORT.
So export the __tdx_module_call() and move the TDX Module IDs to asm/tdx.h.
The functions with the __ prefix are usually lower-level interfaces which should be internal. Usually.
Why aren't you exporting the tdx_module_call() one instead?
On 10/12/22 3:18 AM, Borislav Petkov wrote:
On Wed, Sep 28, 2022 at 02:55:33PM -0700, Kuppuswamy Sathyanarayanan wrote:
To support TDX attestation, the TDX guest user interface driver must use the __tdx module_call() function in the driver to allow the user to obtain the TDREPORT.
So export the __tdx_module_call() and move the TDX Module IDs to asm/tdx.h.
The functions with the __ prefix are usually lower-level interfaces which should be internal. Usually.
Why aren't you exporting the tdx_module_call() one instead?
tdx_module_call() calls panic() on a non-zero error value. So it is only used for cases where failure is fatal to the guest. But in the case of TDG.MR.REPORT TDCALL, there are valid cases for failure (like invalid param or busy condition) and the failure is non-fatal.
So we should create a new wrapper for this use case or use __tdx_module_call() which is already exposed in asm/tdx.h.
On Wed, Oct 12, 2022 at 06:35:56AM -0700, Sathyanarayanan Kuppuswamy wrote:
So we should create a new wrapper for this use case or use
Yes, you got it - a new wrapper pls.
Thx.
On 10/12/22 7:27 AM, Borislav Petkov wrote:
On Wed, Oct 12, 2022 at 06:35:56AM -0700, Sathyanarayanan Kuppuswamy wrote:
So we should create a new wrapper for this use case or use
Yes, you got it - a new wrapper pls.
Ok. I will add a new wrapper to get the TDREPORT.
+/*
+ * Add a wrapper for TDG.MR.REPORT TDCALL. It is used in TDX guest
+ * driver module to get the TDREPORT.
+ */
+long tdx_mcall_get_report(void *reportdata, void *tdreport, u8 subtype)
+{
+ if (subtype || !reportdata || !tdreport)
+ return -EINVAL;
+
+ /*
+ * 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.
+ */
+ return __tdx_module_call(TDX_GET_REPORT, virt_to_phys(tdreport),
+ virt_to_phys(reportdata), subtype, 0, NULL);
+}
+EXPORT_SYMBOL_GPL(tdx_mcall_get_report);
Thx.
On Wed, Oct 12, 2022 at 08:44:04AM -0700, Sathyanarayanan Kuppuswamy wrote:
On 10/12/22 7:27 AM, Borislav Petkov wrote:
On Wed, Oct 12, 2022 at 06:35:56AM -0700, Sathyanarayanan Kuppuswamy wrote:
So we should create a new wrapper for this use case or use
Yes, you got it - a new wrapper pls.
Ok. I will add a new wrapper to get the TDREPORT.
+/*
- Add a wrapper for TDG.MR.REPORT TDCALL. It is used in TDX guest
- driver module to get the TDREPORT.
*/
+long tdx_mcall_get_report(void *reportdata, void *tdreport, u8 subtype)
Why "long"?
Why void *? Don't you have real types for these?
+{
if (subtype || !reportdata || !tdreport)
return -EINVAL;
How could that happen if you control all callers?
/*
* 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.
*/
return __tdx_module_call(TDX_GET_REPORT, virt_to_phys(tdreport),
virt_to_phys(reportdata), subtype, 0, NULL);
If you check for NULL, why are you not validating that these are valid pointers as well? You can't have it both ways.
thanks,
greg k-h
Hi,
On 10/12/22 9:23 AM, Greg Kroah-Hartman wrote:
On Wed, Oct 12, 2022 at 08:44:04AM -0700, Sathyanarayanan Kuppuswamy wrote:
On 10/12/22 7:27 AM, Borislav Petkov wrote:
On Wed, Oct 12, 2022 at 06:35:56AM -0700, Sathyanarayanan Kuppuswamy wrote:
So we should create a new wrapper for this use case or use
Yes, you got it - a new wrapper pls.
Ok. I will add a new wrapper to get the TDREPORT.
+/*
- Add a wrapper for TDG.MR.REPORT TDCALL. It is used in TDX guest
- driver module to get the TDREPORT.
*/
+long tdx_mcall_get_report(void *reportdata, void *tdreport, u8 subtype)
Why "long"?
We used long because __tdx_module_call() call returns u64 value.
Alternatively, we can also check for return value of __tdx_module_call() here and return 0/-EIO as return values. In this case we can change return value to int.
Why void *? Don't you have real types for these?
We use these buffers as an intermediary to transfer data between userspace and the TDX module. In the kernel we don't consume these datas. So we did not define the type of the data.
+{
if (subtype || !reportdata || !tdreport)
return -EINVAL;
How could that happen if you control all callers?
I have added it as a safety check against any incorrect usage in future. I will remove it.
thanks,
greg k-h
On Wed, Oct 12, 2022 at 10:13:50AM -0700, Sathyanarayanan Kuppuswamy wrote:
Hi,
On 10/12/22 9:23 AM, Greg Kroah-Hartman wrote:
On Wed, Oct 12, 2022 at 08:44:04AM -0700, Sathyanarayanan Kuppuswamy wrote:
On 10/12/22 7:27 AM, Borislav Petkov wrote:
On Wed, Oct 12, 2022 at 06:35:56AM -0700, Sathyanarayanan Kuppuswamy wrote:
So we should create a new wrapper for this use case or use
Yes, you got it - a new wrapper pls.
Ok. I will add a new wrapper to get the TDREPORT.
+/*
- Add a wrapper for TDG.MR.REPORT TDCALL. It is used in TDX guest
- driver module to get the TDREPORT.
*/
+long tdx_mcall_get_report(void *reportdata, void *tdreport, u8 subtype)
Why "long"?
We used long because __tdx_module_call() call returns u64 value.
Great, then use u64 please. Or if you are returning negative errors, use s64 to be specific.
Alternatively, we can also check for return value of __tdx_module_call() here and return 0/-EIO as return values. In this case we can change return value to int.
That would make more sense, right?
Why void *? Don't you have real types for these?
We use these buffers as an intermediary to transfer data between userspace and the TDX module. In the kernel we don't consume these datas. So we did not define the type of the data.
Then these are userspace pointers? Why are they not marked as such?
thanks,
greg k-h
On 10/12/22 10:26 AM, Greg Kroah-Hartman wrote:
On Wed, Oct 12, 2022 at 10:13:50AM -0700, Sathyanarayanan Kuppuswamy wrote:
Hi,
On 10/12/22 9:23 AM, Greg Kroah-Hartman wrote:
On Wed, Oct 12, 2022 at 08:44:04AM -0700, Sathyanarayanan Kuppuswamy wrote:
On 10/12/22 7:27 AM, Borislav Petkov wrote:
On Wed, Oct 12, 2022 at 06:35:56AM -0700, Sathyanarayanan Kuppuswamy wrote:
So we should create a new wrapper for this use case or use
Yes, you got it - a new wrapper pls.
Ok. I will add a new wrapper to get the TDREPORT.
+/*
- Add a wrapper for TDG.MR.REPORT TDCALL. It is used in TDX guest
- driver module to get the TDREPORT.
*/
+long tdx_mcall_get_report(void *reportdata, void *tdreport, u8 subtype)
Why "long"?
We used long because __tdx_module_call() call returns u64 value.
Great, then use u64 please. Or if you are returning negative errors, use s64 to be specific.
Alternatively, we can also check for return value of __tdx_module_call() here and return 0/-EIO as return values. In this case we can change return value to int.
That would make more sense, right?
Yes. I will change it as mentioned above.
Why void *? Don't you have real types for these?
We use these buffers as an intermediary to transfer data between userspace and the TDX module. In the kernel we don't consume these datas. So we did not define the type of the data.
Then these are userspace pointers? Why are they not marked as such?
They are not userspace pointers. Since we need to pass physical addresses of reportdata and tdreport buffers to the TDX Module, we cannot directly use userspace pointers. So we allocate these intermediary buffers in the TDX guest driver and use it to copy the data from/to user pointers.
thanks,
greg k-h
TDX guest driver exposes IOCTL interfaces to service TDX guest user-specific requests. Currently, it is only used to allow the user to get the TDREPORT to support TDX attestation.
Details about the TDX attestation process are documented in Documentation/x86/tdx.rst, and the IOCTL details are documented in Documentation/virt/coco/tdx-guest.rst.
Operations like getting TDREPORT 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. The AMD sev-guest driver also uses IOCTL interface to support attestation.
[Bagas Sanjaya: Ack is for documentation portion] Acked-by: Kai Huang kai.huang@intel.com Acked-by: Kirill A. Shutemov kirill.shutemov@linux.intel.com Acked-by: Wander Lairson Costa wander@redhat.com Reviewed-by: Bagas Sanjaya bagasdotme@gmail.com Reviewed-by: Tony Luck tony.luck@intel.com Reviewed-by: Mika Westerberg mika.westerberg@linux.intel.com Signed-off-by: Kuppuswamy Sathyanarayanan sathyanarayanan.kuppuswamy@linux.intel.com ---
Changes since v13: * Converted the driver from built-in to a driver module as per Greg's suggestion. * Moved the driver to drivers/virt/coco to match AMD SEV. * Added support to autoload the driver based on X86_FEATURE_TDX_GUEST CPU feature. * Squashed patch titled "Documentation/x86: Document TDX attestation process" with this patch. * Since the attestation process is already documented in Documentation/x86/tdx.rst, remove it from the commit log. * Modified the commit log to match the new format. * Explicitly included the required header files. * Fixed magic number usage in reserved member check.
Changes since v13: * Fixed the commit log as per review suggestion. * Explicitly included the required header files. * Fixed magic number usage in reserved member check.
Changes since v12: * Added check to ensure reserved entries are set as 0.
Changes since v11: * Renamed DRIVER_NAME to TDX_GUEST_DEVICE and moved it to arch/x86/include/uapi/asm/tdx.h. * Fixed default error number in tdx_guest_ioctl(). * Moved tdx_misc_dev definition out of tdx_guest_init() as per Greg's suggestion. * Reordered struct tdx_report_req to avoid holes and added required padding.
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/
Documentation/virt/coco/tdx-guest.rst | 42 ++++++++ Documentation/virt/index.rst | 1 + Documentation/x86/tdx.rst | 43 ++++++++ arch/x86/include/asm/tdx.h | 1 + drivers/virt/Kconfig | 2 + drivers/virt/Makefile | 1 + drivers/virt/coco/tdx-guest/Kconfig | 10 ++ drivers/virt/coco/tdx-guest/Makefile | 2 + drivers/virt/coco/tdx-guest/tdx-guest.c | 131 ++++++++++++++++++++++++ include/uapi/linux/tdx-guest.h | 53 ++++++++++ 10 files changed, 286 insertions(+) create mode 100644 Documentation/virt/coco/tdx-guest.rst create mode 100644 drivers/virt/coco/tdx-guest/Kconfig create mode 100644 drivers/virt/coco/tdx-guest/Makefile create mode 100644 drivers/virt/coco/tdx-guest/tdx-guest.c create mode 100644 include/uapi/linux/tdx-guest.h
diff --git a/Documentation/virt/coco/tdx-guest.rst b/Documentation/virt/coco/tdx-guest.rst new file mode 100644 index 000000000000..4fe72829bdd0 --- /dev/null +++ b/Documentation/virt/coco/tdx-guest.rst @@ -0,0 +1,42 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=================================================================== +TDX Guest API Documentation +=================================================================== + +1. General description +====================== + +The TDX guest driver exposes IOCTL interfaces via /dev/tdx-guest misc +device to allow userspace to get certain TDX guest specific details. + +2. API description +================== + +In this section, for each supported IOCTL, following information is +provided along with a 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). + +2.1 TDX_CMD_GET_REPORT +---------------------- + +:Input parameters: struct tdx_report_req +:Output: Upon successful execution, TDREPORT data is copied to + tdx_report_req.tdreport and return 0. Return -EIO on + TDCALL failure or standard error number on other common + failures. + +The TDX_CMD_GET_REPORT IOCTL can be used by the attestation software to +get the TDREPORT from the TDX module using TDCALL[TDG.MR.REPORT]. + +Reference +--------- + +TDX reference material is collected here: + +https://www.intel.com/content/www/us/en/developer/articles/technical/intel-t... + +The driver is based on TDX module specification v1.0 and TDX GHCI specification v1.0. diff --git a/Documentation/virt/index.rst b/Documentation/virt/index.rst index 2f1cffa87b1b..56e003ff28ff 100644 --- a/Documentation/virt/index.rst +++ b/Documentation/virt/index.rst @@ -14,6 +14,7 @@ Linux Virtualization Support ne_overview acrn/index coco/sev-guest + coco/tdx-guest hyperv/index
.. only:: html and subproject diff --git a/Documentation/x86/tdx.rst b/Documentation/x86/tdx.rst index b8fa4329e1a5..014b769923a4 100644 --- a/Documentation/x86/tdx.rst +++ b/Documentation/x86/tdx.rst @@ -210,6 +210,49 @@ 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 want to use attestation to verify that the guest is the +desired one before releasing the encryption keys to mount the encrypted +rootfs or secondary drive. + +The TDX module records the state of the TDX guest in various stages of +the guest boot process using build time measurement register (MRTD) and +runtime measurement registers (RTMR). Measurements related to guest +initial configuration and firmware image are 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 as an example, please refer to TDX +Virtual Firmware design specification, sec titled "TD Measurement". At +TDX guest runtime, the attestation process is used to attest to these +measurements. + +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. 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". + +After getting the TDREPORT, the second step of the attestation process +is to send it to the Quoting Enclave (QE) to generate the Quote. 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 Quoting Enclave to verify the TDREPORT locally +and convert it to a remotely verifiable Quote. Method of sending TDREPORT +to QE is implementation specific. Attestation software can choose +whatever communication channel available (i.e. vsock or TCP/IP) to +send the TDREPORT to QE and receive the Quote. + References ==========
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 34c00d8a5263..f710035a0bdb 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -21,6 +21,7 @@ /* 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
#ifndef __ASSEMBLY__ diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig index 87ef258cec64..f79ab13a5c28 100644 --- a/drivers/virt/Kconfig +++ b/drivers/virt/Kconfig @@ -52,4 +52,6 @@ source "drivers/virt/coco/efi_secret/Kconfig"
source "drivers/virt/coco/sev-guest/Kconfig"
+source "drivers/virt/coco/tdx-guest/Kconfig" + endif diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile index 093674e05c40..e9aa6fc96fab 100644 --- a/drivers/virt/Makefile +++ b/drivers/virt/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/ obj-$(CONFIG_ACRN_HSM) += acrn/ obj-$(CONFIG_EFI_SECRET) += coco/efi_secret/ obj-$(CONFIG_SEV_GUEST) += coco/sev-guest/ +obj-$(CONFIG_INTEL_TDX_GUEST) += coco/tdx-guest/ diff --git a/drivers/virt/coco/tdx-guest/Kconfig b/drivers/virt/coco/tdx-guest/Kconfig new file mode 100644 index 000000000000..14246fc2fb02 --- /dev/null +++ b/drivers/virt/coco/tdx-guest/Kconfig @@ -0,0 +1,10 @@ +config TDX_GUEST_DRIVER + tristate "TDX Guest driver" + depends on INTEL_TDX_GUEST + help + The driver provides userspace interface to communicate with + the TDX module to request the TDX guest details like attestation + report. + + To compile this driver as module, choose M here. The module will + be called tdx-guest. diff --git a/drivers/virt/coco/tdx-guest/Makefile b/drivers/virt/coco/tdx-guest/Makefile new file mode 100644 index 000000000000..4396ec319589 --- /dev/null +++ b/drivers/virt/coco/tdx-guest/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_TDX_GUEST_DRIVER) += tdx-guest.o diff --git a/drivers/virt/coco/tdx-guest/tdx-guest.c b/drivers/virt/coco/tdx-guest/tdx-guest.c new file mode 100644 index 000000000000..3a4aa5590963 --- /dev/null +++ b/drivers/virt/coco/tdx-guest/tdx-guest.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TDX guest user interface driver + * + * Copyright (C) 2022 Intel Corporation + */ + +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/string.h> +#include <linux/uaccess.h> + +#include <uapi/linux/tdx-guest.h> + +#include <asm/cpu_device_id.h> +#include <asm/tdx.h> + +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. + */ + if (req.subtype || req.rpd_len != TDX_REPORTDATA_LEN || + req.tdr_len != TDX_REPORT_LEN) + return -EINVAL; + + if (memchr_inv(req.reserved, 0, sizeof(req.reserved))) + return -EINVAL; + + reportdata = kmalloc(req.rpd_len, GFP_KERNEL); + if (!reportdata) + return -ENOMEM; + + tdreport = kzalloc(req.tdr_len, GFP_KERNEL); + if (!tdreport) { + ret = -ENOMEM; + goto out; + } + + 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) +{ + switch (cmd) { + case TDX_CMD_GET_REPORT: + return tdx_get_report((void __user *)arg); + default: + return -ENOTTY; + } +} + +static const struct file_operations tdx_guest_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = tdx_guest_ioctl, + .llseek = no_llseek, +}; + +static struct miscdevice tdx_misc_dev = { + .name = TDX_GUEST_DEVICE, + .minor = MISC_DYNAMIC_MINOR, + .fops = &tdx_guest_fops, +}; + +static int __init tdx_guest_init(void) +{ + if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) + return -ENODEV; + + return misc_register(&tdx_misc_dev); +} +module_init(tdx_guest_init); + +static void __exit tdx_guest_exit(void) +{ + misc_deregister(&tdx_misc_dev); +} +module_exit(tdx_guest_exit); + +static const struct x86_cpu_id tdx_guest_ids[] = { + X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); + +MODULE_AUTHOR("Kuppuswamy Sathyanarayanan sathyanarayanan.kuppuswamy@linux.intel.com"); +MODULE_DESCRIPTION("TDX Guest Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/uapi/linux/tdx-guest.h b/include/uapi/linux/tdx-guest.h new file mode 100644 index 000000000000..8a3785eac3de --- /dev/null +++ b/include/uapi/linux/tdx-guest.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_TDX_GUEST_H_ +#define _UAPI_LINUX_TDX_GUEST_H_ + +#include <linux/ioctl.h> +#include <linux/types.h> + +#define TDX_GUEST_DEVICE "tdx-guest" + +/* 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. + * + * @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. + * @tdreport: TDREPORT output from TDCALL[TDG.MR.REPORT]. + * @rpd_len: Length of the REPORTDATA (fixed as 64 bytes by the TDX + * Module specification, but a parameter is added to handle future + * extension). + * @tdr_len: Length of the TDREPORT (fixed as 1024 bytes by the TDX + * Module specification, but a parameter is added to accommodate + * future extension). + * @subtype: Subtype of TDREPORT (fixed as 0 by the TDX Module specification, + * but added a parameter to handle future extension). + * @reserved: Reserved entries to handle future requirements. Should be + * filled with zeroes. + * + * Used in TDX_CMD_GET_REPORT IOCTL request. + */ +struct tdx_report_req { + __u64 reportdata; + __u64 tdreport; + __u32 rpd_len; + __u32 tdr_len; + __u8 subtype; + __u8 reserved[7]; +}; + +/* + * 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', 1, struct tdx_report_req) + +#endif /* _UAPI_LINUX_TDX_GUEST_H_ */
On Wed, Sep 28, 2022 at 02:55:34PM -0700, Kuppuswamy Sathyanarayanan wrote:
TDX guest driver exposes IOCTL interfaces to service TDX guest user-specific requests. Currently, it is only used to allow the user to get the TDREPORT to support TDX attestation.
Details about the TDX attestation process are documented in Documentation/x86/tdx.rst, and the IOCTL details are documented in Documentation/virt/coco/tdx-guest.rst.
Operations like getting TDREPORT 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. The AMD sev-guest driver also uses IOCTL interface to support attestation.
[Bagas Sanjaya: Ack is for documentation portion] Acked-by: Kai Huang kai.huang@intel.com Acked-by: Kirill A. Shutemov kirill.shutemov@linux.intel.com Acked-by: Wander Lairson Costa wander@redhat.com Reviewed-by: Bagas Sanjaya bagasdotme@gmail.com Reviewed-by: Tony Luck tony.luck@intel.com Reviewed-by: Mika Westerberg mika.westerberg@linux.intel.com Signed-off-by: Kuppuswamy Sathyanarayanan sathyanarayanan.kuppuswamy@linux.intel.com
Changes since v13:
- Converted the driver from built-in to a driver module as per Greg's suggestion.
- Moved the driver to drivers/virt/coco to match AMD SEV.
- Added support to autoload the driver based on X86_FEATURE_TDX_GUEST CPU feature.
- Squashed patch titled "Documentation/x86: Document TDX attestation process" with this patch.
- Since the attestation process is already documented in Documentation/x86/tdx.rst, remove it from the commit log.
- Modified the commit log to match the new format.
- Explicitly included the required header files.
- Fixed magic number usage in reserved member check.
Changes since v13:
- Fixed the commit log as per review suggestion.
- Explicitly included the required header files.
- Fixed magic number usage in reserved member check.
Changes since v12:
- Added check to ensure reserved entries are set as 0.
Changes since v11:
- Renamed DRIVER_NAME to TDX_GUEST_DEVICE and moved it to arch/x86/include/uapi/asm/tdx.h.
- Fixed default error number in tdx_guest_ioctl().
- Moved tdx_misc_dev definition out of tdx_guest_init() as per Greg's suggestion.
- Reordered struct tdx_report_req to avoid holes and added required padding.
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/
Documentation/virt/coco/tdx-guest.rst | 42 ++++++++ Documentation/virt/index.rst | 1 + Documentation/x86/tdx.rst | 43 ++++++++ arch/x86/include/asm/tdx.h | 1 + drivers/virt/Kconfig | 2 + drivers/virt/Makefile | 1 + drivers/virt/coco/tdx-guest/Kconfig | 10 ++ drivers/virt/coco/tdx-guest/Makefile | 2 + drivers/virt/coco/tdx-guest/tdx-guest.c | 131 ++++++++++++++++++++++++ include/uapi/linux/tdx-guest.h | 53 ++++++++++ 10 files changed, 286 insertions(+) create mode 100644 Documentation/virt/coco/tdx-guest.rst create mode 100644 drivers/virt/coco/tdx-guest/Kconfig create mode 100644 drivers/virt/coco/tdx-guest/Makefile create mode 100644 drivers/virt/coco/tdx-guest/tdx-guest.c create mode 100644 include/uapi/linux/tdx-guest.h
diff --git a/Documentation/virt/coco/tdx-guest.rst b/Documentation/virt/coco/tdx-guest.rst new file mode 100644 index 000000000000..4fe72829bdd0 --- /dev/null +++ b/Documentation/virt/coco/tdx-guest.rst @@ -0,0 +1,42 @@ +.. SPDX-License-Identifier: GPL-2.0
+=================================================================== +TDX Guest API Documentation +===================================================================
+1. General description +======================
+The TDX guest driver exposes IOCTL interfaces via /dev/tdx-guest misc +device to allow userspace to get certain TDX guest specific details.
+2. API description +==================
+In this section, for each supported IOCTL, following information is +provided along with a 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).
+2.1 TDX_CMD_GET_REPORT +----------------------
+:Input parameters: struct tdx_report_req +:Output: Upon successful execution, TDREPORT data is copied to
tdx_report_req.tdreport and return 0. Return -EIO on
TDCALL failure or standard error number on other common
failures.
+The TDX_CMD_GET_REPORT IOCTL can be used by the attestation software to +get the TDREPORT from the TDX module using TDCALL[TDG.MR.REPORT].
+Reference +---------
+TDX reference material is collected here:
+https://www.intel.com/content/www/us/en/developer/articles/technical/intel-t...
+The driver is based on TDX module specification v1.0 and TDX GHCI specification v1.0. diff --git a/Documentation/virt/index.rst b/Documentation/virt/index.rst index 2f1cffa87b1b..56e003ff28ff 100644 --- a/Documentation/virt/index.rst +++ b/Documentation/virt/index.rst @@ -14,6 +14,7 @@ Linux Virtualization Support ne_overview acrn/index coco/sev-guest
- coco/tdx-guest hyperv/index
.. only:: html and subproject diff --git a/Documentation/x86/tdx.rst b/Documentation/x86/tdx.rst index b8fa4329e1a5..014b769923a4 100644 --- a/Documentation/x86/tdx.rst +++ b/Documentation/x86/tdx.rst @@ -210,6 +210,49 @@ 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 want to use attestation to verify that the guest is the +desired one before releasing the encryption keys to mount the encrypted +rootfs or secondary drive.
+The TDX module records the state of the TDX guest in various stages of +the guest boot process using build time measurement register (MRTD) and +runtime measurement registers (RTMR). Measurements related to guest +initial configuration and firmware image are 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 as an example, please refer to TDX +Virtual Firmware design specification, sec titled "TD Measurement". At +TDX guest runtime, the attestation process is used to attest to these +measurements.
+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. 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".
+After getting the TDREPORT, the second step of the attestation process +is to send it to the Quoting Enclave (QE) to generate the Quote. 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 Quoting Enclave to verify the TDREPORT locally +and convert it to a remotely verifiable Quote. Method of sending TDREPORT +to QE is implementation specific. Attestation software can choose +whatever communication channel available (i.e. vsock or TCP/IP) to +send the TDREPORT to QE and receive the Quote.
References
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 34c00d8a5263..f710035a0bdb 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -21,6 +21,7 @@ /* 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 #ifndef __ASSEMBLY__ diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig index 87ef258cec64..f79ab13a5c28 100644 --- a/drivers/virt/Kconfig +++ b/drivers/virt/Kconfig @@ -52,4 +52,6 @@ source "drivers/virt/coco/efi_secret/Kconfig" source "drivers/virt/coco/sev-guest/Kconfig" +source "drivers/virt/coco/tdx-guest/Kconfig"
endif diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile index 093674e05c40..e9aa6fc96fab 100644 --- a/drivers/virt/Makefile +++ b/drivers/virt/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/ obj-$(CONFIG_ACRN_HSM) += acrn/ obj-$(CONFIG_EFI_SECRET) += coco/efi_secret/ obj-$(CONFIG_SEV_GUEST) += coco/sev-guest/ +obj-$(CONFIG_INTEL_TDX_GUEST) += coco/tdx-guest/ diff --git a/drivers/virt/coco/tdx-guest/Kconfig b/drivers/virt/coco/tdx-guest/Kconfig new file mode 100644 index 000000000000..14246fc2fb02 --- /dev/null +++ b/drivers/virt/coco/tdx-guest/Kconfig @@ -0,0 +1,10 @@ +config TDX_GUEST_DRIVER
- tristate "TDX Guest driver"
- depends on INTEL_TDX_GUEST
- help
The driver provides userspace interface to communicate with
the TDX module to request the TDX guest details like attestation
report.
To compile this driver as module, choose M here. The module will
be called tdx-guest.
diff --git a/drivers/virt/coco/tdx-guest/Makefile b/drivers/virt/coco/tdx-guest/Makefile new file mode 100644 index 000000000000..4396ec319589 --- /dev/null +++ b/drivers/virt/coco/tdx-guest/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_TDX_GUEST_DRIVER) += tdx-guest.o diff --git a/drivers/virt/coco/tdx-guest/tdx-guest.c b/drivers/virt/coco/tdx-guest/tdx-guest.c new file mode 100644 index 000000000000..3a4aa5590963 --- /dev/null +++ b/drivers/virt/coco/tdx-guest/tdx-guest.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-only +/*
- TDX guest user interface driver
- Copyright (C) 2022 Intel Corporation
- */
+#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/string.h> +#include <linux/uaccess.h>
+#include <uapi/linux/tdx-guest.h>
+#include <asm/cpu_device_id.h> +#include <asm/tdx.h>
+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.
*/
- if (req.subtype || req.rpd_len != TDX_REPORTDATA_LEN ||
req.tdr_len != TDX_REPORT_LEN)
return -EINVAL;
- if (memchr_inv(req.reserved, 0, sizeof(req.reserved)))
return -EINVAL;
- reportdata = kmalloc(req.rpd_len, GFP_KERNEL);
- if (!reportdata)
return -ENOMEM;
- tdreport = kzalloc(req.tdr_len, GFP_KERNEL);
- if (!tdreport) {
ret = -ENOMEM;
goto out;
- }
- 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)
+{
- switch (cmd) {
- case TDX_CMD_GET_REPORT:
return tdx_get_report((void __user *)arg);
- default:
return -ENOTTY;
- }
+}
+static const struct file_operations tdx_guest_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = tdx_guest_ioctl,
- .llseek = no_llseek,
+};
+static struct miscdevice tdx_misc_dev = {
- .name = TDX_GUEST_DEVICE,
- .minor = MISC_DYNAMIC_MINOR,
- .fops = &tdx_guest_fops,
+};
+static int __init tdx_guest_init(void) +{
- if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
return -ENODEV;
- return misc_register(&tdx_misc_dev);
+} +module_init(tdx_guest_init);
+static void __exit tdx_guest_exit(void) +{
- misc_deregister(&tdx_misc_dev);
+} +module_exit(tdx_guest_exit);
+static const struct x86_cpu_id tdx_guest_ids[] = {
- X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL),
- {}
+}; +MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids);
+MODULE_AUTHOR("Kuppuswamy Sathyanarayanan sathyanarayanan.kuppuswamy@linux.intel.com"); +MODULE_DESCRIPTION("TDX Guest Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/uapi/linux/tdx-guest.h b/include/uapi/linux/tdx-guest.h new file mode 100644 index 000000000000..8a3785eac3de --- /dev/null +++ b/include/uapi/linux/tdx-guest.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_TDX_GUEST_H_ +#define _UAPI_LINUX_TDX_GUEST_H_
+#include <linux/ioctl.h> +#include <linux/types.h>
+#define TDX_GUEST_DEVICE "tdx-guest"
nit: I think now we can use KBUILD_MODNAME, can't we?
+/* 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.
- @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.
- @tdreport: TDREPORT output from TDCALL[TDG.MR.REPORT].
- @rpd_len: Length of the REPORTDATA (fixed as 64 bytes by the TDX
- Module specification, but a parameter is added to handle future
- extension).
- @tdr_len: Length of the TDREPORT (fixed as 1024 bytes by the TDX
- Module specification, but a parameter is added to accommodate
- future extension).
- @subtype: Subtype of TDREPORT (fixed as 0 by the TDX Module specification,
- but added a parameter to handle future extension).
- @reserved: Reserved entries to handle future requirements. Should be
- filled with zeroes.
- Used in TDX_CMD_GET_REPORT IOCTL request.
- */
+struct tdx_report_req {
- __u64 reportdata;
- __u64 tdreport;
- __u32 rpd_len;
- __u32 tdr_len;
- __u8 subtype;
- __u8 reserved[7];
+};
+/*
- 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', 1, struct tdx_report_req)
+#endif /* _UAPI_LINUX_TDX_GUEST_H_ */
2.34.1
On 9/29/22 11:02 AM, Wander Lairson Costa wrote:
+#define TDX_GUEST_DEVICE "tdx-guest"
nit: I think now we can use KBUILD_MODNAME, can't we?
Yes. We can use it. But I thought user can use this macro and avoid hard coding the device name.
On Thu, Sep 29, 2022 at 11:11:47AM -0700, Sathyanarayanan Kuppuswamy wrote:
On 9/29/22 11:02 AM, Wander Lairson Costa wrote:
+#define TDX_GUEST_DEVICE "tdx-guest"
nit: I think now we can use KBUILD_MODNAME, can't we?
Yes. We can use it. But I thought user can use this macro and avoid hard coding the device name.
What user? Please use KBUILD_MODNAME now instead.
thanks,
greg k-h
On 9/30/22 5:22 AM, Greg Kroah-Hartman wrote:
On Thu, Sep 29, 2022 at 11:11:47AM -0700, Sathyanarayanan Kuppuswamy wrote:
On 9/29/22 11:02 AM, Wander Lairson Costa wrote:
+#define TDX_GUEST_DEVICE "tdx-guest"
nit: I think now we can use KBUILD_MODNAME, can't we?
Yes. We can use it. But I thought user can use this macro and avoid hard coding the device name.
What user? Please use KBUILD_MODNAME now instead.
Ok. I will change it.
thanks,
greg k-h
On Wed, Sep 28, 2022 at 02:55:34PM -0700, Kuppuswamy Sathyanarayanan wrote:
+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.
*/
- if (req.subtype || req.rpd_len != TDX_REPORTDATA_LEN ||
req.tdr_len != TDX_REPORT_LEN)
return -EINVAL;
- if (memchr_inv(req.reserved, 0, sizeof(req.reserved)))
I'm guessing we should issue something here to userspace to let it know why the it fails getting the report.
Otherwise it is starting to look good and boring. :)
Thx.
On 10/12/22 8:47 AM, Borislav Petkov wrote:
On Wed, Sep 28, 2022 at 02:55:34PM -0700, Kuppuswamy Sathyanarayanan wrote:
+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.
*/
- if (req.subtype || req.rpd_len != TDX_REPORTDATA_LEN ||
req.tdr_len != TDX_REPORT_LEN)
return -EINVAL;
- if (memchr_inv(req.reserved, 0, sizeof(req.reserved)))
I'm guessing we should issue something here to userspace to let it know why the it fails getting the report.
I am fine with adding an error message here. What about the above -EINVAL case? Do you suggest adding it there as well?
Otherwise it is starting to look good and boring. :)
Thx.
On Wed, Oct 12, 2022 at 04:01:31PM -0700, Sathyanarayanan Kuppuswamy wrote:
I am fine with adding an error message here. What about the above -EINVAL case? Do you suggest adding it there as well?
Right, you could dump the req.* fields it read from luserspace so that one staring at the error message can actually see which of the fields failed the check.
Thx.
Attestation is used to verify the trustworthiness of a TDX guest. During the guest bring-up, the 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 guest runtime, the attestation process is used to attest to these measurements.
The 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 ID to protect the integrity 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 enable guest userspace to get the TDREPORT.
Add a kernel self test module to test this ABI and verify the validity of the generated TDREPORT.
Reviewed-by: Tony Luck tony.luck@intel.com Reviewed-by: Mika Westerberg mika.westerberg@linux.intel.com Acked-by: Kai Huang kai.huang@intel.com Acked-by: Kirill A. Shutemov kirill.shutemov@linux.intel.com Signed-off-by: Kuppuswamy Sathyanarayanan sathyanarayanan.kuppuswamy@linux.intel.com ---
Changes since v13: * Removed __packed from TDREPORT structs. * Since the guest driver is moved to drivers/virt/coco, removed tools/arch/x86/include header folder usage. * Fixed struct comments to match kernel-doc format. * Fixed commit log as per review comments. * Fixed some format issues in the code.
Changes since v12: * Changed #ifdef DEBUG usage with if (DEBUG). * Initialized reserved entries values to zero.
Changes since v11: * Renamed devname with TDX_GUEST_DEVNAME.
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/testing/selftests/Makefile | 1 + tools/testing/selftests/tdx/Makefile | 7 + tools/testing/selftests/tdx/config | 1 + tools/testing/selftests/tdx/tdx_guest_test.c | 175 +++++++++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 tools/testing/selftests/tdx/Makefile create mode 100644 tools/testing/selftests/tdx/config create mode 100644 tools/testing/selftests/tdx/tdx_guest_test.c
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..8dd43517cd55 --- /dev/null +++ b/tools/testing/selftests/tdx/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +CFLAGS += -O3 -Wl,-no-as-needed -Wall -static + +TEST_GEN_PROGS := tdx_guest_test + +include ../lib.mk diff --git a/tools/testing/selftests/tdx/config b/tools/testing/selftests/tdx/config new file mode 100644 index 000000000000..aa1edc829ab6 --- /dev/null +++ b/tools/testing/selftests/tdx/config @@ -0,0 +1 @@ +CONFIG_TDX_GUEST_DRIVER=y diff --git a/tools/testing/selftests/tdx/tdx_guest_test.c b/tools/testing/selftests/tdx/tdx_guest_test.c new file mode 100644 index 000000000000..5232634d7354 --- /dev/null +++ b/tools/testing/selftests/tdx/tdx_guest_test.c @@ -0,0 +1,175 @@ +// 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 <sys/ioctl.h> + +#include <errno.h> +#include <fcntl.h> + +#include "../kselftest_harness.h" +#include "../../../../include/uapi/linux/tdx-guest.h" + +#define TDX_GUEST_DEVNAME "/dev/tdx-guest" +#define HEX_DUMP_SIZE 8 +#define DEBUG 0 + +/** + * struct tdreport_type - Type header of TDREPORT_STRUCT. + * @type: Type of the TDREPORT (0 - SGX, 81 - TDX, rest are reserved) + * @sub_type: Subtype of the TDREPORT (Default value is 0). + * @version: TDREPORT version (Default value is 0). + * @reserved: Added for future extension. + * + * More details can be found in TDX v1.0 module specification, sec + * titled "REPORTTYPE". + */ +struct tdreport_type { + __u8 type; + __u8 sub_type; + __u8 version; + __u8 reserved; +}; + +/** + * struct reportmac - TDX guest report data, MAC and TEE hashes. + * @type: TDREPORT type header. + * @reserved1: Reserved for future extension. + * @cpu_svn: CPU security version. + * @tee_tcb_info_hash: SHA384 hash of TEE TCB INFO. + * @tee_td_info_hash: SHA384 hash of TDINFO_STRUCT. + * @reportdata: User defined unique data passed in TDG.MR.REPORT request. + * @reserved2: Reserved for future extension. + * @mac: CPU MAC ID. + * + * It is MAC-protected and contains hashes of the remainder of the + * report structure along with user provided report data. More details can + * be found in TDX v1.0 Module specification, sec titled "REPORTMACSTRUCT" + */ +struct reportmac { + struct tdreport_type type; + __u8 reserved1[12]; + __u8 cpu_svn[16]; + __u8 tee_tcb_info_hash[48]; + __u8 tee_td_info_hash[48]; + __u8 reportdata[64]; + __u8 reserved2[32]; + __u8 mac[32]; +}; + +/** + * struct td_info - TDX guest measurements and configuration. + * @attr: TDX Guest attributes (like debug, spet_disable, etc). + * @xfam: Extended features allowed mask. + * @mrtd: Build time measurement register. + * @mrconfigid: Software-defined ID for non-owner-defined configuration + * of the guest - e.g., run-time or OS configuration. + * @mrowner: Software-defined ID for the guest owner. + * @mrownerconfig: Software-defined ID for owner-defined configuration of + * the guest - e.g., specific to the workload. + * @rtmr: Run time measurement registers. + * @reserved: Added for future extension. + * + * 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. More details can be found in TDX v1.0 + * Module specification, sec titled "TDINFO_STRUCT". + */ +struct td_info { + __u8 attr[8]; + __u64 xfam; + __u64 mrtd[6]; + __u64 mrconfigid[6]; + __u64 mrowner[6]; + __u64 mrownerconfig[6]; + __u64 rtmr[24]; + __u64 reserved[14]; +}; + +/* + * struct tdreport - Output of TDCALL[TDG.MR.REPORT]. + * @reportmac: Mac protected header of size 256 bytes. + * @tee_tcb_info: Additional attestable elements in the TCB are not + * reflected in the reportmac. + * @reserved: Added for future extension. + * @tdinfo: Measurements and configuration data of size 512 bytes. + * + * More details can be found in TDX v1.0 Module specification, sec + * titled "TDREPORT_STRUCT". + */ +struct tdreport { + struct reportmac reportmac; + __u8 tee_tcb_info[239]; + __u8 reserved[17]; + struct td_info tdinfo; +}; + +static void print_array_hex(const char *title, const char *prefix_str, + const void *buf, int len) +{ + int i, j, line_len, rowsize = HEX_DUMP_SIZE; + const __u8 *ptr = buf; + + if (!len || !buf) + return; + + printf("\t\t%s", title); + + for (j = 0; j < len; j += rowsize) { + line_len = rowsize < (len - j) ? rowsize : (len - j); + printf("%s%.8x:", prefix_str, j); + for (i = 0; i < line_len; i++) + printf(" %.2x", ptr[j + i]); + printf("\n"); + } + + printf("\n"); +} + +TEST(verify_report) +{ + __u8 reportdata[TDX_REPORTDATA_LEN]; + struct tdx_report_req req; + struct tdreport tdreport; + int devfd, i; + + devfd = open(TDX_GUEST_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); + + memset(req.reserved, 0, sizeof(req.reserved)); + + /* Get TDREPORT */ + ASSERT_EQ(0, ioctl(devfd, TDX_CMD_GET_REPORT, &req)); + + if (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)); + } + + /* 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
linux-kselftest-mirror@lists.linaro.org