@@ -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)
@@ -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 */
@@ -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)
new file mode 100644
@@ -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);
+}
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