[PATCH v10 20/28] KVM: nVMX: Enable support for secondary VM exit controls

From: Sohil Mehta

Date: Fri Sep 11 2026 - 17:58:37 EST


From: "Xin Li (Intel)" <xin@xxxxxxxxx>

Add support for secondary VM exit controls in nested VMX to facilitate
future FRED integration.

Signed-off-by: Xin Li (Intel) <xin@xxxxxxxxx>
Signed-off-by: Sohil Mehta <sohil.mehta@xxxxxxxxx>
---
v10:
- Add MSR_IA32_VMX_EXIT_CTLS2 to emulated_msrs_all[] for live
migration. (Chao Gao)
- Reject vmcs12's secondary VM exit controls that KVM doesn't advertise
to L1 in MSR_IA32_VMX_EXIT_CTLS2.
---
arch/x86/kvm/msrs.c | 1 +
arch/x86/kvm/msrs.h | 2 +-
arch/x86/kvm/vmx/capabilities.h | 1 +
arch/x86/kvm/vmx/nested.c | 29 ++++++++++++++++++++++++++++-
arch/x86/kvm/vmx/nested.h | 5 +++++
arch/x86/kvm/vmx/vmcs12.c | 1 +
arch/x86/kvm/vmx/vmcs12.h | 3 ++-
7 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index 9eabb7b02cb5..3260bddb5f52 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -317,6 +317,7 @@ static const u32 emulated_msrs_all[] = {
MSR_IA32_VMX_PROCBASED_CTLS2,
MSR_IA32_VMX_EPT_VPID_CAP,
MSR_IA32_VMX_VMFUNC,
+ MSR_IA32_VMX_EXIT_CTLS2,

MSR_K7_HWCR,
MSR_KVM_POLL_CONTROL,
diff --git a/arch/x86/kvm/msrs.h b/arch/x86/kvm/msrs.h
index 7cc182a15b3b..845d2cbe80e5 100644
--- a/arch/x86/kvm/msrs.h
+++ b/arch/x86/kvm/msrs.h
@@ -31,7 +31,7 @@ static inline void kvm_pr_unimpl_rdmsr(struct kvm_vcpu *vcpu, u32 msr)
* associated feature that KVM supports for nested virtualization.
*/
#define KVM_FIRST_EMULATED_VMX_MSR MSR_IA32_VMX_BASIC
-#define KVM_LAST_EMULATED_VMX_MSR MSR_IA32_VMX_VMFUNC
+#define KVM_LAST_EMULATED_VMX_MSR MSR_IA32_VMX_EXIT_CTLS2

/*
* KVM's internal, non-ABI indices for synthetic MSRs. The values themselves
diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h
index 95d22a54f856..9cca65b226fd 100644
--- a/arch/x86/kvm/vmx/capabilities.h
+++ b/arch/x86/kvm/vmx/capabilities.h
@@ -36,6 +36,7 @@ struct nested_vmx_msrs {
u32 pinbased_ctls_high;
u32 exit_ctls_low;
u32 exit_ctls_high;
+ u64 secondary_exit_ctls;
u32 entry_ctls_low;
u32 entry_ctls_high;
u32 misc_low;
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 151873407abd..6576935b9d43 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -1596,6 +1596,11 @@ int vmx_set_vmx_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data)
return -EINVAL;
vmx->nested.msrs.vmfunc_controls = data;
return 0;
+ case MSR_IA32_VMX_EXIT_CTLS2:
+ if (data & ~vmcs_config.nested.secondary_exit_ctls)
+ return -EINVAL;
+ vmx->nested.msrs.secondary_exit_ctls = data;
+ return 0;
default:
/*
* The rest of the VMX capability MSRs do not support restore.
@@ -1635,6 +1640,9 @@ int vmx_get_vmx_msr(struct nested_vmx_msrs *msrs, u32 msr_index, u64 *pdata)
if (msr_index == MSR_IA32_VMX_EXIT_CTLS)
*pdata |= VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR;
break;
+ case MSR_IA32_VMX_EXIT_CTLS2:
+ *pdata = msrs->secondary_exit_ctls;
+ break;
case MSR_IA32_VMX_TRUE_ENTRY_CTLS:
case MSR_IA32_VMX_ENTRY_CTLS:
*pdata = vmx_control_msr(
@@ -2576,6 +2584,9 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0
exec_control &= ~VM_EXIT_LOAD_IA32_EFER;
vm_exit_controls_set(vmx, exec_control);

+ if (exec_control & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS)
+ secondary_vm_exit_controls_set(vmx, __secondary_vm_exit_controls_get(vmcs01));
+
/*
* Interrupt/Exception Fields
*/
@@ -3040,6 +3051,11 @@ static int nested_check_vm_exit_controls(struct kvm_vcpu *vcpu,
CC(nested_vmx_check_exit_msr_switch_controls(vcpu, vmcs12)))
return -EINVAL;

+ if (nested_cpu_has_secondary_vm_exit_controls(vmcs12) &&
+ CC(vmcs12->secondary_vm_exit_controls &
+ ~vmx->nested.msrs.secondary_exit_ctls))
+ return -EINVAL;
+
return 0;
}

@@ -7152,7 +7168,8 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf,
VM_EXIT_HOST_ADDR_SPACE_SIZE |
#endif
VM_EXIT_LOAD_IA32_PAT | VM_EXIT_SAVE_IA32_PAT |
- VM_EXIT_CLEAR_BNDCFGS | VM_EXIT_LOAD_CET_STATE;
+ VM_EXIT_CLEAR_BNDCFGS | VM_EXIT_LOAD_CET_STATE |
+ VM_EXIT_ACTIVATE_SECONDARY_CONTROLS;
msrs->exit_ctls_high |=
VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR |
VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_IA32_EFER |
@@ -7165,6 +7182,16 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf,

/* We support free control of debug control saving. */
msrs->exit_ctls_low &= ~VM_EXIT_SAVE_DEBUG_CONTROLS;
+
+ if (msrs->exit_ctls_high & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) {
+ msrs->secondary_exit_ctls = vmcs_conf->vmexit_2nd_ctrl;
+ /*
+ * As the secondary VM exit control is always loaded, do not
+ * advertise any feature in it to nVMX until its nVMX support
+ * is ready.
+ */
+ msrs->secondary_exit_ctls &= 0;
+ }
}

static void nested_vmx_setup_entry_ctls(struct vmcs_config *vmcs_conf,
diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h
index c6de848bd9ce..371fc3498b12 100644
--- a/arch/x86/kvm/vmx/nested.h
+++ b/arch/x86/kvm/vmx/nested.h
@@ -247,6 +247,11 @@ static inline bool nested_cpu_has_save_preemption_timer(struct vmcs12 *vmcs12)
VM_EXIT_SAVE_VMX_PREEMPTION_TIMER;
}

+static inline bool nested_cpu_has_secondary_vm_exit_controls(struct vmcs12 *vmcs12)
+{
+ return vmcs12->vm_exit_controls & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS;
+}
+
static inline bool nested_exit_on_nmi(struct kvm_vcpu *vcpu)
{
return nested_cpu_has_nmi_exiting(get_vmcs12(vcpu));
diff --git a/arch/x86/kvm/vmx/vmcs12.c b/arch/x86/kvm/vmx/vmcs12.c
index 1ebe67c384ad..9d64a89aff00 100644
--- a/arch/x86/kvm/vmx/vmcs12.c
+++ b/arch/x86/kvm/vmx/vmcs12.c
@@ -66,6 +66,7 @@ static const u16 kvm_supported_vmcs12_field_offsets[] __initconst = {
FIELD64(HOST_IA32_PAT, host_ia32_pat),
FIELD64(HOST_IA32_EFER, host_ia32_efer),
FIELD64(HOST_IA32_PERF_GLOBAL_CTRL, host_ia32_perf_global_ctrl),
+ FIELD64(SECONDARY_VM_EXIT_CONTROLS, secondary_vm_exit_controls),
FIELD(PIN_BASED_VM_EXEC_CONTROL, pin_based_vm_exec_control),
FIELD(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control),
FIELD(EXCEPTION_BITMAP, exception_bitmap),
diff --git a/arch/x86/kvm/vmx/vmcs12.h b/arch/x86/kvm/vmx/vmcs12.h
index 21cd1b75e4fd..bb2f406be63d 100644
--- a/arch/x86/kvm/vmx/vmcs12.h
+++ b/arch/x86/kvm/vmx/vmcs12.h
@@ -71,7 +71,7 @@ struct __packed vmcs12 {
u64 pml_address;
u64 encls_exiting_bitmap;
u64 tsc_multiplier;
- u64 padding64[1]; /* room for future expansion */
+ u64 secondary_vm_exit_controls;
/*
* To allow migration of L1 (complete with its L2 guests) between
* machines of different natural widths (32 or 64 bit), we cannot have
@@ -261,6 +261,7 @@ static inline void vmx_check_vmcs12_offsets(void)
CHECK_OFFSET(pml_address, 312);
CHECK_OFFSET(encls_exiting_bitmap, 320);
CHECK_OFFSET(tsc_multiplier, 328);
+ CHECK_OFFSET(secondary_vm_exit_controls, 336);
CHECK_OFFSET(cr0_guest_host_mask, 344);
CHECK_OFFSET(cr4_guest_host_mask, 352);
CHECK_OFFSET(cr0_read_shadow, 360);
--
2.43.0