Linux Documentation
 help / color / mirror / Atom feed
* [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX
@ 2026-09-11 21:36 Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 01/28] KVM: VMX: Enable support for secondary VM exit controls Sohil Mehta
                   ` (27 more replies)
  0 siblings, 28 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

This series enables Intel flexible return and event delivery (FRED) with
KVM VMX to allow guests to utilize FRED.

Change of personnel
-------------------
The patches are being posted after a while. Xin asked me to post the
patches on his behalf but almost all the work in v10 has been done by
him. Many thanks!

I mainly rebased the patches to the latest kvm-x86/next branch and fixed
a handful of issues reported during internal testing and basic AI
reviews. I've avoided making any major code changes or commit message
updates as this is the first iteration that I am posting. 

Going forward, based on the feedback, I'll rework the patches as needed.
I've listed a few opens below that need additional discussion or
clarification. I'm relatively new to the KVM subsystem, so please feel
free to provide suggestions on the series structure, code organization
as well as commit log clarity to smoothen the review process.

FRED Background
---------------
The FRED architecture defines simple new transitions that change
privilege level (ring transitions), designed with two goals:

1) Improve overall performance and response time by replacing event
   delivery through the interrupt descriptor table (IDT event delivery)
   and event return by the IRET instruction with lower latency
   transitions.

2) Improve software robustness by ensuring that event delivery
   establishes the full supervisor context and that event return
   establishes the full user context.

FRED event delivery can effect a transition from ring 3 to ring 0, but it
is used also to deliver events incident to ring 0. One FRED instruction
(ERETU) effects a return from ring 0 to ring 3, while the other (ERETS)
returns while remaining in ring 0.

Intel VMX is extended to run FRED guests, and the major changes are:

1) New VMCS fields for FRED context management: two new event data VMCS
   fields, eight new guest FRED context VMCS fields and eight new host
   FRED context VMCS fields.

2) VMX nested-exception support for proper virtualization of the stack
   levels introduced with the FRED architecture.

For details refer the latest Intel FRED specification which was released
in June 2025 as version 9.0.

Patch structure
---------------
Patches   1-6: Enable the VMX controls and export the host stacks
Patches  7-19: Virtualize the FRED context and advertise it to userspace
Patches 20-25: Extend FRED virtualization to nested VMX
Patches 26-28: Some basic selftests

These patches are also available at:
  https://github.com/sohilmeh/linux-dev/tree/fred-kvm-v10

Main changes in v10
-------------------
 - New patches:
    Emulator CR4 write mask (patch 15)
    CR4.FRED handling on RSM (patch 17)
    Three selftests (patches 26-28)
 - Folded the FRED MSRs into a struct with read/write helpers, and filter all
   nine of them in the nested MSR bitmap instead of just RSP0
 - Improved event data handling including save/restore of the event data
   via KVM_{GET,SET}_VCPU_EVENTS
 - Restructured the nested interruption-information checks as a switch on
   interruption type, and reject bit 13 for non-hardware exceptions
 - Cleared FRED in svm_set_cpu_caps()
 - Reject vmcs12 secondary VM-exit controls KVM doesn't advertise to L1
 - Added MSR_IA32_VMX_EXIT_CTLS2 to the emulated MSR list
 - Updated the author field from Xin's Intel email address to the new one.
 - Removed the old Tested-by tags as the base KVM code and FRED patches
   have changed significantly.

The per-patch changes are listed under each patch.

Testing
-------
Internal testing covered the following on real hardware:
 - Basic guest boot and nested boot to verify FRED was exposed correctly
 - KVM and x86 selftests on L0 and L1 guest
 - KVM unit tests including the FRED ones listed below
 - Guest live migration with FRED

The included selftests in this series are the ones I received from Xin.
There are additional selftests that I am putting together from multiple
sources. However, those are not ready for upstream review yet. The
in-progress selftests are located at:
https://github.com/sohilmeh/linux-dev/commits/fred-kvm-next

Xin posted a set of FRED KVM unit tests for review at:
https://lore.kernel.org/all/20260702065039.3434909-1-xin@zytor.com/ 

There are a couple of known issues worth mentioning: 
 - The x86 selftest sysret_rip asserts failure on FRED hosts as well as
   guest. The issue has been reported upstream [1] but AFAIU hasn't been
   resolved yet.

 - The int1_fred_test KVM selftest from David mentioned below asserts
   failure with the wrong event type with FRED.

Intel Panther Lake systems with FRED support are now available off the
shelf. Clearwater Forest and Diamond Rapids are some of the other Intel
platforms that are expected to have FRED support.

Opens
-----
INT1 event type: David Woodhouse shared a selftest which shows [2] KVM
re-injects an intercepted ICEBP (INT1) #DB as a hardware exception, so a
FRED guest reads event type 3 instead of 5. IIUC, the issue is not FRED
specific but it becomes visible due to FRED. Is that understanding
correct? Also, I am unsure of where the fix should reside or if a
generic fix is already in the works. Any insights here would be helpful.

uAPI: There was some resistance about adding exception event data to the
uAPI [3], suggesting KVM reuse the exception payload and skip updating
register state on re-injection. Xin said that he tried deriving event
data from (vector, payload), but the solution was a bit messy. As
exception_is_nested is needed either way, adding event data to the uAPI
seemed cleaner. Please let me know if the other approach is still
preferable.

Nested virtualization: Both CET and FRED can potentially leave L1
running with L2's state after a failed nested VM-entry. Zhao initiated a
thread for CET [4] which is under active discussion. This series
implements a solution that might need to be revised based on whatever
gets settled there.

Hardware fidelity: I am unsure of the level of fidelity KVM tries to
maintain with respect to the hardware. There are places (such as patch
24, nested_vmx_check_guest_state()) where KVM lets the hardware check
some state instead of performing the checks itself, and cases where it
may fail differently for unusual vmcs12 configurations. I would
appreciate some extra eyes on that.

AMD compatibility: The SVM enabling [5] builds on the same common
plumbing but I haven't had a chance to look at the series yet. Shivansh,
please let me know if there are any incompatibilities. I assume most of
the opens apply to SVM as well so resolutions would help both vendors.

Series split-up: With additional selftests, the number of patches in
this series would exceed 30. Would it be preferable to split the
selftests in to a separate series for easier review? I am inclined to
keep the nested support together to let sashiko review the patches. But,
please let me know if it would be preferable to split that out as well.

Links
-----
[1]: https://lore.kernel.org/lkml/20260326094423.711724-1-yi1.lai@intel.com/
[2]: https://lore.kernel.org/lkml/7f93eb25874ddd13a1ad6e3c75785f11041c8b7f.camel@infradead.org/
[3]: https://lore.kernel.org/all/aahg_PgO5mwjArZ6@google.com/
[4]: https://lore.kernel.org/all/20260904023105.1167376-1-zhao1.liu@intel.com/
[5]: https://lore.kernel.org/all/20260402184240.1939480-1-shivansh.dhiman@amd.com/

Previous versions
-----------------
v9: https://lore.kernel.org/lkml/20251026201911.505204-1-xin@zytor.com/
v8: https://lore.kernel.org/lkml/20251014010950.1568389-1-xin@zytor.com/
v7: https://lore.kernel.org/lkml/20250829153149.2871901-1-xin@zytor.com/
v6: https://lore.kernel.org/lkml/20250821223630.984383-1-xin@zytor.com/
v5: https://lore.kernel.org/lkml/20250723175341.1284463-1-xin@zytor.com/
v4: https://lore.kernel.org/lkml/20250328171205.2029296-1-xin@zytor.com/
v3: https://lore.kernel.org/lkml/20241001050110.3643764-1-xin@zytor.com/
v2: https://lore.kernel.org/lkml/20240207172646.3981-1-xin3.li@intel.com/
v1: https://lore.kernel.org/lkml/20231108183003.5981-1-xin3.li@intel.com/


Sohil Mehta (1):
  KVM: x86: Handle CR4.FRED when emulating RSM

Xin Li (4):
  KVM: x86: Add a new save/restore flag for FRED metadata
  KVM: x86: Include CR4.FRED in the emulator CR4 write mask
  KVM: nVMX: Restrict event data VMCS fields to FRED-supported hosts
  KVM: selftests: Add FRED MSRs to msrs_test

Xin Li (Intel) (23):
  KVM: VMX: Enable support for secondary VM exit controls
  KVM: VMX: Initialize VM entry/exit FRED controls in vmcs_config
  KVM: VMX: Disable FRED if FRED consistency checks fail
  x86/cea: Prefix event stack names with ESTACK_
  x86/cea: Use array indexing to simplify exception stack access
  x86/fred: Export this_cpu_fred_rsp() for KVM usage
  KVM: VMX: Initialize VMCS FRED fields
  KVM: VMX: Set FRED MSR intercepts
  KVM: VMX: Save/restore guest FRED RSP0
  KVM: VMX: Add support for saving and restoring FRED MSRs
  KVM: x86: Add a helper to detect if FRED is enabled for a vCPU
  KVM: VMX: Virtualize FRED nested exception tracking
  KVM: VMX: Virtualize FRED event_data
  KVM: x86: Mark CR4.FRED as not reserved
  KVM: VMX: Dump FRED context in dump_vmcs()
  KVM: x86: Advertise support for FRED
  KVM: nVMX: Enable support for secondary VM exit controls
  KVM: nVMX: Handle FRED VMCS fields in nested VMX context
  KVM: nVMX: Shadow ORIGINAL_EVENT_DATA and INJECTED_EVENT_DATA fields
  KVM: nVMX: Validate FRED-related VMCS fields
  KVM: nVMX: Enable VMX FRED controls
  KVM: selftests: Add a new VM guest mode to run user level code
  KVM: selftests: Add fred exception tests

 Documentation/virt/kvm/api.rst                |  22 ++
 arch/x86/coco/sev/noinstr.c                   |   4 +-
 arch/x86/coco/sev/vc-handle.c                 |   2 +-
 arch/x86/include/asm/cpu_entry_area.h         |  70 ++--
 arch/x86/include/asm/fred.h                   |  10 +
 arch/x86/include/asm/kvm_host.h               |  11 +
 arch/x86/include/asm/msr-index.h              |   3 +
 arch/x86/include/asm/vmx.h                    |  48 ++-
 arch/x86/include/uapi/asm/kvm.h               |   7 +-
 arch/x86/kernel/cpu/common.c                  |  10 +-
 arch/x86/kernel/dumpstack_64.c                |  18 +-
 arch/x86/kernel/fred.c                        |  24 +-
 arch/x86/kernel/traps.c                       |   2 +-
 arch/x86/kvm/cpuid.c                          |   1 +
 arch/x86/kvm/msrs.c                           |  90 +++++-
 arch/x86/kvm/msrs.h                           |   2 +-
 arch/x86/kvm/regs.c                           |   7 +
 arch/x86/kvm/regs.h                           |  21 +-
 arch/x86/kvm/smm.c                            |  18 +-
 arch/x86/kvm/svm/svm.c                        |   5 +-
 arch/x86/kvm/vmx/capabilities.h               |  20 +-
 arch/x86/kvm/vmx/nested.c                     | 253 +++++++++++++--
 arch/x86/kvm/vmx/nested.h                     |  27 ++
 arch/x86/kvm/vmx/vmcs.h                       |  12 +
 arch/x86/kvm/vmx/vmcs12.c                     |  22 ++
 arch/x86/kvm/vmx/vmcs12.h                     |  42 ++-
 arch/x86/kvm/vmx/vmcs_shadow_fields.h         |   4 +
 arch/x86/kvm/vmx/vmx.c                        | 302 +++++++++++++++++-
 arch/x86/kvm/vmx/vmx.h                        |  36 ++-
 arch/x86/kvm/x86.c                            |  69 +++-
 arch/x86/kvm/x86.h                            |   5 +-
 arch/x86/mm/cpu_entry_area.c                  |  34 +-
 arch/x86/mm/fault.c                           |   2 +-
 include/uapi/linux/kvm.h                      |   1 +
 tools/arch/x86/include/asm/msr-index.h        |   2 +
 tools/testing/selftests/kvm/Makefile.kvm      |   1 +
 .../testing/selftests/kvm/include/kvm_util.h  |   1 +
 .../selftests/kvm/include/x86/processor.h     |  34 ++
 tools/testing/selftests/kvm/lib/kvm_util.c    |   5 +-
 .../testing/selftests/kvm/lib/x86/processor.c |  16 +-
 tools/testing/selftests/kvm/x86/fred_test.c   | 291 +++++++++++++++++
 tools/testing/selftests/kvm/x86/msrs_test.c   |  52 ++-
 42 files changed, 1471 insertions(+), 135 deletions(-)
 create mode 100644 tools/testing/selftests/kvm/x86/fred_test.c


base-commit: 76671054f9a1ff6abb976583cd8da37650acdc97
-- 
2.43.0


^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH v10 01/28] KVM: VMX: Enable support for secondary VM exit controls
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 02/28] KVM: VMX: Initialize VM entry/exit FRED controls in vmcs_config Sohil Mehta
                   ` (26 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Introduce infrastructure to support secondary VM exit controls.

Always load the controls when supported by hardware, though all control
bits remain clear in this patch.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - No change
---
 arch/x86/include/asm/msr-index.h |  1 +
 arch/x86/include/asm/vmx.h       |  3 +++
 arch/x86/kvm/vmx/capabilities.h  |  9 ++++++++-
 arch/x86/kvm/vmx/vmcs.h          |  1 +
 arch/x86/kvm/vmx/vmx.c           | 29 +++++++++++++++++++++++++++--
 arch/x86/kvm/vmx/vmx.h           |  7 ++++++-
 6 files changed, 46 insertions(+), 4 deletions(-)

diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 18c4be75e927..ad899d9070e1 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -1274,6 +1274,7 @@
 #define MSR_IA32_VMX_TRUE_ENTRY_CTLS     0x00000490
 #define MSR_IA32_VMX_VMFUNC             0x00000491
 #define MSR_IA32_VMX_PROCBASED_CTLS3	0x00000492
+#define MSR_IA32_VMX_EXIT_CTLS2		0x00000493
 
 #define MSR_IA32_MCU_STAGING_MBOX_ADDR	0x000007a5
 
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index 3f1b3096ff04..1f7cffc118bf 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -119,6 +119,7 @@ struct vmcs {
 #define VM_EXIT_CLEAR_IA32_RTIT_CTL		0x02000000
 #define VM_EXIT_LOAD_CET_STATE                  0x10000000
 #define VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL	0x40000000
+#define VM_EXIT_ACTIVATE_SECONDARY_CONTROLS	0x80000000
 
 #define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR	0x00036dff
 
@@ -275,6 +276,8 @@ enum vmcs_field {
 	SHARED_EPT_POINTER		= 0x0000203C,
 	PID_POINTER_TABLE		= 0x00002042,
 	PID_POINTER_TABLE_HIGH		= 0x00002043,
+	SECONDARY_VM_EXIT_CONTROLS	= 0x00002044,
+	SECONDARY_VM_EXIT_CONTROLS_HIGH	= 0x00002045,
 	GUEST_PHYSICAL_ADDRESS          = 0x00002400,
 	GUEST_PHYSICAL_ADDRESS_HIGH     = 0x00002401,
 	VMCS_LINK_POINTER               = 0x00002800,
diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h
index 810119167f79..6be818ce27bf 100644
--- a/arch/x86/kvm/vmx/capabilities.h
+++ b/arch/x86/kvm/vmx/capabilities.h
@@ -57,8 +57,9 @@ struct vmcs_config {
 	u32 cpu_based_exec_ctrl;
 	u32 cpu_based_2nd_exec_ctrl;
 	u64 cpu_based_3rd_exec_ctrl;
-	u32 vmexit_ctrl;
 	u32 vmentry_ctrl;
+	u32 vmexit_ctrl;
+	u64 vmexit_2nd_ctrl;
 	u64 misc;
 	struct nested_vmx_msrs nested;
 };
@@ -149,6 +150,12 @@ static inline bool cpu_has_tertiary_exec_ctrls(void)
 		CPU_BASED_ACTIVATE_TERTIARY_CONTROLS;
 }
 
+static inline bool cpu_has_secondary_vmexit_ctrls(void)
+{
+	return vmcs_config.vmexit_ctrl &
+		VM_EXIT_ACTIVATE_SECONDARY_CONTROLS;
+}
+
 static inline bool cpu_has_vmx_virtualize_apic_accesses(void)
 {
 	return vmcs_config.cpu_based_2nd_exec_ctrl &
diff --git a/arch/x86/kvm/vmx/vmcs.h b/arch/x86/kvm/vmx/vmcs.h
index 1f16ddeae9cb..ca341c59bab1 100644
--- a/arch/x86/kvm/vmx/vmcs.h
+++ b/arch/x86/kvm/vmx/vmcs.h
@@ -45,6 +45,7 @@ struct vmcs_host_state {
 struct vmcs_controls_shadow {
 	u32 vm_entry;
 	u32 vm_exit;
+	u64 secondary_vm_exit;
 	u32 pin;
 	u32 exec;
 	u32 secondary_exec;
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 504630f0eb40..b4aec4218b7d 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -2751,8 +2751,9 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf,
 	u32 _cpu_based_exec_control = 0;
 	u32 _cpu_based_2nd_exec_control = 0;
 	u64 _cpu_based_3rd_exec_control = 0;
-	u32 _vmexit_control = 0;
 	u32 _vmentry_control = 0;
+	u32 _vmexit_control = 0;
+	u64 _vmexit2_control = 0;
 	u64 basic_msr;
 	u64 misc_msr;
 
@@ -2773,6 +2774,12 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf,
 		{ VM_ENTRY_LOAD_CET_STATE,		VM_EXIT_LOAD_CET_STATE },
 	};
 
+	struct {
+		u32 entry_control;
+		u64 exit_control;
+	} const vmcs_entry_exit2_pairs[] = {
+	};
+
 	memset(vmcs_conf, 0, sizeof(*vmcs_conf));
 
 	if (adjust_vmx_controls(KVM_REQUIRED_VMX_CPU_BASED_VM_EXEC_CONTROL,
@@ -2870,10 +2877,19 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf,
 				&_vmentry_control))
 		return -EIO;
 
+	if (_vmexit_control & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS)
+		_vmexit2_control =
+			adjust_vmx_controls64(KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS,
+					      MSR_IA32_VMX_EXIT_CTLS2);
+
 	if (vmx_check_entry_exit_pairs(vmcs_entry_exit_pairs,
 				       _vmentry_control, _vmexit_control))
 		return -EIO;
 
+	if (vmx_check_entry_exit_pairs(vmcs_entry_exit2_pairs,
+				       _vmentry_control, _vmexit2_control))
+		return -EIO;
+
 	/*
 	 * Some cpus support VM_{ENTRY,EXIT}_IA32_PERF_GLOBAL_CTRL but they
 	 * can't be used due to an errata where VM Exit may incorrectly clear
@@ -2922,8 +2938,9 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf,
 	vmcs_conf->cpu_based_exec_ctrl = _cpu_based_exec_control;
 	vmcs_conf->cpu_based_2nd_exec_ctrl = _cpu_based_2nd_exec_control;
 	vmcs_conf->cpu_based_3rd_exec_ctrl = _cpu_based_3rd_exec_control;
-	vmcs_conf->vmexit_ctrl         = _vmexit_control;
 	vmcs_conf->vmentry_ctrl        = _vmentry_control;
+	vmcs_conf->vmexit_ctrl         = _vmexit_control;
+	vmcs_conf->vmexit_2nd_ctrl     = _vmexit2_control;
 	vmcs_conf->misc	= misc_msr;
 
 #if IS_ENABLED(CONFIG_HYPERV)
@@ -4617,6 +4634,11 @@ static u32 vmx_get_initial_vmexit_ctrl(void)
 		  VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL);
 }
 
+static u64 vmx_secondary_vmexit_ctrl(void)
+{
+	return vmcs_config.vmexit_2nd_ctrl;
+}
+
 void vmx_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu)
 {
 	struct vcpu_vmx *vmx = to_vmx(vcpu);
@@ -4960,6 +4982,9 @@ static void init_vmcs(struct vcpu_vmx *vmx)
 
 	vm_exit_controls_set(vmx, vmx_get_initial_vmexit_ctrl());
 
+	if (cpu_has_secondary_vmexit_ctrls())
+		secondary_vm_exit_controls_set(vmx, vmx_secondary_vmexit_ctrl());
+
 	/* 22.2.1, 20.8.1 */
 	vm_entry_controls_set(vmx, vmx_get_initial_vmentry_ctrl());
 
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index dc8517f15bc4..6295f9302cd9 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -501,7 +501,11 @@ static inline u8 vmx_get_rvi(void)
 	       VM_EXIT_PT_CONCEAL_PIP |					\
 	       VM_EXIT_CLEAR_IA32_RTIT_CTL |				\
 	       VM_EXIT_LOAD_CET_STATE |					\
-	       VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL)
+	       VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL |			\
+	       VM_EXIT_ACTIVATE_SECONDARY_CONTROLS)
+
+#define KVM_REQUIRED_VMX_SECONDARY_VM_EXIT_CONTROLS (0)
+#define KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS (0)
 
 #define KVM_REQUIRED_VMX_PIN_BASED_VM_EXEC_CONTROL			\
 	(PIN_BASED_EXT_INTR_MASK |					\
@@ -615,6 +619,7 @@ static __always_inline void lname##_controls_changebit(struct vcpu_vmx *vmx, u##
 }
 BUILD_CONTROLS_SHADOW(vm_entry, VM_ENTRY_CONTROLS, 32)
 BUILD_CONTROLS_SHADOW(vm_exit, VM_EXIT_CONTROLS, 32)
+BUILD_CONTROLS_SHADOW(secondary_vm_exit, SECONDARY_VM_EXIT_CONTROLS, 64)
 BUILD_CONTROLS_SHADOW(pin, PIN_BASED_VM_EXEC_CONTROL, 32)
 BUILD_CONTROLS_SHADOW(exec, CPU_BASED_VM_EXEC_CONTROL, 32)
 BUILD_CONTROLS_SHADOW(secondary_exec, SECONDARY_VM_EXEC_CONTROL, 32)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 02/28] KVM: VMX: Initialize VM entry/exit FRED controls in vmcs_config
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 01/28] KVM: VMX: Enable support for secondary VM exit controls Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 03/28] KVM: VMX: Disable FRED if FRED consistency checks fail Sohil Mehta
                   ` (25 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Setup VM entry/exit FRED controls in the global vmcs_config for proper
FRED VMCS fields management:
  1) load guest FRED state upon VM entry.
  2) save guest FRED state during VM exit.
  3) load host FRED state during VM exit.

Also add FRED control consistency checks to the existing VM entry/exit
consistency check framework.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
Reviewed-by: Chao Gao <chao.gao@intel.com>
Reviewed-by: Binbin Wu <binbin.wu@linux.intel.com>
---
v10:
 - No change
---
 arch/x86/include/asm/vmx.h | 4 ++++
 arch/x86/kvm/vmx/vmx.c     | 2 ++
 arch/x86/kvm/vmx/vmx.h     | 7 +++++--
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index 1f7cffc118bf..682f09933612 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -121,6 +121,9 @@ struct vmcs {
 #define VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL	0x40000000
 #define VM_EXIT_ACTIVATE_SECONDARY_CONTROLS	0x80000000
 
+#define SECONDARY_VM_EXIT_SAVE_IA32_FRED	BIT_ULL(0)
+#define SECONDARY_VM_EXIT_LOAD_IA32_FRED	BIT_ULL(1)
+
 #define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR	0x00036dff
 
 #define VM_ENTRY_LOAD_DEBUG_CONTROLS            0x00000004
@@ -134,6 +137,7 @@ struct vmcs {
 #define VM_ENTRY_PT_CONCEAL_PIP			0x00020000
 #define VM_ENTRY_LOAD_IA32_RTIT_CTL		0x00040000
 #define VM_ENTRY_LOAD_CET_STATE                 0x00100000
+#define VM_ENTRY_LOAD_IA32_FRED			0x00800000
 
 #define VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR	0x000011ff
 
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index b4aec4218b7d..062631e2dd37 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -2778,6 +2778,8 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf,
 		u32 entry_control;
 		u64 exit_control;
 	} const vmcs_entry_exit2_pairs[] = {
+		{ VM_ENTRY_LOAD_IA32_FRED,
+			SECONDARY_VM_EXIT_SAVE_IA32_FRED | SECONDARY_VM_EXIT_LOAD_IA32_FRED },
 	};
 
 	memset(vmcs_conf, 0, sizeof(*vmcs_conf));
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index 6295f9302cd9..7a22e1b1a273 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -477,7 +477,8 @@ static inline u8 vmx_get_rvi(void)
 	 VM_ENTRY_LOAD_BNDCFGS |					\
 	 VM_ENTRY_PT_CONCEAL_PIP |					\
 	 VM_ENTRY_LOAD_IA32_RTIT_CTL |					\
-	 VM_ENTRY_LOAD_CET_STATE)
+	 VM_ENTRY_LOAD_CET_STATE |					\
+	 VM_ENTRY_LOAD_IA32_FRED)
 
 #define __KVM_REQUIRED_VMX_VM_EXIT_CONTROLS				\
 	(VM_EXIT_SAVE_DEBUG_CONTROLS |					\
@@ -505,7 +506,9 @@ static inline u8 vmx_get_rvi(void)
 	       VM_EXIT_ACTIVATE_SECONDARY_CONTROLS)
 
 #define KVM_REQUIRED_VMX_SECONDARY_VM_EXIT_CONTROLS (0)
-#define KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS (0)
+#define KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS			\
+	     (SECONDARY_VM_EXIT_SAVE_IA32_FRED |			\
+	      SECONDARY_VM_EXIT_LOAD_IA32_FRED)
 
 #define KVM_REQUIRED_VMX_PIN_BASED_VM_EXEC_CONTROL			\
 	(PIN_BASED_EXT_INTR_MASK |					\
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 03/28] KVM: VMX: Disable FRED if FRED consistency checks fail
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 01/28] KVM: VMX: Enable support for secondary VM exit controls Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 02/28] KVM: VMX: Initialize VM entry/exit FRED controls in vmcs_config Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 04/28] x86/cea: Prefix event stack names with ESTACK_ Sohil Mehta
                   ` (24 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Do not virtualize FRED if FRED consistency checks fail.

Either on broken hardware, or when run KVM on top of another hypervisor
before the underlying hypervisor implements nested FRED correctly.

Suggested-by: Chao Gao <chao.gao@intel.com>
Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
Reviewed-by: Chao Gao <chao.gao@intel.com>
---
v10:
 - Remove the comment about why not check FRED VM-exit controls in
   cpu_has_vmx_fred() (Sean).
 - Remove redundant parentheses (Sean).
---
 arch/x86/kvm/vmx/capabilities.h | 5 +++++
 arch/x86/kvm/vmx/vmx.c          | 3 +++
 2 files changed, 8 insertions(+)

diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h
index 6be818ce27bf..95d22a54f856 100644
--- a/arch/x86/kvm/vmx/capabilities.h
+++ b/arch/x86/kvm/vmx/capabilities.h
@@ -409,6 +409,11 @@ static inline bool vmx_pebs_supported(void)
 	       !enable_mediated_pmu;
 }
 
