[PATCH v1 3/6] soc: mediatek: virt: geniezone: Introduce GenieZone hypervisor support
AngeloGioacchino Del Regno
angelogioacchino.delregno at collabora.com
Fri Apr 14 04:53:18 PDT 2023
Il 13/04/23 11:07, Yi-De Wu ha scritto:
> From: "Yingshiuan Pan" <yingshiuan.pan at mediatek.com>
>
> GenieZone is MediaTek proprietary hypervisor solution, and it is running
> in EL2 stand alone as a type-I hypervisor. This patch exports a set of
> ioctl interfaces for userspace VMM (e.g., crosvm) to operate guest VMs
> lifecycle (creation, running, and destroy) on GenieZone.
>
> Signed-off-by: Yingshiuan Pan <yingshiuan.pan at mediatek.com>
> Signed-off-by: Yi-De Wu <yi-de.wu at mediatek.com>
> ---
> arch/arm64/include/uapi/asm/gzvm_arch.h | 79 ++++
> drivers/soc/mediatek/Kconfig | 2 +
> drivers/soc/mediatek/Makefile | 1 +
> drivers/soc/mediatek/virt/geniezone/Kconfig | 17 +
> drivers/soc/mediatek/virt/geniezone/Makefile | 5 +
> drivers/soc/mediatek/virt/geniezone/gzvm.h | 103 ++++
> .../soc/mediatek/virt/geniezone/gzvm_hyp.h | 72 +++
> .../soc/mediatek/virt/geniezone/gzvm_main.c | 233 +++++++++
> .../soc/mediatek/virt/geniezone/gzvm_vcpu.c | 266 +++++++++++
> drivers/soc/mediatek/virt/geniezone/gzvm_vm.c | 444 ++++++++++++++++++
> include/uapi/linux/gzvm_common.h | 217 +++++++++
> 11 files changed, 1439 insertions(+)
> create mode 100644 arch/arm64/include/uapi/asm/gzvm_arch.h
> create mode 100644 drivers/soc/mediatek/virt/geniezone/Kconfig
> create mode 100644 drivers/soc/mediatek/virt/geniezone/Makefile
> create mode 100644 drivers/soc/mediatek/virt/geniezone/gzvm.h
> create mode 100644 drivers/soc/mediatek/virt/geniezone/gzvm_hyp.h
> create mode 100644 drivers/soc/mediatek/virt/geniezone/gzvm_main.c
> create mode 100644 drivers/soc/mediatek/virt/geniezone/gzvm_vcpu.c
> create mode 100644 drivers/soc/mediatek/virt/geniezone/gzvm_vm.c
> create mode 100644 include/uapi/linux/gzvm_common.h
>
> diff --git a/arch/arm64/include/uapi/asm/gzvm_arch.h b/arch/arm64/include/uapi/asm/gzvm_arch.h
> new file mode 100644
> index 000000000000..3714f96c832b
> --- /dev/null
> +++ b/arch/arm64/include/uapi/asm/gzvm_arch.h
> @@ -0,0 +1,79 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Copyright (c) 2023 MediaTek Inc.
> + */
> +
> +#ifndef __GZVM_ARCH_H__
> +#define __GZVM_ARCH_H__
> +
> +#include <asm/ptrace.h>
> +
> +/*
> + * Architecture specific registers are to be defined in arch headers and
> + * ORed with the arch identifier.
> + */
> +#define GZVM_REG_ARM 0x4000000000000000ULL
> +#define GZVM_REG_ARM64 0x6000000000000000ULL
> +
> +#define GZVM_REG_SIZE_SHIFT 52
> +#define GZVM_REG_SIZE_MASK 0x00f0000000000000ULL
> +#define GZVM_REG_SIZE_U8 0x0000000000000000ULL
> +#define GZVM_REG_SIZE_U16 0x0010000000000000ULL
> +#define GZVM_REG_SIZE_U32 0x0020000000000000ULL
> +#define GZVM_REG_SIZE_U64 0x0030000000000000ULL
> +#define GZVM_REG_SIZE_U128 0x0040000000000000ULL
> +#define GZVM_REG_SIZE_U256 0x0050000000000000ULL
> +#define GZVM_REG_SIZE_U512 0x0060000000000000ULL
> +#define GZVM_REG_SIZE_U1024 0x0070000000000000ULL
> +#define GZVM_REG_SIZE_U2048 0x0080000000000000ULL
> +
> +#define GZVM_NR_SPSR 5
> +struct gzvm_regs {
> + struct user_pt_regs regs; /* sp = sp_el0 */
> +
> + __u64 sp_el1;
> + __u64 elr_el1;
> +
> + __u64 spsr[GZVM_NR_SPSR];
> +
> + struct user_fpsimd_state fp_regs;
> +};
> +
> +/* If you need to interpret the index values, here is the key: */
> +#define GZVM_REG_ARM_COPROC_MASK 0x000000000FFF0000
> +#define GZVM_REG_ARM_COPROC_SHIFT 16
> +
> +/* Normal registers are mapped as coprocessor 16. */
> +#define GZVM_REG_ARM_CORE (0x0010 << GZVM_REG_ARM_COPROC_SHIFT)
> +#define GZVM_REG_ARM_CORE_REG(name) (offsetof(struct gzvm_regs, name) / sizeof(__u32))
> +
> +/* Some registers need more space to represent values. */
> +#define GZVM_REG_ARM_DEMUX (0x0011 << GZVM_REG_ARM_COPROC_SHIFT)
> +#define GZVM_REG_ARM_DEMUX_ID_MASK 0x000000000000FF00
> +#define GZVM_REG_ARM_DEMUX_ID_SHIFT 8
> +#define GZVM_REG_ARM_DEMUX_ID_CCSIDR (0x00 << GZVM_REG_ARM_DEMUX_ID_SHIFT)
> +#define GZVM_REG_ARM_DEMUX_VAL_MASK 0x00000000000000FF
> +#define GZVM_REG_ARM_DEMUX_VAL_SHIFT 0
> +
> +/* AArch64 system registers */
> +#define GZVM_REG_ARM64_SYSREG (0x0013 << GZVM_REG_ARM_COPROC_SHIFT)
> +#define GZVM_REG_ARM64_SYSREG_OP0_MASK 0x000000000000c000
> +#define GZVM_REG_ARM64_SYSREG_OP0_SHIFT 14
> +#define GZVM_REG_ARM64_SYSREG_OP1_MASK 0x0000000000003800
> +#define GZVM_REG_ARM64_SYSREG_OP1_SHIFT 11
> +#define GZVM_REG_ARM64_SYSREG_CRN_MASK 0x0000000000000780
> +#define GZVM_REG_ARM64_SYSREG_CRN_SHIFT 7
> +#define GZVM_REG_ARM64_SYSREG_CRM_MASK 0x0000000000000078
> +#define GZVM_REG_ARM64_SYSREG_CRM_SHIFT 3
> +#define GZVM_REG_ARM64_SYSREG_OP2_MASK 0x0000000000000007
> +#define GZVM_REG_ARM64_SYSREG_OP2_SHIFT 0
> +
> +/* Physical Timer EL0 Registers */
> +#define GZVM_REG_ARM_PTIMER_CTL ARM64_SYS_REG(3, 3, 14, 2, 1)
> +#define GZVM_REG_ARM_PTIMER_CVAL ARM64_SYS_REG(3, 3, 14, 2, 2)
> +#define GZVM_REG_ARM_PTIMER_CNT ARM64_SYS_REG(3, 3, 14, 0, 1)
> +
> +/* SVE registers */
> +#define GZVM_REG_ARM64_SVE (0x0015 << KVM_REG_ARM_COPROC_SHIFT)
> +
> +#endif /* __GZVM_ARCH_H__ */
> diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
> index a88cf04fc803..01fad024a1c1 100644
> --- a/drivers/soc/mediatek/Kconfig
> +++ b/drivers/soc/mediatek/Kconfig
> @@ -91,4 +91,6 @@ config MTK_SVS
> chip process corner, temperatures and other factors. Then DVFS
> driver could apply SVS bank voltage to PMIC/Buck.
>
> +source "drivers/soc/mediatek/virt/geniezone/Kconfig"
> +
> endmenu
> diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
> index 8c0ddacbcde8..e5d7225c1d08 100644
> --- a/drivers/soc/mediatek/Makefile
> +++ b/drivers/soc/mediatek/Makefile
> @@ -9,3 +9,4 @@ obj-$(CONFIG_MTK_SCPSYS_PM_DOMAINS) += mtk-pm-domains.o
> obj-$(CONFIG_MTK_MMSYS) += mtk-mmsys.o
> obj-$(CONFIG_MTK_MMSYS) += mtk-mutex.o
> obj-$(CONFIG_MTK_SVS) += mtk-svs.o
> +obj-$(CONFIG_MTK_GZVM) += virt/geniezone/
> diff --git a/drivers/soc/mediatek/virt/geniezone/Kconfig b/drivers/soc/mediatek/virt/geniezone/Kconfig
> new file mode 100644
> index 000000000000..6fad3c30f8d9
> --- /dev/null
> +++ b/drivers/soc/mediatek/virt/geniezone/Kconfig
> @@ -0,0 +1,17 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +config MTK_GZVM
> + tristate "GenieZone Hypervisor driver for guest VM operation"
> + depends on ARM64
> + depends on KVM
> + 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.
> diff --git a/drivers/soc/mediatek/virt/geniezone/Makefile b/drivers/soc/mediatek/virt/geniezone/Makefile
> new file mode 100644
> index 000000000000..e1dfbb9c568d
> --- /dev/null
> +++ b/drivers/soc/mediatek/virt/geniezone/Makefile
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +gzvm-y := gzvm_main.o gzvm_vm.o gzvm_vcpu.o
> +
> +obj-$(CONFIG_MTK_GZVM) += gzvm.o
> diff --git a/drivers/soc/mediatek/virt/geniezone/gzvm.h b/drivers/soc/mediatek/virt/geniezone/gzvm.h
> new file mode 100644
> index 000000000000..43f215d4b0da
> --- /dev/null
> +++ b/drivers/soc/mediatek/virt/geniezone/gzvm.h
> @@ -0,0 +1,103 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2023 MediaTek Inc.
> + */
> +
> +#ifndef __GZVM_H__
> +#define __GZVM_H__
> +
> +#include <linux/srcu.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/gzvm_common.h>
> +#include "gzvm_hyp.h"
> +
> +#define MODULE_NAME "gzvm"
> +#define GZVM_VCPU_MMAP_SIZE PAGE_SIZE
> +#define INVALID_VM_ID 0xffff
> +
> +/* VM's memory slot descriptor */
You've documented almost all of the members of this struct ... can you please
change that to kerneldoc?
/**
* struct gzvm_memslot - VM memory slot descriptor
* @base_qfn: Base of guest page frame
* @npages: Number of pages this slot covers
* @userspace_addr: Corresponding user-space VA
* .....other members
*/
> +struct gzvm_memslot {
> + u64 base_gfn; /* begin of guest page frame */
> + unsigned long npages; /* number of pages this slot covers */
> + unsigned long userspace_addr; /* corresponding userspace va */
> + struct vm_area_struct *vma; /* vma related to this userspace addr */
> + u32 flags;
> + u32 slot_id;
> +};
> +
> +/* pre-declaration for circular reference in struct gzvm */
> +struct gzvm_vcpu;
> +
Same here, if you could add kerneldoc description to this struct, that would
be highly appreciated, as it would greatly increase human readability.
> +struct gzvm {
> + struct gzvm_vcpu *vcpus[GZVM_MAX_VCPUS];
> + struct mm_struct *mm; /* userspace tied to this vm */
> + struct gzvm_memslot memslot[GZVM_MAX_MEM_REGION];
> + struct mutex lock;
> + struct list_head vm_list;
> + struct list_head devices;
> + gzvm_id_t vm_id;
> +
> + struct {
> + spinlock_t lock;
> + struct list_head items;
> + struct list_head resampler_list;
> + struct mutex resampler_lock;
> + } irqfds;
> + struct hlist_head irq_ack_notifier_list;
> + struct srcu_struct irq_srcu;
> + struct mutex irq_lock;
> +};
> +
> +struct gzvm_vcpu {
> + struct gzvm *gzvm;
> + int vcpuid;
> + struct mutex lock;
> + struct gzvm_vcpu_run *run;
> + struct gzvm_vcpu_hwstate *hwstate;
> +};
> +
> +/**
> + * allocate 2 pages for data sharing between driver and gz hypervisor
> + * |- page 0 -|- page 1 -|
> + * |gzvm_vcpu_run|......|hwstate|.......|
> + */
> +#define GZVM_VCPU_RUN_MAP_SIZE (PAGE_SIZE * 2)
> +
> +long gzvm_dev_ioctl_check_extension(struct gzvm *gzvm, unsigned long args);
> +
> +void gzvm_destroy_vcpu(struct gzvm_vcpu *vcpu);
> +int gzvm_vm_ioctl_create_vcpu(struct gzvm *gzvm, u32 cpuid);
> +int gzvm_dev_ioctl_create_vm(unsigned long vm_type);
> +
> +int gzvm_arm_get_reg(struct gzvm_vcpu *vcpu, const struct gzvm_one_reg *reg);
> +int gzvm_arm_set_reg(struct gzvm_vcpu *vcpu, const struct gzvm_one_reg *reg);
> +
> +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);
Could you please keep function signatures after *all* definitions?
> +
> +#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_32, \
> + SMC_ENTITY_MTK, (GZVM_FUNCID_START + (func)))
> +
> +#define MT_HVC_GZVM_CREATE_VM GZVM_HCALL_ID(GZVM_FUNC_CREATE_VM)
> +#define MT_HVC_GZVM_DESTROY_VM GZVM_HCALL_ID(GZVM_FUNC_DESTROY_VM)
> +#define MT_HVC_GZVM_CREATE_VCPU GZVM_HCALL_ID(GZVM_FUNC_CREATE_VCPU)
> +#define MT_HVC_GZVM_DESTROY_VCPU GZVM_HCALL_ID(GZVM_FUNC_DESTROY_VCPU)
> +#define MT_HVC_GZVM_SET_MEMREGION GZVM_HCALL_ID(GZVM_FUNC_SET_MEMREGION)
> +#define MT_HVC_GZVM_RUN GZVM_HCALL_ID(GZVM_FUNC_RUN)
> +#define MT_HVC_GZVM_GET_REGS GZVM_HCALL_ID(GZVM_FUNC_GET_REGS)
> +#define MT_HVC_GZVM_SET_REGS GZVM_HCALL_ID(GZVM_FUNC_SET_REGS)
> +#define MT_HVC_GZVM_GET_ONE_REG GZVM_HCALL_ID(GZVM_FUNC_GET_ONE_REG)
> +#define MT_HVC_GZVM_SET_ONE_REG GZVM_HCALL_ID(GZVM_FUNC_SET_ONE_REG)
> +#define MT_HVC_GZVM_IRQ_LINE GZVM_HCALL_ID(GZVM_FUNC_IRQ_LINE)
> +#define MT_HVC_GZVM_CREATE_DEVICE GZVM_HCALL_ID(GZVM_FUNC_CREATE_DEVICE)
> +#define MT_HVC_GZVM_PROBE GZVM_HCALL_ID(GZVM_FUNC_PROBE)
> +#define MT_HVC_GZVM_ENABLE_CAP GZVM_HCALL_ID(GZVM_FUNC_ENABLE_CAP)
> +
(move them all here)
> +int gz_err_to_errno(unsigned long err);
> +
> +#endif /* __GZVM_H__ */
..snip..
> diff --git a/drivers/soc/mediatek/virt/geniezone/gzvm_main.c b/drivers/soc/mediatek/virt/geniezone/gzvm_main.c
> new file mode 100644
> index 000000000000..1fabe4a579da
> --- /dev/null
> +++ b/drivers/soc/mediatek/virt/geniezone/gzvm_main.c
> @@ -0,0 +1,233 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2023 MediaTek Inc.
> + */
> +
> +#include <linux/anon_inodes.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/device.h>
> +#include <linux/file.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/slab.h>
> +#include "gzvm.h"
> +
> +static void (*invoke_gzvm_fn)(unsigned long, unsigned long, unsigned long,
> + unsigned long, unsigned long, unsigned long,
> + unsigned long, unsigned long,
> + struct arm_smccc_res *);
> +
> +static void gzvm_hvc(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)
> +{
> + arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
> +}
> +
> +static void gzvm_smc(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)
> +{
> + arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
> +}
Why are you wrapping HVC and SMC functions? You're not doing anything special,
so you can simply call arm_amccc_{hvc,smc}() instead of specifying new wrappers.
Or are you worried about that changing signature all of a sudden?
This is not downstream, you don't have to worry about that. :-)
> +
> +static int gzvm_probe_conduit(void)
> +{
> + struct arm_smccc_res res;
> +
> + arm_smccc_hvc(MT_HVC_GZVM_PROBE, 0, 0, 0, 0, 0, 0, 0, &res);
> + if (res.a0 == 0) {
> + invoke_gzvm_fn = gzvm_hvc;
invoke_gzvm_fn = arm_smccc_hvc;
> + return 0;
> + }
> +
> + arm_smccc_smc(MT_HVC_GZVM_PROBE, 0, 0, 0, 0, 0, 0, 0, &res);
> + if (res.a0 == 0) {
> + invoke_gzvm_fn = gzvm_smc;
invoke_gzvm_fn = arm_smccc_smc;
> + return 0;
> + }
> +
> + return -ENXIO;
> +}
> +
> +/**
> + * @brief geniezone hypercall wrapper
> + * @return int geniezone's return value will be converted to Linux errno
> + */
Kerneldoc please:
/**
* gzvm_hypcall_wrapper() - GenieZone HyperCall wrapper
* @a0: ...
* @a1: ....
* ......
* Return: Zero for success, or a negative error number.
*
* The GenieZone return values are different from Linux error codes, hence
* in case of error value, it is converted to a Linux negative error number.
*/
> +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)
> +{
> + invoke_gzvm_fn(a0, a1, a2, a3, a4, a5, a6, a7, res);
> + return gz_err_to_errno(res->a0);
> +}
> +
> +/**
> + * @brief Convert geniezone return value to standard errno
> + *
> + * @param err return value from geniezone hypercall (a0)
> + * @return int errno
> + */
> +int gz_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:
> + return -EOPNOTSUPP;
> + case ERR_NOT_IMPLEMENTED:
> + return -EOPNOTSUPP;
> + case ERR_FAULT:
> + return -EFAULT;
> + default:
default:
break;
}
return -EINVAL;
};
> +
> +static int gzvm_cap_arm_vm_ipa_size(void __user *argp)
> +{
> + u64 value = CONFIG_ARM64_PA_BITS;
> +
> + if (copy_to_user(argp, &value, sizeof(u64)))
> + return -EFAULT;
> +
> + return 0;
> +}
> +
> +/**
> + * @brief Check if given capability is support or not
> + *
> + * @param args in/out u64 pointer from userspace
> + * @retval 0: support, no error
> + * @retval -EOPNOTSUPP: not support
> + * @retval -EFAULT: failed to get data from userspace
> + */
kerneldoc again, please.
> +long gzvm_dev_ioctl_check_extension(struct gzvm *gzvm, unsigned long args)
> +{
> + int ret = -EOPNOTSUPP;
> + __u64 cap, success = 1;
> + void __user *argp = (void __user *) args;
> +
> + if (copy_from_user(&cap, argp, sizeof(uint64_t)))
> + return -EFAULT;
> +
> + switch (cap) {
> + case GZVM_CAP_ARM_PROTECTED_VM:
> + if (copy_to_user(argp, &success, sizeof(uint64_t)))
> + return -EFAULT;
> + ret = 0;
> + break;
> + case GZVM_CAP_ARM_VM_IPA_SIZE:
> + ret = gzvm_cap_arm_vm_ipa_size(argp);
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + }
> +
> + return ret;
> +}
> + > +static long gzvm_dev_ioctl(struct file *filp, unsigned int cmd,
> + unsigned long user_args)
> +{
> + long ret = -ENOTTY;
> +
> + switch (cmd) {
> + case GZVM_CREATE_VM:
> + ret = gzvm_dev_ioctl_create_vm(user_args);
> + break;
> + case GZVM_CHECK_EXTENSION:
> + if (!user_args)
> + return -EINVAL;
> + ret = gzvm_dev_ioctl_check_extension(NULL, user_args);
> + break;
> + default:
> + ret = -ENOTTY;
> + }
> +
> + return ret;
> +}
> +
> +static const struct file_operations gzvm_chardev_ops = {
> + .unlocked_ioctl = gzvm_dev_ioctl,
> + .llseek = noop_llseek,
> +};
> +
> +static struct miscdevice gzvm_dev = {
> + .minor = MISC_DYNAMIC_MINOR,
> + .name = MODULE_NAME,
> + .fops = &gzvm_chardev_ops,
> +};
> +
> +static int gzvm_drv_probe(struct platform_device *pdev)
> +{
> + if (!of_device_is_available(dev_of_node(&pdev->dev))) {
Uhm, is there something I don't get here?
This call looks odd; you're probing GZVM in this function....
> + dev_info(&pdev->dev, "GenieZone hypervisor is not available\n");
> + return -ENODEV;
> + }
> +
> + if (gzvm_probe_conduit() != 0) {
> + dev_err(&pdev->dev, "Not found available conduit\n");
> + return -ENODEV;
> + }
> +
> + return 0;
> +}
> +
> +static int gzvm_drv_remove(struct platform_device *pdev)
> +{
> + misc_deregister(&gzvm_dev);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id gzvm_of_match[] = {
> + { .compatible = "mediatek,gzvm", },
{ .compatible = "mediatek,geniezone-hyp" },
or
{ .compatible = "mediatek,geniezone" },
..makes it a bit more descriptive and human readable.
> + {},
^^^ always end with { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, gzvm_of_match);
> +
> +static struct platform_driver gzvm_driver = {
> + .probe = gzvm_drv_probe,
> + .remove = gzvm_drv_remove,
> + .driver = {
> + .name = MODULE_NAME,
> + .owner = THIS_MODULE,
> + .of_match_table = gzvm_of_match,
Fix indentation please.
> + },
> +};
> +
> +static int __init gzvm_init(void)
You don't need this at all, as all you're doing here is registering your platform
driver; and that's even a module_init(), so you can simply do
module_platform_driver(gzvm_driver);
...without open coding init/exit functions.
> +{
> + int ret = 0;
> +
> + ret = platform_driver_register(&gzvm_driver);
> + if (ret)
> + pr_err("Failed to register gzvm driver.\n");
> +
> + return ret;
> +}
> +
> +static void __exit gzvm_exit(void)
> +{
> + platform_driver_unregister(&gzvm_driver);
> +}
> +
> +module_init(gzvm_init);
> +module_exit(gzvm_exit);
> +
> +MODULE_AUTHOR("MediaTek");
> +MODULE_DESCRIPTION("GenieZone interface for VMM");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/soc/mediatek/virt/geniezone/gzvm_vcpu.c b/drivers/soc/mediatek/virt/geniezone/gzvm_vcpu.c
> new file mode 100644
> index 000000000000..726db866dfcf
> --- /dev/null
> +++ b/drivers/soc/mediatek/virt/geniezone/gzvm_vcpu.c
> @@ -0,0 +1,266 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2023 MediaTek Inc.
> + */
> +
> +#include <asm/sysreg.h>
> +#include <linux/anon_inodes.h>
> +#include <linux/file.h>
> +#include <linux/mm.h>
> +#include <linux/slab.h>
> +#include "gzvm.h"
> +
> +static int gzvm_vcpu_update_one_reg_hyp(struct gzvm_vcpu *vcpu, __u64 reg_id,
> + bool is_write, __u64 *data)
> +{
> + struct arm_smccc_res res;
> + unsigned long a1;
> + int ret;
> +
> + a1 = assemble_vm_vcpu_tuple(vcpu->gzvm->vm_id, vcpu->vcpuid);
> + if (!is_write) {
> + ret = gzvm_hypcall_wrapper(MT_HVC_GZVM_GET_ONE_REG,
> + a1, reg_id, 0, 0, 0, 0, 0, &res);
> + if (ret == 0)
> + *data = res.a1;
> + } else {
> + ret = gzvm_hypcall_wrapper(MT_HVC_GZVM_SET_ONE_REG,
> + a1, reg_id, *data, 0, 0, 0, 0, &res);
> + }
> +
> + return ret;
> +}
> +
> +static long gzvm_vcpu_update_one_reg(struct gzvm_vcpu *vcpu, void * __user argp,
> + bool is_write)
> +{
> + long ret;
> + __u64 reg_size, data = 0;
> + struct gzvm_one_reg reg;
> + void __user *reg_addr;
Please reorder those variables.
struct gzvm_one_reg reg;
void __user *reg_addr;
u64 data = 0;
u64 reg_sz;
long ret;
> +
> + if (copy_from_user(®, argp, sizeof(reg)))
> + return -EFAULT;
> + reg_addr = (void __user *)reg.addr;
> +
> + /* reg id follows KVM's encoding */
> + switch (reg.id & GZVM_REG_ARM_COPROC_MASK) {
> + case GZVM_REG_ARM_CORE:
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
Make it more readable please...
reg_size = (reg.id & GZVM_REG_SIZE_MASK) >> GZVM_REG_SIZE_SHIFT;
reg_size = BIT(reg_size);
...or, even better, you can use bitfield macros, such as FIELD_PREP(), which
would simplify that even more.
> + reg_size = 1 << ((reg.id & GZVM_REG_SIZE_MASK) >> GZVM_REG_SIZE_SHIFT);
> + if (is_write) {
> + if (copy_from_user(&data, reg_addr, reg_size))
> + return -EFAULT;
> + }
> +
> + ret = gzvm_vcpu_update_one_reg_hyp(vcpu, reg.id, is_write, &data);
> +
if (ret)
return ret;
if (!is_write) {
if (copy_to_user.....)
return -EFAULT;
}
return 0;
> + if (!is_write && ret == 0) {
> + if (copy_to_user(reg_addr, &data, reg_size))
> + return -EFAULT;
> + }
> +
> + return ret;
> +}
> +
> +/**
> + * @brief Handle vcpu run ioctl, entry point to guest and exit point from guest
> + *
> + * @param filp
> + * @param argp pointer to struct gzvm_vcpu_run in userspace
> + * @return long
> + */
> +static long gzvm_vcpu_run(struct gzvm_vcpu *vcpu, void * __user argp)
> +{
> + unsigned long id_tuple;
> + struct arm_smccc_res res;
> + bool need_userspace = false;
> +
> + if (copy_from_user(vcpu->run, argp, sizeof(struct gzvm_vcpu_run)))
> + return -EFAULT;
> +
> + if (vcpu->run->immediate_exit == 1)
> + return -EINTR;
> +
> + id_tuple = assemble_vm_vcpu_tuple(vcpu->gzvm->vm_id, vcpu->vcpuid);
> + do {
> + gzvm_hypcall_wrapper(MT_HVC_GZVM_RUN, id_tuple, 0, 0, 0, 0, 0,
> + 0, &res);
> + switch (res.a1) {
> + case GZVM_EXIT_MMIO:
> + need_userspace = true;
> + break;
> + /*
> + * geniezone's responsibility to fill corresponding data
> + * structure
> + */
> + case GZVM_EXIT_HVC:
/* fallthrough */
> + case GZVM_EXIT_EXCEPTION:
/* fallthrough */
> + case GZVM_EXIT_DEBUG:
/* fallthrough */
> + case GZVM_EXIT_FAIL_ENTRY:
/* fallthrough */
> + case GZVM_EXIT_INTERNAL_ERROR:
/* fallthrough */
> + case GZVM_EXIT_SYSTEM_EVENT:
/* fallthrough */
> + case GZVM_EXIT_SHUTDOWN:
> + need_userspace = true;
> + break;
> + case GZVM_EXIT_IRQ:
> + break;
> + case GZVM_EXIT_UNKNOWN:
/* fallthrough */
> + default:
> + pr_err("vcpu unknown exit\n");
Also, please use dev_err() when possible.
> + need_userspace = true;
> + goto out;
> + }
> + } while (!need_userspace);
> +
> +out:
> + if (copy_to_user(argp, vcpu->run, sizeof(struct gzvm_vcpu_run)))
> + return -EFAULT;
> + return 0;
> +}
> +
> +static long gzvm_vcpu_ioctl(struct file *filp, unsigned int ioctl,
> + unsigned long arg)
> +{
> + int ret = -ENOTTY;
> + void __user *argp = (void __user *)arg;
> + struct gzvm_vcpu *vcpu = filp->private_data;
> +
> + switch (ioctl) {
> + case GZVM_RUN:
> + ret = gzvm_vcpu_run(vcpu, argp);
> + break;
> + case GZVM_GET_ONE_REG:
> + ret = gzvm_vcpu_update_one_reg(vcpu, argp, false /*is_write*/);
> + break;
> + case GZVM_SET_ONE_REG:
> + ret = gzvm_vcpu_update_one_reg(vcpu, argp, true /*is_write*/);
> + break;
> + default:
> + ret = -ENOTTY;
instead of initializing `ret`, you can just....
return -ENOTTY;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static const struct file_operations gzvm_vcpu_fops = {
> + .unlocked_ioctl = gzvm_vcpu_ioctl,
> + .llseek = noop_llseek,
> +};
> +
> +static int gzvm_destroy_vcpu_hyp(gzvm_id_t vm_id, int vcpuid)
> +{
> + struct arm_smccc_res res;
> + unsigned long a1;
> +
> + a1 = assemble_vm_vcpu_tuple(vm_id, vcpuid);
> + gzvm_hypcall_wrapper(MT_HVC_GZVM_DESTROY_VCPU, a1, 0, 0, 0, 0, 0, 0,
> + &res);
> +
> + return 0;
> +}
> +
> +/**
> + * @brief call smc to gz hypervisor to create vcpu
> + *
> + * @param run virtual address of vcpu->run
> + * @return int
> + */
kerneldoc please
> +static int gzvm_create_vcpu_hyp(gzvm_id_t vm_id, int vcpuid, void *run)
> +{
> + struct arm_smccc_res res;
> + unsigned long a1, a2;
> + int ret;
> +
> + a1 = assemble_vm_vcpu_tuple(vm_id, vcpuid);
> + a2 = (__u64)virt_to_phys(run);
> + ret = gzvm_hypcall_wrapper(MT_HVC_GZVM_CREATE_VCPU, a1, a2, 0, 0, 0, 0,
> + 0, &res);
> +
> + return ret;
> +}
> +
> +/* Caller must hold the vm lock */
> +void gzvm_destroy_vcpu(struct gzvm_vcpu *vcpu)
> +{
> + if (!vcpu)
> + return;
> +
> + gzvm_destroy_vcpu_hyp(vcpu->gzvm->vm_id, vcpu->vcpuid);
> + /* clean guest's data */
> + memset(vcpu->run, 0, GZVM_VCPU_RUN_MAP_SIZE);
> + free_pages_exact(vcpu->run, GZVM_VCPU_RUN_MAP_SIZE);
> + kfree(vcpu);
> +}
> +
> +#define ITOA_MAX_LEN 12 /* Maximum size needed for holding an integer. */
Move definitions at the beginning of the file, please.
> +/**
> + * @brief Allocates an inode for the vcpu.
> + */
Kerneldoc!
> +static int create_vcpu_fd(struct gzvm_vcpu *vcpu)
> +{
> + /* sizeof("gzvm-vcpu:") + max(strlen(itoa(vcpuid))) + null */
> + char name[10 + ITOA_MAX_LEN + 1];
> +
> + snprintf(name, sizeof(name), "gzvm-vcpu:%d", vcpu->vcpuid);
> + return anon_inode_getfd(name, &gzvm_vcpu_fops, vcpu, O_RDWR | O_CLOEXEC);
> +}
> +
> +/**
> + * @brief GZVM_CREATE_VCPU
> + *
> + * @param cpuid = arg
> + * @return fd of vcpu, negative errno if error occurs
> + */
kerneldoc!!!! :-)
> +int gzvm_vm_ioctl_create_vcpu(struct gzvm *gzvm, u32 cpuid)
> +{
> + struct gzvm_vcpu *vcpu;
> + int ret;
> +
> + if (cpuid >= GZVM_MAX_VCPUS)
> + return -EINVAL;
> +
> + vcpu = kzalloc(sizeof(*vcpu), GFP_KERNEL);
> + if (!vcpu)
> + return -ENOMEM;
> +
> + BUILD_BUG_ON((sizeof(*vcpu->run)) > PAGE_SIZE);
> + BUILD_BUG_ON(sizeof(struct gzvm_vcpu_hwstate) > PAGE_SIZE);
Do you really need to crash the kernel?!
You must have a very, very good reason to do that, please justify that carefully
and also write that as a comment in this function.
> + /**
> + * allocate 2 pages for data sharing between driver and gz hypervisor
> + * |- page 0 -|- page 1 -|
> + * |gzvm_vcpu_run|......|hwstate|.......|
> + */
> + vcpu->run = alloc_pages_exact(GZVM_VCPU_RUN_MAP_SIZE,
> + GFP_KERNEL_ACCOUNT | __GFP_ZERO);
> + if (!vcpu->run) {
> + ret = -ENOMEM;
> + goto free_vcpu;
> + }
> + vcpu->hwstate = (void *)vcpu->run + PAGE_SIZE;
> + vcpu->vcpuid = cpuid;
> + vcpu->gzvm = gzvm;
> + mutex_init(&vcpu->lock);
> +
> + ret = gzvm_create_vcpu_hyp(gzvm->vm_id, vcpu->vcpuid, vcpu->run);
> + if (ret < 0)
> + goto free_vcpu_run;
> +
> + ret = create_vcpu_fd(vcpu);
> + if (ret < 0)
> + goto free_vcpu_run;
> + gzvm->vcpus[cpuid] = vcpu;
> +
> + return ret;
> +
> +free_vcpu_run:
> + free_pages_exact(vcpu->run, GZVM_VCPU_RUN_MAP_SIZE);
> +free_vcpu:
> + kfree(vcpu);
> + return ret;
> +}
> diff --git a/drivers/soc/mediatek/virt/geniezone/gzvm_vm.c b/drivers/soc/mediatek/virt/geniezone/gzvm_vm.c
> new file mode 100644
> index 000000000000..df4ccdc3b7f0
> --- /dev/null
> +++ b/drivers/soc/mediatek/virt/geniezone/gzvm_vm.c
> @@ -0,0 +1,444 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2023 MediaTek Inc.
> + */
> +
> +#include <linux/anon_inodes.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/file.h>
> +#include <linux/kdev_t.h>
> +#include <linux/kvm_host.h>
> +#include <linux/miscdevice.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/version.h>
> +#include "gzvm.h"
> +
> +static DEFINE_MUTEX(gzvm_list_lock);
> +static LIST_HEAD(gzvm_list);
> +
> +
> +/**
> + * @brief Translate gfn (guest ipa) to pfn (host pa), result is in @pfn
> + *
> + * Leverage KVM's `gfn_to_pfn_memslot`. Because `gfn_to_pfn_memslot` needs
> + * kvm_memory_slot as parameter, this function populates necessary fileds
> + * for calling `gfn_to_pfn_memslot`.
> + *
> + * @retval 0 succeed
> + * @retval -EFAULT failed to convert
> + */
kerneldoc please
> +int gzvm_gfn_to_pfn_memslot(struct gzvm_memslot *memslot, u64 gfn, u64 *pfn)
> +{
> + hfn_t __pfn;
> + struct kvm_memory_slot kvm_slot = {0};
> +
> + kvm_slot.base_gfn = memslot->base_gfn;
> + kvm_slot.npages = memslot->npages;
> + kvm_slot.dirty_bitmap = NULL;
> + kvm_slot.userspace_addr = memslot->userspace_addr;
> + kvm_slot.flags = memslot->flags;
> + kvm_slot.id = memslot->slot_id;
> + kvm_slot.as_id = 0;
> +
> + __pfn = gfn_to_pfn_memslot(&kvm_slot, gfn);
> + if (is_error_noslot_pfn(__pfn)) {
> + *pfn = 0;
> + return -EFAULT;
> + }
> +
> + *pfn = __pfn;
> + return 0;
> +}
> +
> +/**
> + * @brief Populate pa to buffer until full
> + *
> + * @return int how much pages we've fill in, negative if error
> + */
kerneldoc
> +static int fill_constituents(struct mem_region_addr_range *consti,
> + int *consti_cnt, int max_nr_consti, gfn_t gfn,
> + u32 total_pages, struct gzvm_memslot *slot)
> +{
> + int i, nr_pages;
> + hfn_t pfn, prev_pfn;
> + gfn_t gfn_end;
hfn_t pfn, prev_pfn;
gfn_t gfn_end;
int nr_pages = 1;
int i = 0;
> +
> + if (unlikely(total_pages == 0))
> + return -EINVAL;
> + gfn_end = gfn + total_pages;
> +
> + /* entry 0 */
> + if (gzvm_gfn_to_pfn_memslot(slot, gfn, &pfn) != 0)
> + return -EFAULT;
> + consti[0].address = PFN_PHYS(pfn);
> + consti[0].pg_cnt = 1;
> + gfn++;
> + prev_pfn = pfn;
> + i = 0;
> + nr_pages = 1;
stack initialized, remove...
> + while (i < max_nr_consti && gfn < gfn_end) {
> + if (gzvm_gfn_to_pfn_memslot(slot, gfn, &pfn) != 0)
> + return -EFAULT;
> + if (pfn == (prev_pfn + 1)) {
> + consti[i].pg_cnt++;
> + } else {
> + i++;
> + if (i >= max_nr_consti)
> + break;
> + consti[i].address = PFN_PHYS(pfn);
> + consti[i].pg_cnt = 1;
> + }
> + prev_pfn = pfn;
> + gfn++;
> + nr_pages++;
> + }
if (i == max_nr_consti)
i++;
*consti_cnt = i;
> + if (i == max_nr_consti)
> + *consti_cnt = i;
> + else
> + *consti_cnt = (i + 1);
> +
> + return nr_pages;
> +}
> +
> +/**
> + * @brief Register memory region to GZ
> + *
> + * @param gzvm
> + * @param memslot
> + * @return int
> + */
kerneldoc
> +static int
> +register_memslot_addr_range(struct gzvm *gzvm, struct gzvm_memslot *memslot)
> +{
> + struct gzvm_memory_region_ranges *region;
> + u32 buf_size;
> + int max_nr_consti, remain_pages;
> + gfn_t gfn, gfn_end;
struct gzvm_memory_region_ranges *region;
gfn_t gfn, gfn_end;
int max_nr_consti, remain_pages;
u32 buf_size;
> +
> + buf_size = PAGE_SIZE * 2;
> + region = alloc_pages_exact(buf_size, GFP_KERNEL);
> + if (!region)
> + return -ENOMEM;
> + max_nr_consti = (buf_size - sizeof(*region)) /
> + sizeof(struct mem_region_addr_range);
> +
> + region->slot = memslot->slot_id;
> + remain_pages = memslot->npages;
> + gfn = memslot->base_gfn;
> + gfn_end = gfn + remain_pages;
> + while (gfn < gfn_end) {
> + struct arm_smccc_res res;
> + int nr_pages;
> +
> + nr_pages = fill_constituents(region->constituents,
> + ®ion->constituent_cnt,
> + max_nr_consti, gfn,
> + remain_pages, memslot);
> + region->gpa = PFN_PHYS(gfn);
> + region->total_pages = nr_pages;
> +
> + remain_pages -= nr_pages;
> + gfn += nr_pages;
> +
> + gzvm_hypcall_wrapper(MT_HVC_GZVM_SET_MEMREGION, gzvm->vm_id,
> + buf_size, virt_to_phys(region), 0, 0, 0, 0, &res);
> +
> + if (res.a0 != 0) {
> + pr_err("Failed to register memregion to hypervisor\n");
dev_err() please
> + free_pages_exact(region, buf_size);
> + return -EFAULT;
> + }
> + }
> + free_pages_exact(region, buf_size);
> + return 0;
> +}
> +
> +/**
> + * @brief Set memory region of guest
> + *
> + * @param gzvm struct gzvm
> + * @param mem struct gzvm_userspace_memory_region: input from user
> + * @retval -EXIO memslot is out-of-range
> + * @retval -EFAULT cannot find corresponding vma
> + * @retval -EINVAL region size and vma size does not match
> + */
kerneldoc
> +static int gzvm_vm_ioctl_set_memory_region(struct gzvm *gzvm,
> + struct gzvm_userspace_memory_region *mem)
> +{
> + struct vm_area_struct *vma;
> + struct gzvm_memslot *memslot;
> + unsigned long size;
> + __u32 slot;
> +
> + slot = mem->slot;
> + if (slot >= GZVM_MAX_MEM_REGION)
> + return -ENXIO;
> + memslot = &gzvm->memslot[slot];
> +
> + vma = vma_lookup(gzvm->mm, mem->userspace_addr);
> + if (!vma)
> + return -EFAULT;
> +
> + size = vma->vm_end - vma->vm_start;
> + if (size != mem->memory_size)
> + return -EINVAL;
> +
> + memslot->base_gfn = __phys_to_pfn(mem->guest_phys_addr);
> + memslot->npages = size >> PAGE_SHIFT;
> + memslot->userspace_addr = mem->userspace_addr;
> + memslot->vma = vma;
> + memslot->flags = mem->flags;
> + memslot->slot_id = mem->slot;
> + return register_memslot_addr_range(gzvm, memslot);
> +}
> +
> +static int gzvm_vm_enable_cap_hyp(struct gzvm *gzvm,
> + struct gzvm_enable_cap *cap,
> + struct arm_smccc_res *res)
Please don't introduce new functions doing just one call.
> +{
> + int ret;
> +
> + ret = gzvm_hypcall_wrapper(MT_HVC_GZVM_ENABLE_CAP, gzvm->vm_id,
> + cap->cap, cap->args[0], cap->args[1],
> + cap->args[2], cap->args[3], cap->args[4],
> + res); > + return ret;
> +}
> +
> +/**
> + * @brief Get pvmfw size from hypervisor, return in x1, and return to userspace
> + * in args[1].
> + * @retval 0 succeed
> + * @retval -EINVAL hypervisor return invalid results
> + * @retval -EFAULT fail to copy back to userspace buffer
> + */
kerneldoc ..... here and everywhere else, I will stop saying that every time.
> +static int gzvm_vm_ioctl_get_pvmfw_size(struct gzvm *gzvm,
> + struct gzvm_enable_cap *cap,
> + void __user *argp)
> +{
> + struct arm_smccc_res res = {0};
> +
> + if (gzvm_vm_enable_cap_hyp(gzvm, cap, &res) != 0)
> + return -EINVAL;
> +
> + cap->args[1] = res.a1;
> + if (copy_to_user(argp, cap, sizeof(*cap)))
> + return -EFAULT;
> +
> + return 0;
> +}
> +
> +/**
> + * @brief Proceed GZVM_CAP_ARM_PROTECTED_VM's subcommands
> + * @retval 0 succeed
> + * @retval -EINVAL invalid subcommand or arguments
> + */
> +static int gzvm_vm_ioctl_cap_pvm(struct gzvm *gzvm, struct gzvm_enable_cap *cap,
> + void __user *argp)
> +{
> + int ret = -EINVAL;
> + struct arm_smccc_res res = {0};
> +
> + switch (cap->args[0]) {
> + case GZVM_CAP_ARM_PVM_SET_PVMFW_IPA:
> + ret = gzvm_vm_enable_cap_hyp(gzvm, cap, &res);
> + break;
> + case GZVM_CAP_ARM_PVM_GET_PVMFW_SIZE:
> + ret = gzvm_vm_ioctl_get_pvmfw_size(gzvm, cap, argp);
> + break;
> + default:
> + ret = -EINVAL;
return -EINVAL;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int gzvm_vm_ioctl_enable_cap(struct gzvm *gzvm,
> + struct gzvm_enable_cap *cap,
> + void __user *argp)
> +{
> + int ret = -EINVAL;
> +
> + switch (cap->cap) {
> + case GZVM_CAP_ARM_PROTECTED_VM:
> + ret = gzvm_vm_ioctl_cap_pvm(gzvm, cap, argp);
> + break;
> + default:
> + ret = -EINVAL;
return -EINVAL;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +/**
> + * @brief ioctl handler of VM FD
> + */
> +static long gzvm_vm_ioctl(struct file *filp, unsigned int ioctl,
> + unsigned long arg)
> +{
> + long ret = -ENOTTY;
> + void __user *argp = (void __user *)arg;
> + struct gzvm *gzvm = filp->private_data;
> +
> + switch (ioctl) {
> + case GZVM_CHECK_EXTENSION:
> + ret = gzvm_dev_ioctl_check_extension(gzvm, arg);
> + break;
> + case GZVM_CREATE_VCPU:
> + ret = gzvm_vm_ioctl_create_vcpu(gzvm, arg);
> + break;
> + case GZVM_SET_USER_MEMORY_REGION: {
> + struct gzvm_userspace_memory_region userspace_mem;
> +
> + ret = -EFAULT;
> + if (copy_from_user(&userspace_mem, argp,
> + sizeof(userspace_mem)))
> + goto out;
> + ret = gzvm_vm_ioctl_set_memory_region(gzvm, &userspace_mem);
> + break;
> + }
> + case GZVM_ENABLE_CAP: {
> + struct gzvm_enable_cap cap;
> +
> + ret = -EFAULT;
> + if (copy_from_user(&cap, argp, sizeof(cap)))
> + goto out;
> +
> + ret = gzvm_vm_ioctl_enable_cap(gzvm, &cap, argp);
> + break;
> + }
> + default:
> + ret = -ENOTTY;
return -ENOTTY;
> + }
> +out:
> + return ret;
> +}
> +
..snip..
> +
> +static void gzvm_destroy_vm(struct gzvm *gzvm)
> +{ > + pr_info("VM-%u is going to be destroyed\n", gzvm->vm_id);
dev_info() please.
> +
> + mutex_lock(&gzvm->lock);
> +
> + gzvm_destroy_vcpus(gzvm);
> + gzvm_destroy_vm_hyp(gzvm->vm_id);
> +
> + mutex_lock(&gzvm_list_lock);
> + list_del(&gzvm->vm_list);
> + mutex_unlock(&gzvm_list_lock);
> +
> + mutex_unlock(&gzvm->lock);
> +
> + kfree(gzvm);
> +}
> +
> +static int gzvm_vm_release(struct inode *inode, struct file *filp)
> +{
> + struct gzvm *gzvm = filp->private_data;
> +
> + gzvm_destroy_vm(gzvm);
> + return 0;
> +}
> +
> +static const struct file_operations gzvm_vm_fops = {
> + .release = gzvm_vm_release,
> + .unlocked_ioctl = gzvm_vm_ioctl,
> + .llseek = noop_llseek,
> +};
> +
> +static int gzvm_create_vm_hyp(void)
> +{
> + struct arm_smccc_res res;
> +
> + gzvm_hypcall_wrapper(MT_HVC_GZVM_CREATE_VM, 0, 0, 0, 0, 0, 0, 0, &res);
> +
> + if (res.a0 != 0)
> + return -EFAULT;
> + return res.a1;
> +}
> +
> +static struct gzvm *gzvm_create_vm(unsigned long vm_type)
> +{
> + int ret;
> + struct gzvm *gzvm;
> +
> + gzvm = kzalloc(sizeof(struct gzvm), GFP_KERNEL);
> + if (!gzvm)
> + return ERR_PTR(-ENOMEM);
> +
> + ret = gzvm_create_vm_hyp();
> + if (ret < 0)
> + goto err;
You're doing that only once in this function, so you don't need a label.
if (ret) {
kfree(gzvm);
return ERR_PTR(ret);
}
> +
> + gzvm->vm_id = ret;
> + gzvm->mm = current->mm;
> + mutex_init(&gzvm->lock);
> + INIT_LIST_HEAD(&gzvm->devices);
> + mutex_init(&gzvm->irq_lock);
> + pr_info("VM-%u is created\n", gzvm->vm_id);
> +
> + mutex_lock(&gzvm_list_lock);
> + list_add(&gzvm->vm_list, &gzvm_list);
> + mutex_unlock(&gzvm_list_lock);
> +
> + return gzvm;
> +
> +err:
> + kfree(gzvm);
> + return ERR_PTR(ret);
> +}
> +
> +/**
> + * @brief create vm fd
> + *
> + * @param vm_type
> + * @return int fd of vm, negative if error
> + */
> +int gzvm_dev_ioctl_create_vm(unsigned long vm_type)
> +{
> + struct gzvm *gzvm;
> + int ret;
> +
> + gzvm = gzvm_create_vm(vm_type);
if (IS_ERR(gzvm))
return PTR_ERR(gzvm);
> + if (IS_ERR(gzvm)) {
> + ret = PTR_ERR(gzvm);
> + goto error;
> + }
> +
> + ret = anon_inode_getfd("gzvm-vm", &gzvm_vm_fops, gzvm,
> + O_RDWR | O_CLOEXEC);
if (ret)
return ret;
return 0;
}
> + if (ret < 0)
> + goto error;
> +
> +error:
> + return ret;
> +}
...but anyway, all of this must go to drivers/virt/.
Regards,
Angelo
More information about the Linux-mediatek
mailing list