diff options
-rw-r--r-- | arch/x86/kvm/x86.c | 69 |
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 | } |
3292 | EXPORT_SYMBOL_GPL(emulator_write_emulated); | 3292 | EXPORT_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 | |||
3294 | static int emulator_cmpxchg_emulated(unsigned long addr, | 3304 | static 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 | |||
3327 | emul_write: | 3354 | emul_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 | } |