1
2
3
4
5
6
7
8
9
10#include <linux/module.h>
11#include <linux/mm.h>
12#include <linux/pagemap.h>
13#include <linux/highmem.h>
14
15#include <asm/cacheflush.h>
16#include <asm/cachetype.h>
17#include <asm/highmem.h>
18#include <asm/smp_plat.h>
19#include <asm/system.h>
20#include <asm/tlbflush.h>
21#include <asm/smp_plat.h>
22
23#include "mm.h"
24
25#ifdef CONFIG_CPU_CACHE_VIPT
26
27#define ALIAS_FLUSH_START 0xffff4000
28
29static void flush_pfn_alias(unsigned long pfn, unsigned long vaddr)
30{
31 unsigned long to = ALIAS_FLUSH_START + (CACHE_COLOUR(vaddr) << PAGE_SHIFT);
32 const int zero = 0;
33
34 set_pte_ext(TOP_PTE(to), pfn_pte(pfn, PAGE_KERNEL), 0);
35 flush_tlb_kernel_page(to);
36
37 asm( "mcrr p15, 0, %1, %0, c14\n"
38 " mcr p15, 0, %2, c7, c10, 4"
39 :
40 : "r" (to), "r" (to + PAGE_SIZE - L1_CACHE_BYTES), "r" (zero)
41 : "cc");
42}
43
44static void flush_icache_alias(unsigned long pfn, unsigned long vaddr, unsigned long len)
45{
46 unsigned long colour = CACHE_COLOUR(vaddr);
47 unsigned long offset = vaddr & (PAGE_SIZE - 1);
48 unsigned long to;
49
50 set_pte_ext(TOP_PTE(ALIAS_FLUSH_START) + colour, pfn_pte(pfn, PAGE_KERNEL), 0);
51 to = ALIAS_FLUSH_START + (colour << PAGE_SHIFT) + offset;
52 flush_tlb_kernel_page(to);
53 flush_icache_range(to, to + len);
54}
55
56void flush_cache_mm(struct mm_struct *mm)
57{
58 if (cache_is_vivt()) {
59 vivt_flush_cache_mm(mm);
60 return;
61 }
62
63 if (cache_is_vipt_aliasing()) {
64 asm( "mcr p15, 0, %0, c7, c14, 0\n"
65 " mcr p15, 0, %0, c7, c10, 4"
66 :
67 : "r" (0)
68 : "cc");
69 }
70}
71
72void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
73{
74 if (cache_is_vivt()) {
75 vivt_flush_cache_range(vma, start, end);
76 return;
77 }
78
79 if (cache_is_vipt_aliasing()) {
80 asm( "mcr p15, 0, %0, c7, c14, 0\n"
81 " mcr p15, 0, %0, c7, c10, 4"
82 :
83 : "r" (0)
84 : "cc");
85 }
86
87 if (vma->vm_flags & VM_EXEC)
88 __flush_icache_all();
89}
90
91void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsigned long pfn)
92{
93 if (cache_is_vivt()) {
94 vivt_flush_cache_page(vma, user_addr, pfn);
95 return;
96 }
97
98 if (cache_is_vipt_aliasing()) {
99 flush_pfn_alias(pfn, user_addr);
100 __flush_icache_all();
101 }
102
103 if (vma->vm_flags & VM_EXEC && icache_is_vivt_asid_tagged())
104 __flush_icache_all();
105}
106
107#else
108#define flush_pfn_alias(pfn,vaddr) do { } while (0)
109#define flush_icache_alias(pfn,vaddr,len) do { } while (0)
110#endif
111
112static void flush_ptrace_access_other(void *args)
113{
114 __flush_icache_all();
115}
116
117static
118void flush_ptrace_access(struct vm_area_struct *vma, struct page *page,
119 unsigned long uaddr, void *kaddr, unsigned long len)
120{
121 if (cache_is_vivt()) {
122 if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) {
123 unsigned long addr = (unsigned long)kaddr;
124 __cpuc_coherent_kern_range(addr, addr + len);
125 }
126 return;
127 }
128
129 if (cache_is_vipt_aliasing()) {
130 flush_pfn_alias(page_to_pfn(page), uaddr);
131 __flush_icache_all();
132 return;
133 }
134
135
136 if (vma->vm_flags & VM_EXEC) {
137 unsigned long addr = (unsigned long)kaddr;
138 if (icache_is_vipt_aliasing())
139 flush_icache_alias(page_to_pfn(page), uaddr, len);
140 else
141 __cpuc_coherent_kern_range(addr, addr + len);
142 if (cache_ops_need_broadcast())
143 smp_call_function(flush_ptrace_access_other,
144 NULL, 1);
145 }
146}
147
148
149
150
151
152
153
154
155void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
156 unsigned long uaddr, void *dst, const void *src,
157 unsigned long len)
158{
159#ifdef CONFIG_SMP
160 preempt_disable();
161#endif
162 memcpy(dst, src, len);
163 flush_ptrace_access(vma, page, uaddr, dst, len);
164#ifdef CONFIG_SMP
165 preempt_enable();
166#endif
167}
168
169void __flush_dcache_page(struct address_space *mapping, struct page *page)
170{
171
172
173
174
175
176 if (!PageHighMem(page)) {
177 __cpuc_flush_dcache_area(page_address(page), PAGE_SIZE);
178 } else {
179 void *addr = kmap_high_get(page);
180 if (addr) {
181 __cpuc_flush_dcache_area(addr, PAGE_SIZE);
182 kunmap_high(page);
183 } else if (cache_is_vipt()) {
184
185 addr = kmap_atomic(page);
186 __cpuc_flush_dcache_area(addr, PAGE_SIZE);
187 kunmap_atomic(addr);
188 }
189 }
190
191
192
193
194
195
196 if (mapping && cache_is_vipt_aliasing())
197 flush_pfn_alias(page_to_pfn(page),
198 page->index << PAGE_CACHE_SHIFT);
199}
200
201static void __flush_dcache_aliases(struct address_space *mapping, struct page *page)
202{
203 struct mm_struct *mm = current->active_mm;
204 struct vm_area_struct *mpnt;
205 struct prio_tree_iter iter;
206 pgoff_t pgoff;
207
208
209
210
211
212
213
214 pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
215
216 flush_dcache_mmap_lock(mapping);
217 vma_prio_tree_foreach(mpnt, &iter, &mapping->i_mmap, pgoff, pgoff) {
218 unsigned long offset;
219
220
221
222
223 if (mpnt->vm_mm != mm)
224 continue;
225 if (!(mpnt->vm_flags & VM_MAYSHARE))
226 continue;
227 offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT;
228 flush_cache_page(mpnt, mpnt->vm_start + offset, page_to_pfn(page));
229 }
230 flush_dcache_mmap_unlock(mapping);
231}
232
233#if __LINUX_ARM_ARCH__ >= 6
234void __sync_icache_dcache(pte_t pteval)
235{
236 unsigned long pfn;
237 struct page *page;
238 struct address_space *mapping;
239
240 if (!pte_present_user(pteval))
241 return;
242 if (cache_is_vipt_nonaliasing() && !pte_exec(pteval))
243
244 return;
245 pfn = pte_pfn(pteval);
246 if (!pfn_valid(pfn))
247 return;
248
249 page = pfn_to_page(pfn);
250 if (cache_is_vipt_aliasing())
251 mapping = page_mapping(page);
252 else
253 mapping = NULL;
254
255 if (!test_and_set_bit(PG_dcache_clean, &page->flags))
256 __flush_dcache_page(mapping, page);
257
258 if (cache_is_vipt_nonaliasing() || pte_exec(pteval))
259 __flush_icache_all();
260}
261#endif
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281void flush_dcache_page(struct page *page)
282{
283 struct address_space *mapping;
284
285
286
287
288
289 if (page == ZERO_PAGE(0))
290 return;
291
292 mapping = page_mapping(page);
293
294 if (!cache_ops_need_broadcast() &&
295 mapping && !mapping_mapped(mapping))
296 clear_bit(PG_dcache_clean, &page->flags);
297 else {
298 __flush_dcache_page(mapping, page);
299 if (mapping && cache_is_vivt())
300 __flush_dcache_aliases(mapping, page);
301 else if (mapping)
302 __flush_icache_all();
303 set_bit(PG_dcache_clean, &page->flags);
304 }
305}
306EXPORT_SYMBOL(flush_dcache_page);
307
308
309
310
311
312
313
314
315
316
317void __flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned long vmaddr)
318{
319 unsigned long pfn;
320
321
322 if (cache_is_vipt_nonaliasing())
323 return;
324
325
326
327
328 pfn = page_to_pfn(page);
329 if (cache_is_vivt()) {
330 flush_cache_page(vma, vmaddr, pfn);
331 } else {
332
333
334
335
336 flush_pfn_alias(pfn, vmaddr);
337 __flush_icache_all();
338 }
339
340
341
342
343
344
345 __cpuc_flush_dcache_area(page_address(page), PAGE_SIZE);
346}
347