From nobody Fri Dec 19 16:07:26 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B3B8ACE7A8E for ; Tue, 26 Sep 2023 04:17:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233555AbjIZERU (ORCPT ); Tue, 26 Sep 2023 00:17:20 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39970 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229472AbjIZERS (ORCPT ); Tue, 26 Sep 2023 00:17:18 -0400 Received: from mgamail.intel.com (mgamail.intel.com [192.55.52.88]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D6C05BF for ; Mon, 25 Sep 2023 21:17:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1695701831; x=1727237831; h=subject:from:to:cc:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=b7vp5OcVBoNhgN64VhsxskMsEKIzR4SqcCbOS2xbSV8=; b=XwY6W78SJ/KIXmy2wY2I6K8uumqVsu9GFo+epbHRNLz80UIYzqBWEIWD 6RvvrhTEjkE2r86IglipAkqEe3HJj6SLJS/iSdkWEtj3mBRRyk4MwhTlQ qg9m4taKBFAY9TIhubUtLJxgoXLMWwpxYb5jLc6UK7yGUTeiz9SEqkn6T qEMOnUHcdW/nlWfrsEqQxL+Akvst6ldaOHs+9xE6XRmzszs9DZPEXEzIY JGrZsCXaR4IVJ6Iv5bYxSArt9ThQaRln8l2xUcrHH4J6IC5wv5+cVQiyU yRCKQA7x59T3IST5umeCUoj2JDgQyOkEJGoLhuICQRE7TN/9b6ywsxbtG A==; X-IronPort-AV: E=McAfee;i="6600,9927,10843"; a="412395794" X-IronPort-AV: E=Sophos;i="6.03,177,1694761200"; d="scan'208";a="412395794" Received: from fmsmga008.fm.intel.com ([10.253.24.58]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 Sep 2023 21:17:05 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10843"; a="814314514" X-IronPort-AV: E=Sophos;i="6.03,177,1694761200"; d="scan'208";a="814314514" Received: from fvivekku-mobl.amr.corp.intel.com (HELO dwillia2-xfh.jf.intel.com) ([10.251.18.72]) by fmsmga008-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 Sep 2023 21:17:04 -0700 Subject: [PATCH v4 1/6] virt: coco: Add a coco/Makefile and coco/Kconfig From: Dan Williams To: linux-coco@lists.linux.dev Cc: Kuppuswamy Sathyanarayanan , peterz@infradead.org, linux-kernel@vger.kernel.org, x86@kernel.org, dave.hansen@linux.intel.com Date: Mon, 25 Sep 2023 21:17:03 -0700 Message-ID: <169570182376.596431.13665321099443794459.stgit@dwillia2-xfh.jf.intel.com> In-Reply-To: <169570181657.596431.6178773442587231200.stgit@dwillia2-xfh.jf.intel.com> References: <169570181657.596431.6178773442587231200.stgit@dwillia2-xfh.jf.intel.com> User-Agent: StGit/0.18-3-g996c MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org In preparation for adding another coco build target, relieve drivers/virt/Makefile of the responsibility to track new compilation unit additions to drivers/virt/coco/, and do the same for drivers/virt/Kconfig. Reviewed-by: Kuppuswamy Sathyanarayanan Signed-off-by: Dan Williams --- drivers/virt/Kconfig | 6 +----- drivers/virt/Makefile | 4 +--- drivers/virt/coco/Kconfig | 9 +++++++++ drivers/virt/coco/Makefile | 7 +++++++ 4 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 drivers/virt/coco/Kconfig create mode 100644 drivers/virt/coco/Makefile diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig index f79ab13a5c28..40129b6f0eca 100644 --- a/drivers/virt/Kconfig +++ b/drivers/virt/Kconfig @@ -48,10 +48,6 @@ source "drivers/virt/nitro_enclaves/Kconfig" =20 source "drivers/virt/acrn/Kconfig" =20 -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" =20 endif diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile index e9aa6fc96fab..f29901bd7820 100644 --- a/drivers/virt/Makefile +++ b/drivers/virt/Makefile @@ -9,6 +9,4 @@ obj-y +=3D vboxguest/ =20 obj-$(CONFIG_NITRO_ENCLAVES) +=3D nitro_enclaves/ obj-$(CONFIG_ACRN_HSM) +=3D acrn/ -obj-$(CONFIG_EFI_SECRET) +=3D coco/efi_secret/ -obj-$(CONFIG_SEV_GUEST) +=3D coco/sev-guest/ -obj-$(CONFIG_INTEL_TDX_GUEST) +=3D coco/tdx-guest/ +obj-y +=3D coco/ diff --git a/drivers/virt/coco/Kconfig b/drivers/virt/coco/Kconfig new file mode 100644 index 000000000000..fc5c64f04c4a --- /dev/null +++ b/drivers/virt/coco/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Confidential computing related collateral +# +source "drivers/virt/coco/efi_secret/Kconfig" + +source "drivers/virt/coco/sev-guest/Kconfig" + +source "drivers/virt/coco/tdx-guest/Kconfig" diff --git a/drivers/virt/coco/Makefile b/drivers/virt/coco/Makefile new file mode 100644 index 000000000000..55302ef719ad --- /dev/null +++ b/drivers/virt/coco/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Confidential computing related collateral +# +obj-$(CONFIG_EFI_SECRET) +=3D efi_secret/ +obj-$(CONFIG_SEV_GUEST) +=3D sev-guest/ +obj-$(CONFIG_INTEL_TDX_GUEST) +=3D tdx-guest/ From nobody Fri Dec 19 16:07:26 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A6DCBE81817 for ; Tue, 26 Sep 2023 04:17:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233600AbjIZERZ (ORCPT ); Tue, 26 Sep 2023 00:17:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36026 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229472AbjIZERW (ORCPT ); Tue, 26 Sep 2023 00:17:22 -0400 Received: from mgamail.intel.com (mgamail.intel.com [192.55.52.88]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E7A3BEC for ; Mon, 25 Sep 2023 21:17:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1695701832; x=1727237832; h=subject:from:to:cc:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=U/q/ee1Hi6Rv/vxDVySTbn/n4UowW9g1FmVfGdNU9g4=; b=j/2N/ackmH7e4hcyF3jiA5WS7yBNZkMPTp4aXFauuxBf2Q6iGzn2Oc+d tSCppyz70t3nBpPzv1CspOMlMCAflhAY2BUKaFFryFRxdem0hO7Fmb1IS x6KIhuRKkgMjSnsKrj4DJx499UEigaPIRApNpt/dcMmKbyZk/YrggcRb8 CEeH1yX4T2F/MVZ73fAK2wF0ftKW+pfIC8vGdHYBbpi9aB1H4ReJXLfAo H6qQH20gx2SPas5QhTuK3x7MZt0EWRnGWJtY3dZf9ZCjXcoDQ1lKWsOMQ sD+SJXQ90Jq11zseoxvMQlLO35g8MUtEq9xOfcbvoPPXa22WMtxKb25tQ A==; X-IronPort-AV: E=McAfee;i="6600,9927,10843"; a="412395812" X-IronPort-AV: E=Sophos;i="6.03,177,1694761200"; d="scan'208";a="412395812" Received: from fmsmga008.fm.intel.com ([10.253.24.58]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 Sep 2023 21:17:11 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10843"; a="814314520" X-IronPort-AV: E=Sophos;i="6.03,177,1694761200"; d="scan'208";a="814314520" Received: from fvivekku-mobl.amr.corp.intel.com (HELO dwillia2-xfh.jf.intel.com) ([10.251.18.72]) by fmsmga008-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 Sep 2023 21:17:10 -0700 Subject: [PATCH v4 2/6] configfs-tsm: Introduce a shared ABI for attestation reports From: Dan Williams To: linux-coco@lists.linux.dev Cc: Kuppuswamy Sathyanarayanan , Dionna Amalie Glaze , James Bottomley , Peter Gonda , Greg Kroah-Hartman , Samuel Ortiz , Greg Kroah-Hartman , Thomas Gleixner , peterz@infradead.org, linux-kernel@vger.kernel.org, x86@kernel.org, dave.hansen@linux.intel.com Date: Mon, 25 Sep 2023 21:17:09 -0700 Message-ID: <169570182987.596431.14062417344858914481.stgit@dwillia2-xfh.jf.intel.com> In-Reply-To: <169570181657.596431.6178773442587231200.stgit@dwillia2-xfh.jf.intel.com> References: <169570181657.596431.6178773442587231200.stgit@dwillia2-xfh.jf.intel.com> User-Agent: StGit/0.18-3-g996c MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org One of the common operations of a TSM (Trusted Security Module) is to provide a way for a TVM (confidential computing guest execution environment) to take a measurement of its launch state, sign it and submit it to a verifying party. Upon successful attestation that verifies the integrity of the TVM additional secrets may be deployed. The concept is common across TSMs, but the implementations are unfortunately vendor specific. While the industry grapples with a common definition of this attestation format [1], Linux need not make this problem worse by defining a new ABI per TSM that wants to perform a similar operation. The current momentum has been to invent new ioctl-ABI per TSM per function which at best is an abdication of the kernel's responsibility to make common infrastructure concepts share common ABI. The proposal, targeted to conceptually work with TDX, SEV-SNP, COVE if not more, is to define a configfs interface to retrieve the TSM-specific blob. report=3D/sys/kernel/config/tsm/report/report0 mkdir $report dd if=3Dbinary_userdata_plus_nonce > $report/inblob hexdump $report/outblob This approach later allows for the standardization of the attestation blob format without needing to invent a new ABI. Once standardization happens the standard format can be emitted by $report/outblob and indicated by $report/provider, or a new attribute like "$report/tcg_coco_report" can emit the standard format alongside the vendor format. Review of previous iterations of this interface identified that there is a need to scale report generation for multiple container environments [2]. Configfs enables a model where each container can bind mount one or more report generation item instances. Still, within a container only a single thread can be manipulating a given configuration instance at a time. A 'generation' count is provided to detect conflicts between multiple threads racing to configure a report instance. The SEV-SNP concepts of "extended reports" and "privilege levels" are optionally enabled by selecting 'tsm_report_ext_type' at register_tsm() time. The expectation is that those concepts are generic enough that they may be adopted by other TSM implementations. In other words, configfs-tsm aims to address a superset of TSM specific functionality with a common ABI where attributes may appear, or not appear, based on the = set of concepts the implementation supports. Link: http://lore.kernel.org/r/64961c3baf8ce_142af829436@dwillia2-xfh.jf.in= tel.com.notmuch [1] Link: http://lore.kernel.org/r/57f3a05e-8fcd-4656-beea-56bb8365ae64@linux.m= icrosoft.com [2] Cc: Kuppuswamy Sathyanarayanan Cc: Dionna Amalie Glaze Cc: James Bottomley Cc: Peter Gonda Cc: Greg Kroah-Hartman Cc: Samuel Ortiz Acked-by: Greg Kroah-Hartman Acked-by: Thomas Gleixner Signed-off-by: Dan Williams --- Documentation/ABI/testing/configfs-tsm | 67 +++++ MAINTAINERS | 8 + drivers/virt/coco/Kconfig | 5=20 drivers/virt/coco/Makefile | 1=20 drivers/virt/coco/tsm.c | 411 ++++++++++++++++++++++++++++= ++++ include/linux/tsm.h | 63 +++++ 6 files changed, 555 insertions(+) create mode 100644 Documentation/ABI/testing/configfs-tsm create mode 100644 drivers/virt/coco/tsm.c create mode 100644 include/linux/tsm.h diff --git a/Documentation/ABI/testing/configfs-tsm b/Documentation/ABI/tes= ting/configfs-tsm new file mode 100644 index 000000000000..ba81083046d3 --- /dev/null +++ b/Documentation/ABI/testing/configfs-tsm @@ -0,0 +1,67 @@ +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/certs +Date: September, 2023 +KernelVersion: v6.7 +Contact: linux-coco@lists.linux.dev +Description: + (RO) Zero or more certificates in concatenated PEM format. Refer + to implementation specific documentation on which certificates + might be returned. + +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-snp" or "tdx" in the near term, or a common standard format + in the future. + +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) If a TSM implementation 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. diff --git a/MAINTAINERS b/MAINTAINERS index b19995690904..8acbeb029ba1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21889,6 +21889,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/ =20 +TRUSTED SECURITY MODULE (TSM) ATTESTATION REPORTS +M: Dan Williams +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 M: Jiri Slaby diff --git a/drivers/virt/coco/Kconfig b/drivers/virt/coco/Kconfig index fc5c64f04c4a..87d142c1f932 100644 --- a/drivers/virt/coco/Kconfig +++ b/drivers/virt/coco/Kconfig @@ -2,6 +2,11 @@ # # Confidential computing related collateral # + +config TSM_REPORTS + select CONFIGFS_FS + tristate + source "drivers/virt/coco/efi_secret/Kconfig" =20 source "drivers/virt/coco/sev-guest/Kconfig" diff --git a/drivers/virt/coco/Makefile b/drivers/virt/coco/Makefile index 55302ef719ad..18c1aba5edb7 100644 --- a/drivers/virt/coco/Makefile +++ b/drivers/virt/coco/Makefile @@ -2,6 +2,7 @@ # # Confidential computing related collateral # +obj-$(CONFIG_TSM_REPORTS) +=3D tsm.o obj-$(CONFIG_EFI_SECRET) +=3D efi_secret/ obj-$(CONFIG_SEV_GUEST) +=3D sev-guest/ obj-$(CONFIG_INTEL_TDX_GUEST) +=3D tdx-guest/ diff --git a/drivers/virt/coco/tsm.c b/drivers/virt/coco/tsm.c new file mode 100644 index 000000000000..343fc77d0509 --- /dev/null +++ b/drivers/virt/coco/tsm.c @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2023 Intel Corporation. All rights reserved. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct tsm_provider { + const struct tsm_ops *ops; + const struct config_item_type *type; + void *data; +} provider; +static DECLARE_RWSEM(tsm_rwsem); + +/** + * DOC: Trusted Security Module (TSM) Attestation Report Interface + * + * The TSM report interface is a common provider of blobs that facilitate + * attestation of a TVM (confidential computing guest) by an attestation + * service. A TSM report combines a user-defined blob (likely a public-key= with + * a nonce for a key-exchange protocol) with a signed attestation report. = That + * combined blob is then used to obtain secrets provided by an agent that = can + * validate the attestation report. The expectation is that this interface= is + * invoked infrequently, however configfs allows for multiple agents to + * own their own report generation instances to generate reports as + * often as needed. + * + * The attestation report format is TSM provider specific, when / if a sta= ndard + * materializes that can be published instead of the vendor layout. Until = then + * the 'provider' attribute indicates the format of 'outblob'. However, + * the common "return a list of certs" capability across multiple TSM + * implementations is returned in a unified @certs attribute. + */ + +struct tsm_report_state { + struct tsm_report report; + unsigned long write_generation; + unsigned long read_generation; + struct config_item cfg; +}; + +enum tsm_data_select { + TSM_REPORT, + TSM_CERTS, +}; + +static struct tsm_report *to_tsm_report(struct config_item *cfg) +{ + struct tsm_report_state *state =3D + container_of(cfg, struct tsm_report_state, cfg); + + return &state->report; +} + +static struct tsm_report_state *to_state(struct tsm_report *report) +{ + return container_of(report, struct tsm_report_state, report); +} + +static int try_advance_write_generation(struct tsm_report *report) +{ + struct tsm_report_state *state =3D to_state(report); + + lockdep_assert_held_write(&tsm_rwsem); + + /* + * Malicious or broken userspace has written enough times for + * read_generation =3D=3D write_generation by modular arithmetic without = an + * interim read. Stop accepting updates until the current report + * configuration is read. + */ + if (state->write_generation =3D=3D state->read_generation - 1) + return -EBUSY; + state->write_generation++; + return 0; +} + +static ssize_t tsm_report_privlevel_store(struct config_item *cfg, + const char *buf, size_t len) +{ + struct tsm_report *report =3D to_tsm_report(cfg); + unsigned int val; + int rc; + + rc =3D kstrtouint(buf, 0, &val); + if (rc) + return rc; + + /* + * The valid privilege levels that a TSM might accept, if it accepts a + * privilege level setting at all, are a max of TSM_PRIVLEVEL_MAX (see + * SEV-SNP GHCB) and a minimum of a TSM selected floor value no less + * than 0. + */ + if (provider.ops->privlevel_floor > val || val > TSM_PRIVLEVEL_MAX) + return -EINVAL; + + guard(rwsem_write)(&tsm_rwsem); + rc =3D try_advance_write_generation(report); + if (rc) + return rc; + report->desc.privlevel =3D val; + + return len; +} +CONFIGFS_ATTR_WO(tsm_report_, privlevel); + +static ssize_t tsm_report_privlevel_floor_show(struct config_item *cfg, + char *buf) +{ + guard(rwsem_read)(&tsm_rwsem); + return sysfs_emit(buf, "%u\n", provider.ops->privlevel_floor); +} +CONFIGFS_ATTR_RO(tsm_report_, privlevel_floor); + +static ssize_t tsm_report_inblob_write(struct config_item *cfg, + const void *buf, size_t count) +{ + struct tsm_report *report =3D to_tsm_report(cfg); + int rc; + + guard(rwsem_write)(&tsm_rwsem); + rc =3D try_advance_write_generation(report); + if (rc) + return rc; + + report->desc.inblob_len =3D count; + memcpy(report->desc.inblob, buf, count); + return count; +} +CONFIGFS_BIN_ATTR_WO(tsm_report_, inblob, NULL, TSM_INBLOB_MAX); + +static ssize_t tsm_report_generation_show(struct config_item *cfg, char *b= uf) +{ + struct tsm_report *report =3D to_tsm_report(cfg); + struct tsm_report_state *state =3D to_state(report); + + guard(rwsem_read)(&tsm_rwsem); + return sysfs_emit(buf, "%lu\n", state->write_generation); +} +CONFIGFS_ATTR_RO(tsm_report_, generation); + +static ssize_t tsm_report_provider_show(struct config_item *cfg, char *buf) +{ + guard(rwsem_read)(&tsm_rwsem); + return sysfs_emit(buf, "%s\n", provider.ops->name); +} +CONFIGFS_ATTR_RO(tsm_report_, provider); + +static ssize_t __read_report(struct tsm_report *report, void *buf, size_t = count, + enum tsm_data_select select) +{ + loff_t offset =3D 0; + u8 *out, len; + + if (select =3D=3D TSM_REPORT) { + out =3D report->outblob; + len =3D report->outblob_len; + } else { + out =3D report->certs; + len =3D report->certs_len; + } + + if (!buf) + return len; + return memory_read_from_buffer(buf, count, &offset, out, len); +} + +static ssize_t read_cached_report(struct tsm_report *report, void *buf, + size_t count, enum tsm_data_select select) +{ + struct tsm_report_state *state =3D to_state(report); + + guard(rwsem_read)(&tsm_rwsem); + if (!report->desc.inblob_len) + return -EINVAL; + + /* + * A given TSM backend always fills in ->outblob regardless of + * whether the report includes certs or not. + */ + if (!report->outblob || + state->read_generation !=3D state->write_generation) + return -EWOULDBLOCK; + + return __read_report(report, buf, count, select); +} + +static ssize_t tsm_report_read(struct tsm_report *report, void *buf, + size_t count, enum tsm_data_select select) +{ + struct tsm_report_state *state =3D to_state(report); + const struct tsm_ops *ops; + ssize_t rc; + + /* try to read from the existing report if present and valid... */ + rc =3D read_cached_report(report, buf, count, select); + if (rc >=3D 0 || rc !=3D -EWOULDBLOCK) + return rc; + + /* slow path, report may need to be regenerated... */ + guard(rwsem_write)(&tsm_rwsem); + ops =3D provider.ops; + if (!report->desc.inblob_len) + return -EINVAL; + + /* did another thread already generate this report? */ + if (report->outblob && + state->read_generation =3D=3D state->write_generation) + goto out; + + kvfree(report->outblob); + kvfree(report->certs); + report->outblob =3D NULL; + report->certs =3D NULL; + rc =3D ops->report_new(report, provider.data); + if (rc < 0) + return rc; + state->read_generation =3D state->write_generation; +out: + return __read_report(report, buf, count, select); +} + +static ssize_t tsm_report_outblob_read(struct config_item *cfg, void *buf, + size_t count) +{ + struct tsm_report *report =3D to_tsm_report(cfg); + + return tsm_report_read(report, buf, count, TSM_REPORT); +} +CONFIGFS_BIN_ATTR_RO(tsm_report_, outblob, NULL, TSM_OUTBLOB_MAX); + +static ssize_t tsm_report_certs_read(struct config_item *cfg, void *buf, + size_t count) +{ + struct tsm_report *report =3D to_tsm_report(cfg); + + return tsm_report_read(report, buf, count, TSM_CERTS); +} +CONFIGFS_BIN_ATTR_RO(tsm_report_, certs, NULL, TSM_OUTBLOB_MAX); + +#define TSM_DEFAULT_ATTRS() \ + &tsm_report_attr_generation, \ + &tsm_report_attr_provider + +static struct configfs_attribute *tsm_report_attrs[] =3D { + TSM_DEFAULT_ATTRS(), + NULL, +}; + +static struct configfs_bin_attribute *tsm_report_bin_attrs[] =3D { + &tsm_report_attr_inblob, + &tsm_report_attr_outblob, + &tsm_report_attr_certs, + NULL, +}; + +static struct configfs_attribute *tsm_report_extra_attrs[] =3D { + TSM_DEFAULT_ATTRS(), + &tsm_report_attr_privlevel, + &tsm_report_attr_privlevel_floor, + NULL, +}; + +static void tsm_report_item_release(struct config_item *cfg) +{ + struct tsm_report *report =3D to_tsm_report(cfg); + struct tsm_report_state *state =3D to_state(report); + + kvfree(report->certs); + kvfree(report->outblob); + kfree(state); +} + +static struct configfs_item_operations tsm_report_item_ops =3D { + .release =3D tsm_report_item_release, +}; + +const struct config_item_type tsm_report_default_type =3D { + .ct_owner =3D THIS_MODULE, + .ct_bin_attrs =3D tsm_report_bin_attrs, + .ct_attrs =3D tsm_report_attrs, + .ct_item_ops =3D &tsm_report_item_ops, +}; +EXPORT_SYMBOL_GPL(tsm_report_default_type); + +const struct config_item_type tsm_report_ext_type =3D { + .ct_owner =3D THIS_MODULE, + .ct_bin_attrs =3D tsm_report_bin_attrs, + .ct_attrs =3D tsm_report_extra_attrs, + .ct_item_ops =3D &tsm_report_item_ops, +}; +EXPORT_SYMBOL_GPL(tsm_report_ext_type); + +static struct config_item *tsm_report_make_item(struct config_group *group, + const char *name) +{ + struct tsm_report_state *state; + + guard(rwsem_read)(&tsm_rwsem); + if (!provider.ops) + return ERR_PTR(-ENXIO); + + state =3D kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return ERR_PTR(-ENOMEM); + + config_item_init_type_name(&state->cfg, name, provider.type); + return &state->cfg; +} + +static struct configfs_group_operations tsm_report_group_ops =3D { + .make_item =3D tsm_report_make_item, +}; + +static const struct config_item_type tsm_reports_type =3D { + .ct_owner =3D THIS_MODULE, + .ct_group_ops =3D &tsm_report_group_ops, +}; + +static const struct config_item_type tsm_root_group_type =3D { + .ct_owner =3D THIS_MODULE, +}; + +static struct configfs_subsystem tsm_configfs =3D { + .su_group =3D { + .cg_item =3D { + .ci_namebuf =3D "tsm", + .ci_type =3D &tsm_root_group_type, + }, + }, + .su_mutex =3D __MUTEX_INITIALIZER(tsm_configfs.su_mutex), +}; + +static struct config_group *tsm_report_group; + +int tsm_register(const struct tsm_ops *ops, void *priv, + const struct config_item_type *type) +{ + const struct tsm_ops *conflict; + + if (!type) + type =3D &tsm_report_default_type; + if (!(type =3D=3D &tsm_report_default_type || type =3D=3D &tsm_report_ext= _type)) + return -EINVAL; + + guard(rwsem_write)(&tsm_rwsem); + conflict =3D provider.ops; + if (conflict) { + pr_err("\"%s\" ops already registered\n", conflict->name); + return -EBUSY; + } + + provider.ops =3D ops; + provider.data =3D priv; + provider.type =3D type; + return 0; +} +EXPORT_SYMBOL_GPL(tsm_register); + +int tsm_unregister(const struct tsm_ops *ops) +{ + guard(rwsem_write)(&tsm_rwsem); + if (ops !=3D provider.ops) + return -EBUSY; + provider.ops =3D NULL; + provider.data =3D NULL; + provider.type =3D NULL; + return 0; +} +EXPORT_SYMBOL_GPL(tsm_unregister); + +static int __init tsm_init(void) +{ + struct config_group *root =3D &tsm_configfs.su_group; + struct config_group *tsm; + int rc; + + config_group_init(root); + rc =3D configfs_register_subsystem(&tsm_configfs); + if (rc) + return rc; + + tsm =3D configfs_register_default_group(root, "report", + &tsm_reports_type); + if (IS_ERR(tsm)) { + configfs_unregister_subsystem(&tsm_configfs); + return PTR_ERR(tsm); + } + tsm_report_group =3D tsm; + + return 0; +} +module_init(tsm_init); + +static void __exit tsm_exit(void) +{ + configfs_unregister_default_group(tsm_report_group); + configfs_unregister_subsystem(&tsm_configfs); +} +module_exit(tsm_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Provide Trusted Security Module attestation reports vi= a configfs"); diff --git a/include/linux/tsm.h b/include/linux/tsm.h new file mode 100644 index 000000000000..1fe1dba3a912 --- /dev/null +++ b/include/linux/tsm.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __TSM_H +#define __TSM_H + +#include +#include +#include + +#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: report generation options / cached report state + * @outblob: generated evidence to provider to the attestation agent + * @outblob_len: sizeof(outblob) + * @write_generation: conflict detection, and report regeneration tracking + * @read_generation: cached report invalidation tracking + * @cfg: configfs interface + */ +struct tsm_report { + struct tsm_desc desc; + size_t outblob_len; + u8 *outblob; + size_t certs_len; + u8 *certs; +}; + +/* + * arch specific ops, only one is expected to be registered at a time + * i.e. only one of SEV, TDX, COVE, etc. + */ +struct tsm_ops { + const char *name; + const int privlevel_floor; + int (*report_new)(struct tsm_report *desc, void *data); +}; + +extern const struct config_item_type tsm_report_ext_type; +extern const struct config_item_type tsm_report_default_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 */ From nobody Fri Dec 19 16:07:26 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id F135DE81818 for ; Tue, 26 Sep 2023 04:17:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233618AbjIZER3 (ORCPT ); Tue, 26 Sep 2023 00:17:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36052 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233098AbjIZERY (ORCPT ); Tue, 26 Sep 2023 00:17:24 -0400 Received: from mgamail.intel.com (mgamail.intel.com [192.55.52.88]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5A359BF for ; Mon, 25 Sep 2023 21:17:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1695701837; x=1727237837; h=subject:from:to:cc:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=yHkxxHhfcS/p1Zj4sNJBN+HXPs+0Ubf8u2ycubsIO3g=; b=CSqdrlm67xxNdLS93wEThmfZXIFP/QheO7Win5Nf3sjFAYzt8/1aOQ0i sXxVIC2eM1476TZp6qyi24eDEXZH8xGN3Uj2CM7sLiDQWSBkdfEB7ONb8 Y2BdU6sjsZOR1m/fP6lefz44F8ns5+1xQzzT10OsLVdNGfLYHY8oFukPI PzhW9VY+weMuLYFrQQwfJBXA4GQxiFCpQ+CoT3MrLHoAhQ4TJ/z8b2/cr Wd2LvzE6v7/FZzj+nVzgIJmWpdgpd6tpk4rqbbOcQpjwhMQtATDBaQ4Z+ 02MNJdT7Y+r4mhu8jHy6QuS9XR43N9rswfTyRUbmtFOlENJVgSWpFyALI A==; X-IronPort-AV: E=McAfee;i="6600,9927,10843"; a="412395834" X-IronPort-AV: E=Sophos;i="6.03,177,1694761200"; d="scan'208";a="412395834" Received: from fmsmga008.fm.intel.com ([10.253.24.58]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 Sep 2023 21:17:17 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10843"; a="814314528" X-IronPort-AV: E=Sophos;i="6.03,177,1694761200"; d="scan'208";a="814314528" Received: from fvivekku-mobl.amr.corp.intel.com (HELO dwillia2-xfh.jf.intel.com) ([10.251.18.72]) by fmsmga008-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 Sep 2023 21:17:16 -0700 Subject: [PATCH v4 3/6] virt: sevguest: Prep for kernel internal {get, get_ext}_report() From: Dan Williams To: linux-coco@lists.linux.dev Cc: Borislav Petkov , Tom Lendacky , Dionna Glaze , Brijesh Singh , peterz@infradead.org, linux-kernel@vger.kernel.org, x86@kernel.org, dave.hansen@linux.intel.com Date: Mon, 25 Sep 2023 21:17:16 -0700 Message-ID: <169570183602.596431.6477217304734993370.stgit@dwillia2-xfh.jf.intel.com> In-Reply-To: <169570181657.596431.6178773442587231200.stgit@dwillia2-xfh.jf.intel.com> References: <169570181657.596431.6178773442587231200.stgit@dwillia2-xfh.jf.intel.com> User-Agent: StGit/0.18-3-g996c MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org In preparation for using the configs-tsm facility to convey attestation blobs to userspace, switch to using the 'sockptr' api for copying payloads to provided buffers where 'sockptr' handles user vs kernel buffers. While configfs-tsm is meant to replace existing confidential computing ioctl() implementations for attestation report retrieval the old ioctl() path needs to stick around for a deprecation period. No behavior change intended. Cc: Borislav Petkov Cc: Tom Lendacky Cc: Dionna Glaze Cc: Brijesh Singh Signed-off-by: Dan Williams Reviewed-by: Kuppuswamy Sathyanarayanan --- drivers/virt/coco/sev-guest/sev-guest.c | 50 ++++++++++++++++++++-------= ---- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/se= v-guest/sev-guest.c index 97dbe715e96a..c3c9e9ea691f 100644 --- a/drivers/virt/coco/sev-guest/sev-guest.c +++ b/drivers/virt/coco/sev-guest/sev-guest.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include =20 @@ -470,7 +471,13 @@ static int handle_guest_request(struct snp_guest_dev *= snp_dev, u64 exit_code, return 0; } =20 -static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_requ= est_ioctl *arg) +struct snp_req_resp { + sockptr_t req_data; + sockptr_t resp_data; +}; + +static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_requ= est_ioctl *arg, + struct snp_req_resp *io) { struct snp_guest_crypto *crypto =3D snp_dev->crypto; struct snp_report_resp *resp; @@ -479,10 +486,10 @@ static int get_report(struct snp_guest_dev *snp_dev, = struct snp_guest_request_io =20 lockdep_assert_held(&snp_cmd_mutex); =20 - if (!arg->req_data || !arg->resp_data) + if (sockptr_is_null(io->req_data) || sockptr_is_null(io->resp_data)) return -EINVAL; =20 - if (copy_from_user(&req, (void __user *)arg->req_data, sizeof(req))) + if (copy_from_sockptr(&req, io->req_data, sizeof(req))) return -EFAULT; =20 /* @@ -501,7 +508,7 @@ static int get_report(struct snp_guest_dev *snp_dev, st= ruct snp_guest_request_io if (rc) goto e_free; =20 - if (copy_to_user((void __user *)arg->resp_data, resp, sizeof(*resp))) + if (copy_to_sockptr(io->resp_data, resp, sizeof(*resp))) rc =3D -EFAULT; =20 e_free: @@ -550,22 +557,25 @@ static int get_derived_key(struct snp_guest_dev *snp_= dev, struct snp_guest_reque return rc; } =20 -static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_= request_ioctl *arg) +static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_= request_ioctl *arg, + struct snp_req_resp *io) + { struct snp_guest_crypto *crypto =3D snp_dev->crypto; struct snp_ext_report_req req; struct snp_report_resp *resp; int ret, npages =3D 0, resp_len; + sockptr_t certs_address; =20 lockdep_assert_held(&snp_cmd_mutex); =20 - if (!arg->req_data || !arg->resp_data) + if (sockptr_is_null(io->req_data) || sockptr_is_null(io->resp_data)) return -EINVAL; =20 - if (copy_from_user(&req, (void __user *)arg->req_data, sizeof(req))) + if (copy_from_sockptr(&req, io->req_data, sizeof(req))) return -EFAULT; =20 - /* userspace does not want certificate data */ + /* caller does not want certificate data */ if (!req.certs_len || !req.certs_address) goto cmd; =20 @@ -573,8 +583,13 @@ static int get_ext_report(struct snp_guest_dev *snp_de= v, struct snp_guest_reques !IS_ALIGNED(req.certs_len, PAGE_SIZE)) return -EINVAL; =20 - if (!access_ok((const void __user *)req.certs_address, req.certs_len)) - return -EFAULT; + if (sockptr_is_kernel(io->resp_data)) { + certs_address =3D KERNEL_SOCKPTR((void *)req.certs_address); + } else { + certs_address =3D USER_SOCKPTR((void __user *)req.certs_address); + if (!access_ok(certs_address.user, req.certs_len)) + return -EFAULT; + } =20 /* * Initialize the intermediate buffer with all zeros. This buffer @@ -604,21 +619,19 @@ static int get_ext_report(struct snp_guest_dev *snp_d= ev, struct snp_guest_reques if (arg->vmm_error =3D=3D SNP_GUEST_VMM_ERR_INVALID_LEN) { req.certs_len =3D snp_dev->input.data_npages << PAGE_SHIFT; =20 - if (copy_to_user((void __user *)arg->req_data, &req, sizeof(req))) + if (copy_to_sockptr(io->req_data, &req, sizeof(req))) ret =3D -EFAULT; } =20 if (ret) goto e_free; =20 - if (npages && - copy_to_user((void __user *)req.certs_address, snp_dev->certs_data, - req.certs_len)) { + if (npages && copy_to_sockptr(certs_address, snp_dev->certs_data, req.cer= ts_len)) { ret =3D -EFAULT; goto e_free; } =20 - if (copy_to_user((void __user *)arg->resp_data, resp, sizeof(*resp))) + if (copy_to_sockptr(io->resp_data, resp, sizeof(*resp))) ret =3D -EFAULT; =20 e_free: @@ -631,6 +644,7 @@ static long snp_guest_ioctl(struct file *file, unsigned= int ioctl, unsigned long struct snp_guest_dev *snp_dev =3D to_snp_dev(file); void __user *argp =3D (void __user *)arg; struct snp_guest_request_ioctl input; + struct snp_req_resp io; int ret =3D -ENOTTY; =20 if (copy_from_user(&input, argp, sizeof(input))) @@ -651,15 +665,17 @@ static long snp_guest_ioctl(struct file *file, unsign= ed int ioctl, unsigned long return -ENOTTY; } =20 + io.req_data =3D USER_SOCKPTR((void __user *)input.req_data); + io.resp_data =3D USER_SOCKPTR((void __user *)input.resp_data); switch (ioctl) { case SNP_GET_REPORT: - ret =3D get_report(snp_dev, &input); + ret =3D get_report(snp_dev, &input, &io); break; case SNP_GET_DERIVED_KEY: ret =3D get_derived_key(snp_dev, &input); break; case SNP_GET_EXT_REPORT: - ret =3D get_ext_report(snp_dev, &input); + ret =3D get_ext_report(snp_dev, &input, &io); break; default: break; From nobody Fri Dec 19 16:07:26 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B8EFAE81817 for ; Tue, 26 Sep 2023 04:17:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233647AbjIZERn (ORCPT ); Tue, 26 Sep 2023 00:17:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44652 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233633AbjIZERf (ORCPT ); Tue, 26 Sep 2023 00:17:35 -0400 Received: from mgamail.intel.com (mgamail.intel.com [192.55.52.88]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8E93012A for ; Mon, 25 Sep 2023 21:17:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1695701843; x=1727237843; h=subject:from:to:cc:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=bDADOCK5y6EfdtJCioVa1Ksl06xw+gGHZUtfoSD5ALE=; b=V8D8lo5+1HG8X0EJ8TY7l4O2f2Ut1XoMIqARpvbUOonps/MGt51nPAFA ZWSI4/Sr8gZUeUuEzAMKsomMB/8SmaL5Y9iYcNBCqfgAhC1DwfvIeiLRK WE3Iy0IfgTwMnRKam6gHynFinm5i6EskerAty+aj/g2QSFz8cHfjLDJgG E7HSy6nDY9eTPhW50u8ROf1Ww+yrH3obyvrWEfVVhQoYf3yQDAkDLJ7jy 60ilYo5SbCuYral0ynHUL4wrIYoZ90qtr81UL+zphxvuVkYcH3tNeYlMN wRwjAPz00jeerQm7wPZkNO8kIOBVuNX/i3bOfscL/5h+KNCBq1C8R+5qu g==; X-IronPort-AV: E=McAfee;i="6600,9927,10843"; a="412395854" X-IronPort-AV: E=Sophos;i="6.03,177,1694761200"; d="scan'208";a="412395854" Received: from fmsmga008.fm.intel.com ([10.253.24.58]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 Sep 2023 21:17:23 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10843"; a="814314534" X-IronPort-AV: E=Sophos;i="6.03,177,1694761200"; d="scan'208";a="814314534" Received: from fvivekku-mobl.amr.corp.intel.com (HELO dwillia2-xfh.jf.intel.com) ([10.251.18.72]) by fmsmga008-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 Sep 2023 21:17:22 -0700 Subject: [PATCH v4 4/6] mm/slab: Add __free() support for kvfree From: Dan Williams To: linux-coco@lists.linux.dev Cc: Andrew Morton , Peter Zijlstra , Greg Kroah-Hartman , Pankaj Gupta , Greg Kroah-Hartman , linux-kernel@vger.kernel.org, x86@kernel.org, dave.hansen@linux.intel.com Date: Mon, 25 Sep 2023 21:17:22 -0700 Message-ID: <169570184210.596431.3407746911924957078.stgit@dwillia2-xfh.jf.intel.com> In-Reply-To: <169570181657.596431.6178773442587231200.stgit@dwillia2-xfh.jf.intel.com> References: <169570181657.596431.6178773442587231200.stgit@dwillia2-xfh.jf.intel.com> User-Agent: StGit/0.18-3-g996c MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Allow for the declaration of variables that trigger kvfree() when they go out of scope. The check for NULL and call to kvfree() can be elided by the compiler in most cases, otherwise without the NULL check an unnecessary call to kvfree() may be emitted. Peter proposed a comment for this detail [1]. Link: http://lore.kernel.org/r/20230816103102.GF980931@hirez.programming.ki= cks-ass.net [1] Cc: Andrew Morton Cc: Peter Zijlstra Cc: Greg Kroah-Hartman Acked-by: Pankaj Gupta Acked-by: Greg Kroah-Hartman Signed-off-by: Dan Williams --- include/linux/slab.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/slab.h b/include/linux/slab.h index 8228d1276a2f..df4c2d45bb86 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -763,6 +763,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); =20 unsigned int kmem_cache_size(struct kmem_cache *s); From nobody Fri Dec 19 16:07:26 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E72CDE81817 for ; Tue, 26 Sep 2023 04:17:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233660AbjIZERs (ORCPT ); Tue, 26 Sep 2023 00:17:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44788 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233664AbjIZERl (ORCPT ); Tue, 26 Sep 2023 00:17:41 -0400 Received: from mgamail.intel.com (mgamail.intel.com [192.55.52.88]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 89F6DCCD for ; Mon, 25 Sep 2023 21:17:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1695701849; x=1727237849; h=subject:from:to:cc:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=PfDJdV1MeteTn9hq9ZkG+FRq6bGpd3Tn8MKDSNKJEJQ=; b=CNE/9wHO8LnIr9AtWhRS6a+hryPeQWiXSP34uBnKTzROmkR/hZoAq6a/ iNDomDNXUaGoU0dDfpdaXOdyEmIUzcCXxIY9f2a5d+H7WSrQBv7oG/ulP 65/1FNI7E9SR0DpuX0EQxYWquIgt0RFC0h7m7+aEAH8gX+T+fE0S2fJFz 36STBlE6MBxf+cHdgMtEHc3XB5tAdYwekSvtVQyYFEIvN92IudGgAFbeH NZHvpECQoe3NEPUxXQHET4mSXzMVqsz4fwaHBoRd2gklwtJAcN4D3KxXa KFXNMV5CV1Vu65sHTJhXluzVl8T4FBX/UTA0GynV6T3WL21gG/cHnJtd+ A==; X-IronPort-AV: E=McAfee;i="6600,9927,10843"; a="412395867" X-IronPort-AV: E=Sophos;i="6.03,177,1694761200"; d="scan'208";a="412395867" Received: from fmsmga008.fm.intel.com ([10.253.24.58]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 Sep 2023 21:17:29 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10843"; a="814314543" X-IronPort-AV: E=Sophos;i="6.03,177,1694761200"; d="scan'208";a="814314543" Received: from fvivekku-mobl.amr.corp.intel.com (HELO dwillia2-xfh.jf.intel.com) ([10.251.18.72]) by fmsmga008-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 Sep 2023 21:17:28 -0700 Subject: [PATCH v4 5/6] virt: sevguest: Add TSM_REPORTS support for SNP_{GET, GET_EXT}_REPORT From: Dan Williams To: linux-coco@lists.linux.dev Cc: Borislav Petkov , Tom Lendacky , Dionna Glaze , Brijesh Singh , Jeremi Piotrowski , peterz@infradead.org, linux-kernel@vger.kernel.org, x86@kernel.org, dave.hansen@linux.intel.com Date: Mon, 25 Sep 2023 21:17:28 -0700 Message-ID: <169570184829.596431.15991881056638719011.stgit@dwillia2-xfh.jf.intel.com> In-Reply-To: <169570181657.596431.6178773442587231200.stgit@dwillia2-xfh.jf.intel.com> References: <169570181657.596431.6178773442587231200.stgit@dwillia2-xfh.jf.intel.com> User-Agent: StGit/0.18-3-g996c MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The sevguest driver was a first mover in the confidential computing space. As a first mover that afforded some leeway to build the driver without concern for common infrastructure. Now that sevguest is no longer a singleton [1] the common operation of building and transmitting attestation report blobs can / should be made common. In this model the so called "TSM-provider" implementations can share a common envelope ABI even if the contents of that envelope remain vendor-specific. When / if the industry agrees on an attestation record format, that definition can also fit in the same ABI. In the meantime the kernel's maintenance burden is reduced and collaboration on the commons is increased. Convert sevguest to use CONFIG_TSM_REPORTS to retrieve the data that the SNP_GET_EXT_REPORT ioctl produces. An example flow follows for retrieving the report blob via the TSM interface utility, assuming no nonce and VMPL=3D=3D2: report=3D/sys/kernel/config/tsm/report/report0 mkdir $report echo 2 > $report/privlevel dd if=3D/dev/urandom bs=3D64 count=3D1 > $report/inblob hexdump -C $report/outblob cat $report/certs rmdir $report Given that the platform implementation is free to return empty certificate = data if none is available it lets configfs-tsm be simplified if it only needs to worry about one output format. The old ioctls can be lazily deprecated, the main motivation of this effort is to stop the proliferation of new ioctls, and to increase cross-vendor collaboration. Note, only compile-tested. Link: http://lore.kernel.org/r/64961c3baf8ce_142af829436@dwillia2-xfh.jf.in= tel.com.notmuch [1] Cc: Borislav Petkov Cc: Tom Lendacky Cc: Dionna Glaze Cc: Brijesh Singh Cc: Jeremi Piotrowski Signed-off-by: Dan Williams --- drivers/virt/coco/sev-guest/Kconfig | 1=20 drivers/virt/coco/sev-guest/sev-guest.c | 130 +++++++++++++++++++++++++++= ++++ 2 files changed, 131 insertions(+) diff --git a/drivers/virt/coco/sev-guest/Kconfig b/drivers/virt/coco/sev-gu= est/Kconfig index da2d7ca531f0..1cffc72c41cb 100644 --- a/drivers/virt/coco/sev-guest/Kconfig +++ b/drivers/virt/coco/sev-guest/Kconfig @@ -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, diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/se= v-guest/sev-guest.c index c3c9e9ea691f..646feb433b1c 100644 --- a/drivers/virt/coco/sev-guest/sev-guest.c +++ b/drivers/virt/coco/sev-guest/sev-guest.c @@ -16,10 +16,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include =20 @@ -759,6 +761,126 @@ static u8 *get_vmpck(int id, struct snp_secrets_page_= layout *layout, u32 **seqno return key; } =20 +struct snp_msg_report_resp_hdr { + u32 status; + u32 report_size; + u8 rsvd[24]; +}; +#define SNP_REPORT_INVALID_PARAM 0x16 +#define SNP_REPORT_INVALID_KEY_SEL 0x27 + +struct snp_msg_cert_entry { + unsigned char guid[16]; + u32 offset; + u32 length; +}; + +static int sev_report_new(struct tsm_report *report, void *data) +{ + static const struct snp_msg_cert_entry zero_ent =3D { 0 }; + struct tsm_desc *desc =3D &report->desc; + struct snp_guest_dev *snp_dev =3D data; + struct snp_msg_report_resp_hdr hdr; + const int report_size =3D SZ_4K; + const int ext_size =3D SZ_16K; + int ret, size =3D report_size + ext_size; + int certs_size, cert_count, i, offset; + u8 *certs_address; + + if (desc->inblob_len !=3D 64) + return -EINVAL; + + void *buf __free(kvfree) =3D kvzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + guard(mutex)(&snp_cmd_mutex); + certs_address =3D buf + report_size; + struct snp_ext_report_req ext_req =3D { + .data =3D { .vmpl =3D desc->privlevel }, + .certs_address =3D (__u64)certs_address, + .certs_len =3D ext_size, + }; + memcpy(&ext_req.data.user_data, desc->inblob, desc->inblob_len); + + struct snp_guest_request_ioctl input =3D { + .msg_version =3D 1, + .req_data =3D (__u64)&ext_req, + .resp_data =3D (__u64)buf, + }; + struct snp_req_resp io =3D { + .req_data =3D KERNEL_SOCKPTR(&ext_req), + .resp_data =3D KERNEL_SOCKPTR(buf), + }; + + ret =3D get_ext_report(snp_dev, &input, &io); + + if (ret) + return ret; + + memcpy(&hdr, buf, sizeof(hdr)); + if (hdr.status =3D=3D SNP_REPORT_INVALID_PARAM) + return -EINVAL; + if (hdr.status =3D=3D SNP_REPORT_INVALID_KEY_SEL) + return -EINVAL; + if (hdr.status) + return -ENXIO; + if ((hdr.report_size + sizeof(hdr)) > report_size) + return -ENOMEM; + + void *rbuf __free(kvfree) =3D kvzalloc(hdr.report_size, GFP_KERNEL); + if (!rbuf) + return -ENOMEM; + + memcpy(rbuf, buf + sizeof(hdr), hdr.report_size); + report->outblob =3D no_free_ptr(rbuf); + report->outblob_len =3D hdr.report_size; + + for (i =3D 0; i < ext_size / sizeof(struct snp_msg_cert_entry); i++) { + struct snp_msg_cert_entry *certs =3D buf + report_size; + + if (memcmp(&certs[i], &zero_ent, sizeof(zero_ent)) =3D=3D 0) + break; + certs_size +=3D certs[i].length; + } + cert_count =3D i; + + /* No certs to report */ + if (cert_count =3D=3D 0) + return 0; + + /* sanity check that the entire certs table with metadata fits */ + if ((cert_count + 1) * sizeof(zero_ent) + certs_size > ext_size) + return -ENXIO; + + void *cbuf __free(kvfree) =3D kvzalloc(certs_size, GFP_KERNEL); + if (!cbuf) + return -ENOMEM; + + /* Concatenate returned certs */ + for (i =3D 0, offset =3D 0; i < cert_count; i++) { + struct snp_msg_cert_entry *certs =3D buf + report_size; + + memcpy(cbuf + offset, certs_address + certs[i].offset, certs[i].length); + offset +=3D certs[i].length; + } + + report->certs =3D no_free_ptr(cbuf); + report->certs_len =3D certs_size; + + return 0; +} + +static const struct tsm_ops sev_tsm_ops =3D { + .name =3D KBUILD_MODNAME, + .report_new =3D sev_report_new, +}; + +static void unregister_sev_tsm(void *data) +{ + tsm_unregister(&sev_tsm_ops); +} + static int __init sev_guest_probe(struct platform_device *pdev) { struct snp_secrets_page_layout *layout; @@ -832,6 +954,14 @@ static int __init sev_guest_probe(struct platform_devi= ce *pdev) snp_dev->input.resp_gpa =3D __pa(snp_dev->response); snp_dev->input.data_gpa =3D __pa(snp_dev->certs_data); =20 + ret =3D tsm_register(&sev_tsm_ops, snp_dev, &tsm_report_ext_type); + if (ret) + goto e_free_cert_data; + + ret =3D devm_add_action_or_reset(&pdev->dev, unregister_sev_tsm, NULL); + if (ret) + goto e_free_cert_data; + ret =3D misc_register(misc); if (ret) goto e_free_cert_data; From nobody Fri Dec 19 16:07:26 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id CC543CE7A8E for ; Tue, 26 Sep 2023 04:18:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233726AbjIZESH (ORCPT ); Tue, 26 Sep 2023 00:18:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44652 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233637AbjIZERq (ORCPT ); Tue, 26 Sep 2023 00:17:46 -0400 Received: from mgamail.intel.com (mgamail.intel.com [192.55.52.136]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AEB4011F for ; Mon, 25 Sep 2023 21:17:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1695701855; x=1727237855; h=subject:from:to:cc:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=4CvzD8vnm9Hmd+l6kx9YigHSx9vV/7dsCiz6scdND3s=; b=NnJQ2U+QCfN3bCHICBFxwRyBxlXyyiAAgXpFu/TZ2nCkXkMPaAwpOfe/ OSJ11TklIUZXw5VKs1VzZ/Ker6NBVDXxfhy/up3kjnrNl/yCDT8NtjaSK w0YO05YfX1/INRRBuofY4dXQe1meWqUaU1/x3+j/IWfCqPerdOI6yRqGf qZUA4h9NlaqCwyqqJaO4l6WO550l6oGo9gT+HWIz7y/+NPydV9J4UPtrA 0d0E0YtIlVkMT3N8wLbTkz0blkdVP1HNwpGa6bDFVbLCiXkqpdTqoO2NF iqwcWbgWnGuvCk7RKKfaiiQnmfrPovervYxQ/1GB6vhnZNylWXXt7Kl29 Q==; X-IronPort-AV: E=McAfee;i="6600,9927,10843"; a="360856997" X-IronPort-AV: E=Sophos;i="6.03,177,1694761200"; d="scan'208";a="360856997" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by fmsmga106.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 Sep 2023 21:17:34 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10843"; a="698312218" X-IronPort-AV: E=Sophos;i="6.03,177,1694761200"; d="scan'208";a="698312218" Received: from fvivekku-mobl.amr.corp.intel.com (HELO dwillia2-xfh.jf.intel.com) ([10.251.18.72]) by orsmga003-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 Sep 2023 21:17:34 -0700 Subject: [PATCH v4 6/6] virt: tdx-guest: Add Quote generation support using TSM_REPORTS From: Dan Williams To: linux-coco@lists.linux.dev Cc: Kuppuswamy Sathyanarayanan , Erdem Aktas , peterz@infradead.org, linux-kernel@vger.kernel.org, x86@kernel.org, dave.hansen@linux.intel.com Date: Mon, 25 Sep 2023 21:17:34 -0700 Message-ID: <169570185411.596431.3153227201984845022.stgit@dwillia2-xfh.jf.intel.com> In-Reply-To: <169570181657.596431.6178773442587231200.stgit@dwillia2-xfh.jf.intel.com> References: <169570181657.596431.6178773442587231200.stgit@dwillia2-xfh.jf.intel.com> User-Agent: StGit/0.18-3-g996c MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Kuppuswamy Sathyanarayanan In TDX guest, the attestation process is used to verify the TDX guest trustworthiness to other entities before provisioning secrets to the guest. The first step in the attestation process is TDREPORT generation, which involves getting the guest measurement data in the format of TDREPORT, which is further used to validate the authenticity of the TDX guest. TDREPORT by design is integrity-protected and can only be verified on the local machine. To support remote verification of the TDREPORT in a SGX-based attestation, the TDREPORT needs to be sent to the SGX Quoting Enclave (QE) to convert it to a remotely verifiable Quote. SGX QE by design can only run outside of the TDX guest (i.e. in a host process or in a normal VM) and guest can use communication channels like vsock or TCP/IP to send the TDREPORT to the QE. But for security concerns, the TDX guest may not support these communication channels. To handle such cases, TDX defines a GetQuote hypercall which can be used by the guest to request the host VMM to communicate with the SGX QE. More details about GetQuote hypercall can be found in TDX Guest-Host Communication Interface (GHCI) for Intel TDX 1.0, section titled "TDG.VP.VMCALL". Trusted Security Module (TSM) [1] exposes a common ABI for Confidential Computing Guest platforms to get the measurement data via ConfigFS. Extend the TSM framework and add support to allow an attestation agent to get the TDX Quote data (included usage example below). report=3D/sys/kernel/config/tsm/report/report0 mkdir $report dd if=3D/dev/urandom bs=3D64 count=3D1 > $report/inblob hexdump -C $report/outblob rmdir $report GetQuote TDVMCALL requires TD guest pass a 4K aligned shared buffer with TDREPORT data as input, which is further used by the VMM to copy the TD Quote result after successful Quote generation. To create the shared buffer, allocate a large enough memory and mark it shared using set_memory_decrypted() in tdx_guest_init(). This buffer will be re-used for GetQuote requests in the TDX TSM handler. Although this method reserves a fixed chunk of memory for GetQuote requests, such one time allocation can help avoid memory fragmentation related allocation failures later in the uptime of the guest. Since the Quote generation process is not time-critical or frequently used, the current version uses a polling model for Quote requests and it also does not support parallel GetQuote requests. Link: https://lore.kernel.org/lkml/169342399185.3934343.3035845348326944519= .stgit@dwillia2-xfh.jf.intel.com/ [1] Signed-off-by: Kuppuswamy Sathyanarayanan Reviewed-by: Erdem Aktas Signed-off-by: Dan Williams --- arch/x86/coco/tdx/tdx.c | 21 +++ arch/x86/include/asm/shared/tdx.h | 1=20 arch/x86/include/asm/tdx.h | 2=20 drivers/virt/coco/tdx-guest/Kconfig | 1=20 drivers/virt/coco/tdx-guest/tdx-guest.c | 229 +++++++++++++++++++++++++++= ++++ 5 files changed, 253 insertions(+), 1 deletion(-) diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index 1d6b863c42b0..752867b1d11b 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -104,6 +104,27 @@ int tdx_mcall_get_report0(u8 *reportdata, u8 *tdreport) } EXPORT_SYMBOL_GPL(tdx_mcall_get_report0); =20 +/** + * 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" 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)), si= ze, 0, 0); +} +EXPORT_SYMBOL_GPL(tdx_hcall_get_quote); + static void __noreturn tdx_panic(const char *msg) { struct tdx_hypercall_args args =3D { diff --git a/arch/x86/include/asm/shared/tdx.h b/arch/x86/include/asm/share= d/tdx.h index 7513b3bb69b7..9eab19950f39 100644 --- a/arch/x86/include/asm/shared/tdx.h +++ b/arch/x86/include/asm/shared/tdx.h @@ -22,6 +22,7 @@ =20 /* TDX hypercall Leaf IDs */ #define TDVMCALL_MAP_GPA 0x10001 +#define TDVMCALL_GET_QUOTE 0x10002 #define TDVMCALL_REPORT_FATAL_ERROR 0x10003 =20 #ifndef __ASSEMBLY__ diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 603e6d1e9d4a..ebd1cda4875f 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -52,6 +52,8 @@ bool tdx_early_handle_ve(struct pt_regs *regs); =20 int tdx_mcall_get_report0(u8 *reportdata, u8 *tdreport); =20 +u64 tdx_hcall_get_quote(u8 *buf, size_t size); + #else =20 static inline void tdx_early_init(void) { }; diff --git a/drivers/virt/coco/tdx-guest/Kconfig b/drivers/virt/coco/tdx-gu= est/Kconfig index 14246fc2fb02..22dd59e19431 100644 --- a/drivers/virt/coco/tdx-guest/Kconfig +++ b/drivers/virt/coco/tdx-guest/Kconfig @@ -1,6 +1,7 @@ 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 diff --git a/drivers/virt/coco/tdx-guest/tdx-guest.c b/drivers/virt/coco/td= x-guest/tdx-guest.c index 5e44a0fa69bd..1253bf76b570 100644 --- a/drivers/virt/coco/tdx-guest/tdx-guest.c +++ b/drivers/virt/coco/tdx-guest/tdx-guest.c @@ -12,12 +12,60 @@ #include #include #include +#include +#include +#include +#include +#include =20 #include =20 #include #include =20 +/* + * 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" + */ +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 =3D 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 __us= er *req) return ret; } =20 +static void free_quote_buf(void *buf) +{ + size_t len =3D PAGE_ALIGN(GET_QUOTE_BUF_SIZE); + unsigned int count =3D 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 =3D PAGE_ALIGN(GET_QUOTE_BUF_SIZE); + unsigned int count =3D len >> PAGE_SHIFT; + void *addr; + + addr =3D 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= ", + * 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 =3D 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 =3D=3D GET_QUOTE_IN_FLIGHT && i++ < timeout) { + if (msleep_interruptible(MSEC_PER_SEC)) + return -EINTR; + } + + return (i =3D=3D timeout) ? -ETIMEDOUT : 0; +} + +static int tdx_report_new(struct tsm_report *report, void *data) +{ + u8 *buf, *reportdata =3D NULL, *tdreport =3D NULL; + struct tdx_quote_buf *quote_buf =3D quote_data; + struct tsm_desc *desc =3D &report->desc; + int ret; + u64 err; + + /* TODO: switch to guard(mutex_intr) */ + if (mutex_lock_interruptible("e_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 =3D=3D GET_QUOTE_IN_FLIGHT) { + ret =3D -EBUSY; + goto done; + } + + if (desc->inblob_len !=3D TDX_REPORTDATA_LEN) { + ret =3D -EINVAL; + goto done; + } + + reportdata =3D kmalloc(TDX_REPORTDATA_LEN, GFP_KERNEL); + if (!reportdata) { + ret =3D -ENOMEM; + goto done; + } + + tdreport =3D kzalloc(TDX_REPORT_LEN, GFP_KERNEL); + if (!tdreport) { + ret =3D -ENOMEM; + goto done; + } + + memcpy(reportdata, desc->inblob, desc->inblob_len); + + /* Generate TDREPORT0 using "TDG.MR.REPORT" TDCALL */ + ret =3D 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 =3D GET_QUOTE_CMD_VER; + quote_buf->in_len =3D TDX_REPORT_LEN; + + memcpy(quote_buf->data, tdreport, TDX_REPORT_LEN); + + err =3D tdx_hcall_get_quote(quote_data, GET_QUOTE_BUF_SIZE); + if (err) { + pr_err("GetQuote hypercall failed, status:%llx\n", err); + ret =3D -EIO; + goto done; + } + + ret =3D wait_for_quote_completion(quote_buf, getquote_timeout); + if (ret) { + pr_err("GetQuote request timedout\n"); + goto done; + } + + buf =3D kvmemdup(quote_buf->data, quote_buf->out_len, GFP_KERNEL); + if (!buf) { + ret =3D -ENOMEM; + goto done; + } + + report->outblob =3D buf; + report->outblob_len =3D quote_buf->out_len; + + /* + * TODO: parse the PEM-formatted cert chain out of the quote buffer when + * provided + */ +done: + mutex_unlock("e_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[] =3D { }; MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); =20 +static const struct tsm_ops tdx_tsm_ops =3D { + .name =3D KBUILD_MODNAME, + .report_new =3D tdx_report_new, +}; + static int __init tdx_guest_init(void) { + int ret; + if (!x86_match_cpu(tdx_guest_ids)) return -ENODEV; =20 - return misc_register(&tdx_misc_dev); + ret =3D misc_register(&tdx_misc_dev); + if (ret) + return ret; + + quote_data =3D alloc_quote_buf(); + if (!quote_data) { + pr_err("Failed to allocate Quote buffer\n"); + ret =3D -ENOMEM; + goto free_misc; + } + + ret =3D 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); =20 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);