KVM: Prevent guest fpu state from leaking into the host

The lazy fpu changes did not take into account that some vmexit handlers
can sleep. Move loading the guest state into the inner loop so that it
can be reloaded if necessary, and move loading the host state into
vmx_vcpu_put() so it can be performed whenever we relinquish the vcpu.

Signed-off-by: Avi Kivity <avi@qumranet.com>

+28 -9
+3
drivers/kvm/kvm.h
··· 304 char *host_fx_image; 305 char *guest_fx_image; 306 int fpu_active; 307 308 int mmio_needed; 309 int mmio_read_completed; ··· 509 void load_msrs(struct vmx_msr_entry *e, int n); 510 void save_msrs(struct vmx_msr_entry *e, int n); 511 void kvm_resched(struct kvm_vcpu *vcpu); 512 513 int kvm_read_guest(struct kvm_vcpu *vcpu, 514 gva_t addr,
··· 304 char *host_fx_image; 305 char *guest_fx_image; 306 int fpu_active; 307 + int guest_fpu_loaded; 308 309 int mmio_needed; 310 int mmio_read_completed; ··· 508 void load_msrs(struct vmx_msr_entry *e, int n); 509 void save_msrs(struct vmx_msr_entry *e, int n); 510 void kvm_resched(struct kvm_vcpu *vcpu); 511 + void kvm_load_guest_fpu(struct kvm_vcpu *vcpu); 512 + void kvm_put_guest_fpu(struct kvm_vcpu *vcpu); 513 514 int kvm_read_guest(struct kvm_vcpu *vcpu, 515 gva_t addr,
+22
drivers/kvm/kvm_main.c
··· 253 } 254 EXPORT_SYMBOL_GPL(kvm_write_guest); 255 256 /* 257 * Switches to specified vcpu, until a matching vcpu_put() 258 */
··· 253 } 254 EXPORT_SYMBOL_GPL(kvm_write_guest); 255 256 + void kvm_load_guest_fpu(struct kvm_vcpu *vcpu) 257 + { 258 + if (!vcpu->fpu_active || vcpu->guest_fpu_loaded) 259 + return; 260 + 261 + vcpu->guest_fpu_loaded = 1; 262 + fx_save(vcpu->host_fx_image); 263 + fx_restore(vcpu->guest_fx_image); 264 + } 265 + EXPORT_SYMBOL_GPL(kvm_load_guest_fpu); 266 + 267 + void kvm_put_guest_fpu(struct kvm_vcpu *vcpu) 268 + { 269 + if (!vcpu->guest_fpu_loaded) 270 + return; 271 + 272 + vcpu->guest_fpu_loaded = 0; 273 + fx_save(vcpu->guest_fx_image); 274 + fx_restore(vcpu->host_fx_image); 275 + } 276 + EXPORT_SYMBOL_GPL(kvm_put_guest_fpu); 277 + 278 /* 279 * Switches to specified vcpu, until a matching vcpu_put() 280 */
+3 -9
drivers/kvm/vmx.c
··· 280 281 static void vmx_vcpu_put(struct kvm_vcpu *vcpu) 282 { 283 put_cpu(); 284 } 285 ··· 1848 if (vcpu->guest_debug.enabled) 1849 kvm_guest_debug_pre(vcpu); 1850 1851 - if (vcpu->fpu_active) { 1852 - fx_save(vcpu->host_fx_image); 1853 - fx_restore(vcpu->guest_fx_image); 1854 - } 1855 /* 1856 * Loading guest fpu may have cleared host cr0.ts 1857 */ ··· 2010 load_msrs(vcpu->host_msrs, NR_BAD_MSRS); 2011 } 2012 #endif 2013 - 2014 - if (vcpu->fpu_active) { 2015 - fx_save(vcpu->guest_fx_image); 2016 - fx_restore(vcpu->host_fx_image); 2017 - } 2018 2019 vcpu->interrupt_window_open = (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0; 2020
··· 280 281 static void vmx_vcpu_put(struct kvm_vcpu *vcpu) 282 { 283 + kvm_put_guest_fpu(vcpu); 284 put_cpu(); 285 } 286 ··· 1847 if (vcpu->guest_debug.enabled) 1848 kvm_guest_debug_pre(vcpu); 1849 1850 + kvm_load_guest_fpu(vcpu); 1851 + 1852 /* 1853 * Loading guest fpu may have cleared host cr0.ts 1854 */ ··· 2011 load_msrs(vcpu->host_msrs, NR_BAD_MSRS); 2012 } 2013 #endif 2014 2015 vcpu->interrupt_window_open = (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0; 2016