Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[linux-2.6-microblaze.git] / arch / x86 / kvm / mmu / paging_tmpl.h
index 252c778..01fee5f 100644 (file)
@@ -34,9 +34,8 @@
        #define PT_HAVE_ACCESSED_DIRTY(mmu) true
        #ifdef CONFIG_X86_64
        #define PT_MAX_FULL_LEVELS PT64_ROOT_MAX_LEVEL
-       #define CMPXCHG cmpxchg
+       #define CMPXCHG "cmpxchgq"
        #else
-       #define CMPXCHG cmpxchg64
        #define PT_MAX_FULL_LEVELS 2
        #endif
 #elif PTTYPE == 32
@@ -52,7 +51,7 @@
        #define PT_GUEST_DIRTY_SHIFT PT_DIRTY_SHIFT
        #define PT_GUEST_ACCESSED_SHIFT PT_ACCESSED_SHIFT
        #define PT_HAVE_ACCESSED_DIRTY(mmu) true
-       #define CMPXCHG cmpxchg
+       #define CMPXCHG "cmpxchgl"
 #elif PTTYPE == PTTYPE_EPT
        #define pt_element_t u64
        #define guest_walker guest_walkerEPT
@@ -65,7 +64,9 @@
        #define PT_GUEST_DIRTY_SHIFT 9
        #define PT_GUEST_ACCESSED_SHIFT 8
        #define PT_HAVE_ACCESSED_DIRTY(mmu) ((mmu)->ept_ad)
-       #define CMPXCHG cmpxchg64
+       #ifdef CONFIG_X86_64
+       #define CMPXCHG "cmpxchgq"
+       #endif
        #define PT_MAX_FULL_LEVELS PT64_ROOT_MAX_LEVEL
 #else
        #error Invalid PTTYPE value
@@ -147,43 +148,36 @@ static int FNAME(cmpxchg_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
                               pt_element_t __user *ptep_user, unsigned index,
                               pt_element_t orig_pte, pt_element_t new_pte)
 {
-       int npages;
-       pt_element_t ret;
-       pt_element_t *table;
-       struct page *page;
-
-       npages = get_user_pages_fast((unsigned long)ptep_user, 1, FOLL_WRITE, &page);
-       if (likely(npages == 1)) {
-               table = kmap_atomic(page);
-               ret = CMPXCHG(&table[index], orig_pte, new_pte);
-               kunmap_atomic(table);
-
-               kvm_release_page_dirty(page);
-       } else {
-               struct vm_area_struct *vma;
-               unsigned long vaddr = (unsigned long)ptep_user & PAGE_MASK;
-               unsigned long pfn;
-               unsigned long paddr;
-
-               mmap_read_lock(current->mm);
-               vma = find_vma_intersection(current->mm, vaddr, vaddr + PAGE_SIZE);
-               if (!vma || !(vma->vm_flags & VM_PFNMAP)) {
-                       mmap_read_unlock(current->mm);
-                       return -EFAULT;
-               }
-               pfn = ((vaddr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
-               paddr = pfn << PAGE_SHIFT;
-               table = memremap(paddr, PAGE_SIZE, MEMREMAP_WB);
-               if (!table) {
-                       mmap_read_unlock(current->mm);
-                       return -EFAULT;
-               }
-               ret = CMPXCHG(&table[index], orig_pte, new_pte);
-               memunmap(table);
-               mmap_read_unlock(current->mm);
-       }
+       signed char r;
 
-       return (ret != orig_pte);
+       if (!user_access_begin(ptep_user, sizeof(pt_element_t)))
+               return -EFAULT;
+
+#ifdef CMPXCHG
+       asm volatile("1:" LOCK_PREFIX CMPXCHG " %[new], %[ptr]\n"
+                    "setnz %b[r]\n"
+                    "2:"
+                    _ASM_EXTABLE_TYPE_REG(1b, 2b, EX_TYPE_EFAULT_REG, %k[r])
+                    : [ptr] "+m" (*ptep_user),
+                      [old] "+a" (orig_pte),
+                      [r] "=q" (r)
+                    : [new] "r" (new_pte)
+                    : "memory");
+#else
+       asm volatile("1:" LOCK_PREFIX "cmpxchg8b %[ptr]\n"
+                    "setnz %b[r]\n"
+                    "2:"
+                    _ASM_EXTABLE_TYPE_REG(1b, 2b, EX_TYPE_EFAULT_REG, %k[r])
+                    : [ptr] "+m" (*ptep_user),
+                      [old] "+A" (orig_pte),
+                      [r] "=q" (r)
+                    : [new_lo] "b" ((u32)new_pte),
+                      [new_hi] "c" ((u32)(new_pte >> 32))
+                    : "memory");
+#endif
+
+       user_access_end();
+       return r;
 }
 
 static bool FNAME(prefetch_invalid_gpte)(struct kvm_vcpu *vcpu,
@@ -339,7 +333,7 @@ static inline bool FNAME(is_last_gpte)(struct kvm_mmu *mmu,
  */
 static int FNAME(walk_addr_generic)(struct guest_walker *walker,
                                    struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
-                                   gpa_t addr, u32 access)
+                                   gpa_t addr, u64 access)
 {
        int ret;
        pt_element_t pte;
@@ -347,7 +341,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
        gfn_t table_gfn;
        u64 pt_access, pte_access;
        unsigned index, accessed_dirty, pte_pkey;
-       unsigned nested_access;
+       u64 nested_access;
        gpa_t pte_gpa;
        bool have_ad;
        int offset;
@@ -540,7 +534,7 @@ error:
 }
 
 static int FNAME(walk_addr)(struct guest_walker *walker,
-                           struct kvm_vcpu *vcpu, gpa_t addr, u32 access)
+                           struct kvm_vcpu *vcpu, gpa_t addr, u64 access)
 {
        return FNAME(walk_addr_generic)(walker, vcpu, vcpu->arch.mmu, addr,
                                        access);
@@ -988,7 +982,7 @@ static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva, hpa_t root_hpa)
 
 /* Note, @addr is a GPA when gva_to_gpa() translates an L2 GPA to an L1 GPA. */
 static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
-                              gpa_t addr, u32 access,
+                              gpa_t addr, u64 access,
                               struct x86_exception *exception)
 {
        struct guest_walker walker;