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

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

Fixes for mips32 invalidation functions.

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