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

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

Weaken the assumption, that PageMask register contains the right mask everytime.
As a result, TLB exceptions will explicitly write this register when filling TLB entry.
Adjust tlb_print() on mips32 to print PageMask as well.

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