Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:58

0001 /*
0002  * Trivial page table setup for RTEMS
0003  * Purpose: allow write protection of text/RO-data
0004  */
0005 
0006 /*
0007  * Authorship
0008  * ----------
0009  * This software was created by
0010  *     Till Straumann <strauman@slac.stanford.edu>, 4/2002, 2003, 2004,
0011  *     Stanford Linear Accelerator Center, Stanford University.
0012  *
0013  * Acknowledgement of sponsorship
0014  * ------------------------------
0015  * This software was produced by
0016  *     the Stanford Linear Accelerator Center, Stanford University,
0017  *     under Contract DE-AC03-76SFO0515 with the Department of Energy.
0018  *
0019  * Government disclaimer of liability
0020  * ----------------------------------
0021  * Neither the United States nor the United States Department of Energy,
0022  * nor any of their employees, makes any warranty, express or implied, or
0023  * assumes any legal liability or responsibility for the accuracy,
0024  * completeness, or usefulness of any data, apparatus, product, or process
0025  * disclosed, or represents that its use would not infringe privately owned
0026  * rights.
0027  *
0028  * Stanford disclaimer of liability
0029  * --------------------------------
0030  * Stanford University makes no representations or warranties, express or
0031  * implied, nor assumes any liability for the use of this software.
0032  *
0033  * Stanford disclaimer of copyright
0034  * --------------------------------
0035  * Stanford University, owner of the copyright, hereby disclaims its
0036  * copyright and all other rights in this software.  Hence, anyone may
0037  * freely use it for any purpose without restriction.
0038  *
0039  * Maintenance of notices
0040  * ----------------------
0041  * In the interest of clarity regarding the origin and status of this
0042  * SLAC software, this and all the preceding Stanford University notices
0043  * are to remain affixed to any copy or derivative of this software made
0044  * or distributed by the recipient and are to be affixed to any copy of
0045  * software made or distributed by the recipient that contains a copy or
0046  * derivative of this software.
0047  *
0048  * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
0049  */
0050 
0051 /* Chose debugging options */
0052 #undef  DEBUG_MAIN              /* create a standalone (host) program for basic testing */
0053 #undef  DEBUG                   /* target debugging and consistency checking */
0054 #undef  DEBUG_EXC               /* add exception handler which reenables BAT0 and recovers from a page fault */
0055 
0056 #ifdef  DEBUG_MAIN
0057 #undef  DEBUG                   /* must not use these together with DEBUG_MAIN */
0058 #undef  DEBUG_EXC
0059 #endif
0060 
0061 /***************************** INCLUDE HEADERS ****************************/
0062 
0063 #ifndef DEBUG_MAIN
0064 #include <rtems.h>
0065 #include <rtems/bspIo.h>
0066 #include <rtems/score/percpu.h>
0067 #include <libcpu/cpuIdent.h>
0068 #include <libcpu/spr.h>
0069 #ifdef  DEBUG_EXC
0070 #include <bsp.h>
0071 #include <bsp/vectors.h>
0072 #endif
0073 #endif
0074 
0075 #include <stdio.h>
0076 #include <assert.h>
0077 #include <string.h>
0078 
0079 #include <libcpu/pte121.h>
0080 
0081 /************************** CONSTANT DEFINITIONS **************************/
0082 
0083 /* Base 2 logs of some sizes */
0084 
0085 #ifndef DEBUG_MAIN
0086 
0087 #define LD_PHYS_SIZE    32      /* physical address space */
0088 #define LD_PG_SIZE      12      /* page size */
0089 #define LD_PTEG_SIZE    6       /* PTEG size */
0090 #define LD_PTE_SIZE     3       /* PTE size  */
0091 #define LD_SEG_SIZE     28      /* segment size */
0092 #define LD_MIN_PT_SIZE  16      /* minimal size of a page table */
0093 #define LD_HASH_SIZE    19      /* lengh of a hash */
0094 #define LD_VSID_SIZE    24      /* vsid bits in seg. register */
0095 
0096 #else /* DEBUG_MAIN */
0097 
0098 /* Reduced 'fantasy' sizes for testing */
0099 #define LD_PHYS_SIZE    32      /* physical address space */
0100 #define LD_PG_SIZE      6       /* page size */
0101 #define LD_PTEG_SIZE    5       /* PTEG size */
0102 #define LD_PTE_SIZE     3       /* PTE size  */
0103 #define LD_SEG_SIZE     28      /* segment size */
0104 #define LD_MIN_PT_SIZE  7       /* minimal size of a page table */
0105 #define LD_HASH_SIZE    19      /* lengh of a hash */
0106 
0107 #endif /* DEBUG_MAIN */
0108 
0109 /* Derived sizes */
0110 
0111 /* Size of a page index */
0112 #define LD_PI_SIZE      ((LD_SEG_SIZE) - (LD_PG_SIZE))
0113 
0114 /* Number of PTEs in a PTEG */
0115 #define PTE_PER_PTEG    (1<<((LD_PTEG_SIZE)-(LD_PTE_SIZE)))
0116 
0117 /* Segment register bits */
0118 #define KEY_SUP         (1<<30) /* supervisor mode key */
0119 #define KEY_USR         (1<<29) /* user mode key */
0120 
0121 /* The range of effective addresses to scan with 'tlbie'
0122  * instructions in order to flush all TLBs.
0123  * On the 750 and 7400, there are 128 two way I and D TLBs,
0124  * indexed by EA[14:19]. Hence calling
0125  *   tlbie rx, 0
0126  * where rx scans 0x00000, 0x01000, 0x02000, ... 0x3f000
0127  * is sufficient to do the job. The 0 in the tlbie instruction is the L operand
0128  * which selects a 4KiB page size.
0129  */
0130 #define NUM_TLB_PER_WAY 64      /* 750 and 7400 have 128 two way TLBs */
0131 #define FLUSH_EA_RANGE  (NUM_TLB_PER_WAY<<LD_PG_SIZE)
0132 
0133 /*************************** MACRO DEFINITIONS ****************************/
0134 
0135 /* Macros to split a (32bit) 'effective' address into
0136  * VSID (virtual segment id) and PI (page index)
0137  * using a 1:1 mapping of 'effective' to 'virtual'
0138  * addresses.
0139  *
0140  * For 32bit addresses this looks like follows
0141  * (each 'x' or '0' stands for a 'nibble' [4bits]):
0142  *
0143  *         32bit effective address (EA)
0144  *
0145  *              x x x x x x x x
0146  *               |       |
0147  *    0 0 0 0 0 x|x x x x|x x x
0148  *       VSID    |  PI   |  PO (page offset)
0149  *               |       |
0150  */
0151 /* 1:1 VSID of an EA  */
0152 #define VSID121(ea) (((ea)>>LD_SEG_SIZE) & ((1<<(LD_PHYS_SIZE-LD_SEG_SIZE))-1))
0153 /* page index of an EA */
0154 #define PI121(ea)   (((ea)>>LD_PG_SIZE) & ((1<<LD_PI_SIZE)-1))
0155 
0156 /* read VSID from segment register */
0157 #ifndef DEBUG_MAIN
0158 static uint32_t
0159 seg2vsid (uint32_t ea)
0160 {
0161   __asm__ volatile (
0162     ".machine \"push\"\n"
0163     ".machine \"any\"\n"
0164     "mfsrin %0, %0\n"
0165     ".machine \"pop\"" :
0166     "=r" (ea) :
0167     "0" (ea)
0168   );
0169   return ea & ((1 << LD_VSID_SIZE) - 1);
0170 }
0171 #else
0172 #define seg2vsid(ea) VSID121(ea)
0173 #endif
0174 
0175 /* Primary and secondary PTE hash functions */
0176 
0177 /* Compute the primary hash from a VSID and a PI */
0178 #define PTE_HASH1(vsid, pi) (((vsid)^(pi))&((1<<LD_HASH_SIZE)-1))
0179 
0180 /* Compute the secondary hash from a primary hash */
0181 #define PTE_HASH2(hash1) ((~(hash1))&((1<<LD_HASH_SIZE)-1))
0182 
0183 /* Extract the abbreviated page index (which is the
0184  * part of the PI which does not go into the hash
0185  * under all circumstances [10 bits to -> 6bit API])
0186  */
0187 #define API(pi) ((pi)>>((LD_MIN_PT_SIZE)-(LD_PTEG_SIZE)))
0188 
0189 
0190 /* Horrible Macros */
0191 #ifdef __rtems__
0192 /* must not use printf until multitasking is up */
0193 typedef int (*PrintF) (const char *, ...);
0194 static PrintF
0195 whatPrintf (void)
0196 {
0197   return _Thread_Executing ? printf : printk;
0198 }
0199 
0200 #define PRINTF(args...) ((void)(whatPrintf())(args))
0201 #else
0202 #define PRINTF(args...) printf(args)
0203 #endif
0204 
0205 #ifdef DEBUG
0206 static unsigned long triv121PgTblConsistency(
0207   Triv121PgTbl pt, int pass, int expect);
0208 
0209 static int consistencyPass = 0;
0210 #define CONSCHECK(expect) triv121PgTblConsistency(&pgTbl,consistencyPass++,(expect))
0211 #else
0212 #define CONSCHECK(expect) do {} while (0)
0213 #endif
0214 
0215 /**************************** TYPE DEFINITIONS ****************************/
0216 
0217 /* internal description of a trivial page table */
0218 typedef struct Triv121PgTblRec_
0219 {
0220   APte base;
0221   unsigned long size;
0222   int active;
0223 } Triv121PgTblRec;
0224 
0225 
0226 /************************** FORWARD DECLARATIONS *************************/
0227 
0228 #ifdef DEBUG_EXC
0229 static void myhdl (BSP_Exception_frame * excPtr);
0230 #endif
0231 
0232 static void dumpPte (APte pte);
0233 
0234 #ifdef DEBUG
0235 static void
0236 dumpPteg (unsigned long vsid, unsigned long pi, unsigned long hash);
0237 #endif
0238 
0239 unsigned long
0240 triv121IsRangeMapped (long vsid, unsigned long start, unsigned long end);
0241 
0242 static void do_dssall (void);
0243 
0244 /**************************** STATIC VARIABLES ****************************/
0245 
0246 /* dont malloc - we might have to use this before
0247  * we have malloc or even RTEMS workspace available
0248  */
0249 static Triv121PgTblRec pgTbl = { 0 };
0250 
0251 #ifdef DEBUG_EXC
0252 static void *ohdl;              /* keep a pointer to the original handler */
0253 #endif
0254 
0255 /*********************** INLINES & PRIVATE ROUTINES ***********************/
0256 
0257 /* compute the page table entry group (PTEG) of a hash */
0258 static inline APte
0259 ptegOf (Triv121PgTbl pt, unsigned long hash)
0260 {
0261   hash &= ((1 << LD_HASH_SIZE) - 1);
0262   return (APte) (((unsigned long) pt->
0263                   base) | ((hash << LD_PTEG_SIZE) & (pt->size - 1)));
0264 }
0265 
0266 /* see if a vsid/pi combination is already mapped
0267  *
0268  * RETURNS: PTE of mapping / NULL if none exists
0269  *
0270  * NOTE: a vsid<0 is legal and will tell this
0271  *       routine that 'pi' is actually an EA to
0272  *       be split into vsid and pi...
0273  */
0274 static APte
0275 alreadyMapped (Triv121PgTbl pt, long vsid, unsigned long pi)
0276 {
0277   int i;
0278   unsigned long hash, api;
0279   APte pte;
0280 
0281   if (!pt->size)
0282     return 0;
0283 
0284   if (TRIV121_121_VSID == vsid) {
0285     vsid = VSID121 (pi);
0286     pi = PI121 (pi);
0287   } else if (TRIV121_SEG_VSID == vsid) {
0288     vsid = seg2vsid (pi);
0289     pi = PI121 (pi);
0290   }
0291 
0292   hash = PTE_HASH1 (vsid, pi);
0293   api = API (pi);
0294   for (i = 0, pte = ptegOf (pt, hash); i < PTE_PER_PTEG; i++, pte++)
0295     if (pte->v && pte->vsid == vsid && pte->api == api && 0 == pte->h)
0296       return pte;
0297   /* try the secondary hash table */
0298   hash = PTE_HASH2 (hash);
0299   for (i = 0, pte = ptegOf (pt, hash); i < PTE_PER_PTEG; i++, pte++)
0300     if (pte->v && pte->vsid == vsid && pte->api == api && 1 == pte->h)
0301       return pte;
0302   return 0;
0303 }
0304 
0305 /* find the first available slot for  vsid/pi
0306  *
0307  * NOTE: it is NOT legal to pass a vsid<0 / EA combination.
0308  *
0309  * RETURNS free slot with the 'marked' field set. The 'h'
0310  *         field is set to 0 or one, depending on whether
0311  *         the slot was allocated by using the primary or
0312  *         the secondary hash, respectively.
0313  */
0314 static APte
0315 slotFor (Triv121PgTbl pt, unsigned long vsid, unsigned long pi)
0316 {
0317   int i;
0318   unsigned long hash;
0319   APte pte;
0320 
0321   /* primary hash */
0322   hash = PTE_HASH1 (vsid, pi);
0323   /* linear search thru all buckets for this hash */
0324   for (i = 0, pte = ptegOf (pt, hash); i < PTE_PER_PTEG; i++, pte++) {
0325     if (!pte->v && !pte->marked) {
0326       /* found a free PTE; mark it as potentially used and return */
0327       pte->h = 0;               /* found by the primary hash fn */
0328       pte->marked = 1;
0329       return pte;
0330     }
0331   }
0332 
0333 #ifdef DEBUG
0334   /* Strange: if the hash table was allocated big enough,
0335    *          this should not happen (when using a 1:1 mapping)
0336    *          Give them some information...
0337    */
0338   PRINTF ("## First hash bucket full - ");
0339   dumpPteg (vsid, pi, hash);
0340 #endif
0341 
0342   hash = PTE_HASH2 (hash);
0343 #ifdef DEBUG
0344   PRINTF ("   Secondary pteg is 0x%08x\n", (unsigned) ptegOf (pt, hash));
0345 #endif
0346   for (i = 0, pte = ptegOf (pt, hash); i < PTE_PER_PTEG; i++, pte++) {
0347     if (!pte->v && !pte->marked) {
0348       /* mark this pte as potentially used */
0349       pte->marked = 1;
0350       pte->h = 1;
0351       return pte;
0352     }
0353   }
0354 #ifdef DEBUG
0355   /* Even more strange - most likely, something is REALLY messed up */
0356   PRINTF ("## Second hash bucket full - ");
0357   dumpPteg (vsid, pi, hash);
0358 #endif
0359   return 0;
0360 }
0361 
0362 /* unmark all entries */
0363 static void
0364 unmarkAll (Triv121PgTbl pt)
0365 {
0366   unsigned long n = pt->size / sizeof (PTERec);
0367   unsigned long i;
0368   APte pte;
0369   for (i = 0, pte = pt->base; i < n; i++, pte++)
0370     pte->marked = 0;
0371 
0372 }
0373 
0374 /* calculate the minimal size of a page/hash table
0375  * to map a range of 'size' bytes in EA space.
0376  *
0377  * RETURNS: size in 'number of bits', i.e. the
0378  *          integer part of LOGbase2(minsize)
0379  *          is returned.
0380  * NOTE:    G3/G4 machines need at least 16 bits
0381  *          (64k).
0382  */
0383 unsigned long
0384 triv121PgTblLdMinSize (unsigned long size)
0385 {
0386   unsigned long i;
0387   /* round 'size' up to the next page boundary */
0388   size += (1 << LD_PG_SIZE) - 1;
0389   size &= ~((1 << LD_PG_SIZE) - 1);
0390   /* divide by number of PTEs  and multiply
0391    * by the size of a PTE.
0392    */
0393   size >>= LD_PG_SIZE - LD_PTE_SIZE;
0394   /* find the next power of 2 >= size */
0395   for (i = 0; i < LD_PHYS_SIZE; i++) {
0396     if ((1 << i) >= size)
0397       break;
0398   }
0399   /* pop up to the allowed minimum, if necessary */
0400   if (i < LD_MIN_PT_SIZE)
0401     i = LD_MIN_PT_SIZE;
0402   return i;
0403 }
0404 
0405 /* initialize a trivial page table of 2^ldSize bytes
0406  * at 'base' in memory.
0407  *
0408  * RETURNS: OPAQUE HANDLE (not the hash table address)
0409  *          or NULL on failure.
0410  */
0411 Triv121PgTbl
0412 triv121PgTblInit (unsigned long base, unsigned ldSize)
0413 {
0414   if (pgTbl.size) {
0415     /* already initialized */
0416     return 0;
0417   }
0418 
0419   if (ldSize < LD_MIN_PT_SIZE)
0420     return 0;                   /* too small */
0421 
0422   if (base & ((1 << ldSize) - 1))
0423     return 0;                   /* misaligned */
0424 
0425   /* This was tested on 604r, 750 and 7400.
0426    * On other CPUs, verify that the TLB invalidation works
0427    * for a new CPU variant and that it has hardware PTE lookup/
0428    * TLB replacement before adding it to this list.
0429    *
0430    * NOTE: The 603 features no hardware PTE lookup - and
0431    *       hence the page tables should NOT be used.
0432    *               Although lookup could be implemented in
0433    *               software this is probably not desirable
0434    *               as it could have an impact on hard realtime
0435    *               performance, screwing deterministic latency!
0436    *               (Could still be useful for debugging, though)
0437    */
0438   if ( ! ppc_cpu_has_hw_ptbl_lkup() )
0439     return 0;                   /* unsupported by this CPU */
0440 
0441   pgTbl.base = (APte) base;
0442   pgTbl.size = 1 << ldSize;
0443   /* clear all page table entries */
0444   memset (pgTbl.base, 0, pgTbl.size);
0445 
0446   CONSCHECK (0);
0447 
0448   /* map the page table itself 'm' and 'readonly' */
0449   if (triv121PgTblMap (&pgTbl,
0450                        TRIV121_121_VSID,
0451                        base,
0452                        (pgTbl.size >> LD_PG_SIZE),
0453                        TRIV121_ATTR_M, TRIV121_PP_RO_PAGE) >= 0)
0454     return 0;
0455 
0456   CONSCHECK ((pgTbl.size >> LD_PG_SIZE));
0457 
0458   return &pgTbl;
0459 }
0460 
0461 /* return the handle of the (one and only) page table
0462  * or NULL if none has been initialized yet.
0463  */
0464 Triv121PgTbl
0465 triv121PgTblGet (void)
0466 {
0467   return pgTbl.size ? &pgTbl : 0;
0468 }
0469 
0470 /* NOTE: this routine returns -1 on success;
0471  *       on failure, the page table index for
0472  *       which no PTE could be allocated is returned
0473  *
0474  * (Consult header about argument/return value
0475  * description)
0476  */
0477 long
0478 triv121PgTblMap (Triv121PgTbl pt,
0479                  long ovsid,
0480                  unsigned long start,
0481                  unsigned long numPages,
0482                  unsigned attributes, unsigned protection)
0483 {
0484   int i, pass;
0485   unsigned long pi;
0486   APte pte;
0487   long vsid;
0488 #ifdef DEBUG
0489   long saved_vsid = ovsid;
0490 #endif
0491 
0492   if (TRIV121_121_VSID == ovsid) {
0493     /* use 1:1 mapping */
0494     ovsid = VSID121 (start);
0495   } else if (TRIV121_SEG_VSID == ovsid) {
0496     ovsid = seg2vsid (start);
0497   }
0498 
0499 #ifdef DEBUG
0500   PRINTF ("Mapping %i (0x%x) pages at 0x%08x for VSID 0x%08x\n",
0501           (unsigned) numPages, (unsigned) numPages,
0502           (unsigned) start, (unsigned) ovsid);
0503 #endif
0504 
0505   /* map in two passes. During the first pass, we try
0506    * to claim entries as needed. The 'slotFor()' routine
0507    * will 'mark' the claimed entries without 'valid'ating
0508    * them.
0509    * If the mapping fails, all claimed entries are unmarked
0510    * and we return the PI for which allocation failed.
0511    *
0512    * Once we know that the allocation would succeed, we
0513    * do a second pass; during the second pass, the PTE
0514    * is actually written.
0515    *
0516    */
0517   for (pass = 0; pass < 2; pass++) {
0518     /* check if we would succeed during the first pass */
0519     for (i = 0, pi = PI121 (start), vsid = ovsid; i < numPages; i++, pi++) {
0520       if (pi >= 1 << LD_PI_SIZE) {
0521         vsid++;
0522         pi = 0;
0523       }
0524       /* leave alone existing mappings for this EA */
0525       if (!alreadyMapped (pt, vsid, pi)) {
0526         if (!(pte = slotFor (pt, vsid, pi))) {
0527           /* no free slot found for page index 'pi' */
0528           unmarkAll (pt);
0529           return pi;
0530         } else {
0531           /* have a free slot; marked by slotFor() */
0532           if (pass) {
0533             /* second pass; do the real work */
0534             pte->vsid = vsid;
0535             /* H was set by slotFor() */
0536             pte->api = API (pi);
0537             /* set up 1:1 mapping */
0538             pte->rpn =
0539               ((((unsigned long) vsid) &
0540                 ((1 << (LD_PHYS_SIZE - LD_SEG_SIZE)) -
0541                  1)) << LD_PI_SIZE) | pi;
0542             pte->wimg = attributes & 0xf;
0543             pte->pp = protection & 0x3;
0544             /* mark it valid */
0545             pte->marked = 0;
0546             if (pt->active) {
0547               uint32_t flags;
0548               rtems_interrupt_disable (flags);
0549               /* order setting 'v' after writing everything else */
0550               __asm__ volatile ("eieio":::"memory");
0551               pte->v = 1;
0552               __asm__ volatile ("sync":::"memory");
0553               rtems_interrupt_enable (flags);
0554             } else {
0555               pte->v = 1;
0556             }
0557 
0558 #ifdef DEBUG
0559             /* add paranoia */
0560             assert (alreadyMapped (pt, vsid, pi) == pte);
0561 #endif
0562           }
0563         }
0564       }
0565     }
0566     unmarkAll (pt);
0567   }
0568 #ifdef DEBUG
0569   {
0570     unsigned long failedat;
0571     CONSCHECK (-1);
0572     /* double check that the requested range is mapped */
0573     failedat =
0574       triv121IsRangeMapped (saved_vsid, start,
0575                             start + (1 << LD_PG_SIZE) * numPages);
0576     if (0x0C0C != failedat) {
0577       PRINTF ("triv121 mapping failed at 0x%08x\n", (unsigned) failedat);
0578       return PI121 (failedat);
0579     }
0580   }
0581 #endif
0582   return TRIV121_MAP_SUCCESS;   /* -1 !! */
0583 }
0584 
0585 unsigned long
0586 triv121PgTblSDR1 (Triv121PgTbl pt)
0587 {
0588   return (((unsigned long) pt->base) & ~((1 << LD_MIN_PT_SIZE) - 1)) |
0589     (((pt->size - 1) >> LD_MIN_PT_SIZE) &
0590      ((1 << (LD_HASH_SIZE - (LD_MIN_PT_SIZE - LD_PTEG_SIZE))) - 1)
0591     );
0592 }
0593 
0594 void
0595 triv121PgTblActivate (Triv121PgTbl pt)
0596 {
0597 #ifndef DEBUG_MAIN
0598   unsigned long          sdr1 = triv121PgTblSDR1 (pt);
0599   register unsigned long tmp0 = 16; /* initial counter value (#segment regs) */
0600   register unsigned long tmp1 = (KEY_USR | KEY_SUP);
0601   register unsigned long tmp2 = (MSR_EE | MSR_IR | MSR_DR);
0602 #endif
0603   pt->active = 1;
0604 
0605 #ifndef DEBUG_MAIN
0606 #ifdef DEBUG_EXC
0607   /* install our exception handler */
0608   ohdl = globalExceptHdl;
0609   globalExceptHdl = myhdl;
0610   __asm__ __volatile__ ("sync"::"memory");
0611 #endif
0612 
0613   /* This section of assembly code takes care of the
0614    * following:
0615    * - get MSR and switch interrupts + MMU off
0616    *
0617    * - load up the segment registers with a
0618    *   1:1 effective <-> virtual mapping;
0619    *   give user & supervisor keys
0620    *
0621    * - flush all TLBs;
0622    *   NOTE: the TLB flushing code is probably
0623    *         CPU dependent!
0624    *
0625    * - setup SDR1
0626    *
0627    * - restore original MSR
0628    */
0629   __asm__ __volatile (
0630     "   .machine \"push\"\n"
0631     "   .machine \"any\"\n"
0632     "   mtctr   %[tmp0]\n"
0633     /* Get MSR and switch interrupts off - just in case.
0634      * Also switch the MMU off; the book
0635      * says that SDR1 must not be changed with either
0636      * MSR_IR or MSR_DR set. I would guess that it could
0637      * be safe as long as the IBAT & DBAT mappings override
0638      * the page table...
0639      */
0640     "   mfmsr   %[tmp0]\n"
0641     "   andc    %[tmp2], %[tmp0], %[tmp2]\n"
0642     "   mtmsr   %[tmp2]\n"
0643     "   isync   \n"
0644     /* set up the segment registers */
0645     "   li      %[tmp2], 0\n"
0646     "1: mtsrin  %[tmp1], %[tmp2]\n"
0647     "   addis   %[tmp2], %[tmp2], 0x1000\n" /* address next SR */
0648     "   addi    %[tmp1], %[tmp1], 1\n"      /* increment VSID  */
0649     "   bdnz    1b\n"
0650     /* Now flush all TLBs, starting with the topmost index */
0651     "   lis     %[tmp2], %[ea_range]@h\n"
0652     "2: addic.  %[tmp2], %[tmp2], -%[pg_sz]\n"    /* address the next one (decrementing) */
0653     "   tlbie   %[tmp2], 0\n"             /* invalidate & repeat */
0654     "   bgt     2b\n"
0655     "   eieio   \n"
0656     "   tlbsync \n"
0657     "   sync    \n"
0658     /* set up SDR1 */
0659     "   mtspr   %[sdr1], %[sdr1val]\n"
0660     /* restore original MSR  */
0661     "   mtmsr   %[tmp0]\n"
0662     "   isync   \n"
0663     "   .machine \"pop\"\n"
0664       :[tmp0]"+r&"(tmp0), [tmp1]"+b&"(tmp1), [tmp2]"+b&"(tmp2)
0665       :[ea_range]"i"(FLUSH_EA_RANGE), [pg_sz]"i" (1 << LD_PG_SIZE),
0666        [sdr1]"i"(SDR1), [sdr1val]"r" (sdr1)
0667       :"ctr", "cc", "memory"
0668   );
0669 
0670   /* At this point, BAT0 is probably still active; it's the
0671    * caller's job to deactivate it...
0672    */
0673 #endif
0674 }
0675 
0676 /**************************  DEBUGGING ROUTINES  *************************/
0677 
0678 /* Exception handler to catch page faults */
0679 #ifdef DEBUG_EXC
0680 
0681 #define BAT_VALID_BOTH  3       /* allow user + super access */
0682 
0683 static void
0684 myhdl (BSP_Exception_frame * excPtr)
0685 {
0686   if (3 == excPtr->_EXC_number) {
0687     unsigned long dsisr;
0688 
0689     /* reactivate DBAT0 and read DSISR */
0690     __asm__ __volatile__ (
0691       "mfspr %0, %1   \n"
0692       "ori   %0, %0, 3\n"
0693       "mtspr %1, %0   \n"
0694       "sync\n"
0695       "mfspr %0, %2\n"
0696         :"=&r" (dsisr)
0697         :"i" (DBAT0U), "i" (DSISR), "i" (BAT_VALID_BOTH)
0698     );
0699 
0700     printk ("Data Access Exception (DSI) # 3\n");
0701     printk ("Reactivated DBAT0 mapping\n");
0702 
0703 
0704     printk ("DSISR 0x%08x\n", dsisr);
0705 
0706     printk ("revectoring to prevent default handler panic().\n");
0707     printk ("NOTE: exception number %i below is BOGUS\n", ASM_DEC_VECTOR);
0708     /* make this exception 'recoverable' for
0709      * the default handler by faking a decrementer
0710      * exception.
0711      * Note that the default handler's message will be
0712      * wrong about the exception number.
0713      */
0714     excPtr->_EXC_number = ASM_DEC_VECTOR;
0715   }
0716 /* now call the original handler */
0717   ((void (*)()) ohdl) (excPtr);
0718 }
0719 #endif
0720 
0721 
0722 
0723 #ifdef DEBUG
0724 /* test the consistency of the page table
0725  *
0726  * 'pass' is merely a number which will be printed
0727  * by this routine, so the caller may give some
0728  * context information.
0729  *
0730  * 'expected' is the number of valid (plus 'marked')
0731  * entries the caller believes the page table should
0732  * have. This routine complains if its count differs.
0733  *
0734  * It basically verifies that the topmost 20bits
0735  * of all VSIDs as well as the unused bits are all
0736  * zero. Then it counts all valid and all 'marked'
0737  * entries, adding them up and comparing them to the
0738  * 'expected' number of occupied slots.
0739  *
0740  * RETURNS: total number of valid plus 'marked' slots.
0741  */
0742 static unsigned long
0743 triv121PgTblConsistency (Triv121PgTbl pt, int pass, int expected)
0744 {
0745   APte pte;
0746   int i;
0747   unsigned v, m;
0748   int warn = 0;
0749   int errs = 0;
0750   static int maxw = 20;         /* mute after detecting this many errors */
0751 
0752   PRINTF ("Checking page table at 0x%08x (size %i==0x%x)\n",
0753           (unsigned) pt->base, (unsigned) pt->size, (unsigned) pt->size);
0754 
0755   if (!pt->base || !pt->size) {
0756     PRINTF ("Uninitialized Page Table!\n");
0757     return 0;
0758   }
0759 
0760   v = m = 0;
0761 #if 1
0762   /* 10/9/2002: I had machine checks crashing after this loop
0763    *            terminated. Maybe caused by speculative loads
0764    *            from beyond the valid memory area (since the
0765    *            page hash table sits at the top of physical
0766    *            memory).
0767    *            Very bizarre - the other loops in this file
0768    *            seem to be fine. Maybe there is a compiler bug??
0769    *            For the moment, I let the loop run backwards...
0770    *
0771    *                        Also see the comment a couple of lines down.
0772    */
0773   for (i = pt->size / sizeof (PTERec) - 1, pte = pt->base + i; i >= 0;
0774        i--, pte--)
0775 #else
0776   for (i = 0, pte = pt->base; i < pt->size / sizeof (PTERec); i++, pte++)
0777 #endif
0778   {
0779     int err = 0;
0780     char buf[500];
0781     unsigned long *lp = (unsigned long *) pte;
0782 #if 0
0783     /* If I put this bogus while statement here (the body is
0784      * never reached), the original loop works OK
0785      */
0786     while (pte >= pt->base + pt->size / sizeof (PTERec))
0787       /* never reached */ ;
0788 #endif
0789 
0790     if ( /* T.S: allow any VSID... (*lp & (0xfffff0 << 7)) || */ (*(lp + 1) & 0xe00)
0791         || (pte->v && pte->marked)) {
0792       /* check for vsid (without segment bits) == 0, unused bits == 0, valid && marked */
0793       sprintf (buf, "unused bits or v && m");
0794       err = 1;
0795     } else {
0796       if ( (*lp & (0xfffff0 << 7)) ) {
0797         sprintf(buf,"(warning) non-1:1 VSID found");
0798         err = 2;
0799       }
0800       if (pte->v)
0801         v++;
0802       if (pte->marked)
0803         m++;
0804     }
0805     if (err && maxw) {
0806       PRINTF
0807         ("Pass %i -- strange PTE at 0x%08x found for page index %i == 0x%08x:\n",
0808          pass, (unsigned) pte, i, i);
0809       PRINTF ("Reason: %s\n", buf);
0810       dumpPte (pte);
0811       if ( err & 2 ) {
0812          warn++;
0813       } else {
0814          errs++;
0815       }
0816       maxw--;
0817     }
0818   }
0819   if (errs) {
0820     PRINTF ("%i errors %s", errs, warn ? "and ":"");
0821   }
0822   if (warn) {
0823     PRINTF ("%i warnings ",warn);
0824   }
0825   if (errs || warn) {
0826     PRINTF ("found; currently %i entries marked, %i are valid\n",
0827             m, v);
0828   }
0829   v += m;
0830   if (maxw && expected >= 0 && expected != v) {
0831     /* number of occupied slots not what they expected */
0832     PRINTF ("Wrong # of occupied slots detected during pass");
0833     PRINTF ("%i; should be %i (0x%x) is %i (0x%x)\n",
0834             pass, expected, (unsigned) expected, v, (unsigned) v);
0835     maxw--;
0836   }
0837   return v;
0838 }
0839 #endif
0840 
0841 /* Find the PTE for a EA and print its contents
0842  * RETURNS: pte for EA or NULL if no entry was found.
0843  */
0844 APte
0845 triv121DumpEa (unsigned long ea)
0846 {
0847   APte pte;
0848 
0849   pte =
0850     alreadyMapped (&pgTbl, pgTbl.active ? TRIV121_SEG_VSID : TRIV121_121_VSID,
0851                    ea);
0852 
0853   if (pte)
0854     dumpPte (pte);
0855   return pte;
0856 }
0857 
0858 APte
0859 triv121FindPte (unsigned long vsid, unsigned long pi)
0860 {
0861   return alreadyMapped (&pgTbl, vsid, pi);
0862 }
0863 
0864 APte
0865 triv121UnmapEa (unsigned long ea)
0866 {
0867   uint32_t flags;
0868   APte pte;
0869 
0870   if (!pgTbl.active) {
0871     pte = alreadyMapped (&pgTbl, TRIV121_121_VSID, ea);
0872     if (pte)                    /* alreadyMapped checks for pte->v */
0873       pte->v = 0;
0874     return pte;
0875   }
0876 
0877   pte = alreadyMapped (&pgTbl, TRIV121_SEG_VSID, ea);
0878 
0879   if (!pte)
0880     return 0;
0881 
0882   rtems_interrupt_disable (flags);
0883   pte->v = 0;
0884   do_dssall ();
0885   __asm__ volatile ("   sync        \n\t"
0886                 "   tlbie %0, 0 \n\t"
0887                 "   eieio       \n\t"
0888                 "   tlbsync     \n\t"
0889                 "   sync        \n\t"::"r" (ea):"memory");
0890   rtems_interrupt_enable (flags);
0891   return pte;
0892 }
0893 
0894 /* A context synchronizing jump */
0895 #define SYNC_LONGJMP(msr)               \
0896     asm volatile(                       \
0897         "   mtsrr1  %0          \n\t"   \
0898         "   bl      1f          \n\t"   \
0899         "1: mflr    3           \n\t"   \
0900         "   addi    3,3,1f-1b   \n\t"   \
0901         "   mtsrr0  3           \n\t"   \
0902         "   rfi                 \n\t"   \
0903         "1:                     \n\t"   \
0904         :                               \
0905         :"r"(msr)                       \
0906         :"3","lr","memory")
0907 
0908 /* The book doesn't mention dssall when changing PTEs
0909  * but they require it for BAT changes and I guess
0910  * it makes sense in the case of PTEs as well.
0911  * Just do it to be on the safe side...
0912  */
0913 static void
0914 do_dssall (void)
0915 {
0916   /* Before changing BATs, 'dssall' must be issued.
0917    * We check MSR for MSR_VE and issue a 'dssall' if
0918    * MSR_VE is set hoping that
0919    *  a) on non-altivec CPUs MSR_VE reads as zero
0920    *  b) all altivec CPUs use the same bit
0921    *
0922    * NOTE: psim doesn't implement dssall so we skip if we run on psim
0923    */
0924   if ( (_read_MSR () & MSR_VE) && PPC_PSIM != get_ppc_cpu_type() ) {
0925     /* this construct is needed because we don't know
0926      * if this file is compiled with -maltivec.
0927      * (I plan to add altivec support outside of
0928      * RTEMS core and hence I'd rather not
0929      * rely on consistent compiler flags).
0930      */
0931 #define DSSALL  0x7e00066c      /* dssall opcode */
0932     __asm__ volatile (" .long %0"::"i" (DSSALL));
0933 #undef  DSSALL
0934   }
0935 }
0936 
0937 APte
0938 triv121ChangeEaAttributes (unsigned long ea, int wimg, int pp)
0939 {
0940   APte pte;
0941   unsigned long msr;
0942 
0943   if (!pgTbl.active) {
0944     pte = alreadyMapped (&pgTbl, TRIV121_121_VSID, ea);
0945     if (!pte)
0946       return 0;
0947     if (wimg > 0)
0948       pte->wimg = wimg;
0949     if (pp > 0)
0950       pte->pp = pp;
0951     return pte;
0952   }
0953 
0954   pte = alreadyMapped (&pgTbl, TRIV121_SEG_VSID, ea);
0955 
0956   if (!pte)
0957     return 0;
0958 
0959   if (wimg < 0 && pp < 0)
0960     return pte;
0961 
0962   __asm__ volatile ("mfmsr %0":"=r" (msr));
0963 
0964   /* switch MMU and IRQs off */
0965   SYNC_LONGJMP (msr & ~(MSR_EE | MSR_DR | MSR_IR));
0966 
0967   pte->v = 0;
0968   do_dssall ();
0969   __asm__ volatile ("sync":::"memory");
0970   if (wimg >= 0)
0971     pte->wimg = wimg;
0972   if (pp >= 0)
0973     pte->pp = pp;
0974   __asm__ volatile ("tlbie %0, 0; eieio"::"r" (ea):"memory");
0975   pte->v = 1;
0976   __asm__ volatile ("tlbsync; sync":::"memory");
0977 
0978   /* restore, i.e., switch MMU and IRQs back on */
0979   SYNC_LONGJMP (msr);
0980 
0981   return pte;
0982 }
0983 
0984 static void
0985 pgtblChangePP (Triv121PgTbl pt, int pp)
0986 {
0987   unsigned long n = pt->size >> LD_PG_SIZE;
0988   unsigned long b, i;
0989 
0990   for (i = 0, b = (unsigned long) pt->base; i < n;
0991        i++, b += (1 << LD_PG_SIZE)) {
0992     triv121ChangeEaAttributes (b, -1, pp);
0993   }
0994 }
0995 
0996 void
0997 triv121MakePgTblRW ()
0998 {
0999   pgtblChangePP (&pgTbl, TRIV121_PP_RW_PAGE);
1000 }
1001 
1002 void
1003 triv121MakePgTblRO ()
1004 {
1005   pgtblChangePP (&pgTbl, TRIV121_PP_RO_PAGE);
1006 }
1007 
1008 long
1009 triv121DumpPte (APte pte)
1010 {
1011   if (pte)
1012     dumpPte (pte);
1013   return 0;
1014 }
1015 
1016 
1017 #ifdef DEBUG
1018 /* Dump an entire PTEG */
1019 
1020 static void
1021 dumpPteg (unsigned long vsid, unsigned long pi, unsigned long hash)
1022 {
1023   APte pte = ptegOf (&pgTbl, hash);
1024   int i;
1025   PRINTF ("hash 0x%08x, pteg 0x%08x (vsid 0x%08x, pi 0x%08x)\n",
1026           (unsigned) hash, (unsigned) pte, (unsigned) vsid, (unsigned) pi);
1027   for (i = 0; i < PTE_PER_PTEG; i++, pte++) {
1028     PRINTF ("pte 0x%08x is 0x%08x : 0x%08x\n",
1029             (unsigned) pte,
1030             (unsigned) *(unsigned long *) pte,
1031             (unsigned) *(((unsigned long *) pte) + 1));
1032   }
1033 }
1034 #endif
1035 
1036 /* Verify that a range of addresses is mapped the page table.
1037  * start/end are segment offsets or EAs (if vsid has one of
1038  * the special values), respectively.
1039  *
1040  * RETURNS: address of the first page for which no
1041  *          PTE was found (i.e. page index * page size)
1042  *
1043  *          ON SUCCESS, the special value 0x0C0C ("OKOK")
1044  *          [which is not page aligned and hence is not
1045  *          a valid page address].
1046  */
1047 
1048 unsigned long
1049 triv121IsRangeMapped (long vsid, unsigned long start, unsigned long end)
1050 {
1051 unsigned pi;
1052 
1053   start &= ~((1 << LD_PG_SIZE) - 1);
1054   while (start < end) {
1055     if ( TRIV121_SEG_VSID != vsid && TRIV121_121_VSID != vsid )
1056       pi = PI121(start);
1057     else
1058       pi = start;
1059     if (!alreadyMapped (&pgTbl, vsid, pi))
1060       return start;
1061     start += 1 << LD_PG_SIZE;
1062   }
1063   return 0x0C0C;                /* OKOK - not on a page boundary */
1064 }
1065 
1066 
1067 #include <stdlib.h>
1068 
1069 /* print a PTE */
1070 static void
1071 dumpPte (APte pte)
1072 {
1073   if (0 == ((unsigned long) pte & ((1 << LD_PTEG_SIZE) - 1)))
1074     PRINTF ("PTEG--");
1075   else
1076     PRINTF ("......");
1077   if (pte->v) {
1078     PRINTF ("VSID: 0x%08x H:%1i API: 0x%02x\n", pte->vsid, pte->h, pte->api);
1079     PRINTF ("      ");
1080     PRINTF ("RPN:  0x%08x WIMG: 0x%1x, (m %1i), pp: 0x%1x\n",
1081             pte->rpn, pte->wimg, pte->marked, pte->pp);
1082   } else {
1083     PRINTF ("xxxxxx\n");
1084     PRINTF ("      ");
1085     PRINTF ("xxxxxx\n");
1086   }
1087 }
1088 
1089 
1090 #if defined(DEBUG_MAIN)
1091 /* dump page table entries from index 'from' to 'to'
1092  * The special values (unsigned)-1 are allowed which
1093  * cause the routine to dump the entire table.
1094  *
1095  * RETURNS 0
1096  */
1097 int
1098 triv121PgTblDump (Triv121PgTbl pt, unsigned from, unsigned to)
1099 {
1100   int i;
1101   APte pte;
1102   PRINTF ("Dumping PT [size 0x%08x == %i] at 0x%08x\n",
1103           (unsigned) pt->size, (unsigned) pt->size, (unsigned) pt->base);
1104   if (from > pt->size >> LD_PTE_SIZE)
1105     from = 0;
1106   if (to > pt->size >> LD_PTE_SIZE)
1107     to = (pt->size >> LD_PTE_SIZE);
1108   for (i = from, pte = pt->base + from; i < (long) to; i++, pte++) {
1109     dumpPte (pte);
1110   }
1111   return 0;
1112 }
1113 
1114 
1115 
1116 #define LD_DBG_PT_SIZE  LD_MIN_PT_SIZE
1117 
1118 int
1119 main (int argc, char **argv)
1120 {
1121   unsigned long base, start, numPages;
1122   unsigned long size = 1 << LD_DBG_PT_SIZE;
1123   Triv121PgTbl pt;
1124 
1125   base = (unsigned long) malloc (size << 1);
1126 
1127   assert (base);
1128 
1129   /* align pt */
1130   base += size - 1;
1131   base &= ~(size - 1);
1132 
1133   assert (pt = triv121PgTblInit (base, LD_DBG_PT_SIZE));
1134 
1135   triv121PgTblDump (pt, (unsigned) -1, (unsigned) -1);
1136   do {
1137     do {
1138       PRINTF ("Start Address:");
1139       fflush (stdout);
1140     } while (1 != scanf ("%i", &start));
1141     do {
1142       PRINTF ("# pages:");
1143       fflush (stdout);
1144     } while (1 != scanf ("%i", &numPages));
1145   } while (TRIV121_MAP_SUCCESS ==
1146            triv121PgTblMap (pt, TRIV121_121_VSID, start, numPages,
1147                             TRIV121_ATTR_IO_PAGE, 2)
1148            && 0 == triv121PgTblDump (pt, (unsigned) -1, (unsigned) -1));
1149 }
1150 #endif