diff mbox series

[v16,2/3] virt: Add TDX guest driver

Message ID 20221028002820.3303030-3-sathyanarayanan.kuppuswamy@linux.intel.com (mailing list archive)
State New
Headers show
Series Add TDX Guest Attestation support | expand

Commit Message

Kuppuswamy Sathyanarayanan Oct. 28, 2022, 12:28 a.m. UTC
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 v15:
 * Removed error messages in tdx_get_report() as per Greg's suggestion.
 * Removed #ifdef MODULE usage for MODULE_DEVICE_TABLE.
 * Added copyright info for the header file.

Changes since v14:
 * Used tdx_mcall_get_report() wrapper instead of __tdx_module_call()
   call.
 * Added pr_err() messages for some failure cases in tdx_get_report().
 * Used KBUILD_MODNAME instead of device name.
 * Rebased on top of v6.1-rc1

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 +++++++++
 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 | 121 ++++++++++++++++++++++++
 include/uapi/linux/tdx-guest.h          |  55 +++++++++++
 9 files changed, 277 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

Comments

Greg KH Oct. 28, 2022, 6:25 a.m. UTC | #1
On Thu, Oct 27, 2022 at 05:28:19PM -0700, Kuppuswamy Sathyanarayanan wrote:
> +	/*
> +	 * Check for valid input values. Per TDX Module 1.0
> +	 * specification, section titled "TDG.MR.REPORT",
> +	 * REPORTDATA length is fixed as TDX_REPORTDATA_LEN,

If this length is fixed, then you can never change it, so why have it at
all?

> +	 * 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 */
> +	ret = tdx_mcall_get_report(reportdata, tdreport, req.subtype);
> +	if (ret)
> +		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);

You know the type of this pointer here, why not cast it instead of
having to cast it from void * again?

> +	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 = KBUILD_MODNAME,
> +	.minor = MISC_DYNAMIC_MINOR,
> +	.fops = &tdx_guest_fops,
> +};
> +
> +static const struct x86_cpu_id tdx_guest_ids[] = {
> +	X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL),
> +	{}
> +};
> +MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids);
> +
> +static int __init tdx_guest_init(void)
> +{
> +	if (!x86_match_cpu(tdx_guest_ids))
> +		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);
> +
> +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..29453e6a7ced
> --- /dev/null
> +++ b/include/uapi/linux/tdx-guest.h
> @@ -0,0 +1,55 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Userspace interface for TDX guest driver
> + *
> + * Copyright (C) 2022 Intel Corporation
> + */
> +
> +#ifndef _UAPI_LINUX_TDX_GUEST_H_
> +#define _UAPI_LINUX_TDX_GUEST_H_
> +
> +#include <linux/ioctl.h>
> +#include <linux/types.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

As these are fixed values, why do you have to say them again in the
structure?

If you ever change them, wonderful, make a new ioctl.  You can't change
this one as userspace would have to also change, there is no way you
could mix/match kernel versions and userspace and not have problems.

So just fix these values, like you have, and remove them from the
structure definition as there's no way you can change them anyway.

> +
> +/**
> + * struct tdx_report_req - Request struct for TDX_CMD_GET_REPORT IOCTL.
> + *
> + * @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].

These are userspace pointers, document them as such please.

> + * @rpd_len: Length of the REPORTDATA (fixed as 64 bytes by the TDX Module
> + *           specification, but a parameter is added to handle future
> + *           extension).

As I say above, this can't be changed, right?

> + * @tdr_len: Length of the TDREPORT (fixed as 1024 bytes by the TDX Module
> + *           specification, but a parameter is added to accommodate future
> + *           extension).

Again, can't be changed.

> + * @subtype: Subtype of TDREPORT (fixed as 0 by the TDX Module specification,
> + *           but added a parameter to handle future extension).

If this too is fixed, you can't change it.

thanks,

greg k-h
Kuppuswamy Sathyanarayanan Oct. 29, 2022, 11:17 p.m. UTC | #2
Hi Greg

On 10/27/22 11:25 PM, Greg Kroah-Hartman wrote:
> On Thu, Oct 27, 2022 at 05:28:19PM -0700, Kuppuswamy Sathyanarayanan wrote:

>> +
>> +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);
> 
> You know the type of this pointer here, why not cast it instead of
> having to cast it from void * again?

The only place we use arg pointer is in copy_from_user() function,
which expects void __user * pointer. So why cast it as struct
tdx_report_req * here?


>> +
>> +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..29453e6a7ced
>> --- /dev/null
>> +++ b/include/uapi/linux/tdx-guest.h
>> @@ -0,0 +1,55 @@
>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>> +/*
>> + * Userspace interface for TDX guest driver
>> + *
>> + * Copyright (C) 2022 Intel Corporation
>> + */
>> +
>> +#ifndef _UAPI_LINUX_TDX_GUEST_H_
>> +#define _UAPI_LINUX_TDX_GUEST_H_
>> +
>> +#include <linux/ioctl.h>
>> +#include <linux/types.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
> 
> As these are fixed values, why do you have to say them again in the
> structure?

