From nobody Wed Oct 8 18:23:28 2025 Received: from mail-pj1-f73.google.com (mail-pj1-f73.google.com [209.85.216.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EFADB16DEB3 for ; Thu, 26 Jun 2025 00:12:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750896757; cv=none; b=EN9o910KyfSICLQa3tyWR3+ZDKfIfelRUoKsQqRx26IlCZoT19O+uedOr0+IHrXj41Un3P8IdCJd/fs/AOjYMHUJ+RZGWzbIQTaO2h6gu3BsbEkJrQJU8++7n5K02dEe1mEvQvgAfwoFLUyOtRUMCetV708eIAp+7Vw2thzn21U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750896757; c=relaxed/simple; bh=S8YepjEcxw5aH+WyUJggwrBoMjNRQccpLKuukAOR0KI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=AyT0JPGuIV8n1OBVa3/0SWC7rVDOjFQdDxO40VyLOkTq7JeTo0/k+AKVnu52y1fzM7zCEO5/LWfiT8U4oosq1GZpcK8NjJL40EPyW7GW8ZPlkkR66QW68JMpm87Kow4mKjdA4/BZ3EAwjmulsuXAA6g9VOPbu8PKcGB1yCesAwI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=ZGYnwZMy; arc=none smtp.client-ip=209.85.216.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="ZGYnwZMy" Received: by mail-pj1-f73.google.com with SMTP id 98e67ed59e1d1-313d346dc8dso363296a91.1 for ; Wed, 25 Jun 2025 17:12:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1750896755; x=1751501555; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:from:to:cc:subject:date:message-id:reply-to; bh=yHMfQhJxDmXXWypElOJUlq66wLvvI9/lh/NpqNUW0Yc=; b=ZGYnwZMy+x+LXIlYgfZsUSN4ZFRFOxzd8gaQegM9wzipZck/jv/fVBb1rx2onw4hKJ QqfpKPGo1S9Q7b3E2D5QXzw+aquCVQNpFGKob6LQ7sRHKEeSFuVWXygqg7IYuWeUJjwo SpY5cVKYUOaygIjkhG835p3DsJIDNiJjmNXrByZfXn0DxnzvY7e6CW94UZPaWSOxMTRX X6mDkEFaBMR/GTv5/nXj3PzepyAoS9dMfeDM8/2BD8hJggpF1If5thRMJWFGbrAhX5LI M5h0qlUax8+EJPW+nBh5iC7okGMb34ytxjmR66xx1nNeWE2tUIACKxxFl8zoLjWcoynF AtOg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750896755; x=1751501555; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=yHMfQhJxDmXXWypElOJUlq66wLvvI9/lh/NpqNUW0Yc=; b=NjZR+DD1dUFl8wIf5uvsnh66ML/sFCc84VccbtTZrqO2ggwDZZdefBg4J8LSrrIE0a 9TrXleGaJY90Ij5Oe2XqSZfSAWC9InF3+To/gtKyNhrCvCfK3WVJRV+glzSCoclUO326 +5MO2PgZnoaHTezb/Cqc1a2xfLuPWDxBL2YZPsKAaZwcbyA2hjQFiJRAPLxdi1/+txL8 MDzal0Ij9xRg8r7OeOe9oHhY/IcSM8Ar0C9ZfDK62LkSBVRb9rbkutMqMISfhYPBLSUO z+aOWgkZ+TloQAgIFzuh3taWsmfx/xotyxCJLJnuiowYBlnrToO5QOTd15I1caTCNhul +n8g== X-Forwarded-Encrypted: i=1; AJvYcCUsZDRG5g+a0RXwiu1VZWIW26ad9pUn7/tvMMW3GMHv8ulgcCE9XyIDQckPv6xsQ+2l5Tp5dSaWvwBqeto=@vger.kernel.org X-Gm-Message-State: AOJu0Yw2RZ3qvZRrYYMO8q/QrJbo1JaRJp2AoJMf0+Eh4/y0yOr/1Zcl ngfYewaYQQG59gHazwhmwf6mESkQEEShhjtY1bIcv43jpvYqYJn7ot9y/khltCq5zf0kHWy4WvZ oFwboIA== X-Google-Smtp-Source: AGHT+IFowPBB2GQmhp5GlgAqt6FlhUMcLhBMaNbsbDieOoJyK6IVevkDm0adw8K1pYlNDs+iSlSqacu2PHU= X-Received: from pjbnd12.prod.google.com ([2002:a17:90b:4ccc:b0:2e0:915d:d594]) (user=seanjc job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:350e:b0:312:f54e:ba28 with SMTP id 98e67ed59e1d1-315f2689fa0mr7190023a91.24.1750896755366; Wed, 25 Jun 2025 17:12:35 -0700 (PDT) Reply-To: Sean Christopherson Date: Wed, 25 Jun 2025 17:12:24 -0700 In-Reply-To: <20250626001225.744268-1-seanjc@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250626001225.744268-1-seanjc@google.com> X-Mailer: git-send-email 2.50.0.727.gbf7dc18ff4-goog Message-ID: <20250626001225.744268-5-seanjc@google.com> Subject: [PATCH v5 4/5] KVM: selftests: Test behavior of KVM_X86_DISABLE_EXITS_APERFMPERF From: Sean Christopherson To: Paolo Bonzini , Sean Christopherson , Marc Zyngier , Oliver Upton Cc: kvm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev, linux-kernel@vger.kernel.org, Jim Mattson Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Jim Mattson For a VCPU thread pinned to a single LPU, verify that interleaved host and guest reads of IA32_[AM]PERF return strictly increasing values when APERFMPERF exiting is disabled. Run the test in both L1 and L2 to verify that KVM passes through the APERF and MPERF MSRs when L1 doesn't want to intercept them (or any MSRs). Signed-off-by: Jim Mattson Link: https://lore.kernel.org/r/20250530185239.2335185-4-jmattson@google.com Co-developed-by: Sean Christopherson Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../selftests/kvm/x86/aperfmperf_test.c | 213 ++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 tools/testing/selftests/kvm/x86/aperfmperf_test.c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selft= ests/kvm/Makefile.kvm index 028456f1aae1..40920445bfbe 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -135,6 +135,7 @@ TEST_GEN_PROGS_x86 +=3D x86/amx_test TEST_GEN_PROGS_x86 +=3D x86/max_vcpuid_cap_test TEST_GEN_PROGS_x86 +=3D x86/triple_fault_event_test TEST_GEN_PROGS_x86 +=3D x86/recalc_apic_map_test +TEST_GEN_PROGS_x86 +=3D x86/aperfmperf_test TEST_GEN_PROGS_x86 +=3D access_tracking_perf_test TEST_GEN_PROGS_x86 +=3D coalesced_io_test TEST_GEN_PROGS_x86 +=3D dirty_log_perf_test diff --git a/tools/testing/selftests/kvm/x86/aperfmperf_test.c b/tools/test= ing/selftests/kvm/x86/aperfmperf_test.c new file mode 100644 index 000000000000..8b15a13df939 --- /dev/null +++ b/tools/testing/selftests/kvm/x86/aperfmperf_test.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Test for KVM_X86_DISABLE_EXITS_APERFMPERF + * + * Copyright (C) 2025, Google LLC. + * + * Test the ability to disable VM-exits for rdmsr of IA32_APERF and + * IA32_MPERF. When these VM-exits are disabled, reads of these MSRs + * return the host's values. + * + * Note: Requires read access to /dev/cpu//msr to read host MSRs. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "kvm_util.h" +#include "processor.h" +#include "svm_util.h" +#include "test_util.h" +#include "vmx.h" + +#define NUM_ITERATIONS 10000 + +static int open_dev_msr(int cpu) +{ + char path[PATH_MAX]; + + snprintf(path, sizeof(path), "/dev/cpu/%d/msr", cpu); + return open_path_or_exit(path, O_RDONLY); +} + +static uint64_t read_dev_msr(int msr_fd, uint32_t msr) +{ + uint64_t data; + ssize_t rc; + + rc =3D pread(msr_fd, &data, sizeof(data), msr); + TEST_ASSERT(rc =3D=3D sizeof(data), "Read of MSR 0x%x failed", msr); + + return data; +} + +static void guest_read_aperf_mperf(void) +{ + int i; + + for (i =3D 0; i < NUM_ITERATIONS; i++) + GUEST_SYNC2(rdmsr(MSR_IA32_APERF), rdmsr(MSR_IA32_MPERF)); +} + +#define L2_GUEST_STACK_SIZE 64 + +static void l2_guest_code(void) +{ + guest_read_aperf_mperf(); + GUEST_DONE(); +} + +static void l1_svm_code(struct svm_test_data *svm) +{ + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + struct vmcb *vmcb =3D svm->vmcb; + + generic_svm_setup(svm, l2_guest_code, &l2_guest_stack[L2_GUEST_STACK_SIZE= ]); + run_guest(vmcb, svm->vmcb_gpa); +} + +static void l1_vmx_code(struct vmx_pages *vmx) +{ + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + + GUEST_ASSERT_EQ(prepare_for_vmx_operation(vmx), true); + GUEST_ASSERT_EQ(load_vmcs(vmx), true); + + prepare_vmcs(vmx, NULL, &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + /* + * Enable MSR bitmaps (the bitmap itself is allocated, zeroed, and set + * in the VMCS by prepare_vmcs()), as MSR exiting mandatory on Intel. + */ + vmwrite(CPU_BASED_VM_EXEC_CONTROL, + vmreadz(CPU_BASED_VM_EXEC_CONTROL) | CPU_BASED_USE_MSR_BITMAPS); + + GUEST_ASSERT(!vmwrite(GUEST_RIP, (u64)l2_guest_code)); + GUEST_ASSERT(!vmlaunch()); +} + +static void guest_code(void *nested_test_data) +{ + guest_read_aperf_mperf(); + + if (this_cpu_has(X86_FEATURE_SVM)) + l1_svm_code(nested_test_data); + else if (this_cpu_has(X86_FEATURE_VMX)) + l1_vmx_code(nested_test_data); + else + GUEST_DONE(); + + TEST_FAIL("L2 should have signaled 'done'"); +} + +static void guest_no_aperfmperf(void) +{ + uint64_t msr_val; + uint8_t vector; + + vector =3D rdmsr_safe(MSR_IA32_APERF, &msr_val); + GUEST_ASSERT(vector =3D=3D GP_VECTOR); + + vector =3D rdmsr_safe(MSR_IA32_APERF, &msr_val); + GUEST_ASSERT(vector =3D=3D GP_VECTOR); + + GUEST_DONE(); +} + +int main(int argc, char *argv[]) +{ + const bool has_nested =3D kvm_cpu_has(X86_FEATURE_SVM) || kvm_cpu_has(X86= _FEATURE_VMX); + uint64_t host_aperf_before, host_mperf_before; + vm_vaddr_t nested_test_data_gva; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + int msr_fd, cpu, i; + + /* Sanity check that APERF/MPERF are unsupported by default. */ + vm =3D vm_create_with_one_vcpu(&vcpu, guest_no_aperfmperf); + vcpu_run(vcpu); + TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE); + kvm_vm_free(vm); + + cpu =3D pin_self_to_any_cpu(); + + msr_fd =3D open_dev_msr(cpu); + + /* + * This test requires a non-standard VM initialization, because + * KVM_ENABLE_CAP cannot be used on a VM file descriptor after + * a VCPU has been created. + */ + vm =3D vm_create(1); + + TEST_REQUIRE(vm_check_cap(vm, KVM_CAP_X86_DISABLE_EXITS) & + KVM_X86_DISABLE_EXITS_APERFMPERF); + + vm_enable_cap(vm, KVM_CAP_X86_DISABLE_EXITS, + KVM_X86_DISABLE_EXITS_APERFMPERF); + + vcpu =3D vm_vcpu_add(vm, 0, guest_code); + + if (!has_nested) + nested_test_data_gva =3D NONCANONICAL; + else if (kvm_cpu_has(X86_FEATURE_SVM)) + vcpu_alloc_svm(vm, &nested_test_data_gva); + else + vcpu_alloc_vmx(vm, &nested_test_data_gva); + + vcpu_args_set(vcpu, 1, nested_test_data_gva); + + host_aperf_before =3D read_dev_msr(msr_fd, MSR_IA32_APERF); + host_mperf_before =3D read_dev_msr(msr_fd, MSR_IA32_MPERF); + + for (i =3D 0; i <=3D NUM_ITERATIONS * (1 + has_nested); i++) { + uint64_t host_aperf_after, host_mperf_after; + uint64_t guest_aperf, guest_mperf; + struct ucall uc; + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); + + switch (get_ucall(vcpu, &uc)) { + case UCALL_DONE: + goto done; + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + case UCALL_SYNC: + guest_aperf =3D uc.args[0]; + guest_mperf =3D uc.args[1]; + + host_aperf_after =3D read_dev_msr(msr_fd, MSR_IA32_APERF); + host_mperf_after =3D read_dev_msr(msr_fd, MSR_IA32_MPERF); + + TEST_ASSERT(host_aperf_before < guest_aperf, + "APERF: host_before (0x%" PRIx64 ") >=3D guest (0x%" PRIx64 ")", + host_aperf_before, guest_aperf); + TEST_ASSERT(guest_aperf < host_aperf_after, + "APERF: guest (0x%" PRIx64 ") >=3D host_after (0x%" PRIx64 ")", + guest_aperf, host_aperf_after); + TEST_ASSERT(host_mperf_before < guest_mperf, + "MPERF: host_before (0x%" PRIx64 ") >=3D guest (0x%" PRIx64 ")", + host_mperf_before, guest_mperf); + TEST_ASSERT(guest_mperf < host_mperf_after, + "MPERF: guest (0x%" PRIx64 ") >=3D host_after (0x%" PRIx64 ")", + guest_mperf, host_mperf_after); + + host_aperf_before =3D host_aperf_after; + host_mperf_before =3D host_mperf_after; + + break; + } + } + TEST_FAIL("Didn't receive UCALL_DONE\n"); +done: + kvm_vm_free(vm); + close(msr_fd); + + return 0; +} --=20 2.50.0.727.gbf7dc18ff4-goog