Skip to content

Commit

Permalink
virt: Add TDX guest driver
Browse files Browse the repository at this point in the history
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 is documented in
Documentation/x86/tdx.rst, and the details about the IOCTLs 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.

This driver is similar to AMD sev-guest driver, which also uses
IOCTL interface to support attestation.

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>
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
  • Loading branch information
Kuppuswamy Sathyanarayanan committed Sep 21, 2022
1 parent 93eb886 commit 3a38d73
Show file tree
Hide file tree
Showing 9 changed files with 291 additions and 0 deletions.
46 changes: 46 additions & 0 deletions Documentation/virt/coco/tdx-guest.rst
@@ -0,0 +1,46 @@
.. 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.
Currently it is only used to allow the userspace to get the TDREPORT.
Possible future use cases are to allow userspace to get entries from
the Confidential Computing Event Log (CCEL) table or to get storage
keys from the Storage Volume Key Table (SVKL).

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.
39 changes: 39 additions & 0 deletions Documentation/x86/tdx.rst
Expand Up @@ -210,6 +210,45 @@ 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.

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
==========

Expand Down
1 change: 1 addition & 0 deletions arch/x86/include/asm/tdx.h
Expand Up @@ -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__
Expand Down
2 changes: 2 additions & 0 deletions drivers/virt/Kconfig
Expand Up @@ -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
1 change: 1 addition & 0 deletions drivers/virt/Makefile
Expand Up @@ -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/
10 changes: 10 additions & 0 deletions 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.
2 changes: 2 additions & 0 deletions 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
137 changes: 137 additions & 0 deletions drivers/virt/coco/tdx-guest/tdx-guest.c
@@ -0,0 +1,137 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* TDX guest user interface driver
*
* Copyright (C) 2022 Intel Corporation
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/mod_devicetable.h>

#include <uapi/linux/tdx-guest.h>

#include <asm/tdx.h>
#include <asm/cpu_device_id.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)
{
void __user *argp = (void __user *)arg;
long ret = -ENOTTY;

switch (cmd) {
case TDX_CMD_GET_REPORT:
ret = tdx_get_report(argp);
break;
default:
break;
}

return ret;
}

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");
53 changes: 53 additions & 0 deletions 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/types.h>
#include <linux/ioctl.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_ */

0 comments on commit 3a38d73

Please sign in to comment.