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
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 <arch/mm/asid.h>
31#include <mm/tlb.h>
32#include <mm/page.h>
33#include <mm/vm.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
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
46static pte_t *find_mapping_and_check(__address badvaddr);
47
48static void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn);
49static void prepare_entry_hi(entry_hi_t *hi, asid_t asid, __address addr);
50
51/** Initialize TLB
52 *
53 * Initialize TLB.
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 * Process TLB Refill Exception.
82 *
83 * @param pstate Interrupted register context.
84 */
85void tlb_refill(struct exception_regdump *pstate)
86{
87 entry_lo_t lo;
88 entry_hi_t hi;
89 __address badvaddr;
90 pte_t *pte;
91
92 badvaddr = cp0_badvaddr_read();
93
94 spinlock_lock(&VM->lock);
95
96 pte = find_mapping_and_check(badvaddr);
97 if (!pte)
98 goto fail;
99
100 /*
101 * Record access to PTE.
102 */
103 pte->a = 1;
104
105 prepare_entry_hi(&hi, VM->asid, badvaddr);
106 prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->lo.d, pte->lo.c, pte->lo.pfn);
107
108 /*
109 * New entry is to be inserted into TLB
110 */
111 cp0_entry_hi_write(hi.value);
112 if ((badvaddr/PAGE_SIZE) % 2 == 0) {
113 cp0_entry_lo0_write(lo.value);
114 cp0_entry_lo1_write(0);
115 }
116 else {
117 cp0_entry_lo0_write(0);
118 cp0_entry_lo1_write(lo.value);
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
130/** Process TLB Invalid Exception
131 *
132 * Process TLB Invalid Exception.
133 *
134 * @param pstate Interrupted register context.
135 */
136void tlb_invalid(struct exception_regdump *pstate)
137{
138 tlb_index_t index;
139 __address badvaddr;
140 entry_lo_t lo;
141 entry_hi_t hi;
142 pte_t *pte;
143
144 badvaddr = cp0_badvaddr_read();
145
146 /*
147 * Locate the faulting entry in TLB.
148 */
149 hi.value = cp0_entry_hi_read();
150 prepare_entry_hi(&hi, hi.asid, badvaddr);
151 cp0_entry_hi_write(hi.value);
152 tlbp();
153 index.value = cp0_index_read();
154
155 spinlock_lock(&VM->lock);
156
157 /*
158 * Fail if the entry is not in TLB.
159 */
160 if (index.p) {
161 printf("TLB entry not found.\n");
162 goto fail;
163 }
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
179 prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->lo.d, pte->lo.c, pte->lo.pfn);
180
181 /*
182 * The entry is to be updated in TLB.
183 */
184 if ((badvaddr/PAGE_SIZE) % 2 == 0)
185 cp0_entry_lo0_write(lo.value);
186 else
187 cp0_entry_lo1_write(lo.value);
188 tlbwi();
189
190 spinlock_unlock(&VM->lock);
191 return;
192
193fail:
194 spinlock_unlock(&VM->lock);
195 tlb_invalid_fail(pstate);
196}
197
198/** Process TLB Modified Exception
199 *
200 * Process TLB Modified Exception.
201 *
202 * @param pstate Interrupted register context.
203 */
204void tlb_modified(struct exception_regdump *pstate)
205{
206 tlb_index_t index;
207 __address badvaddr;
208 entry_lo_t lo;
209 entry_hi_t hi;
210 pte_t *pte;
211
212 badvaddr = cp0_badvaddr_read();
213
214 /*
215 * Locate the faulting entry in TLB.
216 */
217 hi.value = cp0_entry_hi_read();
218 prepare_entry_hi(&hi, hi.asid, badvaddr);
219 cp0_entry_hi_write(hi.value);
220 tlbp();
221 index.value = cp0_index_read();
222
223 spinlock_lock(&VM->lock);
224
225 /*
226 * Fail if the entry is not in TLB.
227 */
228 if (index.p) {
229 printf("TLB entry not found.\n");
230 goto fail;
231 }
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;
252 pte->lo.d = 1;
253
254 prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->w, pte->lo.c, pte->lo.pfn);
255
256 /*
257 * The entry is to be updated in TLB.
258 */
259 if ((badvaddr/PAGE_SIZE) % 2 == 0)
260 cp0_entry_lo0_write(lo.value);
261 else
262 cp0_entry_lo1_write(lo.value);
263 tlbwi();
264
265 spinlock_unlock(&VM->lock);
266 return;
267
268fail:
269 spinlock_unlock(&VM->lock);
270 tlb_modified_fail(pstate);
271}
272
273void tlb_refill_fail(struct exception_regdump *pstate)
274{
275 char *symbol = "";
276 char *sym2 = "";
277
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;
284 panic("%X: TLB Refill Exception at %X(%s<-%s)\n", cp0_badvaddr_read(), pstate->epc, symbol, sym2);
285}
286
287
288void tlb_invalid_fail(struct exception_regdump *pstate)
289{
290 char *symbol = "";
291
292 char *s = get_symtab_entry(pstate->epc);
293 if (s)
294 symbol = s;
295 panic("%X: TLB Invalid Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
296}
297
298void tlb_modified_fail(struct exception_regdump *pstate)
299{
300 char *symbol = "";
301
302 char *s = get_symtab_entry(pstate->epc);
303 if (s)
304 symbol = s;
305 panic("%X: TLB Modified Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
306}
307
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{
319 entry_hi_t hi;
320 pte_t *pte;
321
322 hi.value = cp0_entry_hi_read();
323
324 /*
325 * Handler cannot succeed if the ASIDs don't match.
326 */
327 if (hi.asid != VM->asid) {
328 printf("EntryHi.asid=%d, VM->asid=%d\n", hi.asid, VM->asid);
329 return NULL;
330 }
331
332 /*
333 * Handler cannot succeed if badvaddr has no mapping.
334 */
335 pte = page_mapping_find(badvaddr, 0);
336 if (!pte) {
337 printf("No such mapping.\n");
338 return NULL;
339 }
340
341 /*
342 * Handler cannot succeed if the mapping is marked as invalid.
343 */
344 if (!pte->lo.v) {
345 printf("Invalid mapping.\n");
346 return NULL;
347 }
348
349 return pte;
350}
351
352void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn)
353{
354 lo->value = 0;
355 lo->g = g;
356 lo->v = v;
357 lo->d = d;
358 lo->c = c;
359 lo->pfn = pfn;
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;
366}
367
368/** Print contents of TLB. */
369void tlb_print(void)
370{
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 }
389}
390
391/** Invalidate all TLB entries. */
392void tlb_invalidate_all(void)
393{
394 ipl_t ipl;
395 entry_lo_t lo0, lo1;
396 int i;
397
398 ipl = interrupts_disable();
399
400 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
401 cp0_index_write(i);
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
413 tlbwi();
414 }
415
416 interrupts_restore(ipl);
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{
425 ipl_t ipl;
426 entry_lo_t lo0, lo1;
427 entry_hi_t hi;
428 int i;
429
430 ASSERT(asid != ASID_INVALID);
431
432 ipl = interrupts_disable();
433
434 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
435 cp0_index_write(i);
436 tlbr();
437
438 hi.value = cp0_entry_hi_read();
439
440 if (hi.asid == asid) {
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
450 tlbwi();
451 }
452 }
453
454 interrupts_restore(ipl);
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{
464 ipl_t ipl;
465 entry_lo_t lo0, lo1;
466 entry_hi_t hi;
467 tlb_index_t index;
468
469 ASSERT(asid != ASID_INVALID);
470
471 ipl = interrupts_disable();
472
473 hi.value = 0;
474 prepare_entry_hi(&hi, asid, page);
475 cp0_entry_hi_write(hi.value);
476
477 tlbp();
478 index.value = cp0_index_read();
479
480 if (!index.p) {
481 /* Entry was found, index register contains valid index. */
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
493 tlbwi();
494 }
495
496 interrupts_restore(ipl);
497}
Note: See TracBrowser for help on using the repository browser.