diff options
author | Max Filippov <jcmvbkbc@gmail.com> | 2014-07-21 10:54:11 -0400 |
---|---|---|
committer | Max Filippov <jcmvbkbc@gmail.com> | 2014-08-14 03:59:20 -0400 |
commit | a91902db2990909ea5e6b110811b448f2e8f1571 (patch) | |
tree | 133c118a40292c1c4480acaaa1df1add1b70eaf0 | |
parent | 7128039fe2dd3d59da9e4ffa036f3aaa3ba87b9f (diff) |
xtensa: implement clear_user_highpage and copy_user_highpage
Existing clear_user_page and copy_user_page cannot be used with highmem
because they calculate physical page address from its virtual address
and do it incorrectly in case of high memory page mapped with
kmap_atomic. Also kmap is not needed, as most likely userspace mapping
color would be different from the kmapped color.
Provide clear_user_highpage and copy_user_highpage functions that
determine if temporary mapping is needed for the pages. Move most of the
logic of the former clear_user_page and copy_user_page to
xtensa/mm/cache.c only leaving temporary mapping setup, invalidation and
clearing/copying in the xtensa/mm/misc.S. Rename these functions to
clear_page_alias and copy_page_alias.
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
-rw-r--r-- | arch/xtensa/include/asm/cacheflush.h | 2 | ||||
-rw-r--r-- | arch/xtensa/include/asm/page.h | 12 | ||||
-rw-r--r-- | arch/xtensa/mm/cache.c | 63 | ||||
-rw-r--r-- | arch/xtensa/mm/misc.S | 116 |
4 files changed, 127 insertions, 66 deletions
diff --git a/arch/xtensa/include/asm/cacheflush.h b/arch/xtensa/include/asm/cacheflush.h index 555a98a18453..e72aaca7a77f 100644 --- a/arch/xtensa/include/asm/cacheflush.h +++ b/arch/xtensa/include/asm/cacheflush.h | |||
@@ -37,6 +37,7 @@ | |||
37 | * specials for cache aliasing: | 37 | * specials for cache aliasing: |
38 | * | 38 | * |
39 | * __flush_invalidate_dcache_page_alias(vaddr,paddr) | 39 | * __flush_invalidate_dcache_page_alias(vaddr,paddr) |
40 | * __invalidate_dcache_page_alias(vaddr,paddr) | ||
40 | * __invalidate_icache_page_alias(vaddr,paddr) | 41 | * __invalidate_icache_page_alias(vaddr,paddr) |
41 | */ | 42 | */ |
42 | 43 | ||
@@ -62,6 +63,7 @@ extern void __flush_invalidate_dcache_range(unsigned long, unsigned long); | |||
62 | 63 | ||
63 | #if defined(CONFIG_MMU) && (DCACHE_WAY_SIZE > PAGE_SIZE) | 64 | #if defined(CONFIG_MMU) && (DCACHE_WAY_SIZE > PAGE_SIZE) |
64 | extern void __flush_invalidate_dcache_page_alias(unsigned long, unsigned long); | 65 | extern void __flush_invalidate_dcache_page_alias(unsigned long, unsigned long); |
66 | extern void __invalidate_dcache_page_alias(unsigned long, unsigned long); | ||
65 | #else | 67 | #else |
66 | static inline void __flush_invalidate_dcache_page_alias(unsigned long virt, | 68 | static inline void __flush_invalidate_dcache_page_alias(unsigned long virt, |
67 | unsigned long phys) { } | 69 | unsigned long phys) { } |
diff --git a/arch/xtensa/include/asm/page.h b/arch/xtensa/include/asm/page.h index 47f582333f6b..11721ccd7f23 100644 --- a/arch/xtensa/include/asm/page.h +++ b/arch/xtensa/include/asm/page.h | |||
@@ -134,6 +134,7 @@ static inline __attribute_const__ int get_order(unsigned long size) | |||
134 | #endif | 134 | #endif |
135 | 135 | ||
136 | struct page; | 136 | struct page; |
137 | struct vm_area_struct; | ||
137 | extern void clear_page(void *page); | 138 | extern void clear_page(void *page); |
138 | extern void copy_page(void *to, void *from); | 139 | extern void copy_page(void *to, void *from); |
139 | 140 | ||
@@ -143,8 +144,15 @@ extern void copy_page(void *to, void *from); | |||
143 | */ | 144 | */ |
144 | 145 | ||
145 | #if DCACHE_WAY_SIZE > PAGE_SIZE | 146 | #if DCACHE_WAY_SIZE > PAGE_SIZE |
146 | extern void clear_user_page(void*, unsigned long, struct page*); | 147 | extern void clear_page_alias(void *vaddr, unsigned long paddr); |
147 | extern void copy_user_page(void*, void*, unsigned long, struct page*); | 148 | extern void copy_page_alias(void *to, void *from, |
149 | unsigned long to_paddr, unsigned long from_paddr); | ||
150 | |||
151 | #define clear_user_highpage clear_user_highpage | ||
152 | void clear_user_highpage(struct page *page, unsigned long vaddr); | ||
153 | #define __HAVE_ARCH_COPY_USER_HIGHPAGE | ||
154 | void copy_user_highpage(struct page *to, struct page *from, | ||
155 | unsigned long vaddr, struct vm_area_struct *vma); | ||
148 | #else | 156 | #else |
149 | # define clear_user_page(page, vaddr, pg) clear_page(page) | 157 | # define clear_user_page(page, vaddr, pg) clear_page(page) |
150 | # define copy_user_page(to, from, vaddr, pg) copy_page(to, from) | 158 | # define copy_user_page(to, from, vaddr, pg) copy_page(to, from) |
diff --git a/arch/xtensa/mm/cache.c b/arch/xtensa/mm/cache.c index 63cbb867dadd..96aea6624318 100644 --- a/arch/xtensa/mm/cache.c +++ b/arch/xtensa/mm/cache.c | |||
@@ -63,6 +63,69 @@ | |||
63 | #error "HIGHMEM is not supported on cores with aliasing cache." | 63 | #error "HIGHMEM is not supported on cores with aliasing cache." |
64 | #endif | 64 | #endif |
65 | 65 | ||
66 | #if (DCACHE_WAY_SIZE > PAGE_SIZE) | ||
67 | static inline void kmap_invalidate_coherent(struct page *page, | ||
68 | unsigned long vaddr) | ||
69 | { | ||
70 | if (!DCACHE_ALIAS_EQ(page_to_phys(page), vaddr)) { | ||
71 | unsigned long kvaddr; | ||
72 | |||
73 | if (!PageHighMem(page)) { | ||
74 | kvaddr = (unsigned long)page_to_virt(page); | ||
75 | |||
76 | __invalidate_dcache_page(kvaddr); | ||
77 | } else { | ||
78 | kvaddr = TLBTEMP_BASE_1 + | ||
79 | (page_to_phys(page) & DCACHE_ALIAS_MASK); | ||
80 | |||
81 | __invalidate_dcache_page_alias(kvaddr, | ||
82 | page_to_phys(page)); | ||
83 | } | ||
84 | } | ||
85 | } | ||
86 | |||
87 | static inline void *coherent_kvaddr(struct page *page, unsigned long base, | ||
88 | unsigned long vaddr, unsigned long *paddr) | ||
89 | { | ||
90 | if (PageHighMem(page) || !DCACHE_ALIAS_EQ(page_to_phys(page), vaddr)) { | ||
91 | *paddr = page_to_phys(page); | ||
92 | return (void *)(base + (vaddr & DCACHE_ALIAS_MASK)); | ||
93 | } else { | ||
94 | *paddr = 0; | ||
95 | return page_to_virt(page); | ||
96 | } | ||
97 | } | ||
98 | |||
99 | void clear_user_highpage(struct page *page, unsigned long vaddr) | ||
100 | { | ||
101 | unsigned long paddr; | ||
102 | void *kvaddr = coherent_kvaddr(page, TLBTEMP_BASE_1, vaddr, &paddr); | ||
103 | |||
104 | pagefault_disable(); | ||
105 | kmap_invalidate_coherent(page, vaddr); | ||
106 | set_bit(PG_arch_1, &page->flags); | ||
107 | clear_page_alias(kvaddr, paddr); | ||
108 | pagefault_enable(); | ||
109 | } | ||
110 | |||
111 | void copy_user_highpage(struct page *dst, struct page *src, | ||
112 | unsigned long vaddr, struct vm_area_struct *vma) | ||
113 | { | ||
114 | unsigned long dst_paddr, src_paddr; | ||
115 | void *dst_vaddr = coherent_kvaddr(dst, TLBTEMP_BASE_1, vaddr, | ||
116 | &dst_paddr); | ||
117 | void *src_vaddr = coherent_kvaddr(src, TLBTEMP_BASE_2, vaddr, | ||
118 | &src_paddr); | ||
119 | |||
120 | pagefault_disable(); | ||
121 | kmap_invalidate_coherent(dst, vaddr); | ||
122 | set_bit(PG_arch_1, &dst->flags); | ||
123 | copy_page_alias(dst_vaddr, src_vaddr, dst_paddr, src_paddr); | ||
124 | pagefault_enable(); | ||
125 | } | ||
126 | |||
127 | #endif /* DCACHE_WAY_SIZE > PAGE_SIZE */ | ||
128 | |||
66 | #if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK | 129 | #if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK |
67 | 130 | ||
68 | /* | 131 | /* |
diff --git a/arch/xtensa/mm/misc.S b/arch/xtensa/mm/misc.S index 1f68558dbcc2..11a01c3e9cea 100644 --- a/arch/xtensa/mm/misc.S +++ b/arch/xtensa/mm/misc.S | |||
@@ -110,41 +110,24 @@ ENTRY(__tlbtemp_mapping_start) | |||
110 | #if (DCACHE_WAY_SIZE > PAGE_SIZE) | 110 | #if (DCACHE_WAY_SIZE > PAGE_SIZE) |
111 | 111 | ||
112 | /* | 112 | /* |
113 | * clear_user_page (void *addr, unsigned long vaddr, struct page *page) | 113 | * clear_page_alias(void *addr, unsigned long paddr) |
114 | * a2 a3 a4 | 114 | * a2 a3 |
115 | */ | 115 | */ |
116 | 116 | ||
117 | ENTRY(clear_user_page) | 117 | ENTRY(clear_page_alias) |
118 | 118 | ||
119 | entry a1, 32 | 119 | entry a1, 32 |
120 | 120 | ||
121 | /* Mark page dirty and determine alias. */ | 121 | /* Skip setting up a temporary DTLB if not aliased low page. */ |
122 | 122 | ||
123 | movi a7, (1 << PG_ARCH_1) | 123 | movi a5, PAGE_OFFSET |
124 | l32i a5, a4, PAGE_FLAGS | 124 | movi a6, 0 |
125 | xor a6, a2, a3 | 125 | beqz a3, 1f |
126 | extui a3, a3, PAGE_SHIFT, DCACHE_ALIAS_ORDER | ||
127 | extui a6, a6, PAGE_SHIFT, DCACHE_ALIAS_ORDER | ||
128 | or a5, a5, a7 | ||
129 | slli a3, a3, PAGE_SHIFT | ||
130 | s32i a5, a4, PAGE_FLAGS | ||
131 | 126 | ||
132 | /* Skip setting up a temporary DTLB if not aliased. */ | 127 | /* Setup a temporary DTLB for the addr. */ |
133 | |||
134 | beqz a6, 1f | ||
135 | |||
136 | /* Invalidate kernel page. */ | ||
137 | |||
138 | mov a10, a2 | ||
139 | call8 __invalidate_dcache_page | ||
140 | |||
141 | /* Setup a temporary DTLB with the color of the VPN */ | ||
142 | |||
143 | movi a4, ((PAGE_KERNEL | _PAGE_HW_WRITE) - PAGE_OFFSET) & 0xffffffff | ||
144 | movi a5, TLBTEMP_BASE_1 # virt | ||
145 | add a6, a2, a4 # ppn | ||
146 | add a2, a5, a3 # add 'color' | ||
147 | 128 | ||
129 | addi a6, a3, (PAGE_KERNEL | _PAGE_HW_WRITE) | ||
130 | mov a4, a2 | ||
148 | wdtlb a6, a2 | 131 | wdtlb a6, a2 |
149 | dsync | 132 | dsync |
150 | 133 | ||
@@ -165,62 +148,43 @@ ENTRY(clear_user_page) | |||
165 | 148 | ||
166 | /* We need to invalidate the temporary idtlb entry, if any. */ | 149 | /* We need to invalidate the temporary idtlb entry, if any. */ |
167 | 150 | ||
168 | 1: addi a2, a2, -PAGE_SIZE | 151 | 1: idtlb a4 |
169 | idtlb a2 | ||
170 | dsync | 152 | dsync |
171 | 153 | ||
172 | retw | 154 | retw |
173 | 155 | ||
174 | ENDPROC(clear_user_page) | 156 | ENDPROC(clear_page_alias) |
175 | 157 | ||
176 | /* | 158 | /* |
177 | * copy_page_user (void *to, void *from, unsigned long vaddr, struct page *page) | 159 | * copy_page_alias(void *to, void *from, |
178 | * a2 a3 a4 a5 | 160 | * a2 a3 |
161 | * unsigned long to_paddr, unsigned long from_paddr) | ||
162 | * a4 a5 | ||
179 | */ | 163 | */ |
180 | 164 | ||
181 | ENTRY(copy_user_page) | 165 | ENTRY(copy_page_alias) |
182 | 166 | ||
183 | entry a1, 32 | 167 | entry a1, 32 |
184 | 168 | ||
185 | /* Mark page dirty and determine alias for destination. */ | 169 | /* Skip setting up a temporary DTLB for destination if not aliased. */ |
186 | |||
187 | movi a8, (1 << PG_ARCH_1) | ||
188 | l32i a9, a5, PAGE_FLAGS | ||
189 | xor a6, a2, a4 | ||
190 | xor a7, a3, a4 | ||
191 | extui a4, a4, PAGE_SHIFT, DCACHE_ALIAS_ORDER | ||
192 | extui a6, a6, PAGE_SHIFT, DCACHE_ALIAS_ORDER | ||
193 | extui a7, a7, PAGE_SHIFT, DCACHE_ALIAS_ORDER | ||
194 | or a9, a9, a8 | ||
195 | slli a4, a4, PAGE_SHIFT | ||
196 | s32i a9, a5, PAGE_FLAGS | ||
197 | movi a5, ((PAGE_KERNEL | _PAGE_HW_WRITE) - PAGE_OFFSET) & 0xffffffff | ||
198 | |||
199 | beqz a6, 1f | ||
200 | |||
201 | /* Invalidate dcache */ | ||
202 | |||
203 | mov a10, a2 | ||
204 | call8 __invalidate_dcache_page | ||
205 | 170 | ||
206 | /* Setup a temporary DTLB with a matching color. */ | 171 | movi a6, 0 |
172 | movi a7, 0 | ||
173 | beqz a4, 1f | ||
207 | 174 | ||
208 | movi a8, TLBTEMP_BASE_1 # base | 175 | /* Setup a temporary DTLB for destination. */ |
209 | add a6, a2, a5 # ppn | ||
210 | add a2, a8, a4 # add 'color' | ||
211 | 176 | ||
177 | addi a6, a4, (PAGE_KERNEL | _PAGE_HW_WRITE) | ||
212 | wdtlb a6, a2 | 178 | wdtlb a6, a2 |
213 | dsync | 179 | dsync |
214 | 180 | ||
215 | /* Skip setting up a temporary DTLB for destination if not aliased. */ | 181 | /* Skip setting up a temporary DTLB for source if not aliased. */ |
216 | 182 | ||
217 | 1: beqz a7, 1f | 183 | 1: beqz a5, 1f |
218 | 184 | ||
219 | /* Setup a temporary DTLB with a matching color. */ | 185 | /* Setup a temporary DTLB for source. */ |
220 | 186 | ||
221 | movi a8, TLBTEMP_BASE_2 # base | 187 | addi a7, a5, PAGE_KERNEL |
222 | add a7, a3, a5 # ppn | ||
223 | add a3, a8, a4 | ||
224 | addi a8, a3, 1 # way1 | 188 | addi a8, a3, 1 # way1 |
225 | 189 | ||
226 | wdtlb a7, a8 | 190 | wdtlb a7, a8 |
@@ -271,7 +235,7 @@ ENTRY(copy_user_page) | |||
271 | 235 | ||
272 | retw | 236 | retw |
273 | 237 | ||
274 | ENDPROC(copy_user_page) | 238 | ENDPROC(copy_page_alias) |
275 | 239 | ||
276 | #endif | 240 | #endif |
277 | 241 | ||
@@ -300,6 +264,30 @@ ENTRY(__flush_invalidate_dcache_page_alias) | |||
300 | retw | 264 | retw |
301 | 265 | ||
302 | ENDPROC(__flush_invalidate_dcache_page_alias) | 266 | ENDPROC(__flush_invalidate_dcache_page_alias) |
267 | |||
268 | /* | ||
269 | * void __invalidate_dcache_page_alias (addr, phys) | ||
270 | * a2 a3 | ||
271 | */ | ||
272 | |||
273 | ENTRY(__invalidate_dcache_page_alias) | ||
274 | |||
275 | entry sp, 16 | ||
276 | |||
277 | movi a7, 0 # required for exception handler | ||
278 | addi a6, a3, (PAGE_KERNEL | _PAGE_HW_WRITE) | ||
279 | mov a4, a2 | ||
280 | wdtlb a6, a2 | ||
281 | dsync | ||
282 | |||
283 | ___invalidate_dcache_page a2 a3 | ||
284 | |||
285 | idtlb a4 | ||
286 | dsync | ||
287 | |||
288 | retw | ||
289 | |||
290 | ENDPROC(__invalidate_dcache_page_alias) | ||
303 | #endif | 291 | #endif |
304 | 292 | ||
305 | ENTRY(__tlbtemp_mapping_itlb) | 293 | ENTRY(__tlbtemp_mapping_itlb) |