source: mainline/kernel/arch/mips32/src/mm/tlb.c@ 0d387d2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0d387d2 was edebc15c, checked in by Martin Decky <martin@…>, 17 years ago

physical memory detection in MSIM (discontinous regions supported)
remove Sgi Indy (ARC) support — it was unmaintaned, untested for years and without uspace support

  • Property mode set to 100644
File size: 12.9 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 <symtab.h>
44#include <synch/spinlock.h>
45#include <print.h>
46#include <debug.h>
47#include <align.h>
48#include <interrupt.h>
49
50static void tlb_refill_fail(istate_t *istate);
51static void tlb_invalid_fail(istate_t *istate);
52static void tlb_modified_fail(istate_t *istate);
53
54static pte_t *find_mapping_and_check(uintptr_t badvaddr, int access, istate_t *istate, int *pfrc);
55
56/** Initialize TLB
57 *
58 * Initialize TLB.
59 * Invalidate all entries and mark wired entries.
60 */
61void tlb_arch_init(void)
62{
63 int i;
64
65 cp0_pagemask_write(TLB_PAGE_MASK_16K);
66 cp0_entry_hi_write(0);
67 cp0_entry_lo0_write(0);
68 cp0_entry_lo1_write(0);
69
70 /* Clear and initialize TLB. */
71
72 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
73 cp0_index_write(i);
74 tlbwi();
75 }
76
77 /*
78 * The kernel is going to make use of some wired
79 * entries (e.g. mapping kernel stacks in kseg3).
80 */
81 cp0_wired_write(TLB_WIRED);
82}
83
84/** Process TLB Refill Exception
85 *
86 * Process TLB Refill Exception.
87 *
88 * @param istate Interrupted register context.
89 */
90void tlb_refill(istate_t *istate)
91{
92 entry_lo_t lo;
93 entry_hi_t hi;
94 asid_t asid;
95 uintptr_t badvaddr;
96 pte_t *pte;
97 int pfrc;
98
99 badvaddr = cp0_badvaddr_read();
100
101 spinlock_lock(&AS->lock);
102 asid = AS->asid;
103 spinlock_unlock(&AS->lock);
104
105 page_table_lock(AS, true);
106
107 pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate, &pfrc);
108 if (!pte) {
109 switch (pfrc) {
110 case AS_PF_FAULT:
111 goto fail;
112 break;
113 case AS_PF_DEFER:
114 /*
115 * The page fault came during copy_from_uspace()
116 * or copy_to_uspace().
117 */
118 page_table_unlock(AS, true);
119 return;
120 default:
121 panic("unexpected pfrc (%d)\n", pfrc);
122 }
123 }
124
125 /*
126 * Record access to PTE.
127 */
128 pte->a = 1;
129
130 tlb_prepare_entry_hi(&hi, asid, badvaddr);
131 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->cacheable, pte->pfn);
132
133 /*
134 * New entry is to be inserted into TLB
135 */
136 cp0_entry_hi_write(hi.value);
137 if ((badvaddr/PAGE_SIZE) % 2 == 0) {
138 cp0_entry_lo0_write(lo.value);
139 cp0_entry_lo1_write(0);
140 }
141 else {
142 cp0_entry_lo0_write(0);
143 cp0_entry_lo1_write(lo.value);
144 }
145 cp0_pagemask_write(TLB_PAGE_MASK_16K);
146 tlbwr();
147
148 page_table_unlock(AS, true);
149 return;
150
151fail:
152 page_table_unlock(AS, true);
153 tlb_refill_fail(istate);
154}
155
156/** Process TLB Invalid Exception
157 *
158 * Process TLB Invalid Exception.
159 *
160 * @param istate Interrupted register context.
161 */
162void tlb_invalid(istate_t *istate)
163{
164 tlb_index_t index;
165 uintptr_t badvaddr;
166 entry_lo_t lo;
167 entry_hi_t hi;
168 pte_t *pte;
169 int pfrc;
170
171 badvaddr = cp0_badvaddr_read();
172
173 /*
174 * Locate the faulting entry in TLB.
175 */
176 hi.value = cp0_entry_hi_read();
177 tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
178 cp0_entry_hi_write(hi.value);
179 tlbp();
180 index.value = cp0_index_read();
181
182 page_table_lock(AS, true);
183
184 /*
185 * Fail if the entry is not in TLB.
186 */
187 if (index.p) {
188 printf("TLB entry not found.\n");
189 goto fail;
190 }
191
192 pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate, &pfrc);
193 if (!pte) {
194 switch (pfrc) {
195 case AS_PF_FAULT:
196 goto fail;
197 break;
198 case AS_PF_DEFER:
199 /*
200 * The page fault came during copy_from_uspace()
201 * or copy_to_uspace().
202 */
203 page_table_unlock(AS, true);
204 return;
205 default:
206 panic("unexpected pfrc (%d)\n", pfrc);
207 }
208 }
209
210 /*
211 * Read the faulting TLB entry.
212 */
213 tlbr();
214
215 /*
216 * Record access to PTE.
217 */
218 pte->a = 1;
219
220 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->cacheable, pte->pfn);
221
222 /*
223 * The entry is to be updated in TLB.
224 */
225 if ((badvaddr/PAGE_SIZE) % 2 == 0)
226 cp0_entry_lo0_write(lo.value);
227 else
228 cp0_entry_lo1_write(lo.value);
229 cp0_pagemask_write(TLB_PAGE_MASK_16K);
230 tlbwi();
231
232 page_table_unlock(AS, true);
233 return;
234
235fail:
236 page_table_unlock(AS, true);
237 tlb_invalid_fail(istate);
238}
239
240/** Process TLB Modified Exception
241 *
242 * Process TLB Modified Exception.
243 *
244 * @param istate Interrupted register context.
245 */
246void tlb_modified(istate_t *istate)
247{
248 tlb_index_t index;
249 uintptr_t badvaddr;
250 entry_lo_t lo;
251 entry_hi_t hi;
252 pte_t *pte;
253 int pfrc;
254
255 badvaddr = cp0_badvaddr_read();
256
257 /*
258 * Locate the faulting entry in TLB.
259 */
260 hi.value = cp0_entry_hi_read();
261 tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
262 cp0_entry_hi_write(hi.value);
263 tlbp();
264 index.value = cp0_index_read();
265
266 page_table_lock(AS, true);
267
268 /*
269 * Fail if the entry is not in TLB.
270 */
271 if (index.p) {
272 printf("TLB entry not found.\n");
273 goto fail;
274 }
275
276 pte = find_mapping_and_check(badvaddr, PF_ACCESS_WRITE, istate, &pfrc);
277 if (!pte) {
278 switch (pfrc) {
279 case AS_PF_FAULT:
280 goto fail;
281 break;
282 case AS_PF_DEFER:
283 /*
284 * The page fault came during copy_from_uspace()
285 * or copy_to_uspace().
286 */
287 page_table_unlock(AS, true);
288 return;
289 default:
290 panic("unexpected pfrc (%d)\n", pfrc);
291 }
292 }
293
294 /*
295 * Fail if the page is not writable.
296 */
297 if (!pte->w)
298 goto fail;
299
300 /*
301 * Read the faulting TLB entry.
302 */
303 tlbr();
304
305 /*
306 * Record access and write to PTE.
307 */
308 pte->a = 1;
309 pte->d = 1;
310
311 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->w, pte->cacheable, pte->pfn);
312
313 /*
314 * The entry is to be updated in TLB.
315 */
316 if ((badvaddr/PAGE_SIZE) % 2 == 0)
317 cp0_entry_lo0_write(lo.value);
318 else
319 cp0_entry_lo1_write(lo.value);
320 cp0_pagemask_write(TLB_PAGE_MASK_16K);
321 tlbwi();
322
323 page_table_unlock(AS, true);
324 return;
325
326fail:
327 page_table_unlock(AS, true);
328 tlb_modified_fail(istate);
329}
330
331void tlb_refill_fail(istate_t *istate)
332{
333 char *symbol = "";
334 char *sym2 = "";
335
336 char *s = get_symtab_entry(istate->epc);
337 if (s)
338 symbol = s;
339 s = get_symtab_entry(istate->ra);
340 if (s)
341 sym2 = s;
342
343 fault_if_from_uspace(istate, "TLB Refill Exception on %p", cp0_badvaddr_read());
344 panic("%x: TLB Refill Exception at %x(%s<-%s)\n", cp0_badvaddr_read(), istate->epc, symbol, sym2);
345}
346
347
348void tlb_invalid_fail(istate_t *istate)
349{
350 char *symbol = "";
351
352 char *s = get_symtab_entry(istate->epc);
353 if (s)
354 symbol = s;
355 fault_if_from_uspace(istate, "TLB Invalid Exception on %p", cp0_badvaddr_read());
356 panic("%x: TLB Invalid Exception at %x(%s)\n", cp0_badvaddr_read(), istate->epc, symbol);
357}
358
359void tlb_modified_fail(istate_t *istate)
360{
361 char *symbol = "";
362
363 char *s = get_symtab_entry(istate->epc);
364 if (s)
365 symbol = s;
366 fault_if_from_uspace(istate, "TLB Modified Exception on %p", cp0_badvaddr_read());
367 panic("%x: TLB Modified Exception at %x(%s)\n", cp0_badvaddr_read(), istate->epc, symbol);
368}
369
370/** Try to find PTE for faulting address
371 *
372 * Try to find PTE for faulting address.
373 * The AS->lock must be held on entry to this function.
374 *
375 * @param badvaddr Faulting virtual address.
376 * @param access Access mode that caused the fault.
377 * @param istate Pointer to interrupted state.
378 * @param pfrc Pointer to variable where as_page_fault() return code will be stored.
379 *
380 * @return PTE on success, NULL otherwise.
381 */
382pte_t *find_mapping_and_check(uintptr_t badvaddr, int access, istate_t *istate, int *pfrc)
383{
384 entry_hi_t hi;
385 pte_t *pte;
386
387 hi.value = cp0_entry_hi_read();
388
389 /*
390 * Handler cannot succeed if the ASIDs don't match.
391 */
392 if (hi.asid != AS->asid) {
393 printf("EntryHi.asid=%d, AS->asid=%d\n", hi.asid, AS->asid);
394 return NULL;
395 }
396
397 /*
398 * Check if the mapping exists in page tables.
399 */
400 pte = page_mapping_find(AS, badvaddr);
401 if (pte && pte->p) {
402 /*
403 * Mapping found in page tables.
404 * Immediately succeed.
405 */
406 return pte;
407 } else {
408 int rc;
409
410 /*
411 * Mapping not found in page tables.
412 * Resort to higher-level page fault handler.
413 */
414 page_table_unlock(AS, true);
415 switch (rc = as_page_fault(badvaddr, access, istate)) {
416 case AS_PF_OK:
417 /*
418 * The higher-level page fault handler succeeded,
419 * The mapping ought to be in place.
420 */
421 page_table_lock(AS, true);
422 pte = page_mapping_find(AS, badvaddr);
423 ASSERT(pte && pte->p);
424 return pte;
425 break;
426 case AS_PF_DEFER:
427 page_table_lock(AS, true);
428 *pfrc = AS_PF_DEFER;
429 return NULL;
430 break;
431 case AS_PF_FAULT:
432 page_table_lock(AS, true);
433 printf("Page fault.\n");
434 *pfrc = AS_PF_FAULT;
435 return NULL;
436 break;
437 default:
438 panic("unexpected rc (%d)\n", rc);
439 }
440
441 }
442}
443
444void tlb_prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, bool cacheable, uintptr_t pfn)
445{
446 lo->value = 0;
447 lo->g = g;
448 lo->v = v;
449 lo->d = d;
450 lo->c = cacheable ? PAGE_CACHEABLE_EXC_WRITE : PAGE_UNCACHED;
451 lo->pfn = pfn;
452}
453
454void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
455{
456 hi->value = ALIGN_DOWN(addr, PAGE_SIZE * 2);
457 hi->asid = asid;
458}
459
460/** Print contents of TLB. */
461void tlb_print(void)
462{
463 page_mask_t mask;
464 entry_lo_t lo0, lo1;
465 entry_hi_t hi, hi_save;
466 unsigned int i;
467
468 hi_save.value = cp0_entry_hi_read();
469
470 printf("# ASID VPN2 MASK G V D C PFN\n");
471 printf("-- ---- ------ ---- - - - - ------\n");
472
473 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
474 cp0_index_write(i);
475 tlbr();
476
477 mask.value = cp0_pagemask_read();
478 hi.value = cp0_entry_hi_read();
479 lo0.value = cp0_entry_lo0_read();
480 lo1.value = cp0_entry_lo1_read();
481
482 printf("%-2u %-4u %#6x %#4x %1u %1u %1u %1u %#6x\n",
483 i, hi.asid, hi.vpn2, mask.mask,
484 lo0.g, lo0.v, lo0.d, lo0.c, lo0.pfn);
485 printf(" %1u %1u %1u %1u %#6x\n",
486 lo1.g, lo1.v, lo1.d, lo1.c, lo1.pfn);
487 }
488
489 cp0_entry_hi_write(hi_save.value);
490}
491
492/** Invalidate all not wired TLB entries. */
493void tlb_invalidate_all(void)
494{
495 ipl_t ipl;
496 entry_lo_t lo0, lo1;
497 entry_hi_t hi_save;
498 int i;
499
500 hi_save.value = cp0_entry_hi_read();
501 ipl = interrupts_disable();
502
503 for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
504 cp0_index_write(i);
505 tlbr();
506
507 lo0.value = cp0_entry_lo0_read();
508 lo1.value = cp0_entry_lo1_read();
509
510 lo0.v = 0;
511 lo1.v = 0;
512
513 cp0_entry_lo0_write(lo0.value);
514 cp0_entry_lo1_write(lo1.value);
515
516 tlbwi();
517 }
518
519 interrupts_restore(ipl);
520 cp0_entry_hi_write(hi_save.value);
521}
522
523/** Invalidate all TLB entries belonging to specified address space.
524 *
525 * @param asid Address space identifier.
526 */
527void tlb_invalidate_asid(asid_t asid)
528{
529 ipl_t ipl;
530 entry_lo_t lo0, lo1;
531 entry_hi_t hi, hi_save;
532 int i;
533
534 ASSERT(asid != ASID_INVALID);
535
536 hi_save.value = cp0_entry_hi_read();
537 ipl = interrupts_disable();
538
539 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
540 cp0_index_write(i);
541 tlbr();
542
543 hi.value = cp0_entry_hi_read();
544
545 if (hi.asid == asid) {
546 lo0.value = cp0_entry_lo0_read();
547 lo1.value = cp0_entry_lo1_read();
548
549 lo0.v = 0;
550 lo1.v = 0;
551
552 cp0_entry_lo0_write(lo0.value);
553 cp0_entry_lo1_write(lo1.value);
554
555 tlbwi();
556 }
557 }
558
559 interrupts_restore(ipl);
560 cp0_entry_hi_write(hi_save.value);
561}
562
563/** Invalidate TLB entries for specified page range belonging to specified address space.
564 *
565 * @param asid Address space identifier.
566 * @param page First page whose TLB entry is to be invalidated.
567 * @param cnt Number of entries to invalidate.
568 */
569void tlb_invalidate_pages(asid_t asid, uintptr_t page, count_t cnt)
570{
571 unsigned int i;
572 ipl_t ipl;
573 entry_lo_t lo0, lo1;
574 entry_hi_t hi, hi_save;
575 tlb_index_t index;
576
577 ASSERT(asid != ASID_INVALID);
578
579 hi_save.value = cp0_entry_hi_read();
580 ipl = interrupts_disable();
581
582 for (i = 0; i < cnt + 1; i += 2) {
583 hi.value = 0;
584 tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
585 cp0_entry_hi_write(hi.value);
586
587 tlbp();
588 index.value = cp0_index_read();
589
590 if (!index.p) {
591 /* Entry was found, index register contains valid index. */
592 tlbr();
593
594 lo0.value = cp0_entry_lo0_read();
595 lo1.value = cp0_entry_lo1_read();
596
597 lo0.v = 0;
598 lo1.v = 0;
599
600 cp0_entry_lo0_write(lo0.value);
601 cp0_entry_lo1_write(lo1.value);
602
603 tlbwi();
604 }
605 }
606
607 interrupts_restore(ipl);
608 cp0_entry_hi_write(hi_save.value);
609}
610
611/** @}
612 */
Note: See TracBrowser for help on using the repository browser.