+static inline bool cpu_has_vmx_fred(void)
+{
+	return vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_FRED;
+}
+
 static inline bool cpu_has_notify_vmexit(void)
 {
 	return vmcs_config.cpu_based_2nd_exec_ctrl &
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 062631e2dd37..2a2218e0983b 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -8122,6 +8122,9 @@ static __init void vmx_set_cpu_caps(void)
 		kvm_cpu_cap_check_and_set(X86_FEATURE_DTES64);
 	}
 
+	if (!cpu_has_vmx_fred())
+		kvm_cpu_cap_clear(X86_FEATURE_FRED);
+
 	if (!enable_pmu)
 		kvm_cpu_cap_clear(X86_FEATURE_PDCM);
 	kvm_caps.supported_perf_cap = vmx_get_perf_capabilities();
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 04/28] x86/cea: Prefix event stack names with ESTACK_
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (2 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 03/28] KVM: VMX: Disable FRED if FRED consistency checks fail Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 05/28] x86/cea: Use array indexing to simplify exception stack access Sohil Mehta
                   ` (23 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Add the ESTACK_ prefix to event stack names to improve clarity and
readability.  Without the prefix, names like DF, NMI, and DB are too
brief and potentially ambiguous.

This renaming also prepares for converting __this_cpu_ist_top_va from
a macro into a function that accepts an enum exception_stack_ordering
argument, without requiring changes to existing callsites.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
Acked-by: Dave Hansen <dave.hansen@linux.intel.com>
---
v10:
 - No change
---
 arch/x86/coco/sev/noinstr.c           |  4 ++--
 arch/x86/coco/sev/vc-handle.c         |  2 +-
 arch/x86/include/asm/cpu_entry_area.h | 26 +++++++++++++-------------
 arch/x86/kernel/cpu/common.c          | 10 +++++-----
 arch/x86/kernel/dumpstack_64.c        | 14 +++++++-------
 arch/x86/kernel/fred.c                |  6 +++---
 arch/x86/kernel/traps.c               |  2 +-
 arch/x86/mm/cpu_entry_area.c          | 12 ++++++------
 arch/x86/mm/fault.c                   |  2 +-
 9 files changed, 39 insertions(+), 39 deletions(-)

diff --git a/arch/x86/coco/sev/noinstr.c b/arch/x86/coco/sev/noinstr.c
index e1e03f12fc7b..8a695ab67821 100644
--- a/arch/x86/coco/sev/noinstr.c
+++ b/arch/x86/coco/sev/noinstr.c
@@ -31,7 +31,7 @@ static __always_inline bool on_vc_stack(struct pt_regs *regs)
 	if (ip_within_syscall_gap(regs))
 		return false;
 
-	return ((sp >= __this_cpu_ist_bottom_va(VC)) && (sp < __this_cpu_ist_top_va(VC)));
+	return ((sp >= __this_cpu_ist_bottom_va(ESTACK_VC)) && (sp < __this_cpu_ist_top_va(ESTACK_VC)));
 }
 
 /*
@@ -83,7 +83,7 @@ void noinstr __sev_es_ist_exit(void)
 	/* Read IST entry */
 	ist = __this_cpu_read(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC]);
 
-	if (WARN_ON(ist == __this_cpu_ist_top_va(VC)))
+	if (WARN_ON(ist == __this_cpu_ist_top_va(ESTACK_VC)))
 		return;
 
 	/* Read back old IST entry and write it to the TSS */
diff --git a/arch/x86/coco/sev/vc-handle.c b/arch/x86/coco/sev/vc-handle.c
index 96b62b49b2b5..134c14b489ef 100644
--- a/arch/x86/coco/sev/vc-handle.c
+++ b/arch/x86/coco/sev/vc-handle.c
@@ -871,7 +871,7 @@ static enum es_result vc_handle_exitcode(struct es_em_ctxt *ctxt,
 
 static __always_inline bool is_vc2_stack(unsigned long sp)
 {
-	return (sp >= __this_cpu_ist_bottom_va(VC2) && sp < __this_cpu_ist_top_va(VC2));
+	return (sp >= __this_cpu_ist_bottom_va(ESTACK_VC2) && sp < __this_cpu_ist_top_va(ESTACK_VC2));
 }
 
 static __always_inline bool vc_from_invalid_context(struct pt_regs *regs)
diff --git a/arch/x86/include/asm/cpu_entry_area.h b/arch/x86/include/asm/cpu_entry_area.h
index 462fc34f1317..d0f884c28178 100644
--- a/arch/x86/include/asm/cpu_entry_area.h
+++ b/arch/x86/include/asm/cpu_entry_area.h
@@ -18,19 +18,19 @@
 
 /* Macro to enforce the same ordering and stack sizes */
 #define ESTACKS_MEMBERS(guardsize, optional_stack_size)		\
-	char	DF_stack_guard[guardsize];			\
-	char	DF_stack[EXCEPTION_STKSZ];			\
-	char	NMI_stack_guard[guardsize];			\
-	char	NMI_stack[EXCEPTION_STKSZ];			\
-	char	DB_stack_guard[guardsize];			\
-	char	DB_stack[EXCEPTION_STKSZ];			\
-	char	MCE_stack_guard[guardsize];			\
-	char	MCE_stack[EXCEPTION_STKSZ];			\
-	char	VC_stack_guard[guardsize];			\
-	char	VC_stack[optional_stack_size];			\
-	char	VC2_stack_guard[guardsize];			\
-	char	VC2_stack[optional_stack_size];			\
-	char	IST_top_guard[guardsize];			\
+	char	ESTACK_DF_stack_guard[guardsize];		\
+	char	ESTACK_DF_stack[EXCEPTION_STKSZ];		\
+	char	ESTACK_NMI_stack_guard[guardsize];		\
+	char	ESTACK_NMI_stack[EXCEPTION_STKSZ];		\
+	char	ESTACK_DB_stack_guard[guardsize];		\
+	char	ESTACK_DB_stack[EXCEPTION_STKSZ];		\
+	char	ESTACK_MCE_stack_guard[guardsize];		\
+	char	ESTACK_MCE_stack[EXCEPTION_STKSZ];		\
+	char	ESTACK_VC_stack_guard[guardsize];		\
+	char	ESTACK_VC_stack[optional_stack_size];		\
+	char	ESTACK_VC2_stack_guard[guardsize];		\
+	char	ESTACK_VC2_stack[optional_stack_size];		\
+	char	ESTACK_IST_top_guard[guardsize];		\
 
 /* The exception stacks' physical storage. No guard pages required */
 struct exception_stacks {
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index a3df21d26460..99ec284fb829 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -2372,12 +2372,12 @@ static inline void setup_getcpu(int cpu)
 static inline void tss_setup_ist(struct tss_struct *tss)
 {
 	/* Set up the per-CPU TSS IST stacks */
-	tss->x86_tss.ist[IST_INDEX_DF] = __this_cpu_ist_top_va(DF);
-	tss->x86_tss.ist[IST_INDEX_NMI] = __this_cpu_ist_top_va(NMI);
-	tss->x86_tss.ist[IST_INDEX_DB] = __this_cpu_ist_top_va(DB);
-	tss->x86_tss.ist[IST_INDEX_MCE] = __this_cpu_ist_top_va(MCE);
+	tss->x86_tss.ist[IST_INDEX_DF]	= __this_cpu_ist_top_va(ESTACK_DF);
+	tss->x86_tss.ist[IST_INDEX_NMI]	= __this_cpu_ist_top_va(ESTACK_NMI);
+	tss->x86_tss.ist[IST_INDEX_DB]	= __this_cpu_ist_top_va(ESTACK_DB);
+	tss->x86_tss.ist[IST_INDEX_MCE]	= __this_cpu_ist_top_va(ESTACK_MCE);
 	/* Only mapped when SEV-ES is active */
-	tss->x86_tss.ist[IST_INDEX_VC] = __this_cpu_ist_top_va(VC);
+	tss->x86_tss.ist[IST_INDEX_VC]	= __this_cpu_ist_top_va(ESTACK_VC);
 }
 #else /* CONFIG_X86_64 */
 static inline void tss_setup_ist(struct tss_struct *tss) { }
diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c
index 6c5defd6569a..40f51e278171 100644
--- a/arch/x86/kernel/dumpstack_64.c
+++ b/arch/x86/kernel/dumpstack_64.c
@@ -73,7 +73,7 @@ struct estack_pages {
 	 PFN_DOWN(CEA_ESTACK_OFFS(st) + CEA_ESTACK_SIZE(st) - 1)] = {	\
 		.offs	= CEA_ESTACK_OFFS(st),				\
 		.size	= CEA_ESTACK_SIZE(st),				\
-		.type	= STACK_TYPE_EXCEPTION + ESTACK_ ##st, }
+		.type	= STACK_TYPE_EXCEPTION + st, }
 
 /*
  * Array of exception stack page descriptors. If the stack is larger than
@@ -83,12 +83,12 @@ struct estack_pages {
  */
 static const
 struct estack_pages estack_pages[CEA_ESTACK_PAGES] ____cacheline_aligned = {
-	EPAGERANGE(DF),
-	EPAGERANGE(NMI),
-	EPAGERANGE(DB),
-	EPAGERANGE(MCE),
-	EPAGERANGE(VC),
-	EPAGERANGE(VC2),
+	EPAGERANGE(ESTACK_DF),
+	EPAGERANGE(ESTACK_NMI),
+	EPAGERANGE(ESTACK_DB),
+	EPAGERANGE(ESTACK_MCE),
+	EPAGERANGE(ESTACK_VC),
+	EPAGERANGE(ESTACK_VC2),
 };
 
 static __always_inline bool in_exception_stack(unsigned long *stack, struct stack_info *info)
diff --git a/arch/x86/kernel/fred.c b/arch/x86/kernel/fred.c
index 117aa06d25ca..1c452a389a70 100644
--- a/arch/x86/kernel/fred.c
+++ b/arch/x86/kernel/fred.c
@@ -84,7 +84,7 @@ void cpu_init_fred_rsps(void)
 	       FRED_STKLVL(X86_TRAP_DF,  FRED_DF_STACK_LEVEL));
 
 	/* The FRED equivalents to IST stacks... */
-	wrmsrq(MSR_IA32_FRED_RSP1, __this_cpu_ist_top_va(DB));
-	wrmsrq(MSR_IA32_FRED_RSP2, __this_cpu_ist_top_va(NMI));
-	wrmsrq(MSR_IA32_FRED_RSP3, __this_cpu_ist_top_va(DF));
+	wrmsrq(MSR_IA32_FRED_RSP1, __this_cpu_ist_top_va(ESTACK_DB));
+	wrmsrq(MSR_IA32_FRED_RSP2, __this_cpu_ist_top_va(ESTACK_NMI));
+	wrmsrq(MSR_IA32_FRED_RSP3, __this_cpu_ist_top_va(ESTACK_DF));
 }
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 30aa8369957e..8afd607a5873 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -1075,7 +1075,7 @@ asmlinkage __visible noinstr struct pt_regs *vc_switch_off_ist(struct pt_regs *r
 
 	if (!get_stack_info_noinstr(stack, current, &info) || info.type == STACK_TYPE_ENTRY ||
 	    info.type > STACK_TYPE_EXCEPTION_LAST)
-		sp = __this_cpu_ist_top_va(VC2);
+		sp = __this_cpu_ist_top_va(ESTACK_VC2);
 
 sync:
 	/*
diff --git a/arch/x86/mm/cpu_entry_area.c b/arch/x86/mm/cpu_entry_area.c
index 575f863f3c75..9fa371af8abc 100644
--- a/arch/x86/mm/cpu_entry_area.c
+++ b/arch/x86/mm/cpu_entry_area.c
@@ -151,15 +151,15 @@ static void __init percpu_setup_exception_stacks(unsigned int cpu)
 	 * by guard pages so each stack must be mapped separately. DB2 is
 	 * not mapped; it just exists to catch triple nesting of #DB.
 	 */
-	cea_map_stack(DF);
-	cea_map_stack(NMI);
-	cea_map_stack(DB);
-	cea_map_stack(MCE);
+	cea_map_stack(ESTACK_DF);
+	cea_map_stack(ESTACK_NMI);
+	cea_map_stack(ESTACK_DB);
+	cea_map_stack(ESTACK_MCE);
 
 	if (IS_ENABLED(CONFIG_AMD_MEM_ENCRYPT)) {
 		if (cc_platform_has(CC_ATTR_GUEST_STATE_ENCRYPT)) {
-			cea_map_stack(VC);
-			cea_map_stack(VC2);
+			cea_map_stack(ESTACK_VC);
+			cea_map_stack(ESTACK_VC2);
 		}
 	}
 }
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 45b99c3b1442..f6ee1cc3f125 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -677,7 +677,7 @@ page_fault_oops(struct pt_regs *regs, unsigned long error_code,
 		 * and then double-fault, though, because we're likely to
 		 * break the console driver and lose most of the stack dump.
 		 */
-		call_on_stack(__this_cpu_ist_top_va(DF) - sizeof(void*),
+		call_on_stack(__this_cpu_ist_top_va(ESTACK_DF) - sizeof(void*),
 			      handle_stack_overflow,
 			      ASM_CALL_ARG3,
 			      , [arg1] "r" (regs), [arg2] "r" (address), [arg3] "r" (&info));
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 05/28] x86/cea: Use array indexing to simplify exception stack access
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (3 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 04/28] x86/cea: Prefix event stack names with ESTACK_ Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 06/28] x86/fred: Export this_cpu_fred_rsp() for KVM usage Sohil Mehta
                   ` (22 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Refactor struct cea_exception_stacks to leverage array indexing for
exception stack access, improving code clarity and eliminating the
need for the ESTACKS_MEMBERS() macro.

Convert __this_cpu_ist_{bottom,top}_va() from macros to functions,
allowing removal of the now-obsolete CEA_ESTACK_BOT and CEA_ESTACK_TOP
macros.

Also drop CEA_ESTACK_SIZE, which just duplicated EXCEPTION_STKSZ.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
Acked-by: Dave Hansen <dave.hansen@linux.intel.com>
---
v10:
 - Check if the stack argument is within the array bounds of
   event_stacks (Boris).

v9:
 - Refactor first and then export in a separate patch (Dave Hansen).
---
 arch/x86/include/asm/cpu_entry_area.h | 52 ++++++++++++---------------
 arch/x86/kernel/dumpstack_64.c        |  4 +--
 arch/x86/mm/cpu_entry_area.c          | 22 +++++++++++-
 3 files changed, 45 insertions(+), 33 deletions(-)

diff --git a/arch/x86/include/asm/cpu_entry_area.h b/arch/x86/include/asm/cpu_entry_area.h
index d0f884c28178..509e52fc3a0f 100644
--- a/arch/x86/include/asm/cpu_entry_area.h
+++ b/arch/x86/include/asm/cpu_entry_area.h
@@ -16,6 +16,19 @@
 #define VC_EXCEPTION_STKSZ	0
 #endif
 
+/*
+ * The exception stack ordering in [cea_]exception_stacks
+ */
+enum exception_stack_ordering {
+	ESTACK_DF,
+	ESTACK_NMI,
+	ESTACK_DB,
+	ESTACK_MCE,
+	ESTACK_VC,
+	ESTACK_VC2,
+	N_EXCEPTION_STACKS
+};
+
 /* Macro to enforce the same ordering and stack sizes */
 #define ESTACKS_MEMBERS(guardsize, optional_stack_size)		\
 	char	ESTACK_DF_stack_guard[guardsize];		\
@@ -39,37 +52,22 @@ struct exception_stacks {
 
 /* The effective cpu entry area mapping with guard pages. */
 struct cea_exception_stacks {
-	ESTACKS_MEMBERS(PAGE_SIZE, EXCEPTION_STKSZ)
+	struct {
+		char stack_guard[PAGE_SIZE];
+		char stack[EXCEPTION_STKSZ];
+	} event_stacks[N_EXCEPTION_STACKS];
+	char IST_top_guard[PAGE_SIZE];
 };
 
-/*
- * The exception stack ordering in [cea_]exception_stacks
- */
-enum exception_stack_ordering {
-	ESTACK_DF,
-	ESTACK_NMI,
-	ESTACK_DB,
-	ESTACK_MCE,
-	ESTACK_VC,
-	ESTACK_VC2,
-	N_EXCEPTION_STACKS
-};
-
-#define CEA_ESTACK_SIZE(st)					\
-	sizeof(((struct cea_exception_stacks *)0)->st## _stack)
-
-#define CEA_ESTACK_BOT(ceastp, st)				\
-	((unsigned long)&(ceastp)->st## _stack)
-
-#define CEA_ESTACK_TOP(ceastp, st)				\
-	(CEA_ESTACK_BOT(ceastp, st) + CEA_ESTACK_SIZE(st))
-
 #define CEA_ESTACK_OFFS(st)					\
-	offsetof(struct cea_exception_stacks, st## _stack)
+	offsetof(struct cea_exception_stacks, event_stacks[st].stack)
 
 #define CEA_ESTACK_PAGES					\
 	(sizeof(struct cea_exception_stacks) / PAGE_SIZE)
 
+extern unsigned long __this_cpu_ist_top_va(enum exception_stack_ordering stack);
+extern unsigned long __this_cpu_ist_bottom_va(enum exception_stack_ordering stack);
+
 #endif
 
 #ifdef CONFIG_X86_32
@@ -144,10 +142,4 @@ static __always_inline struct entry_stack *cpu_entry_stack(int cpu)
 	return &get_cpu_entry_area(cpu)->entry_stack_page.stack;
 }
 
-#define __this_cpu_ist_top_va(name)					\
-	CEA_ESTACK_TOP(__this_cpu_read(cea_exception_stacks), name)
-
-#define __this_cpu_ist_bottom_va(name)					\
-	CEA_ESTACK_BOT(__this_cpu_read(cea_exception_stacks), name)
-
 #endif
diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c
index 40f51e278171..93b10b264e53 100644
--- a/arch/x86/kernel/dumpstack_64.c
+++ b/arch/x86/kernel/dumpstack_64.c
@@ -70,9 +70,9 @@ struct estack_pages {
 
 #define EPAGERANGE(st)							\
 	[PFN_DOWN(CEA_ESTACK_OFFS(st)) ...				\
-	 PFN_DOWN(CEA_ESTACK_OFFS(st) + CEA_ESTACK_SIZE(st) - 1)] = {	\
+	 PFN_DOWN(CEA_ESTACK_OFFS(st) + EXCEPTION_STKSZ - 1)] = {	\
 		.offs	= CEA_ESTACK_OFFS(st),				\
-		.size	= CEA_ESTACK_SIZE(st),				\
+		.size	= EXCEPTION_STKSZ,				\
 		.type	= STACK_TYPE_EXCEPTION + st, }
 
 /*
diff --git a/arch/x86/mm/cpu_entry_area.c b/arch/x86/mm/cpu_entry_area.c
index 9fa371af8abc..a47dfd686bd3 100644
--- a/arch/x86/mm/cpu_entry_area.c
+++ b/arch/x86/mm/cpu_entry_area.c
@@ -18,6 +18,26 @@ static DEFINE_PER_CPU_PAGE_ALIGNED(struct entry_stack_page, entry_stack_storage)
 static DEFINE_PER_CPU_PAGE_ALIGNED(struct exception_stacks, exception_stacks);
 DEFINE_PER_CPU(struct cea_exception_stacks*, cea_exception_stacks);
 
+/*
+ * Typically invoked by entry code, so must be noinstr.
+ */
+noinstr unsigned long __this_cpu_ist_bottom_va(enum exception_stack_ordering stack)
+{
+	struct cea_exception_stacks *s;
+
+	BUILD_BUG_ON(ESTACK_DF != 0);
+	BUG_ON(stack >= N_EXCEPTION_STACKS);
+
+	s = __this_cpu_read(cea_exception_stacks);
+
+	return (unsigned long)&s->event_stacks[stack].stack;
+}
+
+noinstr unsigned long __this_cpu_ist_top_va(enum exception_stack_ordering stack)
+{
+	return __this_cpu_ist_bottom_va(stack) + EXCEPTION_STKSZ;
+}
+
 static DEFINE_PER_CPU_READ_MOSTLY(unsigned long, _cea_offset);
 
 static __always_inline unsigned int cea_offset(unsigned int cpu)
@@ -132,7 +152,7 @@ static void __init percpu_setup_debug_store(unsigned int cpu)
 
 #define cea_map_stack(name) do {					\
 	npages = sizeof(estacks->name## _stack) / PAGE_SIZE;		\
-	cea_map_percpu_pages(cea->estacks.name## _stack,		\
+	cea_map_percpu_pages(cea->estacks.event_stacks[name].stack,	\
 			estacks->name## _stack, npages, PAGE_KERNEL);	\
 	} while (0)
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 06/28] x86/fred: Export this_cpu_fred_rsp() for KVM usage
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (4 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 05/28] x86/cea: Use array indexing to simplify exception stack access Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 07/28] KVM: VMX: Initialize VMCS FRED fields Sohil Mehta
                   ` (21 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Introduce and export this_cpu_fred_rsp() to provide KVM with a self-
explanatory interface for retrieving per-CPU FRED regular stacks for
stack levels 1->3.

FRED introduced new fields in the VMCS host-state area for stack levels
1–>3 (HOST_IA32_FRED_RSP[123]), which correspond to the per-CPU FRED
regular stacks for stack levels 1->3.  KVM must populate these fields
each time a vCPU is loaded onto a CPU to ensure a complete valid FRED
event delivery context immediately after any VM-Exits.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - Replace direct, raw use of __this_cpu_ist_top_va() with a self-
   explanatory this_cpu_fred_rsp() helper for better readability (Sean).
---
 arch/x86/include/asm/fred.h | 10 ++++++++++
 arch/x86/kernel/fred.c      | 24 +++++++++++++++++++++---
 2 files changed, 31 insertions(+), 3 deletions(-)

diff --git a/arch/x86/include/asm/fred.h b/arch/x86/include/asm/fred.h
index 18a2f811c358..85b851f16bae 100644
--- a/arch/x86/include/asm/fred.h
+++ b/arch/x86/include/asm/fred.h
@@ -35,6 +35,13 @@
 
 #ifndef __ASSEMBLER__
 
+enum fred_stack_level {
+	FRED_STACK_LEVEL_0,
+	FRED_STACK_LEVEL_1,
+	FRED_STACK_LEVEL_2,
+	FRED_STACK_LEVEL_3
+};
+
 #ifdef CONFIG_X86_FRED
 #include <linux/kernel.h>
 #include <linux/sched/task_stack.h>
@@ -105,6 +112,8 @@ static __always_inline void fred_update_rsp0(void)
 		__this_cpu_write(fred_rsp0, rsp0);
 	}
 }
+
+unsigned long this_cpu_fred_rsp(enum fred_stack_level lvl);
 #else /* CONFIG_X86_FRED */
 static __always_inline unsigned long fred_event_data(struct pt_regs *regs) { return 0; }
 static inline void cpu_init_fred_exceptions(void) { }
@@ -112,6 +121,7 @@ static inline void cpu_init_fred_rsps(void) { }
 static inline void fred_complete_exception_setup(void) { }
 static inline void fred_sync_rsp0(unsigned long rsp0) { }
 static inline void fred_update_rsp0(void) { }
+static inline unsigned long this_cpu_fred_rsp(enum fred_stack_level lvl) { return 0; }
 #endif /* CONFIG_X86_FRED */
 #endif /* !__ASSEMBLER__ */
 
diff --git a/arch/x86/kernel/fred.c b/arch/x86/kernel/fred.c
index 1c452a389a70..68947c53a494 100644
--- a/arch/x86/kernel/fred.c
+++ b/arch/x86/kernel/fred.c
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 #include <linux/kernel.h>
+#include <linux/kvm_types.h>
 
 #include <asm/desc.h>
 #include <asm/fred.h>
@@ -69,6 +70,23 @@ void cpu_init_fred_exceptions(void)
 	setup_clear_cpu_cap(X86_FEATURE_SYSCALL32);
 }
 
+unsigned long this_cpu_fred_rsp(enum fred_stack_level lvl)
+{
+	switch (lvl) {
+	case FRED_STACK_LEVEL_0:
+		return __this_cpu_read(fred_rsp0);
+	case FRED_STACK_LEVEL_1:
+		return __this_cpu_ist_top_va(ESTACK_DB);
+	case FRED_STACK_LEVEL_2:
+		return __this_cpu_ist_top_va(ESTACK_NMI);
+	case FRED_STACK_LEVEL_3:
+		return __this_cpu_ist_top_va(ESTACK_DF);
+	default:
+		BUG();
+	}
+}
+EXPORT_SYMBOL_FOR_KVM(this_cpu_fred_rsp);
+
 /* Must be called after setup_cpu_entry_areas() */
 void cpu_init_fred_rsps(void)
 {
@@ -84,7 +102,7 @@ void cpu_init_fred_rsps(void)
 	       FRED_STKLVL(X86_TRAP_DF,  FRED_DF_STACK_LEVEL));
 
 	/* The FRED equivalents to IST stacks... */
-	wrmsrq(MSR_IA32_FRED_RSP1, __this_cpu_ist_top_va(ESTACK_DB));
-	wrmsrq(MSR_IA32_FRED_RSP2, __this_cpu_ist_top_va(ESTACK_NMI));
-	wrmsrq(MSR_IA32_FRED_RSP3, __this_cpu_ist_top_va(ESTACK_DF));
+	wrmsrq(MSR_IA32_FRED_RSP1, this_cpu_fred_rsp(FRED_STACK_LEVEL_1));
+	wrmsrq(MSR_IA32_FRED_RSP2, this_cpu_fred_rsp(FRED_STACK_LEVEL_2));
+	wrmsrq(MSR_IA32_FRED_RSP3, this_cpu_fred_rsp(FRED_STACK_LEVEL_3));
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 07/28] KVM: VMX: Initialize VMCS FRED fields
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (5 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 06/28] x86/fred: Export this_cpu_fred_rsp() for KVM usage Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 08/28] KVM: VMX: Set FRED MSR intercepts Sohil Mehta
                   ` (20 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Initialize host VMCS FRED fields with host FRED MSRs' value and
guest VMCS FRED fields to 0.

FRED CPU state is managed in 9 new FRED MSRs:
        IA32_FRED_CONFIG,
        IA32_FRED_STKLVLS,
        IA32_FRED_RSP0,
        IA32_FRED_RSP1,
        IA32_FRED_RSP2,
        IA32_FRED_RSP3,
        IA32_FRED_SSP1,
        IA32_FRED_SSP2,
        IA32_FRED_SSP3,
as well as a few existing CPU registers and MSRs:
        CR4.FRED,
        IA32_STAR,
        IA32_KERNEL_GS_BASE,
        IA32_PL0_SSP (also known as IA32_FRED_SSP0).

CR4, IA32_KERNEL_GS_BASE and IA32_STAR are already well managed.
Except IA32_FRED_RSP0 and IA32_FRED_SSP0, all other FRED CPU state
MSRs have corresponding VMCS fields in both the host-state and
guest-state areas.  So KVM just needs to initialize them, and with
proper VM entry/exit FRED controls, a FRED CPU will keep tracking
host and guest FRED CPU state in VMCS automatically.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
Reviewed-by: Chao Gao <chao.gao@intel.com>
Reviewed-by: Binbin Wu <binbin.wu@linux.intel.com>
---
v10:
 - s/rdmsrl/rdmsrq (Chao)
 - Remove a terse comment which doesn't help at all (Sean).
 - Replace direct, raw use of __this_cpu_ist_top_va() with a self-
   explanatory this_cpu_fred_rsp() helper for better readability (Sean).
 - Add cpu_feature_enabled(X86_FEATURE_FRED) to patch out accesses to
   VMCS FRED fields (Sean).
 - Remove "#ifdef CONFIG_X86_64" after switching to this_cpu_fred_rsp()
   (Sean).
---
 arch/x86/include/asm/kvm_host.h |  3 +++
 arch/x86/include/asm/vmx.h      | 32 ++++++++++++++++++++++++++++++++
 arch/x86/kvm/vmx/vmx.c          | 33 +++++++++++++++++++++++++++++++++
 3 files changed, 68 insertions(+)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 683bb8bf43a9..8d3999772ac2 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -294,6 +294,9 @@ struct kvm_host_values {
 	u64 xss;
 	u64 s_cet;
 	u64 arch_capabilities;
+
+	u64 fred_config;
+	u64 fred_stklvls;
 };
 extern struct kvm_host_values kvm_host;
 
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index 682f09933612..4720619f09ac 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -306,12 +306,44 @@ enum vmcs_field {
 	GUEST_BNDCFGS_HIGH              = 0x00002813,
 	GUEST_IA32_RTIT_CTL		= 0x00002814,
 	GUEST_IA32_RTIT_CTL_HIGH	= 0x00002815,
+	GUEST_IA32_FRED_CONFIG		= 0x0000281a,
+	GUEST_IA32_FRED_CONFIG_HIGH	= 0x0000281b,
+	GUEST_IA32_FRED_RSP1		= 0x0000281c,
+	GUEST_IA32_FRED_RSP1_HIGH	= 0x0000281d,
+	GUEST_IA32_FRED_RSP2		= 0x0000281e,
+	GUEST_IA32_FRED_RSP2_HIGH	= 0x0000281f,
+	GUEST_IA32_FRED_RSP3		= 0x00002820,
+	GUEST_IA32_FRED_RSP3_HIGH	= 0x00002821,
+	GUEST_IA32_FRED_STKLVLS		= 0x00002822,
+	GUEST_IA32_FRED_STKLVLS_HIGH	= 0x00002823,
+	GUEST_IA32_FRED_SSP1		= 0x00002824,
+	GUEST_IA32_FRED_SSP1_HIGH	= 0x00002825,
+	GUEST_IA32_FRED_SSP2		= 0x00002826,
+	GUEST_IA32_FRED_SSP2_HIGH	= 0x00002827,
+	GUEST_IA32_FRED_SSP3		= 0x00002828,
+	GUEST_IA32_FRED_SSP3_HIGH	= 0x00002829,
 	HOST_IA32_PAT			= 0x00002c00,
 	HOST_IA32_PAT_HIGH		= 0x00002c01,
 	HOST_IA32_EFER			= 0x00002c02,
 	HOST_IA32_EFER_HIGH		= 0x00002c03,
 	HOST_IA32_PERF_GLOBAL_CTRL	= 0x00002c04,
 	HOST_IA32_PERF_GLOBAL_CTRL_HIGH	= 0x00002c05,
+	HOST_IA32_FRED_CONFIG		= 0x00002c08,
+	HOST_IA32_FRED_CONFIG_HIGH	= 0x00002c09,
+	HOST_IA32_FRED_RSP1		= 0x00002c0a,
+	HOST_IA32_FRED_RSP1_HIGH	= 0x00002c0b,
+	HOST_IA32_FRED_RSP2		= 0x00002c0c,
+	HOST_IA32_FRED_RSP2_HIGH	= 0x00002c0d,
+	HOST_IA32_FRED_RSP3		= 0x00002c0e,
+	HOST_IA32_FRED_RSP3_HIGH	= 0x00002c0f,
+	HOST_IA32_FRED_STKLVLS		= 0x00002c10,
+	HOST_IA32_FRED_STKLVLS_HIGH	= 0x00002c11,
+	HOST_IA32_FRED_SSP1		= 0x00002c12,
+	HOST_IA32_FRED_SSP1_HIGH	= 0x00002c13,
+	HOST_IA32_FRED_SSP2		= 0x00002c14,
+	HOST_IA32_FRED_SSP2_HIGH	= 0x00002c15,
+	HOST_IA32_FRED_SSP3		= 0x00002c16,
+	HOST_IA32_FRED_SSP3_HIGH	= 0x00002c17,
 	PIN_BASED_VM_EXEC_CONTROL       = 0x00004000,
 	CPU_BASED_VM_EXEC_CONTROL       = 0x00004002,
 	EXCEPTION_BITMAP                = 0x00004004,
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 2a2218e0983b..dc80ca5f804d 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -1562,6 +1562,12 @@ void vmx_vcpu_load_vmcs(struct kvm_vcpu *vcpu, int cpu)
 				    (unsigned long)(cpu_entry_stack(cpu) + 1));
 		}
 
+		if (cpu_feature_enabled(X86_FEATURE_FRED) && kvm_cpu_cap_has(X86_FEATURE_FRED)) {
+			vmcs_write64(HOST_IA32_FRED_RSP1, this_cpu_fred_rsp(FRED_STACK_LEVEL_1));
+			vmcs_write64(HOST_IA32_FRED_RSP2, this_cpu_fred_rsp(FRED_STACK_LEVEL_2));
+			vmcs_write64(HOST_IA32_FRED_RSP3, this_cpu_fred_rsp(FRED_STACK_LEVEL_3));
+		}
+
 		vmx->loaded_vmcs->cpu = cpu;
 	}
 }
@@ -4500,6 +4506,17 @@ void vmx_set_constant_host_state(struct vcpu_vmx *vmx)
 	 */
 	vmcs_write16(HOST_DS_SELECTOR, 0);
 	vmcs_write16(HOST_ES_SELECTOR, 0);
+
+	if (cpu_feature_enabled(X86_FEATURE_FRED) && kvm_cpu_cap_has(X86_FEATURE_FRED)) {
+		/* FRED CONFIG and STKLVLS are the same on all CPUs */
+		vmcs_write64(HOST_IA32_FRED_CONFIG, kvm_host.fred_config);
+		vmcs_write64(HOST_IA32_FRED_STKLVLS, kvm_host.fred_stklvls);
+
+		/* Linux doesn't support kernel shadow stacks, thus SSPs are 0s */
+		vmcs_write64(HOST_IA32_FRED_SSP1, 0);
+		vmcs_write64(HOST_IA32_FRED_SSP2, 0);
+		vmcs_write64(HOST_IA32_FRED_SSP3, 0);
+	}
 #else
 	vmcs_write16(HOST_DS_SELECTOR, __KERNEL_DS);  /* 22.2.4 */
 	vmcs_write16(HOST_ES_SELECTOR, __KERNEL_DS);  /* 22.2.4 */
@@ -5030,6 +5047,17 @@ static void init_vmcs(struct vcpu_vmx *vmx)
 	}
 
 	vmx_setup_uret_msrs(vmx);
+
+	if (cpu_feature_enabled(X86_FEATURE_FRED) && kvm_cpu_cap_has(X86_FEATURE_FRED)) {
+		vmcs_write64(GUEST_IA32_FRED_CONFIG, 0);
+		vmcs_write64(GUEST_IA32_FRED_RSP1, 0);
+		vmcs_write64(GUEST_IA32_FRED_RSP2, 0);
+		vmcs_write64(GUEST_IA32_FRED_RSP3, 0);
+		vmcs_write64(GUEST_IA32_FRED_STKLVLS, 0);
+		vmcs_write64(GUEST_IA32_FRED_SSP1, 0);
+		vmcs_write64(GUEST_IA32_FRED_SSP2, 0);
+		vmcs_write64(GUEST_IA32_FRED_SSP3, 0);
+	}
 }
 
 static void __vmx_vcpu_reset(struct kvm_vcpu *vcpu)
@@ -8883,6 +8911,11 @@ __init int vmx_hardware_setup(void)
 
 	kvm_caps.inapplicable_quirks &= ~KVM_X86_QUIRK_IGNORE_GUEST_PAT;
 
+	if (cpu_feature_enabled(X86_FEATURE_FRED) && kvm_cpu_cap_has(X86_FEATURE_FRED)) {
+		rdmsrq(MSR_IA32_FRED_CONFIG, kvm_host.fred_config);
+		rdmsrq(MSR_IA32_FRED_STKLVLS, kvm_host.fred_stklvls);
+	}
+
 	return 0;
 }
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 08/28] KVM: VMX: Set FRED MSR intercepts
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (6 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 07/28] KVM: VMX: Initialize VMCS FRED fields Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 09/28] KVM: VMX: Save/restore guest FRED RSP0 Sohil Mehta
                   ` (19 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

On a userspace MSR filter change, set FRED MSR intercepts.

Because the following eight FRED MSRs,
        MSR_IA32_FRED_RSP[123], MSR_IA32_FRED_STKLVLS,
        MSR_IA32_FRED_SSP[123], MSR_IA32_FRED_CONFIG,
are used by the kernel itself to take an exception at any time, they
should be context-switched by Intel VT-x automatically in order to
preserve the FRED architectural invariant that there should NEVER be
a "gap" during which it is unsafe to take an exception.

KVM leverages Intel VT-x hardware to automatically context switch the
eight FRED MSRs using:

  1) Dedicated host and guest VMCS fields for each MSR.

  2) VM-entry/exit controls to manage the automated loading and saving
     of the eight FRED MSRs.

Consequently, passing these MSRs through to the guest would only add
unnecessary handling code without benefit.

Both MSR_IA32_FRED_RSP0 and MSR_IA32_FRED_SSP0 (aka MSR_IA32_PL0_SSP)
are dedicated for userspace event delivery, IOW they are NOT used in
any kernel event delivery and the execution of ERETS.  Thus KVM can
run safely with guest values in the two MSRs.  As a result, save and
restore of their guest values are deferred until vCPU context switch,
Host MSR_IA32_FRED_RSP0 is restored upon returning to userspace, and
Host MSR_IA32_PL0_SSP is managed with XRSTORS/XSAVES.

MSR_IA32_PL0_SSP (aka MSR_IA32_FRED_SSP0) is part of CET supervisor
state, but all four FRED SSP MSRs are architecturally visible on any
processor that enumerates FRED.  Even if CET is absent, these MSRs
remain accessible via RDMSR/WRMSR, though FRED transitions will not
use them.

Intercept MSR_IA32_PL0_SSP if CET shadow stacks are unsupported (even
with FRED present).  Since this MSR is rarely accessed and ignored by
XSAVES in this configuration, interception avoids the overhead of
manually context switching the hardware MSR during vcpu_load/put.

This behavior is consistent with the current setup in
vmx_recalc_msr_intercepts(), so no change is needed to the interception
logic for MSR_IA32_PL0_SSP.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - Improve the commit message and code comment with an explanation
   from hpa (Chao Gao and Dave Hansen).
---
 arch/x86/kvm/vmx/vmx.c | 67 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index dc80ca5f804d..09bbbc680aa7 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -4304,6 +4304,72 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
 				  MSR_TYPE_RW, intercept);
 }
 
+static void vmx_set_intercept_for_fred_msr(struct kvm_vcpu *vcpu)
+{
+	bool intercept = !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED);
+
+	if (!kvm_cpu_cap_has(X86_FEATURE_FRED))
+		return;
+
+	/*
+	 * Because the following eight FRED MSRs,
+	 * 	MSR_IA32_FRED_RSP[123], MSR_IA32_FRED_STKLVLS,
+	 * 	MSR_IA32_FRED_SSP[123], MSR_IA32_FRED_CONFIG,
+	 * are used by the kernel itself to take an exception at any time, they
+	 * should be context-switched by Intel VT-x automatically in order to
+	 * preserve the FRED architectural invariant that there should NEVER be
+	 * a "gap" during which it is unsafe to take an exception.
+	 *
+	 * KVM leverages Intel VT-x hardware to automatically context switch the
+	 * eight FRED MSRs using:
+	 *
+	 *   1) Dedicated host and guest VMCS fields for each MSR.
+	 *
+	 *   2) VM-entry/exit controls to manage the automated loading and saving
+	 *      of the eight FRED MSRs.
+	 *
+	 * Consequently, passing these MSRs through to the guest would only add
+	 * unnecessary handling code without benefit.
+	 */
+	vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP1, MSR_TYPE_RW, intercept);
+	vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP2, MSR_TYPE_RW, intercept);
+	vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP3, MSR_TYPE_RW, intercept);
+	vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_STKLVLS, MSR_TYPE_RW, intercept);
+	vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_SSP1, MSR_TYPE_RW, intercept);
+	vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_SSP2, MSR_TYPE_RW, intercept);
+	vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_SSP3, MSR_TYPE_RW, intercept);
+	vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_CONFIG, MSR_TYPE_RW, intercept);
+
+	/*
+	 * MSR_IA32_FRED_RSP0 and MSR_IA32_PL0_SSP (aka MSR_IA32_FRED_SSP0) are
+	 * designed for event delivery while executing in userspace.  Since KVM
+	 * operates entirely in kernel mode (CPL is always 0 after any VM exit),
+	 * it can safely retain and operate with guest-defined values for these
+	 * MSRs.
+	 *
+	 * Disabling interception of the two MSRs offers two advantages:
+	 *   1) Simplicity: Eliminates dedicated MSR handling code.
+	 *   2) Performance: Avoids frequent VM-exits since the two MSRs are
+	 *      per user thread variables and frequently accessed.
+	 *
+	 * MSR_IA32_PL0_SSP (aka MSR_IA32_FRED_SSP0) is part of CET supervisor
+	 * state, but all four FRED SSP MSRs are architecturally visible on any
+	 * processor that enumerates FRED.  Even if CET is absent, these MSRs
+	 * remain accessible via RDMSR/WRMSR, though FRED transitions will not
+	 * use them.
+	 *
+	 * Intercept MSR_IA32_PL0_SSP if CET shadow stacks are unsupported (even
+	 * with FRED present).  Since this MSR is rarely accessed and ignored by
+	 * XSAVES in this configuration, interception avoids the overhead of
+	 * manually context switching the hardware MSR during vcpu_load/put.
+	 *
+	 * This behavior is consistent with the current setup in
+	 * vmx_recalc_msr_intercepts(), so no change is needed to the interception
+	 * logic for MSR_IA32_PL0_SSP.
+	 */
+	vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP0, MSR_TYPE_RW, intercept);
+}
+
 static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu)
 {
 	bool intercept;
@@ -4371,6 +4437,7 @@ static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu)
 	}
 
 	vmx_recalc_pmu_msr_intercepts(vcpu);
+	vmx_set_intercept_for_fred_msr(vcpu);
 
 	/*
 	 * x2APIC and LBR MSR intercepts are modified on-demand and cannot be
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 09/28] KVM: VMX: Save/restore guest FRED RSP0
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (7 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 08/28] KVM: VMX: Set FRED MSR intercepts Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 10/28] KVM: VMX: Add support for saving and restoring FRED MSRs Sohil Mehta
                   ` (18 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Save guest FRED RSP0 in vmx_prepare_switch_to_host() and restore it
in vmx_prepare_switch_to_guest() because MSR_IA32_FRED_RSP0 is passed
through to the guest, thus is volatile/unknown.

Note, host FRED RSP0 is restored in arch_exit_to_user_mode_prepare(),
regardless of whether it is modified in KVM.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
Reviewed-by: Chao Gao <chao.gao@intel.com>
Reviewed-by: Binbin Wu <binbin.wu@linux.intel.com>
---
v10:
 - No change

Note, an equivalent TDX side implementation is missing. No TDX module
with FRED support exists yet so this would be an issue for future
platforms. A generic discussion regarding this is ongoing at:
https://lore.kernel.org/kvm/20260827031837.2863609-1-binbin.wu@linux.intel.com/
---
 arch/x86/kvm/vmx/vmx.c | 13 +++++++++++++
 arch/x86/kvm/vmx/vmx.h |  1 +
 2 files changed, 14 insertions(+)

diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 09bbbc680aa7..b6b73ab9cfb7 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -1395,6 +1395,9 @@ void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu)
 	}
 
 	wrmsrq(MSR_KERNEL_GS_BASE, vmx->msr_guest_kernel_gs_base);