These length recommendations are provided by the TDX Module, and there is
a slight possibility that the TDX Module will increase the maximum size
of the REPORTDATA and TDREPORT in the future. To handle such length
changes, rather than inventing a new IOCTL for it in the future, making
the current one flexible to handle such changes seems better. One less ABI
to maintain is always better, right? My initial design did use fixed size
buffers like you have recommended, but later changed it as per review
suggestion to make the ABI flexible.


> 
> If you ever change them, wonderful, make a new ioctl.  You can't change
> this one as userspace would have to also change, there is no way you
> could mix/match kernel versions and userspace and not have problems.


Removing the length based checks in the kernel (in tdx_get_report()) and
directly passing the user input to the TDX Module can also avoid the 
usespace/kernel version mix/match issues you have mentioned. Does such
a solution acceptable?

> 
> So just fix these values, like you have, and remove them from the
> structure definition as there's no way you can change them anyway.
> 

With above details, if you think it is still better to remove the length
params, I can make the change.


>> +
>> +/**
>> + * struct tdx_report_req - Request struct for TDX_CMD_GET_REPORT IOCTL.
>> + *
>> + * @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].
> 
> These are userspace pointers, document them as such please.

Will following change do?

@reportdata: Address of the user buffer with user-defined REPORTDATA to be
             included into TDREPORT.
@tdreport: Address of the user buffer to store TDREPORT output from TDCALL[TDG.MR.REPORT]


> 
> thanks,
> 
> greg k-h
Greg KH Oct. 30, 2022, 6:53 a.m. UTC | #3
On Sat, Oct 29, 2022 at 04:17:39PM -0700, Sathyanarayanan Kuppuswamy wrote:
> Hi Greg
> 
> On 10/27/22 11:25 PM, Greg Kroah-Hartman wrote:
> > On Thu, Oct 27, 2022 at 05:28:19PM -0700, Kuppuswamy Sathyanarayanan wrote:
> 
> >> +
> >> +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);
> > 
> > You know the type of this pointer here, why not cast it instead of
> > having to cast it from void * again?
> 
> The only place we use arg pointer is in copy_from_user() function,
> which expects void __user * pointer. So why cast it as struct
> tdx_report_req * here?

Because then your function will show the true type and you don't have to
cast it again.

> >> +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..29453e6a7ced
> >> --- /dev/null
> >> +++ b/include/uapi/linux/tdx-guest.h
> >> @@ -0,0 +1,55 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> >> +/*
> >> + * Userspace interface for TDX guest driver
> >> + *
> >> + * Copyright (C) 2022 Intel Corporation
> >> + */
> >> +
> >> +#ifndef _UAPI_LINUX_TDX_GUEST_H_
> >> +#define _UAPI_LINUX_TDX_GUEST_H_
> >> +
> >> +#include <linux/ioctl.h>
> >> +#include <linux/types.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
> > 
> > As these are fixed values, why do you have to say them again in the
> > structure?
> 
> These length recommendations are provided by the TDX Module, and there is
> a slight possibility that the TDX Module will increase the maximum size
> of the REPORTDATA and TDREPORT in the future.

We do not write kernel code for "slight possibilities sometime in the
future".

> To handle such length
> changes, rather than inventing a new IOCTL for it in the future, making
> the current one flexible to handle such changes seems better.

Please work through the code and see how that would really look, and
what would break if you were to change that in the future (remember
kernel code and userspace code is not upgraded at the same time.)

