aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/kvm/x86.c69
1 files changed, 48 insertions, 21 deletions
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 7a8fe9051ecf..855f3ea9edd1 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3291,41 +3291,68 @@ int emulator_write_emulated(unsigned long addr,
3291} 3291}
3292EXPORT_SYMBOL_GPL(emulator_write_emulated); 3292EXPORT_SYMBOL_GPL(emulator_write_emulated);
3293 3293
3294#define CMPXCHG_TYPE(t, ptr, old, new) \
3295 (cmpxchg((t *)(ptr), *(t *)(old), *(t *)(new)) == *(t *)(old))
3296
3297#ifdef CONFIG_X86_64
3298# define CMPXCHG64(ptr, old, new) CMPXCHG_TYPE(u64, ptr, old, new)
3299#else
3300# define CMPXCHG64(ptr, old, new) \
3301 (cmpxchg64((u64 *)(ptr), *(u64 *)(old), *(u *)(new)) == *(u64 *)(old))
3302#endif
3303
3294static int emulator_cmpxchg_emulated(unsigned long addr, 3304static int emulator_cmpxchg_emulated(unsigned long addr,
3295 const void *old, 3305 const void *old,
3296 const void *new, 3306 const void *new,
3297 unsigned int bytes, 3307 unsigned int bytes,
3298 struct kvm_vcpu *vcpu) 3308 struct kvm_vcpu *vcpu)
3299{ 3309{
3300 printk_once(KERN_WARNING "kvm: emulating exchange as write\n"); 3310 gpa_t gpa;
3301#ifndef CONFIG_X86_64 3311 struct page *page;
3302 /* guests cmpxchg8b have to be emulated atomically */ 3312 char *kaddr;
3303 if (bytes == 8) { 3313 bool exchanged;
3304 gpa_t gpa;
3305 struct page *page;
3306 char *kaddr;
3307 u64 val;
3308 3314
3309 gpa = kvm_mmu_gva_to_gpa_write(vcpu, addr, NULL); 3315 /* guests cmpxchg8b have to be emulated atomically */
3316 if (bytes > 8 || (bytes & (bytes - 1)))
3317 goto emul_write;
3310 3318
3311 if (gpa == UNMAPPED_GVA || 3319 gpa = kvm_mmu_gva_to_gpa_write(vcpu, addr, NULL);
3312 (gpa & PAGE_MASK) == APIC_DEFAULT_PHYS_BASE)
3313 goto emul_write;
3314 3320
3315 if (((gpa + bytes - 1) & PAGE_MASK) != (gpa & PAGE_MASK)) 3321 if (gpa == UNMAPPED_GVA ||
3316 goto emul_write; 3322 (gpa & PAGE_MASK) == APIC_DEFAULT_PHYS_BASE)
3323 goto emul_write;
3317 3324
3318 val = *(u64 *)new; 3325 if (((gpa + bytes - 1) & PAGE_MASK) != (gpa & PAGE_MASK))
3326 goto emul_write;
3319 3327
3320 page = gfn_to_page(vcpu->kvm, gpa >> PAGE_SHIFT); 3328 page = gfn_to_page(vcpu->kvm, gpa >> PAGE_SHIFT);
3321 3329
3322 kaddr = kmap_atomic(page, KM_USER0); 3330 kaddr = kmap_atomic(page, KM_USER0);
3323 set_64bit((u64 *)(kaddr + offset_in_page(gpa)), val); 3331 kaddr += offset_in_page(gpa);
3324 kunmap_atomic(kaddr, KM_USER0); 3332 switch (bytes) {
3325 kvm_release_page_dirty(page); 3333 case 1:
3334 exchanged = CMPXCHG_TYPE(u8, kaddr, old, new);
3335 break;
3336 case 2:
3337 exchanged = CMPXCHG_TYPE(u16, kaddr, old, new);
3338 break;
3339 case 4:
3340 exchanged = CMPXCHG_TYPE(u32, kaddr, old, new);
3341 break;
3342 case 8:
3343 exchanged = CMPXCHG64(kaddr, old, new);
3344 break;
3345 default:
3346 BUG();
3326 } 3347 }
3348 kunmap_atomic(kaddr, KM_USER0);
3349 kvm_release_page_dirty(page);
3350
3351 if (!exchanged)
3352 return X86EMUL_CMPXCHG_FAILED;
3353
3327emul_write: 3354emul_write:
3328#endif 3355 printk_once(KERN_WARNING "kvm: emulating exchange as write\n");
3329 3356
3330 return emulator_write_emulated(addr, new, bytes, vcpu); 3357 return emulator_write_emulated(addr, new, bytes, vcpu);
3331} 3358}