+
+	if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED))
+		wrmsrns(MSR_IA32_FRED_RSP0, vmx->msr_guest_fred_rsp0);
 #else
 	savesegment(fs, fs_sel);
 	savesegment(gs, gs_sel);
@@ -1439,6 +1442,16 @@ static void vmx_prepare_switch_to_host(struct vcpu_vmx *vmx)
 	invalidate_tss_limit();
 #ifdef CONFIG_X86_64
 	wrmsrq(MSR_KERNEL_GS_BASE, vmx->vt.msr_host_kernel_gs_base);
+
+	if (guest_cpu_cap_has(&vmx->vcpu, X86_FEATURE_FRED)) {
+		vmx->msr_guest_fred_rsp0 = read_msr(MSR_IA32_FRED_RSP0);
+		/*
+		 * Synchronize the current value in hardware to the kernel's
+		 * local cache.  The desired host RSP0 will be set when the
+		 * CPU exits to userspace (RSP0 is a per-task value).
+		 */
+		fred_sync_rsp0(vmx->msr_guest_fred_rsp0);
+	}
 #endif
 	load_fixmap_gdt(raw_smp_processor_id());
 	vmx->vt.guest_state_loaded = false;
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index 7a22e1b1a273..14fd3f57d5c1 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -222,6 +222,7 @@ struct vcpu_vmx {
 	bool                  guest_uret_msrs_loaded;
 #ifdef CONFIG_X86_64
 	u64		      msr_guest_kernel_gs_base;
+	u64		      msr_guest_fred_rsp0;
 #endif
 
 	u64		      spec_ctrl;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 10/28] KVM: VMX: Add support for saving and restoring FRED MSRs
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (8 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 09/28] KVM: VMX: Save/restore guest FRED RSP0 Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 11/28] KVM: x86: Add a helper to detect if FRED is enabled for a vCPU Sohil Mehta
                   ` (17 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Introduce support for handling FRED MSR access requests, enabling both
host and guest to read and write FRED MSRs, which is essential for VM
save/restore and live migration, and allows userspace tools such as QEMU
to access the relevant MSRs.

Specially, intercept accesses to the FRED SSP0 MSR (IA32_PL0_SSP), which
remains accessible when FRED is enumerated even if CET is not.  This
ensures the guest value is fully virtual and does not alter the hardware
FRED SSP0 MSR.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
Reviewed-by: Chao Gao <chao.gao@intel.com>
---
v10:
 - Fix a corner case identified by Chao Gao.
---
 arch/x86/include/asm/kvm_host.h  |  5 ++
 arch/x86/include/asm/msr-index.h |  2 +
 arch/x86/kvm/msrs.c              | 89 ++++++++++++++++++++++++++++++--
 arch/x86/kvm/vmx/vmx.c           | 45 ++++++++++++++++
 4 files changed, 138 insertions(+), 3 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 8d3999772ac2..0fd5d4edeebb 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1032,6 +1032,11 @@ struct kvm_vcpu_arch {
 #if IS_ENABLED(CONFIG_HYPERV)
 	hpa_t hv_root_tdp;
 #endif
+	/*
+	 * Stores the FRED SSP0 MSR when CET is not supported, prompting KVM
+	 * to intercept its accesses.
+	 */
+	u64 fred_ssp0_fallback;
 };
 
 struct kvm_lpage_info {
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index ad899d9070e1..b386709515c4 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -64,6 +64,8 @@
 #define MSR_IA32_FRED_SSP3	0x1d3			/* Level 3 shadow stack pointer */
 #define MSR_IA32_FRED_CONFIG	0x1d4			/* Entrypoint and interrupt stack level */
 
+#define FRED_CONFIG_RESERVED	(BIT_ULL(2) | GENMASK_ULL(5, 4) | BIT_ULL(11))
+
 /* Intel MSRs. Some also available on other CPUs */
 #define MSR_TEST_CTRL				0x00000033
 #define MSR_TEST_CTRL_SPLIT_LOCK_DETECT_BIT	29
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index 66fa7140d65d..9eabb7b02cb5 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -193,6 +193,9 @@ static const u32 msrs_to_save_base[] = {
 	MSR_STAR,
 #ifdef CONFIG_X86_64
 	MSR_CSTAR, MSR_KERNEL_GS_BASE, MSR_SYSCALL_MASK, MSR_LSTAR,
+	MSR_IA32_FRED_RSP0, MSR_IA32_FRED_RSP1, MSR_IA32_FRED_RSP2,
+	MSR_IA32_FRED_RSP3, MSR_IA32_FRED_STKLVLS, MSR_IA32_FRED_SSP1,
+	MSR_IA32_FRED_SSP2, MSR_IA32_FRED_SSP3, MSR_IA32_FRED_CONFIG,
 #endif
 	MSR_IA32_TSC, MSR_IA32_CR_PAT, MSR_VM_HSAVE_PA,
 	MSR_IA32_FEAT_CTL, MSR_IA32_BNDCFGS, MSR_TSC_AUX,
@@ -772,7 +775,7 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data,
 		 * architecture. Intercepting XRSTORS/XSAVES for this
 		 * special case isn't deemed worthwhile.
 		 */
-	case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB:
+	case MSR_IA32_PL1_SSP ... MSR_IA32_INT_SSP_TAB:
 		if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK))
 			return KVM_MSR_RET_UNSUPPORTED;
 		/*
@@ -787,6 +790,52 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data,
 		if (index != MSR_IA32_INT_SSP_TAB && !IS_ALIGNED(data, 4))
 			return 1;
 		break;
+	case MSR_IA32_FRED_STKLVLS:
+		if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED))
+			return KVM_MSR_RET_UNSUPPORTED;
+		break;
+	case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_RSP3:
+	case MSR_IA32_FRED_SSP1 ... MSR_IA32_FRED_CONFIG: {
+		u64 reserved_bits = 0;
+
+		if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED))
+			return KVM_MSR_RET_UNSUPPORTED;
+
+		if (is_noncanonical_msr_address(data, vcpu))
+			return 1;
+
+		switch (index) {
+		case MSR_IA32_FRED_CONFIG:
+			reserved_bits = FRED_CONFIG_RESERVED;
+			break;
+		case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_RSP3:
+			reserved_bits = GENMASK_ULL(5, 0);
+			break;
+		case MSR_IA32_FRED_SSP1 ... MSR_IA32_FRED_SSP3:
+			reserved_bits = GENMASK_ULL(2, 0);
+			break;
+		default:
+			WARN_ON_ONCE(1);
+			return 1;
+		}
+
+		if (data & reserved_bits)
+			return 1;
+
+		break;
+	}
+	case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */
+		if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) &&
+		    !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED))
+			return KVM_MSR_RET_UNSUPPORTED;
+
+		if (is_noncanonical_msr_address(data, vcpu))
+			return 1;
+
+		if (!IS_ALIGNED(data, 4))
+			return 1;
+
+		break;
 	}
 
 	msr.data = data;
@@ -841,10 +890,19 @@ static int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data,
 		if (!host_initiated)
 			return 1;
 		fallthrough;
-	case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB:
+	case MSR_IA32_PL1_SSP ... MSR_IA32_INT_SSP_TAB:
 		if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK))
 			return KVM_MSR_RET_UNSUPPORTED;
 		break;
+	case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_CONFIG:
+		if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED))
+			return KVM_MSR_RET_UNSUPPORTED;
+		break;
+	case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */
+		if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) &&
+		    !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED))
+			return KVM_MSR_RET_UNSUPPORTED;
+		break;
 	}
 
 	msr.index = index;
@@ -1833,7 +1891,15 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 		break;
 #endif
 	case MSR_IA32_U_CET:
+		kvm_set_xstate_msr(vcpu, msr_info);
+		break;
 	case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP:
+		if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) {
+			WARN_ON_ONCE(msr != MSR_IA32_FRED_SSP0);
+			vcpu->arch.fred_ssp0_fallback = data;
+			break;
+		}
+
 		kvm_set_xstate_msr(vcpu, msr_info);
 		break;
 	default:
@@ -2186,7 +2252,15 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 		break;
 #endif
 	case MSR_IA32_U_CET:
+		kvm_get_xstate_msr(vcpu, msr_info);
+		break;
 	case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP:
+		if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) {
+			WARN_ON_ONCE(msr_info->index != MSR_IA32_FRED_SSP0);
+			msr_info->data = vcpu->arch.fred_ssp0_fallback;
+			break;
+		}
+
 		kvm_get_xstate_msr(vcpu, msr_info);
 		break;
 	default:
@@ -2656,10 +2730,19 @@ static void kvm_probe_msr_to_save(u32 msr_index)
 		if (!kvm_cpu_cap_has(X86_FEATURE_LM))
 			return;
 		fallthrough;
-	case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP:
+	case MSR_IA32_PL1_SSP ... MSR_IA32_PL3_SSP:
 		if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK))
 			return;
 		break;
+	case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_CONFIG:
+		if (!kvm_cpu_cap_has(X86_FEATURE_FRED))
+			return;
+		break;
+	case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */
+		if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK) &&
+		    !kvm_cpu_cap_has(X86_FEATURE_FRED))
+			return;
+		break;
 	default:
 		break;
 	}
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index b6b73ab9cfb7..362bd4dda298 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -1489,6 +1489,18 @@ static void vmx_write_guest_kernel_gs_base(struct vcpu_vmx *vmx, u64 data)
 	vmx_write_guest_host_msr(vmx, MSR_KERNEL_GS_BASE, data,
 				 &vmx->msr_guest_kernel_gs_base);
 }
+
+static u64 vmx_read_guest_fred_rsp0(struct vcpu_vmx *vmx)
+{
+	return vmx_read_guest_host_msr(vmx, MSR_IA32_FRED_RSP0,
+				       &vmx->msr_guest_fred_rsp0);
+}
+
+static void vmx_write_guest_fred_rsp0(struct vcpu_vmx *vmx, u64 data)
+{
+	vmx_write_guest_host_msr(vmx, MSR_IA32_FRED_RSP0, data,
+				 &vmx->msr_guest_fred_rsp0);
+}
 #endif
 
 static void grow_ple_window(struct kvm_vcpu *vcpu)
@@ -2140,6 +2152,27 @@ int vmx_get_feature_msr(u32 msr, u64 *data)
 	}
 }
 
+#ifdef CONFIG_X86_64
+static const u32 fred_msr_vmcs_fields[] = {
+	GUEST_IA32_FRED_RSP1,
+	GUEST_IA32_FRED_RSP2,
+	GUEST_IA32_FRED_RSP3,
+	GUEST_IA32_FRED_STKLVLS,
+	GUEST_IA32_FRED_SSP1,
+	GUEST_IA32_FRED_SSP2,
+	GUEST_IA32_FRED_SSP3,
+	GUEST_IA32_FRED_CONFIG,
+};
+
+static_assert(MSR_IA32_FRED_CONFIG - MSR_IA32_FRED_RSP1 ==
+	      ARRAY_SIZE(fred_msr_vmcs_fields) - 1);
+
+static u32 fred_msr_to_vmcs(u32 msr)
+{
+	return fred_msr_vmcs_fields[msr - MSR_IA32_FRED_RSP1];
+}
+#endif
+
 /*
  * Reads an msr value (of 'msr_info->index') into 'msr_info->data'.
  * Returns 0 on success, non-0 otherwise.
@@ -2162,6 +2195,12 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 	case MSR_KERNEL_GS_BASE:
 		msr_info->data = vmx_read_guest_kernel_gs_base(vmx);
 		break;
+	case MSR_IA32_FRED_RSP0:
+		msr_info->data = vmx_read_guest_fred_rsp0(vmx);
+		break;
+	case MSR_IA32_FRED_RSP1 ... MSR_IA32_FRED_CONFIG:
+		msr_info->data = vmcs_read64(fred_msr_to_vmcs(msr_info->index));
+		break;
 #endif
 	case MSR_EFER:
 		return kvm_get_msr_common(vcpu, msr_info);
@@ -2394,6 +2433,12 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 			vmx_update_exception_bitmap(vcpu);
 		}
 		break;
+	case MSR_IA32_FRED_RSP0:
+		vmx_write_guest_fred_rsp0(vmx, data);
+		break;
+	case MSR_IA32_FRED_RSP1 ... MSR_IA32_FRED_CONFIG:
+		vmcs_write64(fred_msr_to_vmcs(msr_index), data);
+		break;
 #endif
 	case MSR_IA32_SYSENTER_CS:
 		if (is_guest_mode(vcpu))
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 11/28] KVM: x86: Add a helper to detect if FRED is enabled for a vCPU
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (9 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 10/28] KVM: VMX: Add support for saving and restoring FRED MSRs Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 12/28] KVM: x86: Add a new save/restore flag for FRED metadata Sohil Mehta
                   ` (16 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Add is_fred_enabled() to detect if FRED is enabled on a vCPU.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
[ Sean: removed the "kvm_" prefix from the function name ]
Signed-off-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
Tested-by: Shan Kang <shan.kang@intel.com>
Tested-by: Xuelian Guo <xuelian.guo@intel.com>
Reviewed-by: Chao Gao <chao.gao@intel.com>
---
v10:
 - Add back the change log (Binbin Wu and Sean).
---
 arch/x86/kvm/regs.h | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/arch/x86/kvm/regs.h b/arch/x86/kvm/regs.h
index 447f0ec3e63e..4e641a6c20f9 100644
--- a/arch/x86/kvm/regs.h
+++ b/arch/x86/kvm/regs.h
@@ -364,6 +364,21 @@ static __always_inline bool kvm_is_cr4_bit_set(struct kvm_vcpu *vcpu,
 	return !!kvm_read_cr4_bits(vcpu, cr4_bit);
 }
 
+/*
+ * It's enough to check just CR4.FRED (X86_CR4_FRED) to tell if
+ * a vCPU is running with FRED enabled, because:
+ * 1) CR4.FRED can be set to 1 only _after_ IA32_EFER.LMA = 1.
+ * 2) To leave IA-32e mode, CR4.FRED must be cleared first.
+ */
+static inline bool is_fred_enabled(struct kvm_vcpu *vcpu)
+{
+#ifdef CONFIG_X86_64
+	return kvm_is_cr4_bit_set(vcpu, X86_CR4_FRED);
+#else
+	return false;
+#endif
+}
+
 static inline ulong kvm_read_cr3(struct kvm_vcpu *vcpu)
 {
 	if (!kvm_register_is_available(vcpu, VCPU_REG_CR3))
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 12/28] KVM: x86: Add a new save/restore flag for FRED metadata
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (10 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 11/28] KVM: x86: Add a helper to detect if FRED is enabled for a vCPU Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 13/28] KVM: VMX: Virtualize FRED nested exception tracking Sohil Mehta
                   ` (15 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: Xin Li <xin@zytor.com>

Introduce a new save/restore flag for FRED metadata (specifically, the
event nested flag and data) being saved and restored during VM save/
restore and live migration.

Signed-off-by: Xin Li <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - Advertise KVM_CAP_X86_FRED_EVENT only when KVM can virtualize FRED, and
   return -EINVAL from KVM_ENABLE_CAP otherwise.
 - Document the new return value in api.rst.
---
 Documentation/virt/kvm/api.rst  | 22 ++++++++++++++++++++++
 arch/x86/include/asm/kvm_host.h |  1 +
 arch/x86/include/uapi/asm/kvm.h |  1 +
 arch/x86/kvm/x86.c              | 18 ++++++++++++++++++
 include/uapi/linux/kvm.h        |  1 +
 5 files changed, 43 insertions(+)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 4eb7e75a7473..22ef94f3eb3a 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -1199,6 +1199,10 @@ The following bits are defined in the flags field:
   triple_fault_pending field contains a valid state. This bit will
   be set whenever KVM_CAP_X86_TRIPLE_FAULT_EVENT is enabled.
 
+- KVM_VCPUEVENT_VALID_FRED_STATE may be set to inform that the exception
+  state includes FRED state (specifically, the event nested flag and data).
+  This bit will be set whenever KVM_CAP_X86_FRED_EVENT is enabled.
+
 ARM64:
 ^^^^^^
 
@@ -1301,6 +1305,11 @@ If KVM_CAP_X86_TRIPLE_FAULT_EVENT is enabled, KVM_VCPUEVENT_VALID_TRIPLE_FAULT
 can be set in flags field to signal that the triple_fault field contains
 a valid state and shall be written into the VCPU.
 
+If KVM_CAP_X86_FRED_EVENT is enabled, KVM_VCPUEVENT_VALID_FRED_STATE can be set
+in the flags field to inform that the exception state contains FRED state
+(specifically, the event nested flag and data), which shall be written into the
+VCPU.
+
 ARM64:
 ^^^^^^
 
@@ -8979,6 +8988,19 @@ enabled, cmma can't be enabled anymore and pfmfi and the storage key
 interpretation are disabled. If cmma has already been enabled or the
 hpage_2g module parameter is not set to 1, -EINVAL is returned.
 
+7.48 KVM_CAP_X86_FRED_EVENT
+---------------------------
+
+:Architectures: x86
+:Parameters: args[0] whether feature should be enabled or not
+:Returns: 0 on success; -EINVAL if KVM cannot virtualize FRED.
+
+With this capability enabled, KVM allows exception save and restore operations
+to include FRED event context (specifically, the event nested flag and data).
+When injecting a FRED exception during VM entry, FRED event delivery relies
+on this information to select the correct event stack level and apply proper
+event data.
+
 8. Other capabilities.
 ======================
 
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 0fd5d4edeebb..dd68db17fdae 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1286,6 +1286,7 @@ struct kvm_arch {
 	bool has_mapped_host_mmio;
 	bool guest_can_read_msr_platform_info;
 	bool exception_payload_enabled;
+	bool exception_fred_state_enabled;
 
 	bool triple_fault_event;
 
diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
index 1585ec804066..6a67832c59bb 100644
--- a/arch/x86/include/uapi/asm/kvm.h
+++ b/arch/x86/include/uapi/asm/kvm.h
@@ -331,6 +331,7 @@ struct kvm_reinject_control {
 #define KVM_VCPUEVENT_VALID_SMM		0x00000008
 #define KVM_VCPUEVENT_VALID_PAYLOAD	0x00000010
 #define KVM_VCPUEVENT_VALID_TRIPLE_FAULT	0x00000020
+#define KVM_VCPUEVENT_VALID_FRED_STATE	0x00000040
 
 /* Interrupt shadow states */
 #define KVM_X86_SHADOW_INT_MOV_SS	0x01
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 4b3681796c75..01198aba27cf 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2288,6 +2288,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_PRE_FAULT_MEMORY:
 		r = tdp_enabled;
 		break;
+	case KVM_CAP_X86_FRED_EVENT:
+		r = kvm_cpu_cap_has(X86_FEATURE_FRED);
+		break;
 	case KVM_CAP_X86_APIC_BUS_CYCLES_NS:
 		r = kvm ? kvm->arch.apic_bus_cycle_ns : APIC_BUS_CYCLE_NS_DEFAULT;
 		break;
@@ -3022,6 +3025,8 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu,
 			 | KVM_VCPUEVENT_VALID_SMM);
 	if (vcpu->kvm->arch.exception_payload_enabled)
 		events->flags |= KVM_VCPUEVENT_VALID_PAYLOAD;
+	if (vcpu->kvm->arch.exception_fred_state_enabled)
+		events->flags |= KVM_VCPUEVENT_VALID_FRED_STATE;
 	if (vcpu->kvm->arch.triple_fault_event) {
 		events->triple_fault.pending = kvm_test_request(KVM_REQ_TRIPLE_FAULT, vcpu);
 		events->flags |= KVM_VCPUEVENT_VALID_TRIPLE_FAULT;
@@ -3036,6 +3041,7 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
 			      | KVM_VCPUEVENT_VALID_SHADOW
 			      | KVM_VCPUEVENT_VALID_SMM
 			      | KVM_VCPUEVENT_VALID_PAYLOAD
+			      | KVM_VCPUEVENT_VALID_FRED_STATE
 			      | KVM_VCPUEVENT_VALID_TRIPLE_FAULT))
 		return -EINVAL;
 
@@ -3051,6 +3057,11 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
 		events->exception_has_payload = 0;
 	}
 
+	if (events->flags & KVM_VCPUEVENT_VALID_FRED_STATE) {
+		if (!vcpu->kvm->arch.exception_fred_state_enabled)
+			return -EINVAL;
+	}
+
 	if ((events->exception.injected || events->exception.pending) &&
 	    (events->exception.nr > 31 || events->exception.nr == NMI_VECTOR))
 		return -EINVAL;
@@ -4038,6 +4049,13 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 		kvm->arch.exception_payload_enabled = cap->args[0];
 		r = 0;
 		break;
+	case KVM_CAP_X86_FRED_EVENT:
+		r = -EINVAL;
+		if (!kvm_cpu_cap_has(X86_FEATURE_FRED))
+			break;
+		kvm->arch.exception_fred_state_enabled = cap->args[0];
+		r = 0;
+		break;
 	case KVM_CAP_X86_TRIPLE_FAULT_EVENT:
 		kvm->arch.triple_fault_event = cap->args[0];
 		r = 0;
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 9fc8dfdfd65f..c5ffc4e7ddb1 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -998,6 +998,7 @@ struct kvm_enable_cap {
 #define KVM_CAP_S390_VSIE_ESAMODE 248
 #define KVM_CAP_S390_HPAGE_2G 249
 #define KVM_CAP_ARM_PMU_V3_STRICT 250
+#define KVM_CAP_X86_FRED_EVENT 251
 
 struct kvm_irq_routing_irqchip {
 	__u32 irqchip;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 13/28] KVM: VMX: Virtualize FRED nested exception tracking
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (11 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 12/28] KVM: x86: Add a new save/restore flag for FRED metadata Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 14/28] KVM: VMX: Virtualize FRED event_data Sohil Mehta
                   ` (14 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Set the VMX nested exception bit in VM-entry interruption information
field when injecting a nested exception using FRED event delivery to
ensure:
  1) A nested exception is injected on a correct stack level.
  2) The nested bit defined in FRED stack frame is set.

