Back to home page

LXR

 
 

    


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

0001 /**
0002  * @file
0003  *
0004  * @ingroup RTEMSBSPsPowerPCVirtex4MMU
0005  *
0006  * @brief Implementation of routines to manipulate the PPC 405 mmu.
0007  *
0008  *        Since this is a real-time OS we want to stay away from
0009  *        software TLB replacement.
0010  */
0011 /*
0012  * Authorship
0013  * ----------
0014  * This software was created by
0015  *     Till Straumann <strauman@slac.stanford.edu>, 2005-2007,
0016  *     Stanford Linear Accelerator Center, Stanford University.
0017  * and was transcribed for the PPC 405 by
0018  *     R. Claus <claus@slac.stanford.edu>, 2012,
0019  *       Stanford Linear Accelerator Center, Stanford University,
0020  *
0021  * Acknowledgement of sponsorship
0022  * ------------------------------
0023  * This software was produced by
0024  *     the Stanford Linear Accelerator Center, Stanford University,
0025  *     under Contract DE-AC03-76SFO0515 with the Department of Energy.
0026  *
0027  * Government disclaimer of liability
0028  * ----------------------------------
0029  * Neither the United States nor the United States Department of Energy,
0030  * nor any of their employees, makes any warranty, express or implied, or
0031  * assumes any legal liability or responsibility for the accuracy,
0032  * completeness, or usefulness of any data, apparatus, product, or process
0033  * disclosed, or represents that its use would not infringe privately owned
0034  * rights.
0035  *
0036  * Stanford disclaimer of liability
0037  * --------------------------------
0038  * Stanford University makes no representations or warranties, express or
0039  * implied, nor assumes any liability for the use of this software.
0040  *
0041  * Stanford disclaimer of copyright
0042  * --------------------------------
0043  * Stanford University, owner of the copyright, hereby disclaims its
0044  * copyright and all other rights in this software.  Hence, anyone may
0045  * freely use it for any purpose without restriction.
0046  *
0047  * Maintenance of notices
0048  * ----------------------
0049  * In the interest of clarity regarding the origin and status of this
0050  * SLAC software, this and all the preceding Stanford University notices
0051  * are to remain affixed to any copy or derivative of this software made
0052  * or distributed by the recipient and are to be affixed to any copy of
0053  * software made or distributed by the recipient that contains a copy or
0054  * derivative of this software.
0055  *
0056  * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
0057  */
0058 
0059 /* 405 MSR definitions; note that there are *substantial* differences
0060  * compared to classic powerpc; in particular, IS/DS are *different*
0061  * from IR/DR.
0062  *
0063  * Also: To disable/enable all external interrupts, CE and EE must both be
0064  *       controlled.
0065  */
0066 #include <rtems.h>
0067 #include <rtems/bspIo.h>
0068 #include <rtems/powerpc/powerpc.h>
0069 #include <rtems/score/sysstate.h>
0070 #include <inttypes.h>
0071 #include <stdio.h>
0072 
0073 #include <bsp/mmu.h>
0074 
0075 
0076 #ifdef DEBUG
0077 #define STATIC
0078 #else
0079 #define STATIC static
0080 #endif
0081 
0082 
0083 bsp_tlb_entry_t* bsp_mmu_cache = 0;
0084 
0085 
0086 /* Since it is likely that these routines are used during
0087  * early initialization when stdio is not available yet
0088  * we provide a helper that resorts to 'printk()'
0089  */
0090 static void
0091 myprintf(FILE *f, char *fmt, ...)
0092 {
0093   va_list ap;
0094   va_start(ap, fmt);
0095 
0096   if (!f || !_System_state_Is_up(_System_state_Get())) {
0097     /* Might be called at an early stage when stdio is not yet initialized. */
0098     vprintk(fmt,ap);
0099   } else {
0100     vfprintf(f,fmt,ap);
0101   }
0102   va_end(ap);
0103 }
0104 
0105 
0106 void
0107 bsp_mmu_dump_cache(FILE *f)
0108 {
0109   bsp_tlb_idx_t idx;
0110   if ( !bsp_mmu_cache ) {
0111     myprintf(stderr,"MMU TLB cache not initialized\n");
0112     return;
0113   }
0114   for ( idx=0; idx<NTLBS; idx++ ) {
0115     bsp_tlb_entry_t *tlb = bsp_mmu_cache + idx;
0116     if ( !tlb->hi.v )
0117       continue;
0118     myprintf(f, "#%2i: EA 0x%08x .. 0x%08x, TID  0x%03x, EU0  0x%01x\n",
0119              idx,
0120              tlb->hi.epn << 10,
0121              (tlb->hi.epn << 10) + (1024<<(2*tlb->hi.size))-1,
0122              tlb->id.tid,
0123              tlb->hi.att);
0124     myprintf(f, "     PA 0x%08"PRIx32" .. 0x%08"PRIx32", PERM 0x%03x, WIMG 0x%02x\n",
0125              tlb->lo.rpn << 10,
0126              (tlb->lo.rpn << 10) + (1024<<(2*tlb->hi.size))-1,
0127              tlb->lo.perm,
0128              tlb->lo.wimg);
0129   }
0130 }
0131 
0132 static void
0133 fetch(bsp_tlb_idx_t key, bsp_tlb_entry_t* tlb)
0134 {
0135   register uint32_t tmp;
0136   __asm__ volatile (".machine \"push\"        \n\t"
0137                     ".machine \"any\"         \n\t"
0138                     "mfpid   %[tmp]           \n\t"
0139                     "stw     %[tmp],0(%[tlb]) \n\t"
0140                     "tlbrehi %[tmp],%[key]    \n\t"
0141                     "stw     %[tmp],4(%[tlb]) \n\t"
0142                     "tlbrelo %[tmp],%[key]    \n\t"
0143                     "stw     %[tmp],8(%[tlb]) \n\t"
0144                     "sync                     \n\t"
0145                     ".machine \"pop\"         \n\t"
0146                     : [tmp]"=&r"(tmp)
0147                     : [key]"r"(key),
0148                       [tlb]"b"(tlb)
0149                     );
0150 }
0151 
0152 
0153 static void
0154 store(bsp_tlb_idx_t key, bsp_tlb_entry_t* tlb)
0155 {
0156   register uint32_t tmp;
0157   __asm__ volatile (".machine \"push\"        \n\t"
0158                     ".machine \"any\"         \n\t"
0159                     "lwz     %[tmp],0(%[tlb]) \n\t"
0160                     "mtpid   %[tmp]           \n\t"
0161                     "lwz     %[tmp],4(%[tlb]) \n\t"
0162                     "tlbwehi %[tmp],%[key]    \n\t"
0163                     "lwz     %[tmp],8(%[tlb]) \n\t"
0164                     "tlbwelo %[tmp],%[key]    \n\t"
0165                     ".machine \"pop\"         \n\t"
0166                     : [tmp]"=&r"(tmp)
0167                     : [tlb]"b"(tlb),
0168                       [key]"r"(key)
0169                     );
0170 }
0171 
0172 
0173 static void
0174 commit(void)
0175 {
0176   __asm__ volatile("isync           \n\t");
0177 }
0178 
0179 
0180 /*
0181  * Read a TLB entry from the hardware and store the current settings in the
0182  * bsp_mmu_cache[] structure.
0183  *
0184  * The routine can perform this operation quietly or
0185  * print information to a file.
0186  *
0187  *   'idx': which TLB entry to access.
0188  * 'quiet': perform operation silently (no info printed)
0189  *          if nonzero.
0190  *     'f': open FILE where to print information. May be
0191  *          NULL in which case 'stdout' is used.
0192  *
0193  * RETURNS:
0194  *       0: success; TLB entry is VALID
0195  *      +1: success but TLB entry is INVALID
0196  *     < 0: error (-1: invalid argument)
0197  *                (-2: driver not initialized)
0198  */
0199 int
0200 bsp_mmu_update(bsp_tlb_idx_t key, bool quiet, FILE *f)
0201 {
0202   rtems_interrupt_level  lvl;
0203   bsp_tlb_entry_t*       tlb;
0204   bsp_tlb_idx_t          idx;
0205 
0206   idx = key;
0207 
0208   if ( idx < 0 || idx > NTLBS-1 )
0209     return -1;
0210 
0211   if (!bsp_mmu_cache)
0212     return -2;
0213 
0214   tlb = bsp_mmu_cache + idx;
0215 
0216   rtems_interrupt_disable(lvl);
0217 
0218   fetch(idx, tlb);
0219 
0220   rtems_interrupt_enable(lvl);
0221 
0222   if ( tlb->hi.v ) {
0223     if ( !quiet ) {
0224 /*
0225                "TLB Entry #  0 spans EA range     0x00000000 - 0x00000000
0226                "Mapping:     VA     [TID 0x00 / EPN 0x00000] -> RPN 0x00000"
0227                "Size:        TSIZE 0x0  (4^sz KB = 000000 KB = 0x00000000 B)
0228                "Attributes:  PERM  0x00 (ex/wr/zsel) WIMG 0x00 EU0 0x0"
0229 */
0230       myprintf(f,
0231                "TLB Entry # %2d spans EA range     0x%08x - 0x%08x\n",
0232                idx,
0233                (tlb->hi.epn << 10),
0234                (tlb->hi.epn << 10) + (1024<<(2*tlb->hi.size)) - 1
0235                );
0236 
0237       myprintf(f,
0238                "Mapping:     VA     [TID 0x%02x / EPN 0x%05x] -> RPN 0x%05"PRIx32"\n",
0239                tlb->id.tid, tlb->hi.epn, tlb->lo.rpn
0240                );
0241       myprintf(f,
0242                "Size:        TSIZE 0x%x  (4^sz KB = %6d KB = 0x%08x B)\n",
0243                tlb->hi.size, (1<<(2*tlb->hi.size)), (1024<<(2*tlb->hi.size))
0244                );
0245       myprintf(f,
0246                "Attributes:  PERM  0x%02x (ex/wr/zsel) WIMG 0x%02x EU0 0x%01x\n",
0247                tlb->lo.perm, tlb->lo.wimg, tlb->hi.att
0248                );
0249     }
0250   } else {
0251     if ( !quiet ) {
0252       myprintf(f,
0253                "TLB Entry # %2d <OFF> (size 0x%x = 0x%xb)\n",
0254                idx, tlb->hi.size, (1024<<(2*tlb->hi.size))
0255                );
0256     }
0257     return 1;
0258   }
0259   return 0;
0260 }
0261 
0262 /* Initialize cache.  Should be done only once although this is not enforced.
0263  *
0264  * RETURNS: zero on success, nonzero on error; in this case the driver will
0265  *          refuse to change TLB entries (other than disabling them).
0266  */
0267 int
0268 bsp_mmu_initialize()
0269 {
0270   static bsp_tlb_entry_t mmu_cache[NTLBS];
0271   bsp_tlb_entry_t*       tlb = mmu_cache;  /* Should malloc if it's not too early */
0272   rtems_interrupt_level  lvl;
0273 
0274   bsp_tlb_idx_t idx;
0275   rtems_interrupt_disable(lvl);
0276   for (idx=0; idx<NTLBS; tlb++, idx++)
0277   {
0278     fetch(idx, tlb);
0279   }
0280   rtems_interrupt_enable(lvl);
0281 
0282   bsp_mmu_cache = mmu_cache;
0283   return 0;
0284 }
0285 
0286 /* Find first free TLB entry by examining all entries' valid bit.  The first
0287  * entry without the valid bit set is returned.
0288  *
0289  * RETURNS: A free TLB entry number.  -1 if no entry can be found.
0290  */
0291 bsp_tlb_idx_t
0292 bsp_mmu_find_first_free()
0293 {
0294   bsp_tlb_idx_t   idx;
0295   bsp_tlb_entry_t entry;
0296 
0297   for (idx=0; idx<NTLBS; idx++) {
0298     register uint32_t tmp;
0299     __asm__ volatile (".machine \"push\"        \n\t"
0300                       ".machine \"any\"         \n\t"
0301                       "tlbrehi %[tmp],%[idx]    \n\t"
0302                       "stw     %[tmp],4(%[tlb]) \n\t" /* entry.hi */
0303                       "sync                     \n\t"
0304                       ".machine \"pop\"         \n\t"
0305                       : [tmp]"=&r"(tmp)
0306                       : [idx]"r"(idx),
0307                         [tlb]"b"(&entry)
0308                       : "memory"
0309                       );
0310     if (!(entry.hi.v))
0311       break;
0312   }
0313   return (idx < NTLBS) ? idx : -1;
0314 }
0315 
0316 /*
0317  * Write TLB entry (can also be used to disable an entry).
0318  *
0319  * The routine checks against the cached data in
0320  * bsp_mmu_cache[] to prevent the user from generating
0321  * overlapping entries.
0322  *
0323  *   'idx': TLB entry # to manipulate
0324  *    'ea': Effective address (must be page aligned)
0325  *    'pa': Physical  address (must be page aligned)
0326  *    'sz': Page size selector; page size is
0327  *          1024 * 2^(2*sz) bytes.
0328  *          'sz' may also be one of the following:
0329  *          - page size in bytes ( >= 1024 ); the selector
0330  *            value is then computed by this routine.
0331  *            However, 'sz' must be a valid page size
0332  *            or -1 will be returned.
0333  *          - a value < 0 to invalidate/disable the
0334  *            TLB entry.
0335  *  'flgs': Page's little-endian & user-defined flags, permissions and attributes
0336  *   'tid': Translation ID
0337  *
0338  * RETURNS: 0 on success, nonzero on error:
0339  *
0340  *         >0: requested mapping would overlap with
0341  *             existing mapping in other entry. Return
0342  *             value gives conflicting entry + 1; i.e.,
0343  *             if a value of 4 is returned then the request
0344  *             conflicts with existing mapping in entry 3.
0345  *         -1: invalid argument
0346  *         -3: driver not initialized (or initialization failed).
0347  *         <0: other error
0348  */
0349 bsp_tlb_idx_t
0350 bsp_mmu_write(bsp_tlb_idx_t idx, uint32_t ea, uint32_t pa, uint sz,
0351               uint32_t flgs, uint32_t tid)
0352 {
0353   bsp_tlb_entry_t       tlb;
0354   uint32_t              msk;
0355   bsp_tlb_idx_t         lkup;
0356   rtems_interrupt_level lvl;
0357 
0358   if ( sz >= 1024 ) {
0359     /* Assume they literally specify a size */
0360     msk = sz;
0361     sz  = 0;
0362     while ( msk != (1024u<<(2*sz)) ) {
0363       if ( ++sz > 7 ) {
0364         return -1;
0365       }
0366     }
0367     /* OK, acceptable */
0368   }
0369 
0370   msk = sz > 0 ? (1024u<<(2*sz)) - 1 : 0;
0371 
0372   if ( !bsp_mmu_cache && sz > 0 ) {
0373     myprintf(stderr,"MMU driver not initialized; refusing to enable any entry\n");
0374     return -3;
0375   }
0376 
0377   if ( (ea & msk) || (pa & msk) ) {
0378     myprintf(stderr,"Misaligned EA (%08x) or PA (%08x) (mask is %08x)\n", ea, pa, msk);
0379     return -1;
0380   }
0381 
0382   if ( idx < 0 || idx > NTLBS-1 )
0383     return -1;
0384 
0385   if ( sz > 7 ) {
0386     myprintf(stderr,"Invalid size %u = %08x = %u KB\n", sz, 1024u<<(2*sz), (1024u<<(2*sz))/1024);
0387     return -1;
0388   }
0389 
0390   if ( sz >=0 ) {
0391     lkup = bsp_mmu_match(ea, sz, tid);
0392 
0393     if ( lkup < -1 ) {
0394       /* some error */
0395       return lkup;
0396     }
0397     if ( (lkup >= 0) && (lkup != idx) && (bsp_mmu_cache[lkup].hi.v != 0) ) {
0398       myprintf(stderr,"TLB #%i overlaps with requested mapping\n", lkup);
0399       bsp_mmu_update( lkup, false, stderr);
0400       return lkup+1;
0401     }
0402   }
0403 
0404   /* OK to proceed */
0405   tlb.id.tid  = tid;
0406   tlb.hi.v    = sz >= 0;
0407   tlb.hi.size = sz;
0408   tlb.hi.epn  = (ea & (0xfffffc00 << (sz + sz))) >> 10;
0409   tlb.lo.rpn  = (pa & (0xfffffc00 << (sz + sz))) >> 10;
0410   tlb.hi.att  = (flgs & MMU_M_ATTR) >> MMU_V_ATTR;
0411   tlb.lo.perm = (flgs & MMU_M_PERM) >> MMU_V_PERM;
0412   tlb.lo.wimg = (flgs & MMU_M_PROP) >> MMU_V_PROP;
0413 
0414   rtems_interrupt_disable(lvl);
0415 
0416   store(idx, &tlb);
0417 
0418   commit();
0419 
0420   rtems_interrupt_enable(lvl);
0421 
0422   /* update cache */
0423   bsp_mmu_update(idx, true, 0);
0424 
0425   return 0;
0426 }
0427 
0428 /*
0429  * Check if a ea/sz/tid mapping overlaps with an existing entry.
0430  *
0431  *    'ea': The Effective Address to match against
0432  *    'sz': The 'logarithmic' size selector; the page size
0433  *          is 1024*2^(2*sz).
0434  *   'tid': The TID to match against
0435  *
0436  * RETURNS:
0437  *     >= 0: index of the TLB entry that already provides a mapping
0438  *           which overlaps within the ea range.
0439  *       -1: SUCCESS (no conflicting entry found)
0440  *     <=-2: ERROR (invalid input)
0441  */
0442 bsp_tlb_idx_t
0443 bsp_mmu_match(uint32_t ea, int sz, uint32_t tid)
0444 {
0445   bsp_tlb_idx_t    idx;
0446   uint32_t         m,a;
0447   bsp_tlb_entry_t* tlb;
0448 
0449   if ( sz < 0 || sz > 7 )
0450     return -4;
0451 
0452   sz = (1024<<(2*sz));
0453 
0454   if ( !bsp_mmu_cache ) {
0455     /* cache not initialized */
0456     return -3;
0457   }
0458 
0459   if ( ea & (sz-1) ) {
0460     /* misaligned ea */
0461     return -2;
0462   }
0463 
0464   for ( idx=0, tlb=bsp_mmu_cache; idx<NTLBS; idx++, tlb++ ) {
0465     if ( ! tlb->hi.v )
0466       continue;
0467     if ( tlb->id.tid && tlb->id.tid != tid )
0468       continue;
0469     /* TID matches a valid entry */
0470     m  = (1024<<(2*tlb->hi.size)) - 1;
0471     /* calculate starting address of this entry */
0472     a  = tlb->hi.epn << 10;
0473     if ( ea <= a + m && ea + sz -1 >= a ) {
0474       /* overlap */
0475       return idx;
0476     }
0477   }
0478   return -1;
0479 }
0480 
0481 /* Find TLB index that maps 'ea/tid' combination
0482  *
0483  *    'ea': Effective address to match against
0484  *   'tid': The TID to match against
0485  *
0486  * RETURNS: index 'key' which indicates whether
0487  *          the mapping was found.
0488  *
0489  *          On error (no mapping) -1 is returned.
0490  */
0491 bsp_tlb_idx_t
0492 bsp_mmu_find(uint32_t ea, uint32_t tid)
0493 {
0494   rtems_interrupt_level  lvl;
0495   register uint32_t      pid;
0496   register bsp_tlb_idx_t idx;
0497   register int           failure;
0498 
0499   rtems_interrupt_disable(lvl);
0500 
0501   __asm__ volatile (".machine \"push\"\n\t"
0502                     ".machine \"any\"\n\t"
0503                     "mfpid  %[pid]         \n\t" /* Save PID */
0504                     "mtpid  %[tid]         \n\t"
0505                     "tlbsx. %[idx],0,%[ea] \n\t" /* Failure changes the index reg randomly. */
0506                     "mfcr   %[failure]     \n\t"
0507                     "mtpid  %[pid]         \n\t" /* Restore PID */
0508                     ".machine \"pop\"\n\t"
0509                     : [pid]"=r"(pid),
0510                       [idx]"=&r"(idx),
0511                       [failure]"=&r"(failure)
0512                     : [tid]"r"(tid),
0513                       [ea]"r"(ea)
0514                     : "cc"
0515                     );
0516 
0517   rtems_interrupt_enable(lvl);
0518 
0519   return (failure & 0x20000000) ? idx : -1;
0520 }
0521 
0522 /* Mark TLB entry as invalid ('disabled').
0523  *
0524  * 'key': TLB entry (index).
0525  *
0526  * RETURNS: zero on success, nonzero on error (TLB unchanged).
0527  *
0528  * NOTE:  If a TLB entry is disabled the associated
0529  *        entry in bsp_mmu_cache[] is also
0530  *        marked as disabled.
0531  */
0532 int
0533 bsp_mmu_invalidate(bsp_tlb_idx_t key)
0534 {
0535   bsp_tlb_idx_t         k0;
0536   rtems_interrupt_level lvl;
0537   bsp_tlb_entry_t       tlb;
0538   uint32_t              msr;
0539 
0540   /* Minimal guard against bad key */
0541   if ( key < 0 || key > NTLBS-1 )
0542     return -1;
0543 
0544   _CPU_MSR_GET(msr);
0545 
0546   /* While address translation is enabled... */
0547   if (msr & (PPC_MSR_IR | PPC_MSR_DR))
0548   {
0549     /* Must not invalidate page 0 which holds vectors, text etc...  */
0550     k0 = bsp_mmu_find(0, 0);
0551     if ( -1 == k0 ) {
0552       myprintf(stderr,"No mapping for address 0 found\n");
0553       return -2;
0554     }
0555 
0556     /* NOTE: we assume PID is ignored */
0557     if ( k0 == key ) {
0558       myprintf(stderr,"Cannot invalidate page holding address 0 (always needed)\n");
0559       return -3;
0560     }
0561   }
0562 
0563   rtems_interrupt_disable(lvl);
0564 
0565   fetch(key, &tlb);
0566 
0567   /* Invalidate old entries */
0568   tlb.hi.v = 0;
0569 
0570   store(key, &tlb);
0571 
0572   commit();
0573 
0574   /* update cache */
0575   bsp_mmu_cache[ key ].hi.v = tlb.hi.v;
0576 
0577   rtems_interrupt_enable(lvl);
0578 
0579   return 0;
0580 }