> One less ABI
> to maintain is always better, right? My initial design did use fixed size
> buffers like you have recommended, but later changed it as per review
> suggestion to make the ABI flexible.

Again, work through and try to determine if the added complexity will
really work here.

What is wrong with just adding a new ioctl if in the future, you really
do need to change something?  That way you are sure that nothing will
break and userspace will be finen with it.  It is not like you are
forbidden to add new ioctls later, you would have to change the kernel
code no matter what anyway.

Keep it simple please.

greg k-h
Wander Lairson Costa Nov. 1, 2022, 1:33 p.m. UTC | #4
On Sun, Oct 30, 2022 at 07:53:19AM +0100, Greg Kroah-Hartman wrote:
> On Sat, Oct 29, 2022 at 04:17:39PM -0700, Sathyanarayanan Kuppuswamy wrote:
> > Hi Greg
> > 
> > On 10/27/22 11:25 PM, Greg Kroah-Hartman wrote:
> > > On Thu, Oct 27, 2022 at 05:28:19PM -0700, Kuppuswamy Sathyanarayanan wrote:
> > 
> > >> +
> > >> +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);
> > > 
> > > You know the type of this pointer here, why not cast it instead of
> > > having to cast it from void * again?
> > 
> > The only place we use arg pointer is in copy_from_user() function,
> > which expects void __user * pointer. So why cast it as struct
> > tdx_report_req * here?
> 
> Because then your function will show the true type and you don't have to
> cast it again.
> 

If we are taking this route, isn't better to move the copy_from_user
call to tdx_guest_ioctl and pass the resulting struct tdx_report_req
pointer to tdx_get_report?
Kuppuswamy Sathyanarayanan Nov. 2, 2022, 6:18 a.m. UTC | #5
Hi Greg,

On 10/29/22 11:53 PM, Greg Kroah-Hartman wrote:
> On Sat, Oct 29, 2022 at 04:17:39PM -0700, Sathyanarayanan Kuppuswamy wrote:
>> Hi Greg
>>
>> On 10/27/22 11:25 PM, Greg Kroah-Hartman wrote:
>>> On Thu, Oct 27, 2022 at 05:28:19PM -0700, Kuppuswamy Sathyanarayanan wrote:
>>
>>>> +
>>>> +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);
>>>
>>> You know the type of this pointer here, why not cast it instead of
>>> having to cast it from void * again?
>>
>> The only place we use arg pointer is in copy_from_user() function,
>> which expects void __user * pointer. So why cast it as struct
>> tdx_report_req * here?
> 
> Because then your function will show the true type and you don't have to
> cast it again.
> 
>>>> +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..29453e6a7ced
>>>> --- /dev/null
>>>> +++ b/include/uapi/linux/tdx-guest.h
>>>> @@ -0,0 +1,55 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>>>> +/*
>>>> + * Userspace interface for TDX guest driver
>>>> + *
>>>> + * Copyright (C) 2022 Intel Corporation
>>>> + */
>>>> +
>>>> +#ifndef _UAPI_LINUX_TDX_GUEST_H_
>>>> +#define _UAPI_LINUX_TDX_GUEST_H_
>>>> +
>>>> +#include <linux/ioctl.h>
>>>> +#include <linux/types.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
>>>
>>> As these are fixed values, why do you have to say them again in the
>>> structure?
>>
>> These length recommendations are provided by the TDX Module, and there is
>> a slight possibility that the TDX Module will increase the maximum size
>> of the REPORTDATA and TDREPORT in the future.
> 
> We do not write kernel code for "slight possibilities sometime in the
> future".
> 
>> To handle such length
>> changes, rather than inventing a new IOCTL for it in the future, making
>> the current one flexible to handle such changes seems better.
> 
> Please work through the code and see how that would really look, and
> what would break if you were to change that in the future (remember
> kernel code and userspace code is not upgraded at the same time.)
> 
>> One less ABI
>> to maintain is always better, right? My initial design did use fixed size
>> buffers like you have recommended, but later changed it as per review
>> suggestion to make the ABI flexible.
> 
> Again, work through and try to determine if the added complexity will
> really work here.
> 
> What is wrong with just adding a new ioctl if in the future, you really
> do need to change something?  That way you are sure that nothing will
> break and userspace will be finen with it.  It is not like you are
> forbidden to add new ioctls later, you would have to change the kernel
> code no matter what anyway.
> 
> Keep it simple please.