The event stack level used by FRED event delivery depends on whether
the event was a nested exception encountered during delivery of an
earlier event, because a nested exception is "regarded" as happening
on ring 0.  E.g., when #PF is configured to use stack level 1 in
IA32_FRED_STKLVLS MSR:
  - nested #PF will be delivered on the stack pointed by IA32_FRED_RSP1
    MSR when encountered in ring 3 and ring 0.
  - normal #PF will be delivered on the stack pointed by IA32_FRED_RSP0
    MSR when encountered in ring 3.

The VMX nested-exception support ensures a correct event stack level is
chosen when a VM entry injects a nested exception.

In addition, save/restore the nested flag of an exception during VM
save/restore and live migration to ensure a correct event stack level
is chosen when a nested exception is injected through FRED event
delivery across VM save/restore and live migration.

Note, like the other fields of struct kvm_queued_exception, the nested
flag is meaningful only while the exception is pending or injected. It
is not consulted otherwise, and so doesn't need to be cleared when an
exception is dropped.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
[ Sean: reworked kvm_requeue_exception() to simply the code changes ]
Signed-off-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
Reviewed-by: Chao Gao <chao.gao@intel.com>
---
v10:
 - Rename exception status member variable nested to is_nested (Sean).
 - Rename KVM_CAP_EXCEPTION_NESTED_FLAG to KVM_CAP_X86_FRED_EVENT, to
   include both FRED event nested flag and data.
 - Track the nested flag of an exception being queued in a local
   variable (so that a flag left over from a no-longer-live exception
   can't be folded into a new one).
---
 arch/x86/include/asm/kvm_host.h |  1 +
 arch/x86/include/asm/vmx.h      |  5 ++++-
 arch/x86/include/uapi/asm/kvm.h |  3 ++-
 arch/x86/kvm/svm/svm.c          |  2 +-
 arch/x86/kvm/vmx/vmx.c          | 10 ++++++++--
 arch/x86/kvm/x86.c              | 18 +++++++++++++++++-
 arch/x86/kvm/x86.h              |  4 +++-
 7 files changed, 36 insertions(+), 7 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index dd68db17fdae..994c354dc47b 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -695,6 +695,7 @@ struct kvm_queued_exception {
 	u32 error_code;
 	unsigned long payload;
 	bool has_payload;
+	bool is_nested;
 };
 
 /*
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index 4720619f09ac..7eb08f84a7e5 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -152,6 +152,7 @@ struct vmcs {
 #define VMX_BASIC_INOUT				BIT_ULL(54)
 #define VMX_BASIC_TRUE_CTLS			BIT_ULL(55)
 #define VMX_BASIC_NO_HW_ERROR_CODE_CC		BIT_ULL(56)
+#define VMX_BASIC_NESTED_EXCEPTION		BIT_ULL(58)
 #define VMX_BASIC_NO_SEAMRET_INVD_VMCS		BIT_ULL(60)
 
 static inline u32 vmx_basic_vmcs_revision_id(u64 vmx_basic)
@@ -451,13 +452,15 @@ enum vmcs_field {
 #define INTR_INFO_INTR_TYPE_MASK        0x700           /* 10:8 */
 #define INTR_INFO_DELIVER_CODE_MASK     0x800           /* 11 */
 #define INTR_INFO_UNBLOCK_NMI		0x1000		/* 12 */
+#define INTR_INFO_NESTED_EXCEPTION_MASK	0x2000		/* 13 */
 #define INTR_INFO_VALID_MASK            0x80000000      /* 31 */
-#define INTR_INFO_RESVD_BITS_MASK       0x7ffff000
+#define INTR_INFO_RESVD_BITS_MASK       0x7fffd000
 
 #define VECTORING_INFO_VECTOR_MASK           	INTR_INFO_VECTOR_MASK
 #define VECTORING_INFO_TYPE_MASK        	INTR_INFO_INTR_TYPE_MASK
 #define VECTORING_INFO_DELIVER_CODE_MASK    	INTR_INFO_DELIVER_CODE_MASK
 #define VECTORING_INFO_VALID_MASK       	INTR_INFO_VALID_MASK
+#define VECTORING_INFO_NESTED_EXCEPTION_MASK	INTR_INFO_NESTED_EXCEPTION_MASK
 
 #define INTR_TYPE_EXT_INTR		(EVENT_TYPE_EXTINT << 8)	/* external interrupt */
 #define INTR_TYPE_RESERVED		(EVENT_TYPE_RESERVED << 8)	/* reserved */
diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
index 6a67832c59bb..659c8391d40e 100644
--- a/arch/x86/include/uapi/asm/kvm.h
+++ b/arch/x86/include/uapi/asm/kvm.h
@@ -369,7 +369,8 @@ struct kvm_vcpu_events {
 	struct {
 		__u8 pending;
 	} triple_fault;
-	__u8 reserved[26];
+	__u8 reserved[25];
+	__u8 exception_is_nested;
 	__u8 exception_has_payload;
 	__u64 exception_payload;
 };
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index ea647938a2a6..7762db655b28 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -4392,7 +4392,7 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu)
 
 		kvm_requeue_exception(vcpu, vector,
 				      exitintinfo & SVM_EXITINTINFO_VALID_ERR,
-				      error_code);
+				      error_code, false);
 		break;
 	}
 	case SVM_EXITINTINFO_TYPE_INTR:
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 362bd4dda298..25c3301e137b 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -2008,8 +2008,13 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu)
 		vmcs_write32(VM_ENTRY_INSTRUCTION_LEN,
 			     vmx->vcpu.arch.event_exit_inst_len);
 		intr_info |= INTR_TYPE_SOFT_EXCEPTION;
-	} else
+	} else {
 		intr_info |= INTR_TYPE_HARD_EXCEPTION;
+		if (is_fred_enabled(vcpu)) {
+			if (ex->is_nested)
+				intr_info |= INTR_INFO_NESTED_EXCEPTION_MASK;
+		}
+	}
 
 	vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info);
 
@@ -7501,7 +7506,8 @@ static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu,
 
 		kvm_requeue_exception(vcpu, vector,
 				      idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK,
-				      error_code);
+				      error_code,
+				      idt_vectoring_info & VECTORING_INFO_NESTED_EXCEPTION_MASK);
 		break;
 	}
 	case INTR_TYPE_SOFT_INTR:
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 01198aba27cf..94d684210cf6 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -461,6 +461,7 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, unsigned int nr,
 				   bool has_error, u32 error_code,
 				   bool has_payload, unsigned long payload)
 {
+	bool is_nested = false;
 	u32 prev_nr;
 	int class1, class2;
 
@@ -487,6 +488,11 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, unsigned int nr,
 		vcpu->arch.exception.error_code = error_code;
 		vcpu->arch.exception.has_payload = has_payload;
 		vcpu->arch.exception.payload = payload;
+		/* #DF is NOT a nested event, per its definition. */
+		vcpu->arch.exception.is_nested = (nr != DF_VECTOR) &&
+						 (is_nested ||
+						  vcpu->arch.nmi_injected ||
+						  vcpu->arch.interrupt.injected);
 		return;
 	}
 
@@ -510,6 +516,9 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, unsigned int nr,
 
 		kvm_queue_exception_e(vcpu, DF_VECTOR, 0);
 	} else {
+		/* The new exception arrived while delivering the previous one. */
+		is_nested = true;
+
 		/* replace previous exception with a new one in a hope
 		   that instruction re-execution will regenerate lost
 		   exception */
@@ -538,7 +547,8 @@ static void kvm_queue_exception_e_p(struct kvm_vcpu *vcpu, unsigned nr,
 }
 
 void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr,
-			   bool has_error_code, u32 error_code)
+			   bool has_error_code, u32 error_code,
+			   bool is_nested)
 {
 
 	/*
@@ -563,6 +573,7 @@ void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr,
 	vcpu->arch.exception.error_code = error_code;
 	vcpu->arch.exception.has_payload = false;
 	vcpu->arch.exception.payload = 0;
+	vcpu->arch.exception.is_nested = is_nested;
 }
 EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_requeue_exception);
 
@@ -3000,6 +3011,7 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu,
 	events->exception.error_code = ex->error_code;
 	events->exception_has_payload = ex->has_payload;
 	events->exception_payload = ex->payload;
+	events->exception_is_nested = ex->is_nested;
 
 	events->interrupt.injected =
 		vcpu->arch.interrupt.injected && !vcpu->arch.interrupt.soft;
@@ -3060,6 +3072,8 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
 	if (events->flags & KVM_VCPUEVENT_VALID_FRED_STATE) {
 		if (!vcpu->kvm->arch.exception_fred_state_enabled)
 			return -EINVAL;
+	} else {
+		events->exception_is_nested = 0;
 	}
 
 	if ((events->exception.injected || events->exception.pending) &&
@@ -3087,6 +3101,7 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
 	vcpu->arch.exception.error_code = events->exception.error_code;
 	vcpu->arch.exception.has_payload = events->exception_has_payload;
 	vcpu->arch.exception.payload = events->exception_payload;
+	vcpu->arch.exception.is_nested = events->exception_is_nested;
 
 	vcpu->arch.interrupt.injected = events->interrupt.injected;
 	vcpu->arch.interrupt.nr = events->interrupt.nr;
@@ -8978,6 +8993,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
 					   ex->has_payload, ex->payload);
 		ex->injected = false;
 		ex->pending = false;
+		ex->is_nested = false;
 	}
 	vcpu->arch.exception_from_userspace = false;
 
diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
index 0f5919b092e4..2d6bae66cfae 100644
--- a/arch/x86/kvm/x86.h
+++ b/arch/x86/kvm/x86.h
@@ -158,6 +158,7 @@ static inline void kvm_clear_exception_queue(struct kvm_vcpu *vcpu)
 {
 	vcpu->arch.exception.pending = false;
 	vcpu->arch.exception.injected = false;
+	vcpu->arch.exception.is_nested = false;
 	vcpu->arch.exception_vmexit.pending = false;
 }
 
@@ -502,7 +503,8 @@ void kvm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr);
 void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code);
 void kvm_queue_exception_p(struct kvm_vcpu *vcpu, unsigned nr, unsigned long payload);
 void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr,
-			   bool has_error_code, u32 error_code);
+			   bool has_error_code, u32 error_code,
+			   bool is_nested);
 void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault,
 			   bool from_hardware);
 void __kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu,
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 14/28] KVM: VMX: Virtualize FRED event_data
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (12 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 13/28] KVM: VMX: Virtualize FRED nested exception tracking Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 15/28] KVM: x86: Include CR4.FRED in the emulator CR4 write mask Sohil Mehta
                   ` (13 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Set injected-event data when injecting a #PF, #DB, or #NM due to
extended feature disable using FRED event delivery, and save
original-event data for being used as injected-event data.

Unlike IDT using some extra CPU register as part of an event
context, e.g., %cr2 for #PF, FRED saves a complete event context
in its stack frame, e.g., FRED saves the faulting linear address
of a #PF into the event data field defined in its stack frame.

Thus a new VMX control field called injected-event data is added
to provide the event data that will be pushed into a FRED stack
frame for VM entries that inject an event using FRED event delivery.
In addition, a new VM exit information field called original-event
data is added to store the event data that would have saved into a
FRED stack frame for VM exits that occur during FRED event delivery.
After such a VM exit is handled to allow the original-event to be
delivered, the data in the original-event data VMCS field needs to
be set into the injected-event data VMCS field for the injection of
the original event.

In addition, save/restore the event data of an exception during VM
save/restore and live migration to ensure it's properly saved and
restored across VM save/restore and live migration. Clear the
injected-event data field when injecting an IRQ or an NMI as no event
data is currently defined for them.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
[ Sean: reworked event data injection for nested ]
Signed-off-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - Reset event data in kvm_clear_exception_queue() (Chao).
 - Clear event data to 0 to prevent stale data leakage when injecting
   a hardware exception that doesn't require FRED event data.
 - Save/restore event data for properly restoring it for #PF (CR2), #DB
   (DR6) and #NM due to extended feature disable (IA32_XFD_ERR) (Chao).
 - Clear the INJECTED_EVENT_DATA VMCS field during NMI and IRQ
   injection. (Zhao)
---
 arch/x86/include/asm/kvm_host.h |  1 +
 arch/x86/include/asm/vmx.h      |  4 +++
 arch/x86/include/uapi/asm/kvm.h |  5 ++-
 arch/x86/kvm/svm/svm.c          |  2 +-
 arch/x86/kvm/vmx/vmx.c          | 58 ++++++++++++++++++++++++++++++---
 arch/x86/kvm/x86.c              | 23 ++++++++++++-
 arch/x86/kvm/x86.h              |  3 +-
 7 files changed, 88 insertions(+), 8 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 994c354dc47b..21d12d86a18b 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -696,6 +696,7 @@ struct kvm_queued_exception {
 	unsigned long payload;
 	bool has_payload;
 	bool is_nested;
+	u64 event_data;
 };
 
 /*
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index 7eb08f84a7e5..6633988a84da 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -283,8 +283,12 @@ enum vmcs_field {
 	PID_POINTER_TABLE_HIGH		= 0x00002043,
 	SECONDARY_VM_EXIT_CONTROLS	= 0x00002044,
 	SECONDARY_VM_EXIT_CONTROLS_HIGH	= 0x00002045,
+	INJECTED_EVENT_DATA		= 0x00002052,
+	INJECTED_EVENT_DATA_HIGH	= 0x00002053,
 	GUEST_PHYSICAL_ADDRESS          = 0x00002400,
 	GUEST_PHYSICAL_ADDRESS_HIGH     = 0x00002401,
+	ORIGINAL_EVENT_DATA		= 0x00002404,
+	ORIGINAL_EVENT_DATA_HIGH	= 0x00002405,
 	VMCS_LINK_POINTER               = 0x00002800,
 	VMCS_LINK_POINTER_HIGH          = 0x00002801,
 	GUEST_IA32_DEBUGCTL             = 0x00002802,
diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
index 659c8391d40e..c8a6149fcf63 100644
--- a/arch/x86/include/uapi/asm/kvm.h
+++ b/arch/x86/include/uapi/asm/kvm.h
@@ -369,7 +369,10 @@ struct kvm_vcpu_events {
 	struct {
 		__u8 pending;
 	} triple_fault;
-	__u8 reserved[25];
+	__u8 reserved0[11];
+	/* Aligned to a 64-bit boundary */
+	__u64 exception_event_data;
+	__u8 reserved1[6];
 	__u8 exception_is_nested;
 	__u8 exception_has_payload;
 	__u64 exception_payload;
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 7762db655b28..f254a5ff570d 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -4392,7 +4392,7 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu)
 
 		kvm_requeue_exception(vcpu, vector,
 				      exitintinfo & SVM_EXITINTINFO_VALID_ERR,
-				      error_code, false);
+				      error_code, false, 0);
 		break;
 	}
 	case SVM_EXITINTINFO_TYPE_INTR:
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 25c3301e137b..cd041381b322 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -2004,15 +2004,39 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu)
 
 	WARN_ON_ONCE(vmx->vt.emulation_required);
 
