Commit 5e2cb28d authored by Linus Torvalds's avatar Linus Torvalds

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

Pull unified attestation reporting from Dan Williams:
 "In an ideal world there would be a cross-vendor standard attestation
  report format for confidential guests along with a common device
  definition to act as the transport.

  In the real world the situation ended up with multiple platform
  vendors inventing their own attestation report formats with the
  SEV-SNP implementation being a first mover to define a custom
  sev-guest character device and corresponding ioctl(). Later, this
  configfs-tsm proposal intercepted an attempt to add a tdx-guest
  character device and a corresponding new ioctl(). It also anticipated
  ARM and RISC-V showing up with more chardevs and more ioctls().

  The proposal takes for granted that Linux tolerates the vendor report
  format differentiation until a standard arrives. From talking with
  folks involved, it sounds like that standardization work is unlikely
  to resolve anytime soon. It also takes the position that kernfs ABIs
  are easier to maintain than ioctl(). The result is a shared configfs
  mechanism to return per-vendor report-blobs with the option to later
  support a standard when that arrives.

  Part of the goal here also is to get the community into the
  "uncomfortable, but beneficial to the long term maintainability of the
  kernel" state of talking to each other about their differentiation and
  opportunities to collaborate. Think of this like the device-driver
  equivalent of the common memory-management infrastructure for
  confidential-computing being built up in KVM.

  As for establishing an "upstream path for cross-vendor
  confidential-computing device driver infrastructure" this is something
  I want to discuss at Plumbers. At present, the multiple vendor
  proposals for assigning devices to confidential computing VMs likely
  needs a new dedicated repository and maintainer team, but that is a
  discussion for v6.8.

  For now, Greg and Thomas have acked this approach and this is passing
  is AMD, Intel, and Google tests.

  Summary:

   - Introduce configfs-tsm as a shared ABI for confidential computing
     attestation reports

   - Convert sev-guest to additionally support configfs-tsm alongside
     its vendor specific ioctl()

   - Added signed attestation report retrieval to the tdx-guest driver
     forgoing a new vendor specific ioctl()

   - Misc cleanups and a new __free() annotation for kvfree()"

* tag 'tsm-for-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/linux:
  virt: tdx-guest: Add Quote generation support using TSM_REPORTS
  virt: sevguest: Add TSM_REPORTS support for SNP_GET_EXT_REPORT
  mm/slab: Add __free() support for kvfree
  virt: sevguest: Prep for kernel internal get_ext_report()
  configfs-tsm: Introduce a shared ABI for attestation reports
  virt: coco: Add a coco/Makefile and coco/Kconfig
  virt: sevguest: Fix passing a stack buffer as a scatterlist target
