Confidential VMs(CVMs) need to execute hypercall instruction as per the CPU type. Normally KVM emulates the vmcall/vmmcall instruction by patching the guest code at runtime. Such a guest memory manipulation by KVM is not allowed with CVMs and is also undesirable in general.
This series adds support of executing hypercall as per the host cpu vendor. CPU vendor is queried early during selftest setup and guest setup to be reused later.
Changes in v5: 1) Incorporated suggestions from Sean - * Rename the APIs to have "this_cpu*" prefix to better convey the intent of callers to query cpu vendor of the current cpu * Squash patches together to cache, share cpu vendor type and replace current callers of "this_cpu*" with checking the saved host cpu vendor in a single patch.
Changes in v4: 1) Incoporated suggestions from Sean - * Added APIs to query host cpu type * Shared the host cpu type with guests to avoid querying the cpu type again * Modified kvm_hypercall to execute vmcall/vmmcall according to host cpu type. 2) Dropped the separate API for kvm_hypercall.
v4: https://lore.kernel.org/lkml/20221228192438.2835203-1-vannapurve@google.com/
Vishal Annapurve (3): KVM: selftests: x86: Use "this_cpu" prefix for cpu vendor queries KVM: selftests: x86: Cache host CPU vendor (AMD vs. Intel) KVM: selftests: x86: Use host's native hypercall instruction in kvm_hypercall()
.../selftests/kvm/include/x86_64/processor.h | 28 +++++++++-- .../selftests/kvm/lib/x86_64/processor.c | 46 ++++++++----------- .../selftests/kvm/x86_64/fix_hypercall_test.c | 4 +- .../selftests/kvm/x86_64/mmio_warning_test.c | 2 +- .../kvm/x86_64/pmu_event_filter_test.c | 4 +- .../vmx_exception_with_invalid_guest_state.c | 2 +- 6 files changed, 51 insertions(+), 35 deletions(-)
Replace is_intel/amd_cpu helpers with this_cpu_* helpers to better convey the intent of querying vendor of the current cpu.
Suggested-by: Sean Christopherson seanjc@google.com Reviewed-by: David Matlack dmatlack@google.com Signed-off-by: Vishal Annapurve vannapurve@google.com --- .../selftests/kvm/include/x86_64/processor.h | 25 +++++++++++++++--- .../selftests/kvm/lib/x86_64/processor.c | 26 ++----------------- .../selftests/kvm/x86_64/fix_hypercall_test.c | 4 +-- .../selftests/kvm/x86_64/mmio_warning_test.c | 2 +- .../kvm/x86_64/pmu_event_filter_test.c | 4 +-- .../vmx_exception_with_invalid_guest_state.c | 2 +- 6 files changed, 30 insertions(+), 33 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h index 2a5f47d51388..fdb1af5ca611 100644 --- a/tools/testing/selftests/kvm/include/x86_64/processor.h +++ b/tools/testing/selftests/kvm/include/x86_64/processor.h @@ -555,6 +555,28 @@ static inline uint32_t this_cpu_model(void) return x86_model(this_cpu_fms()); }
+static inline bool this_cpu_vendor_string_is(const char *vendor) +{ + const uint32_t *chunk = (const uint32_t *)vendor; + uint32_t eax, ebx, ecx, edx; + + cpuid(0, &eax, &ebx, &ecx, &edx); + return (ebx == chunk[0] && edx == chunk[1] && ecx == chunk[2]); +} + +static inline bool this_cpu_is_intel(void) +{ + return this_cpu_vendor_string_is("GenuineIntel"); +} + +/* + * Exclude early K5 samples with a vendor string of "AMDisbetter!" + */ +static inline bool this_cpu_is_amd(void) +{ + return this_cpu_vendor_string_is("AuthenticAMD"); +} + static inline uint32_t __this_cpu_has(uint32_t function, uint32_t index, uint8_t reg, uint8_t lo, uint8_t hi) { @@ -691,9 +713,6 @@ static inline void cpu_relax(void) "hlt\n" \ )
-bool is_intel_cpu(void); -bool is_amd_cpu(void); - struct kvm_x86_state *vcpu_save_state(struct kvm_vcpu *vcpu); void vcpu_load_state(struct kvm_vcpu *vcpu, struct kvm_x86_state *state); void kvm_x86_state_cleanup(struct kvm_x86_state *state); diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c index acfa1d01e7df..7d1768543b91 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/processor.c +++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c @@ -113,7 +113,7 @@ static void sregs_dump(FILE *stream, struct kvm_sregs *sregs, uint8_t indent)
bool kvm_is_tdp_enabled(void) { - if (is_intel_cpu()) + if (this_cpu_is_intel()) return get_kvm_intel_param_bool("ept"); else return get_kvm_amd_param_bool("npt"); @@ -1006,28 +1006,6 @@ void kvm_x86_state_cleanup(struct kvm_x86_state *state) free(state); }
-static bool cpu_vendor_string_is(const char *vendor) -{ - const uint32_t *chunk = (const uint32_t *)vendor; - uint32_t eax, ebx, ecx, edx; - - cpuid(0, &eax, &ebx, &ecx, &edx); - return (ebx == chunk[0] && edx == chunk[1] && ecx == chunk[2]); -} - -bool is_intel_cpu(void) -{ - return cpu_vendor_string_is("GenuineIntel"); -} - -/* - * Exclude early K5 samples with a vendor string of "AMDisbetter!" - */ -bool is_amd_cpu(void) -{ - return cpu_vendor_string_is("AuthenticAMD"); -} - void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits) { if (!kvm_cpu_has_p(X86_PROPERTY_MAX_PHY_ADDR)) { @@ -1236,7 +1214,7 @@ unsigned long vm_compute_max_gfn(struct kvm_vm *vm) max_gfn = (1ULL << (vm->pa_bits - vm->page_shift)) - 1;
/* Avoid reserved HyperTransport region on AMD processors. */ - if (!is_amd_cpu()) + if (!this_cpu_is_amd()) return max_gfn;
/* On parts with <40 physical address bits, the area is fully hidden */ diff --git a/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c b/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c index 32f7e09ef67c..5489c9836ec8 100644 --- a/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c +++ b/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c @@ -48,10 +48,10 @@ static void guest_main(void) const uint8_t *other_hypercall_insn; uint64_t ret;
- if (is_intel_cpu()) { + if (this_cpu_is_intel()) { native_hypercall_insn = vmx_vmcall; other_hypercall_insn = svm_vmmcall; - } else if (is_amd_cpu()) { + } else if (this_cpu_is_amd()) { native_hypercall_insn = svm_vmmcall; other_hypercall_insn = vmx_vmcall; } else { diff --git a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c index fb02581953a3..b0a2a0bae0f3 100644 --- a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c +++ b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c @@ -93,7 +93,7 @@ int main(void) { int warnings_before, warnings_after;
- TEST_REQUIRE(is_intel_cpu()); + TEST_REQUIRE(this_cpu_is_intel());
TEST_REQUIRE(!vm_is_unrestricted_guest(NULL));
diff --git a/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c b/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c index 2de98fce7edd..c728822461b2 100644 --- a/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c +++ b/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c @@ -363,7 +363,7 @@ static void test_pmu_config_disable(void (*guest_code)(void)) */ static bool use_intel_pmu(void) { - return is_intel_cpu() && + return this_cpu_is_intel() && kvm_cpu_property(X86_PROPERTY_PMU_VERSION) && kvm_cpu_property(X86_PROPERTY_PMU_NR_GP_COUNTERS) && kvm_pmu_has(X86_PMU_FEATURE_BRANCH_INSNS_RETIRED); @@ -397,7 +397,7 @@ static bool use_amd_pmu(void) uint32_t family = kvm_cpu_family(); uint32_t model = kvm_cpu_model();
- return is_amd_cpu() && + return this_cpu_is_amd() && (is_zen1(family, model) || is_zen2(family, model) || is_zen3(family, model)); diff --git a/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c b/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c index 2641b286b4ed..53e1ef2fc774 100644 --- a/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c +++ b/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c @@ -111,7 +111,7 @@ int main(int argc, char *argv[]) struct kvm_vcpu *vcpu; struct kvm_vm *vm;
- TEST_REQUIRE(is_intel_cpu()); + TEST_REQUIRE(this_cpu_is_intel()); TEST_REQUIRE(!vm_is_unrestricted_guest(NULL));
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
Cache the host CPU vendor for userspace and share it with guest code.
All the current callers of this_cpu* actually care about host cpu so they are updated to check host_cpu_is*.
Suggested-by: Sean Christopherson seanjc@google.com Reviewed-by: David Matlack dmatlack@google.com Signed-off-by: Vishal Annapurve vannapurve@google.com --- .../selftests/kvm/include/x86_64/processor.h | 3 +++ tools/testing/selftests/kvm/lib/x86_64/processor.c | 14 ++++++++++++-- .../selftests/kvm/x86_64/fix_hypercall_test.c | 4 ++-- .../selftests/kvm/x86_64/mmio_warning_test.c | 2 +- .../selftests/kvm/x86_64/pmu_event_filter_test.c | 4 ++-- .../vmx_exception_with_invalid_guest_state.c | 2 +- 6 files changed, 21 insertions(+), 8 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h index fdb1af5ca611..c7885f72132a 100644 --- a/tools/testing/selftests/kvm/include/x86_64/processor.h +++ b/tools/testing/selftests/kvm/include/x86_64/processor.h @@ -19,6 +19,9 @@
#include "../kvm_util.h"
+extern bool host_cpu_is_intel; +extern bool host_cpu_is_amd; + #define NMI_VECTOR 0x02
#define X86_EFLAGS_FIXED (1u << 1) diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c index 7d1768543b91..84915bc7d689 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/processor.c +++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c @@ -19,6 +19,8 @@ #define MAX_NR_CPUID_ENTRIES 100
vm_vaddr_t exception_handlers; +bool host_cpu_is_amd; +bool host_cpu_is_intel;
static void regs_dump(FILE *stream, struct kvm_regs *regs, uint8_t indent) { @@ -113,7 +115,7 @@ static void sregs_dump(FILE *stream, struct kvm_sregs *sregs, uint8_t indent)
bool kvm_is_tdp_enabled(void) { - if (this_cpu_is_intel()) + if (host_cpu_is_intel) return get_kvm_intel_param_bool("ept"); else return get_kvm_amd_param_bool("npt"); @@ -555,6 +557,8 @@ static void vcpu_setup(struct kvm_vm *vm, struct kvm_vcpu *vcpu) void kvm_arch_vm_post_create(struct kvm_vm *vm) { vm_create_irqchip(vm); + sync_global_to_guest(vm, host_cpu_is_intel); + sync_global_to_guest(vm, host_cpu_is_amd); }
struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, @@ -1214,7 +1218,7 @@ unsigned long vm_compute_max_gfn(struct kvm_vm *vm) max_gfn = (1ULL << (vm->pa_bits - vm->page_shift)) - 1;
/* Avoid reserved HyperTransport region on AMD processors. */ - if (!this_cpu_is_amd()) + if (!host_cpu_is_amd) return max_gfn;
/* On parts with <40 physical address bits, the area is fully hidden */ @@ -1254,3 +1258,9 @@ bool vm_is_unrestricted_guest(struct kvm_vm *vm)
return get_kvm_intel_param_bool("unrestricted_guest"); } + +void kvm_selftest_arch_init(void) +{ + host_cpu_is_intel = this_cpu_is_intel(); + host_cpu_is_amd = this_cpu_is_amd(); +} diff --git a/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c b/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c index 5489c9836ec8..0f728f05ea82 100644 --- a/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c +++ b/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c @@ -48,10 +48,10 @@ static void guest_main(void) const uint8_t *other_hypercall_insn; uint64_t ret;
- if (this_cpu_is_intel()) { + if (host_cpu_is_intel) { native_hypercall_insn = vmx_vmcall; other_hypercall_insn = svm_vmmcall; - } else if (this_cpu_is_amd()) { + } else if (host_cpu_is_amd) { native_hypercall_insn = svm_vmmcall; other_hypercall_insn = vmx_vmcall; } else { diff --git a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c index b0a2a0bae0f3..ce1ccc4c1503 100644 --- a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c +++ b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c @@ -93,7 +93,7 @@ int main(void) { int warnings_before, warnings_after;
- TEST_REQUIRE(this_cpu_is_intel()); + TEST_REQUIRE(host_cpu_is_intel);
TEST_REQUIRE(!vm_is_unrestricted_guest(NULL));
diff --git a/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c b/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c index c728822461b2..4dbb454e1760 100644 --- a/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c +++ b/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c @@ -363,7 +363,7 @@ static void test_pmu_config_disable(void (*guest_code)(void)) */ static bool use_intel_pmu(void) { - return this_cpu_is_intel() && + return host_cpu_is_intel && kvm_cpu_property(X86_PROPERTY_PMU_VERSION) && kvm_cpu_property(X86_PROPERTY_PMU_NR_GP_COUNTERS) && kvm_pmu_has(X86_PMU_FEATURE_BRANCH_INSNS_RETIRED); @@ -397,7 +397,7 @@ static bool use_amd_pmu(void) uint32_t family = kvm_cpu_family(); uint32_t model = kvm_cpu_model();
- return this_cpu_is_amd() && + return host_cpu_is_amd && (is_zen1(family, model) || is_zen2(family, model) || is_zen3(family, model)); diff --git a/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c b/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c index 53e1ef2fc774..ccdfa5dc1a4d 100644 --- a/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c +++ b/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c @@ -111,7 +111,7 @@ int main(int argc, char *argv[]) struct kvm_vcpu *vcpu; struct kvm_vm *vm;
- TEST_REQUIRE(this_cpu_is_intel()); + TEST_REQUIRE(host_cpu_is_intel); TEST_REQUIRE(!vm_is_unrestricted_guest(NULL));
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
Use the host CPU's native hypercall instruction, i.e. VMCALL vs. VMMCALL, in kvm_hypercall(), as relying on KVM to patch in the native hypercall on a #UD for the "wrong" hypercall requires KVM_X86_QUIRK_FIX_HYPERCALL_INSN to be enabled and flat out doesn't work if guest memory is encrypted with a private key, e.g. for SEV VMs.
Suggested-by: Sean Christopherson seanjc@google.com Reviewed-by: David Matlack dmatlack@google.com Signed-off-by: Vishal Annapurve vannapurve@google.com --- tools/testing/selftests/kvm/lib/x86_64/processor.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c index 84915bc7d689..ae1e573d94ce 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/processor.c +++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c @@ -1144,9 +1144,15 @@ uint64_t kvm_hypercall(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2, { uint64_t r;
- asm volatile("vmcall" + asm volatile("test %[use_vmmcall], %[use_vmmcall]\n\t" + "jnz 1f\n\t" + "vmcall\n\t" + "jmp 2f\n\t" + "1: vmmcall\n\t" + "2:" : "=a"(r) - : "a"(nr), "b"(a0), "c"(a1), "d"(a2), "S"(a3)); + : "a"(nr), "b"(a0), "c"(a1), "d"(a2), "S"(a3), + [use_vmmcall] "r" (host_cpu_is_amd)); return r; }
On Wed, 11 Jan 2023 00:44:42 +0000, Vishal Annapurve wrote:
Confidential VMs(CVMs) need to execute hypercall instruction as per the CPU type. Normally KVM emulates the vmcall/vmmcall instruction by patching the guest code at runtime. Such a guest memory manipulation by KVM is not allowed with CVMs and is also undesirable in general.
This series adds support of executing hypercall as per the host cpu vendor. CPU vendor is queried early during selftest setup and guest setup to be reused later.
[...]
Applied to kvm-x86 selftests, thanks!
[1/3] KVM: selftests: x86: Use "this_cpu" prefix for cpu vendor queries https://github.com/kvm-x86/linux/commit/093db78b8e6b [2/3] KVM: selftests: x86: Cache host CPU vendor (AMD vs. Intel) https://github.com/kvm-x86/linux/commit/832ffb370183 [3/3] KVM: selftests: x86: Use host's native hypercall instruction in kvm_hypercall() https://github.com/kvm-x86/linux/commit/019dfeb31650
-- https://github.com/kvm-x86/linux/tree/next https://github.com/kvm-x86/linux/tree/fixes
linux-kselftest-mirror@lists.linaro.org