Skip to contentSkip to conversation

coco/TSM: Host-side Arm CCA IDE setup via connect/disconnect callbacks

PATCH In review Aneesh Kumar K.V (Arm) 2 participants 19 messages Sep 10 – Sep 26

Subsystems (10)
PCI Crypto API arm64 KVM arm64 +6 more subsystems
lore
Share & tools
lore

Aneesh Kumar K.V (Arm)

author maintainer
#0 lore raw

v5 cover coco/TSM: Host-side Arm CCA IDE setup via connect/disconnect callbacks

This patch series implements the TSM ->connect() and ->disconnect() callbacks required for the Arm CCA IDE setup as per the RMM 2.0bet3 specification [1].

This patchset includes the host-side flow needed by connect/disconnect, including:
- DA feature detection helpers
- host TSM callback wiring and IDE stream allocation support
- creation/registration of RMM pdev descriptors
- RMM pdev communication helpers
- pdev stop and teardown helpers for disconnect
- pdev instantiation from the connect path
- public key registration with RMM

To support public-key handling from the device certificate chain, the series also includes the required X.509 parser updates.

Testing / Usage

To initiate the IDE setup:
echo tsm0 > /sys/bus/pci/devices/$DEVICE/tsm/connect

To disconnect:
echo tsm0 > /sys/bus/pci/devices/$DEVICE/tsm/disconnect

The series is based on:
- https://lore.kernel.org/all/[email protected]
- https://lore.kernel.org/all/[email protected]

Changes from v4:
https://lore.kernel.org/all/[email protected]
* Update rmi_set_pub_key() based on the latest specification.
* Rename structure members to match the specification.
* Rebase on top of the latest dependent series.

Changes from v3:
https://lore.kernel.org/all/[email protected]
* updated the patches to follow the RMM 2.0bet1 specification
* reworked the host-side pdev lifecycle to better match the RMM 2.0bet1 flow, including common pdev state, root-port pdev support, and non-coherent stream setup and teardown
* split PF0 setup into identity collection and conditional public-key installation, and gate DA enablement on RMI_FEATURE_REGISTER_2_DA
* added coordinated handling for RMI_DEV_COMM_EXIT_STREAM_WAIT, along with stream connect/disconnect and stream key refresh/purge support during vdev teardown

Changes from v2:
rfc-v2 https://lore.kernel.org/all/[email protected]
* rebase to latest kernel and core TSM changes
* Address review feedback.

v1:
rfc-v1 https://lore.kernel.org/all/[email protected]

[1] https://developer.arm.com/documentation/den0137/2-0bet3/
[2] https://lore.kernel.org/all/[email protected]
[3] https://lore.kernel.org/all/[email protected]
[4] https://gitlab.arm.com/linux-arm/linux-cca.git cca/topics/cca-tdisp-upstream-rfc-v4

Cc Alexey Kardashevskiy <[email protected]>
Cc Catalin Marinas <[email protected]>
Cc Dan Williams <[email protected]>
Cc Jason Gunthorpe <[email protected]>
Cc Jonathan Cameron <[email protected]>
Cc Marc Zyngier <[email protected]>
Cc Samuel Ortiz <[email protected]>
Cc Steven Price <[email protected]>
Cc Suzuki K Poulose <[email protected]>
Cc Will Deacon <[email protected]>
Cc Xu Yilun <[email protected]>

Aneesh Kumar K.V (Arm) (12):
coco: host: arm64: Prepare host TSM plumbing for IDE streams
coco: host: arm64: Create RMM pdev objects for PCI endpoints
coco: host: arm64: Add RMM pdev communication plumbing
coco: host: arm64: Add RMM pdev stop and destroy helper
coco: host: arm64: Register device public key with RMM
coco: host: arm64: Initialize RMM pdev state for TDISP IDE connect
coco: host: arm64: Coordinate peer stream waits during pdev
communication
coco: host: arm64: Connect RMM pdev streams for IDE devices
coco: host: arm64: Refcount root-port pdevs used by IDE streams PCI/TSM: Move CMA DOE mailbox discovery out of
pci_tsm_pf0_constructor()
coco: host: arm64: Add NCOH_SYS stream support for RC endpoints
coco: host: arm64: Enable PCI TSM connect callbacks

Lukas Wunner (3):
X.509: Make certificate parser public
X.509: Parse Subject Alternative Name in certificates X.509: Move certificate length retrieval into new helper

 crypto/asymmetric_keys/x509_cert_parser.c |   9 +
 crypto/asymmetric_keys/x509_loader.c      |  38 +-
 crypto/asymmetric_keys/x509_parser.h      |  42 +-
 drivers/crypto/ccp/sev-dev-tsm.c          |  13 +
 drivers/firmware/arm_rmm/rmi.c            |   4 +-
 drivers/firmware/smccc/smccc.c            |   6 +
 drivers/pci/tsm.c                         |  13 +-
 drivers/virt/coco/Kconfig                 |   2 +
 drivers/virt/coco/Makefile                |   1 +
 drivers/virt/coco/arm-cca-host/Kconfig    |  26 +
 drivers/virt/coco/arm-cca-host/Makefile   |   5 +
 drivers/virt/coco/arm-cca-host/main.c     | 461 ++++++++++++
 drivers/virt/coco/arm-cca-host/rmi-da.c   | 859 ++++++++++++++++++++++
 drivers/virt/coco/arm-cca-host/rmi-da.h   | 217 ++++++
 include/keys/asymmetric-type.h            |   2 +
 include/keys/x509-parser.h                |  57 ++
 include/linux/arm-rmi-cmds.h              |  85 +++
 include/linux/arm-smccc-rmi.h             | 161 ++++
 18 files changed, 1940 insertions(+), 61 deletions(-)
 create mode 100644 drivers/virt/coco/arm-cca-host/Kconfig
 create mode 100644 drivers/virt/coco/arm-cca-host/Makefile
 create mode 100644 drivers/virt/coco/arm-cca-host/main.c
 create mode 100644 drivers/virt/coco/arm-cca-host/rmi-da.c
 create mode 100644 drivers/virt/coco/arm-cca-host/rmi-da.h
 create mode 100644 include/keys/x509-parser.h
Signature · 1 line
2.43.0
author maintainer
#1 lore raw

PATCH v5 1/15 coco: host: arm64: Prepare host TSM plumbing for IDE streams

Add the initial Arm CCA host driver plumbing needed for device assignment support. Register the RMI SMCCC device, add the host TSM driver, and hook up the PCI TSM probe/remove callbacks when RMM reports Device Assignment support.

This patch only establishes the driver and per-device PCI TSM state needed by later patches. The RMM-facing pdev lifecycle, device communication, key setup, peer stream coordination, IDE stream setup, and connect/disconnect callbacks are added later in the series.

This keeps the patch as preparatory groundwork; complete enablement happens once the follow-up patches wire these pieces into the RMM stream lifecycle.

---
 drivers/firmware/arm_rmm/rmi.c          |   4 +-
 drivers/firmware/smccc/smccc.c          |   6 +
 drivers/virt/coco/Kconfig               |   2 +
 drivers/virt/coco/Makefile              |   1 +
 drivers/virt/coco/arm-cca-host/Kconfig  |  19 +++
 drivers/virt/coco/arm-cca-host/Makefile |   5 +
 drivers/virt/coco/arm-cca-host/main.c   | 192 ++++++++++++++++++++++++
 drivers/virt/coco/arm-cca-host/rmi-da.h |  46 ++++++
 8 files changed, 273 insertions(+), 2 deletions(-)
 create mode 100644 drivers/virt/coco/arm-cca-host/Kconfig
 create mode 100644 drivers/virt/coco/arm-cca-host/Makefile
 create mode 100644 drivers/virt/coco/arm-cca-host/main.c
 create mode 100644 drivers/virt/coco/arm-cca-host/rmi-da.h
drivers/firmware/arm_rmm/rmi.c +2 -2
index 34058e34188d..ba415b66991b 100644
@@ -15,8 +15,8 @@
 
 static bool arm64_rmi_is_available;
 
-/* Currently only the first 2 registers are used by Linux */
-#define RMI_FEAT_REG_COUNT	2
+/* Currently only the first 3 registers are used by Linux */
+#define RMI_FEAT_REG_COUNT	3
 static __ro_after_init unsigned long rmi_feat_reg_cache[RMI_FEAT_REG_COUNT];
 
 unsigned long rmi_feat_reg(unsigned long id)
drivers/firmware/smccc/smccc.c +6 -0
index 854276a3cd57..90b87a622a6b 100644
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/arm-smccc-bus.h>
 #include <linux/arm-smccc-rsi.h>
+#include <linux/arm-smccc-rmi.h>
 
 #include <asm/archrandom.h>
 
@@ -100,6 +101,11 @@ static const struct smccc_device_info smccc_devices[] __initconst = {
 		.requires_smc   = true,
 		.device_name    = "arm-rsi",
 	},
+	{
+		.func_id        = SMC_RMI_VERSION,
+		.requires_smc   = true,
+		.device_name    = "arm-rmi",
+	},
 };
 
 static bool __init smccc_probe_smccc_device(const struct smccc_device_info *smccc_dev)
drivers/virt/coco/Kconfig +2 -0
index f7691f64fbe3..1cbc2134f9ea 100644
@@ -19,5 +19,7 @@ endif
 
 source "drivers/virt/coco/tdx-host/Kconfig"
 
+source "drivers/virt/coco/arm-cca-host/Kconfig"
+
 config TSM
 	bool
drivers/virt/coco/Makefile +1 -0
index b323b0ae4f82..f2310c34daf9 100644
@@ -10,3 +10,4 @@ obj-$(CONFIG_INTEL_TDX_HOST)	+= tdx-host/
 obj-$(CONFIG_ARM_CCA_GUEST)	+= arm-cca-guest/
 obj-$(CONFIG_TSM) 		+= tsm-core.o
 obj-$(CONFIG_TSM_GUEST)		+= guest/
