[PATCH] KVM: Introduce dynamically registered hypercall capability

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This introduces a list of entries which associate a function pointer of
kvm_hc_type to a hypercall number and allows the ability to register and
unregister entries.  In addition, it also allows the ability to retrieve a
function pointer of kvm_hc_type for a given hypercall number which is meant
to be called from the arch-specific section.

The main intent is to allow modules to register hypercalls which they own
rather than requiring the addition of a stub of some sort.  It will also
allow each arch to maintain separate lists of hypercalls rather than having
to respect changes in include/uapi/linux/kvm_para.h

Signed-off-by: Phil White <pwhite@xxxxxxxxxx>
---
 arch/arm/kvm/Makefile               |   2 +-
 arch/arm64/kvm/Makefile             |   1 +
 arch/ia64/kvm/Makefile              |   2 +-
 arch/mips/include/asm/kvm_
para.h    |   6 ++
 arch/mips/kvm/Makefile              |   3 +-
 arch/powerpc/include/asm/kvm_para.h |   7 ++
 arch/powerpc/kvm/Makefile           |   2 +-
 arch/powerpc/kvm/powerpc.c          |   5 ++
 arch/s390/include/asm/kvm_para.h    |   7 +-
 arch/s390/kvm/Makefile              |   2 +-
 arch/x86/include/asm/kvm_para.h     |   6 ++
 arch/x86/kvm/Makefile               |   3 +-
 arch/x86/kvm/x86.c                  |   6 ++
 include/uapi/linux/kvm.h            |   1 +
 include/uapi/linux/kvm_para.h       |  19 +++++-
 virt/kvm/hypercall.c                | 125 ++++++++++++++++++++++++++++++++++++
 16 files changed, 187 insertions(+), 10 deletions(-)
 create mode 100644 virt/kvm/hypercall.c

diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile
index f7057ed..0f9adf9 100644
--- a/arch/arm/kvm/Makefile
+++ b/arch/arm/kvm/Makefile
@@ -15,7 +15,7 @@ AFLAGS_init.o := -Wa,-march=armv7-a$(plus_virt)
 AFLAGS_interrupts.o := -Wa,-march=armv7-a$(plus_virt)

 KVM := ../../../virt/kvm
-kvm-arm-y = $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o
+kvm-arm-y = $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/hypercall.o

 obj-y += kvm-arm.o init.o interrupts.o
 obj-y += arm.o handle_exit.o guest.o mmu.o emulate.o reset.o
diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
index 32a0961..735ea53 100644
--- a/arch/arm64/kvm/Makefile
+++ b/arch/arm64/kvm/Makefile
@@ -12,6 +12,7 @@ ARM=../../../arch/arm/kvm
 obj-$(CONFIG_KVM_ARM_HOST) += kvm.o

 kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o
+kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/hypercall.o
 kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/arm.o $(ARM)/mmu.o $(ARM)/mmio.o
 kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/psci.o $(ARM)/perf.o

diff --git a/arch/ia64/kvm/Makefile b/arch/ia64/kvm/Makefile
index 18e45ec..abbde9a 100644
--- a/arch/ia64/kvm/Makefile
+++ b/arch/ia64/kvm/Makefile
@@ -49,7 +49,7 @@ ccflags-y := -Ivirt/kvm -Iarch/ia64/kvm/
 asflags-y := -Ivirt/kvm -Iarch/ia64/kvm/
 KVM := ../../../virt/kvm

-common-objs = $(KVM)/kvm_main.o $(KVM)/ioapic.o \
+common-objs = $(KVM)/kvm_main.o $(KVM)/ioapic.o $(KVM)/hypercall.o \
                $(KVM)/coalesced_mmio.o $(KVM)/irq_comm.o

 ifeq ($(CONFIG_KVM_DEVICE_ASSIGNMENT),y)
diff --git a/arch/mips/include/asm/kvm_para.h b/arch/mips/include/asm/kvm_para.h
index 5a9aa91..85e44d02 100644
--- a/arch/mips/include/asm/kvm_para.h
+++ b/arch/mips/include/asm/kvm_para.h
@@ -2,6 +2,7 @@
 #define _ASM_MIPS_KVM_PARA_H

 #include <uapi/asm/kvm_para.h>
+#include <linux/kvm.h>

 #define KVM_HYPERCALL ".word 0x42000028"

@@ -105,5 +106,10 @@ static inline bool kvm_para_available(void)
 }
 #endif

+struct kvm_vcpu;
+
+typedef unsigned long (*kvm_hc_type)(struct kvm_vcpu *vcpu, unsigned long a0,
+               unsigned long a1, unsigned long a2, unsigned long a3);
+

 #endif /* _ASM_MIPS_KVM_PARA_H */
diff --git a/arch/mips/kvm/Makefile b/arch/mips/kvm/Makefile
index 401fe02..99afce8 100644
--- a/arch/mips/kvm/Makefile
+++ b/arch/mips/kvm/Makefile
@@ -1,7 +1,8 @@
 # Makefile for KVM support for MIPS
 #

-common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
+common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o \
+               hypercall.o)

 EXTRA_CFLAGS += -Ivirt/kvm -Iarch/mips/kvm

diff --git a/arch/powerpc/include/asm/kvm_para.h
b/arch/powerpc/include/asm/kvm_para.h
index 336a91a..2818a15 100644
--- a/arch/powerpc/include/asm/kvm_para.h
+++ b/arch/powerpc/include/asm/kvm_para.h
@@ -20,6 +20,7 @@
 #define __POWERPC_KVM_PARA_H__

 #include <uapi/asm/kvm_para.h>
+#include <linux/kvm.h>

 #ifdef CONFIG_KVM_GUEST

@@ -66,4 +67,10 @@ static inline bool kvm_check_and_clear_guest_paused(void)
        return false;
 }

+struct kvm_vcpu;
+
+typedef unsigned long (*kvm_hc_type)(struct kvm_vcpu *vcpu,
+               unsigned long param1, unsigned long param2,
+               unsigned long param3, unsigned long param4);
+
 #endif /* __POWERPC_KVM_PARA_H__ */
diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile
index 0570eef..9b5f239 100644
--- a/arch/powerpc/kvm/Makefile
+++ b/arch/powerpc/kvm/Makefile
@@ -8,7 +8,7 @@ ccflags-y := -Ivirt/kvm -Iarch/powerpc/kvm
 KVM := ../../../virt/kvm

 common-objs-y = $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o \
-               $(KVM)/eventfd.o
+               $(KVM)/eventfd.o $(KVM)/hypercall.o

 CFLAGS_e500_mmu.o := -I.
 CFLAGS_e500_mmu_host.o := -I.
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index c1f8f53..d6f7620 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -229,6 +229,11 @@ int kvmppc_kvm_pv(struct kvm_vcpu *vcpu)
                break;
        default:
                r = EV_UNIMPLEMENTED;
+
+               kvm_hc_type *hc = kvm_hypercall_get(nr);
+               if (hc) {
+                       r = (*hc)(vcpu, param1, param2, param3, param4);
+               }
                break;
        }

diff --git a/arch/s390/include/asm/kvm_para.h b/arch/s390/include/asm/kvm_para.h
index e0f8423..781fad0 100644
--- a/arch/s390/include/asm/kvm_para.h
+++ b/arch/s390/include/asm/kvm_para.h
@@ -27,7 +27,7 @@
 #define __S390_KVM_PARA_H

 #include <uapi/asm/kvm_para.h>
-
+#include <linux/kvm.h>


 static inline long kvm_hypercall0(unsigned long nr)
@@ -154,4 +154,9 @@ static inline bool kvm_check_and_clear_guest_paused(void)
        return false;
 }

+struct kvm_vcpu;
+
+typedef unsigned long (*kvm_hc_type)(struct kvm_vcpu *vcpu, unsigned long a0,
+               unsigned long a1, unsigned long a2, unsigned long a3);
+
 #endif /* __S390_KVM_PARA_H */
diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile
index b3b5534..c9a2069 100644
--- a/arch/s390/kvm/Makefile
+++ b/arch/s390/kvm/Makefile
@@ -7,7 +7,7 @@
 # as published by the Free Software Foundation.

 KVM := ../../../virt/kvm
-common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o  $(KVM)/async_pf.o
$(KVM)/irqchip.o
+common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o  $(KVM)/async_pf.o
$(KVM)/irqchip.o $(KVM)/hypercall.o

 ccflags-y := -Ivirt/kvm -Iarch/s390/kvm

diff --git a/arch/x86/include/asm/kvm_para.h b/arch/x86/include/asm/kvm_para.h
index e62cf89..b4f337a 100644
--- a/arch/x86/include/asm/kvm_para.h
+++ b/arch/x86/include/asm/kvm_para.h
@@ -3,6 +3,7 @@

 #include <asm/processor.h>
 #include <asm/alternative.h>
+#include <linux/kvm.h>
 #include <uapi/asm/kvm_para.h>

 extern void kvmclock_init(void);
@@ -134,4 +135,9 @@ static inline void kvm_disable_steal_time(void)
 }
 #endif

+struct kvm_vcpu;
+
+typedef unsigned long (*kvm_hc_type)(struct kvm_vcpu *vcpu, unsigned long a0,
+               unsigned long a1, unsigned long a2, unsigned long a3);
+
 #endif /* _ASM_X86_KVM_PARA_H */
diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
index 25d22b2..3294678 100644
--- a/arch/x86/kvm/Makefile
+++ b/arch/x86/kvm/Makefile
@@ -9,7 +9,8 @@ KVM := ../../../virt/kvm

 kvm-y                  += $(KVM)/kvm_main.o $(KVM)/ioapic.o \
                                $(KVM)/coalesced_mmio.o $(KVM)/irq_comm.o \
-                               $(KVM)/eventfd.o $(KVM)/irqchip.o $(KVM)/vfio.o
+                               $(KVM)/eventfd.o $(KVM)/irqchip.o \
+                               $(KVM)/vfio.o $(KVM)/hypercall.o
 kvm-$(CONFIG_KVM_DEVICE_ASSIGNMENT)    += $(KVM)/assigned-dev.o $(KVM)/iommu.o
 kvm-$(CONFIG_KVM_ASYNC_PF)     += $(KVM)/async_pf.o

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 0033df3..4ad8880 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -5841,6 +5841,7 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
 {
        unsigned long nr, a0, a1, a2, a3, ret;
        int op_64_bit, r = 1;
+       kvm_hc_type hc;

        if (kvm_hv_hypercall_enabled(vcpu->kvm))
                return kvm_hv_hypercall(vcpu);
@@ -5877,6 +5878,11 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
                break;
        default:
                ret = -KVM_ENOSYS;
+
+               hc = kvm_hypercall_get(nr);
+               if (hc) {
+                       ret = (*hc)(vcpu, a0, a1, a2, a3);
+               }
                break;
        }
 out:
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 6076882..023c125 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -11,6 +11,7 @@
 #include <linux/compiler.h>
 #include <linux/ioctl.h>
 #include <asm/kvm.h>
+#include <uapi/linux/kvm_para.h>

 #define KVM_API_VERSION 12

diff --git a/include/uapi/linux/kvm_para.h b/include/uapi/linux/kvm_para.h
index bf6cd7d..1e92db7 100644
--- a/include/uapi/linux/kvm_para.h
+++ b/include/uapi/linux/kvm_para.h
@@ -7,6 +7,7 @@
  * - kvm_hypercall0, kvm_hypercall1...
  * - kvm_arch_para_features
  * - kvm_para_available
+ * - kvm_hc_type
  */

 /* Return values for hypercalls */
@@ -15,6 +16,7 @@
 #define KVM_E2BIG              E2BIG
 #define KVM_EPERM              EPERM

+/* Legacy hypercalls which we need to respect */
 #define KVM_HC_VAPIC_POLL_IRQ          1
 #define KVM_HC_MMU_OP                  2
 #define KVM_HC_FEATURES                        3
@@ -24,9 +26,20 @@
 #define KVM_HC_MIPS_EXIT_VM            7
 #define KVM_HC_MIPS_CONSOLE_OUTPUT     8

-/*
- * hypercalls use architecture specific
- */
+/* The number after which we're comfortable assigning dynamic hypercalls */
+#define KVM_HC_LEGACY                  9
+
 #include <asm/kvm_para.h>
+#include <linux/list.h>
+
+struct kvm_hc {
+       kvm_hc_type hc;
+       uint32_t hc_index;
+       struct list_head hc_list;
+};
+
+extern kvm_hc_type kvm_hypercall_get (uint32_t hc_nr);
+extern int kvm_hypercall_register (struct kvm_hc *hc, uint32_t hc_nr);
+extern void kvm_hypercall_unregister (uint32_t hc_nr);

 #endif /* _UAPI__LINUX_KVM_PARA_H */
diff --git a/virt/kvm/hypercall.c b/virt/kvm/hypercall.c
new file mode 100644
index 0000000..c82578d
--- /dev/null
+++ b/virt/kvm/hypercall.c
@@ -0,0 +1,125 @@
+#include <linux/kvm.h>
+#include <linux/types.h>
+
+LIST_HEAD(hc_list);
+static uint32_t hc_max = KVM_HC_LEGACY;
+
+kvm_hc_type kvm_hypercall_get (uint32_t hc_nr)
+{
+       struct list_head *p;
+       struct kvm_hc *entry;
+       kvm_hc_type ret = NULL;
+
+       list_for_each(p, &hc_list) {
+               entry = list_entry(p, struct kvm_hc, hc_list);
+               if (entry->hc_index == hc_nr) {
+                       ret = entry->hc;
+                       break;
+               } else if (entry->hc_index > hc_nr) {
+                       break;
+               }
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(kvm_hypercall_get);
+
+void kvm_hypercall_unregister (uint32_t hc_nr)
+{
+       struct list_head *p;
+       struct kvm_hc *entry;
+
+       list_for_each(p, &hc_list) {
+               entry = list_entry(p, struct kvm_hc, hc_list);
+               if (entry->hc_index == hc_nr) {
+                       list_del_init(p);
+                       break;
+               } else if (entry->hc_index > hc_nr) {
+                       break;
+               }
+       }
+}
+EXPORT_SYMBOL(kvm_hypercall_unregister);
+
+int kvm_hypercall_register (struct kvm_hc *hc, uint32_t hc_nr)
+{
+       struct list_head *p;
+       struct kvm_hc *entry;
+       uint32_t hc_last_nr = 0;
+
+       /* If no hypercall number is requested, then we dynamically assign one
+        *    more than any hypercall we've seen thus far to avoid reusing
+        *    previously registered hypercalls.  Note that if hc_max is the
+        *    maximum possible value (UINT_MAX), then hc->hc_nr will be equal
+        *    to 0.
+        */
+       if (hc_nr == 0) {
+               hc_nr = hc_max + 1;
+       }
+
+       /* There's three possibilities entering this.
+        * The first is that the caller is requesting an exact hc_nr.  If this
+        *    is the case, then we will go through the list.  If we find an
+        *    entry equal to hc_nr, then we instantly return 1.  Otherwise, we
+        *    go until we've hit a hypercall with a larger number or the last
+        *    element in the list, then break out of the loop.
+        * The second is that the caller is requesting a dynamically assigned
+        *    hc_nr and hc_max != UINT_MAX.  hc_nr will have been set to a
+        *    non-zero number as a result.  We should never find an equal case
+        *    and if we do, then something I don't understand has happened.  So
+        *    we'll break out of the loop if we've hit a hypercall with a
+        *    larger number or the last element in the list.
+        * The third is that the caller is requesting a dynamically assigned
+        *    hc_nr and hc_max == UINT_MAX.  hc_nr will be equal to 0.  If
+        *    anyone had attempted to request 0 in the past, then their hc_nr
+        *    would be dynamically assigned, so the equal case should again
+        *    never happen.  We'll break out of the loop when we've hit a
+        *    hypercall that is at least two greater than the previous in which
+        *    case we'll set hc_nr to the current entry minus 1.  If we hit the
+        *    end of the list, then hc_nr will be equal to 0.
+        */
+       list_for_each(p, &hc_list) {
+               entry = list_entry(p, struct kvm_hc, hc_list);
+               if (entry->hc_index == hc_nr) {
+                       return -1;
+               } else if (entry->hc_index > hc_nr) {
+                       if (hc_nr == 0) {
+                               if ((hc_last_nr+1) != entry->hc_index) {
+                                       hc_nr = entry->hc_index-1;
+                                       break;
+                               }
+                       } else {
+                               break;
+                       }
+               }
+               hc_last_nr = entry->hc_index;
+       }
+
+       /* If hc_nr is zero, then hc_max was UINT_MAX and we couldn't find
+        *    any gaps in the list.  The only thing we need to worry about then
+        *    is if the last hypercall in the list was UINT_MAX.  If it was,
+        *    then we give up.  Otherwise, we set hc_nr to the last hypercall
+        *    in the list plus 1.
+        */
+       if (hc_nr == 0 && hc_last_nr != UINT_MAX) {
+               hc_nr = hc_last_nr+1;
+       } else {
+               return -1;
+       }
+
+       /* At this point, p is either at the first hypercall entry greater than
+        *    hc_nr or it's at the beginning of the list.  In either case, we
+        *    set hc->hc_index to hc_nr and add it right before p.
+        */
+       hc->hc_index = hc_nr;
+       list_add_tail(&hc->hc_list, p);
+
+       /* If this is the largest hypercall we've seen, then we'll want to know
+        *    that.  This helps us avoid reuse of hypercall indices.
+        */
+       if (hc_nr > hc_max)
+               hc_max = hc_nr;
+
+       return 0;
+}
+EXPORT_SYMBOL(kvm_hypercall_register);

--
2.0.4
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux