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

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

remove tlbia instruction, which is unsupported by PowerPC 7450

  • Property mode set to 100644
File size: 10.0 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
44
45/** Try to find PTE for faulting address
46 *
47 * Try to find PTE for faulting address.
48 * The as->lock must be held on entry to this function
49 * if lock is true.
50 *
51 * @param as Address space.
52 * @param lock Lock/unlock the address space.
53 * @param badvaddr Faulting virtual address.
54 * @param access Access mode that caused the fault.
55 * @param istate Pointer to interrupted state.
56 * @param pfrc Pointer to variable where as_page_fault() return code
57 * will be stored.
58 * @return PTE on success, NULL otherwise.
59 *
60 */
61static pte_t *
62find_mapping_and_check(as_t *as, bool lock, uintptr_t badvaddr, int access,
63 istate_t *istate, int *pfrc)
64{
65 /*
66 * Check if the mapping exists in page tables.
67 */
68 pte_t *pte = page_mapping_find(as, badvaddr);
69 if ((pte) && (pte->p)) {
70 /*
71 * Mapping found in page tables.
72 * Immediately succeed.
73 */
74 return pte;
75 } else {
76 int rc;
77
78 /*
79 * Mapping not found in page tables.
80 * Resort to higher-level page fault handler.
81 */
82 page_table_unlock(as, lock);
83 switch (rc = as_page_fault(badvaddr, access, istate)) {
84 case AS_PF_OK:
85 /*
86 * The higher-level page fault handler succeeded,
87 * The mapping ought to be in place.
88 */
89 page_table_lock(as, lock);
90 pte = page_mapping_find(as, badvaddr);
91 ASSERT((pte) && (pte->p));
92 *pfrc = 0;
93 return pte;
94 case AS_PF_DEFER:
95 page_table_lock(as, lock);
96 *pfrc = rc;
97 return NULL;
98 case AS_PF_FAULT:
99 page_table_lock(as, lock);
100 *pfrc = rc;
101 return NULL;
102 default:
103 panic("Unexpected rc (%d).", rc);
104 }
105 }
106}
107
108
109static void pht_refill_fail(uintptr_t badvaddr, istate_t *istate)
110{
111 char *symbol = "";
112 char *sym2 = "";
113
114 char *s = get_symtab_entry(istate->pc);
115 if (s)
116 symbol = s;
117 s = get_symtab_entry(istate->lr);
118 if (s)
119 sym2 = s;
120
121 fault_if_from_uspace(istate,
122 "%p: PHT Refill Exception at %p (%s<-%s).", badvaddr,
123 istate->pc, symbol, sym2);
124 panic("%p: PHT Refill Exception at %p (%s<-%s).", badvaddr,
125 istate->pc, symbol, sym2);
126}
127
128
129static void pht_insert(const uintptr_t vaddr, const pfn_t pfn)
130{
131 uint32_t page = (vaddr >> 12) & 0xffff;
132 uint32_t api = (vaddr >> 22) & 0x3f;
133
134 uint32_t vsid;
135 asm volatile (
136 "mfsrin %0, %1\n"
137 : "=r" (vsid)
138 : "r" (vaddr)
139 );
140
141 uint32_t sdr1;
142 asm volatile (
143 "mfsdr1 %0\n"
144 : "=r" (sdr1)
145 );
146 phte_t *phte = (phte_t *) PA2KA(sdr1 & 0xffff0000);
147
148 /* Primary hash (xor) */
149 uint32_t h = 0;
150 uint32_t hash = vsid ^ page;
151 uint32_t base = (hash & 0x3ff) << 3;
152 uint32_t i;
153 bool found = false;
154
155 /* Find unused or colliding
156 PTE in PTEG */
157 for (i = 0; i < 8; i++) {
158 if ((!phte[base + i].v) || ((phte[base + i].vsid == vsid) &&
159 (phte[base + i].api == api))) {
160 found = true;
161 break;
162 }
163 }
164
165 if (!found) {
166 /* Secondary hash (not) */
167 uint32_t base2 = (~hash & 0x3ff) << 3;
168
169 /* Find unused or colliding
170 PTE in PTEG */
171 for (i = 0; i < 8; i++) {
172 if ((!phte[base2 + i].v) ||
173 ((phte[base2 + i].vsid == vsid) &&
174 (phte[base2 + i].api == api))) {
175 found = true;
176 base = base2;
177 h = 1;
178 break;
179 }
180 }
181
182 if (!found) {
183 // TODO: A/C precedence groups
184 i = page % 8;
185 }
186 }
187
188 phte[base + i].v = 1;
189 phte[base + i].vsid = vsid;
190 phte[base + i].h = h;
191 phte[base + i].api = api;
192 phte[base + i].rpn = pfn;
193 phte[base + i].r = 0;
194 phte[base + i].c = 0;
195 phte[base + i].pp = 2; // FIXME
196}
197
198
199static void pht_real_insert(const uintptr_t vaddr, const pfn_t pfn)
200{
201 uint32_t page = (vaddr >> 12) & 0xffff;
202 uint32_t api = (vaddr >> 22) & 0x3f;
203
204 uint32_t vsid;
205 asm volatile (
206 "mfsrin %0, %1\n"
207 : "=r" (vsid)
208 : "r" (vaddr)
209 );
210
211 uint32_t sdr1;
212 asm volatile (
213 "mfsdr1 %0\n"
214 : "=r" (sdr1)
215 );
216 phte_t *phte_physical = (phte_t *) (sdr1 & 0xffff0000);
217
218 /* Primary hash (xor) */
219 uint32_t h = 0;
220 uint32_t hash = vsid ^ page;
221 uint32_t base = (hash & 0x3ff) << 3;
222 uint32_t i;
223 bool found = false;
224
225 /* Find unused or colliding
226 PTE in PTEG */
227 for (i = 0; i < 8; i++) {
228 if ((!phte_physical[base + i].v) ||
229 ((phte_physical[base + i].vsid == vsid) &&
230 (phte_physical[base + i].api == api))) {
231 found = true;
232 break;
233 }
234 }
235
236 if (!found) {
237 /* Secondary hash (not) */
238 uint32_t base2 = (~hash & 0x3ff) << 3;
239
240 /* Find unused or colliding
241 PTE in PTEG */
242 for (i = 0; i < 8; i++) {
243 if ((!phte_physical[base2 + i].v) ||
244 ((phte_physical[base2 + i].vsid == vsid) &&
245 (phte_physical[base2 + i].api == api))) {
246 found = true;
247 base = base2;
248 h = 1;
249 break;
250 }
251 }
252
253 if (!found) {
254 // TODO: A/C precedence groups
255 i = page % 8;
256 }
257 }
258
259 phte_physical[base + i].v = 1;
260 phte_physical[base + i].vsid = vsid;
261 phte_physical[base + i].h = h;
262 phte_physical[base + i].api = api;
263 phte_physical[base + i].rpn = pfn;
264 phte_physical[base + i].r = 0;
265 phte_physical[base + i].c = 0;
266 phte_physical[base + i].pp = 2; // FIXME
267}
268
269
270/** Process Instruction/Data Storage Interrupt
271 *
272 * @param n Interrupt vector number.
273 * @param istate Interrupted register context.
274 *
275 */
276void pht_refill(int n, istate_t *istate)
277{
278 uintptr_t badvaddr;
279 pte_t *pte;
280 int pfrc;
281 as_t *as;
282 bool lock;
283
284 if (AS == NULL) {
285 as = AS_KERNEL;
286 lock = false;
287 } else {
288 as = AS;
289 lock = true;
290 }
291
292 if (n == VECTOR_DATA_STORAGE) {
293 asm volatile (
294 "mfdar %0\n"
295 : "=r" (badvaddr)
296 );
297 } else
298 badvaddr = istate->pc;
299
300 page_table_lock(as, lock);
301
302 pte = find_mapping_and_check(as, lock, badvaddr,
303 PF_ACCESS_READ /* FIXME */, istate, &pfrc);
304 if (!pte) {
305 switch (pfrc) {
306 case AS_PF_FAULT:
307 goto fail;
308 break;
309 case AS_PF_DEFER:
310 /*
311 * The page fault came during copy_from_uspace()
312 * or copy_to_uspace().
313 */
314 page_table_unlock(as, lock);
315 return;
316 default:
317 panic("Unexpected pfrc (%d).", pfrc);
318 }
319 }
320
321 pte->a = 1; /* Record access to PTE */
322 pht_insert(badvaddr, pte->pfn);
323
324 page_table_unlock(as, lock);
325 return;
326
327fail:
328 page_table_unlock(as, lock);
329 pht_refill_fail(badvaddr, istate);
330}
331
332
333/** Process Instruction/Data Storage Interrupt in Real Mode
334 *
335 * @param n Interrupt vector number.
336 * @param istate Interrupted register context.
337 *
338 */
339bool pht_real_refill(int n, istate_t *istate)
340{
341 uintptr_t badvaddr;
342
343 if (n == VECTOR_DATA_STORAGE) {
344 asm volatile (
345 "mfdar %0\n"
346 : "=r" (badvaddr)
347 );
348 } else
349 badvaddr = istate->pc;
350
351 uint32_t physmem;
352 asm volatile (
353 "mfsprg3 %0\n"
354 : "=r" (physmem)
355 );
356
357 if ((badvaddr >= PA2KA(0)) && (badvaddr < PA2KA(physmem))) {
358 pht_real_insert(badvaddr, KA2PA(badvaddr) >> 12);
359 return true;
360 }
361
362 return false;
363}
364
365
366void tlb_arch_init(void)
367{
368 tlb_invalidate_all();
369}
370
371
372void tlb_invalidate_all(void)
373{
374 asm volatile (
375 "tlbsync\n"
376 );
377}
378
379
380void tlb_invalidate_asid(asid_t asid)
381{
382 uint32_t sdr1;
383 asm volatile (
384 "mfsdr1 %0\n"
385 : "=r" (sdr1)
386 );
387 phte_t *phte = (phte_t *) PA2KA(sdr1 & 0xffff0000);
388
389 uint32_t i;
390 for (i = 0; i < 8192; i++) {
391 if ((phte[i].v) && (phte[i].vsid >= (asid << 4)) &&
392 (phte[i].vsid < ((asid << 4) + 16)))
393 phte[i].v = 0;
394 }
395 tlb_invalidate_all();
396}
397
398
399void tlb_invalidate_pages(asid_t asid, uintptr_t page, count_t cnt)
400{
401 // TODO
402 tlb_invalidate_all();
403}
404
405
406#define PRINT_BAT(name, ureg, lreg) \
407 asm volatile ( \
408 "mfspr %0," #ureg "\n" \
409 "mfspr %1," #lreg "\n" \
410 : "=r" (upper), "=r" (lower) \
411 ); \
412 mask = (upper & 0x1ffc) >> 2; \
413 if (upper & 3) { \
414 uint32_t tmp = mask; \
415 length = 128; \
416 while (tmp) { \
417 if ((tmp & 1) == 0) { \
418 printf("ibat[0]: error in mask\n"); \
419 break; \
420 } \
421 length <<= 1; \
422 tmp >>= 1; \
423 } \
424 } else \
425 length = 0; \
426 printf(name ": page=%.*p frame=%.*p length=%d KB (mask=%#x)%s%s\n", \
427 sizeof(upper) * 2, upper & 0xffff0000, sizeof(lower) * 2, \
428 lower & 0xffff0000, length, mask, \
429 ((upper >> 1) & 1) ? " supervisor" : "", \
430 (upper & 1) ? " user" : "");
431
432
433void tlb_print(void)
434{
435 uint32_t sr;
436
437 for (sr = 0; sr < 16; sr++) {
438 uint32_t vsid;
439 asm volatile (
440 "mfsrin %0, %1\n"
441 : "=r" (vsid)
442 : "r" (sr << 28)
443 );
444 printf("vsid[%d]: VSID=%.*p (ASID=%d)%s%s\n", sr,
445 sizeof(vsid) * 2, vsid & 0xffffff, (vsid & 0xffffff) >> 4,
446 ((vsid >> 30) & 1) ? " supervisor" : "",
447 ((vsid >> 29) & 1) ? " user" : "");
448 }
449
450 uint32_t upper;
451 uint32_t lower;
452 uint32_t mask;
453 uint32_t length;
454
455 PRINT_BAT("ibat[0]", 528, 529);
456 PRINT_BAT("ibat[1]", 530, 531);
457 PRINT_BAT("ibat[2]", 532, 533);
458 PRINT_BAT("ibat[3]", 534, 535);
459
460 PRINT_BAT("dbat[0]", 536, 537);
461 PRINT_BAT("dbat[1]", 538, 539);
462 PRINT_BAT("dbat[2]", 540, 541);
463 PRINT_BAT("dbat[3]", 542, 543);
464}
465
466/** @}
467 */
Note: See TracBrowser for help on using the repository browser.