source: mainline/kernel/arch/mips32/src/mm/tlb.c@ 1dbc43f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1dbc43f was 1dbc43f, checked in by Jakub Jermar <jakub@…>, 13 years ago

Unify user page fault handling in as_page_fault().

  • Remove lots of architecture-dependent boilerplate code.
  • Property mode set to 100644
File size: 9.4 KB
Line 
1/*
2 * Copyright (c) 2003-2004 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup mips32mm
30 * @{
31 */
32/** @file
33 */
34
35#include <arch/mm/tlb.h>
36#include <mm/asid.h>
37#include <mm/tlb.h>
38#include <mm/page.h>
39#include <mm/as.h>
40#include <arch/cp0.h>
41#include <panic.h>
42#include <arch.h>
43#include <synch/mutex.h>
44#include <print.h>
45#include <debug.h>
46#include <align.h>
47#include <interrupt.h>
48#include <symtab.h>
49
50static pte_t *find_mapping_and_check(uintptr_t, int, istate_t *);
51
52/** Initialize TLB.
53 *
54 * Invalidate all entries and mark wired entries.
55 */
56void tlb_arch_init(void)
57{
58 int i;
59
60 cp0_pagemask_write(TLB_PAGE_MASK_16K);
61 cp0_entry_hi_write(0);
62 cp0_entry_lo0_write(0);
63 cp0_entry_lo1_write(0);
64
65 /* Clear and initialize TLB. */
66
67 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
68 cp0_index_write(i);
69 tlbwi();
70 }
71
72 /*
73 * The kernel is going to make use of some wired
74 * entries (e.g. mapping kernel stacks in kseg3).
75 */
76 cp0_wired_write(TLB_WIRED);
77}
78
79/** Process TLB Refill Exception.
80 *
81 * @param istate Interrupted register context.
82 */
83void tlb_refill(istate_t *istate)
84{
85 entry_lo_t lo;
86 entry_hi_t hi;
87 asid_t asid;
88 uintptr_t badvaddr;
89 pte_t *pte;
90
91 badvaddr = cp0_badvaddr_read();
92 asid = AS->asid;
93
94 pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate);
95 if (pte) {
96 /*
97 * Record access to PTE.
98 */
99 pte->a = 1;
100
101 tlb_prepare_entry_hi(&hi, asid, badvaddr);
102 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d,
103 pte->cacheable, pte->pfn);
104
105 /*
106 * New entry is to be inserted into TLB
107 */
108 cp0_entry_hi_write(hi.value);
109 if ((badvaddr / PAGE_SIZE) % 2 == 0) {
110 cp0_entry_lo0_write(lo.value);
111 cp0_entry_lo1_write(0);
112 } else {
113 cp0_entry_lo0_write(0);
114 cp0_entry_lo1_write(lo.value);
115 }
116 cp0_pagemask_write(TLB_PAGE_MASK_16K);
117 tlbwr();
118 }
119}
120
121/** Process TLB Invalid Exception.
122 *
123 * @param istate Interrupted register context.
124 */
125void tlb_invalid(istate_t *istate)
126{
127 tlb_index_t index;
128 uintptr_t badvaddr;
129 entry_lo_t lo;
130 entry_hi_t hi;
131 pte_t *pte;
132
133 badvaddr = cp0_badvaddr_read();
134
135 /*
136 * Locate the faulting entry in TLB.
137 */
138 hi.value = cp0_entry_hi_read();
139 tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
140 cp0_entry_hi_write(hi.value);
141 tlbp();
142 index.value = cp0_index_read();
143
144 ASSERT(!index.p);
145
146 pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate);
147 if (pte) {
148 /*
149 * Read the faulting TLB entry.
150 */
151 tlbr();
152
153 /*
154 * Record access to PTE.
155 */
156 pte->a = 1;
157
158 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d,
159 pte->cacheable, pte->pfn);
160
161 /*
162 * The entry is to be updated in TLB.
163 */
164 if ((badvaddr / PAGE_SIZE) % 2 == 0)
165 cp0_entry_lo0_write(lo.value);
166 else
167 cp0_entry_lo1_write(lo.value);
168 cp0_pagemask_write(TLB_PAGE_MASK_16K);
169 tlbwi();
170 }
171}
172
173/** Process TLB Modified Exception.
174 *
175 * @param istate Interrupted register context.
176 */
177void tlb_modified(istate_t *istate)
178{
179 tlb_index_t index;
180 uintptr_t badvaddr;
181 entry_lo_t lo;
182 entry_hi_t hi;
183 pte_t *pte;
184
185 badvaddr = cp0_badvaddr_read();
186
187 /*
188 * Locate the faulting entry in TLB.
189 */
190 hi.value = cp0_entry_hi_read();
191 tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
192 cp0_entry_hi_write(hi.value);
193 tlbp();
194 index.value = cp0_index_read();
195
196 /*
197 * Fail if the entry is not in TLB.
198 */
199 ASSERT(!index.p);
200
201 pte = find_mapping_and_check(badvaddr, PF_ACCESS_WRITE, istate);
202 if (pte) {
203 /*
204 * Read the faulting TLB entry.
205 */
206 tlbr();
207
208 /*
209 * Record access and write to PTE.
210 */
211 pte->a = 1;
212 pte->d = 1;
213
214 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->w,
215 pte->cacheable, pte->pfn);
216
217 /*
218 * The entry is to be updated in TLB.
219 */
220 if ((badvaddr / PAGE_SIZE) % 2 == 0)
221 cp0_entry_lo0_write(lo.value);
222 else
223 cp0_entry_lo1_write(lo.value);
224 cp0_pagemask_write(TLB_PAGE_MASK_16K);
225 tlbwi();
226 }
227}
228
229/** Try to find PTE for faulting address.
230 *
231 * @param badvaddr Faulting virtual address.
232 * @param access Access mode that caused the fault.
233 * @param istate Pointer to interrupted state.
234 *
235 * @return PTE on success, NULL otherwise.
236 */
237pte_t *find_mapping_and_check(uintptr_t badvaddr, int access, istate_t *istate)
238{
239 entry_hi_t hi;
240 pte_t *pte;
241
242 hi.value = cp0_entry_hi_read();
243
244 ASSERT(hi.asid == AS->asid);
245
246 /*
247 * Check if the mapping exists in page tables.
248 */
249 pte = page_mapping_find(AS, badvaddr, true);
250 if (pte && pte->p && (pte->w || access != PF_ACCESS_WRITE)) {
251 /*
252 * Mapping found in page tables.
253 * Immediately succeed.
254 */
255 return pte;
256 }
257
258 /*
259 * Mapping not found in page tables.
260 * Resort to higher-level page fault handler.
261 */
262 if (as_page_fault(badvaddr, access, istate) == AS_PF_OK) {
263 pte = page_mapping_find(AS, badvaddr, true);
264 ASSERT(pte && pte->p);
265 ASSERT(pte->w || access != PF_ACCESS_WRITE);
266 return pte;
267 }
268
269 return NULL;
270}
271
272void
273tlb_prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, bool cacheable,
274 uintptr_t pfn)
275{
276 lo->value = 0;
277 lo->g = g;
278 lo->v = v;
279 lo->d = d;
280 lo->c = cacheable ? PAGE_CACHEABLE_EXC_WRITE : PAGE_UNCACHED;
281 lo->pfn = pfn;
282}
283
284void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
285{
286 hi->value = ALIGN_DOWN(addr, PAGE_SIZE * 2);
287 hi->asid = asid;
288}
289
290/** Print contents of TLB. */
291void tlb_print(void)
292{
293 page_mask_t mask;
294 entry_lo_t lo0, lo1;
295 entry_hi_t hi, hi_save;
296 unsigned int i;
297
298 hi_save.value = cp0_entry_hi_read();
299
300 printf("[nr] [asid] [vpn2] [mask] [gvdc] [pfn ]\n");
301
302 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
303 cp0_index_write(i);
304 tlbr();
305
306 mask.value = cp0_pagemask_read();
307 hi.value = cp0_entry_hi_read();
308 lo0.value = cp0_entry_lo0_read();
309 lo1.value = cp0_entry_lo1_read();
310
311 printf("%-4u %-6u %#6x %#6x %1u%1u%1u%1u %#6x\n",
312 i, hi.asid, hi.vpn2, mask.mask,
313 lo0.g, lo0.v, lo0.d, lo0.c, lo0.pfn);
314 printf(" %1u%1u%1u%1u %#6x\n",
315 lo1.g, lo1.v, lo1.d, lo1.c, lo1.pfn);
316 }
317
318 cp0_entry_hi_write(hi_save.value);
319}
320
321/** Invalidate all not wired TLB entries. */
322void tlb_invalidate_all(void)
323{
324 ipl_t ipl;
325 entry_lo_t lo0, lo1;
326 entry_hi_t hi_save;
327 int i;
328
329 hi_save.value = cp0_entry_hi_read();
330 ipl = interrupts_disable();
331
332 for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
333 cp0_index_write(i);
334 tlbr();
335
336 lo0.value = cp0_entry_lo0_read();
337 lo1.value = cp0_entry_lo1_read();
338
339 lo0.v = 0;
340 lo1.v = 0;
341
342 cp0_entry_lo0_write(lo0.value);
343 cp0_entry_lo1_write(lo1.value);
344
345 tlbwi();
346 }
347
348 interrupts_restore(ipl);
349 cp0_entry_hi_write(hi_save.value);
350}
351
352/** Invalidate all TLB entries belonging to specified address space.
353 *
354 * @param asid Address space identifier.
355 */
356void tlb_invalidate_asid(asid_t asid)
357{
358 ipl_t ipl;
359 entry_lo_t lo0, lo1;
360 entry_hi_t hi, hi_save;
361 int i;
362
363 ASSERT(asid != ASID_INVALID);
364
365 hi_save.value = cp0_entry_hi_read();
366 ipl = interrupts_disable();
367
368 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
369 cp0_index_write(i);
370 tlbr();
371
372 hi.value = cp0_entry_hi_read();
373
374 if (hi.asid == asid) {
375 lo0.value = cp0_entry_lo0_read();
376 lo1.value = cp0_entry_lo1_read();
377
378 lo0.v = 0;
379 lo1.v = 0;
380
381 cp0_entry_lo0_write(lo0.value);
382 cp0_entry_lo1_write(lo1.value);
383
384 tlbwi();
385 }
386 }
387
388 interrupts_restore(ipl);
389 cp0_entry_hi_write(hi_save.value);
390}
391
392/** Invalidate TLB entries for specified page range belonging to specified
393 * address space.
394 *
395 * @param asid Address space identifier.
396 * @param page First page whose TLB entry is to be invalidated.
397 * @param cnt Number of entries to invalidate.
398 */
399void tlb_invalidate_pages(asid_t asid, uintptr_t page, size_t cnt)
400{
401 unsigned int i;
402 ipl_t ipl;
403 entry_lo_t lo0, lo1;
404 entry_hi_t hi, hi_save;
405 tlb_index_t index;
406
407 if (asid == ASID_INVALID)
408 return;
409
410 hi_save.value = cp0_entry_hi_read();
411 ipl = interrupts_disable();
412
413 for (i = 0; i < cnt + 1; i += 2) {
414 hi.value = 0;
415 tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
416 cp0_entry_hi_write(hi.value);
417
418 tlbp();
419 index.value = cp0_index_read();
420
421 if (!index.p) {
422 /*
423 * Entry was found, index register contains valid
424 * index.
425 */
426 tlbr();
427
428 lo0.value = cp0_entry_lo0_read();
429 lo1.value = cp0_entry_lo1_read();
430
431 lo0.v = 0;
432 lo1.v = 0;
433
434 cp0_entry_lo0_write(lo0.value);
435 cp0_entry_lo1_write(lo1.value);
436
437 tlbwi();
438 }
439 }
440
441 interrupts_restore(ipl);
442 cp0_entry_hi_write(hi_save.value);
443}
444
445/** @}
446 */
Note: See TracBrowser for help on using the repository browser.