All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] KVM: nVMX: store L2's TSC frame in the VM-exit IA32_TSC MSR-store list
@ 2026-07-13 14:46 Amirmohammad Eftekhar
  2026-07-13 15:52 ` sashiko-bot
  2026-07-13 17:28 ` Amirmohammad Eftekhar
  0 siblings, 2 replies; 3+ messages in thread
From: Amirmohammad Eftekhar @ 2026-07-13 14:46 UTC (permalink / raw)
  To: kvm; +Cc: Sean Christopherson, Paolo Bonzini, rossow, Amirmohammad Eftekhar

When L1 lists IA32_TSC (0x10) in vmcs12's VM-exit MSR-store area, the
value KVM auto-stores on a nested VM-exit is computed with
kvm_read_l1_tsc(), which evaluates the TSC in L1's frame
(l1_tsc_offset + host_tsc scaled by l1_tsc_scaling_ratio).  But the
guest that just exited is L2, and RDMSR(IA32_TSC) in L2 returns L2's
frame, (host_tsc scaled by the composed ratio) + the composed offset
(SDM Vol 3C 27.4).  The stored value is therefore short by exactly the
L2 offset O2 (more under scaling), so an L1 that uses this list to bound
the highest TSC L2 could have observed gets a bound that is too low.  A
subsequent L1 re-base can then legitimately place L2's TSC below a value
L2 already read, i.e. drive L2's clock backward -- the exact hazard the
feature added in commit 662f1d1d1931 ("KVM: nVMX: Add support for
capturing highest observable L2 TSC") was meant to prevent.

The bug is wrong by single-level equivalence: a non-nested hypervisor
running a guest with offset O that lists IA32_TSC in its VM-exit
MSR-store area receives the guest's frame; the nested emulation of the
identical setup must produce the identical value.

nested_vmx_vmexit() restores the composed TSC state to L1's before the
MSR-store list is processed, which is why the code reached for
kvm_read_l1_tsc() -- whose contract is genuinely raw input everywhere
else it's called (lapic.c and x86.c's pvclock path both pass it
straight from rdtsc()), consistent with treating the autostored value
the same way.  It just stops one step short: O2/M2 remain readable
from vmcs12 via the vmx_get_l2_tsc_* accessors, so recompose L2's
frame there.  Export kvm_scale_tsc() so nested.c can scale the host
TSC by the composed ratio.

Signed-off-by: Amirmohammad Eftekhar <amirmohammad.eftekhar@cispa.de>
---
 arch/x86/kvm/vmx/nested.c | 20 +++++++++++++++++++-
 arch/x86/kvm/x86.c        |  1 +
 2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 6957bb6f5cf7..610b58933d6b 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -1111,8 +1111,26 @@ static bool nested_vmx_get_vmexit_msr_value(struct kvm_vcpu *vcpu,
 	if (msr_index == MSR_IA32_TSC && vmx->nested.tsc_autostore_slot >= 0) {
 		int slot = vmx->nested.tsc_autostore_slot;
 		u64 host_tsc = vmx->msr_autostore.val[slot].value;
+		u64 l2_multiplier = vmx_get_l2_tsc_multiplier(vcpu);
+		u64 l2_offset, l2_ratio;
 
-		*data = kvm_read_l1_tsc(vcpu, host_tsc);
+		/*
+		 * The guest that just exited is L2, so the value auto-stored on
+		 * its behalf must be L2's frame: RDMSR(IA32_TSC) in L2 reads
+		 * (host_tsc scaled by the composed ratio) + the composed offset
+		 * (SDM Vol 3C 27.4).  nested_vmx_vmexit() has already restored
+		 * the composed TSC state to L1's by the time the MSR-store list
+		 * is processed, so recompose L2's frame from L1 state and the
+		 * vmcs12 TSC controls.  kvm_read_l1_tsc() would instead record
+		 * L1's frame, dropping L2's offset (and scaling) entirely.
+		 */
+		l2_offset = kvm_calc_nested_tsc_offset(vcpu->arch.l1_tsc_offset,
+						       vmx_get_l2_tsc_offset(vcpu),
+						       l2_multiplier);
+		l2_ratio = kvm_calc_nested_tsc_multiplier(vcpu->arch.l1_tsc_scaling_ratio,
+							  l2_multiplier);
+
+		*data = kvm_scale_tsc(host_tsc, l2_ratio) + l2_offset;
 		return true;
 	}
 
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index afcac1042947..0ce0d30e8825 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2641,6 +2641,7 @@ u64 kvm_scale_tsc(u64 tsc, u64 ratio)
 
 	return _tsc;
 }
+EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_scale_tsc);
 
 static u64 kvm_compute_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 target_tsc)
 {
-- 
2.34.1


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

end of thread, other threads:[~2026-07-13 17:29 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-13 14:46 [PATCH] KVM: nVMX: store L2's TSC frame in the VM-exit IA32_TSC MSR-store list Amirmohammad Eftekhar
2026-07-13 15:52 ` sashiko-bot
2026-07-13 17:28 ` Amirmohammad Eftekhar

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.