xref: /qemu/target/i386/kvm/xen-emu.c (revision fb0fd2ce38400e158f3eb483b0f1c553d3551445)
161491cf4SDavid Woodhouse /*
261491cf4SDavid Woodhouse  * Xen HVM emulation support in KVM
361491cf4SDavid Woodhouse  *
461491cf4SDavid Woodhouse  * Copyright © 2019 Oracle and/or its affiliates. All rights reserved.
561491cf4SDavid Woodhouse  * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
661491cf4SDavid Woodhouse  *
761491cf4SDavid Woodhouse  * This work is licensed under the terms of the GNU GPL, version 2 or later.
861491cf4SDavid Woodhouse  * See the COPYING file in the top-level directory.
961491cf4SDavid Woodhouse  *
1061491cf4SDavid Woodhouse  */
1161491cf4SDavid Woodhouse 
1261491cf4SDavid Woodhouse #include "qemu/osdep.h"
1355a3f666SJoao Martins #include "qemu/log.h"
1479b7067dSJoao Martins #include "qemu/main-loop.h"
15*fb0fd2ceSJoao Martins #include "hw/xen/xen.h"
1661491cf4SDavid Woodhouse #include "sysemu/kvm_int.h"
1761491cf4SDavid Woodhouse #include "sysemu/kvm_xen.h"
1861491cf4SDavid Woodhouse #include "kvm/kvm_i386.h"
19bedcc139SJoao Martins #include "exec/address-spaces.h"
2061491cf4SDavid Woodhouse #include "xen-emu.h"
2155a3f666SJoao Martins #include "trace.h"
2279b7067dSJoao Martins #include "sysemu/runstate.h"
2361491cf4SDavid Woodhouse 
24110a0ea5SDavid Woodhouse #include "hw/i386/kvm/xen_overlay.h"
25110a0ea5SDavid Woodhouse 
26bedcc139SJoao Martins #include "hw/xen/interface/version.h"
2779b7067dSJoao Martins #include "hw/xen/interface/sched.h"
28*fb0fd2ceSJoao Martins #include "hw/xen/interface/memory.h"
29*fb0fd2ceSJoao Martins 
30*fb0fd2ceSJoao Martins #include "xen-compat.h"
31*fb0fd2ceSJoao Martins 
32*fb0fd2ceSJoao Martins #ifdef TARGET_X86_64
33*fb0fd2ceSJoao Martins #define hypercall_compat32(longmode) (!(longmode))
34*fb0fd2ceSJoao Martins #else
35*fb0fd2ceSJoao Martins #define hypercall_compat32(longmode) (false)
36*fb0fd2ceSJoao Martins #endif
37bedcc139SJoao Martins 
38bedcc139SJoao Martins static int kvm_gva_rw(CPUState *cs, uint64_t gva, void *_buf, size_t sz,
39bedcc139SJoao Martins                       bool is_write)
40bedcc139SJoao Martins {
41bedcc139SJoao Martins     uint8_t *buf = (uint8_t *)_buf;
42bedcc139SJoao Martins     int ret;
43bedcc139SJoao Martins 
44bedcc139SJoao Martins     while (sz) {
45bedcc139SJoao Martins         struct kvm_translation tr = {
46bedcc139SJoao Martins             .linear_address = gva,
47bedcc139SJoao Martins         };
48bedcc139SJoao Martins 
49bedcc139SJoao Martins         size_t len = TARGET_PAGE_SIZE - (tr.linear_address & ~TARGET_PAGE_MASK);
50bedcc139SJoao Martins         if (len > sz) {
51bedcc139SJoao Martins             len = sz;
52bedcc139SJoao Martins         }
53bedcc139SJoao Martins 
54bedcc139SJoao Martins         ret = kvm_vcpu_ioctl(cs, KVM_TRANSLATE, &tr);
55bedcc139SJoao Martins         if (ret || !tr.valid || (is_write && !tr.writeable)) {
56bedcc139SJoao Martins             return -EFAULT;
57bedcc139SJoao Martins         }
58bedcc139SJoao Martins 
59bedcc139SJoao Martins         cpu_physical_memory_rw(tr.physical_address, buf, len, is_write);
60bedcc139SJoao Martins 
61bedcc139SJoao Martins         buf += len;
62bedcc139SJoao Martins         sz -= len;
63bedcc139SJoao Martins         gva += len;
64bedcc139SJoao Martins     }
65bedcc139SJoao Martins 
66bedcc139SJoao Martins     return 0;
67bedcc139SJoao Martins }
68bedcc139SJoao Martins 
69bedcc139SJoao Martins static inline int kvm_copy_from_gva(CPUState *cs, uint64_t gva, void *buf,
70bedcc139SJoao Martins                                     size_t sz)
71bedcc139SJoao Martins {
72bedcc139SJoao Martins     return kvm_gva_rw(cs, gva, buf, sz, false);
73bedcc139SJoao Martins }
74bedcc139SJoao Martins 
75bedcc139SJoao Martins static inline int kvm_copy_to_gva(CPUState *cs, uint64_t gva, void *buf,
76bedcc139SJoao Martins                                   size_t sz)
77bedcc139SJoao Martins {
78bedcc139SJoao Martins     return kvm_gva_rw(cs, gva, buf, sz, true);
79bedcc139SJoao Martins }
80bedcc139SJoao Martins 
81f66b8a83SJoao Martins int kvm_xen_init(KVMState *s, uint32_t hypercall_msr)
8261491cf4SDavid Woodhouse {
8361491cf4SDavid Woodhouse     const int required_caps = KVM_XEN_HVM_CONFIG_HYPERCALL_MSR |
8461491cf4SDavid Woodhouse         KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL | KVM_XEN_HVM_CONFIG_SHARED_INFO;
8561491cf4SDavid Woodhouse     struct kvm_xen_hvm_config cfg = {
86f66b8a83SJoao Martins         .msr = hypercall_msr,
8761491cf4SDavid Woodhouse         .flags = KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL,
8861491cf4SDavid Woodhouse     };
8961491cf4SDavid Woodhouse     int xen_caps, ret;
9061491cf4SDavid Woodhouse 
9161491cf4SDavid Woodhouse     xen_caps = kvm_check_extension(s, KVM_CAP_XEN_HVM);
9261491cf4SDavid Woodhouse     if (required_caps & ~xen_caps) {
9361491cf4SDavid Woodhouse         error_report("kvm: Xen HVM guest support not present or insufficient");
9461491cf4SDavid Woodhouse         return -ENOSYS;
9561491cf4SDavid Woodhouse     }
9661491cf4SDavid Woodhouse 
9761491cf4SDavid Woodhouse     if (xen_caps & KVM_XEN_HVM_CONFIG_EVTCHN_SEND) {
9861491cf4SDavid Woodhouse         struct kvm_xen_hvm_attr ha = {
9961491cf4SDavid Woodhouse             .type = KVM_XEN_ATTR_TYPE_XEN_VERSION,
10061491cf4SDavid Woodhouse             .u.xen_version = s->xen_version,
10161491cf4SDavid Woodhouse         };
10261491cf4SDavid Woodhouse         (void)kvm_vm_ioctl(s, KVM_XEN_HVM_SET_ATTR, &ha);
10361491cf4SDavid Woodhouse 
10461491cf4SDavid Woodhouse         cfg.flags |= KVM_XEN_HVM_CONFIG_EVTCHN_SEND;
10561491cf4SDavid Woodhouse     }
10661491cf4SDavid Woodhouse 
10761491cf4SDavid Woodhouse     ret = kvm_vm_ioctl(s, KVM_XEN_HVM_CONFIG, &cfg);
10861491cf4SDavid Woodhouse     if (ret < 0) {
10961491cf4SDavid Woodhouse         error_report("kvm: Failed to enable Xen HVM support: %s",
11061491cf4SDavid Woodhouse                      strerror(-ret));
11161491cf4SDavid Woodhouse         return ret;
11261491cf4SDavid Woodhouse     }
11361491cf4SDavid Woodhouse 
11461491cf4SDavid Woodhouse     s->xen_caps = xen_caps;
11561491cf4SDavid Woodhouse     return 0;
11661491cf4SDavid Woodhouse }
11761491cf4SDavid Woodhouse 
1185e691a95SDavid Woodhouse int kvm_xen_init_vcpu(CPUState *cs)
1195e691a95SDavid Woodhouse {
1205e691a95SDavid Woodhouse     int err;
1215e691a95SDavid Woodhouse 
1225e691a95SDavid Woodhouse     /*
1235e691a95SDavid Woodhouse      * The kernel needs to know the Xen/ACPI vCPU ID because that's
1245e691a95SDavid Woodhouse      * what the guest uses in hypercalls such as timers. It doesn't
1255e691a95SDavid Woodhouse      * match the APIC ID which is generally used for talking to the
1265e691a95SDavid Woodhouse      * kernel about vCPUs. And if vCPU threads race with creating
1275e691a95SDavid Woodhouse      * their KVM vCPUs out of order, it doesn't necessarily match
1285e691a95SDavid Woodhouse      * with the kernel's internal vCPU indices either.
1295e691a95SDavid Woodhouse      */
1305e691a95SDavid Woodhouse     if (kvm_xen_has_cap(EVTCHN_SEND)) {
1315e691a95SDavid Woodhouse         struct kvm_xen_vcpu_attr va = {
1325e691a95SDavid Woodhouse             .type = KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID,
1335e691a95SDavid Woodhouse             .u.vcpu_id = cs->cpu_index,
1345e691a95SDavid Woodhouse         };
1355e691a95SDavid Woodhouse         err = kvm_vcpu_ioctl(cs, KVM_XEN_VCPU_SET_ATTR, &va);
1365e691a95SDavid Woodhouse         if (err) {
1375e691a95SDavid Woodhouse             error_report("kvm: Failed to set Xen vCPU ID attribute: %s",
1385e691a95SDavid Woodhouse                          strerror(-err));
1395e691a95SDavid Woodhouse             return err;
1405e691a95SDavid Woodhouse         }
1415e691a95SDavid Woodhouse     }
1425e691a95SDavid Woodhouse 
1435e691a95SDavid Woodhouse     return 0;
1445e691a95SDavid Woodhouse }
1455e691a95SDavid Woodhouse 
14661491cf4SDavid Woodhouse uint32_t kvm_xen_get_caps(void)
14761491cf4SDavid Woodhouse {
14861491cf4SDavid Woodhouse     return kvm_state->xen_caps;
14961491cf4SDavid Woodhouse }
15055a3f666SJoao Martins 
151bedcc139SJoao Martins static bool kvm_xen_hcall_xen_version(struct kvm_xen_exit *exit, X86CPU *cpu,
152bedcc139SJoao Martins                                      int cmd, uint64_t arg)
153bedcc139SJoao Martins {
154bedcc139SJoao Martins     int err = 0;
155bedcc139SJoao Martins 
156bedcc139SJoao Martins     switch (cmd) {
157bedcc139SJoao Martins     case XENVER_get_features: {
158bedcc139SJoao Martins         struct xen_feature_info fi;
159bedcc139SJoao Martins 
160bedcc139SJoao Martins         /* No need for 32/64 compat handling */
161bedcc139SJoao Martins         qemu_build_assert(sizeof(fi) == 8);
162bedcc139SJoao Martins 
163bedcc139SJoao Martins         err = kvm_copy_from_gva(CPU(cpu), arg, &fi, sizeof(fi));
164bedcc139SJoao Martins         if (err) {
165bedcc139SJoao Martins             break;
166bedcc139SJoao Martins         }
167bedcc139SJoao Martins 
168bedcc139SJoao Martins         fi.submap = 0;
169bedcc139SJoao Martins         if (fi.submap_idx == 0) {
170bedcc139SJoao Martins             fi.submap |= 1 << XENFEAT_writable_page_tables |
171bedcc139SJoao Martins                          1 << XENFEAT_writable_descriptor_tables |
172bedcc139SJoao Martins                          1 << XENFEAT_auto_translated_physmap |
173bedcc139SJoao Martins                          1 << XENFEAT_supervisor_mode_kernel;
174bedcc139SJoao Martins         }
175bedcc139SJoao Martins 
176bedcc139SJoao Martins         err = kvm_copy_to_gva(CPU(cpu), arg, &fi, sizeof(fi));
177bedcc139SJoao Martins         break;
178bedcc139SJoao Martins     }
179bedcc139SJoao Martins 
180bedcc139SJoao Martins     default:
181bedcc139SJoao Martins         return false;
182bedcc139SJoao Martins     }
183bedcc139SJoao Martins 
184bedcc139SJoao Martins     exit->u.hcall.result = err;
185bedcc139SJoao Martins     return true;
186bedcc139SJoao Martins }
187bedcc139SJoao Martins 
188*fb0fd2ceSJoao Martins static int xen_set_shared_info(uint64_t gfn)
189*fb0fd2ceSJoao Martins {
190*fb0fd2ceSJoao Martins     uint64_t gpa = gfn << TARGET_PAGE_BITS;
191*fb0fd2ceSJoao Martins     int err;
192*fb0fd2ceSJoao Martins 
193*fb0fd2ceSJoao Martins     QEMU_IOTHREAD_LOCK_GUARD();
194*fb0fd2ceSJoao Martins 
195*fb0fd2ceSJoao Martins     /*
196*fb0fd2ceSJoao Martins      * The xen_overlay device tells KVM about it too, since it had to
197*fb0fd2ceSJoao Martins      * do that on migration load anyway (unless we're going to jump
198*fb0fd2ceSJoao Martins      * through lots of hoops to maintain the fiction that this isn't
199*fb0fd2ceSJoao Martins      * KVM-specific.
200*fb0fd2ceSJoao Martins      */
201*fb0fd2ceSJoao Martins     err = xen_overlay_map_shinfo_page(gpa);
202*fb0fd2ceSJoao Martins     if (err) {
203*fb0fd2ceSJoao Martins             return err;
204*fb0fd2ceSJoao Martins     }
205*fb0fd2ceSJoao Martins 
206*fb0fd2ceSJoao Martins     trace_kvm_xen_set_shared_info(gfn);
207*fb0fd2ceSJoao Martins 
208*fb0fd2ceSJoao Martins     return err;
209*fb0fd2ceSJoao Martins }
210*fb0fd2ceSJoao Martins 
211*fb0fd2ceSJoao Martins static int add_to_physmap_one(uint32_t space, uint64_t idx, uint64_t gfn)
212*fb0fd2ceSJoao Martins {
213*fb0fd2ceSJoao Martins     switch (space) {
214*fb0fd2ceSJoao Martins     case XENMAPSPACE_shared_info:
215*fb0fd2ceSJoao Martins         if (idx > 0) {
216*fb0fd2ceSJoao Martins             return -EINVAL;
217*fb0fd2ceSJoao Martins         }
218*fb0fd2ceSJoao Martins         return xen_set_shared_info(gfn);
219*fb0fd2ceSJoao Martins 
220*fb0fd2ceSJoao Martins     case XENMAPSPACE_grant_table:
221*fb0fd2ceSJoao Martins     case XENMAPSPACE_gmfn:
222*fb0fd2ceSJoao Martins     case XENMAPSPACE_gmfn_range:
223*fb0fd2ceSJoao Martins         return -ENOTSUP;
224*fb0fd2ceSJoao Martins 
225*fb0fd2ceSJoao Martins     case XENMAPSPACE_gmfn_foreign:
226*fb0fd2ceSJoao Martins     case XENMAPSPACE_dev_mmio:
227*fb0fd2ceSJoao Martins         return -EPERM;
228*fb0fd2ceSJoao Martins 
229*fb0fd2ceSJoao Martins     default:
230*fb0fd2ceSJoao Martins         return -EINVAL;
231*fb0fd2ceSJoao Martins     }
232*fb0fd2ceSJoao Martins }
233*fb0fd2ceSJoao Martins 
234*fb0fd2ceSJoao Martins static int do_add_to_physmap(struct kvm_xen_exit *exit, X86CPU *cpu,
235*fb0fd2ceSJoao Martins                              uint64_t arg)
236*fb0fd2ceSJoao Martins {
237*fb0fd2ceSJoao Martins     struct xen_add_to_physmap xatp;
238*fb0fd2ceSJoao Martins     CPUState *cs = CPU(cpu);
239*fb0fd2ceSJoao Martins 
240*fb0fd2ceSJoao Martins     if (hypercall_compat32(exit->u.hcall.longmode)) {
241*fb0fd2ceSJoao Martins         struct compat_xen_add_to_physmap xatp32;
242*fb0fd2ceSJoao Martins 
243*fb0fd2ceSJoao Martins         qemu_build_assert(sizeof(struct compat_xen_add_to_physmap) == 16);
244*fb0fd2ceSJoao Martins         if (kvm_copy_from_gva(cs, arg, &xatp32, sizeof(xatp32))) {
245*fb0fd2ceSJoao Martins             return -EFAULT;
246*fb0fd2ceSJoao Martins         }
247*fb0fd2ceSJoao Martins         xatp.domid = xatp32.domid;
248*fb0fd2ceSJoao Martins         xatp.size = xatp32.size;
249*fb0fd2ceSJoao Martins         xatp.space = xatp32.space;
250*fb0fd2ceSJoao Martins         xatp.idx = xatp32.idx;
251*fb0fd2ceSJoao Martins         xatp.gpfn = xatp32.gpfn;
252*fb0fd2ceSJoao Martins     } else {
253*fb0fd2ceSJoao Martins         if (kvm_copy_from_gva(cs, arg, &xatp, sizeof(xatp))) {
254*fb0fd2ceSJoao Martins             return -EFAULT;
255*fb0fd2ceSJoao Martins         }
256*fb0fd2ceSJoao Martins     }
257*fb0fd2ceSJoao Martins 
258*fb0fd2ceSJoao Martins     if (xatp.domid != DOMID_SELF && xatp.domid != xen_domid) {
259*fb0fd2ceSJoao Martins         return -ESRCH;
260*fb0fd2ceSJoao Martins     }
261*fb0fd2ceSJoao Martins 
262*fb0fd2ceSJoao Martins     return add_to_physmap_one(xatp.space, xatp.idx, xatp.gpfn);
263*fb0fd2ceSJoao Martins }
264*fb0fd2ceSJoao Martins 
265*fb0fd2ceSJoao Martins static bool kvm_xen_hcall_memory_op(struct kvm_xen_exit *exit, X86CPU *cpu,
266*fb0fd2ceSJoao Martins                                    int cmd, uint64_t arg)
267*fb0fd2ceSJoao Martins {
268*fb0fd2ceSJoao Martins     int err;
269*fb0fd2ceSJoao Martins 
270*fb0fd2ceSJoao Martins     switch (cmd) {
271*fb0fd2ceSJoao Martins     case XENMEM_add_to_physmap:
272*fb0fd2ceSJoao Martins         err = do_add_to_physmap(exit, cpu, arg);
273*fb0fd2ceSJoao Martins         break;
274*fb0fd2ceSJoao Martins 
275*fb0fd2ceSJoao Martins     default:
276*fb0fd2ceSJoao Martins         return false;
277*fb0fd2ceSJoao Martins     }
278*fb0fd2ceSJoao Martins 
279*fb0fd2ceSJoao Martins     exit->u.hcall.result = err;
280*fb0fd2ceSJoao Martins     return true;
281*fb0fd2ceSJoao Martins }
282*fb0fd2ceSJoao Martins 
28379b7067dSJoao Martins int kvm_xen_soft_reset(void)
28479b7067dSJoao Martins {
285*fb0fd2ceSJoao Martins     int err;
286*fb0fd2ceSJoao Martins 
28779b7067dSJoao Martins     assert(qemu_mutex_iothread_locked());
28879b7067dSJoao Martins 
28979b7067dSJoao Martins     trace_kvm_xen_soft_reset();
29079b7067dSJoao Martins 
291*fb0fd2ceSJoao Martins     err = xen_overlay_map_shinfo_page(INVALID_GFN);
292*fb0fd2ceSJoao Martins     if (err) {
293*fb0fd2ceSJoao Martins         return err;
294*fb0fd2ceSJoao Martins     }
295*fb0fd2ceSJoao Martins 
29679b7067dSJoao Martins     return 0;
29779b7067dSJoao Martins }
29879b7067dSJoao Martins 
29979b7067dSJoao Martins static int schedop_shutdown(CPUState *cs, uint64_t arg)
30079b7067dSJoao Martins {
30179b7067dSJoao Martins     struct sched_shutdown shutdown;
30279b7067dSJoao Martins     int ret = 0;
30379b7067dSJoao Martins 
30479b7067dSJoao Martins     /* No need for 32/64 compat handling */
30579b7067dSJoao Martins     qemu_build_assert(sizeof(shutdown) == 4);
30679b7067dSJoao Martins 
30779b7067dSJoao Martins     if (kvm_copy_from_gva(cs, arg, &shutdown, sizeof(shutdown))) {
30879b7067dSJoao Martins         return -EFAULT;
30979b7067dSJoao Martins     }
31079b7067dSJoao Martins 
31179b7067dSJoao Martins     switch (shutdown.reason) {
31279b7067dSJoao Martins     case SHUTDOWN_crash:
31379b7067dSJoao Martins         cpu_dump_state(cs, stderr, CPU_DUMP_CODE);
31479b7067dSJoao Martins         qemu_system_guest_panicked(NULL);
31579b7067dSJoao Martins         break;
31679b7067dSJoao Martins 
31779b7067dSJoao Martins     case SHUTDOWN_reboot:
31879b7067dSJoao Martins         qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
31979b7067dSJoao Martins         break;
32079b7067dSJoao Martins 
32179b7067dSJoao Martins     case SHUTDOWN_poweroff:
32279b7067dSJoao Martins         qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
32379b7067dSJoao Martins         break;
32479b7067dSJoao Martins 
32579b7067dSJoao Martins     case SHUTDOWN_soft_reset:
32679b7067dSJoao Martins         qemu_mutex_lock_iothread();
32779b7067dSJoao Martins         ret = kvm_xen_soft_reset();
32879b7067dSJoao Martins         qemu_mutex_unlock_iothread();
32979b7067dSJoao Martins         break;
33079b7067dSJoao Martins 
33179b7067dSJoao Martins     default:
33279b7067dSJoao Martins         ret = -EINVAL;
33379b7067dSJoao Martins         break;
33479b7067dSJoao Martins     }
33579b7067dSJoao Martins 
33679b7067dSJoao Martins     return ret;
33779b7067dSJoao Martins }
33879b7067dSJoao Martins 
33979b7067dSJoao Martins static bool kvm_xen_hcall_sched_op(struct kvm_xen_exit *exit, X86CPU *cpu,
34079b7067dSJoao Martins                                    int cmd, uint64_t arg)
34179b7067dSJoao Martins {
34279b7067dSJoao Martins     CPUState *cs = CPU(cpu);
34379b7067dSJoao Martins     int err = -ENOSYS;
34479b7067dSJoao Martins 
34579b7067dSJoao Martins     switch (cmd) {
34679b7067dSJoao Martins     case SCHEDOP_shutdown:
34779b7067dSJoao Martins         err = schedop_shutdown(cs, arg);
34879b7067dSJoao Martins         break;
34979b7067dSJoao Martins 
350c789b9efSDavid Woodhouse     case SCHEDOP_poll:
351c789b9efSDavid Woodhouse         /*
352c789b9efSDavid Woodhouse          * Linux will panic if this doesn't work. Just yield; it's not
353c789b9efSDavid Woodhouse          * worth overthinking it because with event channel handling
354c789b9efSDavid Woodhouse          * in KVM, the kernel will intercept this and it will never
355c789b9efSDavid Woodhouse          * reach QEMU anyway. The semantics of the hypercall explicltly
356c789b9efSDavid Woodhouse          * permit spurious wakeups.
357c789b9efSDavid Woodhouse          */
358c789b9efSDavid Woodhouse     case SCHEDOP_yield:
359c789b9efSDavid Woodhouse         sched_yield();
360c789b9efSDavid Woodhouse         err = 0;
361c789b9efSDavid Woodhouse         break;
362c789b9efSDavid Woodhouse 
36379b7067dSJoao Martins     default:
36479b7067dSJoao Martins         return false;
36579b7067dSJoao Martins     }
36679b7067dSJoao Martins 
36779b7067dSJoao Martins     exit->u.hcall.result = err;
36879b7067dSJoao Martins     return true;
36979b7067dSJoao Martins }
37079b7067dSJoao Martins 
37155a3f666SJoao Martins static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit)
37255a3f666SJoao Martins {
37355a3f666SJoao Martins     uint16_t code = exit->u.hcall.input;
37455a3f666SJoao Martins 
37555a3f666SJoao Martins     if (exit->u.hcall.cpl > 0) {
37655a3f666SJoao Martins         exit->u.hcall.result = -EPERM;
37755a3f666SJoao Martins         return true;
37855a3f666SJoao Martins     }
37955a3f666SJoao Martins 
38055a3f666SJoao Martins     switch (code) {
38179b7067dSJoao Martins     case __HYPERVISOR_sched_op:
38279b7067dSJoao Martins         return kvm_xen_hcall_sched_op(exit, cpu, exit->u.hcall.params[0],
38379b7067dSJoao Martins                                       exit->u.hcall.params[1]);
384*fb0fd2ceSJoao Martins     case __HYPERVISOR_memory_op:
385*fb0fd2ceSJoao Martins         return kvm_xen_hcall_memory_op(exit, cpu, exit->u.hcall.params[0],
386*fb0fd2ceSJoao Martins                                        exit->u.hcall.params[1]);
387bedcc139SJoao Martins     case __HYPERVISOR_xen_version:
388bedcc139SJoao Martins         return kvm_xen_hcall_xen_version(exit, cpu, exit->u.hcall.params[0],
389bedcc139SJoao Martins                                          exit->u.hcall.params[1]);
39055a3f666SJoao Martins     default:
39155a3f666SJoao Martins         return false;
39255a3f666SJoao Martins     }
39355a3f666SJoao Martins }
39455a3f666SJoao Martins 
39555a3f666SJoao Martins int kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit)
39655a3f666SJoao Martins {
39755a3f666SJoao Martins     if (exit->type != KVM_EXIT_XEN_HCALL) {
39855a3f666SJoao Martins         return -1;
39955a3f666SJoao Martins     }
40055a3f666SJoao Martins 
401110a0ea5SDavid Woodhouse     /*
402110a0ea5SDavid Woodhouse      * The kernel latches the guest 32/64 mode when the MSR is used to fill
403110a0ea5SDavid Woodhouse      * the hypercall page. So if we see a hypercall in a mode that doesn't
404110a0ea5SDavid Woodhouse      * match our own idea of the guest mode, fetch the kernel's idea of the
405110a0ea5SDavid Woodhouse      * "long mode" to remain in sync.
406110a0ea5SDavid Woodhouse      */
407110a0ea5SDavid Woodhouse     if (exit->u.hcall.longmode != xen_is_long_mode()) {
408110a0ea5SDavid Woodhouse         xen_sync_long_mode();
409110a0ea5SDavid Woodhouse     }
410110a0ea5SDavid Woodhouse 
41155a3f666SJoao Martins     if (!do_kvm_xen_handle_exit(cpu, exit)) {
41255a3f666SJoao Martins         /*
41355a3f666SJoao Martins          * Some hypercalls will be deliberately "implemented" by returning
41455a3f666SJoao Martins          * -ENOSYS. This case is for hypercalls which are unexpected.
41555a3f666SJoao Martins          */
41655a3f666SJoao Martins         exit->u.hcall.result = -ENOSYS;
41755a3f666SJoao Martins         qemu_log_mask(LOG_UNIMP, "Unimplemented Xen hypercall %"
41855a3f666SJoao Martins                       PRId64 " (0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64 ")\n",
41955a3f666SJoao Martins                       (uint64_t)exit->u.hcall.input,
42055a3f666SJoao Martins                       (uint64_t)exit->u.hcall.params[0],
42155a3f666SJoao Martins                       (uint64_t)exit->u.hcall.params[1],
42255a3f666SJoao Martins                       (uint64_t)exit->u.hcall.params[2]);
42355a3f666SJoao Martins     }
42455a3f666SJoao Martins 
42555a3f666SJoao Martins     trace_kvm_xen_hypercall(CPU(cpu)->cpu_index, exit->u.hcall.cpl,
42655a3f666SJoao Martins                             exit->u.hcall.input, exit->u.hcall.params[0],
42755a3f666SJoao Martins                             exit->u.hcall.params[1], exit->u.hcall.params[2],
42855a3f666SJoao Martins                             exit->u.hcall.result);
42955a3f666SJoao Martins     return 0;
43055a3f666SJoao Martins }
431