The following are potential solutions to the possible kernel/userspace
mix/match issue that may arise in the future if the acceptable reportdata
length, tdreport length, or subtype values change.

I've attempted to do a sample implementation as you have suggested to
check the pros and cons for both solutions. Please let me know what you
think. Personally I prefer solution 2, as it handles the issue you have
raised and also keeps the ABI flexible.

Solution 1:
------------

This is based on your suggestion. I have dropped the IOCTL req members for
reportdata length (rpd_len), tdreport length (tdr_len) and subtype. I have
also used fixed size buffers to handle the current requirements.

Pros: Implementation is simple and clean.

Cons: May need to add new IOCTL for any future requirement updates.

Following are the ABI and IOCTL handler implementation details (Note: it
is not the complete code, only included required details to show how the
implementation looks):

--- /dev/null
+++ b/include/uapi/linux/tdx-guest.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Userspace interface for TDX guest driver
+ *
+ * Copyright (C) 2022 Intel Corporation
+ */
+
+#ifndef _UAPI_LINUX_TDX_GUEST_H_
+#define _UAPI_LINUX_TDX_GUEST_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.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 - Request struct for TDX_CMD_GET_REPORT IOCTL.
+ *
+ * @reportdata: User buffer with 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: User buffer to store TDREPORT output from TDCALL[TDG.MR.REPORT].
+ */
+struct tdx_report_req {
+       __u8 reportdata[TDX_REPORTDATA_LEN];
+       __u8 tdreport[TDX_REPORT_LEN];
+};