+	/*
+	 * Event data is strictly required only for #PF, #DB, and #NM (due to
+	 * extended feature disable) hardware exceptions under FRED, making it
+	 * straightforward to correctly populate for just these vectors.
+	 *
+	 * Conversely, trying to guarantee that event data is cleanly managed or
+	 * zeroed out across all other exception paths is highly complicated
+	 * and error-prone. A blanket fallback to 0 handles those cases safely.
+	 *
+	 * Therefore, set INJECTED_EVENT_DATA only if all the following are true:
+	 * - Guest has FRED enabled.
+	 * - Exception type is a hardware exception.
+	 * - Exception vector is #PF, #DB, or #NM.
+	 *
+	 * Otherwise, clear it to 0 to prevent stale data leakage.
+	 */
 	if (kvm_exception_is_soft(ex->vector)) {
 		vmcs_write32(VM_ENTRY_INSTRUCTION_LEN,
 			     vmx->vcpu.arch.event_exit_inst_len);
 		intr_info |= INTR_TYPE_SOFT_EXCEPTION;
+		if (is_fred_enabled(vcpu))
+			vmcs_write64(INJECTED_EVENT_DATA, 0);
 	} else {
 		intr_info |= INTR_TYPE_HARD_EXCEPTION;
 		if (is_fred_enabled(vcpu)) {
 			if (ex->is_nested)
 				intr_info |= INTR_INFO_NESTED_EXCEPTION_MASK;
+			if (ex->vector == PF_VECTOR ||
+			    ex->vector == DB_VECTOR ||
+			    ex->vector == NM_VECTOR)
+				vmcs_write64(INJECTED_EVENT_DATA, ex->event_data);
+			else
+				vmcs_write64(INJECTED_EVENT_DATA, 0);
 		}
 	}
 
@@ -5328,6 +5352,14 @@ void vmx_inject_irq(struct kvm_vcpu *vcpu, bool reinjected)
 			     vmx->vcpu.arch.event_exit_inst_len);
 	} else
 		intr |= INTR_TYPE_EXT_INTR;
+
+	/*
+	 * Interrupts define no event data, so clear any value left behind
+	 * by a previously injected exception.
+	 */
+	if (is_fred_enabled(vcpu))
+		vmcs_write64(INJECTED_EVENT_DATA, 0);
+
 	vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr);
 
 	vmx_clear_hlt(vcpu);
@@ -5358,6 +5390,13 @@ void vmx_inject_nmi(struct kvm_vcpu *vcpu)
 		return;
 	}
 
+	/*
+	 * KVM doesn't virtualize the NMI-source reporting feature. So clear
+	 * any value left behind by a previously injected exception.
+	 */
+	if (is_fred_enabled(vcpu))
+		vmcs_write64(INJECTED_EVENT_DATA, 0);
+
 	vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
 			INTR_TYPE_NMI_INTR | INTR_INFO_VALID_MASK | NMI_VECTOR);
 
@@ -7465,7 +7504,8 @@ static void vmx_recover_nmi_blocking(struct vcpu_vmx *vmx)
 static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu,
 				      u32 idt_vectoring_info,
 				      int instr_len_field,
-				      int error_code_field)
+				      int error_code_field,
+				      int event_data_field)
 {
 	u8 vector;
 	int type;
@@ -7500,14 +7540,18 @@ static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu,
 		fallthrough;
 	case INTR_TYPE_HARD_EXCEPTION: {
 		u32 error_code = 0;
+		u64 event_data = 0;
 
 		if (idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK)
 			error_code = vmcs_read32(error_code_field);
+		if (is_fred_enabled(vcpu))
+			event_data = vmcs_read64(event_data_field);
 
 		kvm_requeue_exception(vcpu, vector,
 				      idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK,
 				      error_code,
-				      idt_vectoring_info & VECTORING_INFO_NESTED_EXCEPTION_MASK);
+				      idt_vectoring_info & VECTORING_INFO_NESTED_EXCEPTION_MASK,
+				      event_data);
 		break;
 	}
 	case INTR_TYPE_SOFT_INTR:
@@ -7525,7 +7569,8 @@ static void vmx_complete_interrupts(struct vcpu_vmx *vmx)
 {
 	__vmx_complete_interrupts(&vmx->vcpu, vmx->idt_vectoring_info,
 				  VM_EXIT_INSTRUCTION_LEN,
-				  IDT_VECTORING_ERROR_CODE);
+				  IDT_VECTORING_ERROR_CODE,
+				  ORIGINAL_EVENT_DATA);
 }
 
 void vmx_cancel_injection(struct kvm_vcpu *vcpu)
@@ -7533,7 +7578,8 @@ void vmx_cancel_injection(struct kvm_vcpu *vcpu)
 	__vmx_complete_interrupts(vcpu,
 				  vmcs_read32(VM_ENTRY_INTR_INFO_FIELD),
 				  VM_ENTRY_INSTRUCTION_LEN,
-				  VM_ENTRY_EXCEPTION_ERROR_CODE);
+				  VM_ENTRY_EXCEPTION_ERROR_CODE,
+				  INJECTED_EVENT_DATA);
 
 	vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0);
 }
@@ -7645,6 +7691,10 @@ static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu,
 
 	vmx_disable_fb_clear(vmx);
 
+	/*
+	 * Note, even though FRED delivers the faulting linear address via the
+	 * event data field on the stack, CR2 is still updated.
+	 */
 	if (vcpu->arch.cr2 != native_read_cr2())
 		native_write_cr2(vcpu->arch.cr2);
 
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 94d684210cf6..e10ac6b280f6 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -431,9 +431,22 @@ void kvm_deliver_exception_payload(struct kvm_vcpu *vcpu,
 		 * breakpoint), it is reserved and must be zero in DR6.
 		 */
 		vcpu->arch.dr6 &= ~BIT(12);
+
+		/*
+		 * FRED #DB event data matches DR6, but follows the polarity of
+		 * VMX's pending debug exceptions, not DR6.
+		 */
+		ex->event_data = ex->payload & ~BIT(12);
+		break;
+	case NM_VECTOR:
+		ex->event_data = ex->payload;
 		break;
 	case PF_VECTOR:
 		vcpu->arch.cr2 = ex->payload;
+		ex->event_data = ex->payload;
+		break;
+	default:
+		ex->event_data = 0;
 		break;
 	}
 
@@ -493,6 +506,10 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, unsigned int nr,
 						 (is_nested ||
 						  vcpu->arch.nmi_injected ||
 						  vcpu->arch.interrupt.injected);
+		/*
+		 * Do not update the event data, as the current value may have
+		 * just been restored during a VM save/restore or live migration.
+		 */
 		return;
 	}
 
@@ -548,7 +565,7 @@ static void kvm_queue_exception_e_p(struct kvm_vcpu *vcpu, unsigned nr,
 
 void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr,
 			   bool has_error_code, u32 error_code,
-			   bool is_nested)
+			   bool is_nested, u64 event_data)
 {
 
 	/*
@@ -574,6 +591,7 @@ void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr,
 	vcpu->arch.exception.has_payload = false;
 	vcpu->arch.exception.payload = 0;
 	vcpu->arch.exception.is_nested = is_nested;
+	vcpu->arch.exception.event_data = event_data;
 }
 EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_requeue_exception);
 
@@ -3012,6 +3030,7 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu,
 	events->exception_has_payload = ex->has_payload;
 	events->exception_payload = ex->payload;
 	events->exception_is_nested = ex->is_nested;
+	events->exception_event_data = ex->event_data;
 
 	events->interrupt.injected =
 		vcpu->arch.interrupt.injected && !vcpu->arch.interrupt.soft;
@@ -3074,6 +3093,7 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
 			return -EINVAL;
 	} else {
 		events->exception_is_nested = 0;
+		events->exception_event_data = 0;
 	}
 
 	if ((events->exception.injected || events->exception.pending) &&
@@ -3102,6 +3122,7 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
 	vcpu->arch.exception.has_payload = events->exception_has_payload;
 	vcpu->arch.exception.payload = events->exception_payload;
 	vcpu->arch.exception.is_nested = events->exception_is_nested;
+	vcpu->arch.exception.event_data = events->exception_event_data;
 
 	vcpu->arch.interrupt.injected = events->interrupt.injected;
 	vcpu->arch.interrupt.nr = events->interrupt.nr;
diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
index 2d6bae66cfae..43c5c08f5d5c 100644
--- a/arch/x86/kvm/x86.h
+++ b/arch/x86/kvm/x86.h
@@ -159,6 +159,7 @@ static inline void kvm_clear_exception_queue(struct kvm_vcpu *vcpu)
 	vcpu->arch.exception.pending = false;
 	vcpu->arch.exception.injected = false;
 	vcpu->arch.exception.is_nested = false;
+	vcpu->arch.exception.event_data = 0;
 	vcpu->arch.exception_vmexit.pending = false;
 }
 
@@ -504,7 +505,7 @@ void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code);
 void kvm_queue_exception_p(struct kvm_vcpu *vcpu, unsigned nr, unsigned long payload);
 void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr,
 			   bool has_error_code, u32 error_code,
-			   bool is_nested);
+			   bool is_nested, u64 event_data);
 void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault,
 			   bool from_hardware);
 void __kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu,
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 15/28] KVM: x86: Include CR4.FRED in the emulator CR4 write mask
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (13 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 14/28] KVM: VMX: Virtualize FRED event_data Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 16/28] KVM: x86: Mark CR4.FRED as not reserved Sohil Mehta
                   ` (12 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: Xin Li <xin@zytor.com>

Add CR4.FRED to the emulator's CR4 write mask.  Historically, CR4 was
restricted to its lower 32 bits in the x86 architecture.  With the
introduction of FRED, CR4 is expanded to use its higher 32 bits to
accommodate the FRED enablement bit at position 32.  Update the mask
to ensure the emulator correctly handles FRED initialization and
prevents the inadvertent truncation of high-order bits.

Signed-off-by: Xin Li <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - New patch
---
 arch/x86/kvm/regs.h |  2 +-
 arch/x86/kvm/x86.c  | 12 +++++++++---
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/arch/x86/kvm/regs.h b/arch/x86/kvm/regs.h
index 4e641a6c20f9..ef46918daf43 100644
--- a/arch/x86/kvm/regs.h
+++ b/arch/x86/kvm/regs.h
@@ -8,7 +8,7 @@
 #define KVM_POSSIBLE_CR4_GUEST_BITS				  \
 	(X86_CR4_PVI | X86_CR4_DE | X86_CR4_PCE | X86_CR4_OSFXSR  \
 	 | X86_CR4_OSXMMEXCPT | X86_CR4_PGE | X86_CR4_TSD | X86_CR4_FSGSBASE \
-	 | X86_CR4_CET)
+	 | X86_CR4_CET | X86_CR4_FRED)
 
 #define X86_CR0_PDPTR_BITS    (X86_CR0_CD | X86_CR0_NW | X86_CR0_PG)
 #define X86_CR4_TLBFLUSH_BITS (X86_CR4_PGE | X86_CR4_PCIDE | X86_CR4_PAE | X86_CR4_SMEP)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index e10ac6b280f6..f264bc9c35f9 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -5552,11 +5552,17 @@ static int emulator_set_dr(struct x86_emulate_ctxt *ctxt, int dr,
 	return kvm_set_dr(emul_to_vcpu(ctxt), dr, value);
 }
 
-static u64 mk_cr_64(u64 curr_cr, u32 new_val)
+static u64 mk_cr0_64(u64 curr_cr, u32 new_val)
 {
 	return (curr_cr & ~((1ULL << 32) - 1)) | new_val;
 }
 
+static u64 mk_cr4_64(struct kvm_vcpu *vcpu, u64 curr_cr, u64 new_val)
+{
+	u32 shift = guest_cpu_cap_has(vcpu, X86_FEATURE_FRED) ? 33 : 32;
+	return (curr_cr & ~((1ULL << shift) - 1)) | new_val;
+}
+
 static unsigned long emulator_get_cr(struct x86_emulate_ctxt *ctxt, int cr)
 {
 	struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt);
@@ -5593,7 +5599,7 @@ static int emulator_set_cr(struct x86_emulate_ctxt *ctxt, int cr, ulong val)
 
 	switch (cr) {
 	case 0:
-		res = kvm_set_cr0(vcpu, mk_cr_64(kvm_read_cr0(vcpu), val));
+		res = kvm_set_cr0(vcpu, mk_cr0_64(kvm_read_cr0(vcpu), val));
 		break;
 	case 2:
 		vcpu->arch.cr2 = val;
@@ -5602,7 +5608,7 @@ static int emulator_set_cr(struct x86_emulate_ctxt *ctxt, int cr, ulong val)
 		res = kvm_set_cr3(vcpu, val);
 		break;
 	case 4:
-		res = kvm_set_cr4(vcpu, mk_cr_64(kvm_read_cr4(vcpu), val));
+		res = kvm_set_cr4(vcpu, mk_cr4_64(vcpu, kvm_read_cr4(vcpu), val));
 		break;
 	case 8:
 		res = kvm_set_cr8(vcpu, val);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 16/28] KVM: x86: Mark CR4.FRED as not reserved
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (14 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 15/28] KVM: x86: Include CR4.FRED in the emulator CR4 write mask Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 17/28] KVM: x86: Handle CR4.FRED when emulating RSM Sohil Mehta
                   ` (11 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

The CR4.FRED bit, i.e., CR4[32], is no longer a reserved bit when
guest cpu cap has FRED, i.e.,
  1) All of FRED KVM support is in place.
  2) Guest enumerates FRED.

Otherwise it is still a reserved bit.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - Check CR4.FRED against IA-32e mode in kvm_set_cr4() instead of
   __kvm_is_valid_cr4(), so that KVM_SET_SREGS no longer rejects a CR4
   and EFER pair that are loaded together (e.g., when restoring a FRED
   guest)
---
 arch/x86/kvm/regs.c | 7 +++++++
 arch/x86/kvm/regs.h | 4 +++-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kvm/regs.c b/arch/x86/kvm/regs.c
index 8f66438989e4..ba6c8856b4ff 100644
--- a/arch/x86/kvm/regs.c
+++ b/arch/x86/kvm/regs.c
@@ -429,6 +429,13 @@ int kvm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
 			return 1;
 	}
 
+	/*
+	 * FRED can not be enabled when EFER.LMA=0.  Note, MOV to CR4 outside
+	 * 64-bit mode clears CR4[63:32] so only emulation can get here.
+	 */
+	if ((cr4 & X86_CR4_FRED) && !is_long_mode(vcpu))
+		return 1;
+
 	if ((cr4 & X86_CR4_CET) && !kvm_is_cr0_bit_set(vcpu, X86_CR0_WP))
 		return 1;
 
diff --git a/arch/x86/kvm/regs.h b/arch/x86/kvm/regs.h
index ef46918daf43..5db01693ad07 100644
--- a/arch/x86/kvm/regs.h
+++ b/arch/x86/kvm/regs.h
@@ -28,7 +28,7 @@ static_assert(!(KVM_POSSIBLE_CR0_GUEST_BITS & X86_CR0_PDPTR_BITS));
 			  | X86_CR4_OSXSAVE | X86_CR4_SMEP | X86_CR4_FSGSBASE \
 			  | X86_CR4_OSXMMEXCPT | X86_CR4_LA57 | X86_CR4_VMXE \
 			  | X86_CR4_SMAP | X86_CR4_PKE | X86_CR4_UMIP \
-			  | X86_CR4_LAM_SUP | X86_CR4_CET))
+			  | X86_CR4_LAM_SUP | X86_CR4_CET | X86_CR4_FRED))
 
 #define CR8_RESERVED_BITS (~(unsigned long)X86_CR8_TPR)
 
@@ -423,6 +423,8 @@ static inline bool __kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
 	if (!__cpu_has(__c, X86_FEATURE_SHSTK) &&       \
 	    !__cpu_has(__c, X86_FEATURE_IBT))           \
 		__reserved_bits |= X86_CR4_CET;         \
+	if (!__cpu_has(__c, X86_FEATURE_FRED))          \
+		__reserved_bits |= X86_CR4_FRED;        \
 	__reserved_bits;                                \
 })
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 17/28] KVM: x86: Handle CR4.FRED when emulating RSM
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (15 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 16/28] KVM: x86: Mark CR4.FRED as not reserved Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 18/28] KVM: VMX: Dump FRED context in dump_vmcs() Sohil Mehta
                   ` (10 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

Clear CR4.FRED before CR0.PG when leaving long mode on RSM, and restore
it only after CR0.PG has been set, the same way CR4.PCIDE is already
handled in both directions.

SMI delivery saves all 64 bits of CR4 and RSM restores them so CR4.FRED
survives SMM. But KVM emulates RSM as a series of individual register
writes, and so drives the vCPU through an intermediate state with
CR4.FRED set and EFER.LMA clear. kvm_set_cr4() rejects that combination,
so RSM of a 64-bit FRED guest fails with X86EMUL_UNHANDLEABLE.

Note, SMI delivery needs no such handling, as enter_smm() clears CR4 in
full via the vendor callback rather than kvm_set_cr4().

Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - New patch
---
 arch/x86/kvm/smm.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/arch/x86/kvm/smm.c b/arch/x86/kvm/smm.c
index 656a38dad7e7..a504e93fc5f5 100644
--- a/arch/x86/kvm/smm.c
+++ b/arch/x86/kvm/smm.c
@@ -435,10 +435,10 @@ static int rsm_enter_protected_mode(struct kvm_vcpu *vcpu,
 
 	/*
 	 * First enable PAE, long mode needs it before CR0.PG = 1 is set.
-	 * Then enable protected mode.	However, PCID cannot be enabled
-	 * if EFER.LMA=0, so set it separately.
+	 * Then enable protected mode. However, PCID and FRED cannot be
+	 * enabled if EFER.LMA=0, so set them separately.
 	 */
-	bad = kvm_set_cr4(vcpu, cr4 & ~X86_CR4_PCIDE);
+	bad = kvm_set_cr4(vcpu, cr4 & ~(X86_CR4_PCIDE | X86_CR4_FRED));
 	if (bad)
 		return X86EMUL_UNHANDLEABLE;
 
@@ -446,7 +446,7 @@ static int rsm_enter_protected_mode(struct kvm_vcpu *vcpu,
 	if (bad)
 		return X86EMUL_UNHANDLEABLE;
 
-	if (cr4 & X86_CR4_PCIDE) {
+	if (cr4 & (X86_CR4_PCIDE | X86_CR4_FRED)) {
 		bad = kvm_set_cr4(vcpu, cr4);
 		if (bad)
 			return X86EMUL_UNHANDLEABLE;
@@ -599,10 +599,14 @@ int emulator_leave_smm(struct x86_emulate_ctxt *ctxt)
 		struct kvm_segment cs_desc;
 		unsigned long cr4;
 
-		/* Zero CR4.PCIDE before CR0.PG.  */
+		/*
+		 * Zero CR4.PCIDE and CR4.FRED before CR0.PG, as neither can
+		 * be set while EFER.LMA is 0, and clearing CR0.PG clears
+		 * EFER.LMA.
+		 */
 		cr4 = kvm_read_cr4(vcpu);
-		if (cr4 & X86_CR4_PCIDE)
-			kvm_set_cr4(vcpu, cr4 & ~X86_CR4_PCIDE);
+		if (cr4 & (X86_CR4_PCIDE | X86_CR4_FRED))
+			kvm_set_cr4(vcpu, cr4 & ~(X86_CR4_PCIDE | X86_CR4_FRED));
 
 		/* A 32-bit code segment is required to clear EFER.LMA.  */
 		memset(&cs_desc, 0, sizeof(cs_desc));
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 18/28] KVM: VMX: Dump FRED context in dump_vmcs()
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (16 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 17/28] KVM: x86: Handle CR4.FRED when emulating RSM Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 19/28] KVM: x86: Advertise support for FRED Sohil Mehta
                   ` (9 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Add FRED related VMCS fields to dump_vmcs() to dump FRED context.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - Test the secondary VM-exit controls in the dump_vmcs()
---
 arch/x86/kvm/vmx/vmx.c | 43 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 36 insertions(+), 7 deletions(-)

diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index cd041381b322..3a3315a7fc11 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -1501,6 +1501,9 @@ static void vmx_write_guest_fred_rsp0(struct vcpu_vmx *vmx, u64 data)
 	vmx_write_guest_host_msr(vmx, MSR_IA32_FRED_RSP0, data,
 				 &vmx->msr_guest_fred_rsp0);
 }
+#else
+/* Make sure it builds on 32-bit */
+static u64 vmx_read_guest_fred_rsp0(struct vcpu_vmx *vmx) { return 0; }
 #endif
 
 static void grow_ple_window(struct kvm_vcpu *vcpu)
@@ -6727,7 +6730,7 @@ void dump_vmcs(struct kvm_vcpu *vcpu)
 	struct vcpu_vmx *vmx = to_vmx(vcpu);
 	u32 vmentry_ctl, vmexit_ctl;
 	u32 cpu_based_exec_ctrl, pin_based_exec_ctrl, secondary_exec_control;
-	u64 tertiary_exec_control;
+	u64 tertiary_exec_control, secondary_vmexit_ctl;
 	unsigned long cr4;
 	int efer_slot;
 
@@ -6738,6 +6741,8 @@ void dump_vmcs(struct kvm_vcpu *vcpu)
 
 	vmentry_ctl = vmcs_read32(VM_ENTRY_CONTROLS);
 	vmexit_ctl = vmcs_read32(VM_EXIT_CONTROLS);
+	secondary_vmexit_ctl = cpu_has_secondary_vmexit_ctrls() ?
+			       vmcs_read64(SECONDARY_VM_EXIT_CONTROLS) : 0;
 	cpu_based_exec_ctrl = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);
 	pin_based_exec_ctrl = vmcs_read32(PIN_BASED_VM_EXEC_CONTROL);
 	cr4 = vmcs_readl(GUEST_CR4);
@@ -6784,6 +6789,16 @@ void dump_vmcs(struct kvm_vcpu *vcpu)
 	vmx_dump_sel("LDTR:", GUEST_LDTR_SELECTOR);
 	vmx_dump_dtsel("IDTR:", GUEST_IDTR_LIMIT);
 	vmx_dump_sel("TR:  ", GUEST_TR_SELECTOR);
+	if (vmentry_ctl & VM_ENTRY_LOAD_IA32_FRED)
+		pr_err("FRED guest: config=0x%016llx, stack_levels=0x%016llx\n"
+		       "RSP0=0x%016llx, RSP1=0x%016llx\n"
+		       "RSP2=0x%016llx, RSP3=0x%016llx\n",
+		       vmcs_read64(GUEST_IA32_FRED_CONFIG),
+		       vmcs_read64(GUEST_IA32_FRED_STKLVLS),
+		       vmx_read_guest_fred_rsp0(vmx),
+		       vmcs_read64(GUEST_IA32_FRED_RSP1),
+		       vmcs_read64(GUEST_IA32_FRED_RSP2),
+		       vmcs_read64(GUEST_IA32_FRED_RSP3));
 	efer_slot = vmx_find_loadstore_msr_slot(&vmx->msr_autoload.guest, MSR_EFER);
 	if (vmentry_ctl & VM_ENTRY_LOAD_IA32_EFER)
 		pr_err("EFER= 0x%016llx\n", vmcs_read64(GUEST_IA32_EFER));
@@ -6835,6 +6850,16 @@ void dump_vmcs(struct kvm_vcpu *vcpu)
 	       vmcs_readl(HOST_TR_BASE));
 	pr_err("GDTBase=%016lx IDTBase=%016lx\n",
 	       vmcs_readl(HOST_GDTR_BASE), vmcs_readl(HOST_IDTR_BASE));
+	if (secondary_vmexit_ctl & SECONDARY_VM_EXIT_LOAD_IA32_FRED)
+		pr_err("FRED host: config=0x%016llx, stack_levels=0x%016llx\n"
+		       "RSP0=0x%016lx, RSP1=0x%016llx\n"
+		       "RSP2=0x%016llx, RSP3=0x%016llx\n",
+		       vmcs_read64(HOST_IA32_FRED_CONFIG),
+		       vmcs_read64(HOST_IA32_FRED_STKLVLS),
+		       (unsigned long)task_stack_page(current) + THREAD_SIZE,
+		       vmcs_read64(HOST_IA32_FRED_RSP1),
+		       vmcs_read64(HOST_IA32_FRED_RSP2),
+		       vmcs_read64(HOST_IA32_FRED_RSP3));
 	pr_err("CR0=%016lx CR3=%016lx CR4=%016lx\n",
 	       vmcs_readl(HOST_CR0), vmcs_readl(HOST_CR3),
 	       vmcs_readl(HOST_CR4));
@@ -6860,25 +6885,29 @@ void dump_vmcs(struct kvm_vcpu *vcpu)
 	pr_err("*** Control State ***\n");
 	pr_err("CPUBased=0x%08x SecondaryExec=0x%08x TertiaryExec=0x%016llx\n",
 	       cpu_based_exec_ctrl, secondary_exec_control, tertiary_exec_control);
