aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/kvm/paging_tmpl.h
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/kvm/paging_tmpl.h')
-rw-r--r--drivers/kvm/paging_tmpl.h36
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
84static 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);
117walk:
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