[RFC PATCH 34/45] iommu/arm-smmu-v3: Add host driver for pKVM

Jean-Philippe Brucker
2023-02-01T14:31:57Z

Under protected KVM (pKVM), the host does not have access to guest or
hypervisor memory. This means that devices owned by the host must be
isolated by the SMMU, and the hypervisor is in charge of the SMMU.

Introduce the host component that replaces the normal SMMUv3 driver when
pKVM is enabled, and sends configuration and requests to the actual
driver running in the hypervisor (EL2).

Rather than rely on regular driver probe, pKVM directly calls
kvm_arm_smmu_v3_init(), which synchronously finds all SMMUs and hands
them to the hypervisor. If the regular driver is enabled, it will not
find any free SMMU to drive once it gets probed.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
---
drivers/iommu/arm/arm-smmu-v3/Makefile | 5 ++
include/kvm/arm_smmu_v3.h | 14 +++++
arch/arm64/kvm/arm.c | 18 +++++-
.../iommu/arm/arm-smmu-v3/arm-smmu-v3-kvm.c | 58 +++++++++++++++++++
4 files changed, 94 insertions(+), 1 deletion(-)
create mode 100644 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kvm.c

diff --git a/drivers/iommu/arm/arm-smmu-v3/Makefile b/drivers/iommu/arm/arm-smmu-v3/Makefile
index c4fcc796213c..a90b97d8bae3 100644
--- a/drivers/iommu/arm/arm-smmu-v3/Makefile
+++ b/drivers/iommu/arm/arm-smmu-v3/Makefile
@@ -4,3 +4,8 @@ arm_smmu_v3-objs-y += arm-smmu-v3.o
arm_smmu_v3-objs-y += arm-smmu-v3-common.o
arm_smmu_v3-objs-$(CONFIG_ARM_SMMU_V3_SVA) += arm-smmu-v3-sva.o
arm_smmu_v3-objs := $(arm_smmu_v3-objs-y)
+
+obj-$(CONFIG_ARM_SMMU_V3_PKVM) += arm_smmu_v3_kvm.o
+arm_smmu_v3_kvm-objs-y += arm-smmu-v3-kvm.o
+arm_smmu_v3_kvm-objs-y += arm-smmu-v3-common.o
+arm_smmu_v3_kvm-objs := $(arm_smmu_v3_kvm-objs-y)
diff --git a/include/kvm/arm_smmu_v3.h b/include/kvm/arm_smmu_v3.h
index ed139b0e9612..373b915b6661 100644
--- a/include/kvm/arm_smmu_v3.h
+++ b/include/kvm/arm_smmu_v3.h
@@ -40,4 +40,18 @@ extern struct hyp_arm_smmu_v3_device *kvm_nvhe_sym(kvm_hyp_arm_smmu_v3_smmus);

#endif /* CONFIG_ARM_SMMU_V3_PKVM */

+#ifndef __KVM_NVHE_HYPERVISOR__
+# if IS_ENABLED(CONFIG_ARM_SMMU_V3_PKVM)
+int kvm_arm_smmu_v3_init(unsigned int *count);
+void kvm_arm_smmu_v3_remove(void);
+
+# else /* CONFIG_ARM_SMMU_V3_PKVM */
+static inline int kvm_arm_smmu_v3_init(unsigned int *count)
+{
+ return -ENODEV;
+}
+static void kvm_arm_smmu_v3_remove(void) {}
+# endif /* CONFIG_ARM_SMMU_V3_PKVM */
+#endif /* __KVM_NVHE_HYPERVISOR__ */
+
#endif /* __KVM_ARM_SMMU_V3_H */
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 31faae76d519..a4cd09fc4abf 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -44,6 +44,7 @@
#include <kvm/arm_hypercalls.h>
#include <kvm/arm_pmu.h>
#include <kvm/arm_psci.h>
+#include <kvm/arm_smmu_v3.h>

static enum kvm_mode kvm_mode = KVM_MODE_DEFAULT;
DEFINE_STATIC_KEY_FALSE(kvm_protected_mode_initialized);
@@ -1901,11 +1902,26 @@ static bool init_psci_relay(void)

static int init_stage2_iommu(void)
{
- return KVM_IOMMU_DRIVER_NONE;
+ int ret;
+ unsigned int smmu_count;
+
+ ret = kvm_arm_smmu_v3_init(&smmu_count);
+ if (ret)
+ return ret;
+ else if (!smmu_count)
+ return KVM_IOMMU_DRIVER_NONE;
+ return KVM_IOMMU_DRIVER_SMMUV3;
}

static void remove_stage2_iommu(enum kvm_iommu_driver iommu)
{
+ switch (iommu) {
+ case KVM_IOMMU_DRIVER_SMMUV3:
+ kvm_arm_smmu_v3_remove();
+ break;
+ default:
+ break;
+ }
}

static int init_subsystems(void)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kvm.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kvm.c
new file mode 100644
index 000000000000..4092da8050ef
--- /dev/null
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kvm.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * pKVM host driver for the Arm SMMUv3
+ *
+ * Copyright (C) 2022 Linaro Ltd.
+ */
+#include <linux/of_platform.h>
+
+#include <kvm/arm_smmu_v3.h>
+
+#include "arm-smmu-v3.h"
+
+static int kvm_arm_smmu_probe(struct platform_device *pdev)
+{
+ return -ENOSYS;
+}
+
+static int kvm_arm_smmu_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id arm_smmu_of_match[] = {
+ { .compatible = "arm,smmu-v3", },
+ { },
+};
+
+static struct platform_driver kvm_arm_smmu_driver = {
+ .driver = {
+ .name = "kvm-arm-smmu-v3",
+ .of_match_table = arm_smmu_of_match,
+ },
+ .remove = kvm_arm_smmu_remove,
+};
+
+/**
+ * kvm_arm_smmu_v3_init() - Reserve the SMMUv3 for KVM
+ * @count: on success, number of SMMUs successfully initialized
+ *
+ * Return 0 if all present SMMUv3 were probed successfully, or an error.
+ * If no SMMU was found, return 0, with a count of 0.
+ */
+int kvm_arm_smmu_v3_init(unsigned int *count)
+{
+ int ret;
+
+ ret = platform_driver_probe(&kvm_arm_smmu_driver, kvm_arm_smmu_probe);
+ if (ret)
+ return ret;
+
+ *count = 0;
+ return 0;
+}
+
+void kvm_arm_smmu_v3_remove(void)
+{
+ platform_driver_unregister(&kvm_arm_smmu_driver);
+}
--
2.39.0