Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: nVMX: Fix nested VMX TSC emulation

This patch fixes two corner cases in nested (L2) handling of TSC-related
issues:

1. Somewhat suprisingly, according to the Intel spec, if L1 allows WRMSR to
the TSC MSR without an exit, then this should set L1's TSC value itself - not
offset by vmcs12.TSC_OFFSET (like was wrongly done in the previous code).

2. Allow L1 to disable the TSC_OFFSETING control, and then correctly ignore
the vmcs12.TSC_OFFSET.

Signed-off-by: Nadav Har'El <nyh@il.ibm.com>
Signed-off-by: Avi Kivity <avi@redhat.com>

authored by

Nadav Har'El and committed by
Avi Kivity
27fc51b2 d5c1785d

+21 -10
+21 -10
arch/x86/kvm/vmx.c
··· 1777 1777 */ 1778 1778 static void vmx_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) 1779 1779 { 1780 - vmcs_write64(TSC_OFFSET, offset); 1781 - if (is_guest_mode(vcpu)) 1780 + if (is_guest_mode(vcpu)) { 1782 1781 /* 1783 - * We're here if L1 chose not to trap the TSC MSR. Since 1784 - * prepare_vmcs12() does not copy tsc_offset, we need to also 1785 - * set the vmcs12 field here. 1782 + * We're here if L1 chose not to trap WRMSR to TSC. According 1783 + * to the spec, this should set L1's TSC; The offset that L1 1784 + * set for L2 remains unchanged, and still needs to be added 1785 + * to the newly set TSC to get L2's TSC. 1786 1786 */ 1787 - get_vmcs12(vcpu)->tsc_offset = offset - 1788 - to_vmx(vcpu)->nested.vmcs01_tsc_offset; 1787 + struct vmcs12 *vmcs12; 1788 + to_vmx(vcpu)->nested.vmcs01_tsc_offset = offset; 1789 + /* recalculate vmcs02.TSC_OFFSET: */ 1790 + vmcs12 = get_vmcs12(vcpu); 1791 + vmcs_write64(TSC_OFFSET, offset + 1792 + (nested_cpu_has(vmcs12, CPU_BASED_USE_TSC_OFFSETING) ? 1793 + vmcs12->tsc_offset : 0)); 1794 + } else { 1795 + vmcs_write64(TSC_OFFSET, offset); 1796 + } 1789 1797 } 1790 1798 1791 1799 static void vmx_adjust_tsc_offset(struct kvm_vcpu *vcpu, s64 adjustment) ··· 6493 6485 6494 6486 set_cr4_guest_host_mask(vmx); 6495 6487 6496 - vmcs_write64(TSC_OFFSET, 6497 - vmx->nested.vmcs01_tsc_offset + vmcs12->tsc_offset); 6488 + if (vmcs12->cpu_based_vm_exec_control & CPU_BASED_USE_TSC_OFFSETING) 6489 + vmcs_write64(TSC_OFFSET, 6490 + vmx->nested.vmcs01_tsc_offset + vmcs12->tsc_offset); 6491 + else 6492 + vmcs_write64(TSC_OFFSET, vmx->nested.vmcs01_tsc_offset); 6498 6493 6499 6494 if (enable_vpid) { 6500 6495 /* ··· 6904 6893 6905 6894 load_vmcs12_host_state(vcpu, vmcs12); 6906 6895 6907 - /* Update TSC_OFFSET if vmx_adjust_tsc_offset() was used while L2 ran */ 6896 + /* Update TSC_OFFSET if TSC was changed while L2 ran */ 6908 6897 vmcs_write64(TSC_OFFSET, vmx->nested.vmcs01_tsc_offset); 6909 6898 6910 6899 /* This is needed for same reason as it was needed in prepare_vmcs02 */