-	pr_err("PinBased=0x%08x EntryControls=%08x ExitControls=%08x\n",
-	       pin_based_exec_ctrl, vmentry_ctl, vmexit_ctl);
+	pr_err("PinBased=0x%08x EntryControls=0x%08x\n",
+	       pin_based_exec_ctrl, vmentry_ctl);
+	pr_err("ExitControls=0x%08x SecondaryExitControls=0x%016llx\n",
+	       vmexit_ctl, secondary_vmexit_ctl);
 	pr_err("ExceptionBitmap=%08x PFECmask=%08x PFECmatch=%08x\n",
 	       vmcs_read32(EXCEPTION_BITMAP),
 	       vmcs_read32(PAGE_FAULT_ERROR_CODE_MASK),
 	       vmcs_read32(PAGE_FAULT_ERROR_CODE_MATCH));
-	pr_err("VMEntry: intr_info=%08x errcode=%08x ilen=%08x\n",
+	pr_err("VMEntry: intr_info=%08x errcode=%08x ilen=%08x event_data=%016llx\n",
 	       vmcs_read32(VM_ENTRY_INTR_INFO_FIELD),
 	       vmcs_read32(VM_ENTRY_EXCEPTION_ERROR_CODE),
-	       vmcs_read32(VM_ENTRY_INSTRUCTION_LEN));
+	       vmcs_read32(VM_ENTRY_INSTRUCTION_LEN),
+	       kvm_cpu_cap_has(X86_FEATURE_FRED) ? vmcs_read64(INJECTED_EVENT_DATA) : 0);
 	pr_err("VMExit: intr_info=%08x errcode=%08x ilen=%08x\n",
 	       vmcs_read32(VM_EXIT_INTR_INFO),
 	       vmcs_read32(VM_EXIT_INTR_ERROR_CODE),
 	       vmcs_read32(VM_EXIT_INSTRUCTION_LEN));
 	pr_err("        reason=%08x qualification=%016lx\n",
 	       vmcs_read32(VM_EXIT_REASON), vmcs_readl(EXIT_QUALIFICATION));
-	pr_err("IDTVectoring: info=%08x errcode=%08x\n",
+	pr_err("IDTVectoring: info=%08x errcode=%08x event_data=%016llx\n",
 	       vmcs_read32(IDT_VECTORING_INFO_FIELD),
-	       vmcs_read32(IDT_VECTORING_ERROR_CODE));
+	       vmcs_read32(IDT_VECTORING_ERROR_CODE),
+	       kvm_cpu_cap_has(X86_FEATURE_FRED) ? vmcs_read64(ORIGINAL_EVENT_DATA) : 0);
 	pr_err("TSC Offset = 0x%016llx\n", vmcs_read64(TSC_OFFSET));
 	if (secondary_exec_control & SECONDARY_EXEC_TSC_SCALING)
 		pr_err("TSC Multiplier = 0x%016llx\n",
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 19/28] KVM: x86: Advertise support for FRED
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (17 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 18/28] KVM: VMX: Dump FRED context in dump_vmcs() Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 20/28] KVM: nVMX: Enable support for secondary VM exit controls Sohil Mehta
                   ` (8 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Advertise support for FRED to userspace after changes required to enable
FRED in a KVM guest are in place.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
Reviewed-by: Chao Gao <chao.gao@intel.com>
---
v10:
 - kvm_cpu_cap_clear(X86_FEATURE_FRED) in svm_set_cpu_caps() (Chao).
---
 arch/x86/kvm/cpuid.c   | 1 +
 arch/x86/kvm/svm/svm.c | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index ddb022cb203a..5682bacdfd32 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -1034,6 +1034,7 @@ void kvm_initialize_cpu_caps(void)
 		F(FSRS),
 		F(FSRC),
 		F(WRMSRNS),
+		X86_64_F(FRED),
 		X86_64_F(LKGS),
 		F(AMX_FP16),
 		F(AVX_IFMA),
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index f254a5ff570d..dd19c7b4e904 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5518,6 +5518,9 @@ static __init void svm_set_cpu_caps(void)
 
 	kvm_cpu_cap_clear(X86_FEATURE_IBT);
 
+	/* SVM FRED virtualization not implemented yet */
+	kvm_cpu_cap_clear(X86_FEATURE_FRED);
+
 	/* CPUID 0x80000001 and 0x8000000A (SVM features) */
 	if (nested) {
 		kvm_cpu_cap_set(X86_FEATURE_SVM);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 20/28] KVM: nVMX: Enable support for secondary VM exit controls
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (18 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 19/28] KVM: x86: Advertise support for FRED Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 21/28] KVM: nVMX: Handle FRED VMCS fields in nested VMX context Sohil Mehta
                   ` (7 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

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

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
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


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 21/28] KVM: nVMX: Handle FRED VMCS fields in nested VMX context
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (19 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 20/28] KVM: nVMX: Enable support for secondary VM exit controls Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 22/28] KVM: nVMX: Restrict event data VMCS fields to FRED-supported hosts Sohil Mehta
                   ` (6 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Extend nested VMX context management to include FRED-related VMCS fields,
enabling proper handling of FRED state during nested virtualization.

Because KVM always sets SECONDARY_VM_EXIT_SAVE_IA32_FRED, FRED MSRs are
always saved to vmcs02.  However an L1 VMM may choose to clear this bit,
i.e., not to save FRED MSRs to vmcs12.  This is not a problem when the L1
VMM sets SECONDARY_VM_EXIT_LOAD_IA32_FRED, as KVM then immediately loads
host FRED MSRs of vmcs12 to guest FRED MSRs of vmcs01.  However if the L1
VMM clears SECONDARY_VM_EXIT_LOAD_IA32_FRED, KVM should retain FRED MSRs
to run the L1 VMM.

To propagate guest FRED MSRs from vmcs02 to vmcs01, save them in
sync_vmcs02_to_vmcs12() regardless of whether
SECONDARY_VM_EXIT_SAVE_IA32_FRED is set in vmcs12.  Then, use the saved
values to set guest FRED MSRs in vmcs01 within load_vmcs12_host_state()
when !nested_cpu_load_host_fred_state().

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - Simplify nested FRED MSRs update with read/write helpers (Chao).
 - Add other FRED MSRS to MSR bitmap nested filtering (Chao).
 - Add {ORIGINAL,INJECTED}_EVENT_DATA to shadow fields in a separate
   patch (Sean).
 - Add a new parameter to load_vmcs12_host_state() to indicate the call
   is from a failed VM entry.
 - Update exception status member variable nested to is_nested (Sean).

v9:
 - Rebase to kvm-x86/next.
 - Guard FRED state save/restore with guest_cpu_cap_has(vcpu,
   X86_FEATURE_FRED) (syzbot & Chao).
---
 arch/x86/kvm/vmx/capabilities.h |   5 ++
 arch/x86/kvm/vmx/nested.c       | 107 ++++++++++++++++++++++++++++++--
 arch/x86/kvm/vmx/nested.h       |  17 +++++
 arch/x86/kvm/vmx/vmcs.h         |  11 ++++
 arch/x86/kvm/vmx/vmcs12.c       |  18 ++++++
 arch/x86/kvm/vmx/vmcs12.h       |  39 ++++++++++++
 arch/x86/kvm/vmx/vmx.h          |  23 +++++++
 7 files changed, 214 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h
index 9cca65b226fd..74456f4533e9 100644
--- a/arch/x86/kvm/vmx/capabilities.h
+++ b/arch/x86/kvm/vmx/capabilities.h
@@ -82,6 +82,11 @@ static inline bool cpu_has_vmx_basic_no_hw_errcode_cc(void)
 	return	vmcs_config.basic & VMX_BASIC_NO_HW_ERROR_CODE_CC;
 }
 
+static inline bool cpu_has_vmx_nested_exception(void)
+{
+	return vmcs_config.basic & VMX_BASIC_NESTED_EXCEPTION;
+}
+
 static inline bool cpu_has_virtual_nmis(void)
 {
 	return vmcs_config.pin_based_exec_ctrl & PIN_BASED_VIRTUAL_NMIS &&
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 6576935b9d43..83e846fb3fc8 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -812,6 +812,15 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu,
 	nested_vmx_merge_msr_bitmaps_rw(MSR_FS_BASE);
 	nested_vmx_merge_msr_bitmaps_rw(MSR_GS_BASE);
 	nested_vmx_merge_msr_bitmaps_rw(MSR_KERNEL_GS_BASE);
+	nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_FRED_RSP0);
+	nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_FRED_RSP1);
+	nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_FRED_RSP2);
+	nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_FRED_RSP3);
+	nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_FRED_STKLVLS);
+	nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_FRED_SSP1);
+	nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_FRED_SSP2);
+	nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_FRED_SSP3);
+	nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_FRED_CONFIG);
 #endif
 	nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_SPEC_CTRL);
 	nested_vmx_merge_msr_bitmaps_write(MSR_IA32_PRED_CMD);
@@ -1356,9 +1365,11 @@ static int vmx_restore_vmx_basic(struct vcpu_vmx *vmx, u64 data)
 	const u64 feature_bits = VMX_BASIC_DUAL_MONITOR_TREATMENT |
 				 VMX_BASIC_INOUT |
 				 VMX_BASIC_TRUE_CTLS |
-				 VMX_BASIC_NO_HW_ERROR_CODE_CC;
+				 VMX_BASIC_NO_HW_ERROR_CODE_CC |
+				 VMX_BASIC_NESTED_EXCEPTION;
 
-	const u64 reserved_bits = GENMASK_ULL(63, 57) |
+	const u64 reserved_bits = GENMASK_ULL(63, 59) |
+				  BIT_ULL(57) |
 				  GENMASK_ULL(47, 45) |
 				  BIT_ULL(31);
 
@@ -2599,6 +2610,8 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0
 			     vmcs12->vm_entry_instruction_len);
 		vmcs_write32(GUEST_INTERRUPTIBILITY_INFO,
 			     vmcs12->guest_interruptibility_info);
+		if (cpu_has_vmx_fred())
+			vmcs_write64(INJECTED_EVENT_DATA, vmcs12->injected_event_data);
 		vmx->loaded_vmcs->nmi_known_unmasked =
 			!(vmcs12->guest_interruptibility_info & GUEST_INTR_STATE_NMI);
 	} else {
@@ -2632,6 +2645,30 @@ static void vmcs_write_cet_state(struct kvm_vcpu *vcpu, u64 s_cet,
 	}
 }
 
+static void vmcs_read_fred_msrs(struct vmcs_fred_msrs *msrs)
+{
+	msrs->fred_config = vmcs_read64(GUEST_IA32_FRED_CONFIG);
+	msrs->fred_rsp1 = vmcs_read64(GUEST_IA32_FRED_RSP1);
+	msrs->fred_rsp2 = vmcs_read64(GUEST_IA32_FRED_RSP2);
+	msrs->fred_rsp3 = vmcs_read64(GUEST_IA32_FRED_RSP3);
+	msrs->fred_stklvls = vmcs_read64(GUEST_IA32_FRED_STKLVLS);
+	msrs->fred_ssp1 = vmcs_read64(GUEST_IA32_FRED_SSP1);
+	msrs->fred_ssp2 = vmcs_read64(GUEST_IA32_FRED_SSP2);
+	msrs->fred_ssp3 = vmcs_read64(GUEST_IA32_FRED_SSP3);
+}
+
+static void vmcs_write_fred_msrs(struct vmcs_fred_msrs *msrs)
+{
+	vmcs_write64(GUEST_IA32_FRED_CONFIG, msrs->fred_config);
+	vmcs_write64(GUEST_IA32_FRED_RSP1, msrs->fred_rsp1);
+	vmcs_write64(GUEST_IA32_FRED_RSP2, msrs->fred_rsp2);
+	vmcs_write64(GUEST_IA32_FRED_RSP3, msrs->fred_rsp3);
+	vmcs_write64(GUEST_IA32_FRED_STKLVLS, msrs->fred_stklvls);
+	vmcs_write64(GUEST_IA32_FRED_SSP1, msrs->fred_ssp1);
+	vmcs_write64(GUEST_IA32_FRED_SSP2, msrs->fred_ssp2);
+	vmcs_write64(GUEST_IA32_FRED_SSP3, msrs->fred_ssp3);
+}
+
 static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12)
 {
 	struct hv_enlightened_vmcs *hv_evmcs = nested_vmx_evmcs(vmx);
@@ -2755,6 +2792,10 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12)
 				     vmcs12->guest_ssp, vmcs12->guest_ssp_tbl);
 
 	set_cr4_guest_host_mask(vmx);
+
+	if (guest_cpu_cap_has(&vmx->vcpu, X86_FEATURE_FRED) &&
+	    nested_cpu_load_guest_fred_state(vmcs12))
+		vmcs_write_fred_msrs(&vmcs12->guest_fred_msrs);
 }
 
 /*
@@ -2821,6 +2862,10 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,
 		vmcs_write64(GUEST_IA32_PAT, vcpu->arch.pat);
 	}
 
+	if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED) &&
+	    (!vmx->vcpu.arch.nested_run_pending || !nested_cpu_load_guest_fred_state(vmcs12)))
+		vmcs_write_fred_msrs(&vmx->nested.pre_vmenter_fred_msrs);
+
 	vcpu->arch.tsc_offset = kvm_calc_nested_tsc_offset(
 			vcpu->arch.l1_tsc_offset,
 			vmx_get_l2_tsc_offset(vcpu),
@@ -3630,7 +3675,8 @@ static int nested_vmx_check_permission(struct kvm_vcpu *vcpu)
 }
 
 static void load_vmcs12_host_state(struct kvm_vcpu *vcpu,
-				   struct vmcs12 *vmcs12);
+				   struct vmcs12 *vmcs12,
+				   bool from_failed_vmentry);
 
 /*
  * If from_vmentry is false, this is being called from state restore (either RSM
@@ -3680,6 +3726,10 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
 				    &vmx->nested.pre_vmenter_ssp,
 				    &vmx->nested.pre_vmenter_ssp_tbl);
 
+	if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED) &&
+	    (!vmx->vcpu.arch.nested_run_pending || !nested_cpu_load_guest_fred_state(vmcs12)))
+		vmcs_read_fred_msrs(&vmx->nested.pre_vmenter_fred_msrs);
+
 	/*
 	 * Stash L1's CR3, so that in the event of a "late" VM-Fail, i.e. a
 	 * VM-Fail detected by hardware but not KVM, KVM can unwind its
@@ -3794,7 +3844,7 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
 
 	nested_put_vmcs12_pages(vcpu);
 
-	load_vmcs12_host_state(vcpu, vmcs12);
+	load_vmcs12_host_state(vcpu, vmcs12, true);
 	vmcs12->vm_exit_reason = exit_reason.full;
 	if (enable_shadow_vmcs || nested_vmx_is_evmptr12_valid(vmx))
 		vmx->nested.need_vmcs12_to_shadow_sync = true;
@@ -3983,6 +4033,8 @@ static void vmcs12_save_pending_event(struct kvm_vcpu *vcpu,
 	u32 idt_vectoring;
 	unsigned int nr;
 
+	vmcs12->original_event_data = 0;
+
 	/*
 	 * Per the SDM, VM-Exits due to double and triple faults are never
 	 * considered to occur during event delivery, even if the double/triple
@@ -4021,6 +4073,13 @@ static void vmcs12_save_pending_event(struct kvm_vcpu *vcpu,
 				vcpu->arch.exception.error_code;
 		}
 
+		if ((vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) &&
+		    (vmcs12->guest_cr4 & X86_CR4_FRED) &&
+		    (vcpu->arch.exception.is_nested))
+			idt_vectoring |= VECTORING_INFO_NESTED_EXCEPTION_MASK;
+
+		vmcs12->original_event_data = vcpu->arch.exception.event_data;
+
 		vmcs12->idt_vectoring_info_field = idt_vectoring;
 	} else if (vcpu->arch.nmi_injected) {
 		vmcs12->idt_vectoring_info_field =
@@ -4744,6 +4803,15 @@ static void sync_vmcs02_to_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
 	vmcs_read_cet_state(&vmx->vcpu, &vmcs12->guest_s_cet,
 			    &vmcs12->guest_ssp,
 			    &vmcs12->guest_ssp_tbl);
+
+	if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) {
+		vmcs_read_fred_msrs(&vmx->nested.at_vmexit_fred_msrs);
+
+		if (nested_cpu_save_guest_fred_state(vmcs12))
+			memcpy(&vmcs12->guest_fred_msrs,
+			       &vmx->nested.at_vmexit_fred_msrs,
+			       sizeof(struct vmcs_fred_msrs));
+	}
 }
 
 /*
@@ -4788,6 +4856,21 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,
 
 		vmcs12->vm_exit_intr_info = exit_intr_info;
 		vmcs12->vm_exit_instruction_len = exit_insn_len;
+
+		/*
+		 * When there is a valid original event, the exiting event is a nested
+		 * event during delivery of the earlier original event.
+		 *
+		 * FRED event delivery reflects this relationship by setting the value
+		 * of the nested exception bit of VM-exit interruption information
+		 * (aka exiting-event identification) to that of the valid bit of the
+		 * IDT-vectoring information (aka original-event identification).
+		 */
+		if ((vmcs12->idt_vectoring_info_field & VECTORING_INFO_VALID_MASK) &&
+		    (vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) &&
+		    (vmcs12->guest_cr4 & X86_CR4_FRED))
+			vmcs12->vm_exit_intr_info |= INTR_INFO_NESTED_EXCEPTION_MASK;
+
 		vmcs12->vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO);
 
 		/*
@@ -4814,8 +4897,10 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,
  * This function should be called when the active VMCS is L1's (vmcs01).
  */
 static void load_vmcs12_host_state(struct kvm_vcpu *vcpu,
-				   struct vmcs12 *vmcs12)
+				   struct vmcs12 *vmcs12,
+				   bool from_failed_vmentry)
 {
+	struct vcpu_vmx *vmx = to_vmx(vcpu);
 	enum vm_entry_failure_code ignored;
 	struct kvm_segment seg;
 
@@ -4890,6 +4975,14 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu,
 		WARN_ON_ONCE(__kvm_emulate_msr_write(vcpu, MSR_CORE_PERF_GLOBAL_CTRL,
 						     vmcs12->host_ia32_perf_global_ctrl));
 
+	if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) {
+		if (nested_cpu_load_host_fred_state(vmcs12)) {
+			vmcs_write_fred_msrs(&vmcs12->host_fred_msrs);
+		} else if (!from_failed_vmentry) {
+			vmcs_write_fred_msrs(&vmx->nested.at_vmexit_fred_msrs);
+		}
+	}
+
 	/* Set L1 segment info according to Intel SDM
 	    27.5.2 Loading Host Segment and Descriptor-Table Registers */
 	seg = (struct kvm_segment) {
@@ -5209,7 +5302,7 @@ void __nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason,
 						       vmcs12->vm_exit_intr_error_code,
 						       KVM_ISA_VMX);
 
-		load_vmcs12_host_state(vcpu, vmcs12);
+		load_vmcs12_host_state(vcpu, vmcs12, false);
 
 		/*
 		 * Process events if an injectable IRQ or NMI is pending, even
@@ -7370,6 +7463,8 @@ static void nested_vmx_setup_basic(struct nested_vmx_msrs *msrs)
 		msrs->basic |= VMX_BASIC_INOUT;
 	if (cpu_has_vmx_basic_no_hw_errcode_cc())
 		msrs->basic |= VMX_BASIC_NO_HW_ERROR_CODE_CC;
+	if (cpu_has_vmx_nested_exception())
+		msrs->basic |= VMX_BASIC_NESTED_EXCEPTION;
 }
 
 static void nested_vmx_setup_cr_fixed(struct nested_vmx_msrs *msrs)
diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h
index 371fc3498b12..dc5a1e9cb4e9 100644
--- a/arch/x86/kvm/vmx/nested.h
+++ b/arch/x86/kvm/vmx/nested.h
@@ -272,6 +272,23 @@ static inline bool nested_cpu_has_encls_exit(struct vmcs12 *vmcs12)
 	return nested_cpu_has2(vmcs12, SECONDARY_EXEC_ENCLS_EXITING);
 }
 
+static inline bool nested_cpu_load_guest_fred_state(struct vmcs12 *vmcs12)
+{
+	return vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_FRED;
+}
+
+static inline bool nested_cpu_save_guest_fred_state(struct vmcs12 *vmcs12)
+{
+	return nested_cpu_has_secondary_vm_exit_controls(vmcs12) &&
+	       vmcs12->secondary_vm_exit_controls & SECONDARY_VM_EXIT_SAVE_IA32_FRED;
+}
+
+static inline bool nested_cpu_load_host_fred_state(struct vmcs12 *vmcs12)
+{
+	return nested_cpu_has_secondary_vm_exit_controls(vmcs12) &&
+	       vmcs12->secondary_vm_exit_controls & SECONDARY_VM_EXIT_LOAD_IA32_FRED;
+}
+
 /*
  * if fixed0[i] == 1: val[i] must be 1
  * if fixed1[i] == 0: val[i] must be 0
diff --git a/arch/x86/kvm/vmx/vmcs.h b/arch/x86/kvm/vmx/vmcs.h
index ca341c59bab1..58af10f73789 100644
--- a/arch/x86/kvm/vmx/vmcs.h
+++ b/arch/x86/kvm/vmx/vmcs.h
@@ -52,6 +52,17 @@ struct vmcs_controls_shadow {
 	u64 tertiary_exec;
 };
 
+struct vmcs_fred_msrs {
+	u64 fred_config;
+	u64 fred_rsp1;
+	u64 fred_rsp2;
+	u64 fred_rsp3;
+	u64 fred_stklvls;
+	u64 fred_ssp1;
+	u64 fred_ssp2;
+	u64 fred_ssp3;
+};
+
 /*
  * Track a VMCS that may be loaded on a certain CPU. If it is (cpu!=-1), also
  * remember whether it was VMLAUNCHed, and maintain a linked list of all VMCSs
diff --git a/arch/x86/kvm/vmx/vmcs12.c b/arch/x86/kvm/vmx/vmcs12.c
index 9d64a89aff00..837cb3d568c3 100644
--- a/arch/x86/kvm/vmx/vmcs12.c
+++ b/arch/x86/kvm/vmx/vmcs12.c
@@ -67,6 +67,24 @@ static const u16 kvm_supported_vmcs12_field_offsets[] __initconst = {
 	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),
+	FIELD64(INJECTED_EVENT_DATA, injected_event_data),
+	FIELD64(ORIGINAL_EVENT_DATA, original_event_data),
+	FIELD64(GUEST_IA32_FRED_CONFIG, guest_ia32_fred_config),
+	FIELD64(GUEST_IA32_FRED_RSP1, guest_ia32_fred_rsp1),
+	FIELD64(GUEST_IA32_FRED_RSP2, guest_ia32_fred_rsp2),
+	FIELD64(GUEST_IA32_FRED_RSP3, guest_ia32_fred_rsp3),
+	FIELD64(GUEST_IA32_FRED_STKLVLS, guest_ia32_fred_stklvls),
+	FIELD64(GUEST_IA32_FRED_SSP1, guest_ia32_fred_ssp1),
+	FIELD64(GUEST_IA32_FRED_SSP2, guest_ia32_fred_ssp2),
+	FIELD64(GUEST_IA32_FRED_SSP3, guest_ia32_fred_ssp3),
+	FIELD64(HOST_IA32_FRED_CONFIG, host_ia32_fred_config),
+	FIELD64(HOST_IA32_FRED_RSP1, host_ia32_fred_rsp1),
+	FIELD64(HOST_IA32_FRED_RSP2, host_ia32_fred_rsp2),
+	FIELD64(HOST_IA32_FRED_RSP3, host_ia32_fred_rsp3),
+	FIELD64(HOST_IA32_FRED_STKLVLS, host_ia32_fred_stklvls),
+	FIELD64(HOST_IA32_FRED_SSP1, host_ia32_fred_ssp1),
+	FIELD64(HOST_IA32_FRED_SSP2, host_ia32_fred_ssp2),
+	FIELD64(HOST_IA32_FRED_SSP3, host_ia32_fred_ssp3),
 	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 bb2f406be63d..c4c5cd3722a5 100644
--- a/arch/x86/kvm/vmx/vmcs12.h
+++ b/arch/x86/kvm/vmx/vmcs12.h
@@ -191,6 +191,27 @@ struct __packed vmcs12 {
 	u16 host_gs_selector;
 	u16 host_tr_selector;
 	u16 guest_pml_index;
+	u16 padding16[1]; /* align to 64-bit boundary */
+	struct vmcs_fred_msrs guest_fred_msrs;
+#define guest_ia32_fred_config	guest_fred_msrs.fred_config
+#define guest_ia32_fred_rsp1	guest_fred_msrs.fred_rsp1
+#define guest_ia32_fred_rsp2	guest_fred_msrs.fred_rsp2
+#define guest_ia32_fred_rsp3	guest_fred_msrs.fred_rsp3
+#define guest_ia32_fred_stklvls	guest_fred_msrs.fred_stklvls
+#define guest_ia32_fred_ssp1	guest_fred_msrs.fred_ssp1
+#define guest_ia32_fred_ssp2	guest_fred_msrs.fred_ssp2
+#define guest_ia32_fred_ssp3	guest_fred_msrs.fred_ssp3
+	struct vmcs_fred_msrs host_fred_msrs;
+#define host_ia32_fred_config	host_fred_msrs.fred_config
+#define host_ia32_fred_rsp1	host_fred_msrs.fred_rsp1
+#define host_ia32_fred_rsp2	host_fred_msrs.fred_rsp2
+#define host_ia32_fred_rsp3	host_fred_msrs.fred_rsp3
+#define host_ia32_fred_stklvls	host_fred_msrs.fred_stklvls
+#define host_ia32_fred_ssp1	host_fred_msrs.fred_ssp1
+#define host_ia32_fred_ssp2	host_fred_msrs.fred_ssp2
+#define host_ia32_fred_ssp3	host_fred_msrs.fred_ssp3
+	u64 injected_event_data;
+	u64 original_event_data;
 };
 
 /*
@@ -373,6 +394,24 @@ 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(guest_ia32_fred_config, 1000);
+	CHECK_OFFSET(guest_ia32_fred_rsp1, 1008);
+	CHECK_OFFSET(guest_ia32_fred_rsp2, 1016);
+	CHECK_OFFSET(guest_ia32_fred_rsp3, 1024);
+	CHECK_OFFSET(guest_ia32_fred_stklvls, 1032);
+	CHECK_OFFSET(guest_ia32_fred_ssp1, 1040);
+	CHECK_OFFSET(guest_ia32_fred_ssp2, 1048);
+	CHECK_OFFSET(guest_ia32_fred_ssp3, 1056);
+	CHECK_OFFSET(host_ia32_fred_config, 1064);
+	CHECK_OFFSET(host_ia32_fred_rsp1, 1072);
+	CHECK_OFFSET(host_ia32_fred_rsp2, 1080);
+	CHECK_OFFSET(host_ia32_fred_rsp3, 1088);
+	CHECK_OFFSET(host_ia32_fred_stklvls, 1096);
+	CHECK_OFFSET(host_ia32_fred_ssp1, 1104);
+	CHECK_OFFSET(host_ia32_fred_ssp2, 1112);
+	CHECK_OFFSET(host_ia32_fred_ssp3, 1120);
+	CHECK_OFFSET(injected_event_data, 1128);
+	CHECK_OFFSET(original_event_data, 1136);
 }
 
 extern u16 vmcs12_field_offsets[] __ro_after_init;
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index 14fd3f57d5c1..cc1e4038cd44 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -182,6 +182,29 @@ struct nested_vmx {
 	u64 pre_vmenter_ssp;
 	u64 pre_vmenter_ssp_tbl;
 
+	/*
+	 * Used to snapshot FRED MSRs that may NOT be saved to vmcs12 as specified
+	 * in the VM-Exit controls of vmcs12 configured by L1 VMM.
+	 *
+	 * FRED MSRs are *always* saved into vmcs02 because KVM always sets
+	 * SECONDARY_VM_EXIT_SAVE_IA32_FRED.  However an L1 VMM may choose to clear
+	 * this bit, resulting in FRED MSRs not being propagated to vmcs12 from
+	 * vmcs02.  When the L1 VMM sets SECONDARY_VM_EXIT_LOAD_IA32_FRED, this is
+	 * not a problem, since KVM then immediately loads the host FRED MSRs of
+	 * vmcs12 to the guest FRED MSRs of vmcs01.
+	 *
+	 * But if the L1 VMM clears SECONDARY_VM_EXIT_LOAD_IA32_FRED, KVM should
+	 * retain the FRED MSRs, i.e., propagate the guest FRED MSRs of vmcs02 to
+	 * the guest FRED MSRs of vmcs01.
+	 *
+	 * This structure stores guest FRED MSRs that an L1 VMM opts not to save
+	 * during VM-Exits from L2 to L1.  These MSRs may still be retained for
+	 * running the L1 VMM if SECONDARY_VM_EXIT_LOAD_IA32_FRED is cleared in
+	 * vmcs12.
+	 */
+	struct vmcs_fred_msrs pre_vmenter_fred_msrs;
+	struct vmcs_fred_msrs at_vmexit_fred_msrs;
+
 	u16 vpid02;
 	u16 last_vpid;
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 22/28] KVM: nVMX: Restrict event data VMCS fields to FRED-supported hosts
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (20 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 21/28] KVM: nVMX: Handle FRED VMCS fields in nested VMX context Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 23/28] KVM: nVMX: Shadow ORIGINAL_EVENT_DATA and INJECTED_EVENT_DATA fields Sohil Mehta
                   ` (5 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: Xin Li <xin@zytor.com>

Prevent L1 from accessing the ORIGINAL_EVENT_DATA and INJECTED_EVENT_DATA
VMCS fields via VMREAD or VMWRITE when FRED support is disabled or not
present in KVM.

Signed-off-by: Xin Li <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - Split out of "KVM: nVMX: Guard SHADOW_FIELD_R[OW] macros with VMX
   feature checks" and renamed (Sean).
---
 arch/x86/kvm/vmx/vmcs12.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/arch/x86/kvm/vmx/vmcs12.c b/arch/x86/kvm/vmx/vmcs12.c
index 837cb3d568c3..67c730e31465 100644
--- a/arch/x86/kvm/vmx/vmcs12.c
+++ b/arch/x86/kvm/vmx/vmcs12.c
@@ -222,6 +222,9 @@ static __init bool cpu_has_vmcs12_field(unsigned int idx)
 	case HOST_SSP:
 	case HOST_INTR_SSP_TABLE:
 		return cpu_has_load_cet_ctrl();
+	VMCS12_CASE64(ORIGINAL_EVENT_DATA):
+	VMCS12_CASE64(INJECTED_EVENT_DATA):
+		return cpu_has_vmx_fred();
 
 	/* KVM always emulates PML and the VMX preemption timer in software. */
 	case GUEST_PML_INDEX:
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 23/28] KVM: nVMX: Shadow ORIGINAL_EVENT_DATA and INJECTED_EVENT_DATA fields
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (21 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 22/28] KVM: nVMX: Restrict event data VMCS fields to FRED-supported hosts Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 24/28] KVM: nVMX: Validate FRED-related VMCS fields Sohil Mehta
                   ` (4 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Add ORIGINAL_EVENT_DATA and INJECTED_EVENT_DATA to the list of shadowed
VMCS fields.

Shadowing these fields allows the L1 hypervisor to access them without
triggering a VM-exit to L0, optimizing performance for nested
virtualization workloads that frequently query or inject event data.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - Split out of "KVM: nVMX: Guard SHADOW_FIELD_R[OW] macros with VMX
   feature checks" (Sean).
---
 arch/x86/kvm/vmx/vmcs_shadow_fields.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/x86/kvm/vmx/vmcs_shadow_fields.h b/arch/x86/kvm/vmx/vmcs_shadow_fields.h
index 67e821c2be6d..bdd56f3ca6c3 100644
--- a/arch/x86/kvm/vmx/vmcs_shadow_fields.h
+++ b/arch/x86/kvm/vmx/vmcs_shadow_fields.h
@@ -74,6 +74,10 @@ SHADOW_FIELD_RW(HOST_GS_BASE, host_gs_base)
 /* 64-bit */
 SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS, guest_physical_address)
 SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS_HIGH, guest_physical_address)
+SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA, original_event_data)
+SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA_HIGH, original_event_data)
+SHADOW_FIELD_RW(INJECTED_EVENT_DATA, injected_event_data)
+SHADOW_FIELD_RW(INJECTED_EVENT_DATA_HIGH, injected_event_data)
 #endif
 
 #undef SHADOW_FIELD_RO
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 24/28] KVM: nVMX: Validate FRED-related VMCS fields
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (22 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 23/28] KVM: nVMX: Shadow ORIGINAL_EVENT_DATA and INJECTED_EVENT_DATA fields Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 25/28] KVM: nVMX: Enable VMX FRED controls Sohil Mehta
                   ` (3 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Extend nested VMX field validation to include FRED-specific VMCS fields,
mirroring hardware behavior.

This enables support for nested FRED by ensuring control and guest/host
state fields are properly checked.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - Use has_nested_exception to describe the event being injected rather
   than the CPU capability, for consistency with has_error_code (Chao).
 - Restructure the interruption-information checks into a switch on the
   interruption type (Chao).
 - Add a CC() statement to the reserved interruption type check so a
   VM-entry failure can be correlated with a specific check (Chao).
 - Reject the nested-exception bit (bit 13) for interruption types other
   than hardware exception, and fold in the nested-exception capability
   check.
 - Bound the "other event" vector explicitly instead of relying on a
   default arm.
 - Reject a zero VM-entry instruction length when injecting SYSCALL or
   SYSENTER if the vCPU doesn't enumerate zero-length instruction
   injection.
---
 arch/x86/kvm/vmx/nested.c | 119 +++++++++++++++++++++++++++++++++-----
 arch/x86/kvm/vmx/nested.h |   5 ++
 2 files changed, 111 insertions(+), 13 deletions(-)

diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 83e846fb3fc8..3e0a1ed6440d 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -3111,6 +3111,8 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu,
 					  struct vmcs12 *vmcs12)
 {
 	struct vcpu_vmx *vmx = to_vmx(vcpu);
+	bool fred_enabled = (vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) &&
+			    (vmcs12->guest_cr4 & X86_CR4_FRED);
 
 	if (CC(!vmx_control_verify(vmcs12->vm_entry_controls,
 				    vmx->nested.msrs.entry_ctls_low,
@@ -3128,22 +3130,11 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu,
 		u8 vector = intr_info & INTR_INFO_VECTOR_MASK;
 		u32 intr_type = intr_info & INTR_INFO_INTR_TYPE_MASK;
 		bool has_error_code = intr_info & INTR_INFO_DELIVER_CODE_MASK;
+		bool has_nested_exception = intr_info & INTR_INFO_NESTED_EXCEPTION_MASK;
 		bool urg = nested_cpu_has2(vmcs12,
 					   SECONDARY_EXEC_UNRESTRICTED_GUEST);
 		bool prot_mode = !urg || vmcs12->guest_cr0 & X86_CR0_PE;
 
-		/* VM-entry interruption-info field: interruption type */
-		if (CC(intr_type == INTR_TYPE_RESERVED) ||
-		    CC(intr_type == INTR_TYPE_OTHER_EVENT &&
-		       !nested_cpu_supports_monitor_trap_flag(vcpu)))
-			return -EINVAL;
-
-		/* VM-entry interruption-info field: vector */
-		if (CC(intr_type == INTR_TYPE_NMI_INTR && vector != NMI_VECTOR) ||
-		    CC(intr_type == INTR_TYPE_HARD_EXCEPTION && vector > 31) ||
-		    CC(intr_type == INTR_TYPE_OTHER_EVENT && vector != 0))
-			return -EINVAL;
-
 		/*
 		 * Cannot deliver error code in real mode or if the interrupt
 		 * type is not hardware exception. For other cases, do the
@@ -3167,8 +3158,28 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu,
 		if (CC(intr_info & INTR_INFO_RESVD_BITS_MASK))
 			return -EINVAL;
 
-		/* VM-entry instruction length */
+		if (CC(intr_type == INTR_TYPE_RESERVED))
+			return -EINVAL;
+
+		/*
+		 * Only for hardware exceptions and when the CPU enumerates
+		 * VMX nested-exception support, bit 13 (indicating a nested
+		 * exception) has value 1. Otherwise it is reserved.
+		 */
+		if (CC(has_nested_exception &&
+		       (intr_type != INTR_TYPE_HARD_EXCEPTION ||
+			!nested_cpu_has_nested_exception(vcpu))))
+			return -EINVAL;
+
 		switch (intr_type) {
+		case INTR_TYPE_NMI_INTR:
+			if (CC(vector != NMI_VECTOR))
+				return -EINVAL;
+			break;
+		case INTR_TYPE_HARD_EXCEPTION:
+			if (CC(vector > 31))
+				return -EINVAL;
+			break;
 		case INTR_TYPE_SOFT_EXCEPTION:
 		case INTR_TYPE_SOFT_INTR:
 		case INTR_TYPE_PRIV_SW_EXCEPTION:
@@ -3176,6 +3187,28 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu,
 			    CC(vmcs12->vm_entry_instruction_len == 0 &&
 			    CC(!nested_cpu_has_zero_length_injection(vcpu))))
 				return -EINVAL;
+			break;
+		case INTR_TYPE_OTHER_EVENT:
+			if (CC(vector > 2))
+				return -EINVAL;
+
+			switch (vector) {
+			case 0:
+				if (CC(!nested_cpu_supports_monitor_trap_flag(vcpu)))
+					return -EINVAL;
+				break;
+			case 1:
+			case 2:
+				if (CC(!fred_enabled))
+					return -EINVAL;
+				if (CC(vmcs12->vm_entry_instruction_len > X86_MAX_INSTRUCTION_LENGTH))
+					return -EINVAL;
+				if (CC(vmcs12->vm_entry_instruction_len == 0 &&
+				       !nested_cpu_has_zero_length_injection(vcpu)))
+					return -EINVAL;
+				break;
+			}
+			break;
 		}
 	}
 
@@ -3262,9 +3295,27 @@ static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu,
 	if (ia32e) {
 		if (CC(!(vmcs12->host_cr4 & X86_CR4_PAE)))
 			return -EINVAL;
+		if (nested_cpu_load_host_fred_state(vmcs12)) {
+			if (CC(vmcs12->host_ia32_fred_config & FRED_CONFIG_RESERVED) ||
+			    CC(vmcs12->host_ia32_fred_rsp1 & GENMASK_ULL(5, 0)) ||
+			    CC(vmcs12->host_ia32_fred_rsp2 & GENMASK_ULL(5, 0)) ||
+			    CC(vmcs12->host_ia32_fred_rsp3 & GENMASK_ULL(5, 0)) ||
+			    CC(vmcs12->host_ia32_fred_ssp1 & GENMASK_ULL(2, 0)) ||
+			    CC(vmcs12->host_ia32_fred_ssp2 & GENMASK_ULL(2, 0)) ||
+			    CC(vmcs12->host_ia32_fred_ssp3 & GENMASK_ULL(2, 0)) ||
+			    CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_config & PAGE_MASK, vcpu)) ||
+			    CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp1, vcpu)) ||
+			    CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp2, vcpu)) ||
+			    CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp3, vcpu)) ||
+			    CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp1, vcpu)) ||
+			    CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp2, vcpu)) ||
+			    CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp3, vcpu)))
+				return -EINVAL;
+		}
 	} else {
 		if (CC(vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) ||
 		    CC(vmcs12->host_cr4 & X86_CR4_PCIDE) ||
+		    CC(vmcs12->host_cr4 & X86_CR4_FRED) ||
 		    CC((vmcs12->host_rip) >> 32))
 			return -EINVAL;
 	}
@@ -3447,6 +3498,48 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu,
 	     CC((vmcs12->guest_bndcfgs & MSR_IA32_BNDCFGS_RSVD))))
 		return -EINVAL;
 
+	if (ia32e) {
+		if (nested_cpu_load_guest_fred_state(vmcs12)) {
+			if (CC(vmcs12->guest_ia32_fred_config & FRED_CONFIG_RESERVED) ||
+			    CC(vmcs12->guest_ia32_fred_rsp1 & GENMASK_ULL(5, 0)) ||
+			    CC(vmcs12->guest_ia32_fred_rsp2 & GENMASK_ULL(5, 0)) ||
+			    CC(vmcs12->guest_ia32_fred_rsp3 & GENMASK_ULL(5, 0)) ||
+			    CC(vmcs12->guest_ia32_fred_ssp1 & GENMASK_ULL(2, 0)) ||
+			    CC(vmcs12->guest_ia32_fred_ssp2 & GENMASK_ULL(2, 0)) ||
+			    CC(vmcs12->guest_ia32_fred_ssp3 & GENMASK_ULL(2, 0)) ||
+			    CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_config & PAGE_MASK, vcpu)) ||
+			    CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp1, vcpu)) ||
+			    CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp2, vcpu)) ||
+			    CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp3, vcpu)) ||
+			    CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp1, vcpu)) ||
+			    CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp2, vcpu)) ||
+			    CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp3, vcpu)))
+				return -EINVAL;
+		}
+		if (vmcs12->guest_cr4 & X86_CR4_FRED) {
+			unsigned int ss_dpl = VMX_AR_DPL(vmcs12->guest_ss_ar_bytes);
+
+			if (CC(ss_dpl == 1 || ss_dpl == 2))
+				return -EINVAL;
+
+			switch (ss_dpl) {
+			case 0:
+				if (CC(!(vmcs12->guest_cs_ar_bytes & VMX_AR_L_MASK)))
+					return -EINVAL;
+				break;
+			case 3:
+				if (CC(vmcs12->guest_rflags & X86_EFLAGS_IOPL))
+					return -EINVAL;
+				if (CC(vmcs12->guest_interruptibility_info & GUEST_INTR_STATE_STI))
+					return -EINVAL;
+				break;
+			}
+		}
+	} else {
+		if (CC(vmcs12->guest_cr4 & X86_CR4_FRED))
+			return -EINVAL;
+	}
+
 	if (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE) {
 		if (nested_vmx_check_cet_state_common(vcpu, vmcs12->guest_s_cet,
 						      vmcs12->guest_ssp,
diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h
index dc5a1e9cb4e9..551121824128 100644
--- a/arch/x86/kvm/vmx/nested.h
+++ b/arch/x86/kvm/vmx/nested.h
@@ -334,6 +334,11 @@ static inline bool nested_cpu_has_no_hw_errcode_cc(struct kvm_vcpu *vcpu)
 	return to_vmx(vcpu)->nested.msrs.basic & VMX_BASIC_NO_HW_ERROR_CODE_CC;
 }
 
+static inline bool nested_cpu_has_nested_exception(struct kvm_vcpu *vcpu)
+{
+	return to_vmx(vcpu)->nested.msrs.basic & VMX_BASIC_NESTED_EXCEPTION;
+}
+
 /* No difference in the restrictions on guest and host CR4 in VMX operation. */
 #define nested_guest_cr4_valid	nested_cr4_valid
 #define nested_host_cr4_valid	nested_cr4_valid
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 25/28] KVM: nVMX: Enable VMX FRED controls
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (23 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 24/28] KVM: nVMX: Validate FRED-related VMCS fields Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 26/28] KVM: selftests: Add FRED MSRs to msrs_test Sohil Mehta
                   ` (2 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Permit use of VMX FRED controls in nested VMX now that support for nested
FRED is implemented.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
Reviewed-by: Chao Gao <chao.gao@intel.com>
---
v10:
 - Remove an obsolete comment (Chao).
---
 arch/x86/kvm/vmx/nested.c | 10 +++-------
 arch/x86/kvm/vmx/vmx.c    |  1 +
 2 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 3e0a1ed6440d..4fad9728a2ea 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -7371,12 +7371,8 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf,
 
 	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;
+		msrs->secondary_exit_ctls &= SECONDARY_VM_EXIT_SAVE_IA32_FRED |
+					     SECONDARY_VM_EXIT_LOAD_IA32_FRED;
 	}
 }
 
@@ -7392,7 +7388,7 @@ static void nested_vmx_setup_entry_ctls(struct vmcs_config *vmcs_conf,
 		VM_ENTRY_IA32E_MODE |
 #endif
 		VM_ENTRY_LOAD_IA32_PAT | VM_ENTRY_LOAD_BNDCFGS |
-		VM_ENTRY_LOAD_CET_STATE;
+		VM_ENTRY_LOAD_CET_STATE | VM_ENTRY_LOAD_IA32_FRED;
 	msrs->entry_ctls_high |=
 		(VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR | VM_ENTRY_LOAD_IA32_EFER |
 		 VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL);
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 3a3315a7fc11..f673cd4e8956 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -8150,6 +8150,7 @@ static void nested_vmx_cr_fixed1_bits_update(struct kvm_vcpu *vcpu)
 
 	entry = kvm_find_cpuid_entry_index(vcpu, 0x7, 1);
 	cr4_fixed1_update(X86_CR4_LAM_SUP,    eax, feature_bit(LAM));
+	cr4_fixed1_update(X86_CR4_FRED,       eax, feature_bit(FRED));
 
 #undef cr4_fixed1_update
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 26/28] KVM: selftests: Add FRED MSRs to msrs_test
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (24 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 25/28] KVM: nVMX: Enable VMX FRED controls Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 27/28] KVM: selftests: Add a new VM guest mode to run user level code Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 28/28] KVM: selftests: Add fred exception tests Sohil Mehta
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: Xin Li <xin@zytor.com>

Add FRED MSR coverage to msrs_test, including the FRED RSP0-3, SSP0-3,
STKLVLS and CONFIG MSRs.

Signed-off-by: Xin Li <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - New patch.
---
 tools/arch/x86/include/asm/msr-index.h        |  2 +
 .../selftests/kvm/include/x86/processor.h     |  1 +
 tools/testing/selftests/kvm/x86/msrs_test.c   | 52 ++++++++++++++++++-
 3 files changed, 53 insertions(+), 2 deletions(-)

diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h
index 18c4be75e927..14632e29f981 100644
--- a/tools/arch/x86/include/asm/msr-index.h
+++ b/tools/arch/x86/include/asm/msr-index.h
@@ -64,6 +64,8 @@
 #define MSR_IA32_FRED_SSP3	0x1d3			/* Level 3 shadow stack pointer */
 #define MSR_IA32_FRED_CONFIG	0x1d4			/* Entrypoint and interrupt stack level */
 
+#define FRED_CONFIG_RESERVED		(BIT_ULL(2) | GENMASK_ULL(5, 4) | BIT_ULL(11))
+
 /* Intel MSRs. Some also available on other CPUs */
 #define MSR_TEST_CTRL				0x00000033
 #define MSR_TEST_CTRL_SPLIT_LOCK_DETECT_BIT	29
diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
index 6e6f70035508..916646cb4d89 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -195,6 +195,7 @@ struct kvm_x86_cpu_feature {
 #define	X86_FEATURE_SPEC_CTRL		KVM_X86_CPU_FEATURE(0x7, 0, EDX, 26)
 #define	X86_FEATURE_ARCH_CAPABILITIES	KVM_X86_CPU_FEATURE(0x7, 0, EDX, 29)
 #define	X86_FEATURE_PKS			KVM_X86_CPU_FEATURE(0x7, 0, ECX, 31)
+#define	X86_FEATURE_FRED		KVM_X86_CPU_FEATURE(0x7, 1, EAX, 17)
 #define	X86_FEATURE_XTILECFG		KVM_X86_CPU_FEATURE(0xD, 0, EAX, 17)
 #define	X86_FEATURE_XTILEDATA		KVM_X86_CPU_FEATURE(0xD, 0, EAX, 18)
 #define	X86_FEATURE_XSAVES		KVM_X86_CPU_FEATURE(0xD, 1, EAX, 3)
diff --git a/tools/testing/selftests/kvm/x86/msrs_test.c b/tools/testing/selftests/kvm/x86/msrs_test.c
index f7e39bf887ad..6e2d8b8f1bd3 100644
--- a/tools/testing/selftests/kvm/x86/msrs_test.c
+++ b/tools/testing/selftests/kvm/x86/msrs_test.c
@@ -60,9 +60,23 @@ static const u64 u64_val = 0xaaaa5555aaaa5555ull;
 #define MSR_TEST_CANONICAL(msr, feat)					\
 	__MSR_TEST(msr, #msr, canonical_val, NONCANONICAL, 0, feat)
 
+#define MSR_TEST_CANONICAL2(msr, feat, f2)				\
+	____MSR_TEST(msr, #msr, canonical_val, NONCANONICAL, 0, feat, f2, false)
+
 #define MSR_TEST_KVM(msr, val, rsvd, feat)				\
 	____MSR_TEST(KVM_REG_ ##msr, #msr, val, rsvd, 0, feat, feat, true)
 
+/* FRED RSPs are 64-byte aligned, thus bits 5:0 are all zero */
+static const u64 fred_rsp_canonical_val = canonical_val + 0xbc0;
+
+/*
+ * FRED SSP[123] are 8-byte aligned, thus bits 2:0 are all zero.
+ *
+ * For legacy reasons, FRED SSP0 is allowed to be 4-byte aligned, but treat
+ * it as 8-byte aligned to simplify the tests.
+ */
+static const u64 fred_ssp_canonical_val = canonical_val + 0xbc8;
+
 /*
  * The main struct must be scoped to a function due to the use of structures to
  * define features.  For the global structure, allocate enough space for the
@@ -388,8 +402,9 @@ static void test_msrs(void)
 		MSR_TEST2(MSR_IA32_S_CET, CET_ENDBR_EN, CET_RESERVED, IBT, SHSTK),
 		MSR_TEST2(MSR_IA32_U_CET, CET_SHSTK_EN, CET_RESERVED, SHSTK, IBT),
 		MSR_TEST2(MSR_IA32_U_CET, CET_ENDBR_EN, CET_RESERVED, IBT, SHSTK),
-		MSR_TEST_CANONICAL(MSR_IA32_PL0_SSP, SHSTK),
-		MSR_TEST(MSR_IA32_PL0_SSP, canonical_val, canonical_val | 1, SHSTK),
+		MSR_TEST_CANONICAL2(MSR_IA32_PL0_SSP, SHSTK, FRED),
+		MSR_TEST2(MSR_IA32_PL0_SSP, canonical_val, canonical_val | 1, SHSTK, FRED),
+		MSR_TEST2(MSR_IA32_PL0_SSP, canonical_val, canonical_val | 1, FRED, SHSTK),
 		MSR_TEST_CANONICAL(MSR_IA32_PL1_SSP, SHSTK),
 		MSR_TEST(MSR_IA32_PL1_SSP, canonical_val, canonical_val | 1, SHSTK),
 		MSR_TEST_CANONICAL(MSR_IA32_PL2_SSP, SHSTK),
@@ -398,10 +413,35 @@ static void test_msrs(void)
 		MSR_TEST(MSR_IA32_PL3_SSP, canonical_val, canonical_val | 1, SHSTK),
 
 		MSR_TEST_KVM(GUEST_SSP, canonical_val, NONCANONICAL, SHSTK),
+
+		MSR_TEST_CANONICAL(MSR_IA32_FRED_RSP0, FRED),
+		MSR_TEST(MSR_IA32_FRED_RSP0, fred_rsp_canonical_val, fred_rsp_canonical_val | 1, FRED),
+		MSR_TEST_CANONICAL(MSR_IA32_FRED_RSP1, FRED),
+		MSR_TEST(MSR_IA32_FRED_RSP1, fred_rsp_canonical_val, fred_rsp_canonical_val | 2, FRED),
+		MSR_TEST_CANONICAL(MSR_IA32_FRED_RSP2, FRED),
+		MSR_TEST(MSR_IA32_FRED_RSP2, fred_rsp_canonical_val, fred_rsp_canonical_val | 4, FRED),
+		MSR_TEST_CANONICAL(MSR_IA32_FRED_RSP3, FRED),
+		MSR_TEST(MSR_IA32_FRED_RSP3, fred_rsp_canonical_val, fred_rsp_canonical_val | 7, FRED),
+
+		MSR_TEST_CANONICAL2(MSR_IA32_FRED_SSP0, FRED, SHSTK),
+		MSR_TEST2(MSR_IA32_FRED_SSP0, fred_ssp_canonical_val, fred_ssp_canonical_val | 1, SHSTK, FRED),
+		MSR_TEST2(MSR_IA32_FRED_SSP0, fred_ssp_canonical_val, fred_ssp_canonical_val | 1, FRED, SHSTK),
+		MSR_TEST_CANONICAL(MSR_IA32_FRED_SSP1, FRED),
+		MSR_TEST(MSR_IA32_FRED_SSP1, fred_ssp_canonical_val, fred_ssp_canonical_val | 2, FRED),
+		MSR_TEST_CANONICAL(MSR_IA32_FRED_SSP2, FRED),
+		MSR_TEST(MSR_IA32_FRED_SSP2, fred_ssp_canonical_val, fred_ssp_canonical_val | 4, FRED),
+		MSR_TEST_CANONICAL(MSR_IA32_FRED_SSP3, FRED),
+		MSR_TEST(MSR_IA32_FRED_SSP3, fred_ssp_canonical_val, fred_ssp_canonical_val | 7, FRED),
+
+		MSR_TEST(MSR_IA32_FRED_STKLVLS, 0xe41be41be41be41b, 0, FRED),
+
+		MSR_TEST_CANONICAL(MSR_IA32_FRED_CONFIG, FRED),
+		MSR_TEST(MSR_IA32_FRED_CONFIG, canonical_val, canonical_val | FRED_CONFIG_RESERVED, FRED),
 	};
 
 	const struct kvm_x86_cpu_feature feat_none = X86_FEATURE_NONE;
 	const struct kvm_x86_cpu_feature feat_lm = X86_FEATURE_LM;
+	const struct kvm_x86_cpu_feature feat_fred = X86_FEATURE_FRED;
 
 	/*
 	 * Create three vCPUs, but run them on the same task, to validate KVM's
@@ -432,6 +472,14 @@ static void test_msrs(void)
 	 * immutable once the vCPU has been run.
 	 */
 	for (idx = 0; idx < ARRAY_SIZE(__msrs); idx++) {
+		/*
+		 * Feature word 0x7:1:EAX used by FRED doesn't exist on some CPU
+		 * models, simply skip clearing FRED feature bit.
+		 */
+		if (!memcmp(&msrs[idx].feature, &feat_fred, sizeof(feat_fred)) &&
+		    !kvm_cpu_has(X86_FEATURE_FRED))
+			continue;
+
 		/*
 		 * Don't clear LM; selftests are 64-bit only, and KVM doesn't
 		 * honor LM=0 for MSRs that are supposed to exist if and only
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 27/28] KVM: selftests: Add a new VM guest mode to run user level code
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (25 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 26/28] KVM: selftests: Add FRED MSRs to msrs_test Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  2026-09-11 21:36 ` [PATCH v10 28/28] KVM: selftests: Add fred exception tests Sohil Mehta
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Add a new VM guest mode VM_MODE_PXXV48_4K_USER to set the user bit of
guest page table entries, thus allow user level code to run in guests.

Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - Treat as new patch (last posted in v2).
---
 tools/testing/selftests/kvm/include/kvm_util.h  |  1 +
 tools/testing/selftests/kvm/lib/kvm_util.c      |  5 ++++-
 tools/testing/selftests/kvm/lib/x86/processor.c | 16 ++++++++++++----
 3 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index 5a7a455b5387..981adfcbd5fa 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -191,6 +191,7 @@ enum vm_guest_mode {
 	VM_MODE_P40V48_16K,
 	VM_MODE_P40V48_64K,
 	VM_MODE_PXXVYY_4K,	/* For 48-bit or 57-bit VA, depending on host support */
+	VM_MODE_PXXVYY_4K_USER,	/* For 48bits VA but ANY bits PA with USER bit set */
 	VM_MODE_P47V64_4K,
 	VM_MODE_P44V64_4K,
 	VM_MODE_P36V48_4K,
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 9ddc047d5c27..e79fddedaf39 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -210,6 +210,7 @@ const char *vm_guest_mode_string(u32 i)
 		[VM_MODE_P40V48_16K]	= "PA-bits:40,  VA-bits:48, 16K pages",
 		[VM_MODE_P40V48_64K]	= "PA-bits:40,  VA-bits:48, 64K pages",
 		[VM_MODE_PXXVYY_4K]	= "PA-bits:ANY, VA-bits:48 or 57, 4K pages",
+		[VM_MODE_PXXVYY_4K_USER]= "PA-bits:ANY, VA-bits:48,  4K user pages",
 		[VM_MODE_P47V64_4K]	= "PA-bits:47,  VA-bits:64,  4K pages",
 		[VM_MODE_P44V64_4K]	= "PA-bits:44,  VA-bits:64,  4K pages",
 		[VM_MODE_P36V48_4K]	= "PA-bits:36,  VA-bits:48,  4K pages",
@@ -246,6 +247,7 @@ const struct vm_guest_mode_params vm_guest_mode_params[] = {
 	[VM_MODE_P40V48_16K]	= { 40, 48,  0x4000, 14 },
 	[VM_MODE_P40V48_64K]	= { 40, 48, 0x10000, 16 },
 	[VM_MODE_PXXVYY_4K]	= {  0,  0,  0x1000, 12 },
+	[VM_MODE_PXXVYY_4K_USER]= {  0,  0,  0x1000, 12 },
 	[VM_MODE_P47V64_4K]	= { 47, 64,  0x1000, 12 },
 	[VM_MODE_P44V64_4K]	= { 44, 64,  0x1000, 12 },
 	[VM_MODE_P36V48_4K]	= { 36, 48,  0x1000, 12 },
@@ -337,6 +339,7 @@ struct kvm_vm *____vm_create(struct vm_shape shape)
 		vm->mmu.pgtable_levels = 3;
 		break;
 	case VM_MODE_PXXVYY_4K:
+	case VM_MODE_PXXVYY_4K_USER:
 #ifdef __x86_64__
 		kvm_get_cpu_address_width(&vm->pa_bits, &vm->va_bits);
 		kvm_init_vm_address_properties(vm);
@@ -355,7 +358,7 @@ struct kvm_vm *____vm_create(struct vm_shape shape)
 			vm->mmu.pgtable_levels = 4;
 		}
 #else
-		TEST_FAIL("VM_MODE_PXXVYY_4K not supported on non-x86 platforms");
+		TEST_FAIL("VM_MODE_PXXVYY_4K(_USER) not supported on non-x86 platforms");
 #endif
 		break;
 	case VM_MODE_P47V64_4K:
diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c
index d31fa81ea075..51d713c4c9de 100644
--- a/tools/testing/selftests/kvm/lib/x86/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86/processor.c
@@ -167,6 +167,9 @@ bool kvm_is_tdp_enabled(void)
 static void virt_mmu_init(struct kvm_vm *vm, struct kvm_mmu *mmu,
 			  struct pte_masks *pte_masks)
 {
+	TEST_ASSERT(vm->mode == VM_MODE_PXXVYY_4K || vm->mode == VM_MODE_PXXVYY_4K_USER,
+		    "Unknown or unsupported guest mode: 0x%x", vm->mode);
+
 	/* If needed, create the top-level page table. */
 	if (!mmu->pgd_created) {
 		mmu->pgd = vm_alloc_page_table(vm);
@@ -181,7 +184,7 @@ static void virt_mmu_init(struct kvm_vm *vm, struct kvm_mmu *mmu,
 
 void virt_arch_pgd_alloc(struct kvm_vm *vm)
 {
-	TEST_ASSERT(vm->mode == VM_MODE_PXXVYY_4K,
+	TEST_ASSERT(vm->mode == VM_MODE_PXXVYY_4K || vm->mode == VM_MODE_PXXVYY_4K_USER,
 		    "Unknown or unsupported guest mode: 0x%x", vm->mode);
 
 	struct pte_masks pte_masks = (struct pte_masks){
@@ -239,6 +242,8 @@ static u64 *virt_create_upper_pte(struct kvm_vm *vm,
 		*pte = PTE_PRESENT_MASK(mmu) | PTE_READABLE_MASK(mmu) |
 		       PTE_WRITABLE_MASK(mmu) | PTE_EXECUTABLE_MASK(mmu) |
 		       PTE_ALWAYS_SET_MASK(mmu);
+		if (vm->mode == VM_MODE_PXXVYY_4K_USER)
+			*pte |= PTE_USER_MASK(mmu);
 		if (current_level == target_level)
 			*pte |= PTE_HUGE_MASK(mmu) | (gpa & PHYSICAL_PAGE_MASK);
 		else
@@ -266,7 +271,7 @@ void __virt_pg_map(struct kvm_vm *vm, struct kvm_mmu *mmu, gva_t gva,
 	u64 *pte = &mmu->pgd;
 	int current_level;
 
-	TEST_ASSERT(vm->mode == VM_MODE_PXXVYY_4K,
+	TEST_ASSERT(vm->mode == VM_MODE_PXXVYY_4K || vm->mode == VM_MODE_PXXVYY_4K_USER,
 		    "Unknown or unsupported guest mode: 0x%x", vm->mode);
 
 	TEST_ASSERT((gva % pg_size) == 0,
@@ -316,6 +321,9 @@ void __virt_pg_map(struct kvm_vm *vm, struct kvm_mmu *mmu, gva_t gva,
 		*pte |= PTE_C_BIT_MASK(mmu);
 	else
 		*pte |= PTE_S_BIT_MASK(mmu);
+
+	if (vm->mode == VM_MODE_PXXVYY_4K_USER)
+		*pte |= PTE_USER_MASK(mmu);
 }
 
 void virt_arch_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa)
@@ -372,7 +380,7 @@ static u64 *__vm_get_page_table_entry(struct kvm_vm *vm,
 	TEST_ASSERT(*level >= PG_LEVEL_NONE && *level <= mmu->pgtable_levels,
 		    "Invalid PG_LEVEL_* '%d'", *level);
 
-	TEST_ASSERT(vm->mode == VM_MODE_PXXVYY_4K,
+	TEST_ASSERT(vm->mode == VM_MODE_PXXVYY_4K || vm->mode == VM_MODE_PXXVYY_4K_USER,
 		    "Unknown or unsupported guest mode: 0x%x", vm->mode);
 	TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, (gva >> vm->page_shift)),
 		    "Invalid virtual address, gva: 0x%lx", gva);
@@ -645,7 +653,7 @@ static void vcpu_init_sregs(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
 {
 	struct kvm_sregs sregs;
 
-	TEST_ASSERT(vm->mode == VM_MODE_PXXVYY_4K,
+	TEST_ASSERT(vm->mode == VM_MODE_PXXVYY_4K || vm->mode == VM_MODE_PXXVYY_4K_USER,
 		    "Unknown or unsupported guest mode: 0x%x", vm->mode);
 
 	/* Set mode specific system register values. */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v10 28/28] KVM: selftests: Add fred exception tests
  2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
                   ` (26 preceding siblings ...)
  2026-09-11 21:36 ` [PATCH v10 27/28] KVM: selftests: Add a new VM guest mode to run user level code Sohil Mehta
@ 2026-09-11 21:36 ` Sohil Mehta
  27 siblings, 0 replies; 29+ messages in thread
From: Sohil Mehta @ 2026-09-11 21:36 UTC (permalink / raw)
  To: kvm, x86
  Cc: Paolo Bonzini, Sean Christopherson, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H . Peter Anvin, Xin Li, Andy Lutomirski, Peter Zijlstra,
	Andrew Cooper, Tom Lendacky, Nikunj A Dadhania, Shivansh Dhiman,
	David Woodhouse, Chao Gao, Binbin Wu, Sohil Mehta, Zhao Liu,
	Yosry Ahmed, David Matlack, linux-doc, linux-kernel,
	linux-kselftest

From: "Xin Li (Intel)" <xin@zytor.com>

Add tests for FRED event data and VMX nested-exception.

FRED is designed to save a complete event context in its stack frame,
e.g., FRED saves the faulting linear address of a #PF into a 64-bit
event data field defined in FRED stack frame.  As such, FRED VMX adds
event data handling during VMX transitions.

Besides, FRED introduces event stack levels to dispatch an event handler
onto a stack based on current stack level and stack levels defined in
IA32_FRED_STKLVLS MSR for each exception vector.  VMX nested-exception
support ensures a correct event stack level is chosen when a VM entry
injects a nested exception, which is regarded as occurred in ring 0.

To fully test the underlying FRED VMX code, this test should be run one
more round with EPT disabled to inject page faults as nested exceptions.

Originally-by: Shan Kang <shan.kang@intel.com>
Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
---
v10:
 - Treat as new patch (last posted in v2).
---
 tools/testing/selftests/kvm/Makefile.kvm      |   1 +
 .../selftests/kvm/include/x86/processor.h     |  33 ++
 tools/testing/selftests/kvm/x86/fred_test.c   | 291 ++++++++++++++++++
 3 files changed, 325 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/x86/fred_test.c

diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 96bab7002d39..73aed018f4a3 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -78,6 +78,7 @@ TEST_GEN_PROGS_x86 += x86/feature_msrs_test
 TEST_GEN_PROGS_x86 += x86/evmcs_smm_controls_test
 TEST_GEN_PROGS_x86 += x86/exit_on_emulation_failure_test
 TEST_GEN_PROGS_x86 += x86/fastops_test
+TEST_GEN_PROGS_x86 += x86/fred_test
 TEST_GEN_PROGS_x86 += x86/fix_hypercall_test
 TEST_GEN_PROGS_x86 += x86/hwcr_msr_test
 TEST_GEN_PROGS_x86 += x86/hyperv_clock
diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
index 916646cb4d89..988c45e32689 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -79,6 +79,7 @@ const char *ex_str(int vector);
 #define X86_CR4_SMEP		(1ul << 20)
 #define X86_CR4_SMAP		(1ul << 21)
 #define X86_CR4_PKE		(1ul << 22)
+#define X86_CR4_FRED		(1ul << 32)
 
 struct xstate_header {
 	u64				xstate_bv;
@@ -1646,4 +1647,36 @@ u64 *tdp_get_pte(struct kvm_vm *vm, u64 l2_gpa);
 
 bool sys_clocksource_is_based_on_tsc(void);
 
+/*
+ * FRED related data structures and functions
+ */
+
+#define FRED_SSX_NMI		BIT_ULL(18)
+
+struct fred_stack {
+	u64 r15;
+	u64 r14;
+	u64 r13;
+	u64 r12;
+	u64 bp;
+	u64 bx;
+	u64 r11;
+	u64 r10;
+	u64 r9;
+	u64 r8;
+	u64 ax;
+	u64 cx;
+	u64 dx;
+	u64 si;
+	u64 di;
+	u64 error_code;
+	u64 ip;
+	u64 csx;
+	u64 flags;
+	u64 sp;
+	u64 ssx;
+	u64 event_data;
+	u64 reserved;
+};
+
 #endif /* SELFTEST_KVM_PROCESSOR_H */
diff --git a/tools/testing/selftests/kvm/x86/fred_test.c b/tools/testing/selftests/kvm/x86/fred_test.c
new file mode 100644
index 000000000000..c7d08e5253b9
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/fred_test.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * FRED nested exception tests
+ *
+ * Copyright (C) 2023, Intel, Inc.
+ */
+#define _GNU_SOURCE /* for program_invocation_short_name */
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <asm/msr-index.h>
+
+#include "apic.h"
+#include "kvm_util.h"
+#include "test_util.h"
+#include "guest_modes.h"
+#include "processor.h"
+
+#define IRQ_VECTOR 0xAA
+
+#define FRED_STKLVL(v,l)		(_AT(unsigned long, l) << (2 * (v)))
+#define FRED_CONFIG_ENTRYPOINT(p)	_AT(unsigned long, (p))
+
+/* This address is already mapped in guest page table. */
+#define FRED_VALID_RSP			0x8000
+
+/*
+ * The following addresses are not yet mapped in both EPT and guest page
+ * tables at the beginning.  As a result, it causes an EPT violation VM
+ * exit with an original guest #PF to access any of them for the first
+ * time.
+ *
+ * Use these addresses as guest FRED RSP0 to generate nested #PFs to test
+ * if event data are properly virtualized.
+ */
+static unsigned long fred_invalid_rsp[4] = {
+	0x0,
+	0xf0000000,
+	0xe0000000,
+	0xd0000000,
+};
+
+extern char asm_user_nop[];
+extern char asm_user_ud[];
+extern char asm_done_fault[];
+
+extern void asm_test_fault(int test);
+
+/*
+ * user level code for triggering faults.
+ */
+asm(".pushsection .text\n"
+    ".align 4096\n"
+
+    ".type asm_user_nop, @function\n"
+    "asm_user_nop:\n"
+    "1: .byte 0x90\n"
+    "jmp 1b\n"
+
+    ".org asm_user_nop + 16, 0xcc\n"
+    ".type asm_user_ud, @function\n"
+    "asm_user_ud:\n"
+    /* Trigger a #UD */
+    "ud2\n"
+
+    ".align 4096, 0xcc\n"
+    ".popsection");
+
+/* Send current stack level and #PF address */
+#define GUEST_SYNC_CSL_FA(__stage, __pf_address)		\
+	GUEST_SYNC_ARGS(__stage, __pf_address, 0, 0, 0)
+
+void fred_entry_from_user(struct fred_stack *stack)
+{
+	u32 current_stack_level = rdmsr(MSR_IA32_FRED_CONFIG) & 0x3;
+
+	GUEST_SYNC_CSL_FA(current_stack_level, stack->event_data);
+
+	/* Do NOT go back to user level, continue the next test instead */
+	stack->ssx = 0x18;
+	stack->csx = 0x10;
+	stack->ip = (u64)&asm_done_fault;
+}
+
+void fred_entry_from_kernel(struct fred_stack *stack)
+{
+	/*
+	 * Keep NMI blocked to delay the delivery of the next NMI until
+	 * returning to user level.
+	 * */
+	stack->ssx &= ~FRED_SSX_NMI;
+}
+
+#define PUSH_REGS	\
+	"push %rdi\n"	\
+	"push %rsi\n"	\
+	"push %rdx\n"	\
+	"push %rcx\n"	\
+	"push %rax\n"	\
+	"push %r8\n"	\
+	"push %r9\n"	\
+	"push %r10\n"	\
+	"push %r11\n"	\
+	"push %rbx\n"	\
+	"push %rbp\n"	\
+	"push %r12\n"	\
+	"push %r13\n"	\
+	"push %r14\n"	\
+	"push %r15\n"
+
+#define POP_REGS	\
+	"pop %r15\n"	\
+	"pop %r14\n"	\
+	"pop %r13\n"	\
+	"pop %r12\n"	\
+	"pop %rbp\n"	\
+	"pop %rbx\n"	\
+	"pop %r11\n"	\
+	"pop %r10\n"	\
+	"pop %r9\n"	\
+	"pop %r8\n"	\
+	"pop %rax\n"	\
+	"pop %rcx\n"	\
+	"pop %rdx\n"	\
+	"pop %rsi\n"	\
+	"pop %rdi\n"
+
+/*
+ * FRED entry points.
+ */
+asm(".pushsection .text\n"
+    ".type asm_fred_entrypoint_user, @function\n"
+    ".align 4096\n"
+    "asm_fred_entrypoint_user:\n"
+    PUSH_REGS
+    "movq %rsp, %rdi\n"
+    "call fred_entry_from_user\n"
+    POP_REGS
+    /* Do NOT go back to user level, continue the next test instead */
+    ".byte 0xf2,0x0f,0x01,0xca\n"	/* ERETS */
+
+    ".org asm_fred_entrypoint_user + 256, 0xcc\n"
+    ".type asm_fred_entrypoint_kernel, @function\n"
+    "asm_fred_entrypoint_kernel:\n"
+    PUSH_REGS
+    "movq %rsp, %rdi\n"
+    "call fred_entry_from_kernel\n"
+    POP_REGS
+    ".byte 0xf2,0x0f,0x01,0xca\n"	/* ERETS */
+    ".align 4096, 0xcc\n"
+    ".popsection");
+
+extern char asm_fred_entrypoint_user[];
+
+/*
+ * Prepare a FRED stack frame for ERETU to return to user level code,
+ * nop or ud2.
+ *
+ * Because FRED RSP0 is deliberately not mapped in guest page table,
+ * the delivery of interrupt/NMI or #UD from ring 3 causes a nested
+ * #PF, which is then delivered on FRED RSPx (x is 1, 2 or 3,
+ * determinated by MSR FRED_STKLVL[PF_VECTOR]).
+ */
+asm(".pushsection .text\n"
+    ".type asm_test_fault, @function\n"
+    ".align 4096\n"
+    "asm_test_fault:\n"
+    "endbr64\n"
+    "push %rbp\n"
+    "mov %rsp, %rbp\n"
+    "and $(~0x3f), %rsp\n"
+    "push $0\n"
+    "push $0\n"
+    "mov $0x2b, %rax\n"
+    /* Unblock NMI */
+    "bts $18, %rax\n"
+    /* Set long mode bit */
+    "bts $57, %rax\n"
+    "push %rax\n"
+    /* No stack required for the FRED user level test code */
+    "push $0\n"
+    "pushf\n"
+    "pop %rax\n"
+    /* Allow external interrupts */
+    "bts $9, %rax\n"
+    "push %rax\n"
+    "mov $0x33, %rax\n"
+    "push %rax\n"
+    "cmp $0, %edi\n"
+    "jne 1f\n"
+    "lea asm_user_nop(%rip), %rax\n"
+    "jmp 2f\n"
+    "1: lea asm_user_ud(%rip), %rax\n"
+    "2: push %rax\n"
+    "push $0\n"
+    /* ERETU to user level code to allow event delivery immediately */
+    ".byte 0xf3,0x0f,0x01,0xca\n"
+    "asm_done_fault:\n"
+    "mov %rbp, %rsp\n"
+    "pop %rbp\n"
+    "ret\n"
+    ".align 4096, 0xcc\n"
+    ".popsection");
+
+/*
+ * To fully test the underlying FRED VMX code, this test should be run one
+ * more round with EPT disabled to inject page faults as nested exceptions.
+ */
+static void guest_code(void)
+{
+	wrmsr(MSR_IA32_FRED_CONFIG,
+	      FRED_CONFIG_ENTRYPOINT(asm_fred_entrypoint_user));
+
+	wrmsr(MSR_IA32_FRED_RSP1, FRED_VALID_RSP);
+	wrmsr(MSR_IA32_FRED_RSP2, FRED_VALID_RSP);
+	wrmsr(MSR_IA32_FRED_RSP3, FRED_VALID_RSP);
+
+	/* Enable FRED */
+	set_cr4(get_cr4() | X86_CR4_FRED);
+
+	x2apic_enable();
+
+	wrmsr(MSR_IA32_FRED_STKLVLS, FRED_STKLVL(PF_VECTOR, 1));
+	wrmsr(MSR_IA32_FRED_RSP0, fred_invalid_rsp[1]);
+	/* 1: ud2 to generate #UD */
+	asm_test_fault(1);
+
+	wrmsr(MSR_IA32_FRED_STKLVLS, FRED_STKLVL(PF_VECTOR, 2));
+	wrmsr(MSR_IA32_FRED_RSP0, fred_invalid_rsp[2]);
+	asm volatile("cli");
+	/* Create a pending interrupt on current vCPU */
+	x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT |
+			 APIC_DM_FIXED | IRQ_VECTOR);
+	/* Return to ring 3 */
+	asm_test_fault(0);
+	x2apic_write_reg(APIC_EOI, 0);
+
+	wrmsr(MSR_IA32_FRED_STKLVLS, FRED_STKLVL(PF_VECTOR, 3));
+	wrmsr(MSR_IA32_FRED_RSP0, fred_invalid_rsp[3]);
+	/*
+	 * The first NMI is just to have NMI blocked in ring 0, because
+	 * fred_entry_from_kernel() deliberately clears the NMI bit in
+	 * FRED stack frame.
+	 */
+	x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT |
+			 APIC_DM_NMI | NMI_VECTOR);
+	/* The second NMI will be delivered after returning to ring 3 */
+	x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT |
+			 APIC_DM_NMI | NMI_VECTOR);
+	/* Return to ring 3 */
+	asm_test_fault(0);
+
+	GUEST_DONE();
+}
+
+int main(int argc, char *argv[])
+{
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	struct ucall uc;
+	uint64_t expected_current_stack_level = 1;
+
+	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_FRED));
+
+	vm = __vm_create_with_vcpus(VM_SHAPE(VM_MODE_PXXVYY_4K_USER), 1, 0,
+				    guest_code, &vcpu);
+
+	while (true) {
+		uint64_t r;
+
+		vcpu_run(vcpu);
+
+		r = get_ucall(vcpu, &uc);
+
+		if (r == UCALL_DONE)
+			break;
+
+		if (r == UCALL_SYNC) {
+			TEST_ASSERT((uc.args[1] == expected_current_stack_level) &&
+				    (uc.args[2] == fred_invalid_rsp[expected_current_stack_level] - 8),
+				    "Incorrect stack level %lx and #PF address %lx\n",
+				    uc.args[1], uc.args[2]);
+			expected_current_stack_level++;
+		}
+	}
+
+	kvm_vm_free(vm);
+	return 0;
+}
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2026-09-11 21:44 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-11 21:36 [PATCH v10 00/28] KVM: Enable FRED support with KVM VMX Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 01/28] KVM: VMX: Enable support for secondary VM exit controls Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 02/28] KVM: VMX: Initialize VM entry/exit FRED controls in vmcs_config Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 03/28] KVM: VMX: Disable FRED if FRED consistency checks fail Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 04/28] x86/cea: Prefix event stack names with ESTACK_ Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 05/28] x86/cea: Use array indexing to simplify exception stack access Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 06/28] x86/fred: Export this_cpu_fred_rsp() for KVM usage Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 07/28] KVM: VMX: Initialize VMCS FRED fields Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 08/28] KVM: VMX: Set FRED MSR intercepts Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 09/28] KVM: VMX: Save/restore guest FRED RSP0 Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 10/28] KVM: VMX: Add support for saving and restoring FRED MSRs Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 11/28] KVM: x86: Add a helper to detect if FRED is enabled for a vCPU Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 12/28] KVM: x86: Add a new save/restore flag for FRED metadata Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 13/28] KVM: VMX: Virtualize FRED nested exception tracking Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 14/28] KVM: VMX: Virtualize FRED event_data Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 15/28] KVM: x86: Include CR4.FRED in the emulator CR4 write mask Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 16/28] KVM: x86: Mark CR4.FRED as not reserved Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 17/28] KVM: x86: Handle CR4.FRED when emulating RSM Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 18/28] KVM: VMX: Dump FRED context in dump_vmcs() Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 19/28] KVM: x86: Advertise support for FRED Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 20/28] KVM: nVMX: Enable support for secondary VM exit controls Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 21/28] KVM: nVMX: Handle FRED VMCS fields in nested VMX context Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 22/28] KVM: nVMX: Restrict event data VMCS fields to FRED-supported hosts Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 23/28] KVM: nVMX: Shadow ORIGINAL_EVENT_DATA and INJECTED_EVENT_DATA fields Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 24/28] KVM: nVMX: Validate FRED-related VMCS fields Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 25/28] KVM: nVMX: Enable VMX FRED controls Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 26/28] KVM: selftests: Add FRED MSRs to msrs_test Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 27/28] KVM: selftests: Add a new VM guest mode to run user level code Sohil Mehta
2026-09-11 21:36 ` [PATCH v10 28/28] KVM: selftests: Add fred exception tests Sohil Mehta

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox