source: rtems/c/src/lib/libcpu/powerpc/mpc6xx/mmu/pte121.c @ 0f8b4e9

4.104.114.84.95
Last change on this file since 0f8b4e9 was 0f8b4e9, checked in by Ralf Corsepius <ralf.corsepius@…>, on 11/07/05 at 02:33:57

Eliminate unsigned32.

  • Property mode set to 100644
File size: 28.3 KB
Line 
1/* $Id$ */
2
3/* Trivial page table setup for RTEMS
4 * Purpose: allow write protection of text/ro-data
5 *
6 * Author: Till Straumann <strauman@slac.stanford.edu>, 4/2002
7 */
8
9/* Chose debugging options */
10#undef  DEBUG_MAIN              /* create a standalone (host) program for basic testing */
11#undef  DEBUG                   /* target debugging and consistency checking */
12#undef  DEBUG_EXC               /* add exception handler which reenables BAT0 and recovers from a page fault */
13
14#ifdef  DEBUG_MAIN
15#undef  DEBUG                   /* must not use these together with DEBUG_MAIN */
16#undef  DEBUG_EXC
17#endif
18
19/***************************** INCLUDE HEADERS ****************************/
20
21#ifndef DEBUG_MAIN
22#include <rtems.h>
23#include <rtems/bspIo.h>
24#include <libcpu/cpuIdent.h>
25#include <libcpu/spr.h>
26#ifdef  DEBUG_EXC
27#include <bsp.h>
28#include <bsp/vectors.h>
29#include <libcpu/raw_exception.h>
30#endif
31#endif
32
33#include <stdio.h>
34#include <assert.h>
35#include <string.h>
36
37#include "pte121.h"
38
39/************************** CONSTANT DEFINITIONS **************************/
40
41/* Base 2 logs of some sizes */
42
43#ifndef DEBUG_MAIN
44
45#define LD_PHYS_SIZE    32      /* physical address space */
46#define LD_PG_SIZE              12      /* page size */
47#define LD_PTEG_SIZE    6       /* PTEG size */
48#define LD_PTE_SIZE             3       /* PTE size  */
49#define LD_SEG_SIZE             28      /* segment size */
50#define LD_MIN_PT_SIZE  16      /* minimal size of a page table */
51#define LD_HASH_SIZE    19      /* lengh of a hash */
52#define LD_VSID_SIZE    24      /* vsid bits in seg. register */
53
54#else /* DEBUG_MAIN */
55
56/* Reduced 'fantasy' sizes for testing */
57#define LD_PHYS_SIZE    32      /* physical address space */
58#define LD_PG_SIZE              6       /* page size */
59#define LD_PTEG_SIZE    5       /* PTEG size */
60#define LD_PTE_SIZE             3       /* PTE size  */
61#define LD_SEG_SIZE             28      /* segment size */
62#define LD_MIN_PT_SIZE  7       /* minimal size of a page table */
63#define LD_HASH_SIZE    19      /* lengh of a hash */
64
65#endif /* DEBUG_MAIN */
66
67/* Derived sizes */
68
69/* Size of a page index */
70#define LD_PI_SIZE              ((LD_SEG_SIZE) - (LD_PG_SIZE))
71
72/* Number of PTEs in a PTEG */
73#define PTE_PER_PTEG    (1<<((LD_PTEG_SIZE)-(LD_PTE_SIZE)))
74
75/* Segment register bits */
76#define KEY_SUP                 (1<<30) /* supervisor mode key */
77#define KEY_USR                 (1<<29) /* user mode key */
78
79/* The range of effective addresses to scan with 'tlbie'
80 * instructions in order to flush all TLBs.
81 * On the 750 and 7400, there are 128 two way I and D TLBs,
82 * indexed by EA[14:19]. Hence calling
83 *   tlbie rx
84 * where rx scans 0x00000, 0x01000, 0x02000, ... 0x3f000
85 * is sufficient to do the job
86 */
87#define NUM_TLB_PER_WAY 64      /* 750 and 7400 have 128 two way TLBs */
88#define FLUSH_EA_RANGE  (NUM_TLB_PER_WAY<<LD_PG_SIZE)
89
90/*************************** MACRO DEFINITIONS ****************************/
91
92/* Macros to split a (32bit) 'effective' address into
93 * VSID (virtual segment id) and PI (page index)
94 * using a 1:1 mapping of 'effective' to 'virtual'
95 * addresses.
96 *
97 * For 32bit addresses this looks like follows
98 * (each 'x' or '0' stands for a 'nibble' [4bits]):
99 *
100 *         32bit effective address (EA)
101 *
102 *              x x x x x x x x
103 *               |       |
104 *    0 0 0 0 0 x|x x x x|x x x
105 *       VSID    |  PI   |  PO (page offset)
106 *               |       |
107 */
108/* 1:1 VSID of an EA  */
109#define VSID121(ea) (((ea)>>LD_SEG_SIZE) & ((1<<(LD_PHYS_SIZE-LD_SEG_SIZE))-1))
110/* page index of an EA */
111#define PI121(ea)       (((ea)>>LD_PG_SIZE) & ((1<<LD_PI_SIZE)-1))
112
113/* read VSID from segment register */
114#ifndef DEBUG_MAIN
115static uint32_t
116seg2vsid (uint32_t ea)
117{
118  asm volatile ("mfsrin %0, %0":"=r" (ea):"0" (ea));
119  return ea & ((1 << LD_VSID_SIZE) - 1);
120}
121#else
122#define seg2vsid(ea) VSID121(ea)
123#endif
124
125/* Primary and secondary PTE hash functions */
126
127/* Compute the primary hash from a VSID and a PI */
128#define PTE_HASH1(vsid, pi) (((vsid)^(pi))&((1<<LD_HASH_SIZE)-1))
129
130/* Compute the secondary hash from a primary hash */
131#define PTE_HASH2(hash1) ((~(hash1))&((1<<LD_HASH_SIZE)-1))
132
133/* Extract the abbreviated page index (which is the
134 * part of the PI which does not go into the hash
135 * under all circumstances [10 bits to -> 6bit API])
136 */
137#define API(pi) ((pi)>>((LD_MIN_PT_SIZE)-(LD_PTEG_SIZE)))
138
139
140/* Horrible Macros */
141#ifdef __rtems__
142/* must not use printf until multitasking is up */
143typedef void (*PrintF) (char *, ...);
144static PrintF
145whatPrintf (void)
146{
147  return _Thread_Executing ? (PrintF) printf : printk;
148}
149
150#define PRINTF(args...) ((void)(whatPrintf())(args))
151#else
152#define PRINTF(args...) printf(args)
153#endif
154
155#ifdef DEBUG
156unsigned long triv121PgTblConsistency (Triv121PgTbl pt, int pass, int expect);
157
158static int consistencyPass = 0;
159#define CONSCHECK(expect) triv121PgTblConsistency(&pgTbl,consistencyPass++,(expect))
160#else
161#define CONSCHECK(expect) do {} while (0)
162#endif
163
164/**************************** TYPE DEFINITIONS ****************************/
165
166/* internal description of a trivial page table */
167typedef struct Triv121PgTblRec_
168{
169  APte base;
170  unsigned long size;
171  int active;
172} Triv121PgTblRec;
173
174
175/************************** FORWARD DECLARATIONS *************************/
176
177#ifdef DEBUG_EXC
178static void myhdl (BSP_Exception_frame * excPtr);
179#endif
180
181static void dumpPte (APte pte);
182
183static void
184dumpPteg (unsigned long vsid, unsigned long pi, unsigned long hash);
185unsigned long
186triv121IsRangeMapped (long vsid, unsigned long start, unsigned long end);
187
188static void do_dssall ();
189
190/**************************** STATIC VARIABLES ****************************/
191
192/* dont malloc - we might have to use this before
193 * we have malloc or even RTEMS workspace available
194 */
195static Triv121PgTblRec pgTbl = { 0 };
196
197#ifdef DEBUG_EXC
198static void *ohdl;              /* keep a pointer to the original handler */
199#endif
200
201/*********************** INLINES & PRIVATE ROUTINES ***********************/
202
203/* compute the page table entry group (PTEG) of a hash */
204static inline APte
205ptegOf (Triv121PgTbl pt, unsigned long hash)
206{
207  hash &= ((1 << LD_HASH_SIZE) - 1);
208  return (APte) (((unsigned long) pt->
209                  base) | ((hash << LD_PTEG_SIZE) & (pt->size - 1)));
210}
211
212/* see if a vsid/pi combination is already mapped
213 *
214 * RETURNS: PTE of mapping / NULL if none exists
215 *
216 * NOTE: a vsid<0 is legal and will tell this
217 *       routine that 'pi' is actually an EA to
218 *       be split into vsid and pi...
219 */
220static APte
221alreadyMapped (Triv121PgTbl pt, long vsid, unsigned long pi)
222{
223  int i;
224  unsigned long hash, api;
225  APte pte;
226
227  if (!pt->size)
228    return 0;
229
230  if (TRIV121_121_VSID == vsid) {
231    vsid = VSID121 (pi);
232    pi = PI121 (pi);
233  } else if (TRIV121_SEG_VSID == vsid) {
234    vsid = seg2vsid (pi);
235    pi = PI121 (pi);
236  }
237
238  hash = PTE_HASH1 (vsid, pi);
239  api = API (pi);
240  for (i = 0, pte = ptegOf (pt, hash); i < PTE_PER_PTEG; i++, pte++)
241    if (pte->v && pte->vsid == vsid && pte->api == api && 0 == pte->h)
242      return pte;
243  /* try the secondary hash table */
244  hash = PTE_HASH2 (hash);
245  for (i = 0, pte = ptegOf (pt, hash); i < PTE_PER_PTEG; i++, pte++)
246    if (pte->v && pte->vsid == vsid && pte->api == api && 1 == pte->h)
247      return pte;
248  return 0;
249}
250
251/* find the first available slot for  vsid/pi
252 *
253 * NOTE: it is NOT legal to pass a vsid<0 / EA combination.
254 *
255 * RETURNS free slot with the 'marked' field set. The 'h'
256 *         field is set to 0 or one, depending on whether
257 *         the slot was allocated by using the primary or
258 *         the secondary hash, respectively.
259 */
260static APte
261slotFor (Triv121PgTbl pt, unsigned long vsid, unsigned long pi)
262{
263  int i;
264  unsigned long hash, api;
265  APte pte;
266
267  /* primary hash */
268  hash = PTE_HASH1 (vsid, pi);
269  api = API (pi);
270  /* linear search thru all buckets for this hash */
271  for (i = 0, pte = ptegOf (pt, hash); i < PTE_PER_PTEG; i++, pte++) {
272    if (!pte->v && !pte->marked) {
273      /* found a free PTE; mark it as potentially used and return */
274      pte->h = 0;               /* found by the primary hash fn */
275      pte->marked = 1;
276      return pte;
277    }
278  }
279
280#ifdef DEBUG
281  /* Strange: if the hash table was allocated big enough,
282   *          this should not happen (when using a 1:1 mapping)
283   *          Give them some information...
284   */
285  PRINTF ("## First hash bucket full - ");
286  dumpPteg (vsid, pi, hash);
287#endif
288
289  hash = PTE_HASH2 (hash);
290#ifdef DEBUG
291  PRINTF ("   Secondary pteg is 0x%08x\n", (unsigned) ptegOf (pt, hash));
292#endif
293  for (i = 0, pte = ptegOf (pt, hash); i < PTE_PER_PTEG; i++, pte++) {
294    if (!pte->v && !pte->marked) {
295      /* mark this pte as potentially used */
296      pte->marked = 1;
297      pte->h = 1;
298      return pte;
299    }
300  }
301#ifdef DEBUG
302  /* Even more strange - most likely, something is REALLY messed up */
303  PRINTF ("## Second hash bucket full - ");
304  dumpPteg (vsid, pi, hash);
305#endif
306  return 0;
307}
308
309/* unmark all entries */
310static void
311unmarkAll (Triv121PgTbl pt)
312{
313  unsigned long n = pt->size / sizeof (PTERec);
314  unsigned long i;
315  APte pte;
316  for (i = 0, pte = pt->base; i < n; i++, pte++)
317    pte->marked = 0;
318
319}
320
321/* calculate the minimal size of a page/hash table
322 * to map a range of 'size' bytes in EA space.
323 *
324 * RETURNS: size in 'number of bits', i.e. the
325 *          integer part of LOGbase2(minsize)
326 *          is returned.
327 * NOTE:        G3/G4 machines need at least 16 bits
328 *          (64k).
329 */
330unsigned long
331triv121PgTblLdMinSize (unsigned long size)
332{
333  unsigned long i;
334  /* round 'size' up to the next page boundary */
335  size += (1 << LD_PG_SIZE) - 1;
336  size &= ~((1 << LD_PG_SIZE) - 1);
337  /* divide by number of PTEs  and multiply
338   * by the size of a PTE.
339   */
340  size >>= LD_PG_SIZE - LD_PTE_SIZE;
341  /* find the next power of 2 >= size */
342  for (i = 0; i < LD_PHYS_SIZE; i++) {
343    if ((1 << i) >= size)
344      break;
345  }
346  /* pop up to the allowed minimum, if necessary */
347  if (i < LD_MIN_PT_SIZE)
348    i = LD_MIN_PT_SIZE;
349  return i;
350}
351
352/* initialize a trivial page table of 2^ldSize bytes
353 * at 'base' in memory.
354 *
355 * RETURNS:     OPAQUE HANDLE (not the hash table address)
356 *          or NULL on failure.
357 */
358Triv121PgTbl
359triv121PgTblInit (unsigned long base, unsigned ldSize)
360{
361  if (pgTbl.size) {
362    /* already initialized */
363    return 0;
364  }
365
366  if (ldSize < LD_MIN_PT_SIZE)
367    return 0;                   /* too small */
368
369  if (base & ((1 << ldSize) - 1))
370    return 0;                   /* misaligned */
371
372  /* This was tested on 604r, 750 and 7400.
373   * On other CPUs, verify that the TLB invalidation works
374   * for a new CPU variant and that it has hardware PTE lookup/
375   * TLB replacement before adding it to this list.
376   *
377   * NOTE: The 603 features no hardware PTE lookup - and
378   *       hence the page tables should NOT be used.
379   *               Although lookup could be implemented in
380   *               software this is probably not desirable
381   *               as it could have an impact on hard realtime
382   *               performance, screwing deterministic latency!
383   *               (Could still be useful for debugging, though)
384   */
385  if (   PPC_604 != current_ppc_cpu
386      && PPC_604e != current_ppc_cpu
387      && PPC_604r != current_ppc_cpu
388      && PPC_750  != current_ppc_cpu
389      && PPC_7400 != current_ppc_cpu
390      && PPC_7455 != current_ppc_cpu
391      && PPC_7457 != current_ppc_cpu
392     )
393    return 0;                   /* unsupported by this CPU */
394
395  pgTbl.base = (APte) base;
396  pgTbl.size = 1 << ldSize;
397  /* clear all page table entries */
398  memset (pgTbl.base, 0, pgTbl.size);
399
400  CONSCHECK (0);
401
402  /* map the page table itself 'm' and 'readonly' */
403  if (triv121PgTblMap (&pgTbl,
404                       TRIV121_121_VSID,
405                       base,
406                       (pgTbl.size >> LD_PG_SIZE),
407                       TRIV121_ATTR_M, TRIV121_PP_RO_PAGE) >= 0)
408    return 0;
409
410  CONSCHECK ((pgTbl.size >> LD_PG_SIZE));
411
412  return &pgTbl;
413}
414
415/* return the handle of the (one and only) page table
416 * or NULL if none has been initialized yet.
417 */
418Triv121PgTbl
419triv121PgTblGet (void)
420{
421  return pgTbl.size ? &pgTbl : 0;
422}
423
424/* NOTE: this routine returns -1 on success;
425 *       on failure, the page table index for
426 *       which no PTE could be allocated is returned
427 *
428 * (Consult header about argument/return value
429 * description)
430 */
431long
432triv121PgTblMap (Triv121PgTbl pt,
433                 long ovsid,
434                 unsigned long start,
435                 unsigned long numPages,
436                 unsigned attributes, unsigned protection)
437{
438  int i, pass;
439  unsigned long pi;
440  APte pte;
441  long vsid;
442#ifdef DEBUG
443  long saved_vsid = ovsid;
444#endif
445
446  if (TRIV121_121_VSID == ovsid) {
447    /* use 1:1 mapping */
448    ovsid = VSID121 (start);
449  } else if (TRIV121_SEG_VSID == ovsid) {
450    ovsid = seg2vsid (start);
451  }
452
453#ifdef DEBUG
454  PRINTF ("Mapping %i (0x%x) pages at 0x%08x for VSID 0x%08x\n",
455          (unsigned) numPages, (unsigned) numPages,
456          (unsigned) start, (unsigned) ovsid);
457#endif
458
459  /* map in two passes. During the first pass, we try
460   * to claim entries as needed. The 'slotFor()' routine
461   * will 'mark' the claimed entries without 'valid'ating
462   * them.
463   * If the mapping fails, all claimed entries are unmarked
464   * and we return the PI for which allocation failed.
465   *
466   * Once we know that the allocation would succeed, we
467   * do a second pass; during the second pass, the PTE
468   * is actually written.
469   *
470   */
471  for (pass = 0; pass < 2; pass++) {
472    /* check if we would succeed during the first pass */
473    for (i = 0, pi = PI121 (start), vsid = ovsid; i < numPages; i++, pi++) {
474      if (pi >= 1 << LD_PI_SIZE) {
475        vsid++;
476        pi = 0;
477      }
478      /* leave alone existing mappings for this EA */
479      if (!alreadyMapped (pt, vsid, pi)) {
480        if (!(pte = slotFor (pt, vsid, pi))) {
481          /* no free slot found for page index 'pi' */
482          unmarkAll (pt);
483          return pi;
484        } else {
485          /* have a free slot; marked by slotFor() */
486          if (pass) {
487            /* second pass; do the real work */
488            pte->vsid = vsid;
489            /* H was set by slotFor() */
490            pte->api = API (pi);
491            /* set up 1:1 mapping */
492            pte->rpn =
493              ((((unsigned long) vsid) &
494                ((1 << (LD_PHYS_SIZE - LD_SEG_SIZE)) -
495                 1)) << LD_PI_SIZE) | pi;
496            pte->wimg = attributes & 0xf;
497            pte->pp = protection & 0x3;
498            /* mark it valid */
499            pte->marked = 0;
500            if (pt->active) {
501              uint32_t flags;
502              rtems_interrupt_disable (flags);
503              /* order setting 'v' after writing everything else */
504              asm volatile ("eieio");
505              pte->v = 1;
506              asm volatile ("sync");
507              rtems_interrupt_enable (flags);
508            } else {
509              pte->v = 1;
510            }
511
512#ifdef DEBUG
513            /* add paranoia */
514            assert (alreadyMapped (pt, vsid, pi) == pte);
515#endif
516          }
517        }
518      }
519    }
520    unmarkAll (pt);
521  }
522#ifdef DEBUG
523  {
524    unsigned long failedat;
525    CONSCHECK (-1);
526    /* double check that the requested range is mapped */
527    failedat =
528      triv121IsRangeMapped (saved_vsid, start,
529                            start + (1 << LD_PG_SIZE) * numPages);
530    if (0x0C0C != failedat) {
531      PRINTF ("triv121 mapping failed at 0x%08x\n", (unsigned) failedat);
532      return PI121 (failedat);
533    }
534  }
535#endif
536  return TRIV121_MAP_SUCCESS;   /* -1 !! */
537}
538
539unsigned long
540triv121PgTblSDR1 (Triv121PgTbl pt)
541{
542  return (((unsigned long) pt->base) & ~((1 << LD_MIN_PT_SIZE) - 1)) |
543    (((pt->size - 1) >> LD_MIN_PT_SIZE) &
544     ((1 << (LD_HASH_SIZE - (LD_MIN_PT_SIZE - LD_PTEG_SIZE))) - 1)
545    );
546}
547
548void
549triv121PgTblActivate (Triv121PgTbl pt)
550{
551#ifndef DEBUG_MAIN
552  unsigned long sdr1 = triv121PgTblSDR1 (pt);
553#endif
554  pt->active = 1;
555
556#ifndef DEBUG_MAIN
557#ifdef DEBUG_EXC
558  /* install our exception handler */
559  ohdl = globalExceptHdl;
560  globalExceptHdl = myhdl;
561  __asm__ __volatile__ ("sync");
562#endif
563
564  /* This section of assembly code takes care of the
565   * following:
566   * - get MSR and switch interrupts + MMU off
567   *
568   * - load up the segment registers with a
569   *   1:1 effective <-> virtual mapping;
570   *   give user & supervisor keys
571   *
572   * - flush all TLBs;
573   *   NOTE: the TLB flushing code is probably
574   *         CPU dependent!
575   *
576   * - setup SDR1
577   *
578   * - restore original MSR
579   */
580  __asm__ __volatile (
581    "   mtctr   %0\n"
582    /* Get MSR and switch interrupts off - just in case.
583     * Also switch the MMU off; the book
584     * says that SDR1 must not be changed with either
585     * MSR_IR or MSR_DR set. I would guess that it could
586     * be safe as long as the IBAT & DBAT mappings override
587     * the page table...
588     */
589    "   mfmsr   %0\n"
590    "   andc    %6, %0, %6\n"
591    "   mtmsr   %6\n"
592    "   isync   \n"
593    /* set up the segment registers */
594    "   li              %6, 0\n"
595    "1: mtsrin  %1, %6\n"
596    "   addis   %6, %6, 0x1000\n" /* address next SR */
597    "   addi    %1, %1, 1\n"      /* increment VSID  */
598    "   bdnz    1b\n"
599    /* Now flush all TLBs, starting with the topmost index */
600    "   lis             %6, %2@h\n"
601    "2: addic.  %6, %6, -%3\n"    /* address the next one (decrementing) */
602    "   tlbie   %6\n"             /* invalidate & repeat */
603    "   bgt             2b\n"
604    "   eieio   \n"
605    "   tlbsync \n"
606    "   sync    \n"
607    /* set up SDR1 */
608    "   mtspr   %4, %5\n"
609    /* restore original MSR  */
610    "   mtmsr   %0\n"
611    "   isync   \n"
612      :
613      :"r" (16), "b" (KEY_USR | KEY_SUP),
614       "i" (FLUSH_EA_RANGE), "i" (1 << LD_PG_SIZE),
615       "i" (SDR1), "r" (sdr1), "b" (MSR_EE | MSR_IR | MSR_DR)
616      :"ctr", "cc"
617  );
618
619  /* At this point, BAT0 is probably still active; it's the
620   * caller's job to deactivate it...
621   */
622#endif
623}
624
625/**************************  DEBUGGING ROUTINES  *************************/
626
627/* Exception handler to catch page faults */
628#ifdef DEBUG_EXC
629
630#define BAT_VALID_BOTH  3       /* allow user + super access */
631
632static void
633myhdl (BSP_Exception_frame * excPtr)
634{
635  if (3 == excPtr->_EXC_number) {
636    unsigned long dsisr;
637
638    /* reactivate DBAT0 and read DSISR */
639    __asm__ __volatile__ (
640      "mfspr %0, %1   \n"
641      "ori   %0, %0, 3\n"
642      "mtspr %1, %0   \n"
643      "sync\n"
644      "mfspr %0, %2\n"
645        :"=&r" (dsisr)
646        :"i" (DBAT0U), "i" (DSISR), "i" (BAT_VALID_BOTH)
647    );
648
649    printk ("Data Access Exception (DSI) # 3\n");
650    printk ("Reactivated DBAT0 mapping\n");
651
652
653    printk ("DSISR 0x%08x\n", dsisr);
654
655    printk ("revectoring to prevent default handler panic().\n");
656    printk ("NOTE: exception number %i below is BOGUS\n", ASM_DEC_VECTOR);
657    /* make this exception 'recoverable' for
658     * the default handler by faking a decrementer
659     * exception.
660     * Note that the default handler's message will be
661     * wrong about the exception number.
662     */
663    excPtr->_EXC_number = ASM_DEC_VECTOR;
664  }
665/* now call the original handler */
666  ((void (*)()) ohdl) (excPtr);
667}
668#endif
669
670
671
672/* test the consistency of the page table
673 *
674 * 'pass' is merely a number which will be printed
675 * by this routine, so the caller may give some
676 * context information.
677 *
678 * 'expected' is the number of valid (plus 'marked')
679 * entries the caller believes the page table should
680 * have. This routine complains if its count differs.
681 *
682 * It basically verifies that the topmost 20bits
683 * of all VSIDs as well as the unused bits are all
684 * zero. Then it counts all valid and all 'marked'
685 * entries, adding them up and comparing them to the
686 * 'expected' number of occupied slots.
687 *
688 * RETURNS: total number of valid plus 'marked' slots.
689 */
690unsigned long
691triv121PgTblConsistency (Triv121PgTbl pt, int pass, int expected)
692{
693  APte pte;
694  int i;
695  unsigned v, m;
696  int warn = 0;
697  static int maxw = 20;         /* mute after detecting this many errors */
698
699  PRINTF ("Checking page table at 0x%08x (size %i==0x%x)\n",
700          (unsigned) pt->base, (unsigned) pt->size, (unsigned) pt->size);
701
702  if (!pt->base || !pt->size) {
703    PRINTF ("Uninitialized Page Table!\n");
704    return 0;
705  }
706
707  v = m = 0;
708#if 1
709  /* 10/9/2002: I had machine checks crashing after this loop
710   *            terminated. Maybe caused by speculative loads
711   *            from beyond the valid memory area (since the
712   *            page hash table sits at the top of physical
713   *            memory).
714   *            Very bizarre - the other loops in this file
715   *            seem to be fine. Maybe there is a compiler bug??
716   *            For the moment, I let the loop run backwards...
717   *
718   *                        Also see the comment a couple of lines down.
719   */
720  for (i = pt->size / sizeof (PTERec) - 1, pte = pt->base + i; i >= 0;
721       i--, pte--)
722#else
723  for (i = 0, pte = pt->base; i < pt->size / sizeof (PTERec); i++, pte++)
724#endif
725  {
726    int err = 0;
727    char buf[500];
728    unsigned long *lp = (unsigned long *) pte;
729#if 0
730    /* If I put this bogus while statement here (the body is
731     * never reached), the original loop works OK
732     */
733    while (pte >= pt->base + pt->size / sizeof (PTERec))
734      /* never reached */ ;
735#endif
736
737    if ((*lp & (0xfffff0 << 7)) || *(lp + 1) & 0xe00
738        || (pte->v && pte->marked)) {
739      /* check for vsid (without segment bits) == 0, unused bits == 0, valid && marked */
740      sprintf (buf, "invalid VSID , unused bits or v && m");
741      err = 1;
742    } else {
743      if (pte->v)
744        v++;
745      if (pte->marked)
746        m++;
747    }
748    if (err && maxw) {
749      PRINTF
750        ("Pass %i -- strange PTE at 0x%08x found for page index %i == 0x%08x:\n",
751         pass, (unsigned) pte, i, i);
752      PRINTF ("Reason: %s\n", buf);
753      dumpPte (pte);
754      warn++;
755      maxw--;
756    }
757  }
758  if (warn) {
759    PRINTF ("%i errors found; currently %i entries marked, %i are valid\n",
760            warn, m, v);
761  }
762  v += m;
763  if (maxw && expected >= 0 && expected != v) {
764    /* number of occupied slots not what they expected */
765    PRINTF ("Wrong # of occupied slots detected during pass");
766    PRINTF ("%i; should be %i (0x%x) is %i (0x%x)\n",
767            pass, expected, (unsigned) expected, v, (unsigned) v);
768    maxw--;
769  }
770  return v;
771}
772
773/* Find the PTE for a EA and print its contents
774 * RETURNS: pte for EA or NULL if no entry was found.
775 */
776APte
777triv121DumpEa (unsigned long ea)
778{
779  APte pte;
780
781  pte =
782    alreadyMapped (&pgTbl, pgTbl.active ? TRIV121_SEG_VSID : TRIV121_121_VSID,
783                   ea);
784
785  if (pte)
786    dumpPte (pte);
787  return pte;
788}
789
790APte
791triv121FindPte (unsigned long vsid, unsigned long pi)
792{
793  return alreadyMapped (&pgTbl, vsid, pi);
794}
795
796APte
797triv121UnmapEa (unsigned long ea)
798{
799  uint32_t flags;
800  APte pte;
801
802  if (!pgTbl.active) {
803    pte = alreadyMapped (&pgTbl, TRIV121_121_VSID, ea);
804    if (pte)                    /* alreadyMapped checks for pte->v */
805      pte->v = 0;
806    return pte;
807  }
808
809  pte = alreadyMapped (&pgTbl, TRIV121_SEG_VSID, ea);
810
811  if (!pte)
812    return 0;
813
814  rtems_interrupt_disable (flags);
815  pte->v = 0;
816  do_dssall ();
817  asm volatile ("       sync            \n\t"
818                "       tlbie %0        \n\t"
819                "       eieio           \n\t"
820                "       tlbsync         \n\t"
821                "       sync            \n\t"::"r" (ea));
822  rtems_interrupt_enable (flags);
823  return pte;
824}
825
826/* A context synchronizing jump */
827#define SYNC_LONGJMP(msr)                               \
828        asm volatile(                                           \
829                "       mtsrr1  %0                      \n\t"   \
830                "       bl              1f                      \n\t"   \
831                "1:     mflr    3                       \n\t"   \
832                "       addi    3,3,1f-1b       \n\t"   \
833                "       mtsrr0  3                       \n\t"   \
834                "       rfi                                     \n\t"   \
835                "1:                                             \n\t"   \
836                :                                                               \
837                :"r"(msr)                                               \
838                :"3","lr")
839
840/* The book doesn't mention dssall when changing PTEs
841 * but they require it for BAT changes and I guess
842 * it makes sense in the case of PTEs as well.
843 * Just do it to be on the safe side...
844 */
845static void
846do_dssall ()
847{
848  /* Before changing BATs, 'dssall' must be issued.
849   * We check MSR for MSR_VE and issue a 'dssall' if
850   * MSR_VE is set hoping that
851   *  a) on non-altivec CPUs MSR_VE reads as zero
852   *  b) all altivec CPUs use the same bit
853   */
854  if (_read_MSR () & MSR_VE) {
855    /* this construct is needed because we don't know
856     * if this file is compiled with -maltivec.
857     * (I plan to add altivec support outside of
858     * RTEMS core and hence I'd rather not
859     * rely on consistent compiler flags).
860     */
861#define DSSALL  0x7e00066c      /* dssall opcode */
862    asm volatile ("     .long %0"::"i" (DSSALL));
863#undef  DSSALL
864  }
865}
866
867APte
868triv121ChangeEaAttributes (unsigned long ea, int wimg, int pp)
869{
870  APte pte;
871  unsigned long msr;
872
873  if (!pgTbl.active) {
874    pte = alreadyMapped (&pgTbl, TRIV121_121_VSID, ea);
875    if (!pte)
876      return 0;
877    if (wimg > 0)
878      pte->wimg = wimg;
879    if (pp > 0)
880      pte->pp = pp;
881    return pte;
882  }
883
884  pte = alreadyMapped (&pgTbl, TRIV121_SEG_VSID, ea);
885
886  if (!pte)
887    return 0;
888
889  if (wimg < 0 && pp < 0)
890    return pte;
891
892  asm volatile ("mfmsr %0":"=r" (msr));
893
894  /* switch MMU and IRQs off */
895  SYNC_LONGJMP (msr & ~(MSR_EE | MSR_DR | MSR_IR));
896
897  pte->v = 0;
898  do_dssall ();
899  asm volatile ("sync");
900  if (wimg >= 0)
901    pte->wimg = wimg;
902  if (pp >= 0)
903    pte->pp = pp;
904  asm volatile ("tlbie %0; eieio"::"r" (ea));
905  pte->v = 1;
906  asm volatile ("tlbsync; sync");
907
908  /* restore, i.e., switch MMU and IRQs back on */
909  SYNC_LONGJMP (msr);
910
911  return pte;
912}
913
914static void
915pgtblChangePP (Triv121PgTbl pt, int pp)
916{
917  unsigned long n = pt->size >> LD_PG_SIZE;
918  unsigned long b, i;
919
920  for (i = 0, b = (unsigned long) pt->base; i < n;
921       i++, b += (1 << LD_PG_SIZE)) {
922    triv121ChangeEaAttributes (b, -1, pp);
923  }
924}
925
926void
927triv121MakePgTblRW ()
928{
929  pgtblChangePP (&pgTbl, TRIV121_PP_RW_PAGE);
930}
931
932void
933triv121MakePgTblRO ()
934{
935  pgtblChangePP (&pgTbl, TRIV121_PP_RO_PAGE);
936}
937
938long
939triv121DumpPte (APte pte)
940{
941  if (pte)
942    dumpPte (pte);
943  return 0;
944}
945
946
947#ifdef DEBUG
948/* Dump an entire PTEG */
949
950static void
951dumpPteg (unsigned long vsid, unsigned long pi, unsigned long hash)
952{
953  APte pte = ptegOf (&pgTbl, hash);
954  int i;
955  PRINTF ("hash 0x%08x, pteg 0x%08x (vsid 0x%08x, pi 0x%08x)\n",
956          (unsigned) hash, (unsigned) pte, (unsigned) vsid, (unsigned) pi);
957  for (i = 0; i < PTE_PER_PTEG; i++, pte++) {
958    PRINTF ("pte 0x%08x is 0x%08x : 0x%08x\n",
959            (unsigned) pte,
960            (unsigned) *(unsigned long *) pte,
961            (unsigned) *(((unsigned long *) pte) + 1));
962  }
963}
964#endif
965
966/* Verify that a range of EAs is mapped the page table
967 * (if vsid has one of the special values -- otherwise,
968 * start/end are page indices).
969 *
970 * RETURNS: address of the first page for which no
971 *          PTE was found (i.e. page index * page size)
972 *         
973 *          ON SUCCESS, the special value 0x0C0C ("OKOK")
974 *          [which is not page aligned and hence is not
975 *          a valid page address].
976 */
977unsigned long
978triv121IsRangeMapped (long vsid, unsigned long start, unsigned long end)
979{
980  start &= ~((1 << LD_PG_SIZE) - 1);
981  while (start < end) {
982    if (!alreadyMapped (&pgTbl, vsid, start))
983      return start;
984    start += 1 << LD_PG_SIZE;
985  }
986  return 0x0C0C;                /* OKOK - not on a page boundary */
987}
988
989
990#include <stdlib.h>
991
992/* print a PTE */
993static void
994dumpPte (APte pte)
995{
996  if (0 == ((unsigned long) pte & ((1 << LD_PTEG_SIZE) - 1)))
997    PRINTF ("PTEG--");
998  else
999    PRINTF ("......");
1000  if (pte->v) {
1001    PRINTF ("VSID: 0x%08x H:%1i API: 0x%02x\n", pte->vsid, pte->h, pte->api);
1002    PRINTF ("      ");
1003    PRINTF ("RPN:  0x%08x WIMG: 0x%1x, (m %1i), pp: 0x%1x\n",
1004            pte->rpn, pte->wimg, pte->marked, pte->pp);
1005  } else {
1006    PRINTF ("xxxxxx\n");
1007    PRINTF ("      ");
1008    PRINTF ("xxxxxx\n");
1009  }
1010}
1011
1012
1013/* dump page table entries from index 'from' to 'to'
1014 * The special values (unsigned)-1 are allowed which
1015 * cause the routine to dump the entire table.
1016 *
1017 * RETURNS 0
1018 */
1019int
1020triv121PgTblDump (Triv121PgTbl pt, unsigned from, unsigned to)
1021{
1022  int i;
1023  APte pte;
1024  PRINTF ("Dumping PT [size 0x%08x == %i] at 0x%08x\n",
1025          (unsigned) pt->size, (unsigned) pt->size, (unsigned) pt->base);
1026  if (from > pt->size >> LD_PTE_SIZE)
1027    from = 0;
1028  if (to > pt->size >> LD_PTE_SIZE)
1029    to = (pt->size >> LD_PTE_SIZE);
1030  for (i = from, pte = pt->base + from; i < (long) to; i++, pte++) {
1031    dumpPte (pte);
1032  }
1033  return 0;
1034}
1035
1036
1037#if defined(DEBUG_MAIN)
1038
1039#define LD_DBG_PT_SIZE  LD_MIN_PT_SIZE
1040
1041int
1042main (int argc, char **argv)
1043{
1044  unsigned long base, start, numPages;
1045  unsigned long size = 1 << LD_DBG_PT_SIZE;
1046  Triv121PgTbl pt;
1047
1048  base = (unsigned long) malloc (size << 1);
1049
1050  assert (base);
1051
1052  /* align pt */
1053  base += size - 1;
1054  base &= ~(size - 1);
1055
1056  assert (pt = triv121PgTblInit (base, LD_DBG_PT_SIZE));
1057
1058  triv121PgTblDump (pt, (unsigned) -1, (unsigned) -1);
1059  do {
1060    do {
1061      PRINTF ("Start Address:");
1062      fflush (stdout);
1063    } while (1 != scanf ("%i", &start));
1064    do {
1065      PRINTF ("# pages:");
1066      fflush (stdout);
1067    } while (1 != scanf ("%i", &numPages));
1068  } while (TRIV121_MAP_SUCCESS ==
1069           triv121PgTblMap (pt, TRIV121_121_VSID, start, numPages,
1070                            TRIV121_ATTR_IO_PAGE, 2)
1071           && 0 == triv121PgTblDump (pt, (unsigned) -1, (unsigned) -1));
1072}
1073#endif
Note: See TracBrowser for help on using the repository browser.