[PATCH v2 14/36] KVM: nVMX: Handle virtual timer vector VMCS field

From: isaku . yamahata

Date: Thu Mar 05 2026 - 12:51:04 EST


From: Isaku Yamahata <isaku.yamahata@xxxxxxxxx>

Support virtual timer vector VMCS field.
Opportunistically add a size check of struct vmcs12.

Signed-off-by: Isaku Yamahata <isaku.yamahata@xxxxxxxxx>
---
Changes:
v1 -> v2:
- update for 5fdf86e7353c ("KVM: nVMX: Disallow access to vmcs12 fields
that aren't supported by "hardware"") to use cpu_has_vmcs12_field().
---
arch/x86/kvm/vmx/nested.c | 6 +++++-
arch/x86/kvm/vmx/vmcs12.c | 5 +++++
arch/x86/kvm/vmx/vmcs12.h | 2 ++
arch/x86/kvm/vmx/vmcs_shadow_fields.h | 1 +
4 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 8bb8734cc690..b7561f8f4565 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -2540,7 +2540,8 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0
if (cpu_has_tertiary_exec_ctrls()) {
u64 ctls = 0;

- /* guest apic timer virtualization will come */
+ if (nested_cpu_has_guest_apic_timer(vmcs12))
+ ctls |= TERTIARY_EXEC_GUEST_APIC_TIMER;

tertiary_exec_controls_set(vmx, ctls);
}
@@ -2734,6 +2735,9 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12)
vmcs_write64(EOI_EXIT_BITMAP3, vmcs12->eoi_exit_bitmap3);
}

+ if (nested_cpu_has_guest_apic_timer(vmcs12))
+ vmcs_write16(GUEST_APIC_TIMER_VECTOR, vmcs12->virtual_timer_vector);
+
/*
* If vmcs12 is configured to save TSC on exit via the auto-store list,
* append the MSR to vmcs02's auto-store list so that KVM effectively
diff --git a/arch/x86/kvm/vmx/vmcs12.c b/arch/x86/kvm/vmx/vmcs12.c
index e2e2a99c8aa9..dac796fc20f2 100644
--- a/arch/x86/kvm/vmx/vmcs12.c
+++ b/arch/x86/kvm/vmx/vmcs12.c
@@ -3,6 +3,8 @@

#include "vmcs12.h"

+static_assert(sizeof(struct vmcs12) <= VMCS12_SIZE);
+
#define VMCS12_OFFSET(x) offsetof(struct vmcs12, x)
#define FIELD(number, name) [ENC_TO_VMCS12_IDX(number)] = VMCS12_OFFSET(name)
#define FIELD64(number, name) \
@@ -22,6 +24,7 @@ static const u16 kvm_supported_vmcs12_field_offsets[] __initconst = {
FIELD(GUEST_TR_SELECTOR, guest_tr_selector),
FIELD(GUEST_INTR_STATUS, guest_intr_status),
FIELD(GUEST_PML_INDEX, guest_pml_index),
+ FIELD(GUEST_APIC_TIMER_VECTOR, virtual_timer_vector),
FIELD(HOST_ES_SELECTOR, host_es_selector),
FIELD(HOST_CS_SELECTOR, host_cs_selector),
FIELD(HOST_SS_SELECTOR, host_ss_selector),
@@ -204,6 +207,8 @@ static __init bool cpu_has_vmcs12_field(unsigned int idx)
case HOST_SSP:
case HOST_INTR_SSP_TABLE:
return cpu_has_load_cet_ctrl();
+ case GUEST_APIC_TIMER_VECTOR:
+ return cpu_has_vmx_apic_timer_virt();

/* KVM always emulates PML and the VMX preemption timer in software. */
case GUEST_PML_INDEX:
diff --git a/arch/x86/kvm/vmx/vmcs12.h b/arch/x86/kvm/vmx/vmcs12.h
index b7d30a2cf23f..7a20d6661da0 100644
--- a/arch/x86/kvm/vmx/vmcs12.h
+++ b/arch/x86/kvm/vmx/vmcs12.h
@@ -191,6 +191,7 @@ struct __packed vmcs12 {
u16 host_gs_selector;
u16 host_tr_selector;
u16 guest_pml_index;
+ u16 virtual_timer_vector;
};

/*
@@ -374,6 +375,7 @@ static inline void vmx_check_vmcs12_offsets(void)
CHECK_OFFSET(host_gs_selector, 992);
CHECK_OFFSET(host_tr_selector, 994);
CHECK_OFFSET(guest_pml_index, 996);
+ CHECK_OFFSET(virtual_timer_vector, 998);
}

extern u16 vmcs12_field_offsets[] __ro_after_init;
diff --git a/arch/x86/kvm/vmx/vmcs_shadow_fields.h b/arch/x86/kvm/vmx/vmcs_shadow_fields.h
index cad128d1657b..db1558d11c4c 100644
--- a/arch/x86/kvm/vmx/vmcs_shadow_fields.h
+++ b/arch/x86/kvm/vmx/vmcs_shadow_fields.h
@@ -34,6 +34,7 @@ BUILD_BUG_ON(1)
/* 16-bits */
SHADOW_FIELD_RW(GUEST_INTR_STATUS, guest_intr_status)
SHADOW_FIELD_RW(GUEST_PML_INDEX, guest_pml_index)
+SHADOW_FIELD_RO(GUEST_APIC_TIMER_VECTOR, virtual_timer_vector)
SHADOW_FIELD_RW(HOST_FS_SELECTOR, host_fs_selector)
SHADOW_FIELD_RW(HOST_GS_SELECTOR, host_gs_selector)

--
2.45.2