diff options
-rw-r--r-- | arch/arm/include/asm/kvm_mmu.h | 12 | ||||
-rw-r--r-- | arch/arm/kvm/mmu.c | 157 | ||||
-rw-r--r-- | arch/arm64/include/asm/kvm_mmu.h | 15 |
3 files changed, 111 insertions, 73 deletions
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h index 5c7aa3c1519f..5cc0b0f5f72f 100644 --- a/arch/arm/include/asm/kvm_mmu.h +++ b/arch/arm/include/asm/kvm_mmu.h | |||
@@ -127,6 +127,18 @@ static inline void kvm_set_s2pmd_writable(pmd_t *pmd) | |||
127 | (__boundary - 1 < (end) - 1)? __boundary: (end); \ | 127 | (__boundary - 1 < (end) - 1)? __boundary: (end); \ |
128 | }) | 128 | }) |
129 | 129 | ||
130 | static inline bool kvm_page_empty(void *ptr) | ||
131 | { | ||
132 | struct page *ptr_page = virt_to_page(ptr); | ||
133 | return page_count(ptr_page) == 1; | ||
134 | } | ||
135 | |||
136 | |||
137 | #define kvm_pte_table_empty(ptep) kvm_page_empty(ptep) | ||
138 | #define kvm_pmd_table_empty(pmdp) kvm_page_empty(pmdp) | ||
139 | #define kvm_pud_table_empty(pudp) (0) | ||
140 | |||
141 | |||
130 | struct kvm; | 142 | struct kvm; |
131 | 143 | ||
132 | #define kvm_flush_dcache_to_poc(a,l) __cpuc_flush_dcache_area((a), (l)) | 144 | #define kvm_flush_dcache_to_poc(a,l) __cpuc_flush_dcache_area((a), (l)) |
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index 16f804938b8f..23360610aeac 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c | |||
@@ -90,104 +90,115 @@ static void *mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc) | |||
90 | return p; | 90 | return p; |
91 | } | 91 | } |
92 | 92 | ||
93 | static bool page_empty(void *ptr) | 93 | static void clear_pgd_entry(struct kvm *kvm, pgd_t *pgd, phys_addr_t addr) |
94 | { | 94 | { |
95 | struct page *ptr_page = virt_to_page(ptr); | 95 | pud_t *pud_table __maybe_unused = pud_offset(pgd, 0); |
96 | return page_count(ptr_page) == 1; | 96 | pgd_clear(pgd); |
97 | kvm_tlb_flush_vmid_ipa(kvm, addr); | ||
98 | pud_free(NULL, pud_table); | ||
99 | put_page(virt_to_page(pgd)); | ||
97 | } | 100 | } |
98 | 101 | ||
99 | static void clear_pud_entry(struct kvm *kvm, pud_t *pud, phys_addr_t addr) | 102 | static void clear_pud_entry(struct kvm *kvm, pud_t *pud, phys_addr_t addr) |
100 | { | 103 | { |
101 | if (pud_huge(*pud)) { | 104 | pmd_t *pmd_table = pmd_offset(pud, 0); |
102 | pud_clear(pud); | 105 | VM_BUG_ON(pud_huge(*pud)); |
103 | kvm_tlb_flush_vmid_ipa(kvm, addr); | 106 | pud_clear(pud); |
104 | } else { | 107 | kvm_tlb_flush_vmid_ipa(kvm, addr); |
105 | pmd_t *pmd_table = pmd_offset(pud, 0); | 108 | pmd_free(NULL, pmd_table); |
106 | pud_clear(pud); | ||
107 | kvm_tlb_flush_vmid_ipa(kvm, addr); | ||
108 | pmd_free(NULL, pmd_table); | ||
109 | } | ||
110 | put_page(virt_to_page(pud)); | 109 | put_page(virt_to_page(pud)); |
111 | } | 110 | } |
112 | 111 | ||
113 | static void clear_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr) | 112 | static void clear_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr) |
114 | { | 113 | { |
115 | if (kvm_pmd_huge(*pmd)) { | 114 | pte_t *pte_table = pte_offset_kernel(pmd, 0); |
116 | pmd_clear(pmd); | 115 | VM_BUG_ON(kvm_pmd_huge(*pmd)); |
117 | kvm_tlb_flush_vmid_ipa(kvm, addr); | 116 | pmd_clear(pmd); |
118 | } else { | 117 | kvm_tlb_flush_vmid_ipa(kvm, addr); |
119 | pte_t *pte_table = pte_offset_kernel(pmd, 0); | 118 | pte_free_kernel(NULL, pte_table); |
120 | pmd_clear(pmd); | ||
121 | kvm_tlb_flush_vmid_ipa(kvm, addr); | ||
122 | pte_free_kernel(NULL, pte_table); | ||
123 | } | ||
124 | put_page(virt_to_page(pmd)); | 119 | put_page(virt_to_page(pmd)); |
125 | } | 120 | } |
126 | 121 | ||
127 | static void clear_pte_entry(struct kvm *kvm, pte_t *pte, phys_addr_t addr) | 122 | static void unmap_ptes(struct kvm *kvm, pmd_t *pmd, |
123 | phys_addr_t addr, phys_addr_t end) | ||
128 | { | 124 | { |
129 | if (pte_present(*pte)) { | 125 | phys_addr_t start_addr = addr; |
130 | kvm_set_pte(pte, __pte(0)); | 126 | pte_t *pte, *start_pte; |
131 | put_page(virt_to_page(pte)); | 127 | |
132 | kvm_tlb_flush_vmid_ipa(kvm, addr); | 128 | start_pte = pte = pte_offset_kernel(pmd, addr); |
133 | } | 129 | do { |
130 | if (!pte_none(*pte)) { | ||
131 | kvm_set_pte(pte, __pte(0)); | ||
132 | put_page(virt_to_page(pte)); | ||
133 | kvm_tlb_flush_vmid_ipa(kvm, addr); | ||
134 | } | ||
135 | } while (pte++, addr += PAGE_SIZE, addr != end); | ||
136 | |||
137 | if (kvm_pte_table_empty(start_pte)) | ||
138 | clear_pmd_entry(kvm, pmd, start_addr); | ||
134 | } | 139 | } |
135 | 140 | ||
136 | static void unmap_range(struct kvm *kvm, pgd_t *pgdp, | 141 | static void unmap_pmds(struct kvm *kvm, pud_t *pud, |
137 | unsigned long long start, u64 size) | 142 | phys_addr_t addr, phys_addr_t end) |
138 | { | 143 | { |
139 | pgd_t *pgd; | 144 | phys_addr_t next, start_addr = addr; |
140 | pud_t *pud; | 145 | pmd_t *pmd, *start_pmd; |
141 | pmd_t *pmd; | ||
142 | pte_t *pte; | ||
143 | unsigned long long addr = start, end = start + size; | ||
144 | u64 next; | ||
145 | 146 | ||
146 | while (addr < end) { | 147 | start_pmd = pmd = pmd_offset(pud, addr); |
147 | pgd = pgdp + pgd_index(addr); | 148 | do { |
148 | pud = pud_offset(pgd, addr); | 149 | next = kvm_pmd_addr_end(addr, end); |
149 | pte = NULL; | 150 | if (!pmd_none(*pmd)) { |
150 | if (pud_none(*pud)) { | 151 | if (kvm_pmd_huge(*pmd)) { |
151 | addr = kvm_pud_addr_end(addr, end); | 152 | pmd_clear(pmd); |
152 | continue; | 153 | kvm_tlb_flush_vmid_ipa(kvm, addr); |
153 | } | 154 | put_page(virt_to_page(pmd)); |
154 | 155 | } else { | |
155 | if (pud_huge(*pud)) { | 156 | unmap_ptes(kvm, pmd, addr, next); |
156 | /* | 157 | } |
157 | * If we are dealing with a huge pud, just clear it and | ||
158 | * move on. | ||
159 | */ | ||
160 | clear_pud_entry(kvm, pud, addr); | ||
161 | addr = kvm_pud_addr_end(addr, end); | ||
162 | continue; | ||
163 | } | 158 | } |
159 | } while (pmd++, addr = next, addr != end); | ||
164 | 160 | ||
165 | pmd = pmd_offset(pud, addr); | 161 | if (kvm_pmd_table_empty(start_pmd)) |
166 | if (pmd_none(*pmd)) { | 162 | clear_pud_entry(kvm, pud, start_addr); |
167 | addr = kvm_pmd_addr_end(addr, end); | 163 | } |
168 | continue; | ||
169 | } | ||
170 | 164 | ||
171 | if (!kvm_pmd_huge(*pmd)) { | 165 | static void unmap_puds(struct kvm *kvm, pgd_t *pgd, |
172 | pte = pte_offset_kernel(pmd, addr); | 166 | phys_addr_t addr, phys_addr_t end) |
173 | clear_pte_entry(kvm, pte, addr); | 167 | { |
174 | next = addr + PAGE_SIZE; | 168 | phys_addr_t next, start_addr = addr; |
175 | } | 169 | pud_t *pud, *start_pud; |
176 | 170 | ||
177 | /* | 171 | start_pud = pud = pud_offset(pgd, addr); |
178 | * If the pmd entry is to be cleared, walk back up the ladder | 172 | do { |
179 | */ | 173 | next = kvm_pud_addr_end(addr, end); |
180 | if (kvm_pmd_huge(*pmd) || (pte && page_empty(pte))) { | 174 | if (!pud_none(*pud)) { |
181 | clear_pmd_entry(kvm, pmd, addr); | 175 | if (pud_huge(*pud)) { |
182 | next = kvm_pmd_addr_end(addr, end); | 176 | pud_clear(pud); |
183 | if (page_empty(pmd) && !page_empty(pud)) { | 177 | kvm_tlb_flush_vmid_ipa(kvm, addr); |
184 | clear_pud_entry(kvm, pud, addr); | 178 | put_page(virt_to_page(pud)); |
185 | next = kvm_pud_addr_end(addr, end); | 179 | } else { |
180 | unmap_pmds(kvm, pud, addr, next); | ||
186 | } | 181 | } |
187 | } | 182 | } |
183 | } while (pud++, addr = next, addr != end); | ||
188 | 184 | ||
189 | addr = next; | 185 | if (kvm_pud_table_empty(start_pud)) |
190 | } | 186 | clear_pgd_entry(kvm, pgd, start_addr); |
187 | } | ||
188 | |||
189 | |||
190 | static void unmap_range(struct kvm *kvm, pgd_t *pgdp, | ||
191 | phys_addr_t start, u64 size) | ||
192 | { | ||
193 | pgd_t *pgd; | ||
194 | phys_addr_t addr = start, end = start + size; | ||
195 | phys_addr_t next; | ||
196 | |||
197 | pgd = pgdp + pgd_index(addr); | ||
198 | do { | ||
199 | next = kvm_pgd_addr_end(addr, end); | ||
200 | unmap_puds(kvm, pgd, addr, next); | ||
201 | } while (pgd++, addr = next, addr != end); | ||
191 | } | 202 | } |
192 | 203 | ||
193 | static void stage2_flush_ptes(struct kvm *kvm, pmd_t *pmd, | 204 | static void stage2_flush_ptes(struct kvm *kvm, pmd_t *pmd, |
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index 7d29847a893b..8e138c7c53ac 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h | |||
@@ -125,6 +125,21 @@ static inline void kvm_set_s2pmd_writable(pmd_t *pmd) | |||
125 | #define kvm_pud_addr_end(addr, end) pud_addr_end(addr, end) | 125 | #define kvm_pud_addr_end(addr, end) pud_addr_end(addr, end) |
126 | #define kvm_pmd_addr_end(addr, end) pmd_addr_end(addr, end) | 126 | #define kvm_pmd_addr_end(addr, end) pmd_addr_end(addr, end) |
127 | 127 | ||
128 | static inline bool kvm_page_empty(void *ptr) | ||
129 | { | ||
130 | struct page *ptr_page = virt_to_page(ptr); | ||
131 | return page_count(ptr_page) == 1; | ||
132 | } | ||
133 | |||
134 | #define kvm_pte_table_empty(ptep) kvm_page_empty(ptep) | ||
135 | #ifndef CONFIG_ARM64_64K_PAGES | ||
136 | #define kvm_pmd_table_empty(pmdp) kvm_page_empty(pmdp) | ||
137 | #else | ||
138 | #define kvm_pmd_table_empty(pmdp) (0) | ||
139 | #endif | ||
140 | #define kvm_pud_table_empty(pudp) (0) | ||
141 | |||
142 | |||
128 | struct kvm; | 143 | struct kvm; |
129 | 144 | ||
130 | #define kvm_flush_dcache_to_poc(a,l) __flush_dcache_area((a), (l)) | 145 | #define kvm_flush_dcache_to_poc(a,l) __flush_dcache_area((a), (l)) |