* [RFC PATCH v3 00/10] coco/TSM: Host-side Arm CCA IDE setup via connect/disconnect callbacks
@ 2026-03-12 8:01 Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 01/10] KVM: arm64: RMI: Add and export kvm_has_da_feature helper Aneesh Kumar K.V (Arm)
` (9 more replies)
0 siblings, 10 replies; 11+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:01 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel; +Cc: linux-kernel, Aneesh Kumar K.V (Arm)
This patch series implements the TSM ->connect() and ->disconnect() callbacks
required for the Arm CCA IDE setup as per the RMM ALP17 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.
The series builds upon the TSM framework patches posted at [2] and depends on
the KVM CCA patchset [3]. A git repository containing all the related changes is
available at [4].
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
Previous posting:
rfc-v1 https://lore.kernel.org/all/20250728135216.48084-1-aneesh.kumar@kernel.org
rfc-v2 https://lore.kernel.org/all/20251027095602.1154418-1-aneesh.kumar@kernel.org
Changes from v2:
* rebase to latest kernel and core TSM changes
* Address review feedback.
[1] https://developer.arm.com/-/cdn-downloads/permalink/Architectures/Armv9/DEN0137_1.1-alp17.zip
[2] https://lore.kernel.org/all/20260303000207.1836586-1-dan.j.williams@intel.com
[3] https://lore.kernel.org/all/461fa23f-9add-40e5-a0d0-759030e7c70b@arm.com
[4] https://gitlab.arm.com/linux-arm/linux-cca.git cca/topics/cca-tdisp-upstream-rfc-v3
Aneesh Kumar K.V (Arm) (7):
KVM: arm64: RMI: Add and export kvm_has_da_feature helper
coco: host: arm64: Add host TSM callback and IDE stream allocation
support
coco: host: arm64: Build and register RMM pdev descriptors
coco: host: arm64: Add RMM device communication helpers
coco: host: arm64: Add helper to stop and tear down an RMM pdev
coco: host: arm64: Instantiate RMM pdev during device connect
coco: host: arm64: Register device public key with RMM
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
arch/arm64/include/asm/kvm_rmi.h | 1 +
arch/arm64/include/asm/rmi_cmds.h | 78 +++
arch/arm64/include/asm/rmi_smc.h | 183 ++++++-
arch/arm64/kvm/rmi.c | 6 +
crypto/asymmetric_keys/x509_cert_parser.c | 9 +
crypto/asymmetric_keys/x509_loader.c | 38 +-
crypto/asymmetric_keys/x509_parser.h | 42 +-
drivers/firmware/smccc/rmm.c | 12 +
drivers/firmware/smccc/rmm.h | 8 +
drivers/firmware/smccc/smccc.c | 1 +
drivers/virt/coco/Kconfig | 2 +
drivers/virt/coco/Makefile | 1 +
drivers/virt/coco/arm-cca-host/Kconfig | 23 +
drivers/virt/coco/arm-cca-host/Makefile | 5 +
drivers/virt/coco/arm-cca-host/arm-cca.c | 274 ++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 639 ++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 122 +++++
include/keys/asymmetric-type.h | 2 +
include/keys/x509-parser.h | 57 ++
19 files changed, 1448 insertions(+), 55 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/arm-cca.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
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC PATCH v3 01/10] KVM: arm64: RMI: Add and export kvm_has_da_feature helper
2026-03-12 8:01 [RFC PATCH v3 00/10] coco/TSM: Host-side Arm CCA IDE setup via connect/disconnect callbacks Aneesh Kumar K.V (Arm)
@ 2026-03-12 8:01 ` Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 02/10] coco: host: arm64: Add host TSM callback and IDE stream allocation support Aneesh Kumar K.V (Arm)
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:01 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
Add kvm_has_da_feature() helper for use in later patches.
Update reserved fields based on Alp17 spec.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/kvm_rmi.h | 1 +
arch/arm64/include/asm/rmi_smc.h | 3 ++-
arch/arm64/kvm/rmi.c | 6 ++++++
3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
index 1b2cdaac6c50..a967061af6ed 100644
--- a/arch/arm64/include/asm/kvm_rmi.h
+++ b/arch/arm64/include/asm/kvm_rmi.h
@@ -90,6 +90,7 @@ u32 kvm_realm_ipa_limit(void);
u32 kvm_realm_vgic_nr_lr(void);
u8 kvm_realm_max_pmu_counters(void);
unsigned int kvm_realm_sve_max_vl(void);
+bool kvm_has_da_feature(void);
u64 kvm_realm_reset_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val);
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 1000368f1bca..f6f786bfb9c9 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -86,7 +86,8 @@ enum rmi_ripas {
#define RMI_FEATURE_REGISTER_0_HASH_SHA_512 BIT(33)
#define RMI_FEATURE_REGISTER_0_GICV3_NUM_LRS GENMASK(37, 34)
#define RMI_FEATURE_REGISTER_0_MAX_RECS_ORDER GENMASK(41, 38)
-#define RMI_FEATURE_REGISTER_0_Reserved GENMASK(63, 42)
+#define RMI_FEATURE_REGISTER_0_DA BIT(42)
+#define RMI_FEATURE_REGISTER_0_Reserved GENMASK(63, 61)
#define RMI_REALM_PARAM_FLAG_LPA2 BIT(0)
#define RMI_REALM_PARAM_FLAG_SVE BIT(1)
diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
index 478a73e0b35a..08f3d2362dfd 100644
--- a/arch/arm64/kvm/rmi.c
+++ b/arch/arm64/kvm/rmi.c
@@ -1738,6 +1738,12 @@ int kvm_init_realm_vm(struct kvm *kvm)
return 0;
}
+bool kvm_has_da_feature(void)
+{
+ return rmi_has_feature(RMI_FEATURE_REGISTER_0_DA);
+}
+EXPORT_SYMBOL_GPL(kvm_has_da_feature);
+
void kvm_init_rmi(void)
{
/* Only 4k page size on the host is supported */
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v3 02/10] coco: host: arm64: Add host TSM callback and IDE stream allocation support
2026-03-12 8:01 [RFC PATCH v3 00/10] coco/TSM: Host-side Arm CCA IDE setup via connect/disconnect callbacks Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 01/10] KVM: arm64: RMI: Add and export kvm_has_da_feature helper Aneesh Kumar K.V (Arm)
@ 2026-03-12 8:01 ` Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 03/10] coco: host: arm64: Build and register RMM pdev descriptors Aneesh Kumar K.V (Arm)
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:01 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
Register the TSM callback when the DA feature is supported by KVM.
This driver handles IDE stream setup for both the root port and PCIe
endpoints. Root port IDE stream enablement itself is managed by RMM.
In addition, the driver registers `pci_tsm_ops` with the TSM subsystem.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_smc.h | 2 +
drivers/firmware/smccc/rmm.c | 12 ++
drivers/firmware/smccc/rmm.h | 8 +
drivers/firmware/smccc/smccc.c | 1 +
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/arm-cca.c | 205 +++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 45 +++++
10 files changed, 300 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/rmi-da.h
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index f6f786bfb9c9..9f8b6aa5ea06 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -12,6 +12,8 @@
#include <linux/arm-smccc.h>
+#define RMI_DEV_NAME "arm-rmi-dev"
+
#define SMC_RMI_CALL(func) \
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
ARM_SMCCC_SMC_64, \
diff --git a/drivers/firmware/smccc/rmm.c b/drivers/firmware/smccc/rmm.c
index 03496330630f..7435996dcb90 100644
--- a/drivers/firmware/smccc/rmm.c
+++ b/drivers/firmware/smccc/rmm.c
@@ -23,3 +23,15 @@ void __init register_rsi_device(struct platform_device *pdev)
__devm_auxiliary_device_create(&pdev->dev,
"arm_cca_guest", RSI_DEV_NAME, NULL, 0);
}
+
+void __init register_rmi_device(struct platform_device *pdev)
+{
+ struct arm_smccc_res res;
+ unsigned long host_version = RMI_ABI_VERSION(RMI_ABI_MAJOR_VERSION,
+ RMI_ABI_MINOR_VERSION);
+
+ arm_smccc_1_1_invoke(SMC_RMI_VERSION, host_version, &res);
+ if (res.a0 == RMI_SUCCESS)
+ __devm_auxiliary_device_create(&pdev->dev,
+ "arm_cca_host", RMI_DEV_NAME, NULL, 0);
+}
diff --git a/drivers/firmware/smccc/rmm.h b/drivers/firmware/smccc/rmm.h
index a47a650d4f51..37d0d95a099e 100644
--- a/drivers/firmware/smccc/rmm.h
+++ b/drivers/firmware/smccc/rmm.h
@@ -6,12 +6,20 @@
#ifdef CONFIG_ARM64
#include <asm/rsi_cmds.h>
+#include <asm/rmi_smc.h>
+
void __init register_rsi_device(struct platform_device *pdev);
+void __init register_rmi_device(struct platform_device *pdev);
#else
static void __init register_rsi_device(struct platform_device *pdev)
{
+}
+
+static void __init register_rmi_device(struct platform_device *pdev)
+{
+
}
#endif
#endif
diff --git a/drivers/firmware/smccc/smccc.c b/drivers/firmware/smccc/smccc.c
index fc9b44b7c687..2bf2d59e686d 100644
--- a/drivers/firmware/smccc/smccc.c
+++ b/drivers/firmware/smccc/smccc.c
@@ -97,6 +97,7 @@ static int __init smccc_devices_init(void)
* the required SMCCC function IDs at a supported revision.
*/
register_rsi_device(pdev);
+ register_rmi_device(pdev);
}
if (smccc_trng_available) {
diff --git a/drivers/virt/coco/Kconfig b/drivers/virt/coco/Kconfig
index f7691f64fbe3..1cbc2134f9ea 100644
--- a/drivers/virt/coco/Kconfig
+++ b/drivers/virt/coco/Kconfig
@@ -19,5 +19,7 @@ endif
source "drivers/virt/coco/tdx-host/Kconfig"
+source "drivers/virt/coco/arm-cca-host/Kconfig"
+
config TSM
bool
diff --git a/drivers/virt/coco/Makefile b/drivers/virt/coco/Makefile
index b323b0ae4f82..f2310c34daf9 100644
--- a/drivers/virt/coco/Makefile
+++ b/drivers/virt/coco/Makefile
@@ -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/
diff --git a/drivers/virt/coco/arm-cca-host/Kconfig b/drivers/virt/coco/arm-cca-host/Kconfig
new file mode 100644
index 000000000000..efe40d61d5d8
--- /dev/null
+++ b/drivers/virt/coco/arm-cca-host/Kconfig
@@ -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 KVM
+ select PCI_TSM
+ select AUXILIARY_BUS
+
+ 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.
diff --git a/drivers/virt/coco/arm-cca-host/Makefile b/drivers/virt/coco/arm-cca-host/Makefile
new file mode 100644
index 000000000000..c236827f002c
--- /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-y += 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..639ebd82978a
--- /dev/null
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+
+#include <linux/auxiliary_bus.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 <linux/cleanup.h>
+#include <linux/kvm_host.h>
+
+#include "rmi-da.h"
+
+/* Total number of stream id supported at root port level */
+#define MAX_STREAM_ID 256
+
+static struct pci_tsm *cca_tsm_pci_probe(struct tsm_dev *tsm_dev, struct pci_dev *pdev)
+{
+ int rc;
+
+ if (!is_pci_tsm_pf0(pdev)) {
+ struct cca_host_fn_dsc *fn_dsc __free(kfree) =
+ kzalloc(sizeof(*fn_dsc), GFP_KERNEL);
+
+ if (!fn_dsc)
+ return NULL;
+
+ rc = pci_tsm_link_constructor(pdev, &fn_dsc->pci, tsm_dev);
+ if (rc)
+ return NULL;
+
+ return &no_free_ptr(fn_dsc)->pci;
+ }
+
+ if (!pdev->ide_cap)
+ return NULL;
+
+ struct cca_host_pf0_dsc *pf0_dsc __free(kfree) =
+ kzalloc(sizeof(*pf0_dsc), GFP_KERNEL);
+ if (!pf0_dsc)
+ return NULL;
+
+ rc = pci_tsm_pf0_constructor(pdev, &pf0_dsc->pci, tsm_dev);
+ if (rc)
+ return NULL;
+
+ pci_dbg(pdev, "tsm enabled\n");
+ return &no_free_ptr(pf0_dsc)->pci.base_tsm;
+}
+
+static void cca_tsm_pci_remove(struct pci_tsm *tsm)
+{
+ struct pci_dev *pdev = tsm->pdev;
+
+ if (is_pci_tsm_pf0(pdev)) {
+ struct cca_host_pf0_dsc *pf0_dsc = to_cca_pf0_dsc(pdev);
+
+ pci_tsm_pf0_destructor(&pf0_dsc->pci);
+ kfree(pf0_dsc);
+ } else {
+ kfree(to_cca_fn_dsc(pdev));
+ }
+}
+
+/* For now global for simplicity. Protected by pci_tsm_rwsem */
+static DECLARE_BITMAP(cca_stream_ids, MAX_STREAM_ID);
+static int alloc_stream_id(struct pci_host_bridge *hb)
+{
+ int stream_id;
+
+redo_alloc:
+ stream_id = find_first_zero_bit(cca_stream_ids, MAX_STREAM_ID);
+ if (stream_id == MAX_STREAM_ID)
+ return stream_id;
+
+ if (ida_exists(&hb->ide_stream_ids_ida, stream_id)) {
+ /* mark the stream allocated in the global bitmap. */
+ set_bit(stream_id, cca_stream_ids);
+ goto redo_alloc;
+ }
+ return stream_id;
+}
+
+static int cca_tsm_connect(struct pci_dev *pdev)
+{
+ struct pci_dev *rp = pcie_find_root_port(pdev);
+ struct cca_host_pf0_dsc *pf0_dsc;
+ 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;
+
+ pf0_dsc = to_cca_pf0_dsc(pdev);
+ /* Allocate stream id */
+ stream_id = alloc_stream_id(pci_find_host_bridge(pdev->bus));
+ 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;
+ }
+
+ pf0_dsc->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(ide);
+ if (rc)
+ goto err_tsm;
+
+ /*
+ * Once ide is setup, enable the stream at the 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);
+ pf0_dsc->sel_stream = NULL;
+err_stream_alloc:
+ clear_bit(stream_id, cca_stream_ids);
+
+ return rc;
+}
+
+static void cca_tsm_disconnect(struct pci_dev *pdev)
+{
+ int stream_id;
+ struct pci_ide *ide;
+ struct cca_host_pf0_dsc *pf0_dsc;
+
+ pf0_dsc = to_cca_pf0_dsc(pdev);
+ if (!pf0_dsc)
+ return;
+
+ ide = pf0_dsc->sel_stream;
+ stream_id = ide->stream_id;
+
+ pci_ide_stream_release(ide);
+ pf0_dsc->sel_stream = NULL;
+ clear_bit(stream_id, cca_stream_ids);
+}
+
+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)
+{
+ tsm_unregister(tsm_dev);
+}
+
+static int cca_link_tsm_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct tsm_dev *tsm_dev;
+
+ if (!kvm_has_da_feature())
+ return -ENODEV;
+
+ tsm_dev = tsm_register(&adev->dev, &cca_link_pci_ops);
+ if (IS_ERR(tsm_dev))
+ return PTR_ERR(tsm_dev);
+
+ return devm_add_action_or_reset(&adev->dev, cca_link_tsm_remove,
+ tsm_dev);
+}
+
+static const struct auxiliary_device_id cca_link_tsm_id_table[] = {
+ { .name = KBUILD_MODNAME "." RMI_DEV_NAME },
+ {}
+};
+MODULE_DEVICE_TABLE(auxiliary, cca_link_tsm_id_table);
+
+static struct auxiliary_driver cca_link_tsm_driver = {
+ .probe = cca_link_tsm_probe,
+ .id_table = cca_link_tsm_id_table,
+};
+module_auxiliary_driver(cca_link_tsm_driver);
+MODULE_IMPORT_NS("PCI_IDE");
+MODULE_AUTHOR("Aneesh Kumar <aneesh.kumar@kernel.org>");
+MODULE_DESCRIPTION("ARM CCA Host TSM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
new file mode 100644
index 000000000000..a23955f84e4f
--- /dev/null
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+
+#ifndef _VIRT_COCO_RMM_DA_H_
+#define _VIRT_COCO_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_pf0_dsc - Device Security Context for physical function 0.
+ * @pci: Physical Function 0 TDISP link context
+ * @sel_stream: Selective IDE Stream descriptor
+ */
+struct cca_host_pf0_dsc {
+ struct pci_tsm_pf0 pci;
+ struct pci_ide *sel_stream;
+};
+
+struct cca_host_fn_dsc {
+ struct pci_tsm pci;
+};
+
+static inline struct cca_host_pf0_dsc *to_cca_pf0_dsc(struct pci_dev *pdev)
+{
+ struct pci_tsm *tsm = pdev->tsm;
+
+ if (!tsm || !is_pci_tsm_pf0(pdev))
+ return NULL;
+
+ return container_of(tsm, struct cca_host_pf0_dsc, pci.base_tsm);
+}
+
+static inline struct cca_host_fn_dsc *to_cca_fn_dsc(struct pci_dev *pdev)
+{
+ struct pci_tsm *tsm = pdev->tsm;
+
+ return container_of(tsm, struct cca_host_fn_dsc, pci);
+}
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v3 03/10] coco: host: arm64: Build and register RMM pdev descriptors
2026-03-12 8:01 [RFC PATCH v3 00/10] coco/TSM: Host-side Arm CCA IDE setup via connect/disconnect callbacks Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 01/10] KVM: arm64: RMI: Add and export kvm_has_da_feature helper Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 02/10] coco: host: arm64: Add host TSM callback and IDE stream allocation support Aneesh Kumar K.V (Arm)
@ 2026-03-12 8:01 ` Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 04/10] coco: host: arm64: Add RMM device communication helpers Aneesh Kumar K.V (Arm)
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:01 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
Add the SMCCC plumbing for RMI_PDEV_AUX_COUNT, RMI_PDEV_CREATE, and
RMI_PDEV_GET_STATE, describe the pdev state enum/flags in rmi_smc.h,
and extend the PF0 descriptor so we can hold the RMM-side pdev handle
plus its auxiliary granules.
Implement pdev_create() to delegate backing pages, populate the pdev
parameters from the device's RID, ECAM window, IDE stream, and
non-coherent address ranges, and invoke RMI_PDEV_CREATE. The helper
keeps track of the allocated/assigned granules and unwinds them on
failure, so the host driver can reliably establish the pdev channel
before kicking off further IDE/TSM setup.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 31 +++++
arch/arm64/include/asm/rmi_smc.h | 95 ++++++++++++++-
drivers/virt/coco/arm-cca-host/Makefile | 2 +-
drivers/virt/coco/arm-cca-host/rmi-da.c | 156 ++++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 8 ++
5 files changed, 290 insertions(+), 2 deletions(-)
create mode 100644 drivers/virt/coco/arm-cca-host/rmi-da.c
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index ef53147c1984..4547ce0901a6 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -505,4 +505,35 @@ static inline int rmi_rtt_unmap_unprotected(unsigned long rd,
return res.a0;
}
+static inline unsigned long rmi_pdev_aux_count(unsigned long flags, u64 *aux_count)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_PDEV_AUX_COUNT, flags, &res);
+
+ *aux_count = res.a1;
+ return res.a0;
+}
+
+static inline unsigned long rmi_pdev_create(unsigned long pdev_phys,
+ unsigned long pdev_params_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_PDEV_CREATE,
+ pdev_phys, pdev_params_phys, &res);
+
+ return res.a0;
+}
+
+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 /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 9f8b6aa5ea06..58106740c1f7 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -26,7 +26,7 @@
#define SMC_RMI_DATA_CREATE SMC_RMI_CALL(0x0153)
#define SMC_RMI_DATA_CREATE_UNKNOWN SMC_RMI_CALL(0x0154)
#define SMC_RMI_DATA_DESTROY SMC_RMI_CALL(0x0155)
-
+#define SMC_RMI_PDEV_AUX_COUNT SMC_RMI_CALL(0x0156)
#define SMC_RMI_REALM_ACTIVATE SMC_RMI_CALL(0x0157)
#define SMC_RMI_REALM_CREATE SMC_RMI_CALL(0x0158)
#define SMC_RMI_REALM_DESTROY SMC_RMI_CALL(0x0159)
@@ -47,6 +47,9 @@
#define SMC_RMI_RTT_INIT_RIPAS SMC_RMI_CALL(0x0168)
#define SMC_RMI_RTT_SET_RIPAS SMC_RMI_CALL(0x0169)
+#define SMC_RMI_PDEV_CREATE SMC_RMI_CALL(0x0176)
+#define SMC_RMI_PDEV_GET_STATE SMC_RMI_CALL(0x0178)
+
#define RMI_ABI_MAJOR_VERSION 1
#define RMI_ABI_MINOR_VERSION 0
@@ -269,4 +272,94 @@ struct rec_run {
struct rec_exit exit;
};
+enum rmi_pdev_state {
+ RMI_PDEV_NEW,
+ RMI_PDEV_NEEDS_KEY,
+ RMI_PDEV_HAS_KEY,
+ RMI_PDEV_READY,
+ RMI_PDEV_IDE_RESETTING,
+ RMI_PDEV_COMMUNICATING,
+ RMI_PDEV_STOPPING,
+ RMI_PDEV_STOPPED,
+ RMI_PDEV_ERROR,
+};
+
+#define MAX_PDEV_AUX_GRANULES 32
+#define MAX_IOCOH_ADDR_RANGE 16
+#define MAX_FCOH_ADDR_RANGE 4
+
+#define RMI_PDEV_FLAGS_SPDM BIT(0)
+#define RMI_PDEV_FLAGS_NCOH_IDE BIT(1)
+#define RMI_PDEV_FLAGS_NCOH_ADDR BIT(2)
+#define RMI_PDEV_FLAGS_COH_IDE BIT(3)
+#define RMI_PDEV_FLAGS_COH_ADDR BIT(4)
+#define RMI_PDEV_FLAGS_P2P BIT(5)
+#define RMI_PDEV_FLAGS_COMP_TRUST BIT(6)
+#define RMI_PDEV_FLAGS_CATEGORY_MASK GENMASK(8, 7)
+#define RMI_PDEV_FLAGS_CATEGORY_SHIFT 7
+
+#define RMI_PDEV_FLAGS_CATEGORY_CMEM_CXL 0x1
+
+#define RMI_HASH_SHA_256 0
+#define RMI_HASH_SHA_512 1
+
+struct rmi_pdev_addr_range {
+ u64 base;
+ u64 top;
+};
+
+struct rmi_pdev_params {
+ union {
+ struct {
+ u64 flags;
+ u64 pdev_id;
+ union {
+ u8 segment_id;
+ u64 padding0;
+ };
+ u64 ecam_addr;
+ union {
+ u16 root_id;
+ u64 padding1;
+ };
+ u64 cert_id;
+ union {
+ u16 rid_base;
+ u64 padding2;
+ };
+ union {
+ u16 rid_top;
+ u64 padding3;
+ };
+ union {
+ u8 hash_algo;
+ u64 padding4;
+ };
+ u64 num_aux;
+ u64 ncoh_ide_sid;
+ u64 ncoh_num_addr_range;
+ u64 coh_num_addr_range;
+ };
+ u8 padding5[0x100];
+ };
+
+ union { /* 0x100 */
+ u64 aux_granule[MAX_PDEV_AUX_GRANULES];
+ u8 padding6[0x100];
+ };
+
+ union { /* 0x200 */
+ struct {
+ struct rmi_pdev_addr_range ncoh_addr_range[MAX_IOCOH_ADDR_RANGE];
+ };
+ u8 padding7[0x100];
+ };
+ union { /* 0x300 */
+ struct {
+ struct rmi_pdev_addr_range coh_addr_range[MAX_FCOH_ADDR_RANGE];
+ };
+ u8 padding8[0x100];
+ };
+};
+
#endif /* __ASM_RMI_SMC_H */
diff --git a/drivers/virt/coco/arm-cca-host/Makefile b/drivers/virt/coco/arm-cca-host/Makefile
index c236827f002c..d48e8940af46 100644
--- a/drivers/virt/coco/arm-cca-host/Makefile
+++ b/drivers/virt/coco/arm-cca-host/Makefile
@@ -2,4 +2,4 @@
#
obj-$(CONFIG_ARM_CCA_HOST) += arm-cca-host.o
-arm-cca-host-y += arm-cca.o
+arm-cca-host-y += arm-cca.o rmi-da.o
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
new file mode 100644
index 000000000000..89b61ad5bc00
--- /dev/null
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+
+#include <linux/pci.h>
+#include <linux/pci-ecam.h>
+#include <asm/rmi_cmds.h>
+
+#include "rmi-da.h"
+
+static int pci_dev_addr_range(struct pci_dev *pdev,
+ struct rmi_pdev_addr_range *pdev_addr,
+ struct pci_ide_partner *partner)
+{
+ 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)) {
+ pdev_addr[naddr].base = mem->start;
+ pdev_addr[naddr].top = mem->end + 1;
+ naddr++;
+ }
+ if (resource_assigned(pref)) {
+ pdev_addr[naddr].base = pref->start;
+ pdev_addr[naddr].top = pref->end + 1;
+ naddr++;
+ }
+ return naddr;
+}
+
+static void free_aux_pages(int cnt, void *aux[])
+{
+ int ret;
+
+ while (cnt--) {
+ ret = rmi_granule_undelegate(virt_to_phys(aux[cnt]));
+ if (!ret)
+ free_page((unsigned long)aux[cnt]);
+ }
+}
+
+static int init_pdev_params(struct pci_dev *pdev, struct rmi_pdev_params *params)
+{
+ int rid, ret, i;
+ phys_addr_t aux_phys;
+ struct pci_config_window *cfg = pdev->bus->sysdata;
+ struct cca_host_pf0_dsc *pf0_dsc = to_cca_pf0_dsc(pdev);
+ struct pci_ide *ide = pf0_dsc->sel_stream;
+
+ /* assign the ep device with RMM */
+ rid = pci_dev_id(pdev);
+ params->pdev_id = rid;
+ /* slot number for certificate chain */
+ params->cert_id = 0;
+ /* io coherent spdm/ide and non p2p */
+ params->flags = RMI_PDEV_FLAGS_SPDM | RMI_PDEV_FLAGS_NCOH_IDE |
+ RMI_PDEV_FLAGS_NCOH_ADDR;
+ params->ncoh_ide_sid = ide->stream_id;
+ params->hash_algo = RMI_HASH_SHA_256;
+ /* use the rid and MMIO resources of the end point pdev */
+ params->rid_base = rid;
+ params->rid_top = params->rid_base + 1;
+ params->ecam_addr = cfg->res.start;
+ params->root_id = pci_dev_id(pcie_find_root_port(pdev));
+
+ params->ncoh_num_addr_range = pci_dev_addr_range(pdev,
+ params->ncoh_addr_range,
+ &ide->partner[PCI_IDE_RP]);
+
+ ret = rmi_pdev_aux_count(params->flags, ¶ms->num_aux);
+ if (ret)
+ return -EIO;
+
+ pf0_dsc->num_aux = params->num_aux;
+ for (i = 0; i < params->num_aux; i++) {
+ void *aux = (void *)__get_free_page(GFP_KERNEL);
+
+ if (!aux) {
+ ret = -ENOMEM;
+ goto err_free_aux;
+ }
+
+ aux_phys = virt_to_phys(aux);
+ if (rmi_granule_delegate(aux_phys)) {
+ ret = -EIO;
+ free_page((unsigned long)aux);
+ goto err_free_aux;
+ }
+ params->aux_granule[i] = aux_phys;
+ pf0_dsc->aux[i] = aux;
+ }
+ return 0;
+
+err_free_aux:
+ free_aux_pages(i, pf0_dsc->aux);
+ return ret;
+}
+
+int cca_pdev_create(struct pci_dev *pci_dev)
+{
+ int ret;
+ void *rmm_pdev;
+ bool should_free = true;
+ phys_addr_t rmm_pdev_phys;
+ struct rmi_pdev_params *params;
+ struct cca_host_pf0_dsc *pf0_dsc = to_cca_pf0_dsc(pci_dev);
+
+ rmm_pdev = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!rmm_pdev)
+ return -ENOMEM;
+
+ rmm_pdev_phys = virt_to_phys(rmm_pdev);
+ if (rmi_granule_delegate(rmm_pdev_phys)) {
+ ret = -EIO;
+ goto err_granule_delegate;
+ }
+
+ params = (struct rmi_pdev_params *)get_zeroed_page(GFP_KERNEL);
+ if (!params) {
+ ret = -ENOMEM;
+ goto err_param_alloc;
+ }
+
+ ret = init_pdev_params(pci_dev, params);
+ if (ret)
+ goto err_init_pdev_params;
+
+ if (rmi_pdev_create(rmm_pdev_phys, virt_to_phys(params))) {
+ ret = -EIO;
+ goto err_pdev_create;
+ }
+
+ pf0_dsc->rmm_pdev = rmm_pdev;
+ free_page((unsigned long)params);
+ return 0;
+
+err_pdev_create:
+ free_aux_pages(pf0_dsc->num_aux, pf0_dsc->aux);
+err_init_pdev_params:
+ free_page((unsigned long)params);
+err_param_alloc:
+ if (rmi_granule_undelegate(rmm_pdev_phys))
+ should_free = false;
+err_granule_delegate:
+ if (should_free)
+ free_page((unsigned long)rmm_pdev);
+ return ret;
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index a23955f84e4f..229f3ff6dc6f 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -15,10 +15,17 @@
* struct cca_host_pf0_dsc - Device Security Context for physical function 0.
* @pci: Physical Function 0 TDISP link context
* @sel_stream: Selective IDE Stream descriptor
+ * @rmm_pdev: Delegated granule address of rmm pdev object
+ * @num_ax: Number of auxiliary granules allocated for pdev
+ * @aux: Delegated auxiliary granules
*/
struct cca_host_pf0_dsc {
struct pci_tsm_pf0 pci;
struct pci_ide *sel_stream;
+
+ void *rmm_pdev;
+ int num_aux;
+ void *aux[MAX_PDEV_AUX_GRANULES];
};
struct cca_host_fn_dsc {
@@ -42,4 +49,5 @@ 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);
}
+int cca_pdev_create(struct pci_dev *pdev);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v3 04/10] coco: host: arm64: Add RMM device communication helpers
2026-03-12 8:01 [RFC PATCH v3 00/10] coco/TSM: Host-side Arm CCA IDE setup via connect/disconnect callbacks Aneesh Kumar K.V (Arm)
` (2 preceding siblings ...)
2026-03-12 8:01 ` [RFC PATCH v3 03/10] coco: host: arm64: Build and register RMM pdev descriptors Aneesh Kumar K.V (Arm)
@ 2026-03-12 8:01 ` Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 05/10] coco: host: arm64: Add helper to stop and tear down an RMM pdev Aneesh Kumar K.V (Arm)
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:01 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
- add SMCCC IDs/wrappers for RMI_PDEV_COMMUNICATE/RMI_PDEV_ABORT
- describe the RMM device-communication ABI (struct rmi_dev_comm_*,
cache flags, protocol/object IDs, busy error code)
- track per-PF0 communication state (buffers, workqueue, cache metadata) and
serialize access behind object_lock
- plumb a DOE/SPDM worker (pdev_communicate_work) plus shared helpers that
submit the SMCCC call, cache multi-part responses, and handle retries/abort
- hook the new helpers into the physical function connect path so IDE
setup can drive the device to the expected state
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 20 ++
arch/arm64/include/asm/rmi_smc.h | 63 +++++
drivers/virt/coco/arm-cca-host/arm-cca.c | 50 ++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 281 +++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 66 ++++++
5 files changed, 480 insertions(+)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 4547ce0901a6..b86bf15afcda 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -536,4 +536,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 /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 58106740c1f7..c91cd0e389a9 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -47,6 +47,8 @@
#define SMC_RMI_RTT_INIT_RIPAS SMC_RMI_CALL(0x0168)
#define SMC_RMI_RTT_SET_RIPAS SMC_RMI_CALL(0x0169)
+#define SMC_RMI_PDEV_ABORT SMC_RMI_CALL(0x0174)
+#define SMC_RMI_PDEV_COMMUNICATE SMC_RMI_CALL(0x0175)
#define SMC_RMI_PDEV_CREATE SMC_RMI_CALL(0x0176)
#define SMC_RMI_PDEV_GET_STATE SMC_RMI_CALL(0x0178)
@@ -69,6 +71,7 @@
#define RMI_ERROR_REALM 2
#define RMI_ERROR_REC 3
#define RMI_ERROR_RTT 4
+#define RMI_BUSY 10
enum rmi_ripas {
RMI_EMPTY = 0,
@@ -362,4 +365,64 @@ struct rmi_pdev_params {
};
};
+#define RMI_DEV_COMM_EXIT_CACHE_REQ BIT(0)
+#define RMI_DEV_COMM_EXIT_CACHE_RSP BIT(1)
+#define RMI_DEV_COMM_EXIT_SEND BIT(2)
+#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_NONE 0
+#define RMI_DEV_COMM_RESPONSE 1
+#define RMI_DEV_COMM_ERROR 2
+
+#define RMI_PROTOCOL_SPDM 0
+#define RMI_PROTOCOL_SECURE_SPDM 1
+
+#define RMI_DEV_VCA 0
+#define RMI_DEV_CERTIFICATE 1
+#define RMI_DEV_MEASUREMENTS 2
+#define RMI_DEV_INTERFACE_REPORT 3
+
+struct rmi_dev_comm_enter {
+ union {
+ u8 status;
+ u64 padding0;
+ };
+ u64 req_addr;
+ u64 resp_addr;
+ u64 resp_len;
+};
+
+struct rmi_dev_comm_exit {
+ u64 flags;
+ u64 req_cache_offset;
+ u64 req_cache_len;
+ u64 rsp_cache_offset;
+ u64 rsp_cache_len;
+ union {
+ u8 cache_obj_id;
+ u64 padding0;
+ };
+
+ union {
+ u8 protocol;
+ u64 padding1;
+ };
+ u64 req_delay;
+ u64 req_len;
+ u64 rsp_timeout;
+};
+
+struct rmi_dev_comm_data {
+ union { /* 0x0 */
+ struct rmi_dev_comm_enter enter;
+ u8 padding0[0x800];
+ };
+ union { /* 0x800 */
+ struct rmi_dev_comm_exit exit;
+ u8 padding1[0x800];
+ };
+};
+
#endif /* __ASM_RMI_SMC_H */
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index 639ebd82978a..4ed5e8ec9e91 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -47,6 +47,7 @@ static struct pci_tsm *cca_tsm_pci_probe(struct tsm_dev *tsm_dev, struct pci_dev
rc = pci_tsm_pf0_constructor(pdev, &pf0_dsc->pci, tsm_dev);
if (rc)
return NULL;
+ mutex_init(&pf0_dsc->object_lock);
pci_dbg(pdev, "tsm enabled\n");
return &no_free_ptr(pf0_dsc)->pci.base_tsm;
@@ -66,6 +67,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);
+}
+
/* For now global for simplicity. Protected by pci_tsm_rwsem */
static DECLARE_BITMAP(cca_stream_ids, MAX_STREAM_ID);
static int alloc_stream_id(struct pci_host_bridge *hb)
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 89b61ad5bc00..93512f7e73d5 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -5,6 +5,8 @@
#include <linux/pci.h>
#include <linux/pci-ecam.h>
+#include <linux/pci-doe.h>
+#include <linux/delay.h>
#include <asm/rmi_cmds.h>
#include "rmi-da.h"
@@ -154,3 +156,282 @@ int cca_pdev_create(struct pci_dev *pci_dev)
free_page((unsigned long)rmm_pdev);
return ret;
}
+
+static int doe_send_req_resp(struct pci_tsm *tsm)
+{
+ int data_obj_type;
+ struct cca_host_comm_data *comm_data = to_cca_comm_data(tsm->pdev);
+ struct rmi_dev_comm_exit *io_exit = &comm_data->io_params->exit;
+ u8 protocol = io_exit->protocol;
+
+ if (protocol == RMI_PROTOCOL_SPDM)
+ data_obj_type = PCI_DOE_FEATURE_CMA;
+ else if (protocol == RMI_PROTOCOL_SECURE_SPDM)
+ data_obj_type = PCI_DOE_FEATURE_SSESSION;
+ else
+ return -EINVAL;
+
+ /* delay the send */
+ if (io_exit->req_delay)
+ fsleep(io_exit->req_delay);
+
+ return pci_tsm_doe_transfer(tsm->dsm_dev, data_obj_type,
+ comm_data->req_buff, io_exit->req_len,
+ comm_data->rsp_buff, PAGE_SIZE);
+}
+
+static inline bool pending_dev_communicate(struct rmi_dev_comm_exit *io_exit)
+{
+ bool pending = io_exit->flags & (RMI_DEV_COMM_EXIT_CACHE_REQ |
+ RMI_DEV_COMM_EXIT_CACHE_RSP |
+ RMI_DEV_COMM_EXIT_SEND |
+ RMI_DEV_COMM_EXIT_WAIT |
+ RMI_DEV_COMM_EXIT_MULTI);
+ return pending;
+}
+
+static inline gfp_t cache_obj_id_to_gfp_flags(u8 cache_obj_id)
+{
+ /* These two cache objects are system objects. */
+ if (cache_obj_id == RMI_DEV_VCA || cache_obj_id == RMI_DEV_CERTIFICATE)
+ return GFP_KERNEL;
+ /* rest are per TDI which is associated to a VM */
+ return GFP_KERNEL_ACCOUNT;
+}
+
+static int _do_dev_communicate(enum dev_comm_type type, struct pci_tsm *tsm)
+{
+ unsigned long rmi_ret;
+ gfp_t cache_alloc_flags;
+ int nbytes, cp_len;
+ struct cache_object **cache_objp, *cache_obj;
+ struct cca_host_pf0_dsc *pf0_dsc = to_cca_pf0_dsc(tsm->dsm_dev);
+ struct cca_host_comm_data *comm_data = to_cca_comm_data(tsm->pdev);
+ struct rmi_dev_comm_enter *io_enter = &comm_data->io_params->enter;
+ struct rmi_dev_comm_exit *io_exit = &comm_data->io_params->exit;
+
+redo_communicate:
+
+ if (type == PDEV_COMMUNICATE)
+ rmi_ret = rmi_pdev_communicate(virt_to_phys(pf0_dsc->rmm_pdev),
+ virt_to_phys(comm_data->io_params));
+ else
+ rmi_ret = RMI_ERROR_INPUT;
+ if (rmi_ret != RMI_SUCCESS) {
+ if (rmi_ret == RMI_BUSY)
+ return -EBUSY;
+ return -EIO;
+ }
+
+ if (io_exit->flags & RMI_DEV_COMM_EXIT_CACHE_REQ ||
+ io_exit->flags & RMI_DEV_COMM_EXIT_CACHE_RSP) {
+
+ switch (io_exit->cache_obj_id) {
+ case RMI_DEV_VCA:
+ cache_objp = &pf0_dsc->vca;
+ break;
+ case RMI_DEV_CERTIFICATE:
+ cache_objp = &pf0_dsc->cert_chain.cache;
+ break;
+ default:
+ return -EINVAL;
+ }
+ cache_obj = *cache_objp;
+ cache_alloc_flags = cache_obj_id_to_gfp_flags(io_exit->cache_obj_id);
+ }
+
+ if (io_exit->flags & RMI_DEV_COMM_EXIT_CACHE_REQ)
+ cp_len = io_exit->req_cache_len;
+ else
+ cp_len = io_exit->rsp_cache_len;
+
+ /* response and request len should be <= SZ_4k */
+ if (cp_len > CACHE_CHUNK_SIZE)
+ return -EINVAL;
+
+ if (io_exit->flags & RMI_DEV_COMM_EXIT_CACHE_REQ ||
+ io_exit->flags & RMI_DEV_COMM_EXIT_CACHE_RSP) {
+ int cache_remaining;
+
+ /* new allocation */
+ if (!cache_obj) {
+ int obj_size = struct_size(cache_obj, buf,
+ CACHE_CHUNK_SIZE);
+
+ cache_obj = kvmalloc(obj_size, cache_alloc_flags);
+ if (!cache_obj)
+ return -ENOMEM;
+
+ cache_obj->size = CACHE_CHUNK_SIZE;
+ cache_obj->offset = 0;
+ *cache_objp = cache_obj;
+ }
+
+ cache_remaining = cache_obj->size - cache_obj->offset;
+ if (cp_len > cache_remaining) {
+ struct cache_object *new_obj;
+ int new_size = struct_size(cache_obj, buf,
+ cache_obj->size +
+ CACHE_CHUNK_SIZE);
+
+ if (cache_obj->size + CACHE_CHUNK_SIZE > MAX_CACHE_OBJ_SIZE)
+ return -EINVAL;
+
+ new_obj = kvrealloc(cache_obj, new_size, cache_alloc_flags);
+ if (!new_obj)
+ return -ENOMEM;
+ new_obj->size = cache_obj->size + CACHE_CHUNK_SIZE;
+ *cache_objp = new_obj;
+ }
+
+ /* cache object can change above. */
+ cache_obj = *cache_objp;
+ }
+
+
+ if (io_exit->flags & RMI_DEV_COMM_EXIT_CACHE_REQ) {
+ memcpy(cache_obj->buf + cache_obj->offset,
+ (comm_data->req_buff + io_exit->req_cache_offset), io_exit->req_cache_len);
+ cache_obj->offset += io_exit->req_cache_len;
+ }
+
+ if (io_exit->flags & RMI_DEV_COMM_EXIT_CACHE_RSP) {
+ memcpy(cache_obj->buf + cache_obj->offset,
+ (comm_data->rsp_buff + io_exit->rsp_cache_offset), io_exit->rsp_cache_len);
+ cache_obj->offset += io_exit->rsp_cache_len;
+ }
+
+ /*
+ * wait for last packet request from RMM.
+ * We should not find this because our device communication is synchronous
+ */
+ if (io_exit->flags & RMI_DEV_COMM_EXIT_WAIT)
+ return -EIO;
+
+ /* next packet to send */
+ if (io_exit->flags & RMI_DEV_COMM_EXIT_SEND) {
+ nbytes = doe_send_req_resp(tsm);
+ if (nbytes < 0) {
+ /* report error back to RMM */
+ io_enter->status = RMI_DEV_COMM_ERROR;
+ } else {
+ /* send response back to RMM */
+ io_enter->resp_len = nbytes;
+ io_enter->status = RMI_DEV_COMM_RESPONSE;
+ }
+ } else {
+ /* no data transmitted => no data received */
+ io_enter->resp_len = 0;
+ io_enter->status = RMI_DEV_COMM_NONE;
+ }
+
+ if (pending_dev_communicate(io_exit))
+ goto redo_communicate;
+
+ return 0;
+}
+
+static int do_dev_communicate(enum dev_comm_type type,
+ struct pci_tsm *tsm, unsigned long error_state)
+{
+ int ret, state = error_state;
+ struct rmi_dev_comm_enter *io_enter;
+ struct cca_host_pf0_dsc *pf0_dsc = to_cca_pf0_dsc(tsm->dsm_dev);
+
+ io_enter = &pf0_dsc->comm_data.io_params->enter;
+ io_enter->resp_len = 0;
+ io_enter->status = RMI_DEV_COMM_NONE;
+
+ ret = _do_dev_communicate(type, tsm);
+ if (ret) {
+ if (type == PDEV_COMMUNICATE)
+ rmi_pdev_abort(virt_to_phys(pf0_dsc->rmm_pdev));
+ } else {
+ /*
+ * Some device communication error will transition the
+ * device to error state. Report that.
+ */
+ if (type == PDEV_COMMUNICATE) {
+ if (rmi_pdev_get_state(virt_to_phys(pf0_dsc->rmm_pdev),
+ (enum rmi_pdev_state *)&state))
+ state = error_state;
+ }
+ }
+
+ if (state == error_state)
+ pci_err(tsm->pdev, "device communication error\n");
+
+ return state;
+}
+
+static int wait_for_dev_state(enum dev_comm_type type, struct pci_tsm *tsm,
+ unsigned long target_state,
+ unsigned long error_state)
+{
+ int state;
+
+ do {
+ state = do_dev_communicate(type, tsm, error_state);
+
+ if (state == target_state || state == error_state)
+ return state;
+ } while (1);
+
+ /* can't reach */
+ return error_state;
+}
+
+static int wait_for_pdev_state(struct pci_tsm *tsm, enum rmi_pdev_state target_state)
+{
+ return wait_for_dev_state(PDEV_COMMUNICATE, tsm, target_state, RMI_PDEV_ERROR);
+}
+
+static void pdev_state_transition_workfn(struct work_struct *work)
+{
+ unsigned long state;
+ struct pci_tsm *tsm;
+ struct dev_comm_work *setup_work;
+ struct cca_host_pf0_dsc *pf0_dsc;
+
+ setup_work = container_of(work, struct dev_comm_work, work);
+ tsm = setup_work->tsm;
+ pf0_dsc = to_cca_pf0_dsc(tsm->dsm_dev);
+
+ guard(mutex)(&pf0_dsc->object_lock);
+ state = wait_for_pdev_state(tsm, setup_work->target_state);
+ WARN_ON(state != setup_work->target_state);
+
+ complete(&setup_work->complete);
+}
+
+static int submit_pdev_state_transition_work(struct pci_dev *pdev, int target_state)
+{
+ enum rmi_pdev_state state;
+ struct dev_comm_work comm_work;
+ struct cca_host_pf0_dsc *pf0_dsc = to_cca_pf0_dsc(pdev);
+ struct cca_host_comm_data *comm_data = to_cca_comm_data(pdev);
+
+ INIT_WORK_ONSTACK(&comm_work.work, pdev_state_transition_workfn);
+ init_completion(&comm_work.complete);
+ comm_work.tsm = pdev->tsm;
+ comm_work.target_state = target_state;
+
+ queue_work(comm_data->work_queue, &comm_work.work);
+
+ wait_for_completion(&comm_work.complete);
+ destroy_work_on_stack(&comm_work.work);
+
+ /* check if we reached target state */
+ if (rmi_pdev_get_state(virt_to_phys(pf0_dsc->rmm_pdev), &state))
+ return -EIO;
+
+ if (state != target_state)
+ /* no specific error for this */
+ return -1;
+ return 0;
+}
+
+int cca_pdev_ide_setup(struct pci_dev *pdev)
+{
+ return submit_pdev_state_transition_work(pdev, RMI_PDEV_NEEDS_KEY);
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 229f3ff6dc6f..db4bf893f596 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -9,29 +9,79 @@
#include <linux/pci.h>
#include <linux/pci-ide.h>
#include <linux/pci-tsm.h>
+#include <linux/sizes.h>
#include <asm/rmi_smc.h>
+#define MAX_CACHE_OBJ_SIZE SZ_16M
+#define CACHE_CHUNK_SIZE SZ_4K
+struct cache_object {
+ int size;
+ int offset;
+ u8 buf[] __counted_by(size);
+};
+
+struct dev_comm_work {
+ struct pci_tsm *tsm;
+ int target_state;
+ struct work_struct work;
+ struct completion complete;
+};
+
+struct cca_host_comm_data {
+ void *rsp_buff;
+ void *req_buff;
+ struct rmi_dev_comm_data *io_params;
+ /*
+ * Only one device communication request can be active at
+ * a time. This limitation comes from using the DOE mailbox
+ * at the pdev level. Requests such as get_measurements may
+ * span multiple mailbox messages, which must not be
+ * interleaved with other SPDM requests.
+ */
+ struct workqueue_struct *work_queue;
+};
+
/**
* struct cca_host_pf0_dsc - Device Security Context for physical function 0.
+ * @comm_data: Device communication context
* @pci: Physical Function 0 TDISP link context
* @sel_stream: Selective IDE Stream descriptor
* @rmm_pdev: Delegated granule address of rmm pdev object
* @num_ax: Number of auxiliary granules allocated for pdev
* @aux: Delegated auxiliary granules
+ * @object_lock: lock used to protect access to cached obects in PF0 and TDIs
+ * @cert_chain: cetrificate chain
+ * @vca: SPDM's Version-Capabilities-Algorithms cache object
*/
struct cca_host_pf0_dsc {
+ struct cca_host_comm_data comm_data;
struct pci_tsm_pf0 pci;
struct pci_ide *sel_stream;
void *rmm_pdev;
int num_aux;
void *aux[MAX_PDEV_AUX_GRANULES];
+
+ struct mutex object_lock;
+ struct {
+ struct cache_object *cache;
+
+ void *public_key;
+ size_t public_key_size;
+
+ bool valid;
+ } cert_chain;
+ struct cache_object *vca;
};
struct cca_host_fn_dsc {
struct pci_tsm pci;
};
+enum dev_comm_type {
+ PDEV_COMMUNICATE = 0x1,
+};
+
static inline struct cca_host_pf0_dsc *to_cca_pf0_dsc(struct pci_dev *pdev)
{
struct pci_tsm *tsm = pdev->tsm;
@@ -49,5 +99,21 @@ 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_comm_data *to_cca_comm_data(struct pci_dev *pdev)
+{
+ struct cca_host_pf0_dsc *pf0_dsc;
+
+ pf0_dsc = to_cca_pf0_dsc(pdev);
+ if (pf0_dsc)
+ return &pf0_dsc->comm_data;
+
+ pf0_dsc = to_cca_pf0_dsc(pdev->tsm->dsm_dev);
+ if (pf0_dsc)
+ return &pf0_dsc->comm_data;
+
+ return NULL;
+}
+
int cca_pdev_create(struct pci_dev *pdev);
+int cca_pdev_ide_setup(struct pci_dev *pdev);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v3 05/10] coco: host: arm64: Add helper to stop and tear down an RMM pdev
2026-03-12 8:01 [RFC PATCH v3 00/10] coco/TSM: Host-side Arm CCA IDE setup via connect/disconnect callbacks Aneesh Kumar K.V (Arm)
` (3 preceding siblings ...)
2026-03-12 8:01 ` [RFC PATCH v3 04/10] coco: host: arm64: Add RMM device communication helpers Aneesh Kumar K.V (Arm)
@ 2026-03-12 8:01 ` Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 06/10] coco: host: arm64: Instantiate RMM pdev during device connect Aneesh Kumar K.V (Arm)
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:01 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price, Jonathan Cameron
Add helper to stop and tear down an RMM pdev
- describe the RMI_PDEV_STOP/RMI_PDEV_DESTROY SMC IDs and provide
wrappers in rmi_cmds.h
- implement pdev_stop_and_destroy() so the host driver stops the pdev,
waits for it to reach RMI_PDEV_STOPPED, destroys it, frees auxiliary
granules, and drops the delegated page
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 18 +++++++++++++++
arch/arm64/include/asm/rmi_smc.h | 2 ++
drivers/virt/coco/arm-cca-host/rmi-da.c | 30 +++++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 1 +
4 files changed, 51 insertions(+)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index b86bf15afcda..f10a0dcaa308 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -556,4 +556,22 @@ 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;
+}
+
+static inline unsigned long rmi_pdev_destroy(unsigned long pdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_PDEV_DESTROY, pdev_phys, &res);
+
+ return res.a0;
+}
+
#endif /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index c91cd0e389a9..3a57c7245029 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -50,7 +50,9 @@
#define SMC_RMI_PDEV_ABORT SMC_RMI_CALL(0x0174)
#define SMC_RMI_PDEV_COMMUNICATE SMC_RMI_CALL(0x0175)
#define SMC_RMI_PDEV_CREATE SMC_RMI_CALL(0x0176)
+#define SMC_RMI_PDEV_DESTROY SMC_RMI_CALL(0x0177)
#define SMC_RMI_PDEV_GET_STATE SMC_RMI_CALL(0x0178)
+#define SMC_RMI_PDEV_STOP SMC_RMI_CALL(0x017c)
#define RMI_ABI_MAJOR_VERSION 1
#define RMI_ABI_MINOR_VERSION 0
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 93512f7e73d5..ba6d67e5f54e 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -435,3 +435,33 @@ int cca_pdev_ide_setup(struct pci_dev *pdev)
{
return submit_pdev_state_transition_work(pdev, RMI_PDEV_NEEDS_KEY);
}
+
+void cca_pdev_stop_and_destroy(struct pci_dev *pdev)
+{
+ int ret;
+ struct cca_host_pf0_dsc *pf0_dsc = to_cca_pf0_dsc(pdev);
+ phys_addr_t rmm_pdev_phys = virt_to_phys(pf0_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;
+
+ if (WARN_ON(rmi_pdev_destroy(rmm_pdev_phys)))
+ return;
+
+ kfree(pf0_dsc->cert_chain.public_key);
+ kvfree(pf0_dsc->cert_chain.cache);
+ kvfree(pf0_dsc->vca);
+ pf0_dsc->cert_chain.cache = NULL;
+ pf0_dsc->vca = NULL;
+
+ /* Free the aux granules */
+ free_aux_pages(pf0_dsc->num_aux, pf0_dsc->aux);
+ pf0_dsc->num_aux = 0;
+ if (!rmi_granule_undelegate(rmm_pdev_phys))
+ free_page((unsigned long)pf0_dsc->rmm_pdev);
+ pf0_dsc->rmm_pdev = NULL;
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index db4bf893f596..fbfbcd40beb4 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -116,4 +116,5 @@ 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_ide_setup(struct pci_dev *pdev);
+void cca_pdev_stop_and_destroy(struct pci_dev *pdev);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v3 06/10] coco: host: arm64: Instantiate RMM pdev during device connect
2026-03-12 8:01 [RFC PATCH v3 00/10] coco/TSM: Host-side Arm CCA IDE setup via connect/disconnect callbacks Aneesh Kumar K.V (Arm)
` (4 preceding siblings ...)
2026-03-12 8:01 ` [RFC PATCH v3 05/10] coco: host: arm64: Add helper to stop and tear down an RMM pdev Aneesh Kumar K.V (Arm)
@ 2026-03-12 8:01 ` Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 07/10] X.509: Make certificate parser public Aneesh Kumar K.V (Arm)
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:01 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price, Jonathan Cameron
An RMM pdev object represents a communication channel between the RMM
and a physical device, for example a PCIe device. With the required
helpers now in place, update the connect callback to create an RMM pdev
object.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/virt/coco/arm-cca-host/arm-cca.c | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index 4ed5e8ec9e91..987c1be566ba 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -67,8 +67,8 @@ 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)
+static int init_dev_communication_buffers(struct pci_dev *pdev,
+ struct cca_host_comm_data *comm_data)
{
int ret = -ENOMEM;
@@ -172,6 +172,16 @@ static int cca_tsm_connect(struct pci_dev *pdev)
if (rc)
goto err_tsm;
+ rc = init_dev_communication_buffers(pdev, &pf0_dsc->comm_data);
+ if (rc)
+ goto err_comm_buff;
+ rc = cca_pdev_create(pdev);
+ if (rc)
+ goto err_pdev_create;
+
+ rc = cca_pdev_ide_setup(pdev);
+ if (rc)
+ goto err_ide_setup;
/*
* Once ide is setup, enable the stream at the endpoint
* Root port will be done by RMM
@@ -179,6 +189,12 @@ static int cca_tsm_connect(struct pci_dev *pdev)
pci_ide_stream_enable(pdev, ide);
return 0;
+err_ide_setup:
+ cca_pdev_stop_and_destroy(pdev);
+err_pdev_create:
+ free_dev_communication_buffers(&pf0_dsc->comm_data);
+err_comm_buff:
+ tsm_ide_stream_unregister(ide);
err_tsm:
pci_ide_stream_teardown(rp, ide);
pci_ide_stream_teardown(pdev, ide);
@@ -205,6 +221,9 @@ static void cca_tsm_disconnect(struct pci_dev *pdev)
ide = pf0_dsc->sel_stream;
stream_id = ide->stream_id;
+ cca_pdev_stop_and_destroy(pdev);
+ free_dev_communication_buffers(&pf0_dsc->comm_data);
+
pci_ide_stream_release(ide);
pf0_dsc->sel_stream = NULL;
clear_bit(stream_id, cca_stream_ids);
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v3 07/10] X.509: Make certificate parser public
2026-03-12 8:01 [RFC PATCH v3 00/10] coco/TSM: Host-side Arm CCA IDE setup via connect/disconnect callbacks Aneesh Kumar K.V (Arm)
` (5 preceding siblings ...)
2026-03-12 8:01 ` [RFC PATCH v3 06/10] coco: host: arm64: Instantiate RMM pdev during device connect Aneesh Kumar K.V (Arm)
@ 2026-03-12 8:01 ` Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 08/10] X.509: Parse Subject Alternative Name in certificates Aneesh Kumar K.V (Arm)
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:01 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Lukas Wunner, Dan Williams, Ilpo Järvinen,
Jonathan Cameron, Aneesh Kumar K . V
From: Lukas Wunner <lukas@wunner.de>
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.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
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
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
index b7aeebdddb36..39f1521b773d 100644
--- a/crypto/asymmetric_keys/x509_parser.h
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -5,51 +5,11 @@
* Written by David Howells (dhowells@redhat.com)
*/
-#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);
diff --git a/include/keys/x509-parser.h b/include/keys/x509-parser.h
new file mode 100644
index 000000000000..8b68e720693a
--- /dev/null
+++ b/include/keys/x509-parser.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* X.509 certificate parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifndef _KEYS_X509_PARSER_H
+#define _KEYS_X509_PARSER_H
+
+#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;
+};
+
+struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
+void x509_free_certificate(struct x509_certificate *cert);
+
+DEFINE_FREE(x509_free_certificate, struct x509_certificate *,
+ if (!IS_ERR(_T)) x509_free_certificate(_T))
+
+#endif /* _KEYS_X509_PARSER_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v3 08/10] X.509: Parse Subject Alternative Name in certificates
2026-03-12 8:01 [RFC PATCH v3 00/10] coco/TSM: Host-side Arm CCA IDE setup via connect/disconnect callbacks Aneesh Kumar K.V (Arm)
` (6 preceding siblings ...)
2026-03-12 8:01 ` [RFC PATCH v3 07/10] X.509: Make certificate parser public Aneesh Kumar K.V (Arm)
@ 2026-03-12 8:01 ` Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 09/10] X.509: Move certificate length retrieval into new helper Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 10/10] coco: host: arm64: Register device public key with RMM Aneesh Kumar K.V (Arm)
9 siblings, 0 replies; 11+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:01 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Lukas Wunner, Wilfred Mallawa, Ilpo Järvinen,
Jonathan Cameron, Dan Williams, Aneesh Kumar K . V
From: Lukas Wunner <lukas@wunner.de>
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.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Reviewed-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
crypto/asymmetric_keys/x509_cert_parser.c | 9 +++++++++
include/keys/x509-parser.h | 2 ++
2 files changed, 11 insertions(+)
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 37e4fb9da106..d81b7de4236c 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -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
diff --git a/include/keys/x509-parser.h b/include/keys/x509-parser.h
index 8b68e720693a..4e6a05a8c7a6 100644
--- a/include/keys/x509-parser.h
+++ b/include/keys/x509-parser.h
@@ -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;
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v3 09/10] X.509: Move certificate length retrieval into new helper
2026-03-12 8:01 [RFC PATCH v3 00/10] coco/TSM: Host-side Arm CCA IDE setup via connect/disconnect callbacks Aneesh Kumar K.V (Arm)
` (7 preceding siblings ...)
2026-03-12 8:01 ` [RFC PATCH v3 08/10] X.509: Parse Subject Alternative Name in certificates Aneesh Kumar K.V (Arm)
@ 2026-03-12 8:01 ` Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 10/10] coco: host: arm64: Register device public key with RMM Aneesh Kumar K.V (Arm)
9 siblings, 0 replies; 11+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:01 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Lukas Wunner, Dan Williams, Jonathan Cameron,
Aneesh Kumar K . V
From: Lukas Wunner <lukas@wunner.de>
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.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
crypto/asymmetric_keys/x509_loader.c | 38 +++++++++++++++++++---------
include/keys/asymmetric-type.h | 2 ++
2 files changed, 28 insertions(+), 12 deletions(-)
diff --git a/crypto/asymmetric_keys/x509_loader.c b/crypto/asymmetric_keys/x509_loader.c
index a41741326998..25ff027fad1d 100644
--- a/crypto/asymmetric_keys/x509_loader.c
+++ b/crypto/asymmetric_keys/x509_loader.c
@@ -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),
diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h
index 1b91c8f98688..301efa952e26 100644
--- a/include/keys/asymmetric-type.h
+++ b/include/keys/asymmetric-type.h
@@ -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);
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v3 10/10] coco: host: arm64: Register device public key with RMM
2026-03-12 8:01 [RFC PATCH v3 00/10] coco/TSM: Host-side Arm CCA IDE setup via connect/disconnect callbacks Aneesh Kumar K.V (Arm)
` (8 preceding siblings ...)
2026-03-12 8:01 ` [RFC PATCH v3 09/10] X.509: Move certificate length retrieval into new helper Aneesh Kumar K.V (Arm)
@ 2026-03-12 8:01 ` Aneesh Kumar K.V (Arm)
9 siblings, 0 replies; 11+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:01 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
- 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 IDE setup, extract the final
certificate’s public key, and recognise RSA-3072, ECDSA-P256, and
ECDSA-P384 keys before calling into the RMM.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 9 ++
arch/arm64/include/asm/rmi_smc.h | 18 +++
drivers/virt/coco/arm-cca-host/Kconfig | 4 +
drivers/virt/coco/arm-cca-host/rmi-da.c | 174 +++++++++++++++++++++++-
drivers/virt/coco/arm-cca-host/rmi-da.h | 2 +
5 files changed, 206 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index f10a0dcaa308..339bea517760 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -574,4 +574,13 @@ static inline unsigned long rmi_pdev_destroy(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 /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 3a57c7245029..907e00f4855a 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -52,6 +52,7 @@
#define SMC_RMI_PDEV_CREATE SMC_RMI_CALL(0x0176)
#define SMC_RMI_PDEV_DESTROY SMC_RMI_CALL(0x0177)
#define SMC_RMI_PDEV_GET_STATE SMC_RMI_CALL(0x0178)
+#define SMC_RMI_PDEV_SET_PUBKEY SMC_RMI_CALL(0x017b)
#define SMC_RMI_PDEV_STOP SMC_RMI_CALL(0x017c)
#define RMI_ABI_MAJOR_VERSION 1
@@ -427,4 +428,21 @@ struct rmi_dev_comm_data {
};
};
+#define RMI_SIG_RSASSA_3072 0
+#define RMI_SIG_ECDSA_P256 1
+#define RMI_SIG_ECDSA_P384 2
+
+struct rmi_public_key_params {
+ union {
+ struct {
+ u8 public_key[1024];
+ u8 metadata[1024];
+ u64 public_key_len;
+ u64 metadata_len;
+ u8 rmi_signature_algorithm;
+ } __packed;
+ u8 padding[0x1000];
+ };
+};
+
#endif /* __ASM_RMI_SMC_H */
diff --git a/drivers/virt/coco/arm-cca-host/Kconfig b/drivers/virt/coco/arm-cca-host/Kconfig
index efe40d61d5d8..c5076e2b4eb5 100644
--- a/drivers/virt/coco/arm-cca-host/Kconfig
+++ b/drivers/virt/coco/arm-cca-host/Kconfig
@@ -8,7 +8,11 @@ config ARM_CCA_HOST
depends on PCI
depends on KVM
select PCI_TSM
+ select KEYS
+ select X509_CERTIFICATE_PARSER
select AUXILIARY_BUS
+ select CRYPTO_ECDSA
+ select CRYPTO_RSA
help
ARM CCA RMM firmware is the trusted runtime that enforces memory
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index ba6d67e5f54e..029758ada136 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -8,6 +8,9 @@
#include <linux/pci-doe.h>
#include <linux/delay.h>
#include <asm/rmi_cmds.h>
+#include <crypto/internal/rsa.h>
+#include <keys/asymmetric-type.h>
+#include <keys/x509-parser.h>
#include "rmi-da.h"
@@ -386,6 +389,158 @@ static int wait_for_pdev_state(struct pci_tsm *tsm, enum rmi_pdev_state target_s
return wait_for_dev_state(PDEV_COMMUNICATE, tsm, target_state, RMI_PDEV_ERROR);
}
+static int parse_certificate_chain(struct pci_tsm *tsm)
+{
+ struct cca_host_pf0_dsc *pf0_dsc;
+ unsigned int chain_size;
+ unsigned int offset = 0;
+ u8 *chain_data;
+
+ pf0_dsc = to_cca_pf0_dsc(tsm->pdev);
+
+ /* If device communication didn't results in certificate caching. */
+ if (!pf0_dsc->cert_chain.cache || !pf0_dsc->cert_chain.cache->offset)
+ return -EINVAL;
+
+ chain_size = pf0_dsc->cert_chain.cache->offset;
+ chain_data = pf0_dsc->cert_chain.cache->buf;
+
+ while (offset < chain_size) {
+ ssize_t cert_len =
+ x509_get_certificate_length(chain_data + offset,
+ chain_size - offset);
+ if (cert_len < 0)
+ return cert_len;
+
+ struct x509_certificate *cert __free(x509_free_certificate) =
+ x509_cert_parse(chain_data + offset, cert_len);
+
+ if (IS_ERR(cert)) {
+ pci_warn(tsm->pdev, "parsing of certificate chain not successful\n");
+ return PTR_ERR(cert);
+ }
+
+ /* The key in the last cert in the chain is used */
+ if (offset + cert_len == chain_size) {
+ void *public_key __free(kfree) =
+ kzalloc(cert->pub->keylen, GFP_KERNEL);
+
+ if (!public_key)
+ return -ENOMEM;
+
+ if (!strcmp("ecdsa-nist-p256", cert->pub->pkey_algo)) {
+ pf0_dsc->rmi_signature_algorithm = RMI_SIG_ECDSA_P256;
+ } else if (!strcmp("ecdsa-nist-p384", cert->pub->pkey_algo)) {
+ pf0_dsc->rmi_signature_algorithm = RMI_SIG_ECDSA_P384;
+ } else if (!strcmp("rsa", cert->pub->pkey_algo)) {
+ struct rsa_key rsa_key = {0};
+ size_t skip = 0;
+ int ret;
+
+ ret = rsa_parse_pub_key(&rsa_key, cert->pub->key,
+ cert->pub->keylen);
+ if (ret)
+ return ret;
+
+ while (skip < rsa_key.n_sz && !rsa_key.n[skip])
+ skip++;
+
+ /* check we have 3072 bits len */
+ if ((rsa_key.n_sz - skip) != (3072 >> 3))
+ return -EINVAL;
+
+ pf0_dsc->rmi_signature_algorithm = RMI_SIG_RSASSA_3072;
+ } else {
+ return -EINVAL;
+ }
+
+ memcpy(public_key, cert->pub->key, cert->pub->keylen);
+ pf0_dsc->cert_chain.public_key = no_free_ptr(public_key);
+ pf0_dsc->cert_chain.public_key_size = cert->pub->keylen;
+ pf0_dsc->cert_chain.valid = true;
+ return 0;
+ }
+
+ offset += cert_len;
+ }
+
+ /* something wrong with chain size and parsing. */
+ return -EINVAL;
+}
+
+static inline void key_param_free(struct rmi_public_key_params *param)
+{
+ return free_page((unsigned long)param);
+}
+
+static inline int copy_key_part(u8 *buf, const u8 *key_buf, size_t sz)
+{
+ int skip;
+
+ /* skip leading zero in asn.1 */
+ for (skip = 0; skip < sz; skip++)
+ if (key_buf[skip])
+ break;
+
+ memcpy(buf, key_buf + skip, sz - skip);
+ return sz - skip;
+}
+
+DEFINE_FREE(key_param_free, struct rmi_public_key_params *, if (_T) key_param_free(_T))
+static int pdev_set_public_key(struct pci_tsm *tsm)
+{
+ struct cca_host_pf0_dsc *pf0_dsc;
+
+ pf0_dsc = to_cca_pf0_dsc(tsm->pdev);
+ /* Check that all the necessary information was captured from communication */
+ if (!pf0_dsc->cert_chain.valid)
+ return -EINVAL;
+
+ struct rmi_public_key_params *key_params __free(key_param_free) =
+ (struct rmi_public_key_params *)get_zeroed_page(GFP_KERNEL);
+ if (!key_params)
+ return -ENOMEM;
+
+ key_params->rmi_signature_algorithm = pf0_dsc->rmi_signature_algorithm;
+
+ switch (key_params->rmi_signature_algorithm) {
+ case RMI_SIG_ECDSA_P384:
+ case RMI_SIG_ECDSA_P256:
+ {
+ key_params->public_key_len = pf0_dsc->cert_chain.public_key_size;
+ memcpy(key_params->public_key,
+ pf0_dsc->cert_chain.public_key,
+ pf0_dsc->cert_chain.public_key_size);
+ key_params->metadata_len = 0;
+ break;
+ }
+ case RMI_SIG_RSASSA_3072:
+ {
+ int ret;
+ struct rsa_key rsa_key = {0};
+
+ ret = rsa_parse_pub_key(&rsa_key,
+ pf0_dsc->cert_chain.public_key,
+ pf0_dsc->cert_chain.public_key_size);
+ if (ret)
+ return ret;
+
+ key_params->public_key_len = copy_key_part(key_params->public_key,
+ rsa_key.n, rsa_key.n_sz);
+ key_params->metadata_len = copy_key_part(key_params->metadata,
+ rsa_key.e, rsa_key.e_sz);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ if (rmi_pdev_set_pubkey(virt_to_phys(pf0_dsc->rmm_pdev),
+ virt_to_phys(key_params)))
+ return -ENXIO;
+ return 0;
+}
+
static void pdev_state_transition_workfn(struct work_struct *work)
{
unsigned long state;
@@ -433,7 +588,24 @@ static int submit_pdev_state_transition_work(struct pci_dev *pdev, int target_st
int cca_pdev_ide_setup(struct pci_dev *pdev)
{
- return submit_pdev_state_transition_work(pdev, RMI_PDEV_NEEDS_KEY);
+ int ret;
+
+ ret = submit_pdev_state_transition_work(pdev, RMI_PDEV_NEEDS_KEY);
+ if (ret)
+ return ret;
+ /*
+ * we now have certificate chain in dsm->cert_chain. Parse that and set
+ * the pubkey.
+ */
+ ret = parse_certificate_chain(pdev->tsm);
+ if (ret)
+ return ret;
+
+ ret = pdev_set_public_key(pdev->tsm);
+ if (ret)
+ return ret;
+
+ return submit_pdev_state_transition_work(pdev, RMI_PDEV_READY);
}
void cca_pdev_stop_and_destroy(struct pci_dev *pdev)
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index fbfbcd40beb4..38550103c2a5 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -49,6 +49,7 @@ struct cca_host_comm_data {
* @rmm_pdev: Delegated granule address of rmm pdev object
* @num_ax: Number of auxiliary granules allocated for pdev
* @aux: Delegated auxiliary granules
+ * @rmi_signature_algorithm: Signature algorithm used for public key
* @object_lock: lock used to protect access to cached obects in PF0 and TDIs
* @cert_chain: cetrificate chain
* @vca: SPDM's Version-Capabilities-Algorithms cache object
@@ -62,6 +63,7 @@ struct cca_host_pf0_dsc {
int num_aux;
void *aux[MAX_PDEV_AUX_GRANULES];
+ uint8_t rmi_signature_algorithm;
struct mutex object_lock;
struct {
struct cache_object *cache;
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-03-12 8:03 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-12 8:01 [RFC PATCH v3 00/10] coco/TSM: Host-side Arm CCA IDE setup via connect/disconnect callbacks Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 01/10] KVM: arm64: RMI: Add and export kvm_has_da_feature helper Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 02/10] coco: host: arm64: Add host TSM callback and IDE stream allocation support Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 03/10] coco: host: arm64: Build and register RMM pdev descriptors Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 04/10] coco: host: arm64: Add RMM device communication helpers Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 05/10] coco: host: arm64: Add helper to stop and tear down an RMM pdev Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 06/10] coco: host: arm64: Instantiate RMM pdev during device connect Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 07/10] X.509: Make certificate parser public Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 08/10] X.509: Parse Subject Alternative Name in certificates Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 09/10] X.509: Move certificate length retrieval into new helper Aneesh Kumar K.V (Arm)
2026-03-12 8:01 ` [RFC PATCH v3 10/10] coco: host: arm64: Register device public key with RMM Aneesh Kumar K.V (Arm)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox