source: rtems/bsps/powerpc/virtex5/start/mmu.c @ 762fa62

5
Last change on this file since 762fa62 was 25787041, checked in by Sebastian Huber <sebastian.huber@…>, on 04/25/18 at 08:22:18

bsp/virtex5: Move mmu.c to bsps

This patch is a part of the BSP source reorganization.

Update #3285.

  • Property mode set to 100644
File size: 16.0 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup Virtex5MMU
5 *
6 * @brief Implementation of routines to manipulate the PPC 440 MMU.
7 *
8 *        Since this is a real-time OS we want to stay away from
9 *        software TLB replacement.
10 */
11/*
12 * Authorship
13 * ----------
14 * This software was created by
15 *     Till Straumann <strauman@slac.stanford.edu>, 2005-2007,
16 *         Stanford Linear Accelerator Center, Stanford University.
17 * and was transcribed for the PPC 440 by
18 *     R. Claus <claus@slac.stanford.edu>, 2012,
19 *       Stanford Linear Accelerator Center, Stanford University,
20 *
21 * Acknowledgement of sponsorship
22 * ------------------------------
23 * This software was produced by
24 *     the Stanford Linear Accelerator Center, Stanford University,
25 *         under Contract DE-AC03-76SFO0515 with the Department of Energy.
26 *
27 * Government disclaimer of liability
28 * ----------------------------------
29 * Neither the United States nor the United States Department of Energy,
30 * nor any of their employees, makes any warranty, express or implied, or
31 * assumes any legal liability or responsibility for the accuracy,
32 * completeness, or usefulness of any data, apparatus, product, or process
33 * disclosed, or represents that its use would not infringe privately owned
34 * rights.
35 *
36 * Stanford disclaimer of liability
37 * --------------------------------
38 * Stanford University makes no representations or warranties, express or
39 * implied, nor assumes any liability for the use of this software.
40 *
41 * Stanford disclaimer of copyright
42 * --------------------------------
43 * Stanford University, owner of the copyright, hereby disclaims its
44 * copyright and all other rights in this software.  Hence, anyone may
45 * freely use it for any purpose without restriction.
46 *
47 * Maintenance of notices
48 * ----------------------
49 * In the interest of clarity regarding the origin and status of this
50 * SLAC software, this and all the preceding Stanford University notices
51 * are to remain affixed to any copy or derivative of this software made
52 * or distributed by the recipient and are to be affixed to any copy of
53 * software made or distributed by the recipient that contains a copy or
54 * derivative of this software.
55 *
56 * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
57 */
58
59/* 440 MSR definitions; note that there are *substantial* differences
60 * compared to classic powerpc; in particular, IS/DS are *different*
61 * from IR/DR; the ppc440 MMU cannot be switched off!
62 *
63 * Also: To disable/enable all external interrupts, CE and EE must both be
64 *       controlled.
65 */
66#include <rtems.h>
67#include <rtems/bspIo.h>
68#include <rtems/powerpc/powerpc.h>
69#include <inttypes.h>
70#include <stdio.h>
71
72#include <bsp/mmu.h>
73
74
75#ifdef DEBUG
76#define STATIC
77#else
78#define STATIC static
79#endif
80
81
82bsp_tlb_entry_t* bsp_mmu_cache = 0;
83
84
85/* Since it is likely that these routines are used during
86 * early initialization when stdio is not available yet
87 * we provide a helper that resorts to 'printk()'
88 */
89static void
90myprintf(FILE *f, char *fmt, ...)
91{
92  va_list ap;
93  va_start(ap, fmt);
94
95  if (!f || !_impure_ptr->__sdidinit) {
96    /* Might be called at an early stage when stdio is not yet initialized. */
97    vprintk(fmt,ap);
98  } else {
99    vfprintf(f,fmt,ap);
100  }
101  va_end(ap);
102}
103
104
105void
106bsp_mmu_dump_cache(FILE *f)
107{
108  bsp_tlb_idx_t idx;
109  if ( !bsp_mmu_cache ) {
110    myprintf(stderr,"MMU TLB cache not initialized\n");
111    return;
112  }
113  for ( idx=0; idx<NTLBS; idx++ ) {
114    bsp_tlb_entry_t *tlb = bsp_mmu_cache + idx;
115    if ( !tlb->w0.v )
116      continue;
117    myprintf(f, "#%2i: EA 0x%08x .. 0x%08x, TID 0x%03x, TS %i\n",
118             idx,
119             tlb->w0.epn<<10,
120             (tlb->w0.epn<<10) + (1024<<(2*tlb->w0.size))-1,
121             tlb->id.tid,
122             tlb->w0.ts);
123    myprintf(f, "     PA 0x%08"PRIx32", U0-3 0x%01x, WIMGE 0x%02x, PERM 0x%03x\n",
124             tlb->w1.rpn<<10,
125             tlb->w2.att,
126             tlb->w2.wimge,
127             tlb->w2.perm);
128  }
129}
130
131static void
132fetch(bsp_tlb_idx_t key, bsp_tlb_entry_t* tlb)
133{
134  register uint32_t tmp;
135  __asm__ volatile ("mfpid   %[tmp]            \n\t"
136                    "stw     %[tmp],0(%[tlb])  \n\t"
137                    "tlbre   %[tmp],%[key],0   \n\t"
138                    "stw     %[tmp],4(%[tlb])  \n\t"
139                    "tlbre   %[tmp],%[key],1   \n\t"
140                    "stw     %[tmp],8(%[tlb])  \n\t"
141                    "tlbre   %[tmp],%[key],2   \n\t"
142                    "stw     %[tmp],12(%[tlb]) \n\t"
143                    "sync                      \n\t"
144                    : [tmp]"=&r"(tmp)
145                    : [key]"r"(key),
146                      [tlb]"b"(tlb)
147                    );
148}
149
150
151static void
152store(bsp_tlb_idx_t key, bsp_tlb_entry_t* tlb)
153{
154  register uint32_t tmp;
155  __asm__ volatile ("lwz     %[tmp],0(%[tlb])  \n\t"
156                    "mtpid   %[tmp]            \n\t"
157                    "lwz     %[tmp],4(%[tlb])  \n\t"
158                    "tlbwe   %[tmp],%[idx],0   \n\t"
159                    "lwz     %[tmp],8(%[tlb])  \n\t"
160                    "tlbwe   %[tmp],%[idx],1   \n\t"
161                    "lwz     %[tmp],12(%[tlb]) \n\t"
162                    "tlbwe   %[tmp],%[idx],2   \n\t"
163                    : [tmp]"=&r"(tmp)
164                    : [tlb]"b"(tlb),
165                      [idx]"r"(key)
166                    );
167}
168
169
170static void
171commit(void)
172{
173  __asm__ volatile("isync           \n\t");
174}
175
176
177/*
178 * Read a TLB entry from the hardware store the current settings in the
179 * bsp_mmu_cache[] structure.
180 *
181 * The routine can perform this operation quietly or
182 * print information to a file.
183 *
184 *   'idx': which TLB entry to access.
185 * 'quiet': perform operation silently (no info printed)
186 *          if nonzero.
187 *     'f': open FILE where to print information. May be
188 *          NULL in which case 'stdout' is used.
189 *
190 * RETURNS:
191 *       0: success; TLB entry is VALID
192 *      +1: success but TLB entry is INVALID
193 *     < 0: error (-1: invalid argument)
194 *                (-2: driver not initialized)
195 */
196int
197bsp_mmu_update(bsp_tlb_idx_t key, bool quiet, FILE *f)
198{
199  rtems_interrupt_level lvl;
200  bsp_tlb_entry_t*      tlb;
201  int                   idx;
202
203  idx = key;
204
205  if ( idx < 0 || idx > NTLBS-1 )
206    return -1;
207
208  if (!bsp_mmu_cache)
209    return -2;
210
211  tlb = bsp_mmu_cache + idx;
212
213  rtems_interrupt_disable(lvl);
214
215  fetch(idx, tlb);
216
217  rtems_interrupt_enable(lvl);
218
219  if ( tlb->w0.v ) {
220    if ( !quiet ) {
221/*
222               "TLB Entry #  0 spans EA range    0x00000000 - 0x00000000
223               "Mapping:     VA   [TS 0 / TID 0x00 / EPN 0x00000] -> RPN 0x00000"
224               "Size:        TSIZE 0x0 (4^sz KB = 000000 KB = 0x00000000 B)
225               "Attributes:  PERM  0x000 (ux/uw/ur/sx/sw/sr) WIMGE 0x00 U0-3 0x0"
226*/
227      myprintf(f,
228               "TLB Entry # %2d spans EA range    0x%08x - 0x%08x\n",
229               idx,
230               (tlb->w0.epn << 10),
231               (tlb->w0.epn << 10) + (1024<<(2*tlb->w0.size)) - 1
232               );
233
234      myprintf(f,
235               "Mapping:     VA   [TS %d / TID 0x%02x / EPN 0x%05x] -> RPN 0x%05"PRIx32"\n",
236               tlb->w0.ts, tlb->id.tid, tlb->w0.epn, tlb->w1.rpn
237               );
238      myprintf(f,
239               "Size:        TSIZE 0x%x (4^sz KB = %6d KB = 0x%08x B)\n",
240               tlb->w0.size, (1<<(2*tlb->w0.size)), (1024<<(2*tlb->w0.size))
241               );
242      myprintf(f,
243               "Properties:  PERM  0x%03x (ux/uw/ur/sx/sw/sr) WIMGE 0x%02x U0-3 0x%01x\n",
244               tlb->w2.perm, tlb->w2.wimge, tlb->w2.att
245               );
246    }
247  } else {
248    if ( !quiet ) {
249      myprintf(f, "TLB Entry # %2d <OFF> (size 0x%x = 0x%xb)\n",
250               idx, tlb->w0.size, (1024<<(2*tlb->w0.size)));
251    }
252    return 1;
253  }
254  return 0;
255}
256
257/* Initialize cache.  Should be done only once although this is not enforced.
258 *
259 * RETURNS: zero on success, nonzero on error; in this case the driver will
260 *          refuse to change TLB entries (other than disabling them).
261 */
262int
263bsp_mmu_initialize()
264{
265  static bsp_tlb_entry_t mmu_cache[NTLBS];
266  bsp_tlb_entry_t*       tlb = mmu_cache;  /* Should malloc if it's not too early */
267  rtems_interrupt_level  lvl;
268
269  bsp_tlb_idx_t idx;
270  rtems_interrupt_disable(lvl);
271  for (idx=0; idx<NTLBS; tlb++, idx++)
272  {
273    fetch(idx, tlb);
274  }
275  rtems_interrupt_enable(lvl);
276
277  bsp_mmu_cache = mmu_cache;
278  return 0;
279}
280
281/* Find first free TLB entry by examining all entries' valid bit.  The first
282 * entry without the valid bit set is returned.
283 *
284 * RETURNS: A free TLB entry number.  -1 if no entry can be found.
285 */
286bsp_tlb_idx_t
287bsp_mmu_find_first_free()
288{
289  bsp_tlb_idx_t   idx;
290  bsp_tlb_entry_t entry;
291
292  for (idx=0; idx<NTLBS; idx++) {
293    register uint32_t tmp;
294    __asm__ volatile ("tlbre   %[tmp],%[idx],0   \n\t"
295                      "stw     %[tmp],4(%[tlb])  \n\t" /* entry.w0 */
296                      "sync                      \n\t"
297                      : [tmp]"=&r"(tmp)
298                      : [idx]"r"(idx),
299                        [tlb]"b"(&entry)
300                      : "memory"
301                      );
302    if (!(entry.w0.v))
303      break;
304  }
305  return (idx < NTLBS) ? idx : -1;
306}
307
308/*
309 * Write TLB entry (can also be used to disable an entry).
310 *
311 * The routine checks against the cached data in
312 * bsp_mmu_cache[] to prevent the user from generating
313 * overlapping entries.
314 *
315 *   'idx': TLB entry # to manipulate
316 *    'ea': Effective address (must be page aligned)
317 *    'pa': Physical  address (must be page aligned)
318 *    'sz': Page size selector; page size is
319 *          1024 * 2^(2*sz) bytes.
320 *          'sz' may also be one of the following:
321 *          - page size in bytes ( >= 1024 ); the selector
322 *            value is then computed by this routine.
323 *            However, 'sz' must be a valid page size
324 *            or -1 will be returned.
325 *          - a value < 0 to invalidate/disable the
326 *            TLB entry.
327 *  'flgs': Page's User-defined flags, permissions and WIMGE page attributes
328 *   'tid': Translation ID
329 *    'ts': Translation Space
330 *  'erpn': Extended Real Page Number
331 *
332 * RETURNS: 0 on success, nonzero on error:
333 *
334 *         >0: requested mapping would overlap with
335 *             existing mapping in other entry. Return
336 *             value gives conflicting entry + 1; i.e.,
337 *             if a value of 4 is returned then the request
338 *             conflicts with existing mapping in entry 3.
339 *         -1: invalid argument
340 *         -3: driver not initialized (or initialization failed).
341 *         <0: other error
342 */
343bsp_tlb_idx_t
344bsp_mmu_write(bsp_tlb_idx_t idx, uint32_t ea, uint32_t pa, int sz,
345              uint32_t flgs, uint32_t tid, uint32_t ts, uint32_t erpn)
346{
347  bsp_tlb_entry_t       tlb;
348  uint32_t              msk;
349  bsp_tlb_idx_t         lkup;
350  rtems_interrupt_level lvl;
351
352  if ( sz >= 1024 ) {
353    /* Assume they literally specify a size */
354    msk = sz;
355    sz  = 0;
356    while ( msk != (1024<<(sz+sz)) ) {
357      if ( ++sz > 15 ) {
358        return -1;
359      }
360    }
361    /* OK, acceptable */
362  }
363
364  msk = sz > 0 ? (1024<<(sz+sz)) - 1 : 0;
365
366  if ( !bsp_mmu_cache && sz > 0 ) {
367    myprintf(stderr,"MMU driver not initialized; refusing to enable any entry\n");
368    return -3;
369  }
370
371  if ( (ea & msk) || (pa & msk) ) {
372    myprintf(stderr,"Misaligned EA (%08x) or PA (%08x) (mask is %08x)\n", ea, pa, msk);
373    return -1;
374  }
375
376  if ( idx < 0 || idx > NTLBS-1 )
377    return -1;
378
379  /* Not all 16 possible sizes are supported */
380  if ( sz == 6 || sz == 8 || sz > 9 ) {
381    myprintf(stderr,"Invalid size %u = %08x = %u KB\n", sz, 1024<<(sz+sz), (1024<<(sz+sz))/1024);
382    return -1;
383  }
384
385  if ( sz >=0 ) {
386    lkup = bsp_mmu_match(ea, sz, tid, ts);
387
388    if ( lkup < -1 ) {
389      /* some error */
390      return lkup;
391    }
392
393    if ( lkup >= 0 && lkup != idx && (bsp_mmu_cache[lkup].w0.v != 0) ) {
394      myprintf(stderr,"TLB #%i overlaps with requested mapping\n", lkup);
395      bsp_mmu_update( lkup, false, stderr);
396      return lkup+1;
397    }
398  }
399
400  /* OK to proceed */
401  tlb.id.tid  = tid;
402  tlb.w0.v    = sz >= 0;
403  tlb.w0.ts   = ts;
404  tlb.w0.size = sz;
405  tlb.w0.epn = (ea & (0xfffffc00 << (sz+sz))) >> 10;
406  if (sz < 11) {
407    tlb.w1.rpn  = (pa & (0xfffffc00 << (sz+sz))) >> 10;
408    tlb.w1.erpn = erpn;
409  }
410  else {
411    sz -= 11;
412    tlb.w1.rpn  = 0;
413    tlb.w1.erpn = (erpn & (0xf << (sz+sz))) & 0xf;
414  }
415  tlb.w2.att   = (flgs & MMU_M_ATTR) >> MMU_V_ATTR;
416  tlb.w2.wimge = (flgs & MMU_M_PROP) >> MMU_V_PROP;
417  tlb.w2.perm  = (flgs & MMU_M_PERM) >> MMU_V_PERM;
418
419  rtems_interrupt_disable(lvl);
420
421  store(idx, &tlb);
422
423  commit();
424
425  rtems_interrupt_enable(lvl);
426
427  /* update cache */
428  bsp_mmu_update(idx, true, 0);
429
430  return 0;
431}
432
433/*
434 * Check if a ea/tid/ts/sz mapping overlaps with an existing entry.
435 *
436 *    'ea': The Effective Address to match against
437 *    'sz': The 'logarithmic' size selector; the page size
438 *          is 1024*2^(2*sz).
439 *   'tid': Translation ID
440 *    'ts': Translation Space
441 *
442 * RETURNS:
443 *     >= 0: index of the TLB entry that already provides a mapping
444 *           which overlaps within the ea range.
445 *       -1: SUCCESS (no conflicting entry found)
446 *     <=-2: ERROR (invalid input)
447 */
448bsp_tlb_idx_t
449bsp_mmu_match(uint32_t ea, int sz, uint32_t tid, uint32_t ts)
450{
451  bsp_tlb_idx_t    idx;
452  uint32_t         m,a;
453  bsp_tlb_entry_t* tlb;
454
455  if ( sz < 0 || sz == 6 || sz == 8 || sz > 9 )
456    return -4;
457
458  sz = (1024<<(2*sz));
459
460  if ( !bsp_mmu_cache ) {
461    /* cache not initialized */
462    return -3;
463  }
464
465  if ( ea & (sz-1) ) {
466    /* misaligned ea */
467    return -2;
468  }
469
470  for ( idx=0, tlb=bsp_mmu_cache; idx<NTLBS; idx++, tlb++ ) {
471    if ( ! tlb->w0.v )
472      continue;
473    if ( tlb->id.tid && tlb->id.tid != tid )
474      continue;
475    if ( tlb->w0.ts != ts )
476      continue;
477    /* TID and TS match a valid entry */
478    m  = (1024<<(2*tlb->w0.size)) - 1;
479    /* calculate starting address of this entry */
480    a  = tlb->w0.epn<<10;
481    if ( ea <= a + m && ea + sz -1 >= a ) {
482      /* overlap */
483      return idx;
484    }
485  }
486  return -1;
487}
488
489/* Find TLB index that maps 'ea/tid/ts' combination
490 *
491 *    'ea': Effective address to match against
492 *   'tid': Translation ID
493 *    'ts': Translation Space
494 *
495 * RETURNS: index 'key' which indicates whether
496 *          the mapping was found.
497 *
498 *          On error (no mapping) -1 is returned.
499 */
500bsp_tlb_idx_t
501bsp_mmu_find(uint32_t ea, uint32_t tid, uint32_t ts)
502{
503  rtems_interrupt_level  lvl;
504  register uint32_t      mmucr;
505  register bsp_tlb_idx_t idx;
506  register int           failure;
507
508  rtems_interrupt_disable(lvl);
509
510  __asm__ volatile ("mfspr  %[mmucr],0x3b2  \n\t" /* Save MMUCR */
511                    : [mmucr]"=r"(mmucr)
512                    );
513  __asm__ volatile ("mtspr  0x3b2,%[tid]    \n\t"
514                    "tlbsx. %[idx],0,%[ea]  \n\t" /* Failure changes the index reg randomly. */
515                    "mfcr   %[failure]      \n\t"
516                    "mtspr  0x3b2,%[mmucr]  \n\t" /* Restore MMUCR */
517                    : [idx]"=&r"(idx),
518                      [failure]"=&r"(failure)
519                    : [tid]"r"((mmucr & 0xfffeff00) | (ts << 16) | tid),
520                      [ea]"r"(ea),
521                      [mmucr]"r"(mmucr)
522                    : "cc"
523                    );
524
525  rtems_interrupt_enable(lvl);
526
527  return (failure & 0x20000000) ? idx : -1;
528}
529
530/* Mark TLB entry as invalid ('disabled').
531 *
532 * 'key': TLB entry (index).
533 *
534 * RETURNS: zero on success, nonzero on error (TLB unchanged).
535 *
536 * NOTE:  If a TLB entry is disabled the associated
537 *        entry in bsp_mmu_cache[] is also
538 *        marked as disabled.
539 */
540int
541bsp_mmu_invalidate(bsp_tlb_idx_t key)
542{
543  bsp_tlb_idx_t         k0;
544  rtems_interrupt_level lvl;
545  bsp_tlb_entry_t       tlb;
546
547  /* minimal guard against bad key */
548  if ( key < 0 || key > NTLBS-1 )
549    return -1;
550
551  /* Must not invalidate page 0 which holds vectors, text etc...  */
552  k0 = bsp_mmu_find(0, 0, 0);
553  if ( -1 == k0 ) {
554    myprintf(stderr,"No mapping for address 0 found\n");
555    return -2;
556  }
557
558  /* NOTE: we assume PID is ignored */
559  if ( k0 == key ) {
560    myprintf(stderr,"Cannot invalidate page holding address 0 (always needed)\n");
561    return -3;
562  }
563
564  rtems_interrupt_disable(lvl);
565
566  fetch(key, &tlb);
567
568  /* Invalidate old entries */
569  tlb.w0.v = 0;
570
571  store(key, &tlb);
572
573  commit();
574
575  /* Update cache */
576  bsp_mmu_cache[ key ].w0.v = tlb.w0.v;
577
578  rtems_interrupt_enable(lvl);
579
580  return 0;
581}
Note: See TracBrowser for help on using the repository browser.