This is the second version of a series that lets us run VMware Workstation on Linux on top of KVM.
The most significant change in this series is the introduction of CONFIG_KVM_VMWARE which is, in general, a nice cleanup for various bits of VMware compatibility code that have been scattered around KVM. (first patch)
The rest of the series builds upon the VMware platform to implement features that are needed to run VMware guests without any modifications on top of KVM: - ability to turn on the VMware backdoor at runtime on a per-vm basis (used to be a kernel boot argument only) - support for VMware hypercalls - VMware products have a huge collection of hypercalls, all of which are handled in userspace, - support for handling legacy VMware backdoor in L0 in nested configs - in cases where we have WS running a Windows VBS guest, the L0 would be KVM, L1 Hyper-V so by default VMware Tools backdoor calls endup in Hyper-V which can not handle them, so introduce a cap to let L0 handle those.
The final change in the series is a kselftest of the VMware hypercall functionality.
Cc: Paolo Bonzini pbonzini@redhat.com Cc: Jonathan Corbet corbet@lwn.net Cc: Sean Christopherson seanjc@google.com Cc: Thomas Gleixner tglx@linutronix.de Cc: Ingo Molnar mingo@redhat.com Cc: Borislav Petkov bp@alien8.de Cc: Dave Hansen dave.hansen@linux.intel.com Cc: x86@kernel.org Cc: "H. Peter Anvin" hpa@zytor.com Cc: Zack Rusin zack.rusin@broadcom.com Cc: Doug Covelli doug.covelli@broadcom.com Cc: Shuah Khan shuah@kernel.org Cc: Namhyung Kim namhyung@kernel.org Cc: Arnaldo Carvalho de Melo acme@redhat.com Cc: Michael Ellerman mpe@ellerman.id.au Cc: Joel Stanley joel@jms.id.au Cc: Isaku Yamahata isaku.yamahata@intel.com Cc: kvm@vger.kernel.org Cc: linux-doc@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: linux-kselftest@vger.kernel.org
Zack Rusin (5): KVM: x86: Centralize KVM's VMware code KVM: x86: Allow enabling of the vmware backdoor via a cap KVM: x86: Add support for VMware guest specific hypercalls KVM: x86: Add support for legacy VMware backdoors in nested setups KVM: selftests: x86: Add a test for KVM_CAP_X86_VMWARE_HYPERCALL
Documentation/virt/kvm/api.rst | 86 +++++++- MAINTAINERS | 9 + arch/x86/include/asm/kvm_host.h | 13 ++ arch/x86/kvm/Kconfig | 16 ++ arch/x86/kvm/Makefile | 1 + arch/x86/kvm/emulate.c | 11 +- arch/x86/kvm/kvm_vmware.c | 85 ++++++++ arch/x86/kvm/kvm_vmware.h | 189 ++++++++++++++++++ arch/x86/kvm/pmu.c | 39 +--- arch/x86/kvm/pmu.h | 4 - arch/x86/kvm/svm/nested.c | 6 + arch/x86/kvm/svm/svm.c | 10 +- arch/x86/kvm/vmx/nested.c | 6 + arch/x86/kvm/vmx/vmx.c | 5 +- arch/x86/kvm/x86.c | 74 +++---- arch/x86/kvm/x86.h | 2 - include/uapi/linux/kvm.h | 27 +++ tools/include/uapi/linux/kvm.h | 3 + tools/testing/selftests/kvm/Makefile.kvm | 1 + .../selftests/kvm/x86/vmware_hypercall_test.c | 121 +++++++++++ 20 files changed, 614 insertions(+), 94 deletions(-) create mode 100644 arch/x86/kvm/kvm_vmware.c create mode 100644 arch/x86/kvm/kvm_vmware.h create mode 100644 tools/testing/selftests/kvm/x86/vmware_hypercall_test.c
Add a testcase to exercise KVM_CAP_X86_VMWARE_HYPERCALL and validate that KVM exits to userspace on hypercalls and registers are correctly preserved.
Signed-off-by: Zack Rusin zack.rusin@broadcom.com Cc: Doug Covelli doug.covelli@broadcom.com Cc: Paolo Bonzini pbonzini@redhat.com Cc: Shuah Khan shuah@kernel.org Cc: Sean Christopherson seanjc@google.com Cc: Namhyung Kim namhyung@kernel.org Cc: Joel Stanley joel@jms.id.au Cc: Zack Rusin zack.rusin@broadcom.com Cc: Isaku Yamahata isaku.yamahata@intel.com Cc: Arnaldo Carvalho de Melo acme@redhat.com Cc: linux-kernel@vger.kernel.org Cc: kvm@vger.kernel.org Cc: linux-kselftest@vger.kernel.org --- tools/include/uapi/linux/kvm.h | 3 + tools/testing/selftests/kvm/Makefile.kvm | 1 + .../selftests/kvm/x86/vmware_hypercall_test.c | 121 ++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 tools/testing/selftests/kvm/x86/vmware_hypercall_test.c
diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h index 502ea63b5d2e..3b3ad1827245 100644 --- a/tools/include/uapi/linux/kvm.h +++ b/tools/include/uapi/linux/kvm.h @@ -933,6 +933,9 @@ struct kvm_enable_cap { #define KVM_CAP_PRE_FAULT_MEMORY 236 #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 +#define KVM_CAP_X86_VMWARE_NESTED_BACKDOOR_L0 241
struct kvm_irq_routing_irqchip { __u32 irqchip; diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 4277b983cace..9eea93b330e4 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -90,6 +90,7 @@ TEST_GEN_PROGS_x86 += x86/sync_regs_test TEST_GEN_PROGS_x86 += x86/ucna_injection_test TEST_GEN_PROGS_x86 += x86/userspace_io_test TEST_GEN_PROGS_x86 += x86/userspace_msr_exit_test +TEST_GEN_PROGS_x86 += x86/vmware_hypercall_test TEST_GEN_PROGS_x86 += x86/vmx_apic_access_test TEST_GEN_PROGS_x86 += x86/vmx_close_while_nested_test TEST_GEN_PROGS_x86 += x86/vmx_dirty_log_test diff --git a/tools/testing/selftests/kvm/x86/vmware_hypercall_test.c b/tools/testing/selftests/kvm/x86/vmware_hypercall_test.c new file mode 100644 index 000000000000..8daca272933c --- /dev/null +++ b/tools/testing/selftests/kvm/x86/vmware_hypercall_test.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * vmware_hypercall_test + * + * Copyright (c) 2025 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + * + * Based on: + * xen_vmcall_test.c + * + * Copyright © 2020 Amazon.com, Inc. or its affiliates. + * + * VMware hypercall testing + */ + +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" + +#define ARGVALUE(x) (0xdeadbeef5a5a0000UL + (x)) +#define RETVALUE(x) (0xcafef00dfbfbffffUL + (x)) + +static void guest_code(void) +{ + uint64_t error_code; + uint8_t vector; + + unsigned long rax = ARGVALUE(1); + unsigned long rbx = ARGVALUE(2); + unsigned long rcx = ARGVALUE(3); + unsigned long rdx = ARGVALUE(4); + unsigned long rsi = ARGVALUE(5); + unsigned long rdi = ARGVALUE(6); + register unsigned long rbp __asm__("rbp") = ARGVALUE(7); + + asm volatile(KVM_ASM_SAFE("vmcall") + : "=a"(rax), "=b"(rbx), "=c"(rcx), "=d"(rdx), + "=S"(rsi), "=D"(rdi), + KVM_ASM_SAFE_OUTPUTS(vector, error_code) + : "a"(rax), "b"(rbx), "c"(rcx), "d"(rdx), + "S"(rsi), "D"(rdi), "r"(rbp) + : KVM_ASM_SAFE_CLOBBERS); + GUEST_ASSERT_EQ(rax, RETVALUE(11)); + GUEST_ASSERT_EQ(rbx, RETVALUE(12)); + GUEST_ASSERT_EQ(rcx, RETVALUE(13)); + GUEST_ASSERT_EQ(rdx, RETVALUE(14)); + GUEST_ASSERT_EQ(rsi, RETVALUE(15)); + GUEST_ASSERT_EQ(rdi, RETVALUE(16)); + GUEST_ASSERT_EQ(vector, 12); + GUEST_ASSERT_EQ(error_code, 14); + GUEST_DONE(); +} + +int main(int argc, char *argv[]) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + + if (!kvm_check_cap(KVM_CAP_X86_VMWARE_HYPERCALL)) { + print_skip("KVM_CAP_X86_VMWARE_HYPERCALL not available"); + exit(KSFT_SKIP); + } + + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + + vm_enable_cap(vm, KVM_CAP_X86_VMWARE_HYPERCALL, 1); + + for (;;) { + struct kvm_run *run = vcpu->run; + struct ucall uc; + + vcpu_run(vcpu); + + if (run->exit_reason == KVM_EXIT_VMWARE) { + struct kvm_regs regs; + + TEST_ASSERT_EQ(run->vmware.type, KVM_EXIT_VMWARE_HCALL); + TEST_ASSERT_EQ(run->vmware.hcall.longmode, 1); + TEST_ASSERT_EQ(run->vmware.hcall.cpl, 0); + TEST_ASSERT_EQ(run->vmware.hcall.rax, ARGVALUE(1)); + TEST_ASSERT_EQ(run->vmware.hcall.rbx, ARGVALUE(2)); + TEST_ASSERT_EQ(run->vmware.hcall.rcx, ARGVALUE(3)); + TEST_ASSERT_EQ(run->vmware.hcall.rdx, ARGVALUE(4)); + TEST_ASSERT_EQ(run->vmware.hcall.rsi, ARGVALUE(5)); + TEST_ASSERT_EQ(run->vmware.hcall.rdi, ARGVALUE(6)); + TEST_ASSERT_EQ(run->vmware.hcall.rbp, ARGVALUE(7)); + + run->vmware.hcall.exception.inject = 1; + run->vmware.hcall.exception.vector = 12; + run->vmware.hcall.exception.error_code = 14; + run->vmware.hcall.exception.address = 0; + + run->vmware.hcall.result = RETVALUE(11); + vcpu_regs_get(vcpu, ®s); + regs.rbx = RETVALUE(12); + regs.rcx = RETVALUE(13); + regs.rdx = RETVALUE(14); + regs.rsi = RETVALUE(15); + regs.rdi = RETVALUE(16); + vcpu_regs_set(vcpu, ®s); + continue; + } + + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); + + switch (get_ucall(vcpu, &uc)) { + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + /* NOT REACHED */ + case UCALL_SYNC: + break; + case UCALL_DONE: + goto done; + default: + TEST_FAIL("Unknown ucall 0x%lx.", uc.cmd); + } + } +done: + kvm_vm_free(vm); + return 0; +}
linux-kselftest-mirror@lists.linaro.org