[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <18f54797d5a705719a8fb8dfce74fc4cfb5d1e18.1770116051.git.isaku.yamahata@intel.com>
Date: Tue, 3 Feb 2026 10:17:08 -0800
From: isaku.yamahata@...el.com
To: kvm@...r.kernel.org
Cc: isaku.yamahata@...el.com,
isaku.yamahata@...il.com,
Paolo Bonzini <pbonzini@...hat.com>,
Sean Christopherson <seanjc@...gle.com>,
linux-kernel@...r.kernel.org
Subject: [PATCH 25/32] KVM: selftests: Add nVMX support to timer_latency test case
From: Isaku Yamahata <isaku.yamahata@...el.com>
Support nVMX for the timer_latency test case to exercise the nVMX APIC
timer virtualization.
Signed-off-by: Isaku Yamahata <isaku.yamahata@...el.com>
---
tools/testing/selftests/kvm/include/x86/vmx.h | 10 ++
.../testing/selftests/kvm/x86/timer_latency.c | 132 +++++++++++++++++-
2 files changed, 139 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h
index 96e2b4c630a9..304892500089 100644
--- a/tools/testing/selftests/kvm/include/x86/vmx.h
+++ b/tools/testing/selftests/kvm/include/x86/vmx.h
@@ -24,6 +24,7 @@
#define CPU_BASED_RDTSC_EXITING 0x00001000
#define CPU_BASED_CR3_LOAD_EXITING 0x00008000
#define CPU_BASED_CR3_STORE_EXITING 0x00010000
+#define CPU_BASED_ACTIVATE_TERTIARY_CONTROLS 0x00020000
#define CPU_BASED_CR8_LOAD_EXITING 0x00080000
#define CPU_BASED_CR8_STORE_EXITING 0x00100000
#define CPU_BASED_TPR_SHADOW 0x00200000
@@ -63,6 +64,12 @@
#define SECONDARY_ENABLE_XSAV_RESTORE 0x00100000
#define SECONDARY_EXEC_TSC_SCALING 0x02000000
+/*
+ * Definitions of Tertiary Processor-Based VM-Execution Controls.
+ * It's 64 bit unlike primary/secondary processor based vm-execution controls.
+ */
+#define TERTIARY_EXEC_GUEST_APIC_TIMER 0x0000000000000100ULL
+
#define PIN_BASED_EXT_INTR_MASK 0x00000001
#define PIN_BASED_NMI_EXITING 0x00000008
#define PIN_BASED_VIRTUAL_NMIS 0x00000020
@@ -104,6 +111,7 @@
enum vmcs_field {
VIRTUAL_PROCESSOR_ID = 0x00000000,
POSTED_INTR_NV = 0x00000002,
+ GUEST_APIC_TIMER_VECTOR = 0x0000000a,
GUEST_ES_SELECTOR = 0x00000800,
GUEST_CS_SELECTOR = 0x00000802,
GUEST_SS_SELECTOR = 0x00000804,
@@ -163,6 +171,8 @@ enum vmcs_field {
ENCLS_EXITING_BITMAP_HIGH = 0x0000202F,
TSC_MULTIPLIER = 0x00002032,
TSC_MULTIPLIER_HIGH = 0x00002033,
+ TERTIARY_VM_EXEC_CONTROL = 0x00002034,
+ TERTIARY_VM_EXEC_CONTROL_HIGH = 0x00002035,
GUEST_PHYSICAL_ADDRESS = 0x00002400,
GUEST_PHYSICAL_ADDRESS_HIGH = 0x00002401,
VMCS_LINK_POINTER = 0x00002800,
diff --git a/tools/testing/selftests/kvm/x86/timer_latency.c b/tools/testing/selftests/kvm/x86/timer_latency.c
index a87a744330c8..9d96b7d18dd5 100644
--- a/tools/testing/selftests/kvm/x86/timer_latency.c
+++ b/tools/testing/selftests/kvm/x86/timer_latency.c
@@ -15,6 +15,10 @@
#include "kvm_util.h"
#include "processor.h"
#include "apic.h"
+#include "vmx.h"
+
+#define L2_GUEST_STACK_SIZE 256
+static unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
#define LOCAL_TIMER_VECTOR 0xec
@@ -30,6 +34,7 @@ struct options {
bool use_oneshot_timer;
bool use_x2apic;
bool use_poll;
+ bool nested;
uint64_t timer_inc_ns;
uint64_t allowed_timer_latency_ns;
@@ -304,6 +309,65 @@ static void guest_code(void)
GUEST_DONE();
}
+static void l1_guest_code(struct vmx_pages *vmx_pages)
+{
+ union vmx_ctrl_msr ctls_msr, ctls2_msr;
+ uint64_t pin, ctls, ctls2, ctls3;
+
+ GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages));
+ GUEST_ASSERT(load_vmcs(vmx_pages));
+ prepare_vmcs(vmx_pages, guest_code,
+ &l2_guest_stack[L2_GUEST_STACK_SIZE]);
+
+ /* Check prerequisites */
+ GUEST_ASSERT(!rdmsr_safe(MSR_IA32_VMX_PROCBASED_CTLS, &ctls_msr.val));
+ GUEST_ASSERT(ctls_msr.clr & CPU_BASED_ACTIVATE_SECONDARY_CONTROLS);
+ GUEST_ASSERT(ctls_msr.clr & CPU_BASED_ACTIVATE_TERTIARY_CONTROLS);
+
+ GUEST_ASSERT(!rdmsr_safe(MSR_IA32_VMX_PROCBASED_CTLS2, &ctls2_msr.val));
+ GUEST_ASSERT(ctls2_msr.clr & SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY);
+
+ GUEST_ASSERT(!rdmsr_safe(MSR_IA32_VMX_PROCBASED_CTLS3, &ctls3));
+ GUEST_ASSERT(ctls3 & TERTIARY_EXEC_GUEST_APIC_TIMER);
+
+ /*
+ * SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY requires
+ * PIN_BASED_EXT_INTR_MASK
+ */
+ pin = vmreadz(PIN_BASED_VM_EXEC_CONTROL);
+ pin |= PIN_BASED_EXT_INTR_MASK;
+ GUEST_ASSERT(!vmwrite(PIN_BASED_VM_EXEC_CONTROL, pin));
+
+ ctls = vmreadz(CPU_BASED_VM_EXEC_CONTROL);
+ ctls |= CPU_BASED_USE_MSR_BITMAPS | CPU_BASED_TPR_SHADOW |
+ CPU_BASED_ACTIVATE_SECONDARY_CONTROLS |
+ CPU_BASED_ACTIVATE_TERTIARY_CONTROLS;
+ GUEST_ASSERT(!vmwrite(CPU_BASED_VM_EXEC_CONTROL, ctls));
+
+ /* guest apic timer requires virtual interrutp delivery */
+ ctls2 = vmreadz(SECONDARY_VM_EXEC_CONTROL);
+ ctls2 |= SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE |
+ SECONDARY_EXEC_APIC_REGISTER_VIRT |
+ SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY;
+ vmwrite(SECONDARY_VM_EXEC_CONTROL, ctls2);
+
+ ctls3 = vmreadz(TERTIARY_VM_EXEC_CONTROL);
+ ctls3 |= TERTIARY_EXEC_GUEST_APIC_TIMER;
+ GUEST_ASSERT(!vmwrite(TERTIARY_VM_EXEC_CONTROL, ctls3));
+
+ /*
+ * We don't emulate apic registers(including APIC_LVTT) for simplicity.
+ * Directly set vector for timer interrupt instead.
+ */
+ GUEST_ASSERT(!vmwrite(GUEST_APIC_TIMER_VECTOR, LOCAL_TIMER_VECTOR));
+
+ /* launch L2 */
+ GUEST_ASSERT(!vmlaunch());
+ GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL);
+
+ GUEST_DONE();
+}
+
static void __run_vcpu(struct kvm_vcpu *vcpu)
{
struct ucall uc;
@@ -408,12 +472,40 @@ static void setup_timer_freq(struct kvm_vm *vm,
ns_to_tsc(data, options.allowed_timer_latency_ns);
}
+static void clear_msr_bitmap(struct vmx_pages *vmx, int msr)
+{
+ clear_bit(msr, vmx->msr_hva);
+ clear_bit(msr, vmx->msr_hva + 2048);
+}
+
static void setup(struct kvm_vm **vm__, struct kvm_vcpu **vcpu__)
{
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
- vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+ if (options.nested) {
+ vm_vaddr_t vmx_pages_gva = 0;
+ struct vmx_pages *vmx;
+
+ vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
+
+ vmx = vcpu_alloc_vmx(vm, &vmx_pages_gva);
+ memset(vmx->msr_hva, 0xff, 4096);
+
+ /* Allow nested apic timer virtualization. */
+ clear_msr_bitmap(vmx, MSR_IA32_TSC_DEADLINE);
+
+ /* Rely on x2apic virtualization. */
+ clear_msr_bitmap(vmx, MSR_IA32_APICBASE);
+ clear_msr_bitmap(vmx, APIC_BASE_MSR + (APIC_TDCR >> 4));
+ clear_msr_bitmap(vmx, APIC_BASE_MSR + (APIC_LVTT >> 4));
+ clear_msr_bitmap(vmx, APIC_BASE_MSR + (APIC_SPIV >> 4));
+ clear_msr_bitmap(vmx, APIC_BASE_MSR + (APIC_EOI >> 4));
+
+ vcpu_args_set(vcpu, 1, vmx_pages_gva);
+ } else
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+
vm_install_exception_handler(vm, LOCAL_TIMER_VECTOR,
guest_timer_interrupt_handler);
setup_timer_freq(vm, &shared_data);
@@ -505,6 +597,8 @@ static void help(const char *name)
printf("-P: print result stat\n");
printf("-x: use xAPIC mode\n");
printf("-X: use x2APIC mode (default)\n");
+ printf("-n: Only measure nested VM (L2)\n");
+ printf("-N: Don't measure nested VM (L2)\n");
puts("");
exit(EXIT_SUCCESS);
@@ -514,8 +608,10 @@ int main(int argc, char **argv)
{
int opt;
unsigned int duration = TEST_DURATION_DEFAULT_IN_SEC;
+ bool nested_only = false;
+ bool no_nest = false;
- while ((opt = getopt(argc, argv, "hld:p:a:otxXP")) != -1) {
+ while ((opt = getopt(argc, argv, "hld:p:a:otxXPnN")) != -1) {
switch (opt) {
case 'l':
options.use_poll = true;
@@ -553,6 +649,15 @@ int main(int argc, char **argv)
options.print_result = true;
break;
+ case 'n':
+ nested_only = true;
+ no_nest = false;
+ break;
+ case 'N':
+ nested_only = false;
+ no_nest = true;
+ break;
+
case 'h':
default:
help(argv[0]);
@@ -568,7 +673,28 @@ int main(int argc, char **argv)
if (options.use_x2apic)
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_X2APIC));
- run_test(duration);
+ if (!nested_only) {
+ options.nested = false;
+ run_test(duration);
+ }
+
+ if (!no_nest) {
+ union vmx_ctrl_msr ctls;
+ uint64_t ctls3;
+
+ ctls.val = kvm_get_feature_msr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS);
+ TEST_REQUIRE(ctls.clr & CPU_BASED_ACTIVATE_TERTIARY_CONTROLS);
+
+ ctls3 = kvm_get_feature_msr(MSR_IA32_VMX_PROCBASED_CTLS3);
+ TEST_REQUIRE(ctls3 & TERTIARY_EXEC_GUEST_APIC_TIMER);
+
+ /* L1 doesn't emulate HLT and memory-mapped APIC. */
+ options.use_poll = true;
+ options.use_oneshot_timer = false;
+
+ options.nested = true;
+ run_test(duration);
+ }
return 0;
}
--
2.45.2
Powered by blists - more mailing lists