diff options
Diffstat (limited to 'drivers/kvm/paging_tmpl.h')
-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 | ||