aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAvi Kivity <avi@redhat.com>2010-03-15 07:59:54 -0400
committerAvi Kivity <avi@redhat.com>2010-05-17 05:15:39 -0400
commitdaea3e73cb4ac971bee97f333ae027861d00fc0b (patch)
treef6bf34cb9ad1e4ec85c8ef2a11ddee9d24c73d9a
parent72016f3a4221799a0b1fdf443ef6e29db572a9bb (diff)
KVM: Make locked operations truly atomic
Once upon a time, locked operations were emulated while holding the mmu mutex. Since mmu pages were write protected, it was safe to emulate the writes in a non-atomic manner, since there could be no other writer, either in the guest or in the kernel. These days emulation takes place without holding the mmu spinlock, so the write could be preempted by an unshadowing event, which exposes the page to writes by the guest. This may cause corruption of guest page tables. Fix by using an atomic cmpxchg for these operations. Signed-off-by: Avi Kivity <avi@redhat.com> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
-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}