Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/xtensa/mm/cache.c
10817 views
1
/*
2
* arch/xtensa/mm/cache.c
3
*
4
* This file is subject to the terms and conditions of the GNU General Public
5
* License. See the file "COPYING" in the main directory of this archive
6
* for more details.
7
*
8
* Copyright (C) 2001-2006 Tensilica Inc.
9
*
10
* Chris Zankel <[email protected]>
11
* Joe Taylor
12
* Marc Gauthier
13
*
14
*/
15
16
#include <linux/init.h>
17
#include <linux/signal.h>
18
#include <linux/sched.h>
19
#include <linux/kernel.h>
20
#include <linux/errno.h>
21
#include <linux/string.h>
22
#include <linux/types.h>
23
#include <linux/ptrace.h>
24
#include <linux/bootmem.h>
25
#include <linux/swap.h>
26
#include <linux/pagemap.h>
27
28
#include <asm/bootparam.h>
29
#include <asm/mmu_context.h>
30
#include <asm/tlb.h>
31
#include <asm/tlbflush.h>
32
#include <asm/page.h>
33
#include <asm/pgalloc.h>
34
#include <asm/pgtable.h>
35
36
//#define printd(x...) printk(x)
37
#define printd(x...) do { } while(0)
38
39
/*
40
* Note:
41
* The kernel provides one architecture bit PG_arch_1 in the page flags that
42
* can be used for cache coherency.
43
*
44
* I$-D$ coherency.
45
*
46
* The Xtensa architecture doesn't keep the instruction cache coherent with
47
* the data cache. We use the architecture bit to indicate if the caches
48
* are coherent. The kernel clears this bit whenever a page is added to the
49
* page cache. At that time, the caches might not be in sync. We, therefore,
50
* define this flag as 'clean' if set.
51
*
52
* D-cache aliasing.
53
*
54
* With cache aliasing, we have to always flush the cache when pages are
55
* unmapped (see tlb_start_vma(). So, we use this flag to indicate a dirty
56
* page.
57
*
58
*
59
*
60
*/
61
62
#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK
63
64
/*
65
* Any time the kernel writes to a user page cache page, or it is about to
66
* read from a page cache page this routine is called.
67
*
68
*/
69
70
void flush_dcache_page(struct page *page)
71
{
72
struct address_space *mapping = page_mapping(page);
73
74
/*
75
* If we have a mapping but the page is not mapped to user-space
76
* yet, we simply mark this page dirty and defer flushing the
77
* caches until update_mmu().
78
*/
79
80
if (mapping && !mapping_mapped(mapping)) {
81
if (!test_bit(PG_arch_1, &page->flags))
82
set_bit(PG_arch_1, &page->flags);
83
return;
84
85
} else {
86
87
unsigned long phys = page_to_phys(page);
88
unsigned long temp = page->index << PAGE_SHIFT;
89
unsigned long alias = !(DCACHE_ALIAS_EQ(temp, phys));
90
unsigned long virt;
91
92
/*
93
* Flush the page in kernel space and user space.
94
* Note that we can omit that step if aliasing is not
95
* an issue, but we do have to synchronize I$ and D$
96
* if we have a mapping.
97
*/
98
99
if (!alias && !mapping)
100
return;
101
102
__flush_invalidate_dcache_page((long)page_address(page));
103
104
virt = TLBTEMP_BASE_1 + (temp & DCACHE_ALIAS_MASK);
105
106
if (alias)
107
__flush_invalidate_dcache_page_alias(virt, phys);
108
109
if (mapping)
110
__invalidate_icache_page_alias(virt, phys);
111
}
112
113
/* There shouldn't be an entry in the cache for this page anymore. */
114
}
115
116
117
/*
118
* For now, flush the whole cache. FIXME??
119
*/
120
121
void flush_cache_range(struct vm_area_struct* vma,
122
unsigned long start, unsigned long end)
123
{
124
__flush_invalidate_dcache_all();
125
__invalidate_icache_all();
126
}
127
128
/*
129
* Remove any entry in the cache for this page.
130
*
131
* Note that this function is only called for user pages, so use the
132
* alias versions of the cache flush functions.
133
*/
134
135
void flush_cache_page(struct vm_area_struct* vma, unsigned long address,
136
unsigned long pfn)
137
{
138
/* Note that we have to use the 'alias' address to avoid multi-hit */
139
140
unsigned long phys = page_to_phys(pfn_to_page(pfn));
141
unsigned long virt = TLBTEMP_BASE_1 + (address & DCACHE_ALIAS_MASK);
142
143
__flush_invalidate_dcache_page_alias(virt, phys);
144
__invalidate_icache_page_alias(virt, phys);
145
}
146
147
#endif
148
149
void
150
update_mmu_cache(struct vm_area_struct * vma, unsigned long addr, pte_t *ptep)
151
{
152
unsigned long pfn = pte_pfn(*ptep);
153
struct page *page;
154
155
if (!pfn_valid(pfn))
156
return;
157
158
page = pfn_to_page(pfn);
159
160
/* Invalidate old entry in TLBs */
161
162
invalidate_itlb_mapping(addr);
163
invalidate_dtlb_mapping(addr);
164
165
#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK
166
167
if (!PageReserved(page) && test_bit(PG_arch_1, &page->flags)) {
168
169
unsigned long vaddr = TLBTEMP_BASE_1 + (addr & DCACHE_ALIAS_MASK);
170
unsigned long paddr = (unsigned long) page_address(page);
171
unsigned long phys = page_to_phys(page);
172
173
__flush_invalidate_dcache_page(paddr);
174
175
__flush_invalidate_dcache_page_alias(vaddr, phys);
176
__invalidate_icache_page_alias(vaddr, phys);
177
178
clear_bit(PG_arch_1, &page->flags);
179
}
180
#else
181
if (!PageReserved(page) && !test_bit(PG_arch_1, &page->flags)
182
&& (vma->vm_flags & VM_EXEC) != 0) {
183
unsigned long paddr = (unsigned long) page_address(page);
184
__flush_dcache_page(paddr);
185
__invalidate_icache_page(paddr);
186
set_bit(PG_arch_1, &page->flags);
187
}
188
#endif
189
}
190
191
/*
192
* access_process_vm() has called get_user_pages(), which has done a
193
* flush_dcache_page() on the page.
194
*/
195
196
#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK
197
198
void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
199
unsigned long vaddr, void *dst, const void *src,
200
unsigned long len)
201
{
202
unsigned long phys = page_to_phys(page);
203
unsigned long alias = !(DCACHE_ALIAS_EQ(vaddr, phys));
204
205
/* Flush and invalidate user page if aliased. */
206
207
if (alias) {
208
unsigned long temp = TLBTEMP_BASE_1 + (vaddr & DCACHE_ALIAS_MASK);
209
__flush_invalidate_dcache_page_alias(temp, phys);
210
}
211
212
/* Copy data */
213
214
memcpy(dst, src, len);
215
216
/*
217
* Flush and invalidate kernel page if aliased and synchronize
218
* data and instruction caches for executable pages.
219
*/
220
221
if (alias) {
222
unsigned long temp = TLBTEMP_BASE_1 + (vaddr & DCACHE_ALIAS_MASK);
223
224
__flush_invalidate_dcache_range((unsigned long) dst, len);
225
if ((vma->vm_flags & VM_EXEC) != 0) {
226
__invalidate_icache_page_alias(temp, phys);
227
}
228
229
} else if ((vma->vm_flags & VM_EXEC) != 0) {
230
__flush_dcache_range((unsigned long)dst,len);
231
__invalidate_icache_range((unsigned long) dst, len);
232
}
233
}
234
235
extern void copy_from_user_page(struct vm_area_struct *vma, struct page *page,
236
unsigned long vaddr, void *dst, const void *src,
237
unsigned long len)
238
{
239
unsigned long phys = page_to_phys(page);
240
unsigned long alias = !(DCACHE_ALIAS_EQ(vaddr, phys));
241
242
/*
243
* Flush user page if aliased.
244
* (Note: a simply flush would be sufficient)
245
*/
246
247
if (alias) {
248
unsigned long temp = TLBTEMP_BASE_1 + (vaddr & DCACHE_ALIAS_MASK);
249
__flush_invalidate_dcache_page_alias(temp, phys);
250
}
251
252
memcpy(dst, src, len);
253
}
254
255
#endif
256
257