source: mainline/arch/mips32/src/mm/tlb.c@ 2d01bbd

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2d01bbd was 2d01bbd, checked in by Ondrej Palkovsky <ondrap@…>, 19 years ago

Fixed shrinking of as area.

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