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");