+obj-$(CONFIG_ARM_CCA_HOST)	+= arm-cca-host/
drivers/virt/coco/arm-cca-host/Kconfig new file +19 -0
new file mode 100644
index 000000000000..d2bf44f9776a
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# TSM (TEE Security Manager) host drivers
+#
+config ARM_CCA_HOST
+	tristate "Arm CCA Host driver"
+	depends on ARM64
+	depends on PCI
+	depends on ARM_RMM_RMI
+	depends on HAVE_ARM_SMCCC_DISCOVERY
+	select PCI_TSM
+
+	help
+	  ARM CCA RMM firmware is the trusted runtime that enforces memory
+	  isolation and security for confidential computing on ARM. This driver
+	  provides the interface for communicating with RMM to support secure
+	  device assignment.
+
+	  If you choose 'M' here, this module will be called arm-cca-host.
drivers/virt/coco/arm-cca-host/Makefile new file +5 -0
new file mode 100644
index 000000000000..7a4c2e0e5d26
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+obj-$(CONFIG_ARM_CCA_HOST) += arm-cca-host.o
+
+arm-cca-host-y	+=  main.o
drivers/virt/coco/arm-cca-host/main.c new file +192 -0

192 lines not on this page · raw message

drivers/virt/coco/arm-cca-host/rmi-da.h new file +46 -0

46 lines not on this page · raw message

Signature · 1 line
2.43.0
author maintainer
#2 lore raw

PATCH v5 2/15 coco: host: arm64: Create RMM pdev objects for PCI endpoints

In reply to Aneesh Kumar K.V (Arm)

Add the RMI definitions needed for pdev management, including the pdev state enum, parameter layout, and helpers for RMI_PDEV_CREATE and RMI_PDEV_GET_STATE.

Introduce a host-side pdev descriptor and cca_pdev_create() to allocate and delegate the backing granule, populate the pdev parameters from the PCI endpoint, and issue RMI_PDEV_CREATE to the RMM.

The new helper stores the created RMM pdev handle in the PF0 endpoint descriptor preparing the device for later IDE/TDISP setup.

---
 drivers/virt/coco/arm-cca-host/Makefile |   2 +-
 drivers/virt/coco/arm-cca-host/rmi-da.c | 146 ++++++++++++++++++++++++
 drivers/virt/coco/arm-cca-host/rmi-da.h |  26 +++++
 include/linux/arm-rmi-cmds.h            |  11 ++
 include/linux/arm-smccc-rmi.h           |  43 +++++++
 5 files changed, 227 insertions(+), 1 deletion(-)
 create mode 100644 drivers/virt/coco/arm-cca-host/rmi-da.c
drivers/virt/coco/arm-cca-host/Makefile +1 -1
index 7a4c2e0e5d26..cdbe85d072ae 100644
@@ -2,4 +2,4 @@
 #
 obj-$(CONFIG_ARM_CCA_HOST) += arm-cca-host.o
 
-arm-cca-host-y	+=  main.o
+arm-cca-host-y	+=  main.o rmi-da.o
drivers/virt/coco/arm-cca-host/rmi-da.c new file +146 -0

146 lines not on this page · raw message

drivers/virt/coco/arm-cca-host/rmi-da.h +26 -0
index c5a568cb5674..f1580308777b 100644
@@ -12,13 +12,26 @@
 #include <linux/pci-ide.h>
 #include <linux/pci-tsm.h>
 
+/**
+ * struct cca_host_pdev_dsc - Common RMM pdev context
+ * @rmm_pdev: Delegated page backing the RMM pdev object
+ * @object_lock: Serializes access to the RMM pdev object and PF0/TDI caches
+ */
+struct cca_host_pdev_dsc {
+	void *rmm_pdev;
+	/* lock kept here to simplify the generic lock/unlock paths. */
+	struct mutex object_lock;
+};
+
 /**
  * struct cca_host_pf0_ep_dsc - PF0 endpoint device security context.
  * @pci: Physical Function 0 TDISP link context
+ * @pdev: pdev communication context
  * @sel_stream: Selective IDE Stream descriptor
  */
 struct cca_host_pf0_ep_dsc {
 	struct pci_tsm_pf0 pci;
+	struct cca_host_pdev_dsc pdev;
 	struct pci_ide *sel_stream;
 };
 
@@ -43,4 +56,17 @@ static inline struct cca_host_fn_dsc *to_cca_fn_dsc(struct pci_dev *pdev)
 	return container_of(tsm, struct cca_host_fn_dsc, pci);
 }
 
+static inline struct cca_host_pdev_dsc *to_cca_pdev_dsc(struct pci_dev *pdev)
+{
+	struct cca_host_pf0_ep_dsc *pf0_ep_dsc;
+
+	pf0_ep_dsc = to_cca_pf0_ep_dsc(pdev);
+	if (pf0_ep_dsc)
+		return &pf0_ep_dsc->pdev;
+
+	return NULL;
+}
+
+int cca_pdev_create(struct pci_dev *pdev);
+
 #endif
include/linux/arm-rmi-cmds.h +11 -0
index 746257d77dd6..850ac8e230e2 100644
@@ -8,6 +8,7 @@
 
 #include <linux/arm-smccc-rmi.h>
 #include <linux/bug.h>
+#include <linux/gfp_types.h>
 #include <linux/types.h>
 
 #define RMI_MAX_ADDR_LIST	256
@@ -675,4 +676,14 @@ static inline long rmi_rtt_unprot_unmap(unsigned long rd,
 	return ret;
 }
 
+static inline unsigned long rmi_pdev_get_state(unsigned long pdev_phys, enum rmi_pdev_state *state)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RMI_PDEV_GET_STATE, pdev_phys, &res);
+
+	*state = res.a1;
+	return res.a0;
+}
+
 #endif
include/linux/arm-smccc-rmi.h +43 -0

47 lines not on this page · raw message

Signature · 1 line
2.43.0
author maintainer
#3 lore raw

PATCH v5 3/15 coco: host: arm64: Add RMM pdev communication plumbing

In reply to Aneesh Kumar K.V (Arm)

RMM pdev state transitions and SPDM exchanges are driven through RMI_PDEV_COMMUNICATE after the pdev object has been created. Add the host-side communication infrastructure needed to service those requests.

Define the RMI device communication ABI, add the PDEV_COMMUNICATE and PDEV_ABORT SMCCC wrappers, and allocate the per-PF0 communication pages and ordered workqueue. Add helpers that submit communication requests to RMM, perform the requested DOE SPDM/Secure SPDM transfers, cache multi-part device data such as VCA and certificate-chain objects, and wait for target RMM pdev states under the pdev object lock.

This only adds the shared communication machinery. Later patches use it for public-key registration, pdev initialization, stream setup, and the final connect/disconnect callbacks.

---
 drivers/virt/coco/arm-cca-host/main.c   |  50 +++++
 drivers/virt/coco/arm-cca-host/rmi-da.c | 276 ++++++++++++++++++++++++
 drivers/virt/coco/arm-cca-host/rmi-da.h |  65 ++++++
 include/linux/arm-rmi-cmds.h            |  20 ++
 include/linux/arm-smccc-rmi.h           |  62 ++++++
 5 files changed, 473 insertions(+)
drivers/virt/coco/arm-cca-host/main.c +50 -0
index df2cc761f68b..ce16ce108db7 100644
@@ -44,6 +44,7 @@ static struct pci_tsm *cca_tsm_pci_probe(struct tsm_dev *tsm_dev, struct pci_dev
 	ret = pci_tsm_pf0_constructor(pdev, &pf0_ep_dsc->pci, tsm_dev);
 	if (ret)
 		return NULL;
+	mutex_init(&pf0_ep_dsc->pdev.object_lock);
 
 	pci_dbg(pdev, "tsm enabled\n");
 	return &no_free_ptr(pf0_ep_dsc)->pci.base_tsm;
@@ -63,6 +64,55 @@ static void cca_tsm_pci_remove(struct pci_tsm *tsm)
 	}
 }
 
+static __maybe_unused int init_dev_communication_buffers(struct pci_dev *pdev,
+		struct cca_host_comm_data *comm_data)
+{
+	int ret = -ENOMEM;
+
+	comm_data->io_params = (struct rmi_dev_comm_data *)get_zeroed_page(GFP_KERNEL);
+	if (!comm_data->io_params)
+		goto err_out;
+
+	comm_data->rsp_buff = (void *)__get_free_page(GFP_KERNEL);
+	if (!comm_data->rsp_buff)
+		goto err_res_buff;
+
+	comm_data->req_buff = (void *)__get_free_page(GFP_KERNEL);
+	if (!comm_data->req_buff)
+		goto err_req_buff;
+
+	comm_data->work_queue = alloc_ordered_workqueue("%s %s DEV_COMM", 0,
+						dev_bus_name(&pdev->dev),
+						pci_name(pdev));
+	if (!comm_data->work_queue)
+		goto err_work_queue;
+
+	comm_data->io_params->enter.status = RMI_DEV_COMM_NONE;
+	comm_data->io_params->enter.resp_addr = virt_to_phys(comm_data->rsp_buff);
+	comm_data->io_params->enter.req_addr  = virt_to_phys(comm_data->req_buff);
+	comm_data->io_params->enter.resp_len = 0;
+
+	return 0;
+
+err_work_queue:
+	free_page((unsigned long)comm_data->req_buff);
+err_req_buff:
+	free_page((unsigned long)comm_data->rsp_buff);
+err_res_buff:
+	free_page((unsigned long)comm_data->io_params);
+err_out:
+	return ret;
+}
+
+static inline void free_dev_communication_buffers(struct cca_host_comm_data *comm_data)
+{
+	destroy_workqueue(comm_data->work_queue);
+
+	free_page((unsigned long)comm_data->req_buff);
+	free_page((unsigned long)comm_data->rsp_buff);
+	free_page((unsigned long)comm_data->io_params);
+}
+
 static inline bool cca_pdev_need_sel_ide_streams(struct pci_dev *pdev)
 {
 	return pci_pcie_type(pdev) == PCI_EXP_TYPE_ENDPOINT;
drivers/virt/coco/arm-cca-host/rmi-da.c +276 -0

285 lines not on this page · raw message

drivers/virt/coco/arm-cca-host/rmi-da.h +65 -0

101 lines not on this page · raw message

include/linux/arm-rmi-cmds.h +20 -0
index 850ac8e230e2..c2fc80dd19cc 100644
@@ -686,4 +686,24 @@ static inline unsigned long rmi_pdev_get_state(unsigned long pdev_phys, enum rmi
 	return res.a0;
 }
 
+static inline unsigned long rmi_pdev_communicate(unsigned long pdev_phys,
+						 unsigned long pdev_comm_data_phys)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RMI_PDEV_COMMUNICATE,
+			     pdev_phys, pdev_comm_data_phys, &res);
+
+	return res.a0;
+}
+
+static inline unsigned long rmi_pdev_abort(unsigned long pdev_phys)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RMI_PDEV_ABORT, pdev_phys, &res);
+
+	return res.a0;
+}
+
 #endif
