Rev 598 | Rev 612 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | jermar | 1 | /* |
319 | jermar | 2 | * Copyright (C) 2003-2004 Jakub Jermar |
1 | 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> |
||
130 | decky | 30 | #include <arch/mm/asid.h> |
1 | jermar | 31 | #include <mm/tlb.h> |
391 | jermar | 32 | #include <mm/page.h> |
33 | #include <mm/vm.h> |
||
1 | jermar | 34 | #include <arch/cp0.h> |
35 | #include <panic.h> |
||
36 | #include <arch.h> |
||
268 | palkovsky | 37 | #include <symtab.h> |
391 | jermar | 38 | #include <synch/spinlock.h> |
39 | #include <print.h> |
||
396 | jermar | 40 | #include <debug.h> |
268 | palkovsky | 41 | |
391 | jermar | 42 | static void tlb_refill_fail(struct exception_regdump *pstate); |
43 | static void tlb_invalid_fail(struct exception_regdump *pstate); |
||
44 | static void tlb_modified_fail(struct exception_regdump *pstate); |
||
45 | |||
394 | jermar | 46 | static pte_t *find_mapping_and_check(__address badvaddr); |
399 | jermar | 47 | |
396 | jermar | 48 | static void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn); |
399 | jermar | 49 | static void prepare_entry_hi(entry_hi_t *hi, asid_t asid, __address addr); |
394 | jermar | 50 | |
391 | jermar | 51 | /** Initialize TLB |
52 | * |
||
53 | * Initialize TLB. |
||
54 | * Invalidate all entries and mark wired entries. |
||
55 | */ |
||
569 | jermar | 56 | void tlb_arch_init(void) |
389 | jermar | 57 | { |
599 | jermar | 58 | int i; |
59 | |||
389 | jermar | 60 | cp0_pagemask_write(TLB_PAGE_MASK_16K); |
599 | jermar | 61 | cp0_entry_hi_write(0); |
62 | cp0_entry_lo0_write(0); |
||
63 | cp0_entry_lo1_write(0); |
||
389 | jermar | 64 | |
599 | jermar | 65 | /* Clear and initialize TLB. */ |
66 | |||
67 | for (i = 0; i < TLB_ENTRY_COUNT; i++) { |
||
68 | cp0_index_write(i); |
||
69 | tlbwi(); |
||
70 | } |
||
598 | jermar | 71 | |
389 | jermar | 72 | /* |
73 | * The kernel is going to make use of some wired |
||
391 | jermar | 74 | * entries (e.g. mapping kernel stacks in kseg3). |
389 | jermar | 75 | */ |
76 | cp0_wired_write(TLB_WIRED); |
||
77 | } |
||
78 | |||
391 | jermar | 79 | /** Process TLB Refill Exception |
80 | * |
||
81 | * Process TLB Refill Exception. |
||
82 | * |
||
83 | * @param pstate Interrupted register context. |
||
84 | */ |
||
317 | palkovsky | 85 | void tlb_refill(struct exception_regdump *pstate) |
1 | jermar | 86 | { |
396 | jermar | 87 | entry_lo_t lo; |
399 | jermar | 88 | entry_hi_t hi; |
391 | jermar | 89 | __address badvaddr; |
90 | pte_t *pte; |
||
397 | jermar | 91 | |
391 | jermar | 92 | badvaddr = cp0_badvaddr_read(); |
397 | jermar | 93 | |
394 | jermar | 94 | spinlock_lock(&VM->lock); |
399 | jermar | 95 | |
394 | jermar | 96 | pte = find_mapping_and_check(badvaddr); |
97 | if (!pte) |
||
391 | jermar | 98 | goto fail; |
99 | |||
100 | /* |
||
394 | jermar | 101 | * Record access to PTE. |
391 | jermar | 102 | */ |
394 | jermar | 103 | pte->a = 1; |
391 | jermar | 104 | |
399 | jermar | 105 | prepare_entry_hi(&hi, VM->asid, badvaddr); |
403 | jermar | 106 | prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->lo.d, pte->lo.c, pte->lo.pfn); |
394 | jermar | 107 | |
391 | jermar | 108 | /* |
109 | * New entry is to be inserted into TLB |
||
110 | */ |
||
399 | jermar | 111 | cp0_entry_hi_write(hi.value); |
391 | jermar | 112 | if ((badvaddr/PAGE_SIZE) % 2 == 0) { |
396 | jermar | 113 | cp0_entry_lo0_write(lo.value); |
391 | jermar | 114 | cp0_entry_lo1_write(0); |
115 | } |
||
116 | else { |
||
117 | cp0_entry_lo0_write(0); |
||
396 | jermar | 118 | cp0_entry_lo1_write(lo.value); |
391 | jermar | 119 | } |
120 | tlbwr(); |
||
121 | |||
122 | spinlock_unlock(&VM->lock); |
||
123 | return; |
||
124 | |||
125 | fail: |
||
126 | spinlock_unlock(&VM->lock); |
||
127 | tlb_refill_fail(pstate); |
||
128 | } |
||
129 | |||
394 | jermar | 130 | /** Process TLB Invalid Exception |
131 | * |
||
132 | * Process TLB Invalid Exception. |
||
133 | * |
||
134 | * @param pstate Interrupted register context. |
||
135 | */ |
||
391 | jermar | 136 | void tlb_invalid(struct exception_regdump *pstate) |
137 | { |
||
396 | jermar | 138 | tlb_index_t index; |
394 | jermar | 139 | __address badvaddr; |
396 | jermar | 140 | entry_lo_t lo; |
399 | jermar | 141 | entry_hi_t hi; |
394 | jermar | 142 | pte_t *pte; |
143 | |||
144 | badvaddr = cp0_badvaddr_read(); |
||
145 | |||
146 | /* |
||
147 | * Locate the faulting entry in TLB. |
||
148 | */ |
||
399 | jermar | 149 | hi.value = cp0_entry_hi_read(); |
150 | prepare_entry_hi(&hi, hi.asid, badvaddr); |
||
151 | cp0_entry_hi_write(hi.value); |
||
394 | jermar | 152 | tlbp(); |
396 | jermar | 153 | index.value = cp0_index_read(); |
394 | jermar | 154 | |
155 | spinlock_lock(&VM->lock); |
||
156 | |||
157 | /* |
||
158 | * Fail if the entry is not in TLB. |
||
159 | */ |
||
396 | jermar | 160 | if (index.p) { |
161 | printf("TLB entry not found.\n"); |
||
394 | jermar | 162 | goto fail; |
396 | jermar | 163 | } |
394 | jermar | 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 | |||
403 | jermar | 179 | prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->lo.d, pte->lo.c, pte->lo.pfn); |
394 | jermar | 180 | |
181 | /* |
||
182 | * The entry is to be updated in TLB. |
||
183 | */ |
||
184 | if ((badvaddr/PAGE_SIZE) % 2 == 0) |
||
396 | jermar | 185 | cp0_entry_lo0_write(lo.value); |
394 | jermar | 186 | else |
396 | jermar | 187 | cp0_entry_lo1_write(lo.value); |
394 | jermar | 188 | tlbwi(); |
189 | |||
190 | spinlock_unlock(&VM->lock); |
||
191 | return; |
||
192 | |||
193 | fail: |
||
194 | spinlock_unlock(&VM->lock); |
||
391 | jermar | 195 | tlb_invalid_fail(pstate); |
196 | } |
||
197 | |||
394 | jermar | 198 | /** Process TLB Modified Exception |
199 | * |
||
200 | * Process TLB Modified Exception. |
||
201 | * |
||
202 | * @param pstate Interrupted register context. |
||
203 | */ |
||
391 | jermar | 204 | void tlb_modified(struct exception_regdump *pstate) |
205 | { |
||
396 | jermar | 206 | tlb_index_t index; |
394 | jermar | 207 | __address badvaddr; |
396 | jermar | 208 | entry_lo_t lo; |
399 | jermar | 209 | entry_hi_t hi; |
394 | jermar | 210 | pte_t *pte; |
211 | |||
212 | badvaddr = cp0_badvaddr_read(); |
||
213 | |||
214 | /* |
||
215 | * Locate the faulting entry in TLB. |
||
216 | */ |
||
399 | jermar | 217 | hi.value = cp0_entry_hi_read(); |
218 | prepare_entry_hi(&hi, hi.asid, badvaddr); |
||
219 | cp0_entry_hi_write(hi.value); |
||
394 | jermar | 220 | tlbp(); |
396 | jermar | 221 | index.value = cp0_index_read(); |
394 | jermar | 222 | |
223 | spinlock_lock(&VM->lock); |
||
224 | |||
225 | /* |
||
226 | * Fail if the entry is not in TLB. |
||
227 | */ |
||
396 | jermar | 228 | if (index.p) { |
229 | printf("TLB entry not found.\n"); |
||
394 | jermar | 230 | goto fail; |
396 | jermar | 231 | } |
394 | jermar | 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; |
||
403 | jermar | 252 | pte->lo.d = 1; |
394 | jermar | 253 | |
403 | jermar | 254 | prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->w, pte->lo.c, pte->lo.pfn); |
394 | jermar | 255 | |
256 | /* |
||
257 | * The entry is to be updated in TLB. |
||
258 | */ |
||
259 | if ((badvaddr/PAGE_SIZE) % 2 == 0) |
||
396 | jermar | 260 | cp0_entry_lo0_write(lo.value); |
394 | jermar | 261 | else |
396 | jermar | 262 | cp0_entry_lo1_write(lo.value); |
394 | jermar | 263 | tlbwi(); |
264 | |||
265 | spinlock_unlock(&VM->lock); |
||
266 | return; |
||
267 | |||
268 | fail: |
||
269 | spinlock_unlock(&VM->lock); |
||
391 | jermar | 270 | tlb_modified_fail(pstate); |
271 | } |
||
272 | |||
273 | void tlb_refill_fail(struct exception_regdump *pstate) |
||
274 | { |
||
324 | palkovsky | 275 | char *symbol = ""; |
276 | char *sym2 = ""; |
||
277 | |||
332 | palkovsky | 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; |
||
391 | jermar | 284 | panic("%X: TLB Refill Exception at %X(%s<-%s)\n", cp0_badvaddr_read(), pstate->epc, symbol, sym2); |
1 | jermar | 285 | } |
286 | |||
391 | jermar | 287 | |
288 | void tlb_invalid_fail(struct exception_regdump *pstate) |
||
1 | jermar | 289 | { |
268 | palkovsky | 290 | char *symbol = ""; |
291 | |||
332 | palkovsky | 292 | char *s = get_symtab_entry(pstate->epc); |
293 | if (s) |
||
294 | symbol = s; |
||
394 | jermar | 295 | panic("%X: TLB Invalid Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol); |
1 | jermar | 296 | } |
297 | |||
391 | jermar | 298 | void tlb_modified_fail(struct exception_regdump *pstate) |
389 | jermar | 299 | { |
300 | char *symbol = ""; |
||
301 | |||
302 | char *s = get_symtab_entry(pstate->epc); |
||
303 | if (s) |
||
304 | symbol = s; |
||
394 | jermar | 305 | panic("%X: TLB Modified Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol); |
389 | jermar | 306 | } |
307 | |||
394 | jermar | 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 | */ |
||
317 | pte_t *find_mapping_and_check(__address badvaddr) |
||
318 | { |
||
396 | jermar | 319 | entry_hi_t hi; |
394 | jermar | 320 | pte_t *pte; |
321 | |||
396 | jermar | 322 | hi.value = cp0_entry_hi_read(); |
394 | jermar | 323 | |
324 | /* |
||
325 | * Handler cannot succeed if the ASIDs don't match. |
||
326 | */ |
||
396 | jermar | 327 | if (hi.asid != VM->asid) { |
328 | printf("EntryHi.asid=%d, VM->asid=%d\n", hi.asid, VM->asid); |
||
394 | jermar | 329 | return NULL; |
396 | jermar | 330 | } |
394 | jermar | 331 | |
332 | /* |
||
333 | * Handler cannot succeed if badvaddr has no mapping. |
||
334 | */ |
||
492 | jermar | 335 | pte = page_mapping_find(badvaddr, 0); |
396 | jermar | 336 | if (!pte) { |
337 | printf("No such mapping.\n"); |
||
394 | jermar | 338 | return NULL; |
396 | jermar | 339 | } |
394 | jermar | 340 | |
341 | /* |
||
342 | * Handler cannot succeed if the mapping is marked as invalid. |
||
343 | */ |
||
403 | jermar | 344 | if (!pte->lo.v) { |
396 | jermar | 345 | printf("Invalid mapping.\n"); |
394 | jermar | 346 | return NULL; |
396 | jermar | 347 | } |
394 | jermar | 348 | |
349 | return pte; |
||
350 | } |
||
351 | |||
396 | jermar | 352 | void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn) |
394 | jermar | 353 | { |
399 | jermar | 354 | lo->value = 0; |
394 | jermar | 355 | lo->g = g; |
356 | lo->v = v; |
||
357 | lo->d = d; |
||
358 | lo->c = c; |
||
359 | lo->pfn = pfn; |
||
360 | } |
||
399 | jermar | 361 | |
362 | void 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 | } |
||
569 | jermar | 367 | |
594 | jermar | 368 | /** Print contents of TLB. */ |
569 | jermar | 369 | void tlb_print(void) |
370 | { |
||
594 | jermar | 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 | } |
||
569 | jermar | 389 | } |
598 | jermar | 390 | |
391 | /** Invalidate all TLB entries. */ |
||
392 | void tlb_invalidate_all(void) |
||
393 | { |
||
599 | jermar | 394 | ipl_t ipl; |
395 | entry_lo_t lo0, lo1; |
||
598 | jermar | 396 | int i; |
397 | |||
599 | jermar | 398 | ipl = interrupts_disable(); |
598 | jermar | 399 | |
400 | for (i = 0; i < TLB_ENTRY_COUNT; i++) { |
||
401 | cp0_index_write(i); |
||
599 | jermar | 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 | |||
598 | jermar | 413 | tlbwi(); |
414 | } |
||
599 | jermar | 415 | |
416 | interrupts_restore(ipl); |
||
598 | jermar | 417 | } |
418 | |||
419 | /** Invalidate all TLB entries belonging to specified address space. |
||
420 | * |
||
421 | * @param asid Address space identifier. |
||
422 | */ |
||
423 | void tlb_invalidate_asid(asid_t asid) |
||
424 | { |
||
599 | jermar | 425 | ipl_t ipl; |
426 | entry_lo_t lo0, lo1; |
||
598 | jermar | 427 | entry_hi_t hi; |
428 | int i; |
||
429 | |||
599 | jermar | 430 | ASSERT(asid != ASID_INVALID); |
431 | |||
432 | ipl = interrupts_disable(); |
||
433 | |||
598 | jermar | 434 | for (i = 0; i < TLB_ENTRY_COUNT; i++) { |
435 | cp0_index_write(i); |
||
436 | tlbr(); |
||
437 | |||
599 | jermar | 438 | hi.value = cp0_entry_hi_read(); |
439 | |||
598 | jermar | 440 | if (hi.asid == asid) { |
599 | jermar | 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 | |||
598 | jermar | 450 | tlbwi(); |
451 | } |
||
452 | } |
||
599 | jermar | 453 | |
454 | interrupts_restore(ipl); |
||
598 | jermar | 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 | */ |
||
462 | void tlb_invalidate_page(asid_t asid, __address page) |
||
463 | { |
||
599 | jermar | 464 | ipl_t ipl; |
465 | entry_lo_t lo0, lo1; |
||
598 | jermar | 466 | entry_hi_t hi; |
467 | tlb_index_t index; |
||
468 | |||
599 | jermar | 469 | ASSERT(asid != ASID_INVALID); |
470 | |||
471 | ipl = interrupts_disable(); |
||
472 | |||
598 | jermar | 473 | hi.value = 0; |
474 | prepare_entry_hi(&hi, asid, page); |
||
599 | jermar | 475 | cp0_entry_hi_write(hi.value); |
476 | |||
598 | jermar | 477 | tlbp(); |
478 | index.value = cp0_index_read(); |
||
479 | |||
480 | if (!index.p) { |
||
481 | /* Entry was found, index register contains valid index. */ |
||
599 | jermar | 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 | |||
598 | jermar | 493 | tlbwi(); |
494 | } |
||
599 | jermar | 495 | |
496 | interrupts_restore(ipl); |
||
598 | jermar | 497 | } |