aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Filippov <jcmvbkbc@gmail.com>2014-07-21 10:54:11 -0400
committerMax Filippov <jcmvbkbc@gmail.com>2014-08-14 03:59:20 -0400
commita91902db2990909ea5e6b110811b448f2e8f1571 (patch)
tree133c118a40292c1c4480acaaa1df1add1b70eaf0
parent7128039fe2dd3d59da9e4ffa036f3aaa3ba87b9f (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.h2
-rw-r--r--arch/xtensa/include/asm/page.h12
-rw-r--r--arch/xtensa/mm/cache.c63
-rw-r--r--arch/xtensa/mm/misc.S116
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)
64extern void __flush_invalidate_dcache_page_alias(unsigned long, unsigned long); 65extern void __flush_invalidate_dcache_page_alias(unsigned long, unsigned long);
66extern void __invalidate_dcache_page_alias(unsigned long, unsigned long);
65#else 67#else
66static inline void __flush_invalidate_dcache_page_alias(unsigned long virt, 68static 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
136struct page; 136struct page;
137struct vm_area_struct;
137extern void clear_page(void *page); 138extern void clear_page(void *page);
138extern void copy_page(void *to, void *from); 139extern 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
146extern void clear_user_page(void*, unsigned long, struct page*); 147extern void clear_page_alias(void *vaddr, unsigned long paddr);
147extern void copy_user_page(void*, void*, unsigned long, struct page*); 148extern 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
152void clear_user_highpage(struct page *page, unsigned long vaddr);
153#define __HAVE_ARCH_COPY_USER_HIGHPAGE
154void 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)
67static 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
87static 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
99void 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
111void 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
117ENTRY(clear_user_page) 117ENTRY(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
1681: addi a2, a2, -PAGE_SIZE 1511: idtlb a4
169 idtlb a2
170 dsync 152 dsync
171 153
172 retw 154 retw
173 155
174ENDPROC(clear_user_page) 156ENDPROC(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
181ENTRY(copy_user_page) 165ENTRY(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
2171: beqz a7, 1f 1831: 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
274ENDPROC(copy_user_page) 238ENDPROC(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
302ENDPROC(__flush_invalidate_dcache_page_alias) 266ENDPROC(__flush_invalidate_dcache_page_alias)
267
268/*
269 * void __invalidate_dcache_page_alias (addr, phys)
270 * a2 a3
271 */
272
273ENTRY(__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
290ENDPROC(__invalidate_dcache_page_alias)
303#endif 291#endif
304 292
305ENTRY(__tlbtemp_mapping_itlb) 293ENTRY(__tlbtemp_mapping_itlb)