ANDROID: misc: Add pKVM test module for lazy_pte

Change-Id: I99bc62955243d28728a182e5ed55973bffc1d5d8
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index a43fcbe..7fd4a05 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -74,3 +74,4 @@
 obj-y				+= keba/
 obj-$(CONFIG_UID_SYS_STATS)	+= uid_sys_stats.o
 obj-$(CONFIG_PKVM_SMC_FILTER)  += pkvm-smc/
+obj-m				+= pkvm-test-lazy-pte/
diff --git a/drivers/misc/pkvm-test-lazy-pte/Makefile b/drivers/misc/pkvm-test-lazy-pte/Makefile
new file mode 100644
index 0000000..dedf97e2
--- /dev/null
+++ b/drivers/misc/pkvm-test-lazy-pte/Makefile
@@ -0,0 +1,15 @@
+ifneq ($(KERNELRELEASE),)
+clean-files := hyp/hyp.lds hyp/hyp-reloc.S
+obj-m := pkvm_test_lazy_pte.o
+
+pkvm_test_lazy_pte-y := test-lazy-pte.o hyp/kvm_nvhe.o
+
+$(obj)/hyp/kvm_nvhe.o: FORCE
+	$(Q)$(MAKE) $(build)=$(obj)/hyp $(obj)/hyp/kvm_nvhe.o
+else
+all:
+	make -C $(KDIR) M=$(PWD) modules
+clean:
+	make -C $(KDIR) M=$(PWD) clean
+endif
+
diff --git a/drivers/misc/pkvm-test-lazy-pte/hyp/Makefile b/drivers/misc/pkvm-test-lazy-pte/hyp/Makefile
new file mode 100644
index 0000000..fed1054
--- /dev/null
+++ b/drivers/misc/pkvm-test-lazy-pte/hyp/Makefile
@@ -0,0 +1,2 @@
+hyp-obj-y := test-lazy-pte.o
+include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
diff --git a/drivers/misc/pkvm-test-lazy-pte/hyp/test-lazy-pte.c b/drivers/misc/pkvm-test-lazy-pte/hyp/test-lazy-pte.c
new file mode 100644
index 0000000..3d05764
--- /dev/null
+++ b/drivers/misc/pkvm-test-lazy-pte/hyp/test-lazy-pte.c
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <asm/kvm_pkvm_module.h>
+#include <linux/arm-smccc.h>
+
+const struct pkvm_module_ops *pkvm_ops;
+
+void test_lazy_pte_hyp_hvc(struct user_pt_regs *regs)
+{
+	u64 pfn = regs->regs[1];
+	bool enable = regs->regs[2];
+
+	regs->regs[1] = enable ? pkvm_ops->host_stage2_enable_lazy_pte(pfn, 1) :
+				 pkvm_ops->host_stage2_disable_lazy_pte(pfn, 1);
+	regs->regs[0] = SMCCC_RET_SUCCESS;
+}
+
+int test_lazy_pte_hyp_init(const struct pkvm_module_ops *ops)
+{
+	pkvm_ops = ops;
+	return 0;
+}
diff --git a/drivers/misc/pkvm-test-lazy-pte/test-lazy-pte.c b/drivers/misc/pkvm-test-lazy-pte/test-lazy-pte.c
new file mode 100644
index 0000000..b4453e6
--- /dev/null
+++ b/drivers/misc/pkvm-test-lazy-pte/test-lazy-pte.c
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <asm/kvm_pkvm_module.h>
+
+static unsigned long pkvm_module_token;
+int kvm_nvhe_sym(test_lazy_pte_hyp_init)(const struct pkvm_module_ops *ops);
+void kvm_nvhe_sym(test_lazy_pte_hyp_hvc)(struct user_pt_regs *regs);
+
+static int __init test_lazy_pte_init(void)
+{
+	int ret = pkvm_load_el2_module(kvm_nvhe_sym(test_lazy_pte_hyp_init),
+				       &pkvm_module_token);
+	int hvc, cnt = 1 << 16;
+	void *page;
+
+	if (ret) {
+		pr_err("Failed to load EL2 module: %d\n", ret);
+		return ret;
+	}
+
+	ret = pkvm_register_el2_mod_call(kvm_nvhe_sym(test_lazy_pte_hyp_hvc), pkvm_module_token);
+	if (ret < 0)
+		return ret;
+
+	hvc = ret;
+
+	page = (void *)__get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	while (cnt--) {
+		ret = pkvm_el2_mod_call(hvc, virt_to_pfn(page), true);
+		if (ret) {
+			pr_err("Failed to enable dirty logging on page %px\n", page);
+			break;
+		}
+		ret = pkvm_el2_mod_call(hvc, virt_to_pfn(page), false);
+		if (ret) {
+			pr_err("Failed to disable dirty logging on page %px\n", page);
+			break;
+		}
+	}
+
+	return 0;
+}
+module_init(test_lazy_pte_init);
+
+MODULE_LICENSE("GPL v2");