include/linux/arm-smccc-rmi.h +62 -0

66 lines not on this page · raw message

Signature · 1 line
2.43.0
author maintainer
#4 lore raw

PATCH v5 4/15 coco: host: arm64: Add RMM pdev stop and destroy helper

In reply to Aneesh Kumar K.V (Arm)

Add the host-side helper needed to tear down an RMM pdev object once later disconnect and error paths are wired up.

Describe RMI_PDEV_STOP and RMI_PDEV_DESTROY, add the PDEV_STOP SMCCC wrapper, and issue PDEV_DESTROY through the SRO helper. The new teardown helper stops the pdev, drives device communication until RMM reports RMI_PDEV_STOPPED, destroys the RMM object, releases cached PF0 device-communication state, and undelegates the backing granule.

No caller is added here; this prepares the teardown side of the RMM pdev lifecycle for the follow-up patches that connect it to IDE stream disconnect.

---
 drivers/virt/coco/arm-cca-host/rmi-da.c | 47 ++++++++++++++++++++++++-
 drivers/virt/coco/arm-cca-host/rmi-da.h |  1 +
 include/linux/arm-rmi-cmds.h            |  9 +++++
 3 files changed, 56 insertions(+), 1 deletion(-)
drivers/virt/coco/arm-cca-host/rmi-da.c +46 -1
index 495a3bd2798f..7a72e8eead90 100644
@@ -394,7 +394,7 @@ static void pdev_state_transition_workfn(struct work_struct *work)
 	WARN_ON(state != setup_work->target_state);
 }
 
-static int __maybe_unused submit_pdev_state_transition_work(struct pci_dev *pdev,
+static int submit_pdev_state_transition_work(struct pci_dev *pdev,
 		enum rmi_pdev_state target_state)
 {
 	enum rmi_pdev_state state;
@@ -420,3 +420,48 @@ static int __maybe_unused submit_pdev_state_transition_work(struct pci_dev *pdev
 		return -1;
 	return 0;
 }
+
+static inline int rmi_pdev_destroy(unsigned long pdev_phys,
+		unsigned long *rmi_ret)
+{
+	struct rmi_sro_state *sro __free(kfree) = kmalloc_obj(*sro);
+	if (!sro)
+		return -ENOMEM;
+
+	*rmi_ret = rmi_sro_memxfer_cmd(sro, GFP_KERNEL,
+				       SMC_RMI_PDEV_DESTROY, pdev_phys);
+
+	return 0;
+}
+
+void cca_pdev_stop_and_destroy(struct pci_dev *pdev)
+{
+	int ret;
+	unsigned long rmi_ret;
+	struct cca_host_pdev_dsc *pdev_dsc = to_cca_pdev_dsc(pdev);
+	struct cca_host_pf0_ep_dsc *pf0_ep_dsc = to_cca_pf0_ep_dsc(pdev);
+	phys_addr_t rmm_pdev_phys = virt_to_phys(pdev_dsc->rmm_pdev);
+
+	if (WARN_ON(rmi_pdev_stop(rmm_pdev_phys)))
+		return;
+
+	ret = submit_pdev_state_transition_work(pdev, RMI_PDEV_STOPPED);
+	if (ret)
+		return;
+
+	ret = rmi_pdev_destroy(rmm_pdev_phys, &rmi_ret);
+	if (WARN_ON(ret || rmi_ret))
+		return;
+
+	if (pf0_ep_dsc) {
+		kfree(pf0_ep_dsc->cert_chain.public_key);
+		kvfree(pf0_ep_dsc->cert_chain.cache);
+		kvfree(pf0_ep_dsc->vca);
+		pf0_ep_dsc->cert_chain.cache = NULL;
+		pf0_ep_dsc->vca = NULL;
+	}
+
+	if (!rmi_undelegate_page(rmm_pdev_phys))
+		free_page((unsigned long)pdev_dsc->rmm_pdev);
+	pdev_dsc->rmm_pdev = NULL;
+}
drivers/virt/coco/arm-cca-host/rmi-da.h +1 -0
index 07034b034b53..f3bd106c8a54 100644
@@ -133,5 +133,6 @@ static inline struct cca_host_comm_data *to_cca_comm_data(struct pci_dev *pdev)
 }
 
 int cca_pdev_create(struct pci_dev *pdev);
+void cca_pdev_stop_and_destroy(struct pci_dev *pdev);
 
 #endif
include/linux/arm-rmi-cmds.h +9 -0
index c2fc80dd19cc..f947b2f4909a 100644
@@ -706,4 +706,13 @@ static inline unsigned long rmi_pdev_abort(unsigned long pdev_phys)
 	return res.a0;
 }
 
+static inline unsigned long rmi_pdev_stop(unsigned long pdev_phys)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RMI_PDEV_STOP, pdev_phys, &res);
+
+	return res.a0;
+}
+
 #endif
Signature · 1 line
2.43.0
author maintainer
#5 lore raw

PATCH v5 5/15 X.509: Make certificate parser public

In reply to Aneesh Kumar K.V (Arm)

From: Lukas Wunner <[email protected]>

The upcoming support for PCI device authentication with CMA-SPDM (PCIe r6.1 sec 6.31) requires validating the Subject Alternative Name in X.509 certificates.

High-level functions for X.509 parsing such as key_create_or_update() throw away the internal, low-level struct x509_certificate after extracting the struct public_key and public_key_signature from it. The Subject Alternative Name is thus inaccessible when using those functions.

Afford CMA-SPDM access to the Subject Alternative Name by making struct x509_certificate public, together with the functions for parsing an X.509 certificate into such a struct and freeing such a struct.

The private header file x509_parser.h previously included <linux/time.h> for the definition of time64_t. That definition was since moved to <linux/time64.h> by commit 361a3bf00582 ("time64: Add time64.h header and define struct timespec64"), so adjust the #include directive as part of the move to the new public header file <keys/x509-parser.h>.

No functional change intended.

---
 crypto/asymmetric_keys/x509_parser.h | 42 +--------------------
 include/keys/x509-parser.h           | 55 ++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+), 41 deletions(-)
 create mode 100644 include/keys/x509-parser.h
crypto/asymmetric_keys/x509_parser.h +1 -41
index b7aeebdddb36..39f1521b773d 100644
@@ -5,51 +5,11 @@
  * Written by David Howells ([email protected])
  */
 
-#include <linux/cleanup.h>
-#include <linux/time.h>
-#include <crypto/public_key.h>
-#include <keys/asymmetric-type.h>
-#include <crypto/sha2.h>
-
-struct x509_certificate {
-	struct x509_certificate *next;
-	struct x509_certificate *signer;	/* Certificate that signed this one */
-	struct public_key *pub;			/* Public key details */
-	struct public_key_signature *sig;	/* Signature parameters */
-	u8		sha256[SHA256_DIGEST_SIZE]; /* Hash for blacklist purposes */
-	char		*issuer;		/* Name of certificate issuer */
-	char		*subject;		/* Name of certificate subject */
-	struct asymmetric_key_id *id;		/* Issuer + Serial number */
-	struct asymmetric_key_id *skid;		/* Subject + subjectKeyId (optional) */
-	time64_t	valid_from;
-	time64_t	valid_to;
-	const void	*tbs;			/* Signed data */
-	unsigned	tbs_size;		/* Size of signed data */
-	unsigned	raw_sig_size;		/* Size of signature */
-	const void	*raw_sig;		/* Signature data */
-	const void	*raw_serial;		/* Raw serial number in ASN.1 */
-	unsigned	raw_serial_size;
-	unsigned	raw_issuer_size;
-	const void	*raw_issuer;		/* Raw issuer name in ASN.1 */
-	const void	*raw_subject;		/* Raw subject name in ASN.1 */
-	unsigned	raw_subject_size;
-	unsigned	raw_skid_size;
-	const void	*raw_skid;		/* Raw subjectKeyId in ASN.1 */
-	unsigned	index;
-	bool		seen;			/* Infinite recursion prevention */
-	bool		verified;
-	bool		self_signed;		/* T if self-signed (check unsupported_sig too) */
-	bool		unsupported_sig;	/* T if signature uses unsupported crypto */
-	bool		blacklisted;
-};
+#include <keys/x509-parser.h>
 
 /*
  * x509_cert_parser.c
  */
-extern void x509_free_certificate(struct x509_certificate *cert);
-DEFINE_FREE(x509_free_certificate, struct x509_certificate *,
-	    if (!IS_ERR(_T)) x509_free_certificate(_T))
-extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
 extern int x509_decode_time(time64_t *_t,  size_t hdrlen,
 			    unsigned char tag,
 			    const unsigned char *value, size_t vlen);
include/keys/x509-parser.h new file +55 -0

55 lines not on this page · raw message

Signature · 1 line
2.43.0
author maintainer
#6 lore raw

PATCH v5 6/15 X.509: Parse Subject Alternative Name in certificates

In reply to Aneesh Kumar K.V (Arm)

From: Lukas Wunner <[email protected]>

The upcoming support for PCI device authentication with CMA-SPDM (PCIe r6.1 sec 6.31) requires validating the Subject Alternative Name in X.509 certificates.

Store a pointer to the Subject Alternative Name upon parsing for consumption by CMA-SPDM.

---
 crypto/asymmetric_keys/x509_cert_parser.c | 9 +++++++++
 include/keys/x509-parser.h                | 2 ++
 2 files changed, 11 insertions(+)
crypto/asymmetric_keys/x509_cert_parser.c +9 -0
index bfd10f0195e0..c3ec2846695a 100644
@@ -596,6 +596,15 @@ int x509_process_extension(void *context, size_t hdrlen,
 		return 0;
 	}
 
+	if (ctx->last_oid == OID_subjectAltName) {
+		if (ctx->cert->raw_san)
+			return -EBADMSG;
+
+		ctx->cert->raw_san = v;
+		ctx->cert->raw_san_size = vlen;
+		return 0;
+	}
+
 	if (ctx->last_oid == OID_keyUsage) {
 		/*
 		 * Get hold of the keyUsage bit string
include/keys/x509-parser.h +2 -0
index 8b68e720693a..4e6a05a8c7a6 100644
@@ -38,6 +38,8 @@ struct x509_certificate {
 	unsigned	raw_subject_size;
 	unsigned	raw_skid_size;
 	const void	*raw_skid;		/* Raw subjectKeyId in ASN.1 */
+	const void	*raw_san;		/* Raw subjectAltName in ASN.1 */
+	unsigned	raw_san_size;
 	unsigned	index;
 	bool		seen;			/* Infinite recursion prevention */
 	bool		verified;
Signature · 1 line
2.43.0
author maintainer
#7 lore raw

PATCH v5 7/15 X.509: Move certificate length retrieval into new helper

In reply to Aneesh Kumar K.V (Arm)

From: Lukas Wunner <[email protected]>

The upcoming in-kernel SPDM library (Security Protocol and Data Model,
https://www.dmtf.org/dsp/DSP0274) needs to retrieve the length from ASN.1 DER-encoded X.509 certificates.

Such code already exists in x509_load_certificate_list(), so move it into a new helper for reuse by SPDM.

Export the helper so that SPDM can be tristate. (Some upcoming users of the SPDM libray may be modular, such as SCSI and ATA.)

No functional change intended.

---
 crypto/asymmetric_keys/x509_loader.c | 38 +++++++++++++++++++---------
 include/keys/asymmetric-type.h       |  2 ++
 2 files changed, 28 insertions(+), 12 deletions(-)
crypto/asymmetric_keys/x509_loader.c +26 -12
index 0d516c77cc26..174451b93eda 100644
@@ -4,28 +4,42 @@
 #include <linux/key.h>
 #include <keys/asymmetric-type.h>
 
+ssize_t x509_get_certificate_length(const u8 *p, unsigned long buflen)
+{
+	ssize_t plen;
+
+	/* Each cert begins with an ASN.1 SEQUENCE tag and must be more
+	 * than 256 bytes in size.
+	 */
+	if (buflen < 4)
+		return -EINVAL;
+
+	if (p[0] != 0x30 ||
+	    p[1] != 0x82)
+		return -EINVAL;
+
+	plen = (p[2] << 8) | p[3];
+	plen += 4;
+	if (plen > buflen)
+		return -EINVAL;
+
+	return plen;
+}
+EXPORT_SYMBOL_GPL(x509_get_certificate_length);
+
 int x509_load_certificate_list(const u8 cert_list[],
 			       const unsigned long list_size,
 			       const struct key *keyring)
 {
 	key_ref_t key;
 	const u8 *p, *end;
-	size_t plen;
+	ssize_t plen;
 
 	p = cert_list;
 	end = p + list_size;
 	while (p < end) {
-		/* Each cert begins with an ASN.1 SEQUENCE tag and must be more
-		 * than 256 bytes in size.
-		 */
-		if (end - p < 4)
-			goto dodgy_cert;
-		if (p[0] != 0x30 ||
-		    p[1] != 0x82)
-			goto dodgy_cert;
-		plen = (p[2] << 8) | p[3];
-		plen += 4;
-		if (plen > end - p)
+		plen = x509_get_certificate_length(p, end - p);
+		if (plen < 0)
 			goto dodgy_cert;
 
 		key = key_create_or_update(make_key_ref(keyring, 1),
include/keys/asymmetric-type.h +2 -0
index 1b91c8f98688..301efa952e26 100644
@@ -84,6 +84,8 @@ extern struct key *find_asymmetric_key(struct key *keyring,
 				       const struct asymmetric_key_id *id_2,
 				       bool partial);
 
+ssize_t x509_get_certificate_length(const u8 *p, unsigned long buflen);
+
 int x509_load_certificate_list(const u8 cert_list[], const unsigned long list_size,
 			       const struct key *keyring);
 
Signature · 1 line
2.43.0
author maintainer
#8 lore raw

PATCH v5 8/15 coco: host: arm64: Register device public key with RMM

In reply to Aneesh Kumar K.V (Arm)

- Introduce the SMC_RMI_PDEV_SET_PUBKEY helper and the associated struct rmi_public_key_params so the host can hand the device’s public key to the RMM.

- Parse the certificate chain cached during SPDM session setup, extract the final certificate’s public key, and recognise RSA-3072, ECDSA-P256, and ECDSA-P384 keys before calling into the RMM.

---
 drivers/virt/coco/arm-cca-host/Kconfig  |   7 ++
 drivers/virt/coco/arm-cca-host/rmi-da.c | 152 ++++++++++++++++++++++++
 drivers/virt/coco/arm-cca-host/rmi-da.h |   2 +
 include/linux/arm-rmi-cmds.h            |   9 ++
 include/linux/arm-smccc-rmi.h           |  18 +++
 5 files changed, 188 insertions(+)
drivers/virt/coco/arm-cca-host/Kconfig +7 -0
index d2bf44f9776a..43f0f0228e0c 100644
@@ -9,6 +9,13 @@ config ARM_CCA_HOST
 	depends on ARM_RMM_RMI
 	depends on HAVE_ARM_SMCCC_DISCOVERY
 	select PCI_TSM
+	select KEYS
+	select ASYMMETRIC_KEY_TYPE
+	select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+	select CRYPTO
+	select X509_CERTIFICATE_PARSER
+	select CRYPTO_ECDSA
+	select CRYPTO_RSA
 
 	help
 	  ARM CCA RMM firmware is the trusted runtime that enforces memory
drivers/virt/coco/arm-cca-host/rmi-da.c +152 -0

170 lines not on this page · raw message

drivers/virt/coco/arm-cca-host/rmi-da.h +2 -0
index f3bd106c8a54..92bcd9571cb8 100644
@@ -59,6 +59,7 @@ struct cca_host_pdev_dsc {
  * @pci: Physical Function 0 TDISP link context
  * @pdev: pdev communication context
  * @sel_stream: Selective IDE Stream descriptor
+ * @rmi_signature_algorithm: Signature algorithm used for public key
  * @cert_chain: cetrificate chain
  * @vca: SPDM's Version-Capabilities-Algorithms cache object
  */
@@ -67,6 +68,7 @@ struct cca_host_pf0_ep_dsc {
 	struct cca_host_pdev_dsc pdev;
 	struct pci_ide *sel_stream;
 
+	char *rmi_signature_algorithm;
 	struct {
 		struct cache_object *cache;
 
include/linux/arm-rmi-cmds.h +9 -0
index f947b2f4909a..1f1dc771a8ff 100644
@@ -715,4 +715,13 @@ static inline unsigned long rmi_pdev_stop(unsigned long pdev_phys)
 	return res.a0;
 }
 
+static inline unsigned long rmi_pdev_set_pubkey(unsigned long pdev_phys, unsigned long key_phys)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RMI_PDEV_SET_PUBKEY, pdev_phys, key_phys, &res);
+
+	return res.a0;
+}
+
 #endif
include/linux/arm-smccc-rmi.h +18 -0
index 3133b34354ce..fdc830379060 100644
@@ -596,4 +596,22 @@ struct rmi_dev_comm_data {
 	};
 };
 
+struct rmi_public_key_params {
+	union {
+		struct {
+			union { /* 0x0 */
+				u64 key_addr;
+				u8 padding0[0x400];
+			};
+			union { /* 0x400 */
+				u64 metadata_addr;
+				u8 padding1[0x400];
+			};
+			u64 key_len;
+			u64 metadata_len;
+		};
+		u8 padding[0x1000];
+	};
+};
+
 #endif /* __LINUX_ARM_SMCCC_RMI_H_ */
Signature · 1 line
2.43.0
author maintainer
#9 lore raw

PATCH v5 9/15 coco: host: arm64: Initialize RMM pdev state for TDISP IDE connect

In reply to Aneesh Kumar K.V (Arm)

Update connect() to:
- allocate device-communication buffers,
- create the RMM pdev object,
- perform initial device communication to collect identity, and
- set the device public key when the pdev enters NEEDS_KEY.

---
 drivers/virt/coco/arm-cca-host/main.c   | 47 ++++++++++---
 drivers/virt/coco/arm-cca-host/rmi-da.c | 90 ++++++++++++++++++++++++-
 drivers/virt/coco/arm-cca-host/rmi-da.h |  3 +
 3 files changed, 130 insertions(+), 10 deletions(-)
drivers/virt/coco/arm-cca-host/main.c +39 -8
index ce16ce108db7..318967bab8ca 100644
@@ -64,7 +64,7 @@ static void cca_tsm_pci_remove(struct pci_tsm *tsm)
 	}
 }
 
-static __maybe_unused int init_dev_communication_buffers(struct pci_dev *pdev,
+static int init_dev_communication_buffers(struct pci_dev *pdev,
 		struct cca_host_comm_data *comm_data)
 {
 	int ret = -ENOMEM;
@@ -158,15 +158,43 @@ static int __maybe_unused cca_tsm_connect(struct pci_dev *pdev)
 		ide->partner[PCI_IDE_EP].default_stream = 1;
 		pci_ide_stream_setup(pdev, ide);
 		pci_ide_stream_setup(rp, ide);
+	}
 
-		/*
-		 * Once ide is setup, enable the stream at the endpoint
-		 * Root port will be done by RMM
-		 */
-		pci_ide_stream_enable(pdev, ide);
+	ret = init_dev_communication_buffers(pdev, &pf0_ep_dsc->pdev.comm_data);
+	if (ret)
+		goto err_comm_buff;
+	ret = cca_pdev_create(pdev);
+	if (ret)
+		goto err_pdev_create;
+
+	ret = cca_pdev_collect_identity(pdev);
+	if (ret)
+		goto pdev_destroy;
+
+	if (cca_pdev_needs_key(pdev)) {
+		ret = cca_pdev_set_public_key(pdev);
+		if (ret)
+			goto pdev_destroy;
 	}
+	/*
+	 * Once ide is setup, enable the stream at the endpoint
+	 * Root port will be done by RMM
+	 */
+	if (cca_pdev_need_sel_ide_streams(pdev))
+		pci_ide_stream_enable(pdev, ide);
+
 	return 0;
 
+pdev_destroy:
+	cca_pdev_stop_and_destroy(pdev);
+err_pdev_create:
+	free_dev_communication_buffers(&pf0_ep_dsc->pdev.comm_data);
+err_comm_buff:
+	if (cca_pdev_need_sel_ide_streams(pdev)) {
+		pci_ide_stream_teardown(rp, ide);
+		pci_ide_stream_teardown(pdev, ide);
+		pci_ide_stream_unregister(ide);
+	}
 err_stream:
 	if (cca_pdev_need_sel_ide_streams(pdev))
 		pci_ide_stream_free(ide);
@@ -185,13 +213,16 @@ static void __maybe_unused cca_tsm_disconnect(struct pci_dev *pdev)
 	if (!pf0_ep_dsc)
 		return;
 
-	if (cca_pdev_need_sel_ide_streams(pdev)) {
+	if (cca_pdev_need_sel_ide_streams(pdev))
 		ide = pf0_ep_dsc->sel_stream;
 
+	cca_pdev_stop_and_destroy(pdev);
+	free_dev_communication_buffers(&pf0_ep_dsc->pdev.comm_data);
+
+	if (cca_pdev_need_sel_ide_streams(pdev)) {
 		pci_ide_stream_release(ide);
 		pf0_ep_dsc->sel_stream = NULL;
 	}
-
 }
 
 static struct pci_tsm_ops cca_link_pci_ops = {
drivers/virt/coco/arm-cca-host/rmi-da.c +88 -2

108 lines not on this page · raw message

drivers/virt/coco/arm-cca-host/rmi-da.h +3 -0
index 92bcd9571cb8..dc5dc4246591 100644
@@ -135,6 +135,9 @@ static inline struct cca_host_comm_data *to_cca_comm_data(struct pci_dev *pdev)
 }
 
 int cca_pdev_create(struct pci_dev *pdev);
+int cca_pdev_collect_identity(struct pci_dev *pdev);
+bool cca_pdev_needs_key(struct pci_dev *pdev);
+int cca_pdev_set_public_key(struct pci_dev *pdev);
 void cca_pdev_stop_and_destroy(struct pci_dev *pdev);
 
 #endif
Signature · 1 line
2.43.0
author maintainer
#10 lore raw

PATCH v5 10/15 coco: host: arm64: Coordinate peer stream waits during pdev communication

In reply to Aneesh Kumar K.V (Arm)

RMM stream operations can return RMI_DEV_COMM_EXIT_STREAM_WAIT while one side waits for the peer stream to reach the matching point in the protocol.

Teach arm-cca host device communication to detect STREAM_WAIT and add a helper that runs pdev communication for both sides in parallel until each side has made enough progress, then issue rmi_pdev_stream_complete().

This provides the synchronization needed for stream connect, disconnect, key refresh, and key purge operations.

---
 drivers/virt/coco/arm-cca-host/rmi-da.c | 116 +++++++++++++++++++++++-
 drivers/virt/coco/arm-cca-host/rmi-da.h |  13 +++
 include/linux/arm-smccc-rmi.h           |   1 +
 3 files changed, 125 insertions(+), 5 deletions(-)
drivers/virt/coco/arm-cca-host/rmi-da.c +111 -5
index a046edb6be86..784572116064 100644
@@ -192,7 +192,7 @@ static inline gfp_t cache_obj_id_to_gfp_flags(u8 cache_obj_id)
 	return GFP_KERNEL_ACCOUNT;
 }
 
-static int _do_dev_communicate(enum dev_comm_type type, struct pci_tsm *tsm)
+static int _do_dev_communicate(enum dev_comm_type type, struct pci_tsm *tsm, int *stream_wait)
 {
 	unsigned long rmi_ret;
 	gfp_t cache_alloc_flags;
@@ -324,11 +324,17 @@ static int _do_dev_communicate(enum dev_comm_type type, struct pci_tsm *tsm)
 	if (pending_dev_communicate(io_exit))
 		goto redo_communicate;
 
+	if (io_exit->flags & RMI_DEV_COMM_EXIT_STREAM_WAIT) {
+		if (stream_wait)
+			*stream_wait = 1;
+		else
+			WARN(1, "Unexpected Stream wait status\n");
+	}
 	return 0;
 }
 
 static int do_dev_communicate(enum dev_comm_type type,
-		struct pci_tsm *tsm, unsigned long error_state)
+		struct pci_tsm *tsm, unsigned long error_state, int *stream_wait)
 {
 	int ret, state = error_state;
 	struct rmi_dev_comm_enter *io_enter;
@@ -337,8 +343,10 @@ static int do_dev_communicate(enum dev_comm_type type,
 	io_enter = &pdev_dsc->comm_data.io_params->enter;
 	io_enter->resp_len = 0;
 	io_enter->status = RMI_DEV_COMM_NONE;
+	if (stream_wait)
+		*stream_wait = 0;
 
-	ret = _do_dev_communicate(type, tsm);
+	ret = _do_dev_communicate(type, tsm, stream_wait);
 	if (ret) {
 		if (type == PDEV_COMMUNICATE)
 			rmi_pdev_abort(virt_to_phys(pdev_dsc->rmm_pdev));
@@ -366,7 +374,7 @@ static int wait_for_dev_state(enum dev_comm_type type, struct pci_tsm *tsm,
 	int state;
 
 	do {
-		state = do_dev_communicate(type, tsm, error_state);
+		state = do_dev_communicate(type, tsm, error_state, NULL);
 
 		if (state == target_state || state == error_state)
 			return state;
@@ -584,7 +592,7 @@ static void pdev_collect_identity_workfn(struct work_struct *work)
 
 	guard(mutex)(&pdev_dsc->object_lock);
 
-	do_dev_communicate(PDEV_COMMUNICATE, tsm, RMI_PDEV_ERROR);
+	do_dev_communicate(PDEV_COMMUNICATE, tsm, RMI_PDEV_ERROR, NULL);
 
 	/*
 	 * Don't worry about communication error. The caller will look at
@@ -703,3 +711,101 @@ void cca_pdev_stop_and_destroy(struct pci_dev *pdev)
 		free_page((unsigned long)pdev_dsc->rmm_pdev);
 	pdev_dsc->rmm_pdev = NULL;
 }
+
+static void stream_connect_workfn(struct work_struct *work)
+{
+	int state;
+	int peer_wait = 0;
+	struct pci_tsm *tsm;
+	int my_index, peer_index, target;
+	struct stream_connect_work *stream_work;
+	struct cca_host_pdev_dsc *pdev_dsc;
+
+	stream_work = container_of(work, struct stream_connect_work, work);
+	tsm = stream_work->tsm;
+	pdev_dsc = to_cca_pdev_dsc(tsm->dsm_dev);
+
+	my_index = stream_work->my_index;
+	peer_index = my_index ^ 0x1;
+
+redo_communicate:
+	mutex_lock(&pdev_dsc->object_lock);
+
+	state = do_dev_communicate(PDEV_COMMUNICATE, tsm, RMI_PDEV_ERROR, &peer_wait);
+	if (state != RMI_PDEV_ERROR && peer_wait) {
+
+		if (!stream_work->has_peer) {
+			WARN(1, "Unexpected STREAM_WAIT without peer stream\n");
+			mutex_unlock(&pdev_dsc->object_lock);
+			return;
+		}
+		/*
+		 * Record a fresh target val for this side, then wait until
+		 * peer reaches at least the same target.
+		 */
+		target = atomic_inc_return(&stream_work->sync->val[my_index]);
+
+		wake_up_all(&stream_work->sync->wq);
+
+		mutex_unlock(&pdev_dsc->object_lock);
+
+		/* Wait for peer to make matching progress */
+		wait_event(stream_work->sync->wq,
+			   atomic_read(&stream_work->sync->val[peer_index]) >= target);
+		goto redo_communicate;
+	}
+
+	/* Signal peer if it is waiting on me */
+	atomic_inc_return(&stream_work->sync->val[my_index]);
+	wake_up_all(&stream_work->sync->wq);
+
+	mutex_unlock(&pdev_dsc->object_lock);
+}
+
+static int __maybe_unused submit_stream_work(struct pci_dev *pdev1, struct pci_dev *pdev2,
+		unsigned long stream_handle)
+{
+	phys_addr_t rmm_pdev1_phys, rmm_pdev2_phys = 0;
+	struct cca_host_comm_data *comm_data_pdev1, *comm_data_pdev2;
+	struct cca_host_pdev_dsc *pdev_dsc1, *pdev_dsc2 = NULL;
+	struct stream_sync sync;
+	struct stream_connect_work stream_work_pdev1, stream_work_pdev2;
+
+	comm_data_pdev1 = to_cca_comm_data(pdev1);
+	init_waitqueue_head(&sync.wq);
+	atomic_set(&sync.val[0], 0);
+	atomic_set(&sync.val[1], 0);
+
+	pdev_dsc1 = to_cca_pdev_dsc(pdev1);
+	INIT_WORK_ONSTACK(&stream_work_pdev1.work, stream_connect_workfn);
+	stream_work_pdev1.tsm = pdev1->tsm;
+	stream_work_pdev1.sync = &sync;
+	stream_work_pdev1.my_index = 0;
+	stream_work_pdev1.has_peer = !!pdev2;
+	queue_work(comm_data_pdev1->work_queue, &stream_work_pdev1.work);
+
+	if (pdev2) {
+		comm_data_pdev2 = to_cca_comm_data(pdev2);
+		pdev_dsc2 = to_cca_pdev_dsc(pdev2);
+		INIT_WORK_ONSTACK(&stream_work_pdev2.work, stream_connect_workfn);
+		stream_work_pdev2.tsm = pdev2->tsm;
+		stream_work_pdev2.sync = &sync;
+		stream_work_pdev2.my_index = 1;
+		stream_work_pdev2.has_peer = true;
+		queue_work(comm_data_pdev2->work_queue, &stream_work_pdev2.work);
+	}
+
+	flush_work(&stream_work_pdev1.work);
+	if (pdev2) {
+		flush_work(&stream_work_pdev2.work);
+		destroy_work_on_stack(&stream_work_pdev2.work);
+	}
+
+	destroy_work_on_stack(&stream_work_pdev1.work);
+
+	rmm_pdev1_phys = virt_to_phys(pdev_dsc1->rmm_pdev);
+	if (pdev2)
+		rmm_pdev2_phys = virt_to_phys(pdev_dsc2->rmm_pdev);
+
+	return 0;
+}
drivers/virt/coco/arm-cca-host/rmi-da.h +13 -0
index dc5dc4246591..7b8fe0de7b90 100644
@@ -27,6 +27,19 @@ struct dev_comm_work {
 	struct work_struct work;
 };
 
+struct stream_sync {
+	wait_queue_head_t wq;
+	atomic_t val[2];
+};
+
+struct stream_connect_work {
+	struct pci_tsm *tsm;
+	struct work_struct work;
+	struct stream_sync *sync;
+	u8 my_index;
+	bool has_peer;
+};
+
 struct cca_host_comm_data {
 	void *rsp_buff;
 	void *req_buff;
include/linux/arm-smccc-rmi.h +1 -0
index fdc830379060..1640f2f43344 100644
@@ -540,6 +540,7 @@ struct rmi_pdev_params {
 #define RMI_DEV_COMM_EXIT_WAIT		BIT(3)
 #define RMI_DEV_COMM_EXIT_RSP_RESET	BIT(4)
 #define RMI_DEV_COMM_EXIT_MULTI		BIT(5)
+#define RMI_DEV_COMM_EXIT_STREAM_WAIT	BIT(6)
 
 #define RMI_DEV_COMM_NONE	0
 #define RMI_DEV_COMM_RESPONSE	1
Signature · 1 line
2.43.0
author maintainer
#11 lore raw

PATCH v5 11/15 coco: host: arm64: Connect RMM pdev streams for IDE devices

In reply to Aneesh Kumar K.V (Arm)

Add the RMI definitions for pdev stream management, including the stream parameter layout and helpers for RMI_PDEV_STREAM_CONNECT, RMI_PDEV_STREAM_COMPLETE, and RMI_PDEV_STREAM_DISCONNECT.

Create an RMM pdev for the endpoint's root port when needed, build the non-coherent stream parameters from the endpoint/root-port pdevs, IDE stream ID, and bridge address windows, and issue the RMM stream connect before enabling IDE on the endpoint.

Store the returned stream handle in the PF0 descriptor

---
 drivers/virt/coco/arm-cca-host/main.c   | 124 ++++++++++++++++++++++++
 drivers/virt/coco/arm-cca-host/rmi-da.c |  40 +++++++-
 drivers/virt/coco/arm-cca-host/rmi-da.h |  57 +++++++++++
 include/linux/arm-rmi-cmds.h            |  36 +++++++
 include/linux/arm-smccc-rmi.h           |  37 +++++++
 5 files changed, 293 insertions(+), 1 deletion(-)
drivers/virt/coco/arm-cca-host/main.c +124 -0
index 318967bab8ca..d357bad14325 100644
@@ -113,6 +113,123 @@ static inline void free_dev_communication_buffers(struct cca_host_comm_data *com
 	free_page((unsigned long)comm_data->io_params);
 }
 
+static int cca_root_port_pdev_create(struct pci_dev *rp, struct tsm_dev *tsm_dev)
+{
+	int ret;
+	struct cca_host_rp_dsc *rp_dsc;
+
+	rp_dsc = kzalloc_obj(*rp_dsc);
+	if (!rp_dsc)
+		return -ENOMEM;
+
+	/* we expect this to be asigned early */
+	rp->tsm = &rp_dsc->pci;
+	rp->tsm->dsm_dev = rp;
+	rp->tsm->pdev = rp;
+	rp->tsm->tsm_dev = tsm_dev;
+	mutex_init(&rp_dsc->pdev.object_lock);
+
+	ret = init_dev_communication_buffers(rp, &rp_dsc->pdev.comm_data);
+	if (ret)
+		goto err_comm_buff;
+
+	ret = cca_pdev_create(rp);
+	if (ret)
+		goto err_pdev_create;
+
+	/*
+	 * device communication is still required even though
+	 * there is not identity collection
+	 */
+	ret = cca_pdev_collect_identity(rp);
+	if (ret)
+		goto pdev_destroy;
+
+	return 0;
+
+pdev_destroy:
+	cca_pdev_stop_and_destroy(rp);
+err_pdev_create:
+	free_dev_communication_buffers(&rp_dsc->pdev.comm_data);
+err_comm_buff:
+	kfree(rp_dsc);
+	rp->tsm = NULL;
+	return ret;
+}
+
+static int pci_dev_addr_range(struct pci_dev *pdev, struct rmi_addr_range *pdev_addr)
+{
+	int naddr = 0;
+	struct pci_dev *br;
+	struct resource *mem, *pref;
+
+	br = pci_upstream_bridge(pdev);
+	if (!br)
+		return 0;
+
+	mem = pci_resource_n(br, PCI_BRIDGE_MEM_WINDOW);
+	pref = pci_resource_n(br, PCI_BRIDGE_PREF_MEM_WINDOW);
+	if (resource_assigned(mem))
+		naddr = insert_addr_range_sorted(pdev_addr, naddr,
+						 mem->start, mem->end + 1);
+	if (resource_assigned(pref))
+		naddr = insert_addr_range_sorted(pdev_addr, naddr,
+						 pref->start, pref->end + 1);
+
+	return naddr;
+}
+
+static int cca_pdev_create_ncoh_stream(struct pci_dev *pdev, unsigned long stream_id)
+{
+	int ret;
+	long stream_handle;
+	struct cca_host_rp_dsc *rp_dsc;
+	struct rmi_pdev_stream_params *params;
+	struct pci_dev *rp = pcie_find_root_port(pdev);
+	struct cca_host_pf0_ep_dsc *pf0_ep_dsc = to_cca_pf0_ep_dsc(pdev);
+
+	if (!rp->tsm) {
+		ret = cca_root_port_pdev_create(rp, pf0_ep_dsc->pci.base_tsm.tsm_dev);
+		if (ret)
+			return ret;
+		rp_dsc = to_cca_rp_dsc(rp);
+	} else {
+		rp_dsc = to_cca_rp_dsc(rp);
+		/* Make sure they use the same TSM */
+		if (rp->tsm->tsm_dev != pf0_ep_dsc->pci.base_tsm.tsm_dev)
+			return -EINVAL;
+	}
+
+
+	params = (struct rmi_pdev_stream_params *)get_zeroed_page(GFP_KERNEL);
+	if (!params)
+		return -ENOMEM;
+
+	params->flags = 0;
+	params->stream_type = RMI_PDEV_STREAM_NCOH;
+	params->pdev_1 = virt_to_phys(pf0_ep_dsc->pdev.rmm_pdev);
+	params->pdev_2 = virt_to_phys(rp_dsc->pdev.rmm_pdev);
+	params->ide_sid = stream_id;
+	params->num_addr_range = pci_dev_addr_range(pdev, params->addr_range);
+
+	ret = cca_pdev_stream_connect(pdev, rp, params, &stream_handle);
+	if (!ret)
+		pf0_ep_dsc->stream_handle = stream_handle;
+
+	free_page((unsigned long)params);
+	return ret;
+}
+
+static int cca_pdev_create_streams(struct pci_dev *pdev, unsigned long stream_id)
+{
+	switch (pci_pcie_type(pdev)) {
+	case PCI_EXP_TYPE_ENDPOINT:
+		return cca_pdev_create_ncoh_stream(pdev, stream_id);
+	default:
+		return -EINVAL;
+	}
+}
+
 static inline bool cca_pdev_need_sel_ide_streams(struct pci_dev *pdev)
 {
 	return pci_pcie_type(pdev) == PCI_EXP_TYPE_ENDPOINT;
@@ -176,6 +293,10 @@ static int __maybe_unused cca_tsm_connect(struct pci_dev *pdev)
 		if (ret)
 			goto pdev_destroy;
 	}
+	/* Create IDE streams */
+	ret = cca_pdev_create_streams(pdev, stream_id);
+	if (ret)
+		goto pdev_destroy;
 	/*
 	 * Once ide is setup, enable the stream at the endpoint
 	 * Root port will be done by RMM
@@ -208,6 +329,7 @@ static void __maybe_unused cca_tsm_disconnect(struct pci_dev *pdev)
 {
 	struct pci_ide *ide;
 	struct cca_host_pf0_ep_dsc *pf0_ep_dsc;
+	struct pci_dev *rp = pcie_find_root_port(pdev);
 
 	pf0_ep_dsc = to_cca_pf0_ep_dsc(pdev);
 	if (!pf0_ep_dsc)
@@ -216,6 +338,8 @@ static void __maybe_unused cca_tsm_disconnect(struct pci_dev *pdev)
 	if (cca_pdev_need_sel_ide_streams(pdev))
 		ide = pf0_ep_dsc->sel_stream;
 
+	cca_pdev_disconnect_stream(pdev, rp, pf0_ep_dsc->stream_handle);
+
 	cca_pdev_stop_and_destroy(pdev);
 	free_dev_communication_buffers(&pf0_ep_dsc->pdev.comm_data);
 
drivers/virt/coco/arm-cca-host/rmi-da.c +39 -1
index 784572116064..fcca9f73b5fb 100644
@@ -62,6 +62,10 @@ static int init_pdev_params(struct pci_dev *pdev, struct rmi_pdev_params *params
 		category = RMI_PDEV_FLAGS_CATEGORY_OFF_CHIP_EP;
 		break;
 	}
+	case PCI_EXP_TYPE_ROOT_PORT: {
+		category = RMI_PDEV_FLAGS_CATEGORY_ROOT_PORT;
+		break;
+	}
 	default:
 		return -EINVAL;
 	}
@@ -762,7 +766,7 @@ static void stream_connect_workfn(struct work_struct *work)
 	mutex_unlock(&pdev_dsc->object_lock);
 }
 
-static int __maybe_unused submit_stream_work(struct pci_dev *pdev1, struct pci_dev *pdev2,
+static int submit_stream_work(struct pci_dev *pdev1, struct pci_dev *pdev2,
 		unsigned long stream_handle)
 {
 	phys_addr_t rmm_pdev1_phys, rmm_pdev2_phys = 0;
@@ -806,6 +810,40 @@ static int __maybe_unused submit_stream_work(struct pci_dev *pdev1, struct pci_d
 	rmm_pdev1_phys = virt_to_phys(pdev_dsc1->rmm_pdev);
 	if (pdev2)
 		rmm_pdev2_phys = virt_to_phys(pdev_dsc2->rmm_pdev);
+	/*
+	 * If we had device communication error, this will error out.
+	 */
+	if (rmi_pdev_stream_complete(rmm_pdev1_phys, rmm_pdev2_phys, stream_handle))
+		return -EIO;
 
 	return 0;
 }
+
+int cca_pdev_stream_connect(struct pci_dev *pdev1, struct pci_dev *pdev2,
+		struct rmi_pdev_stream_params *stream_params,
+		unsigned long *stream_handle)
+{
+	phys_addr_t stream_params_phys = virt_to_phys(stream_params);
+
+	if (rmi_pdev_stream_connect(stream_params_phys, stream_handle))
+		return -EIO;
+
+	return submit_stream_work(pdev1, pdev2, *stream_handle);
+}
+
+int cca_pdev_disconnect_stream(struct pci_dev *pdev1,
+		struct pci_dev *pdev2, unsigned long stream_handle)
+{
+
+	phys_addr_t rmm_pdev2_phys = 0;
+	struct cca_host_pdev_dsc *pdev_dsc1 = to_cca_pdev_dsc(pdev1);
+
+	if (pdev2)
+		rmm_pdev2_phys = virt_to_phys(to_cca_pdev_dsc(pdev2)->rmm_pdev);
+
+	if (rmi_pdev_stream_disconnect(virt_to_phys(pdev_dsc1->rmm_pdev),
+				       rmm_pdev2_phys, stream_handle))
+		return -EIO;
+
+	return submit_stream_work(pdev1, pdev2, stream_handle);
+}
drivers/virt/coco/arm-cca-host/rmi-da.h +57 -0

100 lines not on this page · raw message

include/linux/arm-rmi-cmds.h +36 -0
index 1f1dc771a8ff..8db92447dc61 100644
@@ -724,4 +724,40 @@ static inline unsigned long rmi_pdev_set_pubkey(unsigned long pdev_phys, unsigne
 	return res.a0;
 }
 
+static inline unsigned long rmi_pdev_stream_connect(unsigned long stream_params_phys,
+		unsigned long *stream_handle)
+{
+
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RMI_PDEV_STREAM_CONNECT, stream_params_phys, &res);
+
+	*stream_handle = res.a1;
+	return res.a0;
+}
+
+static inline unsigned long rmi_pdev_stream_complete(unsigned long pdev1_phys,
+		unsigned long pdev2_phys, unsigned long stream_handle)
+{
+
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RMI_PDEV_STREAM_COMPLETE, pdev1_phys,
+			     pdev2_phys, stream_handle, &res);
+
+	return res.a0;
+}
+
+static inline unsigned long rmi_pdev_stream_disconnect(unsigned long pdev1_phys,
+		unsigned long pdev2_phys, unsigned long stream_handle)
+{
+
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RMI_PDEV_STREAM_DISCONNECT, pdev1_phys,
+			     pdev2_phys, stream_handle, &res);
+
+	return res.a0;
+}
+
 #endif
include/linux/arm-smccc-rmi.h +37 -0
index 1640f2f43344..f9dd919b696a 100644
@@ -615,4 +615,41 @@ struct rmi_public_key_params {
 	};
 };
 
+#define MAX_STREAM_ADDR_RANGE	16
+
+enum rmi_pdev_stream_type {
+	RMI_PDEV_STREAM_NON_TEE,
+	RMI_PDEV_STREAM_NCOH,
+	RMI_PDEV_STREAM_COH,
+	RMI_PDEV_STREAM_NCOH_SYS,
+	RMI_PDEV_STREAM_COH_SYS,
+};
+
+struct rmi_addr_range {
+	u64 base; /* inclusive */
+	u64 top;  /* exclusive */
+};
+
+struct rmi_pdev_stream_params {
+	union {
+		struct {
+			u64 flags;
+			union {
+				u8 stream_type;
+				u8 padding0[8];
+			};
+			u64 pdev_1;
+			u64 pdev_2;
+			u64 ide_sid;
+			u64 num_addr_range;
+		};
+		u8 padding1[0x100];
+	};
+
+	union { /* 0x100 */
+		struct rmi_addr_range addr_range[MAX_STREAM_ADDR_RANGE];
+		u8 padding2[0xF00];
+	};
+};
+
 #endif /* __LINUX_ARM_SMCCC_RMI_H_ */
Signature · 1 line
2.43.0
#12 lore raw

Re: [PATCH v5 11/15] coco: host: arm64: Connect RMM pdev streams for IDE devices

Hi Aneesh

As I read it, the address ranges handed to RMM in this patch are captured once and at the point cca_tsm_connect() runs. That becomes the address range RMM has on record for the pdev/stream going forward.

So a couple of questions on that..
1. Is connect -> {driver probe / vfio-pci bind } the intended sequence or is that ordering left entirely to userspace/VMM policy?
2. Are the resources/BARs captured at connect time assumed to be fixed for the lifetime of the PDEV? If a PCI rescan (or resource reassignment such as pci=realloc, a hotplug re-enumeration happens after connect, is that expected to be rejected/blocked?

Thanks

author maintainer
#13 lore raw

Re: [PATCH v5 11/15] coco: host: arm64: Connect RMM pdev streams for IDE devices

Quoted 10 lines · Ankit Agrawal

Hi Aneesh

As I read it, the address ranges handed to RMM in this patch are captured once and at the point cca_tsm_connect() runs. That becomes the address range RMM has on record for the pdev/stream going forward.

So a couple of questions on that..
1. Is connect -> {driver probe / vfio-pci bind } the intended sequence or is that ordering left entirely to userspace/VMM policy?

We can tsm connect the device even while its driver is bound.

Quoted 4 lines

2. Are the resources/BARs captured at connect time assumed to be fixed for the lifetime of the PDEV? If a PCI rescan (or resource reassignment such as pci=realloc, a hotplug re-enumeration happens after connect, is that expected to be rejected/blocked?

IIUC, a normal rescan neither rereads nor relocates an already known and assigned BAR? To discover an externally changed BAR, the device would normally need to be removed before rescanning. Device removal reaches pci_destroy_dev(), which calls pci_tsm_destroy() -> pci_tsm_disconnect()

-aneesh

#14 lore raw

Re: [PATCH v5 11/15] coco: host: arm64: Connect RMM pdev streams for IDE devices

In reply to Aneesh Kumar K.V (Arm)

Hi Aneesh

It isn't clear to me why does pci_dev_addr_range() derive addr_range from the upstream bridge PCI_BRIDGE_MEM_WINDOW / PCI_BRIDGE_PREF_MEM_WINDOW rather than from pdev's own regions? Should it be filled up with device regions instead?

IIUC, there may be a couple of issues with the current approach:
1. The bridge's window can be wider than what pdev actually owns due to alignment padding.
2. If the bridge has multiple downstream devices (are there any?), the window spans address space belonging to sibling devices too and not just this pdev. So RMM would get a range that isn't exclusive to the pdev/stream being connected.

author maintainer
#15 lore raw

PATCH v5 12/15 coco: host: arm64: Refcount root-port pdevs used by IDE streams

In reply to Aneesh Kumar K.V (Arm)

Keep the root-port RMM pdev alive while endpoint IDE streams are attached to it.

Add a kref to the root-port descriptor, take a reference when reusing an existing root-port pdev for stream setup, and drop it when the endpoint disconnects. Release the root-port pdev once the final reference is dropped, tearing down the RMM object and its communication buffers at that point.

---
 drivers/virt/coco/arm-cca-host/main.c   | 31 ++++++++++++++++++++++---
 drivers/virt/coco/arm-cca-host/rmi-da.h |  4 ++++
 2 files changed, 32 insertions(+), 3 deletions(-)
drivers/virt/coco/arm-cca-host/main.c +28 -3
index d357bad14325..b74fe1fb14fd 100644
@@ -113,6 +113,23 @@ static inline void free_dev_communication_buffers(struct cca_host_comm_data *com
 	free_page((unsigned long)comm_data->io_params);
 }
 
+static void cca_root_port_pdev_release(struct kref *kref)
+{
+	struct cca_host_rp_dsc *rp_dsc = container_of(kref, struct cca_host_rp_dsc,
+						      tsm_ref);
+	struct pci_dev *rp = rp_dsc->pci.pdev;
+
+	cca_pdev_stop_and_destroy(rp);
+	free_dev_communication_buffers(&rp_dsc->pdev.comm_data);
+	rp->tsm = NULL;
+	kfree(rp_dsc);
+}
+
+static inline void cca_root_port_pdev_put(struct cca_host_rp_dsc *rp_dsc)
+{
+	kref_put(&rp_dsc->tsm_ref, cca_root_port_pdev_release);
+}
+
 static int cca_root_port_pdev_create(struct pci_dev *rp, struct tsm_dev *tsm_dev)
 {
 	int ret;
@@ -127,6 +144,7 @@ static int cca_root_port_pdev_create(struct pci_dev *rp, struct tsm_dev *tsm_dev
 	rp->tsm->dsm_dev = rp;
 	rp->tsm->pdev = rp;
 	rp->tsm->tsm_dev = tsm_dev;
+	kref_init(&rp_dsc->tsm_ref);
 	mutex_init(&rp_dsc->pdev.object_lock);
 
 	ret = init_dev_communication_buffers(rp, &rp_dsc->pdev.comm_data);
@@ -198,12 +216,15 @@ static int cca_pdev_create_ncoh_stream(struct pci_dev *pdev, unsigned long strea
 		/* Make sure they use the same TSM */
 		if (rp->tsm->tsm_dev != pf0_ep_dsc->pci.base_tsm.tsm_dev)
 			return -EINVAL;
-	}
 
+		kref_get(&rp_dsc->tsm_ref);
+	}
 
 	params = (struct rmi_pdev_stream_params *)get_zeroed_page(GFP_KERNEL);
-	if (!params)
+	if (!params) {
+		cca_root_port_pdev_put(rp_dsc);
 		return -ENOMEM;
+	}
 
 	params->flags = 0;
 	params->stream_type = RMI_PDEV_STREAM_NCOH;
@@ -213,7 +234,9 @@ static int cca_pdev_create_ncoh_stream(struct pci_dev *pdev, unsigned long strea
 	params->num_addr_range = pci_dev_addr_range(pdev, params->addr_range);
 
 	ret = cca_pdev_stream_connect(pdev, rp, params, &stream_handle);
-	if (!ret)
+	if (ret)
+		cca_root_port_pdev_put(rp_dsc);
+	else
 		pf0_ep_dsc->stream_handle = stream_handle;
 
 	free_page((unsigned long)params);
@@ -339,6 +362,8 @@ static void __maybe_unused cca_tsm_disconnect(struct pci_dev *pdev)
 		ide = pf0_ep_dsc->sel_stream;
 
 	cca_pdev_disconnect_stream(pdev, rp, pf0_ep_dsc->stream_handle);
+	if (rp)
+		cca_root_port_pdev_put(to_cca_rp_dsc(rp));
 
 	cca_pdev_stop_and_destroy(pdev);
 	free_dev_communication_buffers(&pf0_ep_dsc->pdev.comm_data);
drivers/virt/coco/arm-cca-host/rmi-da.h +4 -0
index ed5d3a58096d..b76cb4af8436 100644
@@ -12,6 +12,9 @@
 #include <linux/pci-ide.h>
 #include <linux/pci-tsm.h>
 #include <linux/sizes.h>
+#include <linux/atomic.h>
+#include <linux/kref.h>
+#include <linux/wait.h>
 
 #define MAX_CACHE_OBJ_SIZE	SZ_16M
 #define CACHE_CHUNK_SIZE	SZ_4K
@@ -104,6 +107,7 @@ struct cca_host_pf0_ep_dsc {
 struct cca_host_rp_dsc {
 	struct pci_tsm pci;
 	struct cca_host_pdev_dsc pdev;
+	struct kref tsm_ref;
 };
 
 struct cca_host_fn_dsc {
Signature · 1 line
2.43.0
author maintainer
#16 lore raw

PATCH v5 13/15 PCI/TSM: Move CMA DOE mailbox discovery out of pci_tsm_pf0_constructor()

In reply to Aneesh Kumar K.V (Arm)

pci_tsm_pf0_constructor() currently looks up a CMA DOE mailbox and fails PF0 initialization when one is not present. That is too strict for all link TSM drivers.

Move CMA DOE mailbox discovery into the low-level PF0 probe callbacks so each driver can decide whether a mailbox is mandatory.

Keep SEV-TIO and TDX requiring a CMA mailbox, while allowing the arm-cca host path to proceed on PF0 devices that do not support IDE and therefore have no DOE-based SPDM path.

---
 drivers/crypto/ccp/sev-dev-tsm.c      | 13 +++++++++++++
 drivers/pci/tsm.c                     | 13 +++++++------
 drivers/virt/coco/arm-cca-host/main.c | 15 ++++++++++++---
 3 files changed, 32 insertions(+), 9 deletions(-)
drivers/crypto/ccp/sev-dev-tsm.c +13 -0
index 46f2539d2d5a..e55eaca16998 100644
@@ -217,6 +217,19 @@ static struct pci_tsm *tio_pf0_probe(struct pci_dev *pdev, struct sev_device *se
 	if (rc)
 		return NULL;
 
+	/* if device have ide cap, setup doe mailbox */
+	if (pdev->ide_cap) {
+		struct pci_doe_mb *doe_mb;
+
+		doe_mb = pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG,
+					      PCI_DOE_FEATURE_CMA);
+		if (!doe_mb)
+			return NULL;
+		dsm->tsm.doe_mb = doe_mb;
+	} else {
+		return NULL;
+	}
+
 	pci_dbg(pdev, "TSM enabled\n");
 	dsm->sev = sev;
 	return &no_free_ptr(dsm)->tsm.base_tsm;
drivers/pci/tsm.c +7 -6
index 5fdcd7f2e820..34805b7ba7a0 100644
@@ -744,12 +744,13 @@ int pci_tsm_pf0_constructor(struct pci_dev *pdev, struct pci_tsm_pf0 *tsm,
 			    struct tsm_dev *tsm_dev)
 {
 	mutex_init(&tsm->lock);
-	tsm->doe_mb = pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG,
-					   PCI_DOE_FEATURE_CMA);
-	if (!tsm->doe_mb) {
-		pci_warn(pdev, "TSM init failure, no CMA mailbox\n");
-		return -ENODEV;
-	}
+	/*
+	 * Note, low-level TSM driver responsible for determining if it wants to
+	 * proceed with a device that has no DOE mailbox. TSM may have an
+	 * alternate method for coordinating TDISP.
+	 */
+	if (!tsm->doe_mb)
+		pci_dbg(pdev, "No CMA mailbox\n");
 
 	return pci_tsm_link_constructor(pdev, &tsm->base_tsm, tsm_dev);
 }
drivers/virt/coco/arm-cca-host/main.c +12 -3
index b74fe1fb14fd..70cbff181561 100644
@@ -12,6 +12,7 @@
 #include <linux/tsm.h>
 #include <linux/vmalloc.h>
 #include <linux/cleanup.h>
+#include <linux/pci-doe.h>
 
 #include "rmi-da.h"
 
@@ -33,14 +34,22 @@ static struct pci_tsm *cca_tsm_pci_probe(struct tsm_dev *tsm_dev, struct pci_dev
 		return &no_free_ptr(fn_dsc)->pci;
 	}
 
-	if (!pdev->ide_cap)
-		return NULL;
-
 	struct cca_host_pf0_ep_dsc *pf0_ep_dsc __free(kfree) =
 		kzalloc(sizeof(*pf0_ep_dsc), GFP_KERNEL);
 	if (!pf0_ep_dsc)
 		return NULL;
 
+	/* if device have ide cap, setup doe mailbox */
+	if (pdev->ide_cap) {
+		struct pci_doe_mb *doe_mb;
+
+		doe_mb = pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG,
+					      PCI_DOE_FEATURE_CMA);
+		if (!doe_mb)
+			return NULL;
+		pf0_ep_dsc->pci.doe_mb = doe_mb;
+	}
+
 	ret = pci_tsm_pf0_constructor(pdev, &pf0_ep_dsc->pci, tsm_dev);
 	if (ret)
 		return NULL;
Signature · 1 line
2.43.0
author maintainer
#17 lore raw

PATCH v5 14/15 coco: host: arm64: Add NCOH_SYS stream support for RC endpoints

In reply to Aneesh Kumar K.V (Arm)

Teach the host CCA pdev setup to handle PCI_EXP_TYPE_RC_END devices.

Classify RC integrated endpoints as RMI_PDEV_FLAGS_CATEGORY_ON_CHIP_EP when building the RMM pdev parameters, and only advertise SPDM support when a DOE mailbox is present.

Also add the stream setup path for these devices by creating an RMI_PDEV_STREAM_NCOH_SYS stream using the endpoint pdev and its bridge address windows. This allows RC endpoints to participate in the TDISP flow without requiring a separate root-port pdev.

---
 drivers/virt/coco/arm-cca-host/main.c   | 28 +++++++++++++++++++++++++
 drivers/virt/coco/arm-cca-host/rmi-da.c | 10 +++++++++
 2 files changed, 38 insertions(+)
drivers/virt/coco/arm-cca-host/main.c +28 -0
index 70cbff181561..b7b0cc0a016c 100644
@@ -252,11 +252,39 @@ static int cca_pdev_create_ncoh_stream(struct pci_dev *pdev, unsigned long strea
 	return ret;
 }
 
+static int cca_pdev_create_ncoh_sys_stream(struct pci_dev *pdev)
+{
+	int ret;
+	long stream_handle;
+	struct rmi_pdev_stream_params *params;
+	struct cca_host_pf0_ep_dsc *pf0_ep_dsc = to_cca_pf0_ep_dsc(pdev);
+
+	params = (struct rmi_pdev_stream_params *)get_zeroed_page(GFP_KERNEL);
+	if (!params)
+		return -ENOMEM;
+
+	params->flags = 0;
+	params->stream_type = RMI_PDEV_STREAM_NCOH_SYS;
+	params->pdev_1 = virt_to_phys(pf0_ep_dsc->pdev.rmm_pdev);
+	params->pdev_2 = 0; /* ignored */
+	params->ide_sid = 0; /* ignored */
+	params->num_addr_range = pci_dev_addr_range(pdev, params->addr_range);
+
+	ret = cca_pdev_stream_connect(pdev, NULL, params, &stream_handle);
+	if (!ret)
+		pf0_ep_dsc->stream_handle = stream_handle;
+
+	free_page((unsigned long)params);
+	return ret;
+}
+
 static int cca_pdev_create_streams(struct pci_dev *pdev, unsigned long stream_id)
 {
 	switch (pci_pcie_type(pdev)) {
 	case PCI_EXP_TYPE_ENDPOINT:
 		return cca_pdev_create_ncoh_stream(pdev, stream_id);
+	case PCI_EXP_TYPE_RC_END:
+		return cca_pdev_create_ncoh_sys_stream(pdev);
 	default:
 		return -EINVAL;
 	}
drivers/virt/coco/arm-cca-host/rmi-da.c +10 -0
index fcca9f73b5fb..794385f818ce 100644
@@ -66,6 +66,16 @@ static int init_pdev_params(struct pci_dev *pdev, struct rmi_pdev_params *params
 		category = RMI_PDEV_FLAGS_CATEGORY_ROOT_PORT;
 		break;
 	}
+	case PCI_EXP_TYPE_RC_END: {
+		struct cca_host_pf0_ep_dsc *pf0_ep_dsc = to_cca_pf0_ep_dsc(pdev);
+
+		/* Use SPDM if present */
+		if (pf0_ep_dsc->pci.doe_mb)
+			params->flags = RMI_PDEV_FLAGS_SPDM;
+
+		category = RMI_PDEV_FLAGS_CATEGORY_ON_CHIP_EP;
+		break;
+	}
 	default:
 		return -EINVAL;
 	}
Signature · 1 line
2.43.0
author maintainer
#18 lore raw

PATCH v5 15/15 coco: host: arm64: Enable PCI TSM connect callbacks

In reply to Aneesh Kumar K.V (Arm)

The previous patches build up the Arm CCA host device-assignment flow: RMM pdev creation and teardown, device communication, public-key setup, peer stream coordination, RMM stream connect/disconnect, and support for both endpoint and RC endpoint stream types.

Now that the connect and disconnect paths contain the complete setup and teardown sequence, register them in pci_tsm_ops and drop the __maybe_unused annotations.

---
 drivers/virt/coco/arm-cca-host/main.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/virt/coco/arm-cca-host/main.c +4 -2
index b7b0cc0a016c..e8afd54c23f6 100644
@@ -295,7 +295,7 @@ static inline bool cca_pdev_need_sel_ide_streams(struct pci_dev *pdev)
 	return pci_pcie_type(pdev) == PCI_EXP_TYPE_ENDPOINT;
 }
 
-static int __maybe_unused cca_tsm_connect(struct pci_dev *pdev)
+static int cca_tsm_connect(struct pci_dev *pdev)
 {
 	struct pci_dev *rp = pcie_find_root_port(pdev);
 	struct cca_host_pf0_ep_dsc *pf0_ep_dsc;
@@ -385,7 +385,7 @@ static int __maybe_unused cca_tsm_connect(struct pci_dev *pdev)
 	return ret;
 }
 
-static void __maybe_unused cca_tsm_disconnect(struct pci_dev *pdev)
+static void cca_tsm_disconnect(struct pci_dev *pdev)
 {
 	struct pci_ide *ide;
 	struct cca_host_pf0_ep_dsc *pf0_ep_dsc;
@@ -414,6 +414,8 @@ static void __maybe_unused cca_tsm_disconnect(struct pci_dev *pdev)
 static struct pci_tsm_ops cca_link_pci_ops = {
 	.probe = cca_tsm_pci_probe,
 	.remove = cca_tsm_pci_remove,
+	.connect = cca_tsm_connect,
+	.disconnect = cca_tsm_disconnect,
 };
 
 static void cca_link_tsm_remove(void *tsm_dev)
Signature · 1 line
2.43.0