Subject: [RFC PATCH v4 03/11] coco: guest: arm64: Add Realm Host Interface and guest DA helper

From: "Aneesh Kumar K.V (Arm)" <[email-masked]>
Date: Mon, 27 Apr 2026 13:57:57 +0530

- Add  guest-side rhi-da helper that drives the vdev TDI state machine
  via RHI host calls and translates the firmware status codes

This provides the basic RHI plumbing that later DA features rely on.

Signed-off-by: Aneesh Kumar K.V (Arm) <[email-masked]>
---
 arch/arm64/include/asm/rhi.h              |  38 +++++
 drivers/virt/coco/arm-cca-guest/Makefile  |   1 +
 drivers/virt/coco/arm-cca-guest/arm-cca.c |   3 +-
 drivers/virt/coco/arm-cca-guest/rhi-da.c  | 163 ++++++++++++++++++++++
 drivers/virt/coco/arm-cca-guest/rhi-da.h  |  14 ++
 5 files changed, 218 insertions(+), 1 deletion(-)
 create mode 100644 drivers/virt/coco/arm-cca-guest/rhi-da.c
 create mode 100644 drivers/virt/coco/arm-cca-guest/rhi-da.h

diff --git a/arch/arm64/include/asm/rhi.h b/arch/arm64/include/asm/rhi.h
index 0895dd92ea1d..88de2d9b34d1 100644
--- a/arch/arm64/include/asm/rhi.h
+++ b/arch/arm64/include/asm/rhi.h
@@ -21,4 +21,42 @@ unsigned long rhi_get_ipa_change_alignment(void);
 #define __RHI_HOSTCONF_GET_IPA_CHANGE_ALIGNMENT BIT(0)
 #define RHI_HOSTCONF_FEATURES		SMC_RHI_CALL(0x004F)
 #define RHI_HOSTCONF_GET_IPA_CHANGE_ALIGNMENT	SMC_RHI_CALL(0x0050)
+
+#define RHI_DA_SUCCESS				0x0
+#define RHI_DA_INCOMPLETE			0x1
+#define RHI_DA_ERROR_DATA_NOT_AVAILABLE		0x2
+#define RHI_DA_ERROR_INVALID_VDEV_ID		0x3
+#define RHI_DA_ERROR_INVALID_OBJECT		0x4
+#define RHI_DA_ERROR_INPUT			0x5
+#define RHI_DA_ERROR_DEVICE			0x6
+#define RHI_DA_ERROR_INVALID_OFFSET		0x7
+#define RHI_DA_ERROR_ACCESS_FAILED		0x8
+#define RHI_DA_ERROR_BUSY			0x9
+#define RHI_DA_ABORTED_OPERATION_HAD_COMPLETED	0xA
+
+#define RHI_DA_FEATURE_OBJECT_SIZE		BIT(0)
+#define RHI_DA_FEATURE_OBJECT_READ		BIT(1)
+#define RHI_DA_FEATURE_VDEV_CONTINUE		BIT(2)
+#define RHI_DA_FEATURE_VDEV_GET_MEASUREMENT	BIT(3)
+#define RHI_DA_FEATURE_VDEV_GET_INTF_REPORT	BIT(4)
+#define RHI_DA_FEATURE_VDEV_SET_TDI_STATE	BIT(5)
+
+#define RHI_DA_BASE_FEATURE	(RHI_DA_FEATURE_OBJECT_SIZE |		\
+				 RHI_DA_FEATURE_OBJECT_READ |		\
+				 RHI_DA_FEATURE_VDEV_GET_INTF_REPORT |	\
+				 RHI_DA_FEATURE_VDEV_GET_MEASUREMENT |	\
+				 RHI_DA_FEATURE_VDEV_SET_TDI_STATE)
+#define RHI_DA_FEATURES			SMC_RHI_CALL(0x004B)
+
+#define RHI_DA_VDEV_CONTINUE		SMC_RHI_CALL(0x0051)
+
+enum rhi_tdi_state {
+	RHI_DA_TDI_CONFIG_UNLOCKED,
+	RHI_DA_TDI_CONFIG_LOCKED,
+	RHI_DA_TDI_CONFIG_RUN,
+};
+#define RHI_DA_VDEV_SET_TDI_STATE	SMC_RHI_CALL(0x0054)
+
+#define RHI_DA_VDEV_ABORT		SMC_RHI_CALL(0x0056)
+
 #endif
