* [PATCH 4/4] [Hypercall] Allow modules registeration for hypercalls.
@ 2007-08-24 23:59 Dor Laor
[not found] ` <64F9B87B6B770947A9F8391472E032160D59004F-yEcIvxbTEBqsx+V+t5oei8rau4O3wl8o3fe8/T/H7NteoWH0uzbU5w@public.gmane.org>
0 siblings, 1 reply; 4+ messages in thread
From: Dor Laor @ 2007-08-24 23:59 UTC (permalink / raw)
To: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
New kernel modules for KVM are upcoming soon, these module
will need to use hypercalls. Before calling the hypercall function,
the kvm_main core module has to make sure it won't get unloaded.
So hypercall register/unregister are added.
Except that the kernel hypercalls handlers are numberes
0-KVM_NR_HYPERCALLS.
All the userspace handlers are above KVM_NR_HYPERCALLS.
Signed-off-by: Dor Laor <dor.laor-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
---
drivers/kvm/kvm.h | 9 ++
drivers/kvm/kvm_main.c | 223
++++++++++++++++++++++++++++++++++++++--------
include/linux/kvm_para.h | 4 +-
3 files changed, 197 insertions(+), 39 deletions(-)
mode change 100644 => 100755 drivers/kvm/kvm_main.c
diff --git a/drivers/kvm/kvm.h b/drivers/kvm/kvm.h
index a42a6f3..37240cf 100644
--- a/drivers/kvm/kvm.h
+++ b/drivers/kvm/kvm.h
@@ -472,6 +472,12 @@ struct kvm_arch_ops {
unsigned char *hypercall_addr);
};
+struct kvm_hypercall {
+ unsigned long (*hypercall)(struct kvm_vcpu*, unsigned long
args[]);
+ struct module *module;
+ int idx;
+};
+
extern struct kvm_arch_ops *kvm_arch_ops;
/* The guest did something we don't support. */
@@ -588,6 +594,9 @@ int kvm_mmu_load(struct kvm_vcpu *vcpu);
void kvm_mmu_unload(struct kvm_vcpu *vcpu);
int kvm_hypercall(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_register_hypercall(struct module *module, struct kvm_hypercall
*hypercall);
+int kvm_unregister_hypercall(struct kvm_hypercall *hypercall);
+
static inline int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva,
u32 error_code)
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c
old mode 100644
new mode 100755
index abd7498..051b47a
--- a/drivers/kvm/kvm_main.c
+++ b/drivers/kvm/kvm_main.c
@@ -20,6 +20,7 @@
#include "segment_descriptor.h"
#include <linux/kvm.h>
+#include <linux/kvm_para.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/percpu.h>
@@ -84,6 +85,23 @@ static struct kvm_stats_debugfs_item {
static struct dentry *debugfs_dir;
+static struct kvm_hypercall hypercalls[KVM_KERNEL_NR_HYPERCALLS];
+
+static int test_hypercall(struct kvm_vcpu *vcpu, unsigned long args[])
+{
+ printk(KERN_DEBUG "%s: hypercall invoked\n", __FUNCTION__);
+
+ return 0;
+}
+
+static struct kvm_hypercall __hypercall_test = {
+ (unsigned long (*)(struct kvm_vcpu*, unsigned long
args[]))test_hypercall,
+ THIS_MODULE,
+ __NR_hypercall_test,
+};
+
+static atomic_t dev_kvm_open_count = ATOMIC_INIT(0);
+
#define MAX_IO_MSRS 256
#define CR0_RESERVED_BITS
\
@@ -302,6 +320,24 @@ static struct kvm *kvm_create_vm(void)
return kvm;
}
+static int kvm_dev_open(struct inode *inode, struct file *filp)
+{
+ int ret = 0;
+
+ if (atomic_inc_return(&dev_kvm_open_count) == 1) {
+ ret = kvm_register_hypercall(THIS_MODULE,
&__hypercall_test);
+ if (ret < 0) {
+ printk(KERN_DEBUG "%s: kvm_register_hypercall
error, "
+ "hypercall: %s\n",
+ __FUNCTION__, "test");
+ goto out_test;
+ }
+ }
+
+out_test:
+ return ret;
+}
+
/*
* Free any memory in @free but not in @dont.
*/
@@ -371,6 +407,20 @@ static void kvm_free_vcpus(struct kvm *kvm)
}
+static int kvm_dev_release(struct inode *inode, struct file *filp)
+{
+ int ret = 0;
+
+ atomic_dec(&dev_kvm_open_count);
+ ret = kvm_unregister_hypercall(&__hypercall_test);
+ if (ret < 0) {
+ printk(KERN_DEBUG "%s:kvm_unregister_hypercall error "
+ "hypercall: %s\n", __FUNCTION__,
"hypercall_test");
+ }
+
+ return ret;
+}
+
static void kvm_destroy_vm(struct kvm *kvm)
{
spin_lock(&kvm_lock);
@@ -1267,50 +1317,145 @@ int kvm_emulate_halt(struct kvm_vcpu *vcpu)
}
EXPORT_SYMBOL_GPL(kvm_emulate_halt);
-int kvm_hypercall(struct kvm_vcpu *vcpu, struct kvm_run *run)
+int kvm_register_hypercall(struct module* module, struct kvm_hypercall
*hypercall)
{
- unsigned long nr, a0, a1, a2, a3, a4, a5, ret;
+ int r = 0;
- kvm_arch_ops->cache_regs(vcpu);
- ret = -KVM_EINVAL;
-#ifdef CONFIG_X86_64
- if (is_long_mode(vcpu)) {
- nr = vcpu->regs[VCPU_REGS_RAX];
- a0 = vcpu->regs[VCPU_REGS_RDI];
- a1 = vcpu->regs[VCPU_REGS_RSI];
- a2 = vcpu->regs[VCPU_REGS_RDX];
- a3 = vcpu->regs[VCPU_REGS_RCX];
- a4 = vcpu->regs[VCPU_REGS_R8];
- a5 = vcpu->regs[VCPU_REGS_R9];
- } else
-#endif
- {
+ if (hypercall->idx >= KVM_KERNEL_NR_HYPERCALLS ||
+ hypercall->idx < 0) {
+ printk(KERN_DEBUG "%s:hypercall registration idx(%d)\n",
+ __FUNCTION__, hypercall->idx);
+ return -EINVAL;
+ }
+
+ spin_lock(&kvm_lock);
+
+ if (hypercalls[hypercall->idx].hypercall) {
+ printk(KERN_DEBUG "%s:hypercall idx(%d) already
taken\n",
+ __FUNCTION__, hypercall->idx);
+ r = -EEXIST;
+ goto out;
+ }
+
+ if (try_module_get(module) < 0) {
+ printk(KERN_DEBUG "%s: module reference count++
failed\n",
+ __FUNCTION__);
+ r = -EINVAL;
+ goto out;
+ }
+
+ hypercalls[hypercall->idx].hypercall = hypercall->hypercall;
+ hypercalls[hypercall->idx].module = module;
+
+out:
+ spin_unlock(&kvm_lock);
+
+ return r;
+}
+EXPORT_SYMBOL_GPL(kvm_register_hypercall);
+
+int kvm_unregister_hypercall(struct kvm_hypercall *hypercall)
+{
+ if (hypercall->idx >= KVM_KERNEL_NR_HYPERCALLS ||
+ hypercall->idx < 0) {
+ printk(KERN_DEBUG "%s:hypercall unregistration
idx(%d)\n",
+ __FUNCTION__, hypercall->idx);
+ return -EINVAL;
+ }
+
+ spin_lock(&kvm_lock);
+ if (!hypercalls[hypercall->idx].hypercall) {
+ printk(KERN_DEBUG "%s:hypercall idx(%d) was not
registered\n",
+ __FUNCTION__, hypercall->idx);
+ spin_unlock(&kvm_lock);
+ return -EEXIST;
+ }
+
+ hypercalls[hypercall->idx].hypercall = 0;
+ module_put(hypercalls[hypercall->idx].module);
+ spin_unlock(&kvm_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kvm_unregister_hypercall);
+
+/*
+ * Generic hypercall dispatcher routine.
+ * Returns 0 for user space handling, 1 on success handling
+ */
+ int kvm_hypercall(struct kvm_vcpu *vcpu, struct kvm_run *run)
+ {
+ unsigned long nr, ret;
+ unsigned long args[6];
+
+ kvm_arch_ops->cache_regs(vcpu);
+ ret = -KVM_EINVAL;
+ #ifdef CONFIG_X86_64
+ if (is_long_mode(vcpu)) {
+ nr = vcpu->regs[VCPU_REGS_RAX];
+ args[0] = vcpu->regs[VCPU_REGS_RDI];
+ args[1] = vcpu->regs[VCPU_REGS_RSI];
+ args[2] = vcpu->regs[VCPU_REGS_RDX];
+ args[3] = vcpu->regs[VCPU_REGS_RCX];
+ args[4] = vcpu->regs[VCPU_REGS_R8];
+ args[5] = vcpu->regs[VCPU_REGS_R9];
+ } else
+ #endif
+ {
nr = vcpu->regs[VCPU_REGS_RBX] & -1u;
- a0 = vcpu->regs[VCPU_REGS_RAX] & -1u;
- a1 = vcpu->regs[VCPU_REGS_RCX] & -1u;
- a2 = vcpu->regs[VCPU_REGS_RDX] & -1u;
- a3 = vcpu->regs[VCPU_REGS_RSI] & -1u;
- a4 = vcpu->regs[VCPU_REGS_RDI] & -1u;
- a5 = vcpu->regs[VCPU_REGS_RBP] & -1u;
- }
- switch (nr) {
- default:
- run->hypercall.nr = nr;
- run->hypercall.args[0] = a0;
- run->hypercall.args[1] = a1;
- run->hypercall.args[2] = a2;
- run->hypercall.args[3] = a3;
- run->hypercall.args[4] = a4;
- run->hypercall.args[5] = a5;
- run->hypercall.ret = ret;
- run->hypercall.longmode = is_long_mode(vcpu);
- kvm_arch_ops->decache_regs(vcpu);
+ args[0] = vcpu->regs[VCPU_REGS_RAX] & -1u;
+ args[1] = vcpu->regs[VCPU_REGS_RCX] & -1u;
+ args[2] = vcpu->regs[VCPU_REGS_RDX] & -1u;
+ args[3] = vcpu->regs[VCPU_REGS_RSI] & -1u;
+ args[4] = vcpu->regs[VCPU_REGS_RDI] & -1u;
+ args[5] = vcpu->regs[VCPU_REGS_RBP] & -1u;
+ }
+
+ if (nr >= KVM_KERNEL_NR_HYPERCALLS || nr < 0) {
+ run->hypercall.nr = nr;
+ memcpy(run->hypercall.args, args, sizeof(args));
+ run->hypercall.ret = 0;
+ run->hypercall.longmode = is_long_mode(vcpu);
+ kvm_arch_ops->decache_regs(vcpu);
run->exit_reason = KVM_EXIT_HYPERCALL;
- return 0;
+ return 0;
+ }
+
+ /* The hypercall might block or do intensive work */
+ vcpu_put(vcpu);
+
+ /*
+ * Increase the hypercall's module ref count assures
+ * that no one will remove the module while a hypercall
+ * is executing.
+ * Theoretically we need to lock to get coherent
module-hypercall
+ * view but practically there's almost no users this mechanism
now.
+ */
+ if (try_module_get(hypercalls[nr].module) < 0) {
+ printk(KERN_DEBUG "%s: module reference count++
failed\n",
+ __FUNCTION__);
+ ret = -EINVAL;
+ goto out;
}
- vcpu->regs[VCPU_REGS_RAX] = ret;
- kvm_arch_ops->decache_regs(vcpu);
+
+ if (!hypercalls[nr].hypercall) {
+ printk(KERN_ERR "%s: hypercall nr(%lx) was not yet
registered\n",
+ __FUNCTION__, nr);
+ ret = -EINVAL;
+ goto out_put;
+ }
+
+ ret = hypercalls[nr].hypercall(vcpu, args);
+
+out_put:
+ module_put(hypercalls[nr].module);
+out:
+ vcpu_load(vcpu);
+
+ vcpu->regs[VCPU_REGS_RAX] = ret;
+ kvm_arch_ops->decache_regs(vcpu);
+
return 1;
}
EXPORT_SYMBOL_GPL(kvm_hypercall);
@@ -2847,6 +2992,8 @@ out:
}
static struct file_operations kvm_chardev_ops = {
+ .open = kvm_dev_open,
+ .release = kvm_dev_release,
.unlocked_ioctl = kvm_dev_ioctl,
.compat_ioctl = kvm_dev_ioctl,
};
diff --git a/include/linux/kvm_para.h b/include/linux/kvm_para.h
index 754e29d..0cf516a 100644
--- a/include/linux/kvm_para.h
+++ b/include/linux/kvm_para.h
@@ -71,6 +71,8 @@ struct kvm_vcpu_para_state {
* No registers are clobbered by the hypercall, except that the
* return value is in RAX.
*/
-#define __NR_hypercalls 0
+
+#define KVM_KERNEL_NR_HYPERCALLS 1
+#define __NR_hypercall_test 0
#endif
-----
In simplicity there is elegance.
Dor Laor ;)
-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems? Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/
^ permalink raw reply related [flat|nested] 4+ messages in thread[parent not found: <64F9B87B6B770947A9F8391472E032160D59004F-yEcIvxbTEBqsx+V+t5oei8rau4O3wl8o3fe8/T/H7NteoWH0uzbU5w@public.gmane.org>]
* Re: [PATCH 4/4] [Hypercall] Allow modules registeration for hypercalls. [not found] ` <64F9B87B6B770947A9F8391472E032160D59004F-yEcIvxbTEBqsx+V+t5oei8rau4O3wl8o3fe8/T/H7NteoWH0uzbU5w@public.gmane.org> @ 2007-08-25 8:29 ` Avi Kivity [not found] ` <46CFE857.8050207-atKUWr5tajBWk0Htik3J/w@public.gmane.org> 2007-08-27 22:22 ` Anthony Liguori 1 sibling, 1 reply; 4+ messages in thread From: Avi Kivity @ 2007-08-25 8:29 UTC (permalink / raw) To: Dor Laor; +Cc: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f Dor Laor wrote: > New kernel modules for KVM are upcoming soon, these module > will need to use hypercalls. Before calling the hypercall function, > the kvm_main core module has to make sure it won't get unloaded. > So hypercall register/unregister are added. > > Except that the kernel hypercalls handlers are numberes > 0-KVM_NR_HYPERCALLS. > All the userspace handlers are above KVM_NR_HYPERCALLS. > But, some hypercalls can be handled either in userspace or in the kernel, depending on what modules are loaded. How is that handled? > > +static struct kvm_hypercall hypercalls[KVM_KERNEL_NR_HYPERCALLS]; > + > +static int test_hypercall(struct kvm_vcpu *vcpu, unsigned long args[]) > +{ > + printk(KERN_DEBUG "%s: hypercall invoked\n", __FUNCTION__); > + > + return 0; > +} > + > +static struct kvm_hypercall __hypercall_test = { > + (unsigned long (*)(struct kvm_vcpu*, unsigned long > args[]))test_hypercall, > + THIS_MODULE, > + __NR_hypercall_test, > +}; > I think we can live without the test hypercall in production code. > + > +static atomic_t dev_kvm_open_count = ATOMIC_INIT(0); > + > #define MAX_IO_MSRS 256 > > #define CR0_RESERVED_BITS > \ > @@ -302,6 +320,24 @@ static struct kvm *kvm_create_vm(void) > return kvm; > } > > +static int kvm_dev_open(struct inode *inode, struct file *filp) > +{ > + int ret = 0; > + > + if (atomic_inc_return(&dev_kvm_open_count) == 1) { > + ret = kvm_register_hypercall(THIS_MODULE, > &__hypercall_test); > Instead of this hack, hypercalls in kvm.ko can have the .module field zeroed, so there's no need to play with our own module count. The hypercalls can be initialized in their array directly. > + > + spin_lock(&kvm_lock); > + > + if (hypercalls[hypercall->idx].hypercall) { > + printk(KERN_DEBUG "%s:hypercall idx(%d) already > taken\n", > + __FUNCTION__, hypercall->idx); > + r = -EEXIST; > + goto out; > + } > + > + if (try_module_get(module) < 0) { > + printk(KERN_DEBUG "%s: module reference count++ > failed\n", > + __FUNCTION__); > + r = -EINVAL; > + goto out; > + } > + > + hypercalls[hypercall->idx].hypercall = hypercall->hypercall; > "smp_wmb();" here. > + hypercalls[hypercall->idx].module = module; > + > +out: > + spin_unlock(&kvm_lock); > + > + return r; > +} > +EXPORT_SYMBOL_GPL(kvm_register_hypercall); > + > > + > +/* > + * Generic hypercall dispatcher routine. > + * Returns 0 for user space handling, 1 on success handling > + */ > + int kvm_hypercall(struct kvm_vcpu *vcpu, struct kvm_run *run) > + { > + if (try_module_get(hypercalls[nr].module) < 0) { > + printk(KERN_DEBUG "%s: module reference count++ > failed\n", > + __FUNCTION__); > + ret = -EINVAL; > EOPNOTSUPP or something. EINVAL is overused. -- Do not meddle in the internals of kernels, for they are subtle and quick to panic. ------------------------------------------------------------------------- This SF.net email is sponsored by: Splunk Inc. Still grepping through log files to find problems? Stop. Now Search log events and configuration files using AJAX and a browser. Download your FREE copy of Splunk now >> http://get.splunk.com/ ^ permalink raw reply [flat|nested] 4+ messages in thread
[parent not found: <46CFE857.8050207-atKUWr5tajBWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH 4/4] [Hypercall] Allow modules registeration for hypercalls. [not found] ` <46CFE857.8050207-atKUWr5tajBWk0Htik3J/w@public.gmane.org> @ 2007-08-25 9:53 ` Dor Laor 0 siblings, 0 replies; 4+ messages in thread From: Dor Laor @ 2007-08-25 9:53 UTC (permalink / raw) To: Avi Kivity; +Cc: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f >> New kernel modules for KVM are upcoming soon, these module >> will need to use hypercalls. Before calling the hypercall function, >> the kvm_main core module has to make sure it won't get unloaded. >> So hypercall register/unregister are added. >> >> Except that the kernel hypercalls handlers are numberes >> 0-KVM_NR_HYPERCALLS. >> All the userspace handlers are above KVM_NR_HYPERCALLS. >> > >But, some hypercalls can be handled either in userspace or in the >kernel, depending on what modules are loaded. How is that handled? Well, now it's not supported and it should be. There are two options: 1. If a kernel handled hypercall is not registered, pass it to userspace for further handling (although it is in the range 0-KVM_NR_HYPERCALLS). 2. Like '1' but also wave the KVM_NR_HYPERCALLS range. > >> >> +static struct kvm_hypercall hypercalls[KVM_KERNEL_NR_HYPERCALLS]; >> + >> +static int test_hypercall(struct kvm_vcpu *vcpu, unsigned long >args[]) >> +{ >> + printk(KERN_DEBUG "%s: hypercall invoked\n", __FUNCTION__); >> + >> + return 0; >> +} >> + >> +static struct kvm_hypercall __hypercall_test = { >> + (unsigned long (*)(struct kvm_vcpu*, unsigned long >> args[]))test_hypercall, >> + THIS_MODULE, >> + __NR_hypercall_test, >> +}; >> > >I think we can live without the test hypercall in production code. Sure, it was here as an example since I yet to write the kernel PV backend drivers. ------------------------------------------------------------------------- This SF.net email is sponsored by: Splunk Inc. Still grepping through log files to find problems? Stop. Now Search log events and configuration files using AJAX and a browser. Download your FREE copy of Splunk now >> http://get.splunk.com/ ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 4/4] [Hypercall] Allow modules registeration for hypercalls. [not found] ` <64F9B87B6B770947A9F8391472E032160D59004F-yEcIvxbTEBqsx+V+t5oei8rau4O3wl8o3fe8/T/H7NteoWH0uzbU5w@public.gmane.org> 2007-08-25 8:29 ` Avi Kivity @ 2007-08-27 22:22 ` Anthony Liguori 1 sibling, 0 replies; 4+ messages in thread From: Anthony Liguori @ 2007-08-27 22:22 UTC (permalink / raw) To: Dor Laor; +Cc: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f I don't really like the idea of having other modules register hypercalls. The hypercall address space is fixed and should be maintained by KVM. I think things like virtio are rare and important enough, that it warrants having kvm.ko know enough about them to provide a higher level registration mechanism. The virtio network driver should register the fact that it's a virtio device, not which hypercalls it cares about. I'm not sure you gain anything by doing the virtio infrastructure in a module either. If anything, a config option in kvm.ko would suffice. Regards, Anthony Liguori On Fri, 2007-08-24 at 16:59 -0700, Dor Laor wrote: > New kernel modules for KVM are upcoming soon, these module > will need to use hypercalls. Before calling the hypercall function, > the kvm_main core module has to make sure it won't get unloaded. > So hypercall register/unregister are added. > > Except that the kernel hypercalls handlers are numberes > 0-KVM_NR_HYPERCALLS. > All the userspace handlers are above KVM_NR_HYPERCALLS. > > Signed-off-by: Dor Laor <dor.laor-atKUWr5tajBWk0Htik3J/w@public.gmane.org> > --- > drivers/kvm/kvm.h | 9 ++ > drivers/kvm/kvm_main.c | 223 > ++++++++++++++++++++++++++++++++++++++-------- > include/linux/kvm_para.h | 4 +- > 3 files changed, 197 insertions(+), 39 deletions(-) > mode change 100644 => 100755 drivers/kvm/kvm_main.c > > diff --git a/drivers/kvm/kvm.h b/drivers/kvm/kvm.h > index a42a6f3..37240cf 100644 > --- a/drivers/kvm/kvm.h > +++ b/drivers/kvm/kvm.h > @@ -472,6 +472,12 @@ struct kvm_arch_ops { > unsigned char *hypercall_addr); > }; > > +struct kvm_hypercall { > + unsigned long (*hypercall)(struct kvm_vcpu*, unsigned long > args[]); > + struct module *module; > + int idx; > +}; > + > extern struct kvm_arch_ops *kvm_arch_ops; > > /* The guest did something we don't support. */ > @@ -588,6 +594,9 @@ int kvm_mmu_load(struct kvm_vcpu *vcpu); > void kvm_mmu_unload(struct kvm_vcpu *vcpu); > > int kvm_hypercall(struct kvm_vcpu *vcpu, struct kvm_run *run); > +int kvm_register_hypercall(struct module *module, struct kvm_hypercall > *hypercall); > +int kvm_unregister_hypercall(struct kvm_hypercall *hypercall); > + > > static inline int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, > u32 error_code) > diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c > old mode 100644 > new mode 100755 > index abd7498..051b47a > --- a/drivers/kvm/kvm_main.c > +++ b/drivers/kvm/kvm_main.c > @@ -20,6 +20,7 @@ > #include "segment_descriptor.h" > > #include <linux/kvm.h> > +#include <linux/kvm_para.h> > #include <linux/module.h> > #include <linux/errno.h> > #include <linux/percpu.h> > @@ -84,6 +85,23 @@ static struct kvm_stats_debugfs_item { > > static struct dentry *debugfs_dir; > > +static struct kvm_hypercall hypercalls[KVM_KERNEL_NR_HYPERCALLS]; > + > +static int test_hypercall(struct kvm_vcpu *vcpu, unsigned long args[]) > +{ > + printk(KERN_DEBUG "%s: hypercall invoked\n", __FUNCTION__); > + > + return 0; > +} > + > +static struct kvm_hypercall __hypercall_test = { > + (unsigned long (*)(struct kvm_vcpu*, unsigned long > args[]))test_hypercall, > + THIS_MODULE, > + __NR_hypercall_test, > +}; > + > +static atomic_t dev_kvm_open_count = ATOMIC_INIT(0); > + > #define MAX_IO_MSRS 256 > > #define CR0_RESERVED_BITS > \ > @@ -302,6 +320,24 @@ static struct kvm *kvm_create_vm(void) > return kvm; > } > > +static int kvm_dev_open(struct inode *inode, struct file *filp) > +{ > + int ret = 0; > + > + if (atomic_inc_return(&dev_kvm_open_count) == 1) { > + ret = kvm_register_hypercall(THIS_MODULE, > &__hypercall_test); > + if (ret < 0) { > + printk(KERN_DEBUG "%s: kvm_register_hypercall > error, " > + "hypercall: %s\n", > + __FUNCTION__, "test"); > + goto out_test; > + } > + } > + > +out_test: > + return ret; > +} > + > /* > * Free any memory in @free but not in @dont. > */ > @@ -371,6 +407,20 @@ static void kvm_free_vcpus(struct kvm *kvm) > > } > > +static int kvm_dev_release(struct inode *inode, struct file *filp) > +{ > + int ret = 0; > + > + atomic_dec(&dev_kvm_open_count); > + ret = kvm_unregister_hypercall(&__hypercall_test); > + if (ret < 0) { > + printk(KERN_DEBUG "%s:kvm_unregister_hypercall error " > + "hypercall: %s\n", __FUNCTION__, > "hypercall_test"); > + } > + > + return ret; > +} > + > static void kvm_destroy_vm(struct kvm *kvm) > { > spin_lock(&kvm_lock); > @@ -1267,50 +1317,145 @@ int kvm_emulate_halt(struct kvm_vcpu *vcpu) > } > EXPORT_SYMBOL_GPL(kvm_emulate_halt); > > -int kvm_hypercall(struct kvm_vcpu *vcpu, struct kvm_run *run) > +int kvm_register_hypercall(struct module* module, struct kvm_hypercall > *hypercall) > { > - unsigned long nr, a0, a1, a2, a3, a4, a5, ret; > + int r = 0; > > - kvm_arch_ops->cache_regs(vcpu); > - ret = -KVM_EINVAL; > -#ifdef CONFIG_X86_64 > - if (is_long_mode(vcpu)) { > - nr = vcpu->regs[VCPU_REGS_RAX]; > - a0 = vcpu->regs[VCPU_REGS_RDI]; > - a1 = vcpu->regs[VCPU_REGS_RSI]; > - a2 = vcpu->regs[VCPU_REGS_RDX]; > - a3 = vcpu->regs[VCPU_REGS_RCX]; > - a4 = vcpu->regs[VCPU_REGS_R8]; > - a5 = vcpu->regs[VCPU_REGS_R9]; > - } else > -#endif > - { > + if (hypercall->idx >= KVM_KERNEL_NR_HYPERCALLS || > + hypercall->idx < 0) { > + printk(KERN_DEBUG "%s:hypercall registration idx(%d)\n", > + __FUNCTION__, hypercall->idx); > + return -EINVAL; > + } > + > + spin_lock(&kvm_lock); > + > + if (hypercalls[hypercall->idx].hypercall) { > + printk(KERN_DEBUG "%s:hypercall idx(%d) already > taken\n", > + __FUNCTION__, hypercall->idx); > + r = -EEXIST; > + goto out; > + } > + > + if (try_module_get(module) < 0) { > + printk(KERN_DEBUG "%s: module reference count++ > failed\n", > + __FUNCTION__); > + r = -EINVAL; > + goto out; > + } > + > + hypercalls[hypercall->idx].hypercall = hypercall->hypercall; > + hypercalls[hypercall->idx].module = module; > + > +out: > + spin_unlock(&kvm_lock); > + > + return r; > +} > +EXPORT_SYMBOL_GPL(kvm_register_hypercall); > + > +int kvm_unregister_hypercall(struct kvm_hypercall *hypercall) > +{ > + if (hypercall->idx >= KVM_KERNEL_NR_HYPERCALLS || > + hypercall->idx < 0) { > + printk(KERN_DEBUG "%s:hypercall unregistration > idx(%d)\n", > + __FUNCTION__, hypercall->idx); > + return -EINVAL; > + } > + > + spin_lock(&kvm_lock); > + if (!hypercalls[hypercall->idx].hypercall) { > + printk(KERN_DEBUG "%s:hypercall idx(%d) was not > registered\n", > + __FUNCTION__, hypercall->idx); > + spin_unlock(&kvm_lock); > + return -EEXIST; > + } > + > + hypercalls[hypercall->idx].hypercall = 0; > + module_put(hypercalls[hypercall->idx].module); > + spin_unlock(&kvm_lock); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(kvm_unregister_hypercall); > + > +/* > + * Generic hypercall dispatcher routine. > + * Returns 0 for user space handling, 1 on success handling > + */ > + int kvm_hypercall(struct kvm_vcpu *vcpu, struct kvm_run *run) > + { > + unsigned long nr, ret; > + unsigned long args[6]; > + > + kvm_arch_ops->cache_regs(vcpu); > + ret = -KVM_EINVAL; > + #ifdef CONFIG_X86_64 > + if (is_long_mode(vcpu)) { > + nr = vcpu->regs[VCPU_REGS_RAX]; > + args[0] = vcpu->regs[VCPU_REGS_RDI]; > + args[1] = vcpu->regs[VCPU_REGS_RSI]; > + args[2] = vcpu->regs[VCPU_REGS_RDX]; > + args[3] = vcpu->regs[VCPU_REGS_RCX]; > + args[4] = vcpu->regs[VCPU_REGS_R8]; > + args[5] = vcpu->regs[VCPU_REGS_R9]; > + } else > + #endif > + { > nr = vcpu->regs[VCPU_REGS_RBX] & -1u; > - a0 = vcpu->regs[VCPU_REGS_RAX] & -1u; > - a1 = vcpu->regs[VCPU_REGS_RCX] & -1u; > - a2 = vcpu->regs[VCPU_REGS_RDX] & -1u; > - a3 = vcpu->regs[VCPU_REGS_RSI] & -1u; > - a4 = vcpu->regs[VCPU_REGS_RDI] & -1u; > - a5 = vcpu->regs[VCPU_REGS_RBP] & -1u; > - } > - switch (nr) { > - default: > - run->hypercall.nr = nr; > - run->hypercall.args[0] = a0; > - run->hypercall.args[1] = a1; > - run->hypercall.args[2] = a2; > - run->hypercall.args[3] = a3; > - run->hypercall.args[4] = a4; > - run->hypercall.args[5] = a5; > - run->hypercall.ret = ret; > - run->hypercall.longmode = is_long_mode(vcpu); > - kvm_arch_ops->decache_regs(vcpu); > + args[0] = vcpu->regs[VCPU_REGS_RAX] & -1u; > + args[1] = vcpu->regs[VCPU_REGS_RCX] & -1u; > + args[2] = vcpu->regs[VCPU_REGS_RDX] & -1u; > + args[3] = vcpu->regs[VCPU_REGS_RSI] & -1u; > + args[4] = vcpu->regs[VCPU_REGS_RDI] & -1u; > + args[5] = vcpu->regs[VCPU_REGS_RBP] & -1u; > + } > + > + if (nr >= KVM_KERNEL_NR_HYPERCALLS || nr < 0) { > + run->hypercall.nr = nr; > + memcpy(run->hypercall.args, args, sizeof(args)); > + run->hypercall.ret = 0; > + run->hypercall.longmode = is_long_mode(vcpu); > + kvm_arch_ops->decache_regs(vcpu); > run->exit_reason = KVM_EXIT_HYPERCALL; > > - return 0; > + return 0; > + } > + > + /* The hypercall might block or do intensive work */ > + vcpu_put(vcpu); > + > + /* > + * Increase the hypercall's module ref count assures > + * that no one will remove the module while a hypercall > + * is executing. > + * Theoretically we need to lock to get coherent > module-hypercall > + * view but practically there's almost no users this mechanism > now. > + */ > + if (try_module_get(hypercalls[nr].module) < 0) { > + printk(KERN_DEBUG "%s: module reference count++ > failed\n", > + __FUNCTION__); > + ret = -EINVAL; > + goto out; > } > - vcpu->regs[VCPU_REGS_RAX] = ret; > - kvm_arch_ops->decache_regs(vcpu); > + > + if (!hypercalls[nr].hypercall) { > + printk(KERN_ERR "%s: hypercall nr(%lx) was not yet > registered\n", > + __FUNCTION__, nr); > + ret = -EINVAL; > + goto out_put; > + } > + > + ret = hypercalls[nr].hypercall(vcpu, args); > + > +out_put: > + module_put(hypercalls[nr].module); > +out: > + vcpu_load(vcpu); > + > + vcpu->regs[VCPU_REGS_RAX] = ret; > + kvm_arch_ops->decache_regs(vcpu); > + > return 1; > } > EXPORT_SYMBOL_GPL(kvm_hypercall); > @@ -2847,6 +2992,8 @@ out: > } > > static struct file_operations kvm_chardev_ops = { > + .open = kvm_dev_open, > + .release = kvm_dev_release, > .unlocked_ioctl = kvm_dev_ioctl, > .compat_ioctl = kvm_dev_ioctl, > }; > diff --git a/include/linux/kvm_para.h b/include/linux/kvm_para.h > index 754e29d..0cf516a 100644 > --- a/include/linux/kvm_para.h > +++ b/include/linux/kvm_para.h > @@ -71,6 +71,8 @@ struct kvm_vcpu_para_state { > * No registers are clobbered by the hypercall, except that the > * return value is in RAX. > */ > -#define __NR_hypercalls 0 > + > +#define KVM_KERNEL_NR_HYPERCALLS 1 > +#define __NR_hypercall_test 0 > > #endif > > > ----- > In simplicity there is elegance. > Dor Laor ;) > > > ------------------------------------------------------------------------- > This SF.net email is sponsored by: Splunk Inc. > Still grepping through log files to find problems? Stop. > Now Search log events and configuration files using AJAX and a browser. > Download your FREE copy of Splunk now >> http://get.splunk.com/ > _______________________________________________ > kvm-devel mailing list > kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org > https://lists.sourceforge.net/lists/listinfo/kvm-devel ------------------------------------------------------------------------- This SF.net email is sponsored by: Splunk Inc. Still grepping through log files to find problems? Stop. Now Search log events and configuration files using AJAX and a browser. Download your FREE copy of Splunk now >> http://get.splunk.com/ ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2007-08-27 22:22 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-08-24 23:59 [PATCH 4/4] [Hypercall] Allow modules registeration for hypercalls Dor Laor
[not found] ` <64F9B87B6B770947A9F8391472E032160D59004F-yEcIvxbTEBqsx+V+t5oei8rau4O3wl8o3fe8/T/H7NteoWH0uzbU5w@public.gmane.org>
2007-08-25 8:29 ` Avi Kivity
[not found] ` <46CFE857.8050207-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
2007-08-25 9:53 ` Dor Laor
2007-08-27 22:22 ` Anthony Liguori
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox