Add a new exit reason for emulator to handle Xen hypercalls.
Albeit these are injected only if guest has initialized the Xen
hypercall page - the hypercall is just a convenience but one
that is done by pretty much all guests. Hence if the guest
sets the hypercall page, we assume a Xen guest is going to
be set up.

Emulator will then panic with:

KVM: unknown exit reason 28
RAX=0000000000000011 RBX=ffffffff81e03e94 RCX=0000000040000000
RDX=0000000000000000
RSI=ffffffff81e03e70 RDI=0000000000000006 RBP=ffffffff81e03e90
RSP=ffffffff81e03e68
R8 =73726576206e6558 R9 =ffffffff81e03e90 R10=ffffffff81e03e94
R11=2e362e34206e6f69
R12=0000000040000004 R13=ffffffff81e03e8c R14=ffffffff81e03e88
R15=0000000000000000
RIP=ffffffff81001228 RFL=00000082 [--S----] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0000 0000000000000000 ffffffff 00c00000
CS =0010 0000000000000000 ffffffff 00a09b00 DPL=0 CS64 [-RA]
SS =0000 0000000000000000 ffffffff 00c00000
DS =0000 0000000000000000 ffffffff 00c00000
FS =0000 0000000000000000 ffffffff 00c00000
GS =0000 ffffffff81f34000 ffffffff 00c00000
LDT=0000 0000000000000000 ffffffff 00c00000
TR =0020 0000000000000000 00000fff 00808b00 DPL=0 TSS64-busy
GDT=     ffffffff81f3c000 0000007f
IDT=     ffffffff83265000 00000fff
CR0=80050033 CR2=ffff880001fa6ff8 CR3=0000000001fa6000 CR4=000406a0
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000
DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
EFER=0000000000000d01
Code=cc cc cc cc cc cc cc cc cc cc cc cc b8 11 00 00 00 0f 01 c1 <c3> cc
cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc b8 12
00 00 00 0f

Signed-off-by: Joao Martins <joao.m.mart...@oracle.com>
---
 arch/x86/include/asm/kvm_host.h | 13 +++++++
 arch/x86/kvm/Makefile           |  2 +-
 arch/x86/kvm/trace.h            | 33 +++++++++++++++++
 arch/x86/kvm/x86.c              | 12 +++++++
 arch/x86/kvm/xen.c              | 79 +++++++++++++++++++++++++++++++++++++++++
 arch/x86/kvm/xen.h              | 10 ++++++
 include/uapi/linux/kvm.h        | 17 ++++++++-
 7 files changed, 164 insertions(+), 2 deletions(-)
 create mode 100644 arch/x86/kvm/xen.c
 create mode 100644 arch/x86/kvm/xen.h

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 9417febf8490..0f469ce439c0 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -79,6 +79,7 @@
 #define KVM_REQ_HV_STIMER              KVM_ARCH_REQ(22)
 #define KVM_REQ_LOAD_EOI_EXITMAP       KVM_ARCH_REQ(23)
 #define KVM_REQ_GET_VMCS12_PAGES       KVM_ARCH_REQ(24)
+#define KVM_REQ_XEN_EXIT               KVM_ARCH_REQ(25)
 
 #define CR0_RESERVED_BITS                                               \
        (~(unsigned long)(X86_CR0_PE | X86_CR0_MP | X86_CR0_EM | X86_CR0_TS \
@@ -533,6 +534,11 @@ struct kvm_vcpu_hv {
        cpumask_t tlb_flush;
 };
 
+/* Xen per vcpu emulation context */
+struct kvm_vcpu_xen {
+       struct kvm_xen_exit exit;
+};
+
 struct kvm_vcpu_arch {
        /*
         * rip and regs accesses must go through
@@ -720,6 +726,7 @@ struct kvm_vcpu_arch {
        unsigned long singlestep_rip;
 
        struct kvm_vcpu_hv hyperv;
+       struct kvm_vcpu_xen xen;
 
        cpumask_var_t wbinvd_dirty_mask;
 
@@ -833,6 +840,11 @@ struct kvm_hv {
        atomic_t num_mismatched_vp_indexes;
 };
 
+/* Xen emulation context */
+struct kvm_xen {
+       u64 xen_hypercall;
+};
+
 enum kvm_irqchip_mode {
        KVM_IRQCHIP_NONE,
        KVM_IRQCHIP_KERNEL,       /* created with KVM_CREATE_IRQCHIP */
@@ -899,6 +911,7 @@ struct kvm_arch {
        struct hlist_head mask_notifier_list;
 
        struct kvm_hv hyperv;
+       struct kvm_xen xen;
 
        #ifdef CONFIG_KVM_MMU_AUDIT
        int audit_point;
diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
index 31ecf7a76d5a..2b46c93c9380 100644
--- a/arch/x86/kvm/Makefile
+++ b/arch/x86/kvm/Makefile
@@ -10,7 +10,7 @@ kvm-$(CONFIG_KVM_ASYNC_PF)    += $(KVM)/async_pf.o
 
 kvm-y                  += x86.o mmu.o emulate.o i8259.o irq.o lapic.o \
                           i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \
-                          hyperv.o page_track.o debugfs.o
+                          hyperv.o xen.o page_track.o debugfs.o
 
 kvm-intel-y            += vmx/vmx.o vmx/vmenter.o vmx/pmu_intel.o vmx/vmcs12.o 
vmx/evmcs.o vmx/nested.o
 kvm-amd-y              += svm.o pmu_amd.o
diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h
index 6432d08c7de7..4fe9fd86292f 100644
--- a/arch/x86/kvm/trace.h
+++ b/arch/x86/kvm/trace.h
@@ -91,6 +91,39 @@ TRACE_EVENT(kvm_hv_hypercall,
 );
 
 /*
+ * Tracepoint for Xen hypercall.
+ */
+TRACE_EVENT(kvm_xen_hypercall,
+       TP_PROTO(unsigned long nr, unsigned long a0, unsigned long a1,
+                unsigned long a2, unsigned long a3, unsigned long a4),
+       TP_ARGS(nr, a0, a1, a2, a3, a4),
+
+       TP_STRUCT__entry(
+               __field(unsigned long, nr)
+               __field(unsigned long, a0)
+               __field(unsigned long, a1)
+               __field(unsigned long, a2)
+               __field(unsigned long, a3)
+               __field(unsigned long, a4)
+       ),
+
+       TP_fast_assign(
+               __entry->nr = nr;
+               __entry->a0 = a0;
+               __entry->a1 = a1;
+               __entry->a2 = a2;
+               __entry->a3 = a3;
+               __entry->a4 = a4;
+       ),
+
+       TP_printk("nr 0x%lx a0 0x%lx a1 0x%lx a2 0x%lx a3 0x%lx a4 0x%lx",
+                __entry->nr, __entry->a0, __entry->a1,  __entry->a2,
+                __entry->a3, __entry->a4)
+);
+
+
+
+/*
  * Tracepoint for PIO.
  */
 
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 47360a4b0d42..be8def385e3f 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -29,6 +29,7 @@
 #include "cpuid.h"
 #include "pmu.h"
 #include "hyperv.h"
+#include "xen.h"
 
 #include <linux/clocksource.h>
 #include <linux/interrupt.h>
@@ -2338,6 +2339,8 @@ static int xen_hvm_config(struct kvm_vcpu *vcpu, u64 data)
        }
        if (kvm_vcpu_write_guest(vcpu, page_addr, page, PAGE_SIZE))
                goto out_free;
+
+       kvm_xen_hypercall_set(kvm);
        r = 0;
 out_free:
        kfree(page);
@@ -7076,6 +7079,9 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
        if (kvm_hv_hypercall_enabled(vcpu->kvm))
                return kvm_hv_hypercall(vcpu);
 
+       if (kvm_xen_hypercall_enabled(vcpu->kvm))
+               return kvm_xen_hypercall(vcpu);
+
        nr = kvm_register_read(vcpu, VCPU_REGS_RAX);
        a0 = kvm_register_read(vcpu, VCPU_REGS_RBX);
        a1 = kvm_register_read(vcpu, VCPU_REGS_RCX);
@@ -7736,6 +7742,12 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
                        r = 0;
                        goto out;
                }
+               if (kvm_check_request(KVM_REQ_XEN_EXIT, vcpu)) {
+                       vcpu->run->exit_reason = KVM_EXIT_XEN;
+                       vcpu->run->xen = vcpu->arch.xen.exit;
+                       r = 0;
+                       goto out;
+               }
 
                /*
                 * KVM_REQ_HV_STIMER has to be processed after
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
new file mode 100644
index 000000000000..76f0e4b812d2
--- /dev/null
+++ b/arch/x86/kvm/xen.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ *
+ * KVM Xen emulation
+ */
+
+#include "x86.h"
+#include "xen.h"
+
+#include <linux/kvm_host.h>
+
+#include <trace/events/kvm.h>
+
+#include "trace.h"
+
+bool kvm_xen_hypercall_enabled(struct kvm *kvm)
+{
+       return READ_ONCE(kvm->arch.xen.xen_hypercall);
+}
+
+bool kvm_xen_hypercall_set(struct kvm *kvm)
+{
+       return WRITE_ONCE(kvm->arch.xen.xen_hypercall, 1);
+}
+
+static void kvm_xen_hypercall_set_result(struct kvm_vcpu *vcpu, u64 result)
+{
+       kvm_register_write(vcpu, VCPU_REGS_RAX, result);
+}
+
+static int kvm_xen_hypercall_complete_userspace(struct kvm_vcpu *vcpu)
+{
+       struct kvm_run *run = vcpu->run;
+
+       kvm_xen_hypercall_set_result(vcpu, run->xen.u.hcall.result);
+       return kvm_skip_emulated_instruction(vcpu);
+}
+
+int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
+{
+       bool longmode;
+       u64 input, params[5];
+
+       input = (u64)kvm_register_read(vcpu, VCPU_REGS_RAX);
+
+       longmode = is_64_bit_mode(vcpu);
+       if (!longmode) {
+               params[0] = (u64)kvm_register_read(vcpu, VCPU_REGS_RBX);
+               params[1] = (u64)kvm_register_read(vcpu, VCPU_REGS_RCX);
+               params[2] = (u64)kvm_register_read(vcpu, VCPU_REGS_RDX);
+               params[3] = (u64)kvm_register_read(vcpu, VCPU_REGS_RSI);
+               params[4] = (u64)kvm_register_read(vcpu, VCPU_REGS_RDI);
+       }
+#ifdef CONFIG_X86_64
+       else {
+               params[0] = (u64)kvm_register_read(vcpu, VCPU_REGS_RDI);
+               params[1] = (u64)kvm_register_read(vcpu, VCPU_REGS_RSI);
+               params[2] = (u64)kvm_register_read(vcpu, VCPU_REGS_RDX);
+               params[3] = (u64)kvm_register_read(vcpu, VCPU_REGS_R10);
+               params[4] = (u64)kvm_register_read(vcpu, VCPU_REGS_R8);
+       }
+#endif
+       trace_kvm_xen_hypercall(input, params[0], params[1], params[2],
+                               params[3], params[4]);
+
+       vcpu->run->exit_reason = KVM_EXIT_XEN;
+       vcpu->run->xen.type = KVM_EXIT_XEN_HCALL;
+       vcpu->run->xen.u.hcall.input = input;
+       vcpu->run->xen.u.hcall.params[0] = params[0];
+       vcpu->run->xen.u.hcall.params[1] = params[1];
+       vcpu->run->xen.u.hcall.params[2] = params[2];
+       vcpu->run->xen.u.hcall.params[3] = params[3];
+       vcpu->run->xen.u.hcall.params[4] = params[4];
+       vcpu->arch.complete_userspace_io =
+               kvm_xen_hypercall_complete_userspace;
+
+       return 0;
+}
diff --git a/arch/x86/kvm/xen.h b/arch/x86/kvm/xen.h
new file mode 100644
index 000000000000..a2ae079c3ef3
--- /dev/null
+++ b/arch/x86/kvm/xen.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. */
+#ifndef __ARCH_X86_KVM_XEN_H__
+#define __ARCH_X86_KVM_XEN_H__
+
+bool kvm_xen_hypercall_enabled(struct kvm *kvm);
+bool kvm_xen_hypercall_set(struct kvm *kvm);
+int kvm_xen_hypercall(struct kvm_vcpu *vcpu);
+
+#endif
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 6d4ea4b6c922..d07520c216a1 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -204,6 +204,18 @@ struct kvm_hyperv_exit {
        } u;
 };
 
+struct kvm_xen_exit {
+#define KVM_EXIT_XEN_HCALL          1
+       __u32 type;
+       union {
+               struct {
+                       __u64 input;
+                       __u64 result;
+                       __u64 params[5];
+               } hcall;
+       } u;
+};
+
 #define KVM_S390_GET_SKEYS_NONE   1
 #define KVM_S390_SKEYS_MAX        1048576
 
@@ -235,6 +247,7 @@ struct kvm_hyperv_exit {
 #define KVM_EXIT_S390_STSI        25
 #define KVM_EXIT_IOAPIC_EOI       26
 #define KVM_EXIT_HYPERV           27
+#define KVM_EXIT_XEN              28
 
 /* For KVM_EXIT_INTERNAL_ERROR */
 /* Emulate instruction failed. */
@@ -392,8 +405,10 @@ struct kvm_run {
                } eoi;
                /* KVM_EXIT_HYPERV */
                struct kvm_hyperv_exit hyperv;
+               /* KVM_EXIT_XEN */
+               struct kvm_xen_exit xen;
                /* Fix the size of the union. */
-               char padding[256];
+               char padding[196];
        };
 
        /* 2048 is the size of the char array used to bound/pad the size
-- 
2.11.0

Reply via email to