source: mainline/kernel/arch/ppc32/src/mm/tlb.c@ 0867321

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0867321 was 0867321, checked in by Martin Decky <martin@…>, 17 years ago

very experimental TLB refill for ppc32

  • Property mode set to 100644
File size: 12.3 KB
Line 
1/*
2 * Copyright (c) 2006 Martin Decky
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/** @addtogroup ppc32mm
30 * @{
31 */
32/** @file
33 */
34
35#include <mm/tlb.h>
36#include <arch/mm/tlb.h>
37#include <arch/interrupt.h>
38#include <interrupt.h>
39#include <mm/as.h>
40#include <arch.h>
41#include <print.h>
42#include <symtab.h>
43#include <macros.h>
44
45
46static unsigned int seed = 10;
47static unsigned int seed_real __attribute__ ((section("K_UNMAPPED_DATA_START"))) = 42;
48
49
50#define TLB_FLUSH \
51 "tlbie %0\n" \
52 "addi %0, %0, 0x1000\n"
53
54
55/** Try to find PTE for faulting address
56 *
57 * Try to find PTE for faulting address.
58 * The as->lock must be held on entry to this function
59 * if lock is true.
60 *
61 * @param as Address space.
62 * @param lock Lock/unlock the address space.
63 * @param badvaddr Faulting virtual address.
64 * @param access Access mode that caused the fault.
65 * @param istate Pointer to interrupted state.
66 * @param pfrc Pointer to variable where as_page_fault() return code
67 * will be stored.
68 * @return PTE on success, NULL otherwise.
69 *
70 */
71static pte_t *
72find_mapping_and_check(as_t *as, bool lock, uintptr_t badvaddr, int access,
73 istate_t *istate, int *pfrc)
74{
75 /*
76 * Check if the mapping exists in page tables.
77 */
78 pte_t *pte = page_mapping_find(as, badvaddr);
79 if ((pte) && (pte->present)) {
80 /*
81 * Mapping found in page tables.
82 * Immediately succeed.
83 */
84 return pte;
85 } else {
86 int rc;
87
88 /*
89 * Mapping not found in page tables.
90 * Resort to higher-level page fault handler.
91 */
92 page_table_unlock(as, lock);
93 switch (rc = as_page_fault(badvaddr, access, istate)) {
94 case AS_PF_OK:
95 /*
96 * The higher-level page fault handler succeeded,
97 * The mapping ought to be in place.
98 */
99 page_table_lock(as, lock);
100 pte = page_mapping_find(as, badvaddr);
101 ASSERT((pte) && (pte->present));
102 *pfrc = 0;
103 return pte;
104 case AS_PF_DEFER:
105 page_table_lock(as, lock);
106 *pfrc = rc;
107 return NULL;
108 case AS_PF_FAULT:
109 page_table_lock(as, lock);
110 *pfrc = rc;
111 return NULL;
112 default:
113 panic("Unexpected rc (%d).", rc);
114 }
115 }
116}
117
118
119static void pht_refill_fail(uintptr_t badvaddr, istate_t *istate)
120{
121 char *symbol = "";
122 char *sym2 = "";
123
124 char *str = get_symtab_entry(istate->pc);
125 if (str)
126 symbol = str;
127 str = get_symtab_entry(istate->lr);
128 if (str)
129 sym2 = str;
130
131 fault_if_from_uspace(istate,
132 "PHT Refill Exception on %p.", badvaddr);
133 panic("%p: PHT Refill Exception at %p (%s<-%s).", badvaddr,
134 istate->pc, symbol, sym2);
135}
136
137
138static void pht_insert(const uintptr_t vaddr, const pte_t *pte)
139{
140 uint32_t page = (vaddr >> 12) & 0xffff;
141 uint32_t api = (vaddr >> 22) & 0x3f;
142
143 uint32_t vsid;
144 asm volatile (
145 "mfsrin %0, %1\n"
146 : "=r" (vsid)
147 : "r" (vaddr)
148 );
149
150 uint32_t sdr1;
151 asm volatile (
152 "mfsdr1 %0\n"
153 : "=r" (sdr1)
154 );
155 phte_t *phte = (phte_t *) PA2KA(sdr1 & 0xffff0000);
156
157 /* Primary hash (xor) */
158 uint32_t h = 0;
159 uint32_t hash = vsid ^ page;
160 uint32_t base = (hash & 0x3ff) << 3;
161 uint32_t i;
162 bool found = false;
163
164 /* Find colliding PTE in PTEG */
165 for (i = 0; i < 8; i++) {
166 if ((phte[base + i].v)
167 && (phte[base + i].vsid == vsid)
168 && (phte[base + i].api == api)
169 && (phte[base + i].h == 0)) {
170 found = true;
171 break;
172 }
173 }
174
175 if (!found) {
176 /* Find unused PTE in PTEG */
177 for (i = 0; i < 8; i++) {
178 if (!phte[base + i].v) {
179 found = true;
180 break;
181 }
182 }
183 }
184
185 if (!found) {
186 /* Secondary hash (not) */
187 uint32_t base2 = (~hash & 0x3ff) << 3;
188
189 /* Find colliding PTE in PTEG */
190 for (i = 0; i < 8; i++) {
191 if ((phte[base2 + i].v)
192 && (phte[base2 + i].vsid == vsid)
193 && (phte[base2 + i].api == api)
194 && (phte[base2 + i].h == 1)) {
195 found = true;
196 base = base2;
197 h = 1;
198 break;
199 }
200 }
201
202 if (!found) {
203 /* Find unused PTE in PTEG */
204 for (i = 0; i < 8; i++) {
205 if (!phte[base2 + i].v) {
206 found = true;
207 base = base2;
208 h = 1;
209 break;
210 }
211 }
212 }
213
214 if (!found)
215 i = RANDI(seed) % 8;
216 }
217
218 phte[base + i].v = 1;
219 phte[base + i].vsid = vsid;
220 phte[base + i].h = h;
221 phte[base + i].api = api;
222 phte[base + i].rpn = pte->pfn;
223 phte[base + i].r = 0;
224 phte[base + i].c = 0;
225 phte[base + i].wimg = (pte->page_cache_disable ? WIMG_NO_CACHE : 0);
226 phte[base + i].pp = 2; // FIXME
227}
228
229
230/** Process Instruction/Data Storage Exception
231 *
232 * @param n Exception vector number.
233 * @param istate Interrupted register context.
234 *
235 */
236void pht_refill(int n, istate_t *istate)
237{
238 uintptr_t badvaddr;
239 pte_t *pte;
240 int pfrc;
241 as_t *as;
242 bool lock;
243
244 if (AS == NULL) {
245 as = AS_KERNEL;
246 lock = false;
247 } else {
248 as = AS;
249 lock = true;
250 }
251
252 if (n == VECTOR_DATA_STORAGE)
253 badvaddr = istate->dar;
254 else
255 badvaddr = istate->pc;
256
257 page_table_lock(as, lock);
258
259 pte = find_mapping_and_check(as, lock, badvaddr,
260 PF_ACCESS_READ /* FIXME */, istate, &pfrc);
261 if (!pte) {
262 switch (pfrc) {
263 case AS_PF_FAULT:
264 goto fail;
265 break;
266 case AS_PF_DEFER:
267 /*
268 * The page fault came during copy_from_uspace()
269 * or copy_to_uspace().
270 */
271 page_table_unlock(as, lock);
272 return;
273 default:
274 panic("Unexpected pfrc (%d).", pfrc);
275 }
276 }
277
278 pte->accessed = 1; /* Record access to PTE */
279 pht_insert(badvaddr, pte);
280
281 page_table_unlock(as, lock);
282 return;
283
284fail:
285 page_table_unlock(as, lock);
286 pht_refill_fail(badvaddr, istate);
287}
288
289
290/** Process Instruction/Data Storage Exception in Real Mode
291 *
292 * @param n Exception vector number.
293 * @param istate Interrupted register context.
294 *
295 */
296bool pht_refill_real(int n, istate_t *istate)
297{
298 uintptr_t badvaddr;
299
300 if (n == VECTOR_DATA_STORAGE)
301 badvaddr = istate->dar;
302 else
303 badvaddr = istate->pc;
304
305 uint32_t physmem;
306 asm volatile (
307 "mfsprg3 %0\n"
308 : "=r" (physmem)
309 );
310
311 if ((badvaddr < PA2KA(0)) || (badvaddr >= PA2KA(physmem)))
312 return false;
313
314 uint32_t page = (badvaddr >> 12) & 0xffff;
315 uint32_t api = (badvaddr >> 22) & 0x3f;
316
317 uint32_t vsid;
318 asm volatile (
319 "mfsrin %0, %1\n"
320 : "=r" (vsid)
321 : "r" (badvaddr)
322 );
323
324 uint32_t sdr1;
325 asm volatile (
326 "mfsdr1 %0\n"
327 : "=r" (sdr1)
328 );
329 phte_t *phte_real = (phte_t *) (sdr1 & 0xffff0000);
330
331 /* Primary hash (xor) */
332 uint32_t h = 0;
333 uint32_t hash = vsid ^ page;
334 uint32_t base = (hash & 0x3ff) << 3;
335 uint32_t i;
336 bool found = false;
337
338 /* Find colliding PTE in PTEG */
339 for (i = 0; i < 8; i++) {
340 if ((phte_real[base + i].v)
341 && (phte_real[base + i].vsid == vsid)
342 && (phte_real[base + i].api == api)
343 && (phte_real[base + i].h == 0)) {
344 found = true;
345 break;
346 }
347 }
348
349 if (!found) {
350 /* Find unused PTE in PTEG */
351 for (i = 0; i < 8; i++) {
352 if (!phte_real[base + i].v) {
353 found = true;
354 break;
355 }
356 }
357 }
358
359 if (!found) {
360 /* Secondary hash (not) */
361 uint32_t base2 = (~hash & 0x3ff) << 3;
362
363 /* Find colliding PTE in PTEG */
364 for (i = 0; i < 8; i++) {
365 if ((phte_real[base2 + i].v)
366 && (phte_real[base2 + i].vsid == vsid)
367 && (phte_real[base2 + i].api == api)
368 && (phte_real[base2 + i].h == 1)) {
369 found = true;
370 base = base2;
371 h = 1;
372 break;
373 }
374 }
375
376 if (!found) {
377 /* Find unused PTE in PTEG */
378 for (i = 0; i < 8; i++) {
379 if (!phte_real[base2 + i].v) {
380 found = true;
381 base = base2;
382 h = 1;
383 break;
384 }
385 }
386 }
387
388 if (!found) {
389 /* Use secondary hash to avoid collisions
390 with usual PHT refill handler. */
391 i = RANDI(seed_real) % 8;
392 base = base2;
393 h = 1;
394 }
395 }
396
397 phte_real[base + i].v = 1;
398 phte_real[base + i].vsid = vsid;
399 phte_real[base + i].h = h;
400 phte_real[base + i].api = api;
401 phte_real[base + i].rpn = KA2PA(badvaddr) >> 12;
402 phte_real[base + i].r = 0;
403 phte_real[base + i].c = 0;
404 phte_real[base + i].wimg = 0;
405 phte_real[base + i].pp = 2; // FIXME
406
407 return true;
408}
409
410
411/** Process ITLB/DTLB Miss Exception in Real Mode
412 *
413 *
414 */
415void tlb_refill_real(int n, uint32_t tlbmiss, ptehi_t ptehi, ptelo_t ptelo, istate_t *istate)
416{
417 uint32_t badvaddr = tlbmiss & 0xfffffffc;
418
419 uint32_t physmem;
420 asm volatile (
421 "mfsprg3 %0\n"
422 : "=r" (physmem)
423 );
424
425 if ((badvaddr < PA2KA(0)) || (badvaddr >= PA2KA(physmem)))
426 return; // FIXME
427
428 ptelo.rpn = KA2PA(badvaddr) >> 12;
429 ptelo.wimg = 0;
430 ptelo.pp = 2; // FIXME
431
432 uint32_t index = 0;
433 asm volatile (
434 "mtspr 981, %0\n"
435 "mtspr 982, %1\n"
436 "tlbld %2\n"
437 "tlbli %2\n"
438 : "=r" (index)
439 : "r" (ptehi),
440 "r" (ptelo)
441 );
442}
443
444
445void tlb_arch_init(void)
446{
447 tlb_invalidate_all();
448}
449
450
451void tlb_invalidate_all(void)
452{
453 uint32_t index;
454 asm volatile (
455 "li %0, 0\n"
456 "sync\n"
457
458 TLB_FLUSH
459 TLB_FLUSH
460 TLB_FLUSH
461 TLB_FLUSH
462 TLB_FLUSH
463 TLB_FLUSH
464 TLB_FLUSH
465 TLB_FLUSH
466
467 TLB_FLUSH
468 TLB_FLUSH
469 TLB_FLUSH
470 TLB_FLUSH
471 TLB_FLUSH
472 TLB_FLUSH
473 TLB_FLUSH
474 TLB_FLUSH
475
476 TLB_FLUSH
477 TLB_FLUSH
478 TLB_FLUSH
479 TLB_FLUSH
480 TLB_FLUSH
481 TLB_FLUSH
482 TLB_FLUSH
483 TLB_FLUSH
484
485 TLB_FLUSH
486 TLB_FLUSH
487 TLB_FLUSH
488 TLB_FLUSH
489 TLB_FLUSH
490 TLB_FLUSH
491 TLB_FLUSH
492 TLB_FLUSH
493
494 TLB_FLUSH
495 TLB_FLUSH
496 TLB_FLUSH
497 TLB_FLUSH
498 TLB_FLUSH
499 TLB_FLUSH
500 TLB_FLUSH
501 TLB_FLUSH
502
503 TLB_FLUSH
504 TLB_FLUSH
505 TLB_FLUSH
506 TLB_FLUSH
507 TLB_FLUSH
508 TLB_FLUSH
509 TLB_FLUSH
510 TLB_FLUSH
511
512 TLB_FLUSH
513 TLB_FLUSH
514 TLB_FLUSH
515 TLB_FLUSH
516 TLB_FLUSH
517 TLB_FLUSH
518 TLB_FLUSH
519 TLB_FLUSH
520
521 TLB_FLUSH
522 TLB_FLUSH
523 TLB_FLUSH
524 TLB_FLUSH
525 TLB_FLUSH
526 TLB_FLUSH
527 TLB_FLUSH
528 TLB_FLUSH
529
530 "eieio\n"
531 "tlbsync\n"
532 "sync\n"
533 : "=r" (index)
534 );
535}
536
537
538void tlb_invalidate_asid(asid_t asid)
539{
540 uint32_t sdr1;
541 asm volatile (
542 "mfsdr1 %0\n"
543 : "=r" (sdr1)
544 );
545 phte_t *phte = (phte_t *) PA2KA(sdr1 & 0xffff0000);
546
547 uint32_t i;
548 for (i = 0; i < 8192; i++) {
549 if ((phte[i].v) && (phte[i].vsid >= (asid << 4)) &&
550 (phte[i].vsid < ((asid << 4) + 16)))
551 phte[i].v = 0;
552 }
553 tlb_invalidate_all();
554}
555
556
557void tlb_invalidate_pages(asid_t asid, uintptr_t page, count_t cnt)
558{
559 // TODO
560 tlb_invalidate_all();
561}
562
563
564#define PRINT_BAT(name, ureg, lreg) \
565 asm volatile ( \
566 "mfspr %0," #ureg "\n" \
567 "mfspr %1," #lreg "\n" \
568 : "=r" (upper), "=r" (lower) \
569 ); \
570 mask = (upper & 0x1ffc) >> 2; \
571 if (upper & 3) { \
572 uint32_t tmp = mask; \
573 length = 128; \
574 while (tmp) { \
575 if ((tmp & 1) == 0) { \
576 printf("ibat[0]: error in mask\n"); \
577 break; \
578 } \
579 length <<= 1; \
580 tmp >>= 1; \
581 } \
582 } else \
583 length = 0; \
584 printf(name ": page=%.*p frame=%.*p length=%d KB (mask=%#x)%s%s\n", \
585 sizeof(upper) * 2, upper & 0xffff0000, sizeof(lower) * 2, \
586 lower & 0xffff0000, length, mask, \
587 ((upper >> 1) & 1) ? " supervisor" : "", \
588 (upper & 1) ? " user" : "");
589
590
591void tlb_print(void)
592{
593 uint32_t sr;
594
595 for (sr = 0; sr < 16; sr++) {
596 uint32_t vsid;
597 asm volatile (
598 "mfsrin %0, %1\n"
599 : "=r" (vsid)
600 : "r" (sr << 28)
601 );
602 printf("sr[%02u]: vsid=%.*p (asid=%u)%s%s\n", sr,
603 sizeof(vsid) * 2, vsid & 0xffffff, (vsid & 0xffffff) >> 4,
604 ((vsid >> 30) & 1) ? " supervisor" : "",
605 ((vsid >> 29) & 1) ? " user" : "");
606 }
607
608 uint32_t upper;
609 uint32_t lower;
610 uint32_t mask;
611 uint32_t length;
612
613 PRINT_BAT("ibat[0]", 528, 529);
614 PRINT_BAT("ibat[1]", 530, 531);
615 PRINT_BAT("ibat[2]", 532, 533);
616 PRINT_BAT("ibat[3]", 534, 535);
617
618 PRINT_BAT("dbat[0]", 536, 537);
619 PRINT_BAT("dbat[1]", 538, 539);
620 PRINT_BAT("dbat[2]", 540, 541);
621 PRINT_BAT("dbat[3]", 542, 543);
622}
623
624/** @}
625 */
Note: See TracBrowser for help on using the repository browser.