--- /dev/null
+++ b/drivers/virt/coco/tdx-guest/tdx-guest.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TDX guest user interface driver
+ *
+ * Copyright (C) 2022 Intel Corporation
+ */
+
+#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(struct tdx_report_req __user *ureq)
+{
+       u8 *reportdata, *tdreport;
+       long ret;
+
+       reportdata = kmalloc(TDX_REPORTDATA_LEN, GFP_KERNEL);
+       if (!reportdata)
+               return -ENOMEM;
+
+       tdreport = kzalloc(TDX_REPORT_LEN, GFP_KERNEL);
+       if (!tdreport) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (copy_from_user(reportdata, ureq->reportdata, TDX_REPORTDATA_LEN)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       /* Generate TDREPORT using "TDG.MR.REPORT" TDCALL */
+       ret = tdx_mcall_get_report(reportdata, tdreport);
+       if (ret)
+               goto out;
+
+       if (copy_to_user(ureq->tdreport, tdreport, TDX_REPORT_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((struct tdx_report_req __user *)arg);
+       default:
+               return -ENOTTY;
+       }
+}

Solution 2:
-----------

In this version, I removed all length and subtype related checks in the
kernel (in tdx get report()) and instead passed the user input directly
to TDG.MR.TDCALL, allowing the TDX Module to return success or failure
based on the user input. Because the kernel does not directly impose any
length or subtype constraints, the userspace/kernel mix/match issue will
not occur.

Pros:

ABI is flexible and we don't have to add new IOCTL if the acceptable
reportlength ,tdreport length or subtype values are changed by the
TDX Module in the future.

Cons:

For now, userspace will pass fixed values to length and subtype members. So
they not currently useful.

Following are the ABI and IOCTL handler implementation details (Note: it
is not the complete code, only included required details to show how the
implementation looks):

--- /dev/null
+++ b/include/uapi/linux/tdx-guest.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Userspace interface for TDX guest driver
+ *
+ * Copyright (C) 2022 Intel Corporation
+ */
+
+#ifndef _UAPI_LINUX_TDX_GUEST_H_
+#define _UAPI_LINUX_TDX_GUEST_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/**
+ * struct tdx_report_req - Request struct for TDX_CMD_GET_REPORT IOCTL.
+ *
+ * @reportdata: Address of user buffer with 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: Address of user buffer to store TDREPORT output from
+ *            TDCALL[TDG.MR.REPORT].
+ * @rpd_len: Length of the REPORTDATA. For acceptable values, refer to TDX
+ *           module specification v1.0 sec titled "TDG.MR.REPORT Leaf".
+ * @tdr_len: Length of the TDREPORT. For acceptable values, refer to TDX
+ *           module specification v1.0 sec titled "TDG.MR.REPORT Leaf".
+ * @subtype: Subtype of TDREPORT. For acceptable values, refer to TDX
+ *           module specification v1.0 sec titled "TDG.MR.REPORT Leaf".
+ * @reserved: Reserved entries to handle future requirements. Must be filled
+ *            with zeroes.
+ */
+struct tdx_report_req {
+       __u64 reportdata;
+       __u64 tdreport;
+       __u32 rpd_len;
+       __u32 tdr_len;
+       __u8 subtype;
+       __u8 reserved[7];
+};

--- /dev/null
+++ b/drivers/virt/coco/tdx-guest/tdx-guest.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TDX guest user interface driver
+ *
+ * Copyright (C) 2022 Intel Corporation
+ */
+
+#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(struct tdx_report_req __user *argp)
+{
+       u8 *reportdata, *tdreport;
+       struct tdx_report_req req;
+       long ret;
+
+       if (copy_from_user(&req, argp, sizeof(req)))
+               return -EFAULT;
+
+       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 */
+       ret = tdx_mcall_get_report(reportdata, tdreport, req.subtype);
+       if (ret)
+               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((struct tdx_report_req __user *)arg);
+       default:
+               return -ENOTTY;
+       }
+}


> 
> greg k-h
Greg KH Nov. 2, 2022, 6:58 a.m. UTC | #6
On Tue, Nov 01, 2022 at 11:18:29PM -0700, Sathyanarayanan Kuppuswamy wrote:
> Hi Greg,
> 
> On 10/29/22 11:53 PM, Greg Kroah-Hartman wrote:
> > On Sat, Oct 29, 2022 at 04:17:39PM -0700, Sathyanarayanan Kuppuswamy wrote:
> >> Hi Greg
> >>
> >> On 10/27/22 11:25 PM, Greg Kroah-Hartman wrote:
> >>> On Thu, Oct 27, 2022 at 05:28:19PM -0700, Kuppuswamy Sathyanarayanan wrote:
> >>
> >>>> +
> >>>> +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);
> >>>
> >>> You know the type of this pointer here, why not cast it instead of
> >>> having to cast it from void * again?
> >>
> >> The only place we use arg pointer is in copy_from_user() function,
> >> which expects void __user * pointer. So why cast it as struct
> >> tdx_report_req * here?
> > 
> > Because then your function will show the true type and you don't have to
> > cast it again.
> > 
> >>>> +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..29453e6a7ced
> >>>> --- /dev/null
> >>>> +++ b/include/uapi/linux/tdx-guest.h
> >>>> @@ -0,0 +1,55 @@
> >>>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> >>>> +/*
> >>>> + * Userspace interface for TDX guest driver
> >>>> + *
> >>>> + * Copyright (C) 2022 Intel Corporation
> >>>> + */
> >>>> +
> >>>> +#ifndef _UAPI_LINUX_TDX_GUEST_H_
> >>>> +#define _UAPI_LINUX_TDX_GUEST_H_
> >>>> +
> >>>> +#include <linux/ioctl.h>
> >>>> +#include <linux/types.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
> >>>
> >>> As these are fixed values, why do you have to say them again in the
> >>> structure?
> >>
> >> These length recommendations are provided by the TDX Module, and there is
> >> a slight possibility that the TDX Module will increase the maximum size
> >> of the REPORTDATA and TDREPORT in the future.
> > 
> > We do not write kernel code for "slight possibilities sometime in the
> > future".
> > 
> >> To handle such length
> >> changes, rather than inventing a new IOCTL for it in the future, making
> >> the current one flexible to handle such changes seems better.
> > 
> > Please work through the code and see how that would really look, and
> > what would break if you were to change that in the future (remember
> > kernel code and userspace code is not upgraded at the same time.)
> > 
> >> One less ABI
> >> to maintain is always better, right? My initial design did use fixed size
> >> buffers like you have recommended, but later changed it as per review
> >> suggestion to make the ABI flexible.
> > 
> > Again, work through and try to determine if the added complexity will
> > really work here.
> > 
> > What is wrong with just adding a new ioctl if in the future, you really
> > do need to change something?  That way you are sure that nothing will
> > break and userspace will be finen with it.  It is not like you are
> > forbidden to add new ioctls later, you would have to change the kernel
> > code no matter what anyway.
> > 
> > Keep it simple please.
> 
> 
> The following are potential solutions to the possible kernel/userspace
> mix/match issue that may arise in the future if the acceptable reportdata
> length, tdreport length, or subtype values change.
> 
> I've attempted to do a sample implementation as you have suggested to
> check the pros and cons for both solutions. Please let me know what you
> think. Personally I prefer solution 2, as it handles the issue you have
> raised and also keeps the ABI flexible.
> 
> Solution 1:
> ------------
> 
> This is based on your suggestion. I have dropped the IOCTL req members for
> reportdata length (rpd_len), tdreport length (tdr_len) and subtype. I have
> also used fixed size buffers to handle the current requirements.
> 
> Pros: Implementation is simple and clean.
> 
> Cons: May need to add new IOCTL for any future requirement updates.
> 
> Following are the ABI and IOCTL handler implementation details (Note: it
> is not the complete code, only included required details to show how the
> implementation looks):

