Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:00

0001 /**
0002  * @file
0003  *
0004  * @ingroup RTEMSBSPsPowerPCVirtex5MMU
0005  *
0006  * @brief Implementation of routines to manipulate the PPC 440 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 440 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 /* 440 MSR definitions; note that there are *substantial* differences
0060  * compared to classic powerpc; in particular, IS/DS are *different*
0061  * from IR/DR; the ppc440 MMU cannot be switched off!
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->w0.v )
0117       continue;
0118     myprintf(f, "#%2i: EA 0x%08x .. 0x%08x, TID 0x%03x, TS %i\n",
0119              idx,
0120              tlb->w0.epn<<10,
0121              (tlb->w0.epn<<10) + (1024<<(2*tlb->w0.size))-1,
0122              tlb->id.tid,
0123              tlb->w0.ts);
0124     myprintf(f, "     PA 0x%08"PRIx32", U0-3 0x%01x, WIMGE 0x%02x, PERM 0x%03x\n",
0125              tlb->w1.rpn<<10,
0126              tlb->w2.att,
0127              tlb->w2.wimge,
0128              tlb->w2.perm);
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 ("mfpid   %[tmp]            \n\t"
0137                     "stw     %[tmp],0(%[tlb])  \n\t"
0138                     "tlbre   %[tmp],%[key],0   \n\t"
0139                     "stw     %[tmp],4(%[tlb])  \n\t"
0140                     "tlbre   %[tmp],%[key],1   \n\t"
0141                     "stw     %[tmp],8(%[tlb])  \n\t"
0142                     "tlbre   %[tmp],%[key],2   \n\t"
0143                     "stw     %[tmp],12(%[tlb]) \n\t"
0144                     "sync                      \n\t"
0145                     : [tmp]"=&r"(tmp)
0146                     : [key]"r"(key),
0147                       [tlb]"b"(tlb)
0148                     );
0149 }
0150 
0151 
0152 static void
0153 store(bsp_tlb_idx_t key, bsp_tlb_entry_t* tlb)
0154 {
0155   register uint32_t tmp;
0156   __asm__ volatile ("lwz     %[tmp],0(%[tlb])  \n\t"
0157                     "mtpid   %[tmp]            \n\t"
0158                     "lwz     %[tmp],4(%[tlb])  \n\t"
0159                     "tlbwe   %[tmp],%[idx],0   \n\t"
0160                     "lwz     %[tmp],8(%[tlb])  \n\t"
0161                     "tlbwe   %[tmp],%[idx],1   \n\t"
0162                     "lwz     %[tmp],12(%[tlb]) \n\t"
0163                     "tlbwe   %[tmp],%[idx],2   \n\t"
0164                     : [tmp]"=&r"(tmp)
0165                     : [tlb]"b"(tlb),
0166                       [idx]"r"(key)
0167                     );
0168 }
0169 
0170 
0171 static void
0172 commit(void)
0173 {
0174   __asm__ volatile("isync           \n\t");
0175 }
0176 
0177 
0178 /*
0179  * Read a TLB entry from the hardware store the current settings in the
0180  * bsp_mmu_cache[] structure.
0181  *
0182  * The routine can perform this operation quietly or
0183  * print information to a file.
0184  *
0185  *   'idx': which TLB entry to access.
0186  * 'quiet': perform operation silently (no info printed)
0187  *          if nonzero.
0188  *     'f': open FILE where to print information. May be
0189  *          NULL in which case 'stdout' is used.
0190  *
0191  * RETURNS:
0192  *       0: success; TLB entry is VALID
0193  *      +1: success but TLB entry is INVALID
0194  *     < 0: error (-1: invalid argument)
0195  *                (-2: driver not initialized)
0196  */
0197 int
0198 bsp_mmu_update(bsp_tlb_idx_t key, bool quiet, FILE *f)
0199 {
0200   rtems_interrupt_level lvl;
0201   bsp_tlb_entry_t*      tlb;
0202   int                   idx;
0203 
0204   idx = key;
0205 
0206   if ( idx < 0 || idx > NTLBS-1 )
0207     return -1;
0208 
0209   if (!bsp_mmu_cache)
0210     return -2;
0211 
0212   tlb = bsp_mmu_cache + idx;
0213 
0214   rtems_interrupt_disable(lvl);
0215 
0216   fetch(idx, tlb);
0217 
0218   rtems_interrupt_enable(lvl);
0219 
0220   if ( tlb->w0.v ) {
0221     if ( !quiet ) {
0222 /*
0223                "TLB Entry #  0 spans EA range    0x00000000 - 0x00000000
0224                "Mapping:     VA   [TS 0 / TID 0x00 / EPN 0x00000] -> RPN 0x00000"
0225                "Size:        TSIZE 0x0 (4^sz KB = 000000 KB = 0x00000000 B)
0226                "Attributes:  PERM  0x000 (ux/uw/ur/sx/sw/sr) WIMGE 0x00 U0-3 0x0"
0227 */
0228       myprintf(f,
0229                "TLB Entry # %2d spans EA range    0x%08x - 0x%08x\n",
0230                idx,
0231                (tlb->w0.epn << 10),
0232                (tlb->w0.epn << 10) + (1024<<(2*tlb->w0.size)) - 1
0233                );
0234 
0235       myprintf(f,
0236                "Mapping:     VA   [TS %d / TID 0x%02x / EPN 0x%05x] -> RPN 0x%05"PRIx32"\n",
0237                tlb->w0.ts, tlb->id.tid, tlb->w0.epn, tlb->w1.rpn
0238                );
0239       myprintf(f,
0240                "Size:        TSIZE 0x%x (4^sz KB = %6d KB = 0x%08x B)\n",
0241                tlb->w0.size, (1<<(2*tlb->w0.size)), (1024<<(2*tlb->w0.size))
0242                );
0243       myprintf(f,
0244                "Properties:  PERM  0x%03x (ux/uw/ur/sx/sw/sr) WIMGE 0x%02x U0-3 0x%01x\n",
0245                tlb->w2.perm, tlb->w2.wimge, tlb->w2.att
0246                );
0247     }
0248   } else {
0249     if ( !quiet ) {
0250       myprintf(f, "TLB Entry # %2d <OFF> (size 0x%x = 0x%xb)\n",
0251                idx, tlb->w0.size, (1024<<(2*tlb->w0.size)));
0252     }
0253     return 1;
0254   }
0255   return 0;
0256 }
0257 
0258 /* Initialize cache.  Should be done only once although this is not enforced.
0259  *
0260  * RETURNS: zero on success, nonzero on error; in this case the driver will
0261  *          refuse to change TLB entries (other than disabling them).
0262  */
0263 int
0264 bsp_mmu_initialize()
0265 {
0266   static bsp_tlb_entry_t mmu_cache[NTLBS];
0267   bsp_tlb_entry_t*       tlb = mmu_cache;  /* Should malloc if it's not too early */
0268   rtems_interrupt_level  lvl;
0269 
0270   bsp_tlb_idx_t idx;
0271   rtems_interrupt_disable(lvl);
0272   for (idx=0; idx<NTLBS; tlb++, idx++)
0273   {
0274     fetch(idx, tlb);
0275   }
0276   rtems_interrupt_enable(lvl);
0277 
0278   bsp_mmu_cache = mmu_cache;
0279   return 0;
0280 }
0281 
0282 /* Find first free TLB entry by examining all entries' valid bit.  The first
0283  * entry without the valid bit set is returned.
0284  *
0285  * RETURNS: A free TLB entry number.  -1 if no entry can be found.
0286  */
0287 bsp_tlb_idx_t
0288 bsp_mmu_find_first_free()
0289 {
0290   bsp_tlb_idx_t   idx;
0291   bsp_tlb_entry_t entry;
0292 
0293   for (idx=0; idx<NTLBS; idx++) {
0294     register uint32_t tmp;
0295     __asm__ volatile ("tlbre   %[tmp],%[idx],0   \n\t"
0296                       "stw     %[tmp],4(%[tlb])  \n\t" /* entry.w0 */
0297                       "sync                      \n\t"
0298                       : [tmp]"=&r"(tmp)
0299                       : [idx]"r"(idx),
0300                         [tlb]"b"(&entry)
0301                       : "memory"
0302                       );
0303     if (!(entry.w0.v))
0304       break;
0305   }
0306   return (idx < NTLBS) ? idx : -1;
0307 }
0308 
0309 /*
0310  * Write TLB entry (can also be used to disable an entry).
0311  *
0312  * The routine checks against the cached data in
0313  * bsp_mmu_cache[] to prevent the user from generating
0314  * overlapping entries.
0315  *
0316  *   'idx': TLB entry # to manipulate
0317  *    'ea': Effective address (must be page aligned)
0318  *    'pa': Physical  address (must be page aligned)
0319  *    'sz': Page size selector; page size is
0320  *          1024 * 2^(2*sz) bytes.
0321  *          'sz' may also be one of the following:
0322  *          - page size in bytes ( >= 1024 ); the selector
0323  *            value is then computed by this routine.
0324  *            However, 'sz' must be a valid page size
0325  *            or -1 will be returned.
0326  *          - a value < 0 to invalidate/disable the
0327  *            TLB entry.
0328  *  'flgs': Page's User-defined flags, permissions and WIMGE page attributes
0329  *   'tid': Translation ID
0330  *    'ts': Translation Space
0331  *  'erpn': Extended Real Page Number
0332  *
0333  * RETURNS: 0 on success, nonzero on error:
0334  *
0335  *         >0: requested mapping would overlap with
0336  *             existing mapping in other entry. Return
0337  *             value gives conflicting entry + 1; i.e.,
0338  *             if a value of 4 is returned then the request
0339  *             conflicts with existing mapping in entry 3.
0340  *         -1: invalid argument
0341  *         -3: driver not initialized (or initialization failed).
0342  *         <0: other error
0343  */
0344 bsp_tlb_idx_t
0345 bsp_mmu_write(bsp_tlb_idx_t idx, uint32_t ea, uint32_t pa, int sz,
0346               uint32_t flgs, uint32_t tid, uint32_t ts, uint32_t erpn)
0347 {
0348   bsp_tlb_entry_t       tlb;
0349   uint32_t              msk;
0350   bsp_tlb_idx_t         lkup;
0351   rtems_interrupt_level lvl;
0352 
0353   if ( sz >= 1024 ) {
0354     /* Assume they literally specify a size */
0355     msk = sz;
0356     sz  = 0;
0357     while ( msk != (1024<<(sz+sz)) ) {
0358       if ( ++sz > 15 ) {
0359         return -1;
0360       }
0361     }
0362     /* OK, acceptable */
0363   }
0364 
0365   msk = sz > 0 ? (1024<<(sz+sz)) - 1 : 0;
0366 
0367   if ( !bsp_mmu_cache && sz > 0 ) {
0368     myprintf(stderr,"MMU driver not initialized; refusing to enable any entry\n");
0369     return -3;
0370   }
0371 
0372   if ( (ea & msk) || (pa & msk) ) {
0373     myprintf(stderr,"Misaligned EA (%08x) or PA (%08x) (mask is %08x)\n", ea, pa, msk);
0374     return -1;
0375   }
0376 
0377   if ( idx < 0 || idx > NTLBS-1 )
0378     return -1;
0379 
0380   /* Not all 16 possible sizes are supported */
0381   if ( sz == 6 || sz == 8 || sz > 9 ) {
0382     myprintf(stderr,"Invalid size %u = %08x = %u KB\n", sz, 1024<<(sz+sz), (1024<<(sz+sz))/1024);
0383     return -1;
0384   }
0385 
0386   if ( sz >=0 ) {
0387     lkup = bsp_mmu_match(ea, sz, tid, ts);
0388 
0389     if ( lkup < -1 ) {
0390       /* some error */
0391       return lkup;
0392     }
0393 
0394     if ( lkup >= 0 && lkup != idx && (bsp_mmu_cache[lkup].w0.v != 0) ) {
0395       myprintf(stderr,"TLB #%i overlaps with requested mapping\n", lkup);
0396       bsp_mmu_update( lkup, false, stderr);
0397       return lkup+1;
0398     }
0399   }
0400 
0401   /* OK to proceed */
0402   tlb.id.tid  = tid;
0403   tlb.w0.v    = sz >= 0;
0404   tlb.w0.ts   = ts;
0405   tlb.w0.size = sz;
0406   tlb.w0.epn = (ea & (0xfffffc00 << (sz+sz))) >> 10;
0407   if (sz < 11) {
0408     tlb.w1.rpn  = (pa & (0xfffffc00 << (sz+sz))) >> 10;
0409     tlb.w1.erpn = erpn;
0410   }
0411   else {
0412     sz -= 11;
0413     tlb.w1.rpn  = 0;
0414     tlb.w1.erpn = (erpn & (0xf << (sz+sz))) & 0xf;
0415   }
0416   tlb.w2.att   = (flgs & MMU_M_ATTR) >> MMU_V_ATTR;
0417   tlb.w2.wimge = (flgs & MMU_M_PROP) >> MMU_V_PROP;
0418   tlb.w2.perm  = (flgs & MMU_M_PERM) >> MMU_V_PERM;
0419 
0420   rtems_interrupt_disable(lvl);
0421 
0422   store(idx, &tlb);
0423 
0424   commit();
0425 
0426   rtems_interrupt_enable(lvl);
0427 
0428   /* update cache */
0429   bsp_mmu_update(idx, true, 0);
0430 
0431   return 0;
0432 }
0433 
0434 /*
0435  * Check if a ea/tid/ts/sz mapping overlaps with an existing entry.
0436  *
0437  *    'ea': The Effective Address to match against
0438  *    'sz': The 'logarithmic' size selector; the page size
0439  *          is 1024*2^(2*sz).
0440  *   'tid': Translation ID
0441  *    'ts': Translation Space
0442  *
0443  * RETURNS:
0444  *     >= 0: index of the TLB entry that already provides a mapping
0445  *           which overlaps within the ea range.
0446  *       -1: SUCCESS (no conflicting entry found)
0447  *     <=-2: ERROR (invalid input)
0448  */
0449 bsp_tlb_idx_t
0450 bsp_mmu_match(uint32_t ea, int sz, uint32_t tid, uint32_t ts)
0451 {
0452   bsp_tlb_idx_t    idx;
0453   uint32_t         m,a;
0454   bsp_tlb_entry_t* tlb;
0455 
0456   if ( sz < 0 || sz == 6 || sz == 8 || sz > 9 )
0457     return -4;
0458 
0459   sz = (1024<<(2*sz));
0460 
0461   if ( !bsp_mmu_cache ) {
0462     /* cache not initialized */
0463     return -3;
0464   }
0465 
0466   if ( ea & (sz-1) ) {
0467     /* misaligned ea */
0468     return -2;
0469   }
0470 
0471   for ( idx=0, tlb=bsp_mmu_cache; idx<NTLBS; idx++, tlb++ ) {
0472     if ( ! tlb->w0.v )
0473       continue;
0474     if ( tlb->id.tid && tlb->id.tid != tid )
0475       continue;
0476     if ( tlb->w0.ts != ts )
0477       continue;
0478     /* TID and TS match a valid entry */
0479     m  = (1024<<(2*tlb->w0.size)) - 1;
0480     /* calculate starting address of this entry */
0481     a  = tlb->w0.epn<<10;
0482     if ( ea <= a + m && ea + sz -1 >= a ) {
0483       /* overlap */
0484       return idx;
0485     }
0486   }
0487   return -1;
0488 }
0489 
0490 /* Find TLB index that maps 'ea/tid/ts' combination
0491  *
0492  *    'ea': Effective address to match against
0493  *   'tid': Translation ID
0494  *    'ts': Translation Space
0495  *
0496  * RETURNS: index 'key' which indicates whether
0497  *          the mapping was found.
0498  *
0499  *          On error (no mapping) -1 is returned.
0500  */
0501 bsp_tlb_idx_t
0502 bsp_mmu_find(uint32_t ea, uint32_t tid, uint32_t ts)
0503 {
0504   rtems_interrupt_level  lvl;
0505   register uint32_t      mmucr;
0506   register bsp_tlb_idx_t idx;
0507   register int           failure;
0508 
0509   rtems_interrupt_disable(lvl);
0510 
0511   __asm__ volatile ("mfspr  %[mmucr],0x3b2  \n\t" /* Save MMUCR */
0512                     : [mmucr]"=r"(mmucr)
0513                     );
0514   __asm__ volatile ("mtspr  0x3b2,%[tid]    \n\t"
0515                     "tlbsx. %[idx],0,%[ea]  \n\t" /* Failure changes the index reg randomly. */
0516                     "mfcr   %[failure]      \n\t"
0517                     "mtspr  0x3b2,%[mmucr]  \n\t" /* Restore MMUCR */
0518                     : [idx]"=&r"(idx),
0519                       [failure]"=&r"(failure)
0520                     : [tid]"r"((mmucr & 0xfffeff00) | (ts << 16) | tid),
0521                       [ea]"r"(ea),
0522                       [mmucr]"r"(mmucr)
0523                     : "cc"
0524                     );
0525 
0526   rtems_interrupt_enable(lvl);
0527 
0528   return (failure & 0x20000000) ? idx : -1;
0529 }
0530 
0531 /* Mark TLB entry as invalid ('disabled').
0532  *
0533  * 'key': TLB entry (index).
0534  *
0535  * RETURNS: zero on success, nonzero on error (TLB unchanged).
0536  *
0537  * NOTE:  If a TLB entry is disabled the associated
0538  *        entry in bsp_mmu_cache[] is also
0539  *        marked as disabled.
0540  */
0541 int
0542 bsp_mmu_invalidate(bsp_tlb_idx_t key)
0543 {
0544   bsp_tlb_idx_t         k0;
0545   rtems_interrupt_level lvl;
0546   bsp_tlb_entry_t       tlb;
0547 
0548   /* minimal guard against bad key */
0549   if ( key < 0 || key > NTLBS-1 )
0550     return -1;
0551 
0552   /* Must not invalidate page 0 which holds vectors, text etc...  */
0553   k0 = bsp_mmu_find(0, 0, 0);
0554   if ( -1 == k0 ) {
0555     myprintf(stderr,"No mapping for address 0 found\n");
0556     return -2;
0557   }
0558 
0559   /* NOTE: we assume PID is ignored */
0560   if ( k0 == key ) {
0561     myprintf(stderr,"Cannot invalidate page holding address 0 (always needed)\n");
0562     return -3;
0563   }
0564 
0565   rtems_interrupt_disable(lvl);
0566 
0567   fetch(key, &tlb);
0568 
0569   /* Invalidate old entries */
0570   tlb.w0.v = 0;
0571 
0572   store(key, &tlb);
0573 
0574   commit();
0575 
0576   /* Update cache */
0577   bsp_mmu_cache[ key ].w0.v = tlb.w0.v;
0578 
0579   rtems_interrupt_enable(lvl);
0580 
0581   return 0;
0582 }