[RFC PATCH v1 31/38] coco: guest: arm64: Add support for fetching interface report and certificate chain from host

Aneesh Kumar K.V (Arm)
2025-07-28T13:55:41Z

Fetch interface report and certificate chain from the host using RHI calls.

Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rsi_cmds.h | 9 ++
arch/arm64/include/asm/rsi_smc.h | 6 ++
drivers/virt/coco/arm-cca-guest/rsi-da.c | 131 +++++++++++++++++++++++
drivers/virt/coco/arm-cca-guest/rsi-da.h | 5 +
4 files changed, 151 insertions(+)

diff --git a/arch/arm64/include/asm/rsi_cmds.h b/arch/arm64/include/asm/rsi_cmds.h
index 1d76f7d37cb6..18fc4e1ce577 100644
--- a/arch/arm64/include/asm/rsi_cmds.h
+++ b/arch/arm64/include/asm/rsi_cmds.h
@@ -219,4 +219,13 @@ static inline unsigned long __rsi_rdev_get_interface_report(unsigned long vdev_i
return res.a0;
}

+static inline unsigned long rsi_host_call(phys_addr_t addr)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RSI_HOST_CALL, addr, &res);
+
+ return res.a0;
+}
+
#endif /* __ASM_RSI_CMDS_H */
diff --git a/arch/arm64/include/asm/rsi_smc.h b/arch/arm64/include/asm/rsi_smc.h
index 6afcccee2ae7..1d762fe3777b 100644
--- a/arch/arm64/include/asm/rsi_smc.h
+++ b/arch/arm64/include/asm/rsi_smc.h
@@ -183,6 +183,12 @@ struct realm_config {
*/
#define SMC_RSI_IPA_STATE_GET SMC_RSI_FID(0x198)

+struct rsi_host_call {
+ u16 imm;
+ u8 padding[6];
+ u64 gprs[31];
+};
+
/*
* Make a Host call.
*
diff --git a/drivers/virt/coco/arm-cca-guest/rsi-da.c b/drivers/virt/coco/arm-cca-guest/rsi-da.c
index 28ec946df1e2..47b379318e7c 100644
--- a/drivers/virt/coco/arm-cca-guest/rsi-da.c
+++ b/drivers/virt/coco/arm-cca-guest/rsi-da.c
@@ -4,6 +4,7 @@
*/

#include <linux/pci.h>
+#include <linux/mem_encrypt.h>
#include <asm/rsi_cmds.h>

#include "rsi-da.h"
@@ -50,6 +51,121 @@ rsi_rdev_get_interface_report(struct pci_dev *pdev, unsigned long vdev_id,
return RSI_SUCCESS;
}

+static long rhi_get_report(int vdev_id, int da_object_type, void **report, int *report_size)
+{
+ int ret, enc_ret = 0;
+ int nr_pages;
+ int max_data_len;
+ void *data_buf_shared, *data_buf_private;
+ struct rsi_host_call *rhicall;
+
+ rhicall = kmalloc(sizeof(struct rsi_host_call), GFP_KERNEL);
+ if (!rhicall)
+ return -ENOMEM;
+
+ rhicall->imm = 0;
+ rhicall->gprs[0] = RHI_DA_FEATURES;
+
+ ret = rsi_host_call(virt_to_phys(rhicall));
+ if (ret != RSI_SUCCESS) {
+ ret = -EIO;
+ goto err_out;
+ }
+
+ if (rhicall->gprs[0] != 0x3) {
+ ret = -EIO;
+ goto err_out;
+ }
+
+ rhicall->imm = 0;
+ rhicall->gprs[0] = RHI_DA_OBJECT_SIZE;
+ rhicall->gprs[1] = vdev_id;
+ rhicall->gprs[2] = da_object_type;
+
+ ret = rsi_host_call(virt_to_phys(rhicall));
+ if (ret != RSI_SUCCESS) {
+ ret = -EIO;
+ goto err_out;
+ }
+ if (rhicall->gprs[0] != RHI_DA_SUCCESS) {
+ ret = -EIO;
+ goto err_out;
+ }
+ max_data_len = rhicall->gprs[1];
+ *report_size = max_data_len;
+
+ /*
+ * We need to share this memory with hypervisor.
+ * So it should be multiple of sharing unit.
+ */
+ max_data_len = ALIGN(max_data_len, PAGE_SIZE);
+ nr_pages = max_data_len >> PAGE_SHIFT;
+
+ if (!max_data_len || nr_pages > MAX_ORDER_NR_PAGES) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ /*
+ * We need to share this memory with hypervisor.
+ * So it should be multiple of sharing unit.
+ */
+ data_buf_shared = (void *)__get_free_pages(GFP_KERNEL, get_order(max_data_len));
+ if (!data_buf_shared) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ data_buf_private = kmalloc(*report_size, GFP_KERNEL);
+ if (!data_buf_private) {
+ ret = -ENOMEM;
+ goto err_private_alloc;
+ }
+
+ ret = set_memory_decrypted((unsigned long)data_buf_shared, nr_pages);
+ if (ret) {
+ ret = -EIO;
+ goto err_decrypt;
+ }
+
+ rhicall->imm = 0;
+ rhicall->gprs[0] = RHI_DA_OBJECT_READ;
+ rhicall->gprs[1] = vdev_id;
+ rhicall->gprs[2] = da_object_type;
+ rhicall->gprs[3] = 0; /* offset within the data buffer */
+ rhicall->gprs[4] = max_data_len;
+ rhicall->gprs[5] = virt_to_phys(data_buf_shared);
+ ret = rsi_host_call(virt_to_phys(rhicall));
+ if (ret != RSI_SUCCESS || rhicall->gprs[0] != RHI_DA_SUCCESS) {
+ ret = -EIO;
+ goto err_rhi_call;
+ }
+
+ memcpy(data_buf_private, data_buf_shared, *report_size);
+ enc_ret = set_memory_encrypted((unsigned long)data_buf_shared, nr_pages);
+ if (!enc_ret)
+ /* If we fail to mark it encrypted don't free it back */
+ free_pages((unsigned long)data_buf_shared, get_order(max_data_len));
+
+ *report = data_buf_private;
+ kfree(rhicall);
+ return 0;
+
+err_rhi_call:
+ enc_ret = set_memory_encrypted((unsigned long)data_buf_shared, nr_pages);
+err_decrypt:
+ kfree(data_buf_private);
+err_private_alloc:
+ if (!enc_ret)
+ /* If we fail to mark it encrypted don't free it back */
+ free_pages((unsigned long)data_buf_shared, get_order(max_data_len));
+err_out:
+ *report = NULL;
+ *report_size = 0;
+ kfree(rhicall);
+ return ret;
+}
+
int rsi_device_lock(struct pci_dev *pdev)
{
unsigned long ret;
@@ -82,5 +198,20 @@ int rsi_device_lock(struct pci_dev *pdev)
return -EOPNOTSUPP;
}

+ /* Now make a host call to copy the interface report to guest. */
+ ret = rhi_get_report(vdev_id, RHI_DA_OBJECT_INTERFACE_REPORT,
+ &dsm->interface_report, &dsm->interface_report_size);
+ if (ret) {
+ pci_err(pdev, "failed to get interface report from the host (%lu)\n", ret);
+ return -EIO;
+ }
+
+ ret = rhi_get_report(vdev_id, RHI_DA_OBJECT_CERTIFICATE,
+ &dsm->certificate, &dsm->certificate_size);
+ if (ret) {
+ pci_err(pdev, "failed to get device certificate from the host (%lu)\n", ret);
+ return -EIO;
+ }
+
return ret;
}
diff --git a/drivers/virt/coco/arm-cca-guest/rsi-da.h b/drivers/virt/coco/arm-cca-guest/rsi-da.h
index f12430c7d792..bd565785ff4b 100644
--- a/drivers/virt/coco/arm-cca-guest/rsi-da.h
+++ b/drivers/virt/coco/arm-cca-guest/rsi-da.h
@@ -9,10 +9,15 @@
#include <linux/pci.h>
#include <linux/pci-tsm.h>
#include <asm/rsi_smc.h>
+#include <asm/rhi.h>

struct cca_guest_dsc {
struct pci_tsm_pf0 pci;
unsigned long instance_id;
+ void *interface_report;
+ int interface_report_size;
+ void *certificate;
+ int certificate_size;
};

static inline struct cca_guest_dsc *to_cca_guest_dsc(struct pci_dev *pdev)
--
2.43.0