[RFC PATCH v1 12/38] coco: host: arm64: CCA host platform device driver

Aneesh Kumar K.V (Arm)
2025-07-28T13:53:48Z

This driver registers the pci_tsm_ops with tsm subsystem.

Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/virt/coco/Kconfig | 2 +
drivers/virt/coco/Makefile | 1 +
drivers/virt/coco/arm-cca-host/Kconfig | 12 ++
drivers/virt/coco/arm-cca-host/Makefile | 5 +
drivers/virt/coco/arm-cca-host/arm-cca.c | 209 +++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmm-da.h | 29 ++++
6 files changed, 258 insertions(+)
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/arm-cca.c
create mode 100644 drivers/virt/coco/arm-cca-host/rmm-da.h

diff --git a/drivers/virt/coco/Kconfig b/drivers/virt/coco/Kconfig
index 57248b088545..43e9508301bf 100644
--- a/drivers/virt/coco/Kconfig
+++ b/drivers/virt/coco/Kconfig
@@ -15,5 +15,7 @@ source "drivers/virt/coco/arm-cca-guest/Kconfig"

source "drivers/virt/coco/guest/Kconfig"

+source "drivers/virt/coco/arm-cca-host/Kconfig"
+
config TSM
tristate
diff --git a/drivers/virt/coco/Makefile b/drivers/virt/coco/Makefile
index 04e124b2d7cf..d0a859dd9eaf 100644
--- a/drivers/virt/coco/Makefile
+++ b/drivers/virt/coco/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_ARM_CCA_GUEST) += arm-cca-guest/

