@@ -9677,6 +9677,9 @@ M: Ze-Yu Wang <ze-yu.wang@mediatek.com>
M: Liju Chen <liju-clr.chen@mediatek.com>
F: Documentation/devicetree/bindings/firmware/mediatek,geniezone.yaml
F: Documentation/virt/geniezone/
+F: arch/arm64/geniezone/
+F: drivers/virt/geniezone/
+F: include/linux/soc/mediatek/gzvm_drv.h
GENWQE (IBM Generic Workqueue Card)
M: Frank Haverkamp <haver@linux.ibm.com>
@@ -4,6 +4,7 @@ obj-$(CONFIG_KVM) += kvm/
obj-$(CONFIG_XEN) += xen/
obj-$(subst m,y,$(CONFIG_HYPERV)) += hyperv/
obj-$(CONFIG_CRYPTO) += crypto/
+obj-$(CONFIG_MTK_GZVM) += geniezone/
# for cleaning
subdir- += boot
new file mode 100644
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Main Makefile for gzvm, this one includes drivers/virt/geniezone/Makefile
+#
+include $(srctree)/drivers/virt/geniezone/Makefile
+
+gzvm-y += vm.o
+
+obj-$(CONFIG_MTK_GZVM) += gzvm.o
new file mode 100644
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ */
+
+#ifndef __GZVM_ARCH_COMMON_H__
+#define __GZVM_ARCH_COMMON_H__
+
+#include <linux/arm-smccc.h>
+
+enum {
+ GZVM_FUNC_PROBE = 12,
+ NR_GZVM_FUNC,
+};
+
+#define SMC_ENTITY_MTK 59
+#define GZVM_FUNCID_START (0x1000)
+#define GZVM_HCALL_ID(func) \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, \
+ SMC_ENTITY_MTK, (GZVM_FUNCID_START + (func)))
+
+#define MT_HVC_GZVM_PROBE GZVM_HCALL_ID(GZVM_FUNC_PROBE)
+
+/**
+ * gzvm_hypcall_wrapper() - the wrapper for hvc calls
+ * @a0: argument passed in registers 0
+ * @a1: argument passed in registers 1
+ * @a2: argument passed in registers 2
+ * @a3: argument passed in registers 3
+ * @a4: argument passed in registers 4
+ * @a5: argument passed in registers 5
+ * @a6: argument passed in registers 6
+ * @a7: argument passed in registers 7
+ * @res: result values from registers 0 to 3
+ *
+ * Return: The wrapper helps caller to convert geniezone errno to Linux errno.
+ */
+int gzvm_hypcall_wrapper(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3,
+ unsigned long a4, unsigned long a5,
+ unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res);
+
+#endif /* __GZVM_ARCH_COMMON_H__ */
new file mode 100644
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/err.h>
+#include <linux/uaccess.h>
+
+#include <linux/soc/mediatek/gzvm_drv.h>
+#include "gzvm_arch_common.h"
+
+/**
+ * gzvm_hypcall_wrapper() - the wrapper for hvc calls
+ * @a0: arguments passed in registers 0
+ * @a1: arguments passed in registers 1
+ * @a2: arguments passed in registers 2
+ * @a3: arguments passed in registers 3
+ * @a4: arguments passed in registers 4
+ * @a5: arguments passed in registers 5
+ * @a6: arguments passed in registers 6
+ * @a7: arguments passed in registers 7
+ * @res: result values from registers 0 to 3
+ *
+ * Return: The wrapper helps caller to convert geniezone errno to Linux errno.
+ */
+int gzvm_hypcall_wrapper(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3,
+ unsigned long a4, unsigned long a5,
+ unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res)
+{
+ struct arm_smccc_1_2_regs res_1_2;
+ struct arm_smccc_1_2_regs args = {
+ .a0 = a0,
+ .a1 = a1,
+ .a2 = a2,
+ .a3 = a3,
+ .a4 = a4,
+ .a5 = a5,
+ .a6 = a6,
+ .a7 = a7,
+ };
+ arm_smccc_1_2_hvc(&args, &res_1_2);
+ res->a0 = res_1_2.a0;
+ res->a1 = res_1_2.a1;
+ res->a2 = res_1_2.a2;
+ res->a3 = res_1_2.a3;
+
+ return gzvm_err_to_errno(res->a0);
+}
+
+int gzvm_arch_probe(struct gzvm_version drv_version,
+ struct gzvm_version *hyp_version)
+{
+ struct arm_smccc_res res;
+ int ret;
+
+ ret = gzvm_hypcall_wrapper(MT_HVC_GZVM_PROBE,
+ drv_version.major,
+ drv_version.minor,
+ drv_version.sub,
+ 0, 0, 0, 0, &res);
+ if (ret)
+ return -ENXIO;
+
+ hyp_version->major = (u32)res.a1;
+ hyp_version->minor = (u32)res.a2;
+ hyp_version->sub = res.a3;
+
+ return 0;
+}
@@ -49,4 +49,6 @@ source "drivers/virt/acrn/Kconfig"
source "drivers/virt/coco/Kconfig"
+source "drivers/virt/geniezone/Kconfig"
+
endif
new file mode 100644
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config MTK_GZVM
+ tristate "GenieZone Hypervisor driver for guest VM operation"
+ depends on ARM64 && EVENTFD
+ help
+ This driver, gzvm, enables to run guest VMs on MTK GenieZone
+ hypervisor. It exports kvm-like interfaces for VMM (e.g., crosvm) in
+ order to operate guest VMs on GenieZone hypervisor.
+
+ GenieZone hypervisor now only supports MediaTek SoC and arm64
+ architecture.
+
+ Select M if you want it be built as a module (gzvm.ko).
+
+ If unsure, say N.
new file mode 100644
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for GenieZone driver, this file should be include in arch's
+# to avoid two ko being generated.
+#
+
+GZVM_DIR ?= ../../../drivers/virt/geniezone
+
+gzvm-y := $(GZVM_DIR)/gzvm_main.o
new file mode 100644
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/kdev_t.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/soc/mediatek/gzvm_drv.h>
+
+static struct gzvm_driver gzvm_drv = {
+ .drv_version = {
+ .major = GZVM_DRV_MAJOR_VERSION,
+ .minor = GZVM_DRV_MINOR_VERSION,
+ .sub = 0,
+ },
+};
+
+/**
+ * gzvm_err_to_errno() - Convert geniezone return value to standard errno
+ *
+ * @err: Return value from geniezone function return
+ *
+ * Return: Standard errno
+ */
+int gzvm_err_to_errno(unsigned long err)
+{
+ int gz_err = (int)err;
+
+ switch (gz_err) {
+ case 0:
+ return 0;
+ case ERR_NO_MEMORY:
+ return -ENOMEM;
+ case ERR_NOT_SUPPORTED:
+ fallthrough;
+ case ERR_NOT_IMPLEMENTED:
+ return -EOPNOTSUPP;
+ case ERR_FAULT:
+ return -EFAULT;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int gzvm_dev_open(struct inode *inode, struct file *file)
+{
+ /*
+ * Reference count to prevent this module is unload without destroying
+ * VM
+ */
+ try_module_get(THIS_MODULE);
+ return 0;
+}
+
+static int gzvm_dev_release(struct inode *inode, struct file *file)
+{
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+static const struct file_operations gzvm_chardev_ops = {
+ .llseek = noop_llseek,
+ .open = gzvm_dev_open,
+ .release = gzvm_dev_release,
+};
+
+static struct miscdevice gzvm_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = KBUILD_MODNAME,
+ .fops = &gzvm_chardev_ops,
+};
+
+static int gzvm_drv_probe(struct platform_device *pdev)
+{
+ if (gzvm_arch_probe(gzvm_drv.drv_version, &gzvm_drv.hyp_version) != 0) {
+ dev_err(&pdev->dev, "Not found available conduit\n");
+ return -ENODEV;
+ }
+
+ pr_debug("Found GenieZone hypervisor version %u.%u.%llu\n",
+ gzvm_drv.hyp_version.major, gzvm_drv.hyp_version.minor,
+ gzvm_drv.hyp_version.sub);
+
+ return misc_register(&gzvm_dev);
+}
+
+static void gzvm_drv_remove(struct platform_device *pdev)
+{
+ misc_deregister(&gzvm_dev);
+}
+
+static const struct of_device_id gzvm_of_match[] = {
+ { .compatible = "mediatek,geniezone" },
+ {/* sentinel */},
+};
+
+static struct platform_driver gzvm_driver = {
+ .probe = gzvm_drv_probe,
+ .remove = gzvm_drv_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = gzvm_of_match,
+ },
+};
+
+module_platform_driver(gzvm_driver);
+
+MODULE_DEVICE_TABLE(of, gzvm_of_match);
+MODULE_AUTHOR("MediaTek");
+MODULE_DESCRIPTION("GenieZone interface for VMM");
+MODULE_LICENSE("GPL");
new file mode 100644
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ */
+
+#ifndef __GZVM_DRV_H__
+#define __GZVM_DRV_H__
+
+/* GZVM version encode */
+#define GZVM_DRV_MAJOR_VERSION 16
+#define GZVM_DRV_MINOR_VERSION 0
+
+struct gzvm_version {
+ u32 major;
+ u32 minor;
+ u64 sub; /* currently, used by hypervisor */
+};
+
+struct gzvm_driver {
+ struct gzvm_version hyp_version;
+ struct gzvm_version drv_version;
+};
+
+/*
+ * These are the definitions of APIs between GenieZone hypervisor and driver,
+ * there's no need to be visible to uapi. Furthermore, we need GenieZone
+ * specific error code in order to map to Linux errno
+ */
+#define NO_ERROR (0)
+#define ERR_NO_MEMORY (-5)
+#define ERR_NOT_SUPPORTED (-24)
+#define ERR_NOT_IMPLEMENTED (-27)
+#define ERR_FAULT (-40)
+
+int gzvm_err_to_errno(unsigned long err);
+
+/* arch-dependant functions */
+int gzvm_arch_probe(struct gzvm_version drv_version,
+ struct gzvm_version *hyp_version);
+
+#endif /* __GZVM_DRV_H__ */