From patchwork Tue Jan 30 09:23:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Williams X-Patchwork-Id: 13537063 X-Patchwork-Delegate: bhelgaas@google.com Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C585257891 for ; Tue, 30 Jan 2024 09:23:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706606635; cv=none; b=hW7W/850/iuZPcu26mR7CgbVriz3/UnFZ+Uf6yIB2aU8PZEXExLTUB3dLfHAzg3NgbFyBxdIx7hcTDuz/EZqoHxlR391xDj9ySXiVYGrhUki26G6iImEZkscbhYmb8rylL7bKhLHVVhGfexmw5n33Tv+E81we0f56ovsSpa76cE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706606635; c=relaxed/simple; bh=6JmjlAh10q3aOYaRcDOCaKFrkNNigOHYiN88I3B3YjI=; h=Subject:From:To:Cc:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=qtCT/Mclumg6nPdZ3JHioKJ8CqL9uYKkL6dGYULSO0hTWEeLF6+4bgSiU3Qbuf/EJL+f2OwMGArHY8DSyAZuRhx/KeeQ1aqPuosJH3ommiErlTXafTurnsHxcHk6F4Xu/wAoehpP5gpG5nbNVrM8OsA4HJc4R/B9QexfB+Imn3Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=H6W9fy/1; arc=none smtp.client-ip=192.198.163.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="H6W9fy/1" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1706606632; x=1738142632; h=subject:from:to:cc:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=6JmjlAh10q3aOYaRcDOCaKFrkNNigOHYiN88I3B3YjI=; b=H6W9fy/1JvLWTDw5OvMwLhinjW5V8Qnen1CFrw6H1Cm4ULwPGoy5SEgc IbOmv5LLB2KTxRYZz18aR32r2sovRbFK3keYq3Nnrb30rCRgFN745SaGD mrTSzUQ82jjIaqsg1rIvvUreg62ZpAwSuyK8D6RMvKJ9/4ln1NpOv/YXu +WZ15ebs/z5VMvlrDkngR4ZTXacDwNvlQwgM9fJox8FCvqSNVpn5nJfDu D6/ZPlqzxvHulPs/dvllYYjBQR+kl/ccKZ/BYWjhk6N7VJeUo2PEWnkad z8AO8E+FqyO7PchBbQatn+sgp31cphUsAEoJXSkj2gHYCsaZ/f3D8YBQG w==; X-IronPort-AV: E=McAfee;i="6600,9927,10968"; a="24699861" X-IronPort-AV: E=Sophos;i="6.05,707,1701158400"; d="scan'208";a="24699861" Received: from orviesa004.jf.intel.com ([10.64.159.144]) by fmvoesa101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 30 Jan 2024 01:23:52 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.05,707,1701158400"; d="scan'208";a="3738922" Received: from djayasin-mobl.amr.corp.intel.com (HELO dwillia2-xfh.jf.intel.com) ([10.209.72.104]) by orviesa004-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 30 Jan 2024 01:23:52 -0800 Subject: [RFC PATCH 1/5] PCI/CMA: Prepare to interoperate with TSM authentication From: Dan Williams To: linux-coco@lists.linux.dev Cc: Wu Hao , Yilun Xu , Lukas Wunner , Samuel Ortiz , Alexey Kardashevskiy , Bjorn Helgaas , linux-pci@vger.kernel.org, gregkh@linuxfoundation.org Date: Tue, 30 Jan 2024 01:23:51 -0800 Message-ID: <170660663177.224441.2104783746551322918.stgit@dwillia2-xfh.jf.intel.com> In-Reply-To: <170660662589.224441.11503798303914595072.stgit@dwillia2-xfh.jf.intel.com> References: <170660662589.224441.11503798303914595072.stgit@dwillia2-xfh.jf.intel.com> User-Agent: StGit/0.18-3-g996c Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 A TSM (TEE Security Manager) is a platform agent that facilitates TEE I/O (device assignment for confidential VMs). It uses PCI CMA, IDE, and TDISP to authenticate, encrypt/integrity-protect the link, and bind device-virtual-functions capable of accessing private memory to confidential VMs (TVMs). Unlike native PCI CMA many of the details of establishing a connection between a device (DSM) and the TSM are abstracted through platform APIs. I.e. in the native case Linux picks the keys and validates the certificates, in the TSM case Linux just sees a "success" from invoking a "connect" API with the TSM. SPDM only allows for one session-owner per transport (DOE), so the expectation is that authentication will only ever be in the "native" established case, or the "tsm" established case. Convert the "authenticated" attribute to reflect {"none", "native"} rather than {"0", "1"} in preparation for a follow-on {"none", "native", "tsm"} possibility. Note: Expect this patch gets folded into "PCI/CMA: Expose in sysfs whether devices are authenticated" and assume Linux never ships the binary authenticated ABI. Cc: Wu Hao Cc: Yilun Xu Cc: Lukas Wunner Cc: Samuel Ortiz Cc: Alexey Kardashevskiy Cc: Bjorn Helgaas Signed-off-by: Dan Williams --- Documentation/ABI/testing/sysfs-bus-pci | 14 ++++++++------ drivers/pci/cma.c | 7 ++++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index bec7c197451e..35b0e11fd0e6 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -505,12 +505,14 @@ What: /sys/bus/pci/devices/.../authenticated Date: November 2023 Contact: Lukas Wunner Description: - This file contains 1 if the device authenticated successfully - with CMA-SPDM (PCIe r6.1 sec 6.31). It contains 0 if the - device failed authentication (and may thus be malicious). - - Writing anything to this file causes reauthentication. - That may be opportune after updating the .cma keyring. + This file contains "native" if the device authenticated + successfully with CMA-SPDM (PCIe r6.1 sec 6.31). It contains + "none" if the device failed authentication (and may thus be + malicious). + + Writing "native" to this file causes reauthentication with + kernel-selected keys and the kernel's certificate chain. That + may be opportune after updating the .cma keyring. The file is not visible if authentication is unsupported by the device. diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c index fb9bb5a637a5..be7d2bb21b4c 100644 --- a/drivers/pci/cma.c +++ b/drivers/pci/cma.c @@ -36,6 +36,9 @@ static ssize_t authenticated_store(struct device *dev, (pdev->cma_init_failed || pdev->doe_init_failed)) return -ENOTTY; + if (!sysfs_streq(buf, "native")) + return -EINVAL; + rc = pci_cma_reauthenticate(pdev); if (rc) return rc; @@ -52,7 +55,9 @@ static ssize_t authenticated_show(struct device *dev, (pdev->cma_init_failed || pdev->doe_init_failed)) return -ENOTTY; - return sysfs_emit(buf, "%u\n", spdm_authenticated(pdev->spdm_state)); + if (spdm_authenticated(pdev->spdm_state)) + return sysfs_emit(buf, "native\n"); + return sysfs_emit(buf, "none\n"); } static DEVICE_ATTR_RW(authenticated); From patchwork Tue Jan 30 09:23:57 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Williams X-Patchwork-Id: 13537064 X-Patchwork-Delegate: bhelgaas@google.com Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 53392605AC for ; Tue, 30 Jan 2024 09:23:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706606641; cv=none; b=CHAe3++JcW4XaMXkeWL56JwHIF3IeTYg3aPGAaqEAXsajsl4g4FLMUBzaEbzWtKYRMIf0GAB1N/HwMldrVJQEsIGFgdn1CLKWkPouyC5y5aqxvhBHulju63OVA5Wqd+LQZwwPN1Un7ONpFXuZXws27GMHQmi9THfYLtWgtd/6IQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706606641; c=relaxed/simple; bh=kAQEKNTOi0wauDH56Q45XINuqYS7KpML6MmMrA9UxTc=; h=Subject:From:To:Cc:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=uom3gh4Jb8T2b3FJSoMBgXSiHWPhj76N80waQV6Xh2yH0R0ldCyg82pvOgPrUoPMEBMVW8WRH0O/uU7OAo2s/WAYpNn+plYA95FsTv2IxeDjfulO3TaDjJhtJjYVuvaGPaUUPoGw0klc96m66wZ+roXXT9tPc4nEw9Jw70KlvsQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=br0HQuhx; arc=none smtp.client-ip=192.198.163.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="br0HQuhx" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1706606638; x=1738142638; h=subject:from:to:cc:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=kAQEKNTOi0wauDH56Q45XINuqYS7KpML6MmMrA9UxTc=; b=br0HQuhx1C5Uq2RrhnZymtOU7jf4lvKOD1GpzJkNXzK4tQqDSWDBGok9 wkxEo3lWtJ8ScIEAs1YZZrjoW05v2Z5hy/N5A6tZJZ7ilTALcKDN4M0AB FhUbsJE3BIHN4HHKhG4U9a2Jg9l/nVCQ3U1qn+htbSHspaqnaASC9cdzC KrwfcDneuIG2eJlzKyZKdGaQnrfSxPgBOg4h/hpdWnvbUhiBRGBMxjiOr X3U2Cllplp0PDzV+W9k3qYYXaKi6MUFVZeY4sYzNkJMj29oeeak4xawrL juMb+5MiJ6rDD2Px3SrKI4De6uZox2ylDcCPiAoB9+mqdo3QtV1Y5QLYm Q==; X-IronPort-AV: E=McAfee;i="6600,9927,10968"; a="24699881" X-IronPort-AV: E=Sophos;i="6.05,707,1701158400"; d="scan'208";a="24699881" Received: from orviesa004.jf.intel.com ([10.64.159.144]) by fmvoesa101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 30 Jan 2024 01:23:57 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.05,707,1701158400"; d="scan'208";a="3738933" Received: from djayasin-mobl.amr.corp.intel.com (HELO dwillia2-xfh.jf.intel.com) ([10.209.72.104]) by orviesa004-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 30 Jan 2024 01:23:58 -0800 Subject: [RFC PATCH 2/5] coco/tsm: Establish a new coco/tsm subdirectory From: Dan Williams To: linux-coco@lists.linux.dev Cc: Wu Hao , Yilun Xu , Samuel Ortiz , Alexey Kardashevskiy , linux-pci@vger.kernel.org, gregkh@linuxfoundation.org Date: Tue, 30 Jan 2024 01:23:57 -0800 Message-ID: <170660663734.224441.8533201007071291342.stgit@dwillia2-xfh.jf.intel.com> In-Reply-To: <170660662589.224441.11503798303914595072.stgit@dwillia2-xfh.jf.intel.com> References: <170660662589.224441.11503798303914595072.stgit@dwillia2-xfh.jf.intel.com> User-Agent: StGit/0.18-3-g996c Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 In preparation for new + common TSM infrastructure, establish drivers/virt/coco/tsm/. The tsm.ko module is renamed to tsm_reports.ko, and some of its symbols moved to the "tsm_report_" namespace to separate it from more generic "tsm" objects / symbols. The old tsm.ko module was only ever demand loaded by kernel internal dependencies, so it should not affect existing userspace module install scripts. Cc: Wu Hao Cc: Yilun Xu Cc: Samuel Ortiz Cc: Alexey Kardashevskiy Signed-off-by: Dan Williams --- drivers/virt/coco/Kconfig | 6 ++---- drivers/virt/coco/Makefile | 4 ++-- drivers/virt/coco/sev-guest/sev-guest.c | 8 ++++---- drivers/virt/coco/tdx-guest/tdx-guest.c | 8 ++++---- drivers/virt/coco/tsm/Kconfig | 7 +++++++ drivers/virt/coco/tsm/Makefile | 6 ++++++ drivers/virt/coco/tsm/reports.c | 24 ++++++++++++------------ include/linux/tsm.h | 24 ++++++++++++------------ 8 files changed, 49 insertions(+), 38 deletions(-) create mode 100644 drivers/virt/coco/tsm/Kconfig create mode 100644 drivers/virt/coco/tsm/Makefile rename drivers/virt/coco/{tsm.c => tsm/reports.c} (94%) diff --git a/drivers/virt/coco/Kconfig b/drivers/virt/coco/Kconfig index 87d142c1f932..1e2eb5e768f9 100644 --- a/drivers/virt/coco/Kconfig +++ b/drivers/virt/coco/Kconfig @@ -3,12 +3,10 @@ # 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" + +source "drivers/virt/coco/tsm/Kconfig" diff --git a/drivers/virt/coco/Makefile b/drivers/virt/coco/Makefile index 18c1aba5edb7..2c9d0a178678 100644 --- a/drivers/virt/coco/Makefile +++ b/drivers/virt/coco/Makefile @@ -1,8 +1,8 @@ # 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/ +obj-y += tsm/ diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c index 87f241825bc3..d058cb8f9708 100644 --- a/drivers/virt/coco/sev-guest/sev-guest.c +++ b/drivers/virt/coco/sev-guest/sev-guest.c @@ -786,7 +786,7 @@ struct snp_msg_cert_entry { static int sev_report_new(struct tsm_report *report, void *data) { struct snp_msg_cert_entry *cert_table; - struct tsm_desc *desc = &report->desc; + struct tsm_report_desc *desc = &report->desc; struct snp_guest_dev *snp_dev = data; struct snp_msg_report_resp_hdr hdr; const u32 report_size = SZ_4K; @@ -885,14 +885,14 @@ static int sev_report_new(struct tsm_report *report, void *data) return 0; } -static const struct tsm_ops sev_tsm_ops = { +static const struct tsm_report_ops sev_tsm_ops = { .name = KBUILD_MODNAME, .report_new = sev_report_new, }; static void unregister_sev_tsm(void *data) { - tsm_unregister(&sev_tsm_ops); + tsm_report_unregister(&sev_tsm_ops); } static int __init sev_guest_probe(struct platform_device *pdev) @@ -968,7 +968,7 @@ static int __init sev_guest_probe(struct platform_device *pdev) snp_dev->input.resp_gpa = __pa(snp_dev->response); snp_dev->input.data_gpa = __pa(snp_dev->certs_data); - ret = tsm_register(&sev_tsm_ops, snp_dev, &tsm_report_extra_type); + ret = tsm_report_register(&sev_tsm_ops, snp_dev, &tsm_report_extra_type); if (ret) goto e_free_cert_data; diff --git a/drivers/virt/coco/tdx-guest/tdx-guest.c b/drivers/virt/coco/tdx-guest/tdx-guest.c index 1253bf76b570..904f16461492 100644 --- a/drivers/virt/coco/tdx-guest/tdx-guest.c +++ b/drivers/virt/coco/tdx-guest/tdx-guest.c @@ -163,7 +163,7 @@ 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; + struct tsm_report_desc *desc = &report->desc; int ret; u64 err; @@ -278,7 +278,7 @@ static const struct x86_cpu_id tdx_guest_ids[] = { }; MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); -static const struct tsm_ops tdx_tsm_ops = { +static const struct tsm_report_ops tdx_tsm_ops = { .name = KBUILD_MODNAME, .report_new = tdx_report_new, }; @@ -301,7 +301,7 @@ static int __init tdx_guest_init(void) goto free_misc; } - ret = tsm_register(&tdx_tsm_ops, NULL, NULL); + ret = tsm_report_register(&tdx_tsm_ops, NULL, NULL); if (ret) goto free_quote; @@ -318,7 +318,7 @@ module_init(tdx_guest_init); static void __exit tdx_guest_exit(void) { - tsm_unregister(&tdx_tsm_ops); + tsm_report_unregister(&tdx_tsm_ops); free_quote_buf(quote_data); misc_deregister(&tdx_misc_dev); } diff --git a/drivers/virt/coco/tsm/Kconfig b/drivers/virt/coco/tsm/Kconfig new file mode 100644 index 000000000000..69f04461c83e --- /dev/null +++ b/drivers/virt/coco/tsm/Kconfig @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# TSM (TEE Security Manager) Common infrastructure + +config TSM_REPORTS + select CONFIGFS_FS + tristate diff --git a/drivers/virt/coco/tsm/Makefile b/drivers/virt/coco/tsm/Makefile new file mode 100644 index 000000000000..b48504a3ccfd --- /dev/null +++ b/drivers/virt/coco/tsm/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# TSM (TEE Security Manager) Common infrastructure + +obj-$(CONFIG_TSM_REPORTS) += tsm_reports.o +tsm_reports-y := reports.o diff --git a/drivers/virt/coco/tsm.c b/drivers/virt/coco/tsm/reports.c similarity index 94% rename from drivers/virt/coco/tsm.c rename to drivers/virt/coco/tsm/reports.c index d1c2db83a8ca..6cb0a0e6783d 100644 --- a/drivers/virt/coco/tsm.c +++ b/drivers/virt/coco/tsm/reports.c @@ -13,7 +13,7 @@ #include static struct tsm_provider { - const struct tsm_ops *ops; + const struct tsm_report_ops *ops; const struct config_item_type *type; void *data; } provider; @@ -98,7 +98,7 @@ static ssize_t tsm_report_privlevel_store(struct config_item *cfg, * 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) + if (provider.ops->privlevel_floor > val || val > TSM_REPORT_PRIVLEVEL_MAX) return -EINVAL; guard(rwsem_write)(&tsm_rwsem); @@ -134,7 +134,7 @@ static ssize_t tsm_report_inblob_write(struct config_item *cfg, memcpy(report->desc.inblob, buf, count); return count; } -CONFIGFS_BIN_ATTR_WO(tsm_report_, inblob, NULL, TSM_INBLOB_MAX); +CONFIGFS_BIN_ATTR_WO(tsm_report_, inblob, NULL, TSM_REPORT_INBLOB_MAX); static ssize_t tsm_report_generation_show(struct config_item *cfg, char *buf) { @@ -201,7 +201,7 @@ 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 = to_state(report); - const struct tsm_ops *ops; + const struct tsm_report_ops *ops; ssize_t rc; /* try to read from the existing report if present and valid... */ @@ -241,7 +241,7 @@ static ssize_t tsm_report_outblob_read(struct config_item *cfg, void *buf, return tsm_report_read(report, buf, count, TSM_REPORT); } -CONFIGFS_BIN_ATTR_RO(tsm_report_, outblob, NULL, TSM_OUTBLOB_MAX); +CONFIGFS_BIN_ATTR_RO(tsm_report_, outblob, NULL, TSM_REPORT_OUTBLOB_MAX); static ssize_t tsm_report_auxblob_read(struct config_item *cfg, void *buf, size_t count) @@ -250,7 +250,7 @@ static ssize_t tsm_report_auxblob_read(struct config_item *cfg, void *buf, return tsm_report_read(report, buf, count, TSM_CERTS); } -CONFIGFS_BIN_ATTR_RO(tsm_report_, auxblob, NULL, TSM_OUTBLOB_MAX); +CONFIGFS_BIN_ATTR_RO(tsm_report_, auxblob, NULL, TSM_REPORT_OUTBLOB_MAX); #define TSM_DEFAULT_ATTRS() \ &tsm_report_attr_generation, \ @@ -353,10 +353,10 @@ static struct configfs_subsystem tsm_configfs = { .su_mutex = __MUTEX_INITIALIZER(tsm_configfs.su_mutex), }; -int tsm_register(const struct tsm_ops *ops, void *priv, - const struct config_item_type *type) +int tsm_report_register(const struct tsm_report_ops *ops, void *priv, + const struct config_item_type *type) { - const struct tsm_ops *conflict; + const struct tsm_report_ops *conflict; if (!type) type = &tsm_report_default_type; @@ -375,9 +375,9 @@ int tsm_register(const struct tsm_ops *ops, void *priv, provider.type = type; return 0; } -EXPORT_SYMBOL_GPL(tsm_register); +EXPORT_SYMBOL_GPL(tsm_report_register); -int tsm_unregister(const struct tsm_ops *ops) +int tsm_report_unregister(const struct tsm_report_ops *ops) { guard(rwsem_write)(&tsm_rwsem); if (ops != provider.ops) @@ -387,7 +387,7 @@ int tsm_unregister(const struct tsm_ops *ops) provider.type = NULL; return 0; } -EXPORT_SYMBOL_GPL(tsm_unregister); +EXPORT_SYMBOL_GPL(tsm_report_unregister); static struct config_group *tsm_report_group; diff --git a/include/linux/tsm.h b/include/linux/tsm.h index de8324a2223c..28753608fcf5 100644 --- a/include/linux/tsm.h +++ b/include/linux/tsm.h @@ -5,25 +5,25 @@ #include #include -#define TSM_INBLOB_MAX 64 -#define TSM_OUTBLOB_MAX SZ_32K +#define TSM_REPORT_INBLOB_MAX 64 +#define TSM_REPORT_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 +#define TSM_REPORT_PRIVLEVEL_MAX 3 /** - * struct tsm_desc - option descriptor for generating tsm report blobs + * struct tsm_report_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 { +struct tsm_report_desc { unsigned int privlevel; size_t inblob_len; - u8 inblob[TSM_INBLOB_MAX]; + u8 inblob[TSM_REPORT_INBLOB_MAX]; }; /** @@ -35,7 +35,7 @@ struct tsm_desc { * @auxblob: (optional) auxiliary data to the report (e.g. certificate data) */ struct tsm_report { - struct tsm_desc desc; + struct tsm_report_desc desc; size_t outblob_len; u8 *outblob; size_t auxblob_len; @@ -43,7 +43,7 @@ struct tsm_report { }; /** - * struct tsm_ops - attributes and operations for tsm instances + * struct tsm_report_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 @@ -52,7 +52,7 @@ struct tsm_report { * 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 { +struct tsm_report_ops { const char *name; const unsigned int privlevel_floor; int (*report_new)(struct tsm_report *report, void *data); @@ -63,7 +63,7 @@ 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); +int tsm_report_register(const struct tsm_report_ops *ops, void *priv, + const struct config_item_type *type); +int tsm_report_unregister(const struct tsm_report_ops *ops); #endif /* __TSM_H */ From patchwork Tue Jan 30 09:24:02 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Williams X-Patchwork-Id: 13537065 X-Patchwork-Delegate: bhelgaas@google.com Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1B06338DEC for ; Tue, 30 Jan 2024 09:24:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706606647; cv=none; b=awyLV6FBkk7yh6Awq/n74SG7J+j3a0Rhjbb8IqC2pTy7HE6EALN5tAGDKFJuZsMi5E8ECIMeYH0lU+Q5Y7FT81kNzl/LEllYnaeqKoPq/mKEqv5ZLlgFWQGl4BTddA4VDUjCC4V+2Tc5BNu0s+FZATvKJFRauFZD1mYFF0NiOlQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706606647; c=relaxed/simple; bh=GkuAJg9CebG58qzZActEEIhXLKXqXyptorLfURmgRsE=; h=Subject:From:To:Cc:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=aeOp6zKV60u8F1ZhA6xdD4E5fRs2hkgqFjYYJj0StWZfVkh4wCpBFxPejFFh/MW23h7O4Wp/elJT7ZCjljStzeoHj2EdF7HKXp7IX2Jbz1WXbd54MuaxUYma+sbhEIlZcnqep+nTnFdzibGaCH01I1JAPRLM4gQCORh0U6dRThM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=VEnWuY4h; arc=none smtp.client-ip=192.198.163.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="VEnWuY4h" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1706606646; x=1738142646; h=subject:from:to:cc:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=GkuAJg9CebG58qzZActEEIhXLKXqXyptorLfURmgRsE=; b=VEnWuY4h0P2tGwhEx9jSMduMyW0pRGjmMp/jBNVNuGKZ0F+Zc1nIc09Z QLStnt1SgmC0ZLzHZAeuMj7gs6mxg8HiyhDr+AuM7E49VDpYsVE77NT8P VzZCNrlPGsluzbcVsVIOJYi8fZqdaiyEj5QUVKp+2WK7UOpP9ggZSlb39 ah5F/SB5MDwJF74e6oVv/CSrVm9gecce8yrEPOvf/S25GyOFQKEJHM9t1 OT6oNUqOT9QIz2pmKkn25B0Uda92fJp9bZ2hcUgyuaNMGRE5gTNQCBFK/ vrtGGl8jrvMIllehiltrSIX52vKeOoZxxXltxFseo/0yobJ+DlP6qgXol Q==; X-IronPort-AV: E=McAfee;i="6600,9927,10968"; a="24699925" X-IronPort-AV: E=Sophos;i="6.05,707,1701158400"; d="scan'208";a="24699925" Received: from orsmga002.jf.intel.com ([10.7.209.21]) by fmvoesa101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 30 Jan 2024 01:24:05 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10968"; a="788141877" X-IronPort-AV: E=Sophos;i="6.05,707,1701158400"; d="scan'208";a="788141877" Received: from djayasin-mobl.amr.corp.intel.com (HELO dwillia2-xfh.jf.intel.com) ([10.209.72.104]) by orsmga002-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 30 Jan 2024 01:24:03 -0800 Subject: [RFC PATCH 3/5] coco/tsm: Introduce a shared class device for TSMs From: Dan Williams To: linux-coco@lists.linux.dev Cc: Xiaoyao Li , Isaku Yamahata , Alexey Kardashevskiy , Wu Hao , Yilun Xu , Tom Lendacky , John Allen , linux-pci@vger.kernel.org, gregkh@linuxfoundation.org Date: Tue, 30 Jan 2024 01:24:02 -0800 Message-ID: <170660664287.224441.947018257307932138.stgit@dwillia2-xfh.jf.intel.com> In-Reply-To: <170660662589.224441.11503798303914595072.stgit@dwillia2-xfh.jf.intel.com> References: <170660662589.224441.11503798303914595072.stgit@dwillia2-xfh.jf.intel.com> User-Agent: StGit/0.18-3-g996c Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 A "tsm" is a platform component that provides an API for securely provisioning resources for a confidential guest (TVM) to consume. "TSM" also happens to be the acronym the PCI specification uses to define the platform agent that carries out device-security operations. That platform capability is commonly called TEE I/O. It is this arrival of TEE I/O platforms that requires the "tsm" concept to grow from a low-level arch-specific detail of TVM instantiation, to a frontend interface to mediate device setup and interact with general purpose kernel subsystems outside of arch/ like the PCI core. Provide a virtual (as in /sys/devices/virtual) class device interface to front all of the aspects of a TSM and TEE I/O that are cross-architecture common. This includes mechanisms like enumerating available platform TEE I/O capabilities and provisioning connections between the platform TSM and device DSMs. It is expected to handle hardware TSMs, like AMD SEV-SNP and ARM CCA where there is a physical TEE coprocessor device running firmware, as well as software TSMs like Intel TDX and RISC-V COVE, where there is a privileged software module loaded at runtime. For now this is just the scaffolding for registering a TSM device and/or TSM-specific attribute groups. Cc: Xiaoyao Li Cc: Isaku Yamahata Cc: Alexey Kardashevskiy Cc: Wu Hao Cc: Yilun Xu Cc: Tom Lendacky Cc: John Allen Signed-off-by: Dan Williams --- Documentation/ABI/testing/sysfs-class-tsm | 12 +++ drivers/virt/coco/tsm/Kconfig | 7 ++ drivers/virt/coco/tsm/Makefile | 3 + drivers/virt/coco/tsm/class.c | 100 +++++++++++++++++++++++++++++ include/linux/tsm.h | 8 ++ 5 files changed, 130 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-tsm create mode 100644 drivers/virt/coco/tsm/class.c diff --git a/Documentation/ABI/testing/sysfs-class-tsm b/Documentation/ABI/testing/sysfs-class-tsm new file mode 100644 index 000000000000..304b50b53e65 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-tsm @@ -0,0 +1,12 @@ +What: /sys/class/tsm/tsm0/host +Date: January 2024 +Contact: linux-coco@lists.linux.dev +Description: + (RO) For hardware TSMs represented by a device in /sys/devices, + @host is a link to that device. + Links to hardware TSM sysfs ABIs: + - Documentation/ABI/testing/sysfs-driver-ccp + + For software TSMs instantiated by a software module, @host is a + directory with attributes for that TSM, and those attributes are + documented below. diff --git a/drivers/virt/coco/tsm/Kconfig b/drivers/virt/coco/tsm/Kconfig index 69f04461c83e..595d86917462 100644 --- a/drivers/virt/coco/tsm/Kconfig +++ b/drivers/virt/coco/tsm/Kconfig @@ -5,3 +5,10 @@ config TSM_REPORTS select CONFIGFS_FS tristate + +config ARCH_HAS_TSM + bool + +config TSM + depends on ARCH_HAS_TSM && SYSFS + tristate diff --git a/drivers/virt/coco/tsm/Makefile b/drivers/virt/coco/tsm/Makefile index b48504a3ccfd..f7561169faed 100644 --- a/drivers/virt/coco/tsm/Makefile +++ b/drivers/virt/coco/tsm/Makefile @@ -4,3 +4,6 @@ obj-$(CONFIG_TSM_REPORTS) += tsm_reports.o tsm_reports-y := reports.o + +obj-$(CONFIG_TSM) += tsm.o +tsm-y := class.o diff --git a/drivers/virt/coco/tsm/class.c b/drivers/virt/coco/tsm/class.c new file mode 100644 index 000000000000..a569fa6b09eb --- /dev/null +++ b/drivers/virt/coco/tsm/class.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2024 Intel Corporation. All rights reserved. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +static DECLARE_RWSEM(tsm_core_rwsem); +struct class *tsm_class; +struct tsm_subsys { + struct device dev; + const struct tsm_info *info; +} *tsm_subsys; + +int tsm_register(const struct tsm_info *info) +{ + struct device *dev __free(put_device) = NULL; + struct tsm_subsys *subsys; + int rc; + + guard(rwsem_write)(&tsm_core_rwsem); + if (tsm_subsys) { + pr_warn("failed to register: \"%s\", \"%s\" already registered\n", + info->name, tsm_subsys->info->name); + return -EBUSY; + } + + subsys = kzalloc(sizeof(*subsys), GFP_KERNEL); + if (!subsys) + return -ENOMEM; + + subsys->info = info; + dev = &subsys->dev; + dev->class = tsm_class; + dev->groups = info->groups; + dev_set_name(dev, "tsm0"); + rc = device_register(dev); + if (rc) + return rc; + + if (info->host) { + rc = sysfs_create_link(&dev->kobj, &info->host->kobj, "host"); + if (rc) + return rc; + } + + /* don't auto-free @dev */ + dev = NULL; + tsm_subsys = subsys; + + return 0; +} +EXPORT_SYMBOL_GPL(tsm_register); + +void tsm_unregister(const struct tsm_info *info) +{ + guard(rwsem_write)(&tsm_core_rwsem); + if (!tsm_subsys || info != tsm_subsys->info) { + pr_warn("failed to unregister: \"%s\", not currently registered\n", + info->name); + return; + } + + if (info->host) + sysfs_remove_link(&tsm_subsys->dev.kobj, "host"); + device_unregister(&tsm_subsys->dev); + tsm_subsys = NULL; +} +EXPORT_SYMBOL_GPL(tsm_unregister); + +static void tsm_release(struct device *dev) +{ + struct tsm_subsys *subsys = container_of(dev, typeof(*subsys), dev); + + kfree(subsys); +} + +static int __init tsm_init(void) +{ + tsm_class = class_create("tsm"); + if (IS_ERR(tsm_class)) + return PTR_ERR(tsm_class); + + tsm_class->dev_release = tsm_release; + return 0; +} +module_init(tsm_init) + +static void __exit tsm_exit(void) +{ + class_destroy(tsm_class); +} +module_exit(tsm_exit) + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Trusted Security Module core device model"); diff --git a/include/linux/tsm.h b/include/linux/tsm.h index 28753608fcf5..8cb8a661ba41 100644 --- a/include/linux/tsm.h +++ b/include/linux/tsm.h @@ -5,6 +5,12 @@ #include #include +struct tsm_info { + const char *name; + struct device *host; + const struct attribute_group **groups; +}; + #define TSM_REPORT_INBLOB_MAX 64 #define TSM_REPORT_OUTBLOB_MAX SZ_32K @@ -66,4 +72,6 @@ extern const struct config_item_type tsm_report_extra_type; int tsm_report_register(const struct tsm_report_ops *ops, void *priv, const struct config_item_type *type); int tsm_report_unregister(const struct tsm_report_ops *ops); +int tsm_register(const struct tsm_info *info); +void tsm_unregister(const struct tsm_info *info); #endif /* __TSM_H */ From patchwork Tue Jan 30 09:24:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Williams X-Patchwork-Id: 13537066 X-Patchwork-Delegate: bhelgaas@google.com Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0360160B97 for ; Tue, 30 Jan 2024 09:24:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706606652; cv=none; b=N+NaQhJ1JTVzDJE7KqDG+RtbWjB7tVELKQk0ehC38W7JykisfOaYKDB+UxQiE7Lqc7cAhviX4zFuDKRhE9GJGMNhLPUZJ/NY4svbOi7xdWIeDwaQPLHE3DRi4eNZxnvgbuR3teAzV42/eVirQ/QHMAzkDoPKs2P4QXu9hwMlMe8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706606652; c=relaxed/simple; bh=nIZRVvuaMSKoi1E+P4eke63UOXkToOu6hJbfGXNf7mU=; h=Subject:From:To:Cc:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=UJEKUpom+hPSPgKQy6BvQrA5GP+x0cEunBPTjB562tEiy+BenycPIBG9RzoAnWL2PIpe0VzlpmGyP4vrtkrttkSKFMYYf8bn8CyXZQU0TvHcdOLxZayF/5UH6iyaJiL4kl7BzPCczUYsrDkJzQfXZ8tZXaacawyrmLisnpPUlWM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=ZKpS3l2g; arc=none smtp.client-ip=192.198.163.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="ZKpS3l2g" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1706606651; x=1738142651; h=subject:from:to:cc:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=nIZRVvuaMSKoi1E+P4eke63UOXkToOu6hJbfGXNf7mU=; b=ZKpS3l2g95DoOoa0Ci4JtZC6jSizgCTcPNjut5CA/httOeQ3BgTny3OH YV6F5iBMEQUfiNcqbaS3bD46So6pk0BUTwgxwe026PPYkO9VEzhgw3jE2 pihiyWzfspJmVgkbFiA4CHVYsYuPmYgwTU7zIS5Wy6bNOwgUaE3UqcH0r 5oQtaWHvE94loLA4BTx+pqTZ8pHfnkLuqC0s7ROWg4WNkeItz1gHS1qWf PTtUYZf/ce33qSKQQOrbigQJSjNAll2la7t1e8cAJvLI8OZQ1H5EJwTsz VLFvGM5gpXkNKuEyUnosykfPrs5oJVaY/ycd7YS64bEZMvBtAnACL6yF0 A==; X-IronPort-AV: E=McAfee;i="6600,9927,10968"; a="24699937" X-IronPort-AV: E=Sophos;i="6.05,707,1701158400"; d="scan'208";a="24699937" Received: from orsmga002.jf.intel.com ([10.7.209.21]) by fmvoesa101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 30 Jan 2024 01:24:08 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10968"; a="788141965" X-IronPort-AV: E=Sophos;i="6.05,707,1701158400"; d="scan'208";a="788141965" Received: from djayasin-mobl.amr.corp.intel.com (HELO dwillia2-xfh.jf.intel.com) ([10.209.72.104]) by orsmga002-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 30 Jan 2024 01:24:08 -0800 Subject: [RFC PATCH 4/5] sysfs: Introduce a mechanism to hide static attribute_groups From: Dan Williams To: linux-coco@lists.linux.dev Cc: linux-pci@vger.kernel.org, gregkh@linuxfoundation.org Date: Tue, 30 Jan 2024 01:24:08 -0800 Message-ID: <170660664848.224441.8152468052311375109.stgit@dwillia2-xfh.jf.intel.com> In-Reply-To: <170660662589.224441.11503798303914595072.stgit@dwillia2-xfh.jf.intel.com> References: <170660662589.224441.11503798303914595072.stgit@dwillia2-xfh.jf.intel.com> User-Agent: StGit/0.18-3-g996c Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add a mechanism for named attribute_groups to hide their directory at sysfs_update_group() time, or otherwise skip emitting the group directory when the group is first registered. It piggybacks on is_visible() in a similar manner as SYSFS_PREALLOC, i.e. special flags in the upper bits of the returned mode. To use it, specify a symbol prefix to DEFINE_SYSFS_GROUP_VISIBLE(), and then pass that same prefix to SYSFS_GROUP_VISIBLE() when assigning the @is_visible() callback: DEFINE_SYSFS_GROUP_VISIBLE($prefix) struct attribute_group $prefix_group = { .name = $name, .is_visible = SYSFS_GROUP_VISIBLE($prefix), }; SYSFS_GROUP_VISIBLE() expects a definition of $prefix_group_visible() and $prefix_attr_visible(), where $prefix_group_visible() just returns true / false and $prefix_attr_visible() behaves as normal. The motivation for this capability is to centralize PCI device authentication in the PCI core with a named sysfs group while keeping that group hidden for devices and platforms that do not meet the requirements. In a PCI topology, most devices will not support authentication, a small subset will support just PCI CMA (Component Measurement and Authentication), a smaller subset will support PCI CMA + PCIe IDE (Link Integrity and Encryption), and only next generation server hosts will start to include a platform TSM (TEE Security Manager). Without this capability the alternatives are: * Check if all attributes are invisible and if so, hide the directory. Beyond trouble getting this to work [1], this is an ABI change for scenarios if userspace happens to depend on group visibility absent any attributes. I.e. this new capability avoids regression since it does not retroactively apply to existing cases. * Publish an empty /sys/bus/pci/devices/$pdev/tsm/ directory for all PCI devices (i.e. for the case when TSM platform support is present, but device support is absent). Unfortunate that this will be a vestigial empty directory in the vast majority of cases. * Reintroduce usage of runtime calls to sysfs_{create,remove}_group() in the PCI core. Bjorn has already indicated that he does not want to see any growth of pci_sysfs_init() [2]. * Drop the named group and simulate a directory by prefixing all TSM-related attributes with "tsm_". Unfortunate to not use the naming capability of a sysfs group as intended. In comparison, there is a small potential for regression if for some reason an @is_visible() callback had dependencies on how many times it was called. Additionally, it is no longer an error to update a group that does not have its directory already present, and it is no longer a WARN() to remove a group that was never visible. Link: https://lore.kernel.org/all/2024012321-envious-procedure-4a58@gregkh/ [1] Link: https://lore.kernel.org/linux-pci/20231019200110.GA1410324@bhelgaas/ [2] Signed-off-by: Dan Williams Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/group.c | 45 ++++++++++++++++++++++++++++------- include/linux/sysfs.h | 63 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 87 insertions(+), 21 deletions(-) diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 138676463336..ccb275cdabcb 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -31,6 +31,17 @@ static void remove_files(struct kernfs_node *parent, kernfs_remove_by_name(parent, (*bin_attr)->attr.name); } +static umode_t __first_visible(const struct attribute_group *grp, struct kobject *kobj) +{ + if (grp->attrs && grp->is_visible) + return grp->is_visible(kobj, grp->attrs[0], 0); + + if (grp->bin_attrs && grp->is_bin_visible) + return grp->is_bin_visible(kobj, grp->bin_attrs[0], 0); + + return 0; +} + static int create_files(struct kernfs_node *parent, struct kobject *kobj, kuid_t uid, kgid_t gid, const struct attribute_group *grp, int update) @@ -52,6 +63,7 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj, kernfs_remove_by_name(parent, (*attr)->name); if (grp->is_visible) { mode = grp->is_visible(kobj, *attr, i); + mode &= ~SYSFS_GROUP_INVISIBLE; if (!mode) continue; } @@ -81,6 +93,7 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj, (*bin_attr)->attr.name); if (grp->is_bin_visible) { mode = grp->is_bin_visible(kobj, *bin_attr, i); + mode &= ~SYSFS_GROUP_INVISIBLE; if (!mode) continue; } @@ -127,16 +140,31 @@ static int internal_create_group(struct kobject *kobj, int update, kobject_get_ownership(kobj, &uid, &gid); if (grp->name) { + umode_t mode = __first_visible(grp, kobj); + + if (mode & SYSFS_GROUP_INVISIBLE) + mode = 0; + else + mode = S_IRWXU | S_IRUGO | S_IXUGO; + if (update) { kn = kernfs_find_and_get(kobj->sd, grp->name); if (!kn) { - pr_warn("Can't update unknown attr grp name: %s/%s\n", - kobj->name, grp->name); - return -EINVAL; + pr_debug("attr grp %s/%s not created yet\n", + kobj->name, grp->name); + /* may have been invisible prior to this update */ + update = 0; + } else if (!mode) { + sysfs_remove_group(kobj, grp); + kernfs_put(kn); + return 0; } - } else { - kn = kernfs_create_dir_ns(kobj->sd, grp->name, - S_IRWXU | S_IRUGO | S_IXUGO, + } + + if (!update) { + if (!mode) + return 0; + kn = kernfs_create_dir_ns(kobj->sd, grp->name, mode, uid, gid, kobj, NULL); if (IS_ERR(kn)) { if (PTR_ERR(kn) == -EEXIST) @@ -279,9 +307,8 @@ void sysfs_remove_group(struct kobject *kobj, if (grp->name) { kn = kernfs_find_and_get(parent, grp->name); if (!kn) { - WARN(!kn, KERN_WARNING - "sysfs group '%s' not found for kobject '%s'\n", - grp->name, kobject_name(kobj)); + pr_debug("sysfs group '%s' not found for kobject '%s'\n", + grp->name, kobject_name(kobj)); return; } } else { diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index b717a70219f6..a42642b277dd 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -61,22 +61,32 @@ do { \ /** * struct attribute_group - data structure used to declare an attribute group. * @name: Optional: Attribute group name - * If specified, the attribute group will be created in - * a new subdirectory with this name. + * If specified, the attribute group will be created in a + * new subdirectory with this name. Additionally when a + * group is named, @is_visible and @is_bin_visible may + * return SYSFS_GROUP_INVISIBLE to control visibility of + * the directory itself. * @is_visible: Optional: Function to return permissions associated with an - * attribute of the group. Will be called repeatedly for each - * non-binary attribute in the group. Only read/write + * attribute of the group. Will be called repeatedly for + * each non-binary attribute in the group. Only read/write * permissions as well as SYSFS_PREALLOC are accepted. Must - * return 0 if an attribute is not visible. The returned value - * will replace static permissions defined in struct attribute. + * return 0 if an attribute is not visible. The returned + * value will replace static permissions defined in struct + * attribute. Use SYSFS_GROUP_VISIBLE() when assigning this + * callback to specify separate _group_visible() and + * _attr_visible() handlers. * @is_bin_visible: * Optional: Function to return permissions associated with a * binary attribute of the group. Will be called repeatedly * for each binary attribute in the group. Only read/write - * permissions as well as SYSFS_PREALLOC are accepted. Must - * return 0 if a binary attribute is not visible. The returned - * value will replace static permissions defined in - * struct bin_attribute. + * permissions as well as SYSFS_PREALLOC (and the + * visibility flags for named groups) are accepted. Must + * return 0 if a binary attribute is not visible. The + * returned value will replace static permissions defined + * in struct bin_attribute. If @is_visible is not set, Use + * SYSFS_GROUP_VISIBLE() when assigning this callback to + * specify separate _group_visible() and _attr_visible() + * handlers. * @attrs: Pointer to NULL terminated list of attributes. * @bin_attrs: Pointer to NULL terminated list of binary attributes. * Either attrs or bin_attrs or both must be provided. @@ -91,13 +101,42 @@ struct attribute_group { struct bin_attribute **bin_attrs; }; +#define SYSFS_PREALLOC 010000 +#define SYSFS_GROUP_INVISIBLE 020000 + +/* + * The first call to is_visible() in the create / update path may + * indicate visibility for the entire group + */ +#define DEFINE_SYSFS_GROUP_VISIBLE(name) \ + static inline umode_t sysfs_group_visible_##name( \ + struct kobject *kobj, struct attribute *attr, int n) \ + { \ + if (n == 0 && !name##_group_visible(kobj)) \ + return SYSFS_GROUP_INVISIBLE; \ + return name##_attr_visible(kobj, attr, n); \ + } + +/* + * Same as DEFINE_SYSFS_GROUP_VISIBLE, but for groups with only binary + * attributes + */ +#define DEFINE_SYSFS_BIN_GROUP_VISIBLE(name) \ + static inline umode_t sysfs_group_visible_##name( \ + struct kobject *kobj, struct bin_attribute *attr, int n) \ + { \ + if (n == 0 && !name##_group_visible(kobj)) \ + return SYSFS_GROUP_INVISIBLE; \ + return name##_attr_visible(kobj, attr, n); \ + } + +#define SYSFS_GROUP_VISIBLE(fn) sysfs_group_visible_##fn + /* * Use these macros to make defining attributes easier. * See include/linux/device.h for examples.. */ -#define SYSFS_PREALLOC 010000 - #define __ATTR(_name, _mode, _show, _store) { \ .attr = {.name = __stringify(_name), \ .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \ From patchwork Tue Jan 30 09:24:14 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Williams X-Patchwork-Id: 13537067 X-Patchwork-Delegate: bhelgaas@google.com Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D3EDD60B98 for ; Tue, 30 Jan 2024 09:24:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706606658; cv=none; b=sA5rpScxPg5NGXmmKl/7MrMtvkYivKAipUGjrXor1PXJ8QRmujaj/bIPwSTUcharWh82KNf1Me0rYPIKTpG719ewf6P0qeyA3T4RXSUsMm91X3A6812aC/QRsFPH7DJhd7X3LsCcREQRKnspTpiQl5fSAE/cSG0MVoTNH0gCpwA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706606658; c=relaxed/simple; bh=rzvrlArXqVPxfk5fdQHv3p57w2Vb9jK7bvVqBB7xYVI=; h=Subject:From:To:Cc:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=p1NN77ASobTRzMkC0WT9oVWCOWu37+VTOQCsWKKhk0ZXxHlo37DiZcSb0k2tjsJA6R+oZW3NWPt0OzzxoNLna9iXtiU2qSLKHyd+BetrAui/8con24iOMJpEcdxSCx8gz2+qt/mCSykcPOVsPWGv2AkhiQI7zrLAFx76KHcVGPQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=Me82qmXa; arc=none smtp.client-ip=192.198.163.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="Me82qmXa" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1706606655; x=1738142655; h=subject:from:to:cc:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=rzvrlArXqVPxfk5fdQHv3p57w2Vb9jK7bvVqBB7xYVI=; b=Me82qmXaUTbCFJto7fR+Sn53NpXL2d/k19QpZzM55xm8xwZn0kvVTXH6 zrhNJkGc9yFgj+4PPGYnz5h96rZ+6Hg7PJhJP4BfOTVjDvT8dxidlJQqY 5xAZZArGZsf0VRwmMiA2fkVrcInfdX82cZw8au+sXleGYSFdDnofWasDT PFPhrU1dqbHUjZa274AaIqUC2RPBYWcss3WYd0BloAoVF1lISNnDdJdyg LepqD8VLtnT0OWOO3/Lx3Ksr0ewkCGrvqUcyJazUIAXcajv5FW2cdWUIf Tc9IExYTeWgrFLsHfFs9UOe/92F7BJhGGBlINbBXzYGS/yl5H59yqUi5i w==; X-IronPort-AV: E=McAfee;i="6600,9927,10968"; a="24699944" X-IronPort-AV: E=Sophos;i="6.05,707,1701158400"; d="scan'208";a="24699944" Received: from orsmga002.jf.intel.com ([10.7.209.21]) by fmvoesa101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 30 Jan 2024 01:24:14 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10968"; a="788142051" X-IronPort-AV: E=Sophos;i="6.05,707,1701158400"; d="scan'208";a="788142051" Received: from djayasin-mobl.amr.corp.intel.com (HELO dwillia2-xfh.jf.intel.com) ([10.209.72.104]) by orsmga002-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 30 Jan 2024 01:24:14 -0800 Subject: [RFC PATCH 5/5] PCI/TSM: Authenticate devices via platform TSM From: Dan Williams To: linux-coco@lists.linux.dev Cc: Wu Hao , Yilun Xu , Lukas Wunner , Samuel Ortiz , Alexey Kardashevskiy , Bjorn Helgaas , linux-pci@vger.kernel.org, gregkh@linuxfoundation.org Date: Tue, 30 Jan 2024 01:24:14 -0800 Message-ID: <170660665391.224441.13963835575448844460.stgit@dwillia2-xfh.jf.intel.com> In-Reply-To: <170660662589.224441.11503798303914595072.stgit@dwillia2-xfh.jf.intel.com> References: <170660662589.224441.11503798303914595072.stgit@dwillia2-xfh.jf.intel.com> User-Agent: StGit/0.18-3-g996c Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The PCIe 6.1 specification, section 11, introduces the Trusted Execution Environment (TEE) Device Interface Security Protocol (TDISP). This interface definition builds upon CMA, component measurement and authentication, and IDE, link integrity and data encryption. It adds support for establishing virtual functions within a device that can be assigned to a confidential VM such that the assigned device is enabled to access guest private memory protected by technologies like Intel TDX, AMD SEV-SNP, RISCV COVE, or ARM CCA. The "TSM" (TEE Security Manager) is a concept in the TDISP specification of an agent that mediates between a device security manager (DSM) and system software in both a VMM and a VM. From a Linux perspective the TSM abstracts many of the details of TDISP, IDE, and CMA. Some of those details leak through at times, but for the most part TDISP is an internal implementation detail of the TSM. Similar to the PCI core extensions to support CONFIG_PCI_CMA, CONFIG_PCI_TSM builds upon that to reuse the "authenticated" sysfs attribute, and add more properties + controls in a tsm/ subdirectory of the PCI device sysfs interface. Unlike CMA that can depend on a local to the PCI core implementation, PCI_TSM needs to be prepared for late loading of the platform TSM driver. Consider that the TSM driver may itself be a PCI driver. Userspace can depend on the common TSM device uevent to know when the PCI core has TSM services enabled. The PCI device tsm/ subdirectory is supplemented by the TSM device pci/ directory for platform global TSM properties + controls. All vendor TSM implementations share the property of asking the VMM to perform DOE mailbox operations on behalf of the TSM. That common capability is centralized in PCI core code that invokes an ->exec() operation callback potentially multiple times to service a given request (struct pci_tsm_req). Future operations / verbs will be handled similarly with the "request + exec" model. For now, only "connect" and "disconnect" are implemented which at a minimum is expected to establish IDE for the link. In addition to requests the low-level TSM implementation is notified of device arrival and departure events so that it can filter devices that the TSM is not prepared to support, or otherwise setup and teardown per-device context. Cc: Wu Hao Cc: Yilun Xu Cc: Lukas Wunner Cc: Samuel Ortiz Cc: Alexey Kardashevskiy Cc: Bjorn Helgaas Signed-off-by: Dan Williams --- Documentation/ABI/testing/sysfs-bus-pci | 43 +++- Documentation/ABI/testing/sysfs-class-tsm | 23 ++ drivers/pci/Kconfig | 15 + drivers/pci/Makefile | 2 drivers/pci/cma.c | 5 drivers/pci/pci-sysfs.c | 3 drivers/pci/pci.h | 14 + drivers/pci/probe.c | 1 drivers/pci/remove.c | 1 drivers/pci/tsm.c | 346 +++++++++++++++++++++++++++++ drivers/virt/coco/tsm/Makefile | 1 drivers/virt/coco/tsm/class.c | 22 +- drivers/virt/coco/tsm/pci.c | 83 +++++++ drivers/virt/coco/tsm/tsm.h | 28 ++ include/linux/pci.h | 3 include/linux/tsm.h | 77 ++++++ include/uapi/linux/pci_regs.h | 3 17 files changed, 662 insertions(+), 8 deletions(-) create mode 100644 drivers/pci/tsm.c create mode 100644 drivers/virt/coco/tsm/pci.c create mode 100644 drivers/virt/coco/tsm/tsm.h diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index 35b0e11fd0e6..0eef2128cf09 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -508,11 +508,16 @@ Description: This file contains "native" if the device authenticated successfully with CMA-SPDM (PCIe r6.1 sec 6.31). It contains "none" if the device failed authentication (and may thus be - malicious). + malicious). It transitions from "native" to "tsm" after + successful connection to a tsm, see the "connect" attribute + below. Writing "native" to this file causes reauthentication with kernel-selected keys and the kernel's certificate chain. That - may be opportune after updating the .cma keyring. + may be opportune after updating the .cma keyring. Note + that once connected to a tsm this returns -EBUSY to attempts to + write "native", i.e. first disconnect from the tsm to retrigger + native authentication. The file is not visible if authentication is unsupported by the device. @@ -529,3 +534,37 @@ Description: The reason why authentication support could not be determined is apparent from "dmesg". To probe for authentication support again, exercise the "remove" and "rescan" attributes. + +What: /sys/bus/pci/devices/.../tsm/ +Date: January 2024 +Contact: linux-coco@lists.linux.dev +Description: + This directory only appears if a device supports CMA and IDE, + and only after a TSM driver has loaded and accepted / setup this + PCI device. Similar to the 'authenticated' attribute, trigger + "remove" and "rescan" to retry the initialization. See + Documentation/ABI/testing/sysfs-class-tsm for enumerating the + platform's TSM capabilities. + +What: /sys/bus/pci/devices/.../tsm/connect +Date: January 2024 +Contact: linux-coco@lists.linux.dev +Description: + (RW) Writing "1" to this file triggers the TSM to establish a + secure connection with the device. This typically includes an + SPDM (DMTF Security Protocols and Data Models) session over PCIe + DOE (Data Object Exchange) and PCIe IDE (Integrity and Data + Encryption) establishment. For TSMs and devices that support + both modes of IDE ("link" and "selective") the "connect_mode" + attribute selects the mode. + +What: /sys/bus/pci/devices/.../tsm/connect_mode +Date: January 2024 +Contact: linux-coco@lists.linux.dev +Description: + (RO) Returns the available connection modes optionally with + brackets around the currently active mode if the device is + connected. For example it may show "link selective" for a + disconnected device, "link [selective]" for a selective + connected device, and it may hide a mode that is not supported + by the device or TSM. diff --git a/Documentation/ABI/testing/sysfs-class-tsm b/Documentation/ABI/testing/sysfs-class-tsm index 304b50b53e65..77957882738a 100644 --- a/Documentation/ABI/testing/sysfs-class-tsm +++ b/Documentation/ABI/testing/sysfs-class-tsm @@ -10,3 +10,26 @@ Description: For software TSMs instantiated by a software module, @host is a directory with attributes for that TSM, and those attributes are documented below. + + +What: /sys/class/tsm/tsm0/pci/link_capable +Date: January, 2024 +Contact: linux-coco@lists.linux.dev +Description: + (RO) When present this returns "1\n" to indicate that the TSM + supports establishing Link IDE with a given root-port attached + device. See "tsm/connect_mode" in + Documentation/ABI/testing/sysfs-bus-pci + + +What: /sys/class/tsm/tsm0/pci/selective_streams +Date: January, 2024 +Contact: linux-coco@lists.linux.dev +Description: + (RO) When present this returns the number of currently available + selective IDE streams available to the TSM. When a stream id is + allocated this number is decremented and a link to the PCI + device(s) consuming the stream(s) appears alonside this + attribute in the /sys/class/tsm/tsm0/pci/ directory. See + "tsm/connect" and "tsm/connect_mode" in + Documentation/ABI/testing/sysfs-bus-pci. diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index a5c3cadddd6f..11d788038d19 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -129,6 +129,21 @@ config PCI_CMA A PCI DOE mailbox is used as transport for DMTF SPDM based authentication, measurement and secure channel establishment. +config PCI_TSM + bool "TEE Security Manager for Device Security" + depends on PCI_CMA + depends on TSM + help + The TEE (Trusted Execution Environment) Device Interface + Security Protocol (TDISP) defines a "TSM" as a platform agent + that manages device authentication, link encryption, link + integrity protection, and assignment of PCI device functions + (virtual or physical) to confidential computing VMs that can + access (DMA) guest private memory. + + Say Y to enable the PCI subsystem to enable the IDE and + TDISP capabilities of devices via TSM semantics. + config PCI_DOE bool diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index cc8b5d1d15b9..c4117d67ea83 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -38,6 +38,8 @@ obj-$(CONFIG_PCI_CMA) += cma.o cma.asn1.o $(obj)/cma.o: $(obj)/cma.asn1.h $(obj)/cma.asn1.o: $(obj)/cma.asn1.c $(obj)/cma.asn1.h +obj-$(CONFIG_PCI_TSM) += tsm.o + # Endpoint library must be initialized before its users obj-$(CONFIG_PCI_ENDPOINT) += endpoint/ diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c index be7d2bb21b4c..5a69e9919589 100644 --- a/drivers/pci/cma.c +++ b/drivers/pci/cma.c @@ -39,6 +39,9 @@ static ssize_t authenticated_store(struct device *dev, if (!sysfs_streq(buf, "native")) return -EINVAL; + if (pci_tsm_authenticated(pdev)) + return -EBUSY; + rc = pci_cma_reauthenticate(pdev); if (rc) return rc; @@ -55,6 +58,8 @@ static ssize_t authenticated_show(struct device *dev, (pdev->cma_init_failed || pdev->doe_init_failed)) return -ENOTTY; + if (pci_tsm_authenticated(pdev)) + return sysfs_emit(buf, "tsm\n"); if (spdm_authenticated(pdev->spdm_state)) return sysfs_emit(buf, "native\n"); return sysfs_emit(buf, "none\n"); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 368c4f71cc55..4327f8c2e6b5 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1654,6 +1654,9 @@ const struct attribute_group *pci_dev_attr_groups[] = { #endif #ifdef CONFIG_PCI_CMA &pci_cma_attr_group, +#endif +#ifdef CONFIG_PCI_TSM + &pci_tsm_attr_group, #endif NULL, }; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 2b7d8d0b2e21..daa20866bc90 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -350,6 +350,20 @@ static inline int pci_cma_reauthenticate(struct pci_dev *pdev) } #endif +#ifdef CONFIG_PCI_TSM +void pci_tsm_init(struct pci_dev *pdev); +void pci_tsm_destroy(struct pci_dev *pdev); +extern const struct attribute_group pci_tsm_attr_group; +bool pci_tsm_authenticated(struct pci_dev *pdev); +#else +static inline void pci_tsm_init(struct pci_dev *pdev) { } +static inline void pci_tsm_destroy(struct pci_dev *pdev) { } +static inline bool pci_tsm_authenticated(struct pci_dev *pdev) +{ + return false; +} +#endif + /** * pci_dev_set_io_state - Set the new error state if possible. * diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6b09c962c0b8..f60d6c3c8c48 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2542,6 +2542,7 @@ static void pci_init_capabilities(struct pci_dev *dev) pci_rcec_init(dev); /* Root Complex Event Collector */ pci_doe_init(dev); /* Data Object Exchange */ pci_cma_init(dev); /* Component Measurement & Auth */ + pci_tsm_init(dev); /* TEE Security Manager connection */ pcie_report_downtraining(dev); pci_init_reset_methods(dev); diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index f009ac578997..228fa6ccf911 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -39,6 +39,7 @@ static void pci_destroy_dev(struct pci_dev *dev) list_del(&dev->bus_list); up_write(&pci_bus_sem); + pci_tsm_destroy(dev); pci_cma_destroy(dev); pci_doe_destroy(dev); pcie_aspm_exit_link_state(dev); diff --git a/drivers/pci/tsm.c b/drivers/pci/tsm.c new file mode 100644 index 000000000000..f74de0ee49a0 --- /dev/null +++ b/drivers/pci/tsm.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TEE Security Manager for the TEE Device Interface Security Protocol + * (TDISP, PCIe r6.1 sec 11) + * + * Copyright(c) 2024 Intel Corporation. All rights reserved. + */ + +#define dev_fmt(fmt) "TSM: " fmt + +#include +#include +#include +#include +#include "pci.h" + +/* collect tsm capable devices to rendezvous with the tsm driver */ +static DEFINE_XARRAY(pci_tsm_devs); + +/* + * Provide a read/write lock against the init / exit of pdev tsm + * capabilities and arrival/departure of a tsm instance + */ +static DECLARE_RWSEM(pci_tsm_rwsem); +static const struct tsm_pci_ops *tsm_ops; + +void generic_pci_tsm_req_free(struct pci_tsm_req *req) +{ + kfree(req); +} +EXPORT_SYMBOL_GPL(generic_pci_tsm_req_free); + +struct pci_tsm_req *generic_pci_tsm_req_alloc(struct pci_dev *pdev, enum pci_tsm_op op) +{ + struct pci_tsm_req *req = kzalloc(sizeof(*req), GFP_KERNEL); + + if (!req) + return NULL; + req->op = op; + return req; +} +EXPORT_SYMBOL_GPL(generic_pci_tsm_req_alloc); + +DEFINE_FREE(req_free, struct pci_tsm_req *, if (_T) tsm_ops->req_free(_T)) + +static int pci_tsm_disconnect(struct pci_dev *pdev) +{ + struct pci_tsm_req *req __free(req_free) = NULL; + + /* opportunistic state checks to skip allocating a request */ + if (pdev->tsm->state < PCI_TSM_CONNECT) + return 0; + + req = tsm_ops->req_alloc(pdev, PCI_TSM_OP_DISCONNECT); + if (!req) + return -ENOMEM; + + scoped_cond_guard(mutex_intr, return -EINTR, tsm_ops->lock) { + enum pci_tsm_op_status status; + + /* revalidate state */ + if (pdev->tsm->state < PCI_TSM_CONNECT) + return 0; + if (pdev->tsm->state < PCI_TSM_INIT) + return -ENXIO; + + do { + status = tsm_ops->exec(pdev, req); + req->seq++; + /* TODO: marshal SPDM request */ + } while (status == PCI_TSM_SPDM_REQ); + + if (status == PCI_TSM_FAIL) + return -EIO; + pdev->tsm->state = PCI_TSM_INIT; + } + return 0; +} + +static int pci_tsm_connect(struct pci_dev *pdev) +{ + struct pci_tsm_req *req __free(req_free) = NULL; + + /* opportunistic state checks to skip allocating a request */ + if (pdev->tsm->state >= PCI_TSM_CONNECT) + return 0; + + req = tsm_ops->req_alloc(pdev, PCI_TSM_OP_CONNECT); + if (!req) + return -ENOMEM; + + scoped_cond_guard(mutex_intr, return -EINTR, tsm_ops->lock) { + enum pci_tsm_op_status status; + + /* revalidate state */ + if (pdev->tsm->state >= PCI_TSM_CONNECT) + return 0; + if (pdev->tsm->state < PCI_TSM_INIT) + return -ENXIO; + + do { + status = tsm_ops->exec(pdev, req); + req->seq++; + } while (status == PCI_TSM_SPDM_REQ); + + if (status == PCI_TSM_FAIL) + return -EIO; + pdev->tsm->state = PCI_TSM_CONNECT; + } + return 0; +} + +static ssize_t connect_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + bool connect; + int rc = kstrtobool(buf, &connect); + struct pci_dev *pdev = to_pci_dev(dev); + + if (rc) + return rc; + + if (connect) { + if (!spdm_authenticated(pdev->spdm_state)) { + pci_dbg(pdev, "SPDM authentication pre-requisite not met.\n"); + return -ENXIO; + } + rc = pci_tsm_connect(pdev); + if (rc) + return rc; + return len; + } + + rc = pci_tsm_disconnect(pdev); + if (rc) + return rc; + return len; +} + +static ssize_t connect_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + return sysfs_emit(buf, "%d\n", pdev->tsm->state >= PCI_TSM_CONNECT); +} +static DEVICE_ATTR_RW(connect); + +static const char *const pci_tsm_modes[] = { + [PCI_TSM_MODE_LINK] = "link", + [PCI_TSM_MODE_SELECTIVE] = "selective", +}; + +static ssize_t connect_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct pci_dev *pdev = to_pci_dev(dev); + int i; + + guard(mutex)(tsm_ops->lock); + if (pdev->tsm->state >= PCI_TSM_CONNECT) + return -EBUSY; + for (i = 0; i < ARRAY_SIZE(pci_tsm_modes); i++) + if (sysfs_streq(buf, pci_tsm_modes[i])) + break; + if (i == PCI_TSM_MODE_LINK) { + if (pdev->tsm->link_capable) + pdev->tsm->mode = PCI_TSM_MODE_LINK; + return -EOPNOTSUPP; + } else if (i == PCI_TSM_MODE_SELECTIVE) { + if (pdev->tsm->selective_capable) + pdev->tsm->mode = PCI_TSM_MODE_SELECTIVE; + return -EOPNOTSUPP; + } else + return -EINVAL; + return len; +} + +static ssize_t connect_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + ssize_t count = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(pci_tsm_modes); i++) { + if (i == PCI_TSM_MODE_LINK) { + if (!pdev->tsm->link_capable) + continue; + } else if (i == PCI_TSM_MODE_SELECTIVE) { + if (!pdev->tsm->selective_capable) + continue; + } + + if (i == pdev->tsm->mode) + count += sysfs_emit_at(buf, count, "[%s] ", + pci_tsm_modes[i]); + else + count += sysfs_emit_at(buf, count, "%s ", + pci_tsm_modes[i]); + } + + if (count) + buf[count - 1] = '\n'; + + return count; +} +static DEVICE_ATTR_RW(connect_mode); + +static umode_t pci_tsm_attr_visible(struct kobject *kobj, struct attribute *a, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct pci_dev *pdev = to_pci_dev(dev); + + if (a == &dev_attr_connect_mode.attr) { + if (pdev->tsm->link_capable || pdev->tsm->selective_capable) + return a->mode; + return 0; + } + + return a->mode; +} + +static bool pci_tsm_group_visible(struct kobject *kobj) +{ + struct device *dev = kobj_to_dev(kobj); + struct pci_dev *pdev = to_pci_dev(dev); + + if (pdev->tsm && pdev->tsm->state > PCI_TSM_IDLE) + return true; + return false; +} +DEFINE_SYSFS_GROUP_VISIBLE(pci_tsm); + +static struct attribute *pci_tsm_attrs[] = { + &dev_attr_connect.attr, + &dev_attr_connect_mode.attr, + NULL, +}; + +const struct attribute_group pci_tsm_attr_group = { + .name = "tsm", + .attrs = pci_tsm_attrs, + .is_visible = SYSFS_GROUP_VISIBLE(pci_tsm), +}; + +static int pci_tsm_add(struct pci_dev *pdev) +{ + lockdep_assert_held(&pci_tsm_rwsem); + if (!tsm_ops) + return 0; + scoped_guard(mutex, tsm_ops->lock) { + if (pdev->tsm->state < PCI_TSM_INIT) { + int rc = tsm_ops->add(pdev); + + if (rc) + return rc; + } + pdev->tsm->state = PCI_TSM_INIT; + } + return sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group); +} + +static void pci_tsm_del(struct pci_dev *pdev) +{ + lockdep_assert_held(&pci_tsm_rwsem); + /* shutdown sysfs operations before tsm delete */ + pdev->tsm->state = PCI_TSM_IDLE; + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group); + guard(mutex)(tsm_ops->lock); + tsm_ops->del(pdev); +} + +int pci_tsm_register(const struct tsm_pci_ops *ops) +{ + struct pci_dev *pdev; + unsigned long index; + + guard(rwsem_write)(&pci_tsm_rwsem); + if (tsm_ops) + return -EBUSY; + tsm_ops = ops; + xa_for_each(&pci_tsm_devs, index, pdev) + pci_tsm_add(pdev); + return 0; +} +EXPORT_SYMBOL_GPL(pci_tsm_register); + +void pci_tsm_unregister(const struct tsm_pci_ops *ops) +{ + struct pci_dev *pdev; + unsigned long index; + + guard(rwsem_write)(&pci_tsm_rwsem); + if (ops != tsm_ops) + return; + xa_for_each(&pci_tsm_devs, index, pdev) + pci_tsm_del(pdev); + tsm_ops = NULL; +} +EXPORT_SYMBOL_GPL(pci_tsm_unregister); + +void pci_tsm_init(struct pci_dev *pdev) +{ + u16 ide_cap; + int rc; + + if (!pdev->cma_capable) + return; + + ide_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_IDE); + if (!ide_cap) + return; + + struct pci_tsm *tsm __free(kfree) = kzalloc(sizeof(*tsm), GFP_KERNEL); + if (!tsm) + return; + + tsm->ide_cap = ide_cap; + + rc = xa_insert(&pci_tsm_devs, (unsigned long)pdev, pdev, GFP_KERNEL); + if (rc) { + pci_dbg(pdev, "failed to register tsm capable device\n"); + return; + } + + guard(rwsem_write)(&pci_tsm_rwsem); + pdev->tsm = no_free_ptr(tsm); + pci_tsm_add(pdev); +} + +void pci_tsm_destroy(struct pci_dev *pdev) +{ + guard(rwsem_write)(&pci_tsm_rwsem); + pci_tsm_del(pdev); + xa_erase(&pci_tsm_devs, (unsigned long)pdev); + kfree(pdev->tsm); + pdev->tsm = NULL; +} + +bool pci_tsm_authenticated(struct pci_dev *pdev) +{ + guard(rwsem_read)(&pci_tsm_rwsem); + return pdev->tsm && pdev->tsm->state >= PCI_TSM_CONNECT; +} diff --git a/drivers/virt/coco/tsm/Makefile b/drivers/virt/coco/tsm/Makefile index f7561169faed..a4f0d07d7d97 100644 --- a/drivers/virt/coco/tsm/Makefile +++ b/drivers/virt/coco/tsm/Makefile @@ -7,3 +7,4 @@ tsm_reports-y := reports.o obj-$(CONFIG_TSM) += tsm.o tsm-y := class.o +tsm-$(CONFIG_PCI_TSM) += pci.o diff --git a/drivers/virt/coco/tsm/class.c b/drivers/virt/coco/tsm/class.c index a569fa6b09eb..a459e51c0892 100644 --- a/drivers/virt/coco/tsm/class.c +++ b/drivers/virt/coco/tsm/class.c @@ -8,13 +8,11 @@ #include #include #include +#include "tsm.h" static DECLARE_RWSEM(tsm_core_rwsem); -struct class *tsm_class; -struct tsm_subsys { - struct device dev; - const struct tsm_info *info; -} *tsm_subsys; +static struct class *tsm_class; +static struct tsm_subsys *tsm_subsys; int tsm_register(const struct tsm_info *info) { @@ -52,6 +50,10 @@ int tsm_register(const struct tsm_info *info) dev = NULL; tsm_subsys = subsys; + rc = tsm_pci_init(info); + if (rc) + pr_err("PCI initialization failure: %d\n", rc); + return 0; } EXPORT_SYMBOL_GPL(tsm_register); @@ -65,6 +67,8 @@ void tsm_unregister(const struct tsm_info *info) return; } + tsm_pci_destroy(info); + if (info->host) sysfs_remove_link(&tsm_subsys->dev.kobj, "host"); device_unregister(&tsm_subsys->dev); @@ -79,6 +83,13 @@ static void tsm_release(struct device *dev) kfree(subsys); } +static const struct attribute_group *tsm_attr_groups[] = { +#ifdef CONFIG_PCI_TSM + &tsm_pci_attr_group, +#endif + NULL, +}; + static int __init tsm_init(void) { tsm_class = class_create("tsm"); @@ -86,6 +97,7 @@ static int __init tsm_init(void) return PTR_ERR(tsm_class); tsm_class->dev_release = tsm_release; + tsm_class->dev_groups = tsm_attr_groups; return 0; } module_init(tsm_init) diff --git a/drivers/virt/coco/tsm/pci.c b/drivers/virt/coco/tsm/pci.c new file mode 100644 index 000000000000..b3684ad7114f --- /dev/null +++ b/drivers/virt/coco/tsm/pci.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2024 Intel Corporation. All rights reserved. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include "tsm.h" + +static ssize_t link_capable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tsm_subsys *subsys = container_of(dev, typeof(*subsys), dev); + + return sysfs_emit(buf, "%u\n", subsys->info->link_stream_capable); +} +static DEVICE_ATTR_RO(link_capable); + +static ssize_t selective_streams_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tsm_subsys *subsys = container_of(dev, typeof(*subsys), dev); + + return sysfs_emit(buf, "%u\n", subsys->info->nr_selective_streams); +} +static DEVICE_ATTR_RO(selective_streams); + +static umode_t tsm_pci_attr_visible(struct kobject *kobj, struct attribute *a, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct tsm_subsys *subsys = container_of(dev, typeof(*subsys), dev); + const struct tsm_info *info = subsys->info; + + if (a == &dev_attr_link_capable.attr) { + if (info->link_stream_capable) + return a->mode; + return 0; + } + + if (a == &dev_attr_selective_streams.attr) { + if (info->nr_selective_streams) + return a->mode; + return 0; + } + + return a->mode; +} + +static bool tsm_pci_group_visible(struct kobject *kobj) +{ + struct device *dev = kobj_to_dev(kobj); + struct tsm_subsys *subsys = container_of(dev, typeof(*subsys), dev); + + if (subsys->info->pci_ops) + return true; + return false; +} +DEFINE_SYSFS_GROUP_VISIBLE(tsm_pci); + +static struct attribute *tsm_pci_attrs[] = { + &dev_attr_link_capable.attr, + &dev_attr_selective_streams.attr, + NULL, +}; + +const struct attribute_group tsm_pci_attr_group = { + .name = "pci", + .attrs = tsm_pci_attrs, + .is_visible = SYSFS_GROUP_VISIBLE(tsm_pci), +}; + +int tsm_pci_init(const struct tsm_info *info) +{ + if (!info->pci_ops) + return 0; + + return pci_tsm_register(info->pci_ops); +} + +void tsm_pci_destroy(const struct tsm_info *info) +{ + pci_tsm_unregister(info->pci_ops); +} diff --git a/drivers/virt/coco/tsm/tsm.h b/drivers/virt/coco/tsm/tsm.h new file mode 100644 index 000000000000..407c388a109b --- /dev/null +++ b/drivers/virt/coco/tsm/tsm.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __TSM_CORE_H +#define __TSM_CORE_H + +#include + +struct tsm_info; +struct tsm_subsys { + struct device dev; + const struct tsm_info *info; +}; + +#ifdef CONFIG_PCI_TSM +int tsm_pci_init(const struct tsm_info *info); +void tsm_pci_destroy(const struct tsm_info *info); +extern const struct attribute_group tsm_pci_attr_group; +#else +static inline int tsm_pci_init(const struct tsm_info *info) +{ + return 0; +} +static inline void tsm_pci_destroy(const struct tsm_info *info) +{ +} +#endif + +#endif /* TSM_CORE_H */ + diff --git a/include/linux/pci.h b/include/linux/pci.h index 4a04ce7685e7..132962b21e04 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -522,6 +522,9 @@ struct pci_dev { struct spdm_state *spdm_state; /* Security Protocol and Data Model */ unsigned int cma_capable:1; /* Authentication supported */ unsigned int cma_init_failed:1; +#endif +#ifdef CONFIG_PCI_TSM + struct pci_tsm *tsm; /* TSM operation state */ #endif u16 acs_cap; /* ACS Capability offset */ phys_addr_t rom; /* Physical address if not from BAR */ diff --git a/include/linux/tsm.h b/include/linux/tsm.h index 8cb8a661ba41..f5dbdfa65d8d 100644 --- a/include/linux/tsm.h +++ b/include/linux/tsm.h @@ -4,11 +4,15 @@ #include #include +#include struct tsm_info { const char *name; struct device *host; const struct attribute_group **groups; + const struct tsm_pci_ops *pci_ops; + unsigned int nr_selective_streams; + unsigned int link_stream_capable:1; }; #define TSM_REPORT_INBLOB_MAX 64 @@ -74,4 +78,77 @@ int tsm_report_register(const struct tsm_report_ops *ops, void *priv, int tsm_report_unregister(const struct tsm_report_ops *ops); int tsm_register(const struct tsm_info *info); void tsm_unregister(const struct tsm_info *info); + +enum pci_tsm_op_status { + PCI_TSM_FAIL = -1, + PCI_TSM_OK, + PCI_TSM_SPDM_REQ, +}; + +enum pci_tsm_op { + PCI_TSM_OP_CONNECT, + PCI_TSM_OP_DISCONNECT, +}; + +struct pci_tsm_req { + enum pci_tsm_op op; + unsigned int seq; +}; + +struct pci_dev; +/** + * struct tsm_pci_ops - Low-level TSM-exported interface to the PCI core + * @add: accept device for tsm operation, locked + * @del: teardown tsm context for @pdev, locked + * @req_alloc: setup context for given operation, unlocked + * @req_free: teardown context for given request, unlocked + * @exec: run @req, may be invoked multiple times per @req, locked + * @lock: tsm work is one device and one op at a time + */ +struct tsm_pci_ops { + int (*add)(struct pci_dev *pdev); + void (*del)(struct pci_dev *pdev); + struct pci_tsm_req *(*req_alloc)(struct pci_dev *pdev, + enum pci_tsm_op op); + struct pci_tsm_req *(*req_free)(struct pci_tsm_req *req); + enum pci_tsm_op_status (*exec)(struct pci_dev *pdev, + struct pci_tsm_req *req); + struct mutex *lock; +}; + +enum pci_tsm_state { + PCI_TSM_IDLE, + PCI_TSM_INIT, + PCI_TSM_CONNECT, +}; + +enum pci_tsm_mode { + PCI_TSM_MODE_LINK, + PCI_TSM_MODE_SELECTIVE, +}; + +struct pci_tsm { + enum pci_tsm_state state; + enum pci_tsm_mode mode; + u16 ide_cap; + unsigned int link_capable:1; + unsigned int selective_capable:1; + void *tsm_data; +}; + +#ifdef CONFIG_PCI_TSM +int pci_tsm_register(const struct tsm_pci_ops *ops); +void pci_tsm_unregister(const struct tsm_pci_ops *ops); +void generic_pci_tsm_req_free(struct pci_tsm_req *req); +struct pci_tsm_req *generic_pci_tsm_req_alloc(struct pci_dev *pdev, + enum pci_tsm_op op); +#else +static inline int pci_tsm_register(const struct tsm_pci_ops *ops) +{ + return 0; +} +static inline void pci_tsm_unregister(const struct tsm_pci_ops *ops) +{ +} +#endif #endif /* __TSM_H */ diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index a39193213ff2..1219d50f8e89 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -742,7 +742,8 @@ #define PCI_EXT_CAP_ID_PL_16GT 0x26 /* Physical Layer 16.0 GT/s */ #define PCI_EXT_CAP_ID_PL_32GT 0x2A /* Physical Layer 32.0 GT/s */ #define PCI_EXT_CAP_ID_DOE 0x2E /* Data Object Exchange */ -#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_DOE +#define PCI_EXT_CAP_ID_IDE 0x30 /* Integrity and Data Encryption */ +#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_IDE #define PCI_EXT_CAP_DSN_SIZEOF 12 #define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40