Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/nios2/mm/tlb.c
26424 views
1
/*
2
* Nios2 TLB handling
3
*
4
* Copyright (C) 2009, Wind River Systems Inc
5
* Implemented by [email protected] and [email protected]
6
*
7
* This file is subject to the terms and conditions of the GNU General Public
8
* License. See the file "COPYING" in the main directory of this archive
9
* for more details.
10
*/
11
12
#include <linux/init.h>
13
#include <linux/sched.h>
14
#include <linux/mm.h>
15
#include <linux/pagemap.h>
16
17
#include <asm/tlb.h>
18
#include <asm/mmu_context.h>
19
#include <asm/cpuinfo.h>
20
21
#define TLB_INDEX_MASK \
22
((((1UL << (cpuinfo.tlb_ptr_sz - cpuinfo.tlb_num_ways_log2))) - 1) \
23
<< PAGE_SHIFT)
24
25
static void get_misc_and_pid(unsigned long *misc, unsigned long *pid)
26
{
27
*misc = RDCTL(CTL_TLBMISC);
28
*misc &= (TLBMISC_PID | TLBMISC_WAY);
29
*pid = *misc & TLBMISC_PID;
30
}
31
32
/*
33
* This provides a PTEADDR value for addr that will cause a TLB miss
34
* (fast TLB miss). TLB invalidation replaces entries with this value.
35
*/
36
static unsigned long pteaddr_invalid(unsigned long addr)
37
{
38
return ((addr | 0xC0000000UL) >> PAGE_SHIFT) << 2;
39
}
40
41
/*
42
* This one is only used for pages with the global bit set so we don't care
43
* much about the ASID.
44
*/
45
static void replace_tlb_one_pid(unsigned long addr, unsigned long mmu_pid, unsigned long tlbacc)
46
{
47
unsigned int way;
48
unsigned long org_misc, pid_misc;
49
50
/* remember pid/way until we return. */
51
get_misc_and_pid(&org_misc, &pid_misc);
52
53
WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2);
54
55
for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
56
unsigned long pteaddr;
57
unsigned long tlbmisc;
58
unsigned long pid;
59
60
tlbmisc = TLBMISC_RD | (way << TLBMISC_WAY_SHIFT);
61
WRCTL(CTL_TLBMISC, tlbmisc);
62
63
pteaddr = RDCTL(CTL_PTEADDR);
64
if (((pteaddr >> 2) & 0xfffff) != (addr >> PAGE_SHIFT))
65
continue;
66
67
tlbmisc = RDCTL(CTL_TLBMISC);
68
pid = (tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK;
69
if (pid != mmu_pid)
70
continue;
71
72
tlbmisc = (mmu_pid << TLBMISC_PID_SHIFT) | TLBMISC_WE |
73
(way << TLBMISC_WAY_SHIFT);
74
WRCTL(CTL_TLBMISC, tlbmisc);
75
if (tlbacc == 0)
76
WRCTL(CTL_PTEADDR, pteaddr_invalid(addr));
77
WRCTL(CTL_TLBACC, tlbacc);
78
/*
79
* There should be only a single entry that maps a
80
* particular {address,pid} so break after a match.
81
*/
82
break;
83
}
84
85
WRCTL(CTL_TLBMISC, org_misc);
86
}
87
88
static void flush_tlb_one_pid(unsigned long addr, unsigned long mmu_pid)
89
{
90
pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr);
91
92
replace_tlb_one_pid(addr, mmu_pid, 0);
93
}
94
95
static void reload_tlb_one_pid(unsigned long addr, unsigned long mmu_pid, pte_t pte)
96
{
97
pr_debug("Reload tlb-entry for vaddr=%#lx\n", addr);
98
99
replace_tlb_one_pid(addr, mmu_pid, pte_val(pte));
100
}
101
102
void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
103
unsigned long end)
104
{
105
unsigned long mmu_pid = get_pid_from_context(&vma->vm_mm->context);
106
107
while (start < end) {
108
flush_tlb_one_pid(start, mmu_pid);
109
start += PAGE_SIZE;
110
}
111
}
112
113
void reload_tlb_page(struct vm_area_struct *vma, unsigned long addr, pte_t pte)
114
{
115
unsigned long mmu_pid = get_pid_from_context(&vma->vm_mm->context);
116
117
reload_tlb_one_pid(addr, mmu_pid, pte);
118
}
119
120
/*
121
* This one is only used for pages with the global bit set so we don't care
122
* much about the ASID.
123
*/
124
static void flush_tlb_one(unsigned long addr)
125
{
126
unsigned int way;
127
unsigned long org_misc, pid_misc;
128
129
pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr);
130
131
/* remember pid/way until we return. */
132
get_misc_and_pid(&org_misc, &pid_misc);
133
134
WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2);
135
136
for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
137
unsigned long pteaddr;
138
unsigned long tlbmisc;
139
140
tlbmisc = TLBMISC_RD | (way << TLBMISC_WAY_SHIFT);
141
WRCTL(CTL_TLBMISC, tlbmisc);
142
143
pteaddr = RDCTL(CTL_PTEADDR);
144
if (((pteaddr >> 2) & 0xfffff) != (addr >> PAGE_SHIFT))
145
continue;
146
147
tlbmisc = RDCTL(CTL_TLBMISC);
148
pr_debug("Flush entry by writing way=%dl pid=%ld\n",
149
way, ((tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK));
150
151
tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT) | (tlbmisc & TLBMISC_PID);
152
WRCTL(CTL_TLBMISC, tlbmisc);
153
WRCTL(CTL_PTEADDR, pteaddr_invalid(addr));
154
WRCTL(CTL_TLBACC, 0);
155
}
156
157
WRCTL(CTL_TLBMISC, org_misc);
158
}
159
160
void flush_tlb_kernel_range(unsigned long start, unsigned long end)
161
{
162
while (start < end) {
163
flush_tlb_one(start);
164
start += PAGE_SIZE;
165
}
166
}
167
168
void dump_tlb_line(unsigned long line)
169
{
170
unsigned int way;
171
unsigned long org_misc;
172
173
pr_debug("dump tlb-entries for line=%#lx (addr %08lx)\n", line,
174
line << (PAGE_SHIFT + cpuinfo.tlb_num_ways_log2));
175
176
/* remember pid/way until we return */
177
org_misc = (RDCTL(CTL_TLBMISC) & (TLBMISC_PID | TLBMISC_WAY));
178
179
WRCTL(CTL_PTEADDR, line << 2);
180
181
for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
182
unsigned long pteaddr;
183
unsigned long tlbmisc;
184
unsigned long tlbacc;
185
186
WRCTL(CTL_TLBMISC, TLBMISC_RD | (way << TLBMISC_WAY_SHIFT));
187
pteaddr = RDCTL(CTL_PTEADDR);
188
tlbmisc = RDCTL(CTL_TLBMISC);
189
tlbacc = RDCTL(CTL_TLBACC);
190
191
if ((tlbacc << PAGE_SHIFT) != 0) {
192
pr_debug("-- way:%02x vpn:0x%08lx phys:0x%08lx pid:0x%02lx flags:%c%c%c%c%c\n",
193
way,
194
(pteaddr << (PAGE_SHIFT-2)),
195
(tlbacc << PAGE_SHIFT),
196
((tlbmisc >> TLBMISC_PID_SHIFT) &
197
TLBMISC_PID_MASK),
198
(tlbacc & _PAGE_READ ? 'r' : '-'),
199
(tlbacc & _PAGE_WRITE ? 'w' : '-'),
200
(tlbacc & _PAGE_EXEC ? 'x' : '-'),
201
(tlbacc & _PAGE_GLOBAL ? 'g' : '-'),
202
(tlbacc & _PAGE_CACHED ? 'c' : '-'));
203
}
204
}
205
206
WRCTL(CTL_TLBMISC, org_misc);
207
}
208
209
void dump_tlb(void)
210
{
211
unsigned int i;
212
213
for (i = 0; i < cpuinfo.tlb_num_lines; i++)
214
dump_tlb_line(i);
215
}
216
217
void flush_tlb_pid(unsigned long mmu_pid)
218
{
219
unsigned long addr = 0;
220
unsigned int line;
221
unsigned int way;
222
unsigned long org_misc, pid_misc;
223
224
/* remember pid/way until we return */
225
get_misc_and_pid(&org_misc, &pid_misc);
226
227
for (line = 0; line < cpuinfo.tlb_num_lines; line++) {
228
WRCTL(CTL_PTEADDR, pteaddr_invalid(addr));
229
230
for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
231
unsigned long tlbmisc;
232
unsigned long pid;
233
234
tlbmisc = TLBMISC_RD | (way << TLBMISC_WAY_SHIFT);
235
WRCTL(CTL_TLBMISC, tlbmisc);
236
tlbmisc = RDCTL(CTL_TLBMISC);
237
pid = (tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK;
238
if (pid != mmu_pid)
239
continue;
240
241
tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT) |
242
(pid << TLBMISC_PID_SHIFT);
243
WRCTL(CTL_TLBMISC, tlbmisc);
244
WRCTL(CTL_TLBACC, 0);
245
}
246
247
addr += PAGE_SIZE;
248
}
249
250
WRCTL(CTL_TLBMISC, org_misc);
251
}
252
253
/*
254
* All entries common to a mm share an asid. To effectively flush these
255
* entries, we just bump the asid.
256
*/
257
void flush_tlb_mm(struct mm_struct *mm)
258
{
259
if (current->mm == mm) {
260
unsigned long mmu_pid = get_pid_from_context(&mm->context);
261
flush_tlb_pid(mmu_pid);
262
} else {
263
memset(&mm->context, 0, sizeof(mm_context_t));
264
}
265
}
266
267
void flush_tlb_all(void)
268
{
269
unsigned long addr = 0;
270
unsigned int line;
271
unsigned int way;
272
unsigned long org_misc, pid_misc;
273
274
/* remember pid/way until we return */
275
get_misc_and_pid(&org_misc, &pid_misc);
276
277
/* Map each TLB entry to physcal address 0 with no-access and a
278
bad ptbase */
279
for (line = 0; line < cpuinfo.tlb_num_lines; line++) {
280
WRCTL(CTL_PTEADDR, pteaddr_invalid(addr));
281
for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
282
// Code such as replace_tlb_one_pid assumes that no duplicate entries exist
283
// for a single address across ways, so also use way as a dummy PID
284
WRCTL(CTL_TLBMISC, TLBMISC_WE | (way << TLBMISC_WAY_SHIFT) |
285
(way << TLBMISC_PID_SHIFT));
286
WRCTL(CTL_TLBACC, 0);
287
}
288
289
addr += PAGE_SIZE;
290
}
291
292
/* restore pid/way */
293
WRCTL(CTL_TLBMISC, org_misc);
294
}
295
296
void set_mmu_pid(unsigned long pid)
297
{
298
unsigned long tlbmisc;
299
300
tlbmisc = RDCTL(CTL_TLBMISC);
301
tlbmisc = (tlbmisc & TLBMISC_WAY);
302
tlbmisc |= (pid & TLBMISC_PID_MASK) << TLBMISC_PID_SHIFT;
303
WRCTL(CTL_TLBMISC, tlbmisc);
304
}
305
306