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" 13*55a3f666SJoao Martins #include "qemu/log.h" 1461491cf4SDavid Woodhouse #include "sysemu/kvm_int.h" 1561491cf4SDavid Woodhouse #include "sysemu/kvm_xen.h" 1661491cf4SDavid Woodhouse #include "kvm/kvm_i386.h" 1761491cf4SDavid Woodhouse #include "xen-emu.h" 18*55a3f666SJoao Martins #include "trace.h" 1961491cf4SDavid Woodhouse 20f66b8a83SJoao Martins int kvm_xen_init(KVMState *s, uint32_t hypercall_msr) 2161491cf4SDavid Woodhouse { 2261491cf4SDavid Woodhouse const int required_caps = KVM_XEN_HVM_CONFIG_HYPERCALL_MSR | 2361491cf4SDavid Woodhouse KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL | KVM_XEN_HVM_CONFIG_SHARED_INFO; 2461491cf4SDavid Woodhouse struct kvm_xen_hvm_config cfg = { 25f66b8a83SJoao Martins .msr = hypercall_msr, 2661491cf4SDavid Woodhouse .flags = KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL, 2761491cf4SDavid Woodhouse }; 2861491cf4SDavid Woodhouse int xen_caps, ret; 2961491cf4SDavid Woodhouse 3061491cf4SDavid Woodhouse xen_caps = kvm_check_extension(s, KVM_CAP_XEN_HVM); 3161491cf4SDavid Woodhouse if (required_caps & ~xen_caps) { 3261491cf4SDavid Woodhouse error_report("kvm: Xen HVM guest support not present or insufficient"); 3361491cf4SDavid Woodhouse return -ENOSYS; 3461491cf4SDavid Woodhouse } 3561491cf4SDavid Woodhouse 3661491cf4SDavid Woodhouse if (xen_caps & KVM_XEN_HVM_CONFIG_EVTCHN_SEND) { 3761491cf4SDavid Woodhouse struct kvm_xen_hvm_attr ha = { 3861491cf4SDavid Woodhouse .type = KVM_XEN_ATTR_TYPE_XEN_VERSION, 3961491cf4SDavid Woodhouse .u.xen_version = s->xen_version, 4061491cf4SDavid Woodhouse }; 4161491cf4SDavid Woodhouse (void)kvm_vm_ioctl(s, KVM_XEN_HVM_SET_ATTR, &ha); 4261491cf4SDavid Woodhouse 4361491cf4SDavid Woodhouse cfg.flags |= KVM_XEN_HVM_CONFIG_EVTCHN_SEND; 4461491cf4SDavid Woodhouse } 4561491cf4SDavid Woodhouse 4661491cf4SDavid Woodhouse ret = kvm_vm_ioctl(s, KVM_XEN_HVM_CONFIG, &cfg); 4761491cf4SDavid Woodhouse if (ret < 0) { 4861491cf4SDavid Woodhouse error_report("kvm: Failed to enable Xen HVM support: %s", 4961491cf4SDavid Woodhouse strerror(-ret)); 5061491cf4SDavid Woodhouse return ret; 5161491cf4SDavid Woodhouse } 5261491cf4SDavid Woodhouse 5361491cf4SDavid Woodhouse s->xen_caps = xen_caps; 5461491cf4SDavid Woodhouse return 0; 5561491cf4SDavid Woodhouse } 5661491cf4SDavid Woodhouse 575e691a95SDavid Woodhouse int kvm_xen_init_vcpu(CPUState *cs) 585e691a95SDavid Woodhouse { 595e691a95SDavid Woodhouse int err; 605e691a95SDavid Woodhouse 615e691a95SDavid Woodhouse /* 625e691a95SDavid Woodhouse * The kernel needs to know the Xen/ACPI vCPU ID because that's 635e691a95SDavid Woodhouse * what the guest uses in hypercalls such as timers. It doesn't 645e691a95SDavid Woodhouse * match the APIC ID which is generally used for talking to the 655e691a95SDavid Woodhouse * kernel about vCPUs. And if vCPU threads race with creating 665e691a95SDavid Woodhouse * their KVM vCPUs out of order, it doesn't necessarily match 675e691a95SDavid Woodhouse * with the kernel's internal vCPU indices either. 685e691a95SDavid Woodhouse */ 695e691a95SDavid Woodhouse if (kvm_xen_has_cap(EVTCHN_SEND)) { 705e691a95SDavid Woodhouse struct kvm_xen_vcpu_attr va = { 715e691a95SDavid Woodhouse .type = KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID, 725e691a95SDavid Woodhouse .u.vcpu_id = cs->cpu_index, 735e691a95SDavid Woodhouse }; 745e691a95SDavid Woodhouse err = kvm_vcpu_ioctl(cs, KVM_XEN_VCPU_SET_ATTR, &va); 755e691a95SDavid Woodhouse if (err) { 765e691a95SDavid Woodhouse error_report("kvm: Failed to set Xen vCPU ID attribute: %s", 775e691a95SDavid Woodhouse strerror(-err)); 785e691a95SDavid Woodhouse return err; 795e691a95SDavid Woodhouse } 805e691a95SDavid Woodhouse } 815e691a95SDavid Woodhouse 825e691a95SDavid Woodhouse return 0; 835e691a95SDavid Woodhouse } 845e691a95SDavid Woodhouse 8561491cf4SDavid Woodhouse uint32_t kvm_xen_get_caps(void) 8661491cf4SDavid Woodhouse { 8761491cf4SDavid Woodhouse return kvm_state->xen_caps; 8861491cf4SDavid Woodhouse } 89*55a3f666SJoao Martins 90*55a3f666SJoao Martins static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) 91*55a3f666SJoao Martins { 92*55a3f666SJoao Martins uint16_t code = exit->u.hcall.input; 93*55a3f666SJoao Martins 94*55a3f666SJoao Martins if (exit->u.hcall.cpl > 0) { 95*55a3f666SJoao Martins exit->u.hcall.result = -EPERM; 96*55a3f666SJoao Martins return true; 97*55a3f666SJoao Martins } 98*55a3f666SJoao Martins 99*55a3f666SJoao Martins switch (code) { 100*55a3f666SJoao Martins default: 101*55a3f666SJoao Martins return false; 102*55a3f666SJoao Martins } 103*55a3f666SJoao Martins } 104*55a3f666SJoao Martins 105*55a3f666SJoao Martins int kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) 106*55a3f666SJoao Martins { 107*55a3f666SJoao Martins if (exit->type != KVM_EXIT_XEN_HCALL) { 108*55a3f666SJoao Martins return -1; 109*55a3f666SJoao Martins } 110*55a3f666SJoao Martins 111*55a3f666SJoao Martins if (!do_kvm_xen_handle_exit(cpu, exit)) { 112*55a3f666SJoao Martins /* 113*55a3f666SJoao Martins * Some hypercalls will be deliberately "implemented" by returning 114*55a3f666SJoao Martins * -ENOSYS. This case is for hypercalls which are unexpected. 115*55a3f666SJoao Martins */ 116*55a3f666SJoao Martins exit->u.hcall.result = -ENOSYS; 117*55a3f666SJoao Martins qemu_log_mask(LOG_UNIMP, "Unimplemented Xen hypercall %" 118*55a3f666SJoao Martins PRId64 " (0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64 ")\n", 119*55a3f666SJoao Martins (uint64_t)exit->u.hcall.input, 120*55a3f666SJoao Martins (uint64_t)exit->u.hcall.params[0], 121*55a3f666SJoao Martins (uint64_t)exit->u.hcall.params[1], 122*55a3f666SJoao Martins (uint64_t)exit->u.hcall.params[2]); 123*55a3f666SJoao Martins } 124*55a3f666SJoao Martins 125*55a3f666SJoao Martins trace_kvm_xen_hypercall(CPU(cpu)->cpu_index, exit->u.hcall.cpl, 126*55a3f666SJoao Martins exit->u.hcall.input, exit->u.hcall.params[0], 127*55a3f666SJoao Martins exit->u.hcall.params[1], exit->u.hcall.params[2], 128*55a3f666SJoao Martins exit->u.hcall.result); 129*55a3f666SJoao Martins return 0; 130*55a3f666SJoao Martins } 131