Naturally, I like this one :)

And you can even make it go faster, with only one allocation, no need
for 2 as your implementation did.

I don't know if speed matters on this, as I don't know how fast the
actual hardware call takes, but making only 1 allocation and removing
all need/worries about length checking and getting that correct is
always a good thing.

Simple is good, especially if it works today.

If you have a new message size/type in the future, great, write a new
ioctl and all is good!

Test your implementations out and see what you feel good about, but
seriously consider keeping this simple if at all possible.

thanks,

greg k-h
Kuppuswamy Sathyanarayanan Nov. 3, 2022, 12:36 a.m. UTC | #7
Hi,

On 11/1/22 11:58 PM, Greg Kroah-Hartman wrote:
> On Tue, Nov 01, 2022 at 11:18:29PM -0700, Sathyanarayanan Kuppuswamy wrote:
>> Hi Greg,
>>
>> On 10/29/22 11:53 PM, Greg Kroah-Hartman wrote:
>>> On Sat, Oct 29, 2022 at 04:17:39PM -0700, Sathyanarayanan Kuppuswamy wrote:
>>>> Hi Greg
>>>>
>>>> On 10/27/22 11:25 PM, Greg Kroah-Hartman wrote:
>>>>> On Thu, Oct 27, 2022 at 05:28:19PM -0700, Kuppuswamy Sathyanarayanan wrote:
>>>>
>>>>>> +
>>>>>> +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);
>>>>>
>>>>> You know the type of this pointer here, why not cast it instead of
>>>>> having to cast it from void * again?
>>>>
>>>> The only place we use arg pointer is in copy_from_user() function,
>>>> which expects void __user * pointer. So why cast it as struct
>>>> tdx_report_req * here?
>>>
>>> Because then your function will show the true type and you don't have to
>>> cast it again.
>>>
>>>>>> +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..29453e6a7ced
>>>>>> --- /dev/null
>>>>>> +++ b/include/uapi/linux/tdx-guest.h
>>>>>> @@ -0,0 +1,55 @@
>>>>>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>>>>>> +/*
>>>>>> + * Userspace interface for TDX guest driver
>>>>>> + *
>>>>>> + * Copyright (C) 2022 Intel Corporation
>>>>>> + */
>>>>>> +
>>>>>> +#ifndef _UAPI_LINUX_TDX_GUEST_H_
>>>>>> +#define _UAPI_LINUX_TDX_GUEST_H_
>>>>>> +
>>>>>> +#include <linux/ioctl.h>
>>>>>> +#include <linux/types.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
>>>>>
>>>>> As these are fixed values, why do you have to say them again in the
>>>>> structure?
>>>>
>>>> These length recommendations are provided by the TDX Module, and there is
>>>> a slight possibility that the TDX Module will increase the maximum size
>>>> of the REPORTDATA and TDREPORT in the future.
>>>
>>> We do not write kernel code for "slight possibilities sometime in the
>>> future".
>>>
>>>> To handle such length
>>>> changes, rather than inventing a new IOCTL for it in the future, making
>>>> the current one flexible to handle such changes seems better.
>>>
>>> Please work through the code and see how that would really look, and
>>> what would break if you were to change that in the future (remember
>>> kernel code and userspace code is not upgraded at the same time.)
>>>
>>>> One less ABI
>>>> to maintain is always better, right? My initial design did use fixed size
>>>> buffers like you have recommended, but later changed it as per review
>>>> suggestion to make the ABI flexible.
>>>
>>> Again, work through and try to determine if the added complexity will
>>> really work here.
>>>
>>> What is wrong with just adding a new ioctl if in the future, you really
>>> do need to change something?  That way you are sure that nothing will
>>> break and userspace will be finen with it.  It is not like you are
>>> forbidden to add new ioctls later, you would have to change the kernel
>>> code no matter what anyway.
>>>
>>> Keep it simple please.
>>
>>
>> The following are potential solutions to the possible kernel/userspace
>> mix/match issue that may arise in the future if the acceptable reportdata
>> length, tdreport length, or subtype values change.
>>
>> I've attempted to do a sample implementation as you have suggested to
>> check the pros and cons for both solutions. Please let me know what you
>> think. Personally I prefer solution 2, as it handles the issue you have
>> raised and also keeps the ABI flexible.
>>
>> Solution 1:
>> ------------
>>
>> This is based on your suggestion. I have dropped the IOCTL req members for
>> reportdata length (rpd_len), tdreport length (tdr_len) and subtype. I have
>> also used fixed size buffers to handle the current requirements.
>>
>> Pros: Implementation is simple and clean.
>>
>> Cons: May need to add new IOCTL for any future requirement updates.
>>
>> Following are the ABI and IOCTL handler implementation details (Note: it
>> is not the complete code, only included required details to show how the
>> implementation looks):
> 
> Naturally, I like this one :)
> 
> And you can even make it go faster, with only one allocation, no need
> for 2 as your implementation did.
> 
> I don't know if speed matters on this, as I don't know how fast the
> actual hardware call takes, but making only 1 allocation and removing
> all need/worries about length checking and getting that correct is
> always a good thing.

Buffer allocation time is very negligible compared to the TDCALL execution
time. So we will not gain much by such optimization, and it is not a time
critical path either. Using separate buffers for input and output, in my
opinion, keeps it cleaner and easier to read. Hope it is fine with you.

> 
> Simple is good, especially if it works today.
>

I am fine with it. If there are no objections, I will go ahead with
this approach.

 
> If you have a new message size/type in the future, great, write a new
> ioctl and all is good!> 
> Test your implementations out and see what you feel good about, but
> seriously consider keeping this simple if at all possible.
> 
> thanks,
> 
> greg k-h
diff mbox series

Patch

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-trust-domain-extensions.html
+
+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/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..775cb463f9c8
--- /dev/null
+++ b/drivers/virt/coco/tdx-guest/Makefile
@@ -0,0 +1,2 @@ 
+# SPDX-License-Identifier: GPL-2.0
+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..12d05617e4f3
--- /dev/null
+++ b/drivers/virt/coco/tdx-guest/tdx-guest.c
@@ -0,0 +1,121 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TDX guest user interface driver
+ *
+ * Copyright (C) 2022 Intel Corporation
+ */
+
+#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;
+
+	/*
+	 * Check for valid input values. 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 */
+	ret = tdx_mcall_get_report(reportdata, tdreport, req.subtype);
+	if (ret)
+		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 = KBUILD_MODNAME,
+	.minor = MISC_DYNAMIC_MINOR,
+	.fops = &tdx_guest_fops,
+};
+
+static const struct x86_cpu_id tdx_guest_ids[] = {
+	X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL),
+	{}
+};
+MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids);
+
+static int __init tdx_guest_init(void)
+{
+	if (!x86_match_cpu(tdx_guest_ids))
+		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);
+
+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..29453e6a7ced
--- /dev/null
+++ b/include/uapi/linux/tdx-guest.h
@@ -0,0 +1,55 @@ 
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Userspace interface for TDX guest driver
+ *
+ * Copyright (C) 2022 Intel Corporation
+ */
+
+#ifndef _UAPI_LINUX_TDX_GUEST_H_
+#define _UAPI_LINUX_TDX_GUEST_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.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 - Request struct for TDX_CMD_GET_REPORT IOCTL.
+ *
+ * @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. Must be filled
+ *            with zeroes.
+ */
+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_ */