VMware products handle hypercalls in userspace. Give KVM the ability to run VMware guests unmodified by fowarding all hypercalls to the userspace. Enabling of the KVM_CAP_X86_VMWARE_HYPERCALL_ENABLE capability turns the feature on - it's off by default. This allows vmx's built on top of KVM to support VMware specific hypercalls. Signed-off-by: Zack Rusin <zack.rusin@xxxxxxxxxxxx> Cc: Doug Covelli <doug.covelli@xxxxxxxxxxxx> Cc: Paolo Bonzini <pbonzini@xxxxxxxxxx> Cc: Jonathan Corbet <corbet@xxxxxxx> Cc: Sean Christopherson <seanjc@xxxxxxxxxx> Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx> Cc: Ingo Molnar <mingo@xxxxxxxxxx> Cc: Borislav Petkov <bp@xxxxxxxxx> Cc: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx> Cc: x86@xxxxxxxxxx Cc: "H. Peter Anvin" <hpa@xxxxxxxxx> Cc: Zack Rusin <zack.rusin@xxxxxxxxxxxx> Cc: kvm@xxxxxxxxxxxxxxx Cc: linux-doc@xxxxxxxxxxxxxxx Cc: linux-kernel@xxxxxxxxxxxxxxx --- Documentation/virt/kvm/api.rst | 57 +++++++++++++++++++++++++-- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/Kconfig | 6 ++- arch/x86/kvm/kvm_vmware.c | 69 +++++++++++++++++++++++++++++++++ arch/x86/kvm/kvm_vmware.h | 16 ++++++++ arch/x86/kvm/x86.c | 11 ++++++ include/uapi/linux/kvm.h | 25 ++++++++++++ 7 files changed, 179 insertions(+), 6 deletions(-) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 24bc80764fdc..6d3d2a509848 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -6624,10 +6624,11 @@ to the byte array. .. note:: For KVM_EXIT_IO, KVM_EXIT_MMIO, KVM_EXIT_OSI, KVM_EXIT_PAPR, KVM_EXIT_XEN, - KVM_EXIT_EPR, KVM_EXIT_X86_RDMSR and KVM_EXIT_X86_WRMSR the corresponding - operations are complete (and guest state is consistent) only after userspace - has re-entered the kernel with KVM_RUN. The kernel side will first finish - incomplete operations and then check for pending signals. + KVM_EXIT_EPR, KVM_EXIT_VMWARE, KVM_EXIT_X86_RDMSR and KVM_EXIT_X86_WRMSR + the corresponding operations are complete (and guest state is consistent) + only after userspace has re-entered the kernel with KVM_RUN. The kernel + side will first finish incomplete operations and then check for pending + signals. The pending state of the operation is not preserved in state which is visible to userspace, thus userspace should ensure that the operation is @@ -8273,6 +8274,54 @@ default value for it is set via the kvm.enable_vmware_backdoor kernel parameter (false when not set). Must be set before any VCPUs have been created. +7.38 KVM_CAP_X86_VMWARE_HYPERCALL +--------------------------------- + +:Architectures: x86 +:Parameters: args[0] whether the feature should be enabled or not +:Returns: 0 on success. + +Capability allows userspace to handle hypercalls. When enabled +whenever the vcpu has executed a VMCALL(Intel) or a VMMCALL(AMD) +instruction kvm will exit to userspace with KVM_EXIT_VMWARE. + +On exit the vmware structure of the kvm_run structure will +look as follows: + +:: + + struct kvm_vmware_exit { + #define KVM_EXIT_VMWARE_HCALL 1 + __u32 type; + __u32 pad1; + union { + struct { + __u32 longmode;/* true if in long/64bit mode */ + __u32 cpl; + __u64 rax, rbx, rcx, rdx, rsi, rdi, rbp; + __u64 result; /* will be written to eax on return */ + struct { + __u32 inject; + __u32 pad2; + __u32 vector; + __u32 error_code; + __u64 address; + } exception; + } hcall; + }; + }; + +The exception structure of the kvm_vmware_call.hcall member allows +the monitor to inject an exception in the guest. On return if the +exception.inject is set true the remaining members of the exception +structure will be used to create and queue up an exception for the +guest. + +Except when running in compatibility mode with VMware hypervisors +userspace handling of hypercalls is discouraged. To implement +such functionality, use KVM_EXIT_IO (x86) or KVM_EXIT_MMIO +(all except s390). + 8. Other capabilities. ====================== diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 5670d7d02d1b..86bacda2802e 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1196,6 +1196,7 @@ struct kvm_xen { /* VMware emulation context */ struct kvm_vmware { bool backdoor_enabled; + bool hypercall_enabled; }; #endif diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig index 9e3be87fc82b..f817601924bd 100644 --- a/arch/x86/kvm/Kconfig +++ b/arch/x86/kvm/Kconfig @@ -183,11 +183,13 @@ config KVM_VMWARE depends on KVM default y help - Provides KVM support for hosting VMware guests. Adds support for - VMware legacy backdoor interface: VMware tools and various userspace + Provides KVM support for hosting VMware guests. KVM features that can + be turned on when this option is enabled include: + - VMware legacy backdoor interface: VMware tools and various userspace utilities running in VMware guests sometimes utilize specially formatted IN, OUT and RDPMC instructions which need to be intercepted. + - VMware hypercall interface: VMware hypercalls exit to userspace If unsure, say "Y". diff --git a/arch/x86/kvm/kvm_vmware.c b/arch/x86/kvm/kvm_vmware.c index b8ede454751f..096adb92ac60 100644 --- a/arch/x86/kvm/kvm_vmware.c +++ b/arch/x86/kvm/kvm_vmware.c @@ -6,6 +6,8 @@ #include "kvm_vmware.h" + #include "x86.h" + bool __read_mostly enable_vmware_backdoor; EXPORT_SYMBOL_GPL(enable_vmware_backdoor); module_param(enable_vmware_backdoor, bool, 0444); @@ -14,3 +16,70 @@ void kvm_vmware_init_vm(struct kvm *kvm) { kvm->arch.vmware.backdoor_enabled = enable_vmware_backdoor; } + +static int complete_hypercall_exit(struct kvm_vcpu *vcpu) +{ + u64 ret = vcpu->run->vmware.hcall.result; + + if (!is_64_bit_hypercall(vcpu)) + ret = (u32)ret; + kvm_rax_write(vcpu, ret); + + if (vcpu->run->vmware.hcall.exception.inject) { + u32 vector = vcpu->run->vmware.hcall.exception.vector; + u32 error_code = vcpu->run->vmware.hcall.exception.error_code; + u32 address = vcpu->run->vmware.hcall.exception.address; + bool has_error_code = x86_exception_has_error_code(vector); + + if (vector == PF_VECTOR) { + struct x86_exception fault = {0}; + + fault.vector = PF_VECTOR; + fault.error_code_valid = true; + fault.error_code = error_code; + fault.address = address; + + kvm_inject_page_fault(vcpu, &fault); + } else if (has_error_code) { + kvm_queue_exception_e(vcpu, vector, error_code); + } else { + kvm_queue_exception(vcpu, vector); + } + + /* + * Don't skip the instruction to deliver the exception + * at the backdoor call + */ + return 1; + } + + return kvm_skip_emulated_instruction(vcpu); +} + +int kvm_vmware_hypercall(struct kvm_vcpu *vcpu) +{ + struct kvm_run *run = vcpu->run; + bool is_64_bit = is_64_bit_hypercall(vcpu); + u64 mask = is_64_bit ? U64_MAX : U32_MAX; + + run->exit_reason = KVM_EXIT_VMWARE; + run->vmware.type = KVM_EXIT_VMWARE_HCALL; + run->vmware.hcall.longmode = is_64_bit; + run->vmware.hcall.rax = kvm_rax_read(vcpu) & mask; + run->vmware.hcall.rbx = kvm_rbx_read(vcpu) & mask; + run->vmware.hcall.rcx = kvm_rcx_read(vcpu) & mask; + run->vmware.hcall.rdx = kvm_rdx_read(vcpu) & mask; + run->vmware.hcall.rsi = kvm_rsi_read(vcpu) & mask; + run->vmware.hcall.rdi = kvm_rdi_read(vcpu) & mask; + run->vmware.hcall.rbp = kvm_rbp_read(vcpu) & mask; + run->vmware.hcall.cpl = kvm_x86_call(get_cpl)(vcpu); + run->vmware.hcall.result = 0; + run->vmware.hcall.exception.inject = 0; + run->vmware.hcall.exception.vector = 0; + run->vmware.hcall.exception.error_code = 0; + run->vmware.hcall.exception.address = 0; + + vcpu->arch.complete_userspace_io = complete_hypercall_exit; + + return 0; +} diff --git a/arch/x86/kvm/kvm_vmware.h b/arch/x86/kvm/kvm_vmware.h index de55c9ee7c0f..846b90091a2a 100644 --- a/arch/x86/kvm/kvm_vmware.h +++ b/arch/x86/kvm/kvm_vmware.h @@ -93,7 +93,13 @@ static inline bool kvm_vmware_is_backdoor_opcode(u8 opcode_len, u8 b) return false; } +static inline bool kvm_vmware_hypercall_enabled(struct kvm *kvm) +{ + return kvm->arch.vmware.hypercall_enabled; +} + void kvm_vmware_init_vm(struct kvm *kvm); +int kvm_vmware_hypercall(struct kvm_vcpu *vcpu); #else /* !CONFIG_KVM_VMWARE */ @@ -126,6 +132,16 @@ static inline void kvm_vmware_init_vm(struct kvm *kvm) { } +static inline bool kvm_vmware_hypercall_enabled(struct kvm *kvm) +{ + return false; +} + +static inline int kvm_vmware_hypercall(struct kvm_vcpu *vcpu) +{ + return 0; +} + #endif /* CONFIG_KVM_VMWARE */ #endif /* __ARCH_X86_KVM_VMWARE_H__ */ diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index a0b0830e5ece..300cef9a37e2 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4652,6 +4652,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_X86_GUEST_MODE: #ifdef CONFIG_KVM_VMWARE case KVM_CAP_X86_VMWARE_BACKDOOR: + case KVM_CAP_X86_VMWARE_HYPERCALL: #endif r = 1; break; @@ -6746,6 +6747,13 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, } mutex_unlock(&kvm->lock); break; + case KVM_CAP_X86_VMWARE_HYPERCALL: + r = -EINVAL; + if (cap->args[0] & ~1) + break; + kvm->arch.vmware.hypercall_enabled = cap->args[0]; + r = 0; + break; #endif default: r = -EINVAL; @@ -10085,6 +10093,9 @@ EXPORT_SYMBOL_GPL(____kvm_emulate_hypercall); int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) { + if (kvm_vmware_hypercall_enabled(vcpu->kvm)) + return kvm_vmware_hypercall(vcpu); + if (kvm_xen_hypercall_enabled(vcpu->kvm)) return kvm_xen_hypercall(vcpu); diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 793d0cf7ae3c..adf1a1449c06 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -135,6 +135,27 @@ struct kvm_xen_exit { } u; }; +struct kvm_vmware_exit { +#define KVM_EXIT_VMWARE_HCALL 1 + __u32 type; + __u32 pad1; + union { + struct { + __u32 longmode; + __u32 cpl; + __u64 rax, rbx, rcx, rdx, rsi, rdi, rbp; + __u64 result; + struct { + __u32 inject; + __u32 pad2; + __u32 vector; + __u32 error_code; + __u64 address; + } exception; + } hcall; + }; +}; + #define KVM_S390_GET_SKEYS_NONE 1 #define KVM_S390_SKEYS_MAX 1048576 @@ -178,6 +199,7 @@ struct kvm_xen_exit { #define KVM_EXIT_NOTIFY 37 #define KVM_EXIT_LOONGARCH_IOCSR 38 #define KVM_EXIT_MEMORY_FAULT 39 +#define KVM_EXIT_VMWARE 40 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -420,6 +442,8 @@ struct kvm_run { } msr; /* KVM_EXIT_XEN */ struct kvm_xen_exit xen; + /* KVM_EXIT_VMWARE */ + struct kvm_vmware_exit vmware; /* KVM_EXIT_RISCV_SBI */ struct { unsigned long extension_id; @@ -930,6 +954,7 @@ struct kvm_enable_cap { #define KVM_CAP_X86_APIC_BUS_CYCLES_NS 237 #define KVM_CAP_X86_GUEST_MODE 238 #define KVM_CAP_X86_VMWARE_BACKDOOR 239 +#define KVM_CAP_X86_VMWARE_HYPERCALL 240 struct kvm_irq_routing_irqchip { __u32 irqchip; -- 2.48.1