parents b1dfbda8 f4738f56
What: /sys/kernel/config/tsm/report/$name/inblob
Date: September, 2023
KernelVersion: v6.7
Contact: linux-coco@lists.linux.dev
Description:
(WO) Up to 64 bytes of user specified binary data. For replay
protection this should include a nonce, but the kernel does not
place any restrictions on the content.
What: /sys/kernel/config/tsm/report/$name/outblob
Date: September, 2023
KernelVersion: v6.7
Contact: linux-coco@lists.linux.dev
Description:
(RO) Binary attestation report generated from @inblob and other
options The format of the report is implementation specific
where the implementation is conveyed via the @provider
attribute.
What: /sys/kernel/config/tsm/report/$name/auxblob
Date: October, 2023
KernelVersion: v6.7
Contact: linux-coco@lists.linux.dev
Description:
(RO) Optional supplemental data that a TSM may emit, visibility
of this attribute depends on TSM, and may be empty if no
auxiliary data is available.
When @provider is "sev_guest" this file contains the
"cert_table" from SEV-ES Guest-Hypervisor Communication Block
Standardization v2.03 Section 4.1.8.1 MSG_REPORT_REQ.
https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/56421.pdf
What: /sys/kernel/config/tsm/report/$name/provider
Date: September, 2023
KernelVersion: v6.7
Contact: linux-coco@lists.linux.dev
Description:
(RO) A name for the format-specification of @outblob like
"sev_guest" [1] or "tdx_guest" [2] in the near term, or a
common standard format in the future.
[1]: SEV Secure Nested Paging Firmware ABI Specification
Revision 1.55 Table 22
https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/56860.pdf
[2]: Intel® Trust Domain Extensions Data Center Attestation
Primitives : Quote Generation Library and Quote Verification
Library Revision 0.8 Appendix 4,5
https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_TDX_DCAP_Quoting_Library_API.pdf
What: /sys/kernel/config/tsm/report/$name/generation
Date: September, 2023
KernelVersion: v6.7
Contact: linux-coco@lists.linux.dev
Description:
(RO) The value in this attribute increments each time @inblob or
any option is written. Userspace can detect conflicts by
checking generation before writing to any attribute and making
sure the number of writes matches expectations after reading
@outblob, or it can prevent conflicts by creating a report
instance per requesting context.
What: /sys/kernel/config/tsm/report/$name/privlevel
Date: September, 2023
KernelVersion: v6.7
Contact: linux-coco@lists.linux.dev
Description:
(WO) Attribute is visible if a TSM implementation provider
supports the concept of attestation reports for TVMs running at
different privilege levels, like SEV-SNP "VMPL", specify the
privilege level via this attribute. The minimum acceptable
value is conveyed via @privlevel_floor and the maximum
acceptable value is TSM_PRIVLEVEL_MAX (3).
What: /sys/kernel/config/tsm/report/$name/privlevel_floor
Date: September, 2023
KernelVersion: v6.7
Contact: linux-coco@lists.linux.dev
Description:
(RO) Indicates the minimum permissible value that can be written
to @privlevel.
......@@ -22058,6 +22058,14 @@ W: https://github.com/srcres258/linux-doc
T: git git://github.com/srcres258/linux-doc.git doc-zh-tw
F: Documentation/translations/zh_TW/
TRUSTED SECURITY MODULE (TSM) ATTESTATION REPORTS
M: Dan Williams <dan.j.williams@intel.com>
L: linux-coco@lists.linux.dev
S: Maintained
F: Documentation/ABI/testing/configfs-tsm
F: drivers/virt/coco/tsm.c
F: include/linux/tsm.h
TTY LAYER AND SERIAL DRIVERS
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
M: Jiri Slaby <jirislaby@kernel.org>
......
......@@ -106,6 +106,27 @@ int tdx_mcall_get_report0(u8 *reportdata, u8 *tdreport)
}
EXPORT_SYMBOL_GPL(tdx_mcall_get_report0);
/**
* tdx_hcall_get_quote() - Wrapper to request TD Quote using GetQuote
* hypercall.
* @buf: Address of the directly mapped shared kernel buffer which
* contains TDREPORT. The same buffer will be used by VMM to
* store the generated TD Quote output.
* @size: size of the tdquote buffer (4KB-aligned).
*
* Refer to section titled "TDG.VP.VMCALL<GetQuote>" in the TDX GHCI
* v1.0 specification for more information on GetQuote hypercall.
* It is used in the TDX guest driver module to get the TD Quote.
*
* Return 0 on success or error code on failure.
*/
u64 tdx_hcall_get_quote(u8 *buf, size_t size)
{
/* Since buf is a shared memory, set the shared (decrypted) bits */
return _tdx_hypercall(TDVMCALL_GET_QUOTE, cc_mkdec(virt_to_phys(buf)), size, 0, 0);
}
EXPORT_SYMBOL_GPL(tdx_hcall_get_quote);
static void __noreturn tdx_panic(const char *msg)
{
struct tdx_module_args args = {
......
......@@ -23,6 +23,7 @@
/* TDX hypercall Leaf IDs */
#define TDVMCALL_MAP_GPA 0x10001
#define TDVMCALL_GET_QUOTE 0x10002
#define TDVMCALL_REPORT_FATAL_ERROR 0x10003
#define TDVMCALL_STATUS_RETRY 1
......
......@@ -56,6 +56,8 @@ bool tdx_early_handle_ve(struct pt_regs *regs);
int tdx_mcall_get_report0(u8 *reportdata, u8 *tdreport);
u64 tdx_hcall_get_quote(u8 *buf, size_t size);
#else
static inline void tdx_early_init(void) { };
......
......@@ -48,10 +48,6 @@ source "drivers/virt/nitro_enclaves/Kconfig"
source "drivers/virt/acrn/Kconfig"
source "drivers/virt/coco/efi_secret/Kconfig"
source "drivers/virt/coco/sev-guest/Kconfig"
source "drivers/virt/coco/tdx-guest/Kconfig"
source "drivers/virt/coco/Kconfig"
endif
......@@ -9,6 +9,4 @@ obj-y += vboxguest/
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/
obj-y += coco/
# SPDX-License-Identifier: GPL-2.0-only
#
# Confidential computing related collateral
#
config TSM_REPORTS
select CONFIGFS_FS
tristate
source "drivers/virt/coco/efi_secret/Kconfig"
source "drivers/virt/coco/sev-guest/Kconfig"
source "drivers/virt/coco/tdx-guest/Kconfig"
# SPDX-License-Identifier: GPL-2.0-only
#
# Confidential computing related collateral
#
obj-$(CONFIG_TSM_REPORTS) += tsm.o
obj-$(CONFIG_EFI_SECRET) += efi_secret/
obj-$(CONFIG_SEV_GUEST) += sev-guest/
obj-$(CONFIG_INTEL_TDX_GUEST) += tdx-guest/
......@@ -5,6 +5,7 @@ config SEV_GUEST
select CRYPTO
select CRYPTO_AEAD2
select CRYPTO_GCM
select TSM_REPORTS
help
SEV-SNP firmware provides the guest a mechanism to communicate with
the PSP without risk from a malicious hypervisor who wishes to read,
......
This diff is collapsed.
config TDX_GUEST_DRIVER
tristate "TDX Guest driver"
depends on INTEL_TDX_GUEST
select TSM_REPORTS
help
The driver provides userspace interface to communicate with
the TDX module to request the TDX guest details like attestation
......
......@@ -12,12 +12,60 @@
#include <linux/mod_devicetable.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/set_memory.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/tsm.h>
#include <linux/sizes.h>
#include <uapi/linux/tdx-guest.h>
#include <asm/cpu_device_id.h>
#include <asm/tdx.h>
/*
* Intel's SGX QE implementation generally uses Quote size less
* than 8K (2K Quote data + ~5K of certificate blob).
*/
#define GET_QUOTE_BUF_SIZE SZ_8K
#define GET_QUOTE_CMD_VER 1
/* TDX GetQuote status codes */
#define GET_QUOTE_SUCCESS 0
#define GET_QUOTE_IN_FLIGHT 0xffffffffffffffff
/* struct tdx_quote_buf: Format of Quote request buffer.
* @version: Quote format version, filled by TD.
* @status: Status code of Quote request, filled by VMM.
* @in_len: Length of TDREPORT, filled by TD.
* @out_len: Length of Quote data, filled by VMM.
* @data: Quote data on output or TDREPORT on input.
*
* More details of Quote request buffer can be found in TDX
* Guest-Host Communication Interface (GHCI) for Intel TDX 1.0,
* section titled "TDG.VP.VMCALL<GetQuote>"
*/
struct tdx_quote_buf {
u64 version;
u64 status;
u32 in_len;
u32 out_len;
u8 data[];
};
/* Quote data buffer */
static void *quote_data;
/* Lock to streamline quote requests */
static DEFINE_MUTEX(quote_lock);
/*
* GetQuote request timeout in seconds. Expect that 30 seconds
* is enough time for QE to respond to any Quote requests.
*/
static u32 getquote_timeout = 30;
static long tdx_get_report0(struct tdx_report_req __user *req)
{
u8 *reportdata, *tdreport;
......@@ -53,6 +101,154 @@ static long tdx_get_report0(struct tdx_report_req __user *req)
return ret;
}
static void free_quote_buf(void *buf)
{
size_t len = PAGE_ALIGN(GET_QUOTE_BUF_SIZE);
unsigned int count = len >> PAGE_SHIFT;
if (set_memory_encrypted((unsigned long)buf, count)) {
pr_err("Failed to restore encryption mask for Quote buffer, leak it\n");
return;
}
free_pages_exact(buf, len);
}
static void *alloc_quote_buf(void)
{
size_t len = PAGE_ALIGN(GET_QUOTE_BUF_SIZE);
unsigned int count = len >> PAGE_SHIFT;
void *addr;
addr = alloc_pages_exact(len, GFP_KERNEL | __GFP_ZERO);
if (!addr)
return NULL;
if (set_memory_decrypted((unsigned long)addr, count)) {
free_pages_exact(addr, len);
return NULL;
}
return addr;
}
/*
* wait_for_quote_completion() - Wait for Quote request completion
* @quote_buf: Address of Quote buffer.
* @timeout: Timeout in seconds to wait for the Quote generation.
*
* As per TDX GHCI v1.0 specification, sec titled "TDG.VP.VMCALL<GetQuote>",
* the status field in the Quote buffer will be set to GET_QUOTE_IN_FLIGHT
* while VMM processes the GetQuote request, and will change it to success
* or error code after processing is complete. So wait till the status
* changes from GET_QUOTE_IN_FLIGHT or the request being timed out.
*/
static int wait_for_quote_completion(struct tdx_quote_buf *quote_buf, u32 timeout)
{
int i = 0;
/*
* Quote requests usually take a few seconds to complete, so waking up
* once per second to recheck the status is fine for this use case.
*/
while (quote_buf->status == GET_QUOTE_IN_FLIGHT && i++ < timeout) {
if (msleep_interruptible(MSEC_PER_SEC))
return -EINTR;
}
return (i == timeout) ? -ETIMEDOUT : 0;
}
static int tdx_report_new(struct tsm_report *report, void *data)
{
u8 *buf, *reportdata = NULL, *tdreport = NULL;
struct tdx_quote_buf *quote_buf = quote_data;
struct tsm_desc *desc = &report->desc;
int ret;
u64 err;
/* TODO: switch to guard(mutex_intr) */
if (mutex_lock_interruptible(&quote_lock))
return -EINTR;
/*
* If the previous request is timedout or interrupted, and the
* Quote buf status is still in GET_QUOTE_IN_FLIGHT (owned by
* VMM), don't permit any new request.
*/
if (quote_buf->status == GET_QUOTE_IN_FLIGHT) {
ret = -EBUSY;
goto done;
}
if (desc->inblob_len != TDX_REPORTDATA_LEN) {
ret = -EINVAL;
goto done;
}
reportdata = kmalloc(TDX_REPORTDATA_LEN, GFP_KERNEL);
if (!reportdata) {
ret = -ENOMEM;
goto done;
}
tdreport = kzalloc(TDX_REPORT_LEN, GFP_KERNEL);
if (!tdreport) {
ret = -ENOMEM;
goto done;
}
memcpy(reportdata, desc->inblob, desc->inblob_len);
/* Generate TDREPORT0 using "TDG.MR.REPORT" TDCALL */
ret = tdx_mcall_get_report0(reportdata, tdreport);
if (ret) {
pr_err("GetReport call failed\n");
goto done;
}
memset(quote_data, 0, GET_QUOTE_BUF_SIZE);
/* Update Quote buffer header */
quote_buf->version = GET_QUOTE_CMD_VER;
quote_buf->in_len = TDX_REPORT_LEN;
memcpy(quote_buf->data, tdreport, TDX_REPORT_LEN);
err = tdx_hcall_get_quote(quote_data, GET_QUOTE_BUF_SIZE);
if (err) {
pr_err("GetQuote hypercall failed, status:%llx\n", err);
ret = -EIO;
goto done;
}
ret = wait_for_quote_completion(quote_buf, getquote_timeout);
if (ret) {
pr_err("GetQuote request timedout\n");
goto done;
}
buf = kvmemdup(quote_buf->data, quote_buf->out_len, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto done;
}
report->outblob = buf;
report->outblob_len = quote_buf->out_len;
/*
* TODO: parse the PEM-formatted cert chain out of the quote buffer when
* provided
*/
done:
mutex_unlock(&quote_lock);
kfree(reportdata);
kfree(tdreport);
return ret;
}
static long tdx_guest_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
......@@ -82,17 +278,48 @@ static const struct x86_cpu_id tdx_guest_ids[] = {
};
MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids);
static const struct tsm_ops tdx_tsm_ops = {
.name = KBUILD_MODNAME,
.report_new = tdx_report_new,
};
static int __init tdx_guest_init(void)
{
int ret;
if (!x86_match_cpu(tdx_guest_ids))
return -ENODEV;
return misc_register(&tdx_misc_dev);
ret = misc_register(&tdx_misc_dev);
if (ret)
return ret;
quote_data = alloc_quote_buf();
if (!quote_data) {
pr_err("Failed to allocate Quote buffer\n");
ret = -ENOMEM;
goto free_misc;
}
ret = tsm_register(&tdx_tsm_ops, NULL, NULL);
if (ret)
goto free_quote;
return 0;
free_quote:
free_quote_buf(quote_data);
free_misc:
misc_deregister(&tdx_misc_dev);
return ret;
}
module_init(tdx_guest_init);
static void __exit tdx_guest_exit(void)
{
tsm_unregister(&tdx_tsm_ops);
free_quote_buf(quote_data);
misc_deregister(&tdx_misc_dev);
}
module_exit(tdx_guest_exit);
......
This diff is collapsed.
......@@ -764,6 +764,8 @@ static inline __alloc_size(1, 2) void *kvcalloc(size_t n, size_t size, gfp_t fla
extern void *kvrealloc(const void *p, size_t oldsize, size_t newsize, gfp_t flags)
__realloc_size(3);
extern void kvfree(const void *addr);
DEFINE_FREE(kvfree, void *, if (_T) kvfree(_T))
extern void kvfree_sensitive(const void *addr, size_t len);
unsigned int kmem_cache_size(struct kmem_cache *s);
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __TSM_H
#define __TSM_H
#include <linux/sizes.h>
#include <linux/types.h>
#define TSM_INBLOB_MAX 64
#define TSM_OUTBLOB_MAX SZ_32K
/*
* Privilege level is a nested permission concept to allow confidential
* guests to partition address space, 4-levels are supported.
*/
#define TSM_PRIVLEVEL_MAX 3
/**
* struct tsm_desc - option descriptor for generating tsm report blobs
* @privlevel: optional privilege level to associate with @outblob
* @inblob_len: sizeof @inblob
* @inblob: arbitrary input data
*/
struct tsm_desc {
unsigned int privlevel;
size_t inblob_len;
u8 inblob[TSM_INBLOB_MAX];
};
/**
* struct tsm_report - track state of report generation relative to options
* @desc: input parameters to @report_new()
* @outblob_len: sizeof(@outblob)
* @outblob: generated evidence to provider to the attestation agent
* @auxblob_len: sizeof(@auxblob)
* @auxblob: (optional) auxiliary data to the report (e.g. certificate data)
*/
struct tsm_report {
struct tsm_desc desc;
size_t outblob_len;
u8 *outblob;
size_t auxblob_len;
u8 *auxblob;
};
/**
* struct tsm_ops - attributes and operations for tsm instances
* @name: tsm id reflected in /sys/kernel/config/tsm/report/$report/provider
* @privlevel_floor: convey base privlevel for nested scenarios
* @report_new: Populate @report with the report blob and auxblob
* (optional), return 0 on successful population, or -errno otherwise
*
* Implementation specific ops, only one is expected to be registered at
* a time i.e. only one of "sev-guest", "tdx-guest", etc.
*/
struct tsm_ops {
const char *name;
const unsigned int privlevel_floor;
int (*report_new)(struct tsm_report *report, void *data);
};
extern const struct config_item_type tsm_report_default_type;
/* publish @privlevel, @privlevel_floor, and @auxblob attributes */
extern const struct config_item_type tsm_report_extra_type;
int tsm_register(const struct tsm_ops *ops, void *priv,
const struct config_item_type *type);
int tsm_unregister(const struct tsm_ops *ops);
#endif /* __TSM_H */
......@@ -68,6 +68,7 @@ typedef enum {
SEV_RET_INVALID_PARAM,
SEV_RET_RESOURCE_LIMIT,
SEV_RET_SECURE_DATA_INVALID,
SEV_RET_INVALID_KEY = 0x27,
SEV_RET_MAX,
} sev_ret_code;
......
......@@ -14,9 +14,11 @@
#include <linux/types.h>
#define SNP_REPORT_USER_DATA_SIZE 64
struct snp_report_req {
/* user data that should be included in the report */
__u8 user_data[64];
__u8 user_data[SNP_REPORT_USER_DATA_SIZE];
/* The vmpl level to be included in the report */
__u32 vmpl;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment