diff options
| -rw-r--r-- | drivers/kvm/paging_tmpl.h | 36 |
1 files changed, 34 insertions, 2 deletions
diff --git a/drivers/kvm/paging_tmpl.h b/drivers/kvm/paging_tmpl.h index 3c40a598ceb8..8086f827120f 100644 --- a/drivers/kvm/paging_tmpl.h +++ b/drivers/kvm/paging_tmpl.h | |||
| @@ -34,7 +34,9 @@ | |||
| 34 | #define PT_LEVEL_BITS PT64_LEVEL_BITS | 34 | #define PT_LEVEL_BITS PT64_LEVEL_BITS |
| 35 | #ifdef CONFIG_X86_64 | 35 | #ifdef CONFIG_X86_64 |
| 36 | #define PT_MAX_FULL_LEVELS 4 | 36 | #define PT_MAX_FULL_LEVELS 4 |
| 37 | #define CMPXCHG cmpxchg | ||
| 37 | #else | 38 | #else |
| 39 | #define CMPXCHG cmpxchg64 | ||
| 38 | #define PT_MAX_FULL_LEVELS 2 | 40 | #define PT_MAX_FULL_LEVELS 2 |
| 39 | #endif | 41 | #endif |
| 40 | #elif PTTYPE == 32 | 42 | #elif PTTYPE == 32 |
| @@ -48,6 +50,7 @@ | |||
| 48 | #define PT_LEVEL_MASK(level) PT32_LEVEL_MASK(level) | 50 | #define PT_LEVEL_MASK(level) PT32_LEVEL_MASK(level) |
| 49 | #define PT_LEVEL_BITS PT32_LEVEL_BITS | 51 | #define PT_LEVEL_BITS PT32_LEVEL_BITS |
| 50 | #define PT_MAX_FULL_LEVELS 2 | 52 | #define PT_MAX_FULL_LEVELS 2 |
| 53 | #define CMPXCHG cmpxchg | ||
| 51 | #else | 54 | #else |
| 52 | #error Invalid PTTYPE value | 55 | #error Invalid PTTYPE value |
| 53 | #endif | 56 | #endif |
| @@ -78,6 +81,26 @@ static gfn_t gpte_to_gfn_pde(pt_element_t gpte) | |||
| 78 | return (gpte & PT_DIR_BASE_ADDR_MASK) >> PAGE_SHIFT; | 81 | return (gpte & PT_DIR_BASE_ADDR_MASK) >> PAGE_SHIFT; |
| 79 | } | 82 | } |
| 80 | 83 | ||
| 84 | static bool FNAME(cmpxchg_gpte)(struct kvm *kvm, | ||
| 85 | gfn_t table_gfn, unsigned index, | ||
| 86 | pt_element_t orig_pte, pt_element_t new_pte) | ||
| 87 | { | ||
| 88 | pt_element_t ret; | ||
| 89 | pt_element_t *table; | ||
| 90 | struct page *page; | ||
| 91 | |||
| 92 | page = gfn_to_page(kvm, table_gfn); | ||
| 93 | table = kmap_atomic(page, KM_USER0); | ||
| 94 | |||
| 95 | ret = CMPXCHG(&table[index], orig_pte, new_pte); | ||
| 96 | |||
| 97 | kunmap_atomic(table, KM_USER0); | ||
| 98 | |||
| 99 | kvm_release_page_dirty(page); | ||
| 100 | |||
| 101 | return (ret != orig_pte); | ||
| 102 | } | ||
| 103 | |||
| 81 | /* | 104 | /* |
| 82 | * Fetch a guest pte for a guest virtual address | 105 | * Fetch a guest pte for a guest virtual address |
| 83 | */ | 106 | */ |
| @@ -91,6 +114,7 @@ static int FNAME(walk_addr)(struct guest_walker *walker, | |||
| 91 | gpa_t pte_gpa; | 114 | gpa_t pte_gpa; |
| 92 | 115 | ||
| 93 | pgprintk("%s: addr %lx\n", __FUNCTION__, addr); | 116 | pgprintk("%s: addr %lx\n", __FUNCTION__, addr); |
| 117 | walk: | ||
| 94 | walker->level = vcpu->mmu.root_level; | 118 | walker->level = vcpu->mmu.root_level; |
| 95 | pte = vcpu->cr3; | 119 | pte = vcpu->cr3; |
| 96 | #if PTTYPE == 64 | 120 | #if PTTYPE == 64 |
| @@ -135,8 +159,10 @@ static int FNAME(walk_addr)(struct guest_walker *walker, | |||
| 135 | 159 | ||
| 136 | if (!(pte & PT_ACCESSED_MASK)) { | 160 | if (!(pte & PT_ACCESSED_MASK)) { |
| 137 | mark_page_dirty(vcpu->kvm, table_gfn); | 161 | mark_page_dirty(vcpu->kvm, table_gfn); |
| 162 | if (FNAME(cmpxchg_gpte)(vcpu->kvm, table_gfn, | ||
| 163 | index, pte, pte|PT_ACCESSED_MASK)) | ||
| 164 | goto walk; | ||
| 138 | pte |= PT_ACCESSED_MASK; | 165 | pte |= PT_ACCESSED_MASK; |
| 139 | kvm_write_guest(vcpu->kvm, pte_gpa, &pte, sizeof(pte)); | ||
| 140 | } | 166 | } |
| 141 | 167 | ||
| 142 | if (walker->level == PT_PAGE_TABLE_LEVEL) { | 168 | if (walker->level == PT_PAGE_TABLE_LEVEL) { |
| @@ -159,9 +185,14 @@ static int FNAME(walk_addr)(struct guest_walker *walker, | |||
| 159 | } | 185 | } |
| 160 | 186 | ||
| 161 | if (write_fault && !is_dirty_pte(pte)) { | 187 | if (write_fault && !is_dirty_pte(pte)) { |
| 188 | bool ret; | ||
| 189 | |||
| 162 | mark_page_dirty(vcpu->kvm, table_gfn); | 190 | mark_page_dirty(vcpu->kvm, table_gfn); |
| 191 | ret = FNAME(cmpxchg_gpte)(vcpu->kvm, table_gfn, index, pte, | ||
| 192 | pte|PT_DIRTY_MASK); | ||
| 193 | if (ret) | ||
| 194 | goto walk; | ||
| 163 | pte |= PT_DIRTY_MASK; | 195 | pte |= PT_DIRTY_MASK; |
| 164 | kvm_write_guest(vcpu->kvm, pte_gpa, &pte, sizeof(pte)); | ||
| 165 | kvm_mmu_pte_write(vcpu, pte_gpa, (u8 *)&pte, sizeof(pte)); | 196 | kvm_mmu_pte_write(vcpu, pte_gpa, (u8 *)&pte, sizeof(pte)); |
| 166 | } | 197 | } |
| 167 | 198 | ||
| @@ -484,3 +515,4 @@ static void FNAME(prefetch_page)(struct kvm_vcpu *vcpu, | |||
| 484 | #undef PT_MAX_FULL_LEVELS | 515 | #undef PT_MAX_FULL_LEVELS |
| 485 | #undef gpte_to_gfn | 516 | #undef gpte_to_gfn |
| 486 | #undef gpte_to_gfn_pde | 517 | #undef gpte_to_gfn_pde |
| 518 | #undef CMPXCHG | ||