obj-$(CONFIG_TSM) += tsm-core.o
obj-y += guest/
+obj-$(CONFIG_ARM_CCA_HOST) += arm-cca-host/
diff --git a/drivers/virt/coco/arm-cca-host/Kconfig b/drivers/virt/coco/arm-cca-host/Kconfig
new file mode 100644
index 000000000000..0f19fbf47613
--- /dev/null
+++ b/drivers/virt/coco/arm-cca-host/Kconfig
@@ -0,0 +1,12 @@
+# 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_TSM
+ select TSM
+
+ help
+ The driver provides TSM backend for ARM CCA
diff --git a/drivers/virt/coco/arm-cca-host/Makefile b/drivers/virt/coco/arm-cca-host/Makefile
new file mode 100644
index 000000000000..ad353b07e95a
--- /dev/null
+++ b/drivers/virt/coco/arm-cca-host/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+obj-$(CONFIG_ARM_CCA_HOST) += arm-cca-host.o
+
+arm-cca-host-$(CONFIG_TSM) += arm-cca.o
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
new file mode 100644
index 000000000000..c8b0e6db1f47
--- /dev/null
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 ARM Ltd.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/pci-tsm.h>
+#include <linux/pci-ide.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/tsm.h>
+#include <linux/vmalloc.h>
+
+#include "rmm-da.h"
+
+/* Number of streams that we can support at the hostbridge level */
+#define CCA_HB_PLATFORM_STREAMS 4
+
+/* Total number of stream id supported at root port level */
+#define MAX_STREAM_ID 256
+
+DEFINE_FREE(vfree, void *, if (!IS_ERR_OR_NULL(_T)) vfree(_T))
+static struct pci_tsm *cca_tsm_pci_probe(struct pci_dev *pdev)
+{
+ int rc;
+ struct pci_host_bridge *hb;
+ struct cca_host_dsc_pf0 *dsc_pf0 __free(vfree) = NULL;
+
+ if (pdev->is_virtfn)
+ return NULL;
+
+ if (!is_pci_tsm_pf0(pdev)) {
+ struct pci_tsm *tsm = kzalloc(sizeof(*tsm), GFP_KERNEL);
+
+ if (!tsm)
+ goto err_out;
+
+ pci_tsm_initialize(pdev, tsm);
+ return tsm;
+ }
+
+ if (!pdev->ide_cap)
+ goto err_out;
+
+ dsc_pf0 = vcalloc(sizeof(*dsc_pf0), GFP_KERNEL);
+ if (!dsc_pf0)
+ goto err_out;
+
+ rc = pci_tsm_pf0_initialize(pdev, &dsc_pf0->pci);
+ if (rc)
+ return NULL;
+ /*
+ * FIXME!!
+ * update the hostbridge details. This should go into
+ * some host bridge probe/init routine.
+ * than the selective index supported by the endpoint
+ */
+ hb = pci_find_host_bridge(pdev->bus);
+ pci_ide_init_nr_streams(hb, CCA_HB_PLATFORM_STREAMS);
+
+ pci_info(pdev, "tsm enabled\n");
+ return &no_free_ptr(dsc_pf0)->pci.tsm;
+
+err_out:
+ return NULL;
+}
+
+static void cca_tsm_pci_remove(struct pci_tsm *tsm)
+{
+ struct pci_dev *pdev = tsm->pdev;
+ struct cca_host_dsc_pf0 *dsc_pf0;
+
+ if (WARN_ON(pdev->is_virtfn))
+ return;
+
+ if (!is_pci_tsm_pf0(pdev)) {
+
+ pci_dbg(tsm->pdev, "tsm disabled\n");
+ kfree(pdev->tsm);
+ return;
+ }
+
+ dsc_pf0 = to_cca_dsc_pf0(pdev);
+ pci_dbg(tsm->pdev, "tsm disabled\n");
+ vfree(dsc_pf0);
+}
+
+/* per root port unique with multiple restrictions. For now global */
+static DECLARE_BITMAP(cca_stream_ids, MAX_STREAM_ID);
+
+static int cca_tsm_connect(struct pci_dev *pdev)
+{
+ struct pci_dev *rp = pcie_find_root_port(pdev);
+ struct cca_host_dsc_pf0 *dsc_pf0;
+ struct pci_ide *ide;
+ int rc, stream_id;
+
+ /* Only function 0 supports connect in host */
+ if (WARN_ON(!is_pci_tsm_pf0(pdev)))
+ return -EIO;
+
+ dsc_pf0 = to_cca_dsc_pf0(pdev);
+ /* Allocate stream id */
+ stream_id = find_first_zero_bit(cca_stream_ids, MAX_STREAM_ID);
+ if (stream_id == MAX_STREAM_ID)
+ return -EBUSY;
+ set_bit(stream_id, cca_stream_ids);
+
+ ide = pci_ide_stream_alloc(pdev);
+ if (!ide) {
+ rc = -ENOMEM;
+ goto err_stream_alloc;
+ }
+
+ dsc_pf0->sel_stream = ide;
+ ide->stream_id = stream_id;
+ rc = pci_ide_stream_register(ide);
+ if (rc)
+ goto err_stream;
+
+ pci_ide_stream_setup(pdev, ide);
+ pci_ide_stream_setup(rp, ide);
+
+ rc = tsm_ide_stream_register(pdev, ide);
+ if (rc)
+ goto err_tsm;
+
+ /*
+ * Once ide is setup enable the stream at endpoint
+ * Root port will be done by RMM
+ */
+ pci_ide_stream_enable(pdev, ide);
+ return 0;
+
+err_tsm:
+ pci_ide_stream_teardown(rp, ide);
+ pci_ide_stream_teardown(pdev, ide);
+ pci_ide_stream_unregister(ide);
+err_stream:
+ pci_ide_stream_free(ide);
+err_stream_alloc:
+ clear_bit(stream_id, cca_stream_ids);
+
+ return rc;
+}
+
+static void cca_tsm_disconnect(struct pci_dev *pdev)
+{
+ struct pci_dev *rp = pcie_find_root_port(pdev);
+ struct cca_host_dsc_pf0 *dsc_pf0;
+ struct pci_ide *ide;
+
+ if (WARN_ON(!is_pci_tsm_pf0(pdev)))
+ return;
+
+ dsc_pf0 = to_cca_dsc_pf0(pdev);
+ ide = dsc_pf0->sel_stream;
+ dsc_pf0->sel_stream = NULL;
+ pci_ide_stream_disable(pdev, ide);
+ tsm_ide_stream_unregister(ide);
+ pci_ide_stream_teardown(rp, ide);
+ pci_ide_stream_teardown(pdev, ide);
+ pci_ide_stream_unregister(ide);
+ clear_bit(ide->stream_id, cca_stream_ids);
+ pci_ide_stream_free(ide);
+}
+
+static const struct pci_tsm_ops cca_pci_ops = {
+ .probe = cca_tsm_pci_probe,
+ .remove = cca_tsm_pci_remove,
+ .connect = cca_tsm_connect,
+ .disconnect = cca_tsm_disconnect,
+};
+
+static void cca_tsm_remove(void *tsm_core)
+{
+ tsm_unregister(tsm_core);
+}
+
+static int cca_tsm_probe(struct platform_device *pdev)
+{
+ struct tsm_core_dev *tsm_core;
+
+ tsm_core = tsm_register(&pdev->dev, NULL, &cca_pci_ops);
+ if (IS_ERR(tsm_core))
+ return PTR_ERR(tsm_core);
+
+ return devm_add_action_or_reset(&pdev->dev, cca_tsm_remove, tsm_core);
+}
+
+static const struct platform_device_id arm_cca_host_id_table[] = {
+ { RMI_DEV_NAME, 0},
+ { }
+};
+MODULE_DEVICE_TABLE(platform, arm_cca_host_id_table);
+
+
+static struct platform_driver cca_tsm_platform_driver = {
+ .probe = cca_tsm_probe,
+ .id_table = arm_cca_host_id_table,
+ .driver = {
+ .name = "cca_tsm",
+ },
+};
+
+MODULE_IMPORT_NS("PCI_IDE");
+module_platform_driver(cca_tsm_platform_driver);
+MODULE_DESCRIPTION("ARM CCA Host TSM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/virt/coco/arm-cca-host/rmm-da.h b/drivers/virt/coco/arm-cca-host/rmm-da.h
new file mode 100644
index 000000000000..840cb584acdd
--- /dev/null
+++ b/drivers/virt/coco/arm-cca-host/rmm-da.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024 ARM Ltd.
+ */
+
+#ifndef RMM_DA_H_
+#define RMM_DA_H_
+
+#include <linux/pci.h>
+#include <linux/pci-ide.h>
+#include <linux/pci-tsm.h>
+#include <asm/rmi_smc.h>
+
+struct cca_host_dsc_pf0 {
+ struct pci_tsm_pf0 pci;
+ struct pci_ide *sel_stream;
+};
+
+static inline struct cca_host_dsc_pf0 *to_cca_dsc_pf0(struct pci_dev *pdev)
+{
+ struct pci_tsm *tsm = pdev->tsm;
+
+ if (!tsm || pdev->is_virtfn || !is_pci_tsm_pf0(pdev))
+ return NULL;
+
+ return container_of(tsm, struct cca_host_dsc_pf0, pci.tsm);
+}
+
+#endif
--
2.43.0