diff --git a/drivers/virt/coco/arm-cca-guest/Makefile b/drivers/virt/coco/arm-cca-guest/Makefile
index 75a120e24fda..65c4cc52c154 100644
--- a/drivers/virt/coco/arm-cca-guest/Makefile
+++ b/drivers/virt/coco/arm-cca-guest/Makefile
@@ -2,3 +2,4 @@
 obj-$(CONFIG_ARM_CCA_GUEST) += arm-cca-guest.o
 
 arm-cca-guest-y +=  arm-cca.o
+arm-cca-guest-$(CONFIG_PCI_TSM) +=  rhi-da.o
diff --git a/drivers/virt/coco/arm-cca-guest/arm-cca.c b/drivers/virt/coco/arm-cca-guest/arm-cca.c
index 6f13c54fab73..0c12aae85e6d 100644
--- a/drivers/virt/coco/arm-cca-guest/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-guest/arm-cca.c
@@ -17,6 +17,7 @@
 
 #ifdef CONFIG_PCI_TSM
 #include "rsi-da.h"
+#include "rhi-da.h"
 #endif
 
 /**
@@ -265,7 +266,7 @@ static int cca_devsec_tsm_probe(struct auxiliary_device *adev,
 
 #ifdef CONFIG_PCI_TSM
 	/* Allow tsm report even if tsm_register fails */
-	if (rsi_has_da_feature())
+	if (rsi_has_da_feature() && rhi_has_da_support())
 		cca_devsec_tsm_register(adev);
 #endif
 
diff --git a/drivers/virt/coco/arm-cca-guest/rhi-da.c b/drivers/virt/coco/arm-cca-guest/rhi-da.c
new file mode 100644
index 000000000000..5b48c8d6ebe3
--- /dev/null
+++ b/drivers/virt/coco/arm-cca-guest/rhi-da.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+
+#include "rsi-da.h"
+#include "rhi-da.h"
+
+/**
+ * map_rhi_da_error - Map an RHI DA status to Linux errno
+ * @rhi_da_error: RHI DA status value to translate
+ *
+ * Return: 0 for %RHI_DA_SUCCESS, %RHI_DA_INCOMPLETE when the caller must
+ * continue the operation with rhi_vdev_continue(), or a negative errno for
+ * all other failures. Unknown status codes are mapped to -EIO.
+ */
+static inline int map_rhi_da_error(unsigned long rhi_da_error)
+{
+	switch (rhi_da_error) {
+	case RHI_DA_SUCCESS:
+		return 0;
+	case RHI_DA_INCOMPLETE:
+		return RHI_DA_INCOMPLETE;
+	case RHI_DA_ERROR_BUSY:
+		return -EBUSY;
+	case RHI_DA_ERROR_INPUT:
+	case RHI_DA_ERROR_INVALID_VDEV_ID:
+		return -EINVAL;
+	case RHI_DA_ERROR_ACCESS_FAILED:
+		return -EFAULT;
+	case RHI_DA_ERROR_DEVICE:
+		return -EIO;
+	case RHI_DA_ERROR_INVALID_OBJECT:
+		return -EINVAL;
+	default:
+		return -EIO;
+	}
+}
+
+bool rhi_has_da_support(void)
+{
+	int ret;
+
+	struct rsi_host_call *rhi_call __free(kfree) =
+		kmalloc(sizeof(*rhi_call), GFP_KERNEL);
+	if (!rhi_call)
+		return -ENOMEM;
+
+	rhi_call->imm = 0;
+	rhi_call->gprs[0] = RHI_DA_FEATURES;
+
+	ret = rsi_host_call(rhi_call);
+	if (ret != RSI_SUCCESS || rhi_call->gprs[0] == SMCCC_RET_NOT_SUPPORTED)
+		return false;
+
+	/* For base DA to work we need these to be supported */
+	if ((rhi_call->gprs[0] & RHI_DA_BASE_FEATURE) == RHI_DA_BASE_FEATURE)
+		return true;
+
+	return false;
+}
+
+static inline int rhi_vdev_continue(unsigned long vdev_id, unsigned long cookie)
+{
+	unsigned long ret;
+
+	struct rsi_host_call *rhi_call __free(kfree) =
+		kmalloc(sizeof(*rhi_call), GFP_KERNEL);
+	if (!rhi_call)
+		return -ENOMEM;
+
+	rhi_call->imm = 0;
+	rhi_call->gprs[0] = RHI_DA_VDEV_CONTINUE;
+	rhi_call->gprs[1] = vdev_id;
+	rhi_call->gprs[2] = cookie;
+
+	ret = rsi_host_call(rhi_call);
+	if (ret != RSI_SUCCESS)
+		return -EIO;
+
+	return map_rhi_da_error(rhi_call->gprs[0]);
+}
+
+static int __rhi_vdev_abort(unsigned long vdev_id, unsigned long *da_error)
+{
+	unsigned long ret;
+	struct rsi_host_call *rhi_call __free(kfree) =
+		kmalloc(sizeof(struct rsi_host_call), GFP_KERNEL);
+	if (!rhi_call)
+		return -ENOMEM;
+
+	rhi_call->imm = 0;
+	rhi_call->gprs[0] = RHI_DA_VDEV_ABORT;
+	rhi_call->gprs[1] = vdev_id;
+
+	ret = rsi_host_call(rhi_call);
+	if (ret != RSI_SUCCESS)
+		return -EIO;
+
+	*da_error = rhi_call->gprs[0];
+	return 0;
+}
+
+static bool should_abort_rhi_call_loop(unsigned long vdev_id)
+{
+	int ret;
+
+	cond_resched();
+	if (signal_pending(current)) {
+		unsigned long da_error;
+
+		ret = __rhi_vdev_abort(vdev_id, &da_error);
+		/* consider all kind of error as not aborted */
+		if (!ret && (da_error == RHI_DA_SUCCESS))
+			return true;
+	}
+	return false;
+}
+
+static int __rhi_vdev_set_tdi_state(unsigned long vdev_id,
+		enum rhi_tdi_state target_state, unsigned long *cookie)
+{
+	unsigned long ret;
+
+	struct rsi_host_call *rhi_call __free(kfree) =
+		kmalloc(sizeof(struct rsi_host_call), GFP_KERNEL);
+	if (!rhi_call)
+		return -ENOMEM;
+
+	rhi_call->imm = 0;
+	rhi_call->gprs[0] = RHI_DA_VDEV_SET_TDI_STATE;
+	rhi_call->gprs[1] = vdev_id;
+	rhi_call->gprs[2] = target_state;
+
+	ret = rsi_host_call(rhi_call);
+	if (ret != RSI_SUCCESS)
+		return -EIO;
+
+	*cookie = rhi_call->gprs[1];
+	return map_rhi_da_error(rhi_call->gprs[0]);
+}
+
+int rhi_vdev_set_tdi_state(struct pci_dev *pdev, enum rhi_tdi_state target_state)
+{
+	int ret;
+	unsigned long cookie;
+	int vdev_id = rsi_vdev_id(pdev);
+
+	for (;;) {
+		ret = __rhi_vdev_set_tdi_state(vdev_id, target_state, &cookie);
+		if (ret != -EBUSY)
+			break;
+		cond_resched();
+	}
+
+	while (ret == RHI_DA_INCOMPLETE) {
+		if (should_abort_rhi_call_loop(vdev_id))
+			return -EINTR;
+		ret = rhi_vdev_continue(vdev_id, cookie);
+	}
+
+	return ret;
+}
diff --git a/drivers/virt/coco/arm-cca-guest/rhi-da.h b/drivers/virt/coco/arm-cca-guest/rhi-da.h
new file mode 100644
index 000000000000..43c1cda8738d
--- /dev/null
+++ b/drivers/virt/coco/arm-cca-guest/rhi-da.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+
+#ifndef _VIRT_COCO_RHI_DA_H_
+#define _VIRT_COCO_RHI_DA_H_
+
+#include <asm/rhi.h>
+
+struct pci_dev;
+bool rhi_has_da_support(void);
+int rhi_vdev_set_tdi_state(struct pci_dev *pdev, enum rhi_tdi_state target_state);
+#endif
-- 
2.43.0