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

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

bsp/virtex4: Move mmu.c to bsps

This patch is a part of the BSP source reorganization.

Update #3285.

  • Property mode set to 100644
File size: 15.4 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup Virtex4MMU
5 *
6 * @brief Implementation of routines to manipulate the PPC 405 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 405 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/* 405 MSR definitions; note that there are *substantial* differences
60 * compared to classic powerpc; in particular, IS/DS are *different*
61 * from IR/DR.
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->hi.v )
116      continue;
117    myprintf(f, "#%2i: EA 0x%08x .. 0x%08x, TID  0x%03x, EU0  0x%01x\n",
118             idx,
119             tlb->hi.epn << 10,
120             (tlb->hi.epn << 10) + (1024<<(2*tlb->hi.size))-1,
121             tlb->id.tid,
122             tlb->hi.att);
123    myprintf(f, "     PA 0x%08"PRIx32" .. 0x%08"PRIx32", PERM 0x%03x, WIMG 0x%02x\n",
124             tlb->lo.rpn << 10,
125             (tlb->lo.rpn << 10) + (1024<<(2*tlb->hi.size))-1,
126             tlb->lo.perm,
127             tlb->lo.wimg);
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                    "tlbrehi %[tmp],%[key]    \n\t"
138                    "stw     %[tmp],4(%[tlb]) \n\t"
139                    "tlbrelo %[tmp],%[key]    \n\t"
140                    "stw     %[tmp],8(%[tlb]) \n\t"
141                    "sync                     \n\t"
142                    : [tmp]"=&r"(tmp)
143                    : [key]"r"(key),
144                      [tlb]"b"(tlb)
145                    );
146}
147
148
149static void
150store(bsp_tlb_idx_t key, bsp_tlb_entry_t* tlb)
151{
152  register uint32_t tmp;
153  __asm__ volatile ("lwz     %[tmp],0(%[tlb]) \n\t"
154                    "mtpid   %[tmp]           \n\t"
155                    "lwz     %[tmp],4(%[tlb]) \n\t"
156                    "tlbwehi %[tmp],%[key]    \n\t"
157                    "lwz     %[tmp],8(%[tlb]) \n\t"
158                    "tlbwelo %[tmp],%[key]    \n\t"
159                    : [tmp]"=&r"(tmp)
160                    : [tlb]"b"(tlb),
161                      [key]"r"(key)
162                    );
163}
164
165
166static void
167commit(void)
168{
169  __asm__ volatile("isync           \n\t");
170}
171
172
173/*
174 * Read a TLB entry from the hardware and store the current settings in the
175 * bsp_mmu_cache[] structure.
176 *
177 * The routine can perform this operation quietly or
178 * print information to a file.
179 *
180 *   'idx': which TLB entry to access.
181 * 'quiet': perform operation silently (no info printed)
182 *          if nonzero.
183 *     'f': open FILE where to print information. May be
184 *          NULL in which case 'stdout' is used.
185 *
186 * RETURNS:
187 *       0: success; TLB entry is VALID
188 *      +1: success but TLB entry is INVALID
189 *     < 0: error (-1: invalid argument)
190 *                (-2: driver not initialized)
191 */
192int
193bsp_mmu_update(bsp_tlb_idx_t key, bool quiet, FILE *f)
194{
195  rtems_interrupt_level  lvl;
196  bsp_tlb_entry_t*       tlb;
197  bsp_tlb_idx_t          idx;
198
199  idx = key;
200
201  if ( idx < 0 || idx > NTLBS-1 )
202    return -1;
203
204  if (!bsp_mmu_cache)
205    return -2;
206
207  tlb = bsp_mmu_cache + idx;
208
209  rtems_interrupt_disable(lvl);
210
211  fetch(idx, tlb);
212
213  rtems_interrupt_enable(lvl);
214
215  if ( tlb->hi.v ) {
216    if ( !quiet ) {
217/*
218               "TLB Entry #  0 spans EA range     0x00000000 - 0x00000000
219               "Mapping:     VA     [TID 0x00 / EPN 0x00000] -> RPN 0x00000"
220               "Size:        TSIZE 0x0  (4^sz KB = 000000 KB = 0x00000000 B)
221               "Attributes:  PERM  0x00 (ex/wr/zsel) WIMG 0x00 EU0 0x0"
222*/
223      myprintf(f,
224               "TLB Entry # %2d spans EA range     0x%08x - 0x%08x\n",
225               idx,
226               (tlb->hi.epn << 10),
227               (tlb->hi.epn << 10) + (1024<<(2*tlb->hi.size)) - 1
228               );
229
230      myprintf(f,
231               "Mapping:     VA     [TID 0x%02x / EPN 0x%05x] -> RPN 0x%05"PRIx32"\n",
232               tlb->id.tid, tlb->hi.epn, tlb->lo.rpn
233               );
234      myprintf(f,
235               "Size:        TSIZE 0x%x  (4^sz KB = %6d KB = 0x%08x B)\n",
236               tlb->hi.size, (1<<(2*tlb->hi.size)), (1024<<(2*tlb->hi.size))
237               );
238      myprintf(f,
239               "Attributes:  PERM  0x%02x (ex/wr/zsel) WIMG 0x%02x EU0 0x%01x\n",
240               tlb->lo.perm, tlb->lo.wimg, tlb->hi.att
241               );
242    }
243  } else {
244    if ( !quiet ) {
245      myprintf(f,
246               "TLB Entry # %2d <OFF> (size 0x%x = 0x%xb)\n",
247               idx, tlb->hi.size, (1024<<(2*tlb->hi.size))
248               );
249    }
250    return 1;
251  }
252  return 0;
253}
254
255/* Initialize cache.  Should be done only once although this is not enforced.
256 *
257 * RETURNS: zero on success, nonzero on error; in this case the driver will
258 *          refuse to change TLB entries (other than disabling them).
259 */
260int
261bsp_mmu_initialize()
262{
263  static bsp_tlb_entry_t mmu_cache[NTLBS];
264  bsp_tlb_entry_t*       tlb = mmu_cache;  /* Should malloc if it's not too early */
265  rtems_interrupt_level  lvl;
266
267  bsp_tlb_idx_t idx;
268  rtems_interrupt_disable(lvl);
269  for (idx=0; idx<NTLBS; tlb++, idx++)
270  {
271    fetch(idx, tlb);
272  }
273  rtems_interrupt_enable(lvl);
274
275  bsp_mmu_cache = mmu_cache;
276  return 0;
277}
278
279/* Find first free TLB entry by examining all entries' valid bit.  The first
280 * entry without the valid bit set is returned.
281 *
282 * RETURNS: A free TLB entry number.  -1 if no entry can be found.
283 */
284bsp_tlb_idx_t
285bsp_mmu_find_first_free()
286{
287  bsp_tlb_idx_t   idx;
288  bsp_tlb_entry_t entry;
289
290  for (idx=0; idx<NTLBS; idx++) {
291    register uint32_t tmp;
292    __asm__ volatile ("tlbrehi %[tmp],%[idx]    \n\t"
293                      "stw     %[tmp],4(%[tlb]) \n\t" /* entry.hi */
294                      "sync                     \n\t"
295                      : [tmp]"=&r"(tmp)
296                      : [idx]"r"(idx),
297                        [tlb]"b"(&entry)
298                      : "memory"
299                      );
300    if (!(entry.hi.v))
301      break;
302  }
303  return (idx < NTLBS) ? idx : -1;
304}
305
306/*
307 * Write TLB entry (can also be used to disable an entry).
308 *
309 * The routine checks against the cached data in
310 * bsp_mmu_cache[] to prevent the user from generating
311 * overlapping entries.
312 *
313 *   'idx': TLB entry # to manipulate
314 *    'ea': Effective address (must be page aligned)
315 *    'pa': Physical  address (must be page aligned)
316 *    'sz': Page size selector; page size is
317 *          1024 * 2^(2*sz) bytes.
318 *          'sz' may also be one of the following:
319 *          - page size in bytes ( >= 1024 ); the selector
320 *            value is then computed by this routine.
321 *            However, 'sz' must be a valid page size
322 *            or -1 will be returned.
323 *          - a value < 0 to invalidate/disable the
324 *            TLB entry.
325 *  'flgs': Page's little-endian & user-defined flags, permissions and attributes
326 *   'tid': Translation ID
327 *
328 * RETURNS: 0 on success, nonzero on error:
329 *
330 *         >0: requested mapping would overlap with
331 *             existing mapping in other entry. Return
332 *             value gives conflicting entry + 1; i.e.,
333 *             if a value of 4 is returned then the request
334 *             conflicts with existing mapping in entry 3.
335 *         -1: invalid argument
336 *         -3: driver not initialized (or initialization failed).
337 *         <0: other error
338 */
339bsp_tlb_idx_t
340bsp_mmu_write(bsp_tlb_idx_t idx, uint32_t ea, uint32_t pa, uint sz,
341              uint32_t flgs, uint32_t tid)
342{
343  bsp_tlb_entry_t       tlb;
344  uint32_t              msk;
345  bsp_tlb_idx_t         lkup;
346  rtems_interrupt_level lvl;
347
348  if ( sz >= 1024 ) {
349    /* Assume they literally specify a size */
350    msk = sz;
351    sz  = 0;
352    while ( msk != (1024u<<(2*sz)) ) {
353      if ( ++sz > 7 ) {
354        return -1;
355      }
356    }
357    /* OK, acceptable */
358  }
359
360  msk = sz > 0 ? (1024u<<(2*sz)) - 1 : 0;
361
362  if ( !bsp_mmu_cache && sz > 0 ) {
363    myprintf(stderr,"MMU driver not initialized; refusing to enable any entry\n");
364    return -3;
365  }
366
367  if ( (ea & msk) || (pa & msk) ) {
368    myprintf(stderr,"Misaligned EA (%08x) or PA (%08x) (mask is %08x)\n", ea, pa, msk);
369    return -1;
370  }
371
372  if ( idx < 0 || idx > NTLBS-1 )
373    return -1;
374
375  if ( sz > 7 ) {
376    myprintf(stderr,"Invalid size %u = %08x = %u KB\n", sz, 1024u<<(2*sz), (1024u<<(2*sz))/1024);
377    return -1;
378  }
379
380  if ( sz >=0 ) {
381    lkup = bsp_mmu_match(ea, sz, tid);
382
383    if ( lkup < -1 ) {
384      /* some error */
385      return lkup;
386    }
387    if ( (lkup >= 0) && (lkup != idx) && (bsp_mmu_cache[lkup].hi.v != 0) ) {
388      myprintf(stderr,"TLB #%i overlaps with requested mapping\n", lkup);
389      bsp_mmu_update( lkup, false, stderr);
390      return lkup+1;
391    }
392  }
393
394  /* OK to proceed */
395  tlb.id.tid  = tid;
396  tlb.hi.v    = sz >= 0;
397  tlb.hi.size = sz;
398  tlb.hi.epn  = (ea & (0xfffffc00 << (sz + sz))) >> 10;
399  tlb.lo.rpn  = (pa & (0xfffffc00 << (sz + sz))) >> 10;
400  tlb.hi.att  = (flgs & MMU_M_ATTR) >> MMU_V_ATTR;
401  tlb.lo.perm = (flgs & MMU_M_PERM) >> MMU_V_PERM;
402  tlb.lo.wimg = (flgs & MMU_M_PROP) >> MMU_V_PROP;
403
404  rtems_interrupt_disable(lvl);
405
406  store(idx, &tlb);
407
408  commit();
409
410  rtems_interrupt_enable(lvl);
411
412  /* update cache */
413  bsp_mmu_update(idx, true, 0);
414
415  return 0;
416}
417
418/*
419 * Check if a ea/sz/tid mapping overlaps with an existing entry.
420 *
421 *    'ea': The Effective Address to match against
422 *    'sz': The 'logarithmic' size selector; the page size
423 *          is 1024*2^(2*sz).
424 *   'tid': The TID to match against
425 *
426 * RETURNS:
427 *     >= 0: index of the TLB entry that already provides a mapping
428 *           which overlaps within the ea range.
429 *       -1: SUCCESS (no conflicting entry found)
430 *     <=-2: ERROR (invalid input)
431 */
432bsp_tlb_idx_t
433bsp_mmu_match(uint32_t ea, int sz, uint32_t tid)
434{
435  bsp_tlb_idx_t    idx;
436  uint32_t         m,a;
437  bsp_tlb_entry_t* tlb;
438
439  if ( sz < 0 || sz > 7 )
440    return -4;
441
442  sz = (1024<<(2*sz));
443
444  if ( !bsp_mmu_cache ) {
445    /* cache not initialized */
446    return -3;
447  }
448
449  if ( ea & (sz-1) ) {
450    /* misaligned ea */
451    return -2;
452  }
453
454  for ( idx=0, tlb=bsp_mmu_cache; idx<NTLBS; idx++, tlb++ ) {
455    if ( ! tlb->hi.v )
456      continue;
457    if ( tlb->id.tid && tlb->id.tid != tid )
458      continue;
459    /* TID matches a valid entry */
460    m  = (1024<<(2*tlb->hi.size)) - 1;
461    /* calculate starting address of this entry */
462    a  = tlb->hi.epn << 10;
463    if ( ea <= a + m && ea + sz -1 >= a ) {
464      /* overlap */
465      return idx;
466    }
467  }
468  return -1;
469}
470
471/* Find TLB index that maps 'ea/tid' combination
472 *
473 *    'ea': Effective address to match against
474 *   'tid': The TID to match against
475 *
476 * RETURNS: index 'key' which indicates whether
477 *          the mapping was found.
478 *
479 *          On error (no mapping) -1 is returned.
480 */
481bsp_tlb_idx_t
482bsp_mmu_find(uint32_t ea, uint32_t tid)
483{
484  rtems_interrupt_level  lvl;
485  register uint32_t      pid;
486  register bsp_tlb_idx_t idx;
487  register int           failure;
488
489  rtems_interrupt_disable(lvl);
490
491  __asm__ volatile ("mfpid  %[pid]         \n\t" /* Save PID */
492                    "mtpid  %[tid]         \n\t"
493                    "tlbsx. %[idx],0,%[ea] \n\t" /* Failure changes the index reg randomly. */
494                    "mfcr   %[failure]     \n\t"
495                    "mtpid  %[pid]         \n\t" /* Restore PID */
496                    : [pid]"=r"(pid),
497                      [idx]"=&r"(idx),
498                      [failure]"=&r"(failure)
499                    : [tid]"r"(tid),
500                      [ea]"r"(ea)
501                    : "cc"
502                    );
503
504  rtems_interrupt_enable(lvl);
505
506  return (failure & 0x20000000) ? idx : -1;
507}
508
509/* Mark TLB entry as invalid ('disabled').
510 *
511 * 'key': TLB entry (index).
512 *
513 * RETURNS: zero on success, nonzero on error (TLB unchanged).
514 *
515 * NOTE:  If a TLB entry is disabled the associated
516 *        entry in bsp_mmu_cache[] is also
517 *        marked as disabled.
518 */
519int
520bsp_mmu_invalidate(bsp_tlb_idx_t key)
521{
522  bsp_tlb_idx_t         k0;
523  rtems_interrupt_level lvl;
524  bsp_tlb_entry_t       tlb;
525  uint32_t              msr;
526
527  /* Minimal guard against bad key */
528  if ( key < 0 || key > NTLBS-1 )
529    return -1;
530
531  _CPU_MSR_GET(msr);
532
533  /* While address translation is enabled... */
534  if (msr & (PPC_MSR_IR | PPC_MSR_DR))
535  {
536    /* Must not invalidate page 0 which holds vectors, text etc...  */
537    k0 = bsp_mmu_find(0, 0);
538    if ( -1 == k0 ) {
539      myprintf(stderr,"No mapping for address 0 found\n");
540      return -2;
541    }
542
543    /* NOTE: we assume PID is ignored */
544    if ( k0 == key ) {
545      myprintf(stderr,"Cannot invalidate page holding address 0 (always needed)\n");
546      return -3;
547    }
548  }
549
550  rtems_interrupt_disable(lvl);
551
552  fetch(key, &tlb);
553
554  /* Invalidate old entries */
555  tlb.hi.v = 0;
556
557  store(key, &tlb);
558
559  commit();
560
561  /* update cache */
562  bsp_mmu_cache[ key ].hi.v = tlb.hi.v;
563
564  rtems_interrupt_enable(lvl);
565
566  return 0;
567}
Note: See TracBrowser for help on using the repository browser.