From nobody Mon Feb 9 00:02:34 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.10]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5550E3C1995; Tue, 3 Feb 2026 18:17:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.10 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770142675; cv=none; b=ebZVKaITlQGTCRLPWpxLRpYrziqRPRkK8hl5Z2T+KElEj0l3TeFAjUdbrQXVRdTI+KS96qV23ioeURdaNklOlIsZvAu4/aJhCwzv8B6kFxgsHlxjZQEmrvE16QCF7B/aolyaVsy+CFNOLOryjQNOCMgxzsH5bM+96amQqWu7sFc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770142675; c=relaxed/simple; bh=zn2LCIv7iXyBsSK01o7/GmoZjwISSXR6b33kXBbRNZ8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=R+P9yS4YNhRaEayzSGvEpE6zFk9/ZrdMr4gYp+OtV2PDD5QhqFrnqoqVcHEJ2Jx0aqizORfUsbgZFhNXiK6RlzYD/vhX82HTo8XQHlRpZZUCVCHFP8cwFHK1c+mBqcuX9r7Gk5AVCQLoW6qxP69fg0CJINVijrrtXhpGrekrXY4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=YymRuExU; arc=none smtp.client-ip=198.175.65.10 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="YymRuExU" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1770142674; x=1801678674; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=zn2LCIv7iXyBsSK01o7/GmoZjwISSXR6b33kXBbRNZ8=; b=YymRuExUcgZH8bIo9VXZP+NwR+a9Bn1EnoFkNkRionQtkWJf78FH0fFh NbEnmmTpw5UZzckl+Pn5rvTWqBKIh2+RaNO7b+VvgWYDZunInKFOBmbZ1 M3fvHASEys2jNEFPaipaBItaRKZy8LfJ0J/KXKaBVybEsTaZPpMST6r9o Cpas2SEz6yCRAWiOa5HRX2sRzPq3ZK8SCyGXykvmJTFcIDIp7AQtZOtCa MItKrTrDnCMda4RSP8cxMyFx2553s5Dc4n2jn/JRprIDjEm64GJY2/v+I X6M5aNOYvj8Vqs4LpxGzIcY9wWfEkvWmIBJMavjzh9roXVhA+jmVF0tqz g==; X-CSE-ConnectionGUID: X0OPC3S5SJau5Y9H4v5ZIA== X-CSE-MsgGUID: SlICvL42QOOoDoJSUws34A== X-IronPort-AV: E=McAfee;i="6800,10657,11691"; a="88745872" X-IronPort-AV: E=Sophos;i="6.21,271,1763452800"; d="scan'208";a="88745872" Received: from fmviesa006.fm.intel.com ([10.60.135.146]) by orvoesa102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 03 Feb 2026 10:17:48 -0800 X-CSE-ConnectionGUID: 1P8CqZaCQFejvo0ij7GrGg== X-CSE-MsgGUID: nZrZKHDqSEKk5EfMMg255w== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.21,271,1763452800"; d="scan'208";a="209605544" Received: from khuang2-desk.gar.corp.intel.com (HELO localhost) ([10.124.221.188]) by fmviesa006-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 03 Feb 2026 10:17:47 -0800 From: isaku.yamahata@intel.com To: kvm@vger.kernel.org Cc: isaku.yamahata@intel.com, isaku.yamahata@gmail.com, Paolo Bonzini , Sean Christopherson , linux-kernel@vger.kernel.org Subject: [PATCH 28/32] KVM: selftests: Add tests nested state of APIC timer virtualization Date: Tue, 3 Feb 2026 10:17:11 -0800 Message-ID: <1cd17fc2aedaa11b2d104dc28ec7e0884267edd6.1770116051.git.isaku.yamahata@intel.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Isaku Yamahata Test vmread/vmwrite for the following VMCS fields related to apic timer virtualization in vmx_set_nested_state_test. - TERTIARY_EXEC_GUEST_APIC_TIMER bit in TERTIARY_VM_EXEC_CONTROL - VIRTUAL_TIMER_VECTOR_OFFSET - GUEST_DEADLINE_OFFSET - GUEST_DEADLINE_SHADOW_OFFSET Signed-off-by: Isaku Yamahata --- tools/testing/selftests/kvm/include/x86/vmx.h | 4 + .../kvm/x86/vmx_set_nested_state_test.c | 237 ++++++++++++++++++ 2 files changed, 241 insertions(+) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/= selftests/kvm/include/x86/vmx.h index 304892500089..a46346ce92cb 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -173,6 +173,8 @@ enum vmcs_field { TSC_MULTIPLIER_HIGH =3D 0x00002033, TERTIARY_VM_EXEC_CONTROL =3D 0x00002034, TERTIARY_VM_EXEC_CONTROL_HIGH =3D 0x00002035, + GUEST_DEADLINE_VIR =3D 0x0000204e, + GUEST_DEADLINE_VIR_HIGH =3D 0x0000204f, GUEST_PHYSICAL_ADDRESS =3D 0x00002400, GUEST_PHYSICAL_ADDRESS_HIGH =3D 0x00002401, VMCS_LINK_POINTER =3D 0x00002800, @@ -195,6 +197,8 @@ enum vmcs_field { GUEST_PDPTR3_HIGH =3D 0x00002811, GUEST_BNDCFGS =3D 0x00002812, GUEST_BNDCFGS_HIGH =3D 0x00002813, + GUEST_DEADLINE_PHY =3D 0x00002830, + GUEST_DEADLINE_PHY_HIGH =3D 0x00002831, HOST_IA32_PAT =3D 0x00002c00, HOST_IA32_PAT_HIGH =3D 0x00002c01, HOST_IA32_EFER =3D 0x00002c02, diff --git a/tools/testing/selftests/kvm/x86/vmx_set_nested_state_test.c b/= tools/testing/selftests/kvm/x86/vmx_set_nested_state_test.c index cbf6a8ff626e..e4c0e6c49e55 100644 --- a/tools/testing/selftests/kvm/x86/vmx_set_nested_state_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_set_nested_state_test.c @@ -25,6 +25,8 @@ #define VMCS12_REVISION 0x11e57ed0 =20 bool have_evmcs; +bool have_procbased_tertiary_ctls; +bool have_apic_timer_virtualization; =20 void test_nested_state(struct kvm_vcpu *vcpu, struct kvm_nested_state *sta= te) { @@ -85,6 +87,233 @@ void set_default_vmx_state(struct kvm_nested_state *sta= te, int size) set_revision_id_for_vmcs12(state, VMCS12_REVISION); } =20 +/* Those values are taken from arch/x86/kvm/vmx/vmcs12.h */ +#define VMCS_LINK_POINTER_OFFSET 176 +#define TERTIARY_VM_EXEC_CONTROL_OFFSET 336 +#define GUEST_CR0_OFFSET 424 +#define GUEST_CR4_OFFSET 440 +#define HOST_CR0_OFFSET 584 +#define HOST_CR4_OFFSET 600 +#define PIN_BASED_VM_EXEC_CONTROL_OFFSET 744 +#define CPU_BASED_VM_EXEC_CONTROL_OFFSET 748 +#define VM_EXIT_CONTROLS_OFFSET 768 +#define VM_ENTRY_CONTROLS_OFFSET 780 +#define SECONDARY_VM_EXEC_CONTROL_OFFSET 804 +#define HOST_CS_SELECTOR_OFFSET 984 +#define HOST_SS_SELECTOR_OFFSET 986 +#define HOST_TR_SELECTOR_OFFSET 994 +#define VIRTUAL_TIMER_VECTOR_OFFSET 998 +#define GUEST_DEADLINE_OFFSET 1000 +#define GUEST_DEADLINE_SHADOW_OFFSET 1008 + +#define KERNEL_CS 0x8 +#define KERNEL_DS 0x10 +#define KERNEL_TSS 0x18 + +/* vcpu with vmxon=3Dfalse is needed to be able to set VMX MSRs. */ +static void nested_vmxoff(struct kvm_vcpu *vcpu, struct kvm_nested_state *= state, + int state_sz) +{ + set_default_vmx_state(state, state_sz); + state->flags =3D 0; + state->hdr.vmx.vmxon_pa =3D -1ull; + state->hdr.vmx.vmcs12_pa =3D -1ull; + test_nested_state(vcpu, state); +} + +static void get_control(struct kvm_vcpu *vcpu, uint32_t msr_index, + uint32_t *fixed0, uint32_t *fixed1) +{ + uint64_t ctls; + + ctls =3D vcpu_get_msr(vcpu, msr_index); + + *fixed0 =3D ctls & 0xffffffff; + *fixed1 =3D ctls >> 32; +} + +static uint32_t *init_control(struct kvm_vcpu *vcpu, + struct kvm_nested_state *state, + uint32_t msr_index, size_t offset) +{ + uint32_t fixed0, fixed1, *vmcs32; + + get_control(vcpu, msr_index, &fixed0, &fixed1); + vmcs32 =3D (uint32_t *)&state->data.vmx->vmcs12[offset]; + + *vmcs32 =3D fixed0; + *vmcs32 &=3D fixed1; + + return vmcs32; +} + +void set_guest_vmx_state(struct kvm_vcpu *vcpu, struct kvm_nested_state *s= tate, + int size) +{ + unsigned long cr0, cr4; + uint32_t *vmcs32; + uint64_t *vmcs64; + + set_default_vmx_state(state, size); + state->flags |=3D KVM_STATE_NESTED_GUEST_MODE; + + /* control */ + init_control(vcpu, state, MSR_IA32_VMX_TRUE_PINBASED_CTLS, + PIN_BASED_VM_EXEC_CONTROL_OFFSET); + + vmcs32 =3D init_control(vcpu, state, MSR_IA32_VMX_TRUE_PROCBASED_CTLS, + CPU_BASED_VM_EXEC_CONTROL_OFFSET); + *vmcs32 |=3D CPU_BASED_ACTIVATE_SECONDARY_CONTROLS; + *vmcs32 &=3D ~CPU_BASED_ACTIVATE_TERTIARY_CONTROLS; + + init_control(vcpu, state, MSR_IA32_VMX_PROCBASED_CTLS2, + SECONDARY_VM_EXEC_CONTROL_OFFSET); + + vmcs32 =3D init_control(vcpu, state, MSR_IA32_VMX_TRUE_EXIT_CTLS, + VM_EXIT_CONTROLS_OFFSET); + *vmcs32 |=3D VM_EXIT_HOST_ADDR_SPACE_SIZE; + + init_control(vcpu, state, MSR_IA32_VMX_TRUE_ENTRY_CTLS, + VM_ENTRY_CONTROLS_OFFSET); + + vmcs64 =3D (uint64_t *)&state->data.vmx->vmcs12[VMCS_LINK_POINTER_OFFSET]; + *vmcs64 =3D -1ull; + + /* host state */ + cr0 =3D vcpu_get_msr(vcpu, MSR_IA32_VMX_CR0_FIXED0); + cr0 &=3D vcpu_get_msr(vcpu, MSR_IA32_VMX_CR0_FIXED1); + cr0 |=3D X86_CR0_PG; + *(unsigned long*)&state->data.vmx->vmcs12[HOST_CR0_OFFSET] =3D cr0; + + cr4 =3D vcpu_get_msr(vcpu, MSR_IA32_VMX_CR4_FIXED0); + cr4 &=3D vcpu_get_msr(vcpu, MSR_IA32_VMX_CR4_FIXED1); + cr4 |=3DX86_CR4_PAE | X86_CR4_VMXE; + *(unsigned long *)&state->data.vmx->vmcs12[HOST_CR4_OFFSET] =3D cr4; + + *(unsigned long *)&state->data.vmx->vmcs12[HOST_CS_SELECTOR_OFFSET] =3D K= ERNEL_CS; + *(unsigned long *)&state->data.vmx->vmcs12[HOST_TR_SELECTOR_OFFSET] =3D K= ERNEL_TSS; + *(unsigned long *)&state->data.vmx->vmcs12[HOST_SS_SELECTOR_OFFSET] =3D K= ERNEL_DS; + + /* guest state */ + cr0 =3D vcpu_get_msr(vcpu, MSR_IA32_VMX_CR0_FIXED0); + cr0 &=3D vcpu_get_msr(vcpu, MSR_IA32_VMX_CR0_FIXED1); + *(unsigned long*)&state->data.vmx->vmcs12[GUEST_CR0_OFFSET] =3D cr0; + + cr4 =3D vcpu_get_msr(vcpu, MSR_IA32_VMX_CR4_FIXED0); + cr4 &=3D vcpu_get_msr(vcpu, MSR_IA32_VMX_CR4_FIXED1); + *(unsigned long *)&state->data.vmx->vmcs12[GUEST_CR4_OFFSET] =3D cr4; + +} + +static void test_tertiary_ctls(struct kvm_vcpu *vcpu, + struct kvm_nested_state *state, + int state_sz) +{ + union vmx_ctrl_msr msr; + uint16_t *vmcs16; + uint32_t *vmcs32; + uint64_t *vmcs64; + uint64_t ctls; + + nested_vmxoff(vcpu, state, state_sz); + + msr.val =3D vcpu_get_msr(vcpu, MSR_IA32_VMX_TRUE_PROCBASED_CTLS); + msr.clr |=3D CPU_BASED_ACTIVATE_SECONDARY_CONTROLS; + msr.clr |=3D CPU_BASED_ACTIVATE_TERTIARY_CONTROLS; + vcpu_set_msr(vcpu, MSR_IA32_VMX_TRUE_PROCBASED_CTLS, msr.val); + + ctls =3D vcpu_get_msr(vcpu, MSR_IA32_VMX_PROCBASED_CTLS3); + ctls |=3D TERTIARY_EXEC_GUEST_APIC_TIMER; + vcpu_set_msr(vcpu, MSR_IA32_VMX_PROCBASED_CTLS3, ctls); + + set_default_vmx_state(state, state_sz); + test_nested_state(vcpu, state); + + vmcs32 =3D (uint32_t *)&state->data.vmx->vmcs12[PIN_BASED_VM_EXEC_CONTROL= _OFFSET]; + *vmcs32 |=3D PIN_BASED_EXT_INTR_MASK; + + vmcs32 =3D (uint32_t *)&state->data.vmx->vmcs12[CPU_BASED_VM_EXEC_CONTROL= _OFFSET]; + *vmcs32 |=3D CPU_BASED_TPR_SHADOW | CPU_BASED_ACTIVATE_TERTIARY_CONTROLS; + + vmcs32 =3D (uint32_t *)&state->data.vmx->vmcs12[SECONDARY_VM_EXEC_CONTROL= _OFFSET]; + *vmcs32 |=3D SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY; + + vmcs64 =3D (uint64_t *)&state->data.vmx->vmcs12[TERTIARY_VM_EXEC_CONTROL_= OFFSET]; + *vmcs64 |=3D TERTIARY_EXEC_GUEST_APIC_TIMER; + + vmcs16 =3D (uint16_t *)&state->data.vmx->vmcs12[VIRTUAL_TIMER_VECTOR_OFFS= ET]; + *vmcs16 =3D 128; + + vmcs64 =3D (uint64_t *)&state->data.vmx->vmcs12[GUEST_DEADLINE_OFFSET]; + /* random non-zero value */ + *vmcs64 =3D 0xffff; + + vmcs64 =3D (uint64_t *)&state->data.vmx->vmcs12[GUEST_DEADLINE_SHADOW_OFF= SET]; + *vmcs64 =3D 0xffff; + + test_nested_state(vcpu, state); +} + +static void test_tertiary_ctls_disabled(struct kvm_vcpu *vcpu, + struct kvm_nested_state *state, + int state_sz) +{ + union vmx_ctrl_msr msr; + uint16_t *vmcs16; + uint32_t *vmcs32; + uint64_t *vmcs64; + + nested_vmxoff(vcpu, state, state_sz); + + msr.val =3D kvm_get_feature_msr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS); + if (msr.clr & CPU_BASED_ACTIVATE_TERTIARY_CONTROLS) { + msr.val =3D vcpu_get_msr(vcpu, MSR_IA32_VMX_TRUE_PROCBASED_CTLS); + msr.clr &=3D ~CPU_BASED_ACTIVATE_TERTIARY_CONTROLS; + vcpu_set_msr(vcpu, MSR_IA32_VMX_TRUE_PROCBASED_CTLS, msr.val); + vcpu_set_msr(vcpu, MSR_IA32_VMX_PROCBASED_CTLS3, 0); + } + + set_guest_vmx_state(vcpu, state, state_sz); + test_nested_state(vcpu, state); + + set_guest_vmx_state(vcpu, state, state_sz); + vmcs32 =3D (uint32_t *)&state->data.vmx->vmcs12[CPU_BASED_VM_EXEC_CONTROL= _OFFSET]; + *vmcs32 |=3D CPU_BASED_ACTIVATE_TERTIARY_CONTROLS; + test_nested_state_expect_einval(vcpu, state); + + set_guest_vmx_state(vcpu, state, state_sz); + vmcs64 =3D (uint64_t *)&state->data.vmx->vmcs12[TERTIARY_VM_EXEC_CONTROL_= OFFSET]; + *vmcs64 |=3D TERTIARY_EXEC_GUEST_APIC_TIMER; + test_nested_state_expect_einval(vcpu, state); + + set_guest_vmx_state(vcpu, state, state_sz); + vmcs16 =3D (uint16_t *)&state->data.vmx->vmcs12[VIRTUAL_TIMER_VECTOR_OFFS= ET]; + *vmcs16 =3D 128; + test_nested_state_expect_einval(vcpu, state); + + set_guest_vmx_state(vcpu, state, state_sz); + vmcs64 =3D (uint64_t *)&state->data.vmx->vmcs12[GUEST_DEADLINE_OFFSET]; + *vmcs64 =3D 0xffff; + test_nested_state_expect_einval(vcpu, state); + + set_guest_vmx_state(vcpu, state, state_sz); + vmcs64 =3D (uint64_t *)&state->data.vmx->vmcs12[GUEST_DEADLINE_SHADOW_OFF= SET]; + *vmcs64 =3D 0xffff; + test_nested_state_expect_einval(vcpu, state); +} + +static void test_vmx_tertiary_ctls(struct kvm_vcpu *vcpu, + struct kvm_nested_state *state, + int state_sz) +{ + vcpu_set_cpuid_feature(vcpu, X86_FEATURE_VMX); + + if (have_procbased_tertiary_ctls) + test_tertiary_ctls(vcpu, state, state_sz); + + test_tertiary_ctls_disabled(vcpu, state, state_sz); +} + void test_vmx_nested_state(struct kvm_vcpu *vcpu) { /* Add a page for VMCS12. */ @@ -244,6 +473,8 @@ void test_vmx_nested_state(struct kvm_vcpu *vcpu) TEST_ASSERT(state->hdr.vmx.vmxon_pa =3D=3D -1ull, "vmxon_pa must be -1ull= ."); TEST_ASSERT(state->hdr.vmx.vmcs12_pa =3D=3D -1ull, "vmcs_pa must be -1ull= ."); =20 + test_vmx_tertiary_ctls(vcpu, state, state_sz); + free(state); } =20 @@ -254,6 +485,12 @@ int main(int argc, char *argv[]) struct kvm_vcpu *vcpu; =20 have_evmcs =3D kvm_check_cap(KVM_CAP_HYPERV_ENLIGHTENED_VMCS); + have_procbased_tertiary_ctls =3D + (kvm_get_feature_msr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS) >> 32) & + CPU_BASED_ACTIVATE_TERTIARY_CONTROLS; + have_apic_timer_virtualization =3D have_procbased_tertiary_ctls && + (kvm_get_feature_msr(MSR_IA32_VMX_PROCBASED_CTLS3) & + TERTIARY_EXEC_GUEST_APIC_TIMER); =20 TEST_REQUIRE(kvm_has_cap(KVM_CAP_NESTED_STATE)); =20 --=20 2.45.2