@@ -896,6 +896,8 @@ struct kvm_grant_table {
/* Xen emulation context */
struct kvm_xen {
u64 xen_hypercall;
+
+#define XEN_SHIM_DOMID 0
domid_t domid;
gfn_t shinfo_addr;
@@ -96,6 +96,16 @@ config KVM_MMU_AUDIT
This option adds a R/W kVM module parameter 'mmu_audit', which allows
auditing of KVM MMU events at runtime.
+config XEN_SHIM
+ tristate "Xen hypercall emulation shim"
+ depends on KVM
+ depends on XEN
+ default m
+ help
+ Shim to support Xen hypercalls on non-Xen hosts. It intercepts grant
+ table and event channels hypercalls same way as Xen hypervisor. This is
+ useful for having Xen backend drivers work on KVM.
+
# OK, it's a little counter-intuitive to do this, but it puts it neatly under
# the virtualization menu.
source "drivers/vhost/Kconfig"
@@ -18,3 +18,4 @@ kvm-amd-y += svm.o pmu_amd.o
obj-$(CONFIG_KVM) += kvm.o
obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
obj-$(CONFIG_KVM_AMD) += kvm-amd.o
+obj-$(CONFIG_XEN_SHIM) += xen-shim.o
new file mode 100644
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Xen hypercall emulation shim
+ */
+
+#define pr_fmt(fmt) "KVM:" KBUILD_MODNAME ": " fmt
+
+#include <asm/kvm_host.h>
+
+#include <xen/xen.h>
+#include <xen/xen-ops.h>
+#include <xen/events.h>
+#include <xen/xenbus.h>
+
+#define BITS_PER_EVTCHN_WORD (sizeof(xen_ulong_t)*8)
+
+static struct kvm_xen shim = { .domid = XEN_SHIM_DOMID };
+
+static void shim_evtchn_setup(struct shared_info *s)
+{
+ int cpu;
+
+ /* Point Xen's shared_info to the domain's sinfo page */
+ HYPERVISOR_shared_info = s;
+
+ /* Evtchns will be marked pending on allocation */
+ memset(s->evtchn_pending, 0, sizeof(s->evtchn_pending));
+ /* ... but we do mask all of -- dom0 expect it. */
+ memset(s->evtchn_mask, 1, sizeof(s->evtchn_mask));
+
+ for_each_possible_cpu(cpu) {
+ struct vcpu_info *vcpu_info;
+ int i;
+
+ /* Direct CPU mapping as far as dom0 is concerned */
+ per_cpu(xen_vcpu_id, cpu) = cpu;
+
+ vcpu_info = &per_cpu(xen_vcpu_info, cpu);
+ memset(vcpu_info, 0, sizeof(*vcpu_info));
+
+ vcpu_info->evtchn_upcall_mask = 0;
+
+ vcpu_info->evtchn_upcall_pending = 0;
+ for (i = 0; i < BITS_PER_EVTCHN_WORD; i++)
+ clear_bit(i, &vcpu_info->evtchn_pending_sel);
+
+ per_cpu(xen_vcpu, cpu) = vcpu_info;
+ }
+}
+
+static int __init shim_register(void)
+{
+ struct shared_info *shinfo;
+
+ shinfo = (struct shared_info *)get_zeroed_page(GFP_KERNEL);
+ if (!shinfo) {
+ pr_err("Failed to allocate shared_info page\n");
+ return -ENOMEM;
+ }
+ shim.shinfo = shinfo;
+
+ idr_init(&shim.port_to_evt);
+ mutex_init(&shim.xen_lock);
+
+ kvm_xen_register_lcall(&shim);
+
+ /* We can handle hypercalls after this point */
+ xen_shim_domain = 1;
+
+ shim_evtchn_setup(shim.shinfo);
+
+ xen_setup_features();
+
+ xen_init_IRQ();
+
+ xenbus_init();
+
+ return 0;
+}
+
+static int __init shim_init(void)
+{
+ if (xen_domain())
+ return -ENODEV;
+
+ return shim_register();
+}
+
+static void __exit shim_exit(void)
+{
+ xenbus_deinit();
+ xen_shim_domain = 0;
+
+ kvm_xen_unregister_lcall();
+ HYPERVISOR_shared_info = NULL;
+ free_page((unsigned long) shim.shinfo);
+ shim.shinfo = NULL;
+}
+
+module_init(shim_init);
+module_exit(shim_exit)
+
+MODULE_AUTHOR("Ankur Arora <ankur.a.arora@oracle.com>,"
+ "Joao Martins <joao.m.martins@oracle.com>");
+MODULE_LICENSE("GPL");
@@ -57,6 +57,9 @@ EXPORT_PER_CPU_SYMBOL(xen_vcpu_info);
enum xen_domain_type xen_domain_type = XEN_NATIVE;
EXPORT_SYMBOL_GPL(xen_domain_type);
+int xen_shim_domain;
+EXPORT_SYMBOL_GPL(xen_shim_domain);
+
unsigned long *machine_to_phys_mapping = (void *)MACH2PHYS_VIRT_START;
EXPORT_SYMBOL(machine_to_phys_mapping);
unsigned long machine_to_phys_nr;
@@ -349,3 +352,45 @@ void xen_arch_unregister_cpu(int num)
}
EXPORT_SYMBOL(xen_arch_unregister_cpu);
#endif
+
+static struct module *find_module_shim(void)
+{
+ static const char name[] = "xen_shim";
+ struct module *module;
+
+ mutex_lock(&module_mutex);
+ module = find_module(name);
+ mutex_unlock(&module_mutex);
+
+ return module;
+}
+
+bool xen_shim_domain_get(void)
+{
+ struct module *shim;
+
+ if (!xen_shim_domain())
+ return false;
+
+ shim = find_module_shim();
+ if (!shim)
+ return false;
+
+ return try_module_get(shim);
+}
+EXPORT_SYMBOL(xen_shim_domain_get);
+
+void xen_shim_domain_put(void)
+{
+ struct module *shim;
+
+ if (!xen_shim_domain())
+ return;
+
+ shim = find_module_shim();
+ if (!shim)
+ return;
+
+ module_put(shim);
+}
+EXPORT_SYMBOL(xen_shim_domain_put);
@@ -89,7 +89,7 @@ static void evtchn_2l_unmask(unsigned port)
unsigned int cpu = get_cpu();
int do_hypercall = 0, evtchn_pending = 0;
- BUG_ON(!irqs_disabled());
+ WARN_ON(!irqs_disabled());
if (unlikely((cpu != cpu_from_evtchn(port))))
do_hypercall = 1;
@@ -1665,7 +1665,7 @@ void xen_callback_vector(void) {}
static bool fifo_events = true;
module_param(fifo_events, bool, 0);
-void __init xen_init_IRQ(void)
+void xen_init_IRQ(void)
{
int ret = -EINVAL;
unsigned int evtchn;
@@ -1683,6 +1683,9 @@ void __init xen_init_IRQ(void)
for (evtchn = 0; evtchn < xen_evtchn_nr_channels(); evtchn++)
mask_evtchn(evtchn);
+ if (xen_shim_domain())
+ return;
+
pirq_needs_eoi = pirq_needs_eoi_flag;
#ifdef CONFIG_X86
@@ -1714,3 +1717,4 @@ void __init xen_init_IRQ(void)
}
#endif
}
+EXPORT_SYMBOL_GPL(xen_init_IRQ);
@@ -31,3 +31,4 @@ void xen_setup_features(void)
xen_features[i * 32 + j] = !!(fi.submap & 1<<j);
}
}
+EXPORT_SYMBOL_GPL(xen_setup_features);
@@ -680,7 +680,7 @@ static struct miscdevice xenbus_dev = {
.fops = &xen_xenbus_fops,
};
-static int __init xenbus_init(void)
+static int __init xenbus_frontend_init(void)
{
int err;
@@ -692,4 +692,4 @@ static int __init xenbus_init(void)
pr_err("Could not register xenbus frontend device\n");
return err;
}
-device_initcall(xenbus_init);
+device_initcall(xenbus_frontend_init);
@@ -10,8 +10,12 @@ enum xen_domain_type {
#ifdef CONFIG_XEN
extern enum xen_domain_type xen_domain_type;
+extern int xen_shim_domain;
+extern bool xen_shim_domain_get(void);
+extern void xen_shim_domain_put(void);
#else
#define xen_domain_type XEN_NATIVE
+#define xen_shim_domain 0
#endif
#ifdef CONFIG_XEN_PVH
@@ -24,6 +28,7 @@ extern bool xen_pvh;
#define xen_pv_domain() (xen_domain_type == XEN_PV_DOMAIN)
#define xen_hvm_domain() (xen_domain_type == XEN_HVM_DOMAIN)
#define xen_pvh_domain() (xen_pvh)
+#define xen_shim_domain() (!xen_domain() && xen_shim_domain)
#include <linux/types.h>
@@ -233,4 +233,7 @@ extern const struct file_operations xen_xenbus_fops;
extern struct xenstore_domain_interface *xen_store_interface;
extern int xen_store_evtchn;
+int xenbus_init(void);
+void xenbus_deinit(void);
+
#endif /* _XEN_XENBUS_H */