forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
VMM use this interface to create vcpu instance which is a fd, and this fd will be for any vcpu operations, such as setting vcpu registers and accepts the most important ioctl GZVM_VCPU_RUN which requests GenieZone hypervisor to do context switch to execute VM's vcpu context. Signed-off-by: Yingshiuan Pan <yingshiuan.pan@mediatek.com> Signed-off-by: Jerry Wang <ze-yu.wang@mediatek.com> Signed-off-by: Yi-De Wu <yi-de.wu@mediatek.com>
- Loading branch information
1 parent
0e3f05a
commit d4ced06
Showing
8 changed files
with
519 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* | ||
* Copyright (c) 2023 MediaTek Inc. | ||
*/ | ||
|
||
#include <asm/sysreg.h> | ||
#include <linux/anon_inodes.h> | ||
#include <linux/device.h> | ||
#include <linux/file.h> | ||
#include <linux/mm.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/slab.h> | ||
#include <linux/gzvm_drv.h> | ||
|
||
/* maximum size needed for holding an integer */ | ||
#define ITOA_MAX_LEN 12 | ||
|
||
static long gzvm_vcpu_update_one_reg(struct gzvm_vcpu *vcpu, void * __user argp, | ||
bool is_write) | ||
{ | ||
struct gzvm_one_reg reg; | ||
void __user *reg_addr; | ||
u64 data = 0; | ||
u64 reg_size; | ||
long ret; | ||
|
||
if (copy_from_user(®, argp, sizeof(reg))) | ||
return -EFAULT; | ||
|
||
reg_addr = (void __user *)reg.addr; | ||
reg_size = (reg.id & GZVM_REG_SIZE_MASK) >> GZVM_REG_SIZE_SHIFT; | ||
reg_size = BIT(reg_size); | ||
|
||
if (is_write) { | ||
if (copy_from_user(&data, reg_addr, reg_size)) | ||
return -EFAULT; | ||
} | ||
|
||
ret = gzvm_arch_vcpu_update_one_reg(vcpu, reg.id, is_write, &data); | ||
|
||
if (ret) | ||
return ret; | ||
|
||
if (!is_write) { | ||
if (copy_to_user(reg_addr, &data, reg_size)) | ||
return -EFAULT; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
/** | ||
* gzvm_vcpu_run() - Handle vcpu run ioctl, entry point to guest and exit | ||
* point from guest | ||
* @argp: pointer to struct gzvm_vcpu_run in userspace | ||
*/ | ||
static long gzvm_vcpu_run(struct gzvm_vcpu *vcpu, void * __user argp) | ||
{ | ||
bool need_userspace = false; | ||
u64 exit_reason; | ||
|
||
if (copy_from_user(vcpu->run, argp, sizeof(struct gzvm_vcpu_run))) | ||
return -EFAULT; | ||
|
||
if (vcpu->run->immediate_exit == 1) | ||
return -EINTR; | ||
|
||
while (!need_userspace && !signal_pending(current)) { | ||
gzvm_arch_vcpu_run(vcpu, &exit_reason); | ||
|
||
switch (exit_reason) { | ||
case GZVM_EXIT_MMIO: | ||
need_userspace = true; | ||
break; | ||
/** | ||
* it's geniezone's responsibility to fill corresponding data | ||
* structure | ||
*/ | ||
case GZVM_EXIT_HYPERCALL: | ||
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: | ||
dev_err(&gzvm_debug_dev->dev, "vcpu unknown exit\n"); | ||
need_userspace = true; | ||
goto out; | ||
} | ||
} | ||
|
||
out: | ||
if (copy_to_user(argp, vcpu->run, sizeof(struct gzvm_vcpu_run))) | ||
return -EFAULT; | ||
if (signal_pending(current)) | ||
return -ERESTARTSYS; | ||
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: | ||
/* is_write */ | ||
ret = gzvm_vcpu_update_one_reg(vcpu, argp, false); | ||
break; | ||
case GZVM_SET_ONE_REG: | ||
/* is_write */ | ||
ret = gzvm_vcpu_update_one_reg(vcpu, argp, true); | ||
break; | ||
default: | ||
break; | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
static const struct file_operations gzvm_vcpu_fops = { | ||
.unlocked_ioctl = gzvm_vcpu_ioctl, | ||
.llseek = noop_llseek, | ||
}; | ||
|
||
/* caller must hold the vm lock */ | ||
void gzvm_destroy_vcpu(struct gzvm_vcpu *vcpu) | ||
{ | ||
if (!vcpu) | ||
return; | ||
|
||
gzvm_arch_destroy_vcpu(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); | ||
} | ||
|
||
/** | ||
* gzvm_destroy_vcpus() - Destroy all vcpus, caller has to hold the vm lock | ||
* | ||
* @gzvm: vm struct that owns the vcpus | ||
*/ | ||
void gzvm_destroy_vcpus(struct gzvm *gzvm) | ||
{ | ||
int i; | ||
|
||
for (i = 0; i < GZVM_MAX_VCPUS; i++) { | ||
gzvm_destroy_vcpu(gzvm->vcpus[i]); | ||
gzvm->vcpus[i] = NULL; | ||
} | ||
} | ||
|
||
/* create_vcpu_fd() - Allocates an inode for the vcpu. */ | ||
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); | ||
} | ||
|
||
/** | ||
* gzvm_vm_ioctl_create_vcpu() | ||
* | ||
* @cpuid: equals arg | ||
* | ||
* Return: Fd of vcpu, negative errno if error occurs | ||
*/ | ||
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; | ||
|
||
/** | ||
* 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->vcpuid = cpuid; | ||
vcpu->gzvm = gzvm; | ||
mutex_init(&vcpu->lock); | ||
|
||
ret = gzvm_arch_create_vcpu(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; | ||
} |
Oops, something went wrong.