1 | /* $Id$ */ |
---|
2 | |
---|
3 | /* |
---|
4 | * Routines to manipulate e500 TLBs; TLB0 (fixed 4k page size) |
---|
5 | * is not very useful so we mostly focus on TLB1 (variable page size). |
---|
6 | * |
---|
7 | * TLB0's 256 entries are 2-way set associative which means that |
---|
8 | * only 2 entries for page index numbers with matching 7 LSBs |
---|
9 | * are available. |
---|
10 | * |
---|
11 | * E.g., look at EA = 0xAAAyy000. 0xAAAyy is the page index. |
---|
12 | * |
---|
13 | * The least-significant 7 bits in 'yy' determine the 'way' |
---|
14 | * in the TLB 0 array. At most two EAs with matching 'yy' bits |
---|
15 | * (the 7 LSBs, that is) can be mapped with TLB0 since there |
---|
16 | * are only two entries per 'way'. |
---|
17 | * |
---|
18 | * Since this is a real-time OS we want to stay away from |
---|
19 | * software TLB replacement. |
---|
20 | */ |
---|
21 | |
---|
22 | /* |
---|
23 | * Authorship |
---|
24 | * ---------- |
---|
25 | * This software was created by |
---|
26 | * Till Straumann <strauman@slac.stanford.edu>, 2005-2007, |
---|
27 | * Stanford Linear Accelerator Center, Stanford University. |
---|
28 | * |
---|
29 | * Acknowledgement of sponsorship |
---|
30 | * ------------------------------ |
---|
31 | * This software was produced by |
---|
32 | * the Stanford Linear Accelerator Center, Stanford University, |
---|
33 | * under Contract DE-AC03-76SFO0515 with the Department of Energy. |
---|
34 | * |
---|
35 | * Government disclaimer of liability |
---|
36 | * ---------------------------------- |
---|
37 | * Neither the United States nor the United States Department of Energy, |
---|
38 | * nor any of their employees, makes any warranty, express or implied, or |
---|
39 | * assumes any legal liability or responsibility for the accuracy, |
---|
40 | * completeness, or usefulness of any data, apparatus, product, or process |
---|
41 | * disclosed, or represents that its use would not infringe privately owned |
---|
42 | * rights. |
---|
43 | * |
---|
44 | * Stanford disclaimer of liability |
---|
45 | * -------------------------------- |
---|
46 | * Stanford University makes no representations or warranties, express or |
---|
47 | * implied, nor assumes any liability for the use of this software. |
---|
48 | * |
---|
49 | * Stanford disclaimer of copyright |
---|
50 | * -------------------------------- |
---|
51 | * Stanford University, owner of the copyright, hereby disclaims its |
---|
52 | * copyright and all other rights in this software. Hence, anyone may |
---|
53 | * freely use it for any purpose without restriction. |
---|
54 | * |
---|
55 | * Maintenance of notices |
---|
56 | * ---------------------- |
---|
57 | * In the interest of clarity regarding the origin and status of this |
---|
58 | * SLAC software, this and all the preceding Stanford University notices |
---|
59 | * are to remain affixed to any copy or derivative of this software made |
---|
60 | * or distributed by the recipient and are to be affixed to any copy of |
---|
61 | * software made or distributed by the recipient that contains a copy or |
---|
62 | * derivative of this software. |
---|
63 | * |
---|
64 | * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 |
---|
65 | */ |
---|
66 | |
---|
67 | /* 8450 MSR definitions; note that there are *substantial* differences |
---|
68 | * compared to classic powerpc; in particular, IS/DS are *different* |
---|
69 | * from IR/DR; the e500 MMU can not be switched off! |
---|
70 | * |
---|
71 | * Also: To disable/enable all external interrupts, CE and EE must both be |
---|
72 | * controlled. |
---|
73 | */ |
---|
74 | #include <rtems.h> |
---|
75 | #include <rtems/bspIo.h> |
---|
76 | #include <inttypes.h> |
---|
77 | #include <stdio.h> |
---|
78 | |
---|
79 | #include "e500_mmu.h" |
---|
80 | |
---|
81 | #if 0 |
---|
82 | #define MSR_UCLE (1<<(63-37)) /* User-mode cache lock PPC: ? */ |
---|
83 | #define MSR_SPE (1<<(63-38)) /* SPE enable PPC: VE (altivec) */ |
---|
84 | #define MSR_WE (1<<(63-45)) /* Wait state enable PPC: POW (pwr. mgmt) */ |
---|
85 | #define MSR_CE (1<<(63-46)) /* Critical-interrupt enable PPC: TGPR (TLBupdt in use) */ |
---|
86 | #define MSR_EE (1<<(63-48)) /* External-interrupt enable PPC: EE */ |
---|
87 | #define MSR_PR (1<<(63-49)) /* User mode PPC: PR */ |
---|
88 | #define MSR_ME (1<<(63-51)) /* Machine-check enable PPC: ME */ |
---|
89 | #define MSR_UBLE (1<<(63-53)) /* User BTB lock enable PPC: SE (sstep enable) */ |
---|
90 | #define MSR_DE (1<<(63-54)) /* Debug-interrupt enable PPC: BE (br. tr. enbl) */ |
---|
91 | #define MSR_IS (1<<(63-58)) /* Instruction address space PPC: IR (inst. MMU enbl) */ |
---|
92 | #define MSR_DS (1<<(63-59)) /* Data address space PPC: DR (data MMU enbl) */ |
---|
93 | #define MSR_PMM (1<<(63-61)) /* Performance-monitor mark PPC: ? */ |
---|
94 | #endif |
---|
95 | |
---|
96 | /* Bit definitions for MAS registers */ |
---|
97 | #define SPR_MAS0 624 |
---|
98 | #define MAS0_TLBSEL ( 1 << (63-35)) /* Which TLB to access */ |
---|
99 | #define MAS0_ESEL(n) ( (0xf & (n)) << (63-47)) /* Selected TLB entry */ |
---|
100 | #define MAS0_ESEL_RD(m) ( ((m) >> (63-47)) & 0xf ) |
---|
101 | #define MAS0_NV ( 1 << (63-63)) /* Next victim */ |
---|
102 | |
---|
103 | #define SPR_MAS1 625 |
---|
104 | #define MAS1_V ( 1 << (63-32)) /* Entry valid */ |
---|
105 | #define MAS1_IPROT ( 1 << (63-33)) /* Invalidate protect */ |
---|
106 | #define MAS1_TID(n) ( (0xff & (n)) << (63-47)) /* Translation ID */ |
---|
107 | #define MAS1_TID_GET(n) ( ((n) >> (63-47)) & 0xfff) |
---|
108 | |
---|
109 | #define MAS1_TS ( 1 << (63-51)) /* Translation space */ |
---|
110 | #define MAS1_TSIZE(n) ( (0xf & (n)) << (63-55)) /* Translation ID */ |
---|
111 | #define MAS1_TSIZE_GET(n) ( ((n)>>(63-55)) & 0xf) |
---|
112 | |
---|
113 | #define SPR_MAS2 626 |
---|
114 | #define MAS2_EPN(n) ( (((1<<21)-1)&(n)) << (63-51)) /* EPN */ |
---|
115 | #define MAS2_EPN_GET(n) (((n)>>(63-51)) & 0xfffff) |
---|
116 | #define MAS2_X0 ( 1 << (63-57)) /* Attr. 0 */ |
---|
117 | #define MAS2_X1 ( 1 << (63-58)) /* Attr. 1 */ |
---|
118 | #define MAS2_W ( 1 << (63-59)) /* Write-through */ |
---|
119 | #define MAS2_I ( 1 << (63-60)) /* Cache-inhibited */ |
---|
120 | #define MAS2_M ( 1 << (63-61)) /* Memory-coherence req. */ |
---|
121 | #define MAS2_G ( 1 << (63-62)) /* Guarded */ |
---|
122 | #define MAS2_E ( 1 << (63-63)) /* Little-endian */ |
---|
123 | #define MAS2_ATTR(x) ( (x) & 0x7f ) |
---|
124 | #define MAS2_ATTR_GET(x) ( (x) & 0x7f ) |
---|
125 | |
---|
126 | #define SPR_MAS3 627 |
---|
127 | #define MAS3_RPN(n) ( (((1<<21)-1)&(n)) << (63-51)) /* RPN */ |
---|
128 | #define MAS3_RPN_GET(n) (((n)>>(63-51)) & 0xfffff) |
---|
129 | #define MAS3_U0 ( 1 << (63-54)) /* User attr. 0 */ |
---|
130 | #define MAS3_U1 ( 1 << (63-55)) /* User attr. 1 */ |
---|
131 | #define MAS3_U2 ( 1 << (63-56)) /* User attr. 2 */ |
---|
132 | #define MAS3_U3 ( 1 << (63-57)) /* User attr. 3 */ |
---|
133 | #define MAS3_UX ( 1 << (63-58)) /* User exec. */ |
---|
134 | #define MAS3_SX ( 1 << (63-59)) /* Super exec. */ |
---|
135 | #define MAS3_UW ( 1 << (63-60)) /* User write */ |
---|
136 | #define MAS3_SW ( 1 << (63-61)) /* Super write */ |
---|
137 | #define MAS3_UR ( 1 << (63-62)) /* User read */ |
---|
138 | #define MAS3_SR ( 1 << (63-63)) /* Super read */ |
---|
139 | |
---|
140 | #define MAS3_PERM(n) ( (n) & 0x3ff ) |
---|
141 | #define MAS3_PERM_GET(n) ( (n) & 0x3ff ) |
---|
142 | |
---|
143 | #define SPR_MAS4 628 |
---|
144 | #define MAS4_TLBSELD ( 1 << (63-35)) /* TLBSEL default */ |
---|
145 | #define MAS4_TIDSELD(n) ( (0x3 & (n)) << (63-47)) /* TID default */ |
---|
146 | #define MAS4_TSIZED(n) ( (0xf & (n)) << (63-55)) /* TSIZE default */ |
---|
147 | #define MAS4_X0D MAS2_X0 |
---|
148 | #define MAS4_X1D MAS2_X1 |
---|
149 | #define MAS4_WD MAS2_W |
---|
150 | #define MAS4_ID MAS2_I |
---|
151 | #define MAS4_MD MAS2_M |
---|
152 | #define MAS4_GD MAS2_G |
---|
153 | #define MAS4_ED MAS2_E |
---|
154 | |
---|
155 | #define SPR_MAS6 630 |
---|
156 | #define MAS6_SPID0(n) ( (0xff & (n)) << (63-55)) /* PID used for search */ |
---|
157 | #define MAS6_SAS ( 1 << (63-63)) /* AS for search */ |
---|
158 | |
---|
159 | #define SPR_PID0 48 |
---|
160 | #define SPR_PID1 633 |
---|
161 | #define SPR_PID2 634 |
---|
162 | |
---|
163 | #define TLBIVAX_TLBSEL (1<<(63-60)) |
---|
164 | #define TLBIVAX_INV_ALL (1<<(63-61)) |
---|
165 | |
---|
166 | #define E500_TLB_ATTR_WIMGE(x) ((x)&0x7f) /* includes user bits */ |
---|
167 | #define E500_TLB_ATTR_WIMGE_GET(x) ((x)&0x7f) |
---|
168 | #define E500_TLB_ATTR_TS (1<<7) |
---|
169 | #define E500_TLB_ATTR_PERM(x) (((x)&0x3ff)<<8) |
---|
170 | #define E500_TLB_ATTR_PERM_GET(x) (((x)>>8)&0x3ff) |
---|
171 | #define E500_TLB_ATTR_TID(x) (((x)&0xfff)<<20) |
---|
172 | #define E500_TLB_ATTR_TID_GET(x) (((x)>>20)&0xfff) |
---|
173 | |
---|
174 | |
---|
175 | #ifdef DEBUG |
---|
176 | #define STATIC |
---|
177 | #else |
---|
178 | #define STATIC static |
---|
179 | #endif |
---|
180 | |
---|
181 | /* Factory to generate inline macros for accessing the MAS registers */ |
---|
182 | #define __RDWRMAS(mas,rmas) \ |
---|
183 | static inline uint32_t _read_MAS##mas(void) \ |
---|
184 | { uint32_t x; asm volatile("mfspr %0, %1": "=r"(x):"i"(rmas)); return x; } \ |
---|
185 | static inline void _write_MAS##mas(uint32_t x) \ |
---|
186 | { asm volatile("mtspr %1, %0":: "r"(x),"i"(rmas)); } |
---|
187 | |
---|
188 | __RDWRMAS(0,SPR_MAS0) |
---|
189 | __RDWRMAS(1,SPR_MAS1) |
---|
190 | __RDWRMAS(2,SPR_MAS2) |
---|
191 | __RDWRMAS(3,SPR_MAS3) |
---|
192 | __RDWRMAS(4,SPR_MAS4) |
---|
193 | __RDWRMAS(6,SPR_MAS6) |
---|
194 | |
---|
195 | #undef __RDWRMAS |
---|
196 | |
---|
197 | static int initialized = 0; |
---|
198 | |
---|
199 | E500_tlb_va_cache_t rtems_e500_tlb_va_cache[16] = { {{0}},}; |
---|
200 | |
---|
201 | /* Since it is likely that these routines are used during |
---|
202 | * early initialization when stdio is not available yet |
---|
203 | * we provide a helper that resorts to 'printk()' |
---|
204 | */ |
---|
205 | static void |
---|
206 | myprintf(FILE *f, char *fmt, ...) |
---|
207 | { |
---|
208 | va_list ap; |
---|
209 | va_start(ap, fmt); |
---|
210 | |
---|
211 | if (!f || !_impure_ptr->__sdidinit) { |
---|
212 | /* |
---|
213 | * Might be called at an early stage when |
---|
214 | * stdio is not yet initialized. |
---|
215 | */ |
---|
216 | vprintk(fmt,ap); |
---|
217 | } else { |
---|
218 | vfprintf(f,fmt,ap); |
---|
219 | } |
---|
220 | va_end(ap); |
---|
221 | } |
---|
222 | |
---|
223 | |
---|
224 | void |
---|
225 | rtems_e500_dmptlbc(FILE *f) |
---|
226 | { |
---|
227 | int i; |
---|
228 | if ( !initialized ) { |
---|
229 | myprintf(stderr,"TLB cache not initialized\n"); |
---|
230 | return; |
---|
231 | } |
---|
232 | for ( i=0; i<16; i++ ) { |
---|
233 | if ( !rtems_e500_tlb_va_cache[i].att.v ) |
---|
234 | continue; |
---|
235 | myprintf(f,"#%2i: TID 0x%03x, TS %i, ea 0x%08x .. 0x%08x\n", |
---|
236 | i, |
---|
237 | rtems_e500_tlb_va_cache[i].va.va_tid, |
---|
238 | rtems_e500_tlb_va_cache[i].att.ts, |
---|
239 | rtems_e500_tlb_va_cache[i].va.va_epn<<12, |
---|
240 | (rtems_e500_tlb_va_cache[i].va.va_epn<<12) + (1024<<(2*rtems_e500_tlb_va_cache[i].att.sz))-1); |
---|
241 | myprintf(f,"PA 0x%08"PRIx32", PERM 0x%03x, WIMGE 0x%02x\n", |
---|
242 | rtems_e500_tlb_va_cache[i].rpn<<12, |
---|
243 | rtems_e500_tlb_va_cache[i].att.perm, |
---|
244 | rtems_e500_tlb_va_cache[i].att.wimge); |
---|
245 | } |
---|
246 | } |
---|
247 | |
---|
248 | #define E500_SELTLB_1 0x1000 |
---|
249 | |
---|
250 | static void seltlb(rtems_e500_tlb_idx key) |
---|
251 | { |
---|
252 | int idx = key & ~E500_SELTLB_1; |
---|
253 | |
---|
254 | if ( key & E500_SELTLB_1 ) { |
---|
255 | _write_MAS0( MAS0_TLBSEL | MAS0_ESEL(idx) ); |
---|
256 | } else { |
---|
257 | _write_MAS0( (idx & 128) ? MAS0_ESEL(1) : MAS0_ESEL(0) ); |
---|
258 | _write_MAS2( MAS2_EPN( idx & 127 ) ); |
---|
259 | } |
---|
260 | } |
---|
261 | |
---|
262 | /* |
---|
263 | * Read a TLB entry from the hardware; if it is a TLB1 entry |
---|
264 | * then the current settings are stored in the |
---|
265 | * rtems_e500_tlb_va_cache[] structure. |
---|
266 | * |
---|
267 | * The routine can perform this operation quietly or |
---|
268 | * print information to a file. |
---|
269 | * |
---|
270 | * 'sel': which TLB array to use; TLB0 (4k) if zero, |
---|
271 | * TLB1 (variable) if nonzero. |
---|
272 | * 'idx': which TLB entry to access. |
---|
273 | * 'quiet': perform operation silently (no info printed) |
---|
274 | * if nonzero. |
---|
275 | * 'f': open FILE where to print information. May be |
---|
276 | * NULL in which case 'stdout' is used. |
---|
277 | * |
---|
278 | * RETURNS: |
---|
279 | * 0: success; TLB entry is VALID |
---|
280 | * +1: success but TLB entry is INVALID |
---|
281 | * < 0: error (-1: invalid argument) |
---|
282 | */ |
---|
283 | int |
---|
284 | rtems_e500_prtlb(rtems_e500_tlb_idx key, int quiet, FILE *f) |
---|
285 | { |
---|
286 | uint32_t mas0, mas1, mas2, mas3; |
---|
287 | rtems_interrupt_level lvl; |
---|
288 | E500_tlb_va_cache_t *tlb; |
---|
289 | E500_tlb_va_cache_t buf; |
---|
290 | int sel, idx; |
---|
291 | |
---|
292 | sel = (key & E500_SELTLB_1) ? 1 : 0; |
---|
293 | idx = key & ~E500_SELTLB_1; |
---|
294 | |
---|
295 | if ( idx < 0 || idx > 255 || ( idx > 15 && sel ) ) |
---|
296 | return -1; |
---|
297 | |
---|
298 | rtems_interrupt_disable(lvl); |
---|
299 | |
---|
300 | seltlb( key ); |
---|
301 | |
---|
302 | asm volatile("tlbre"); |
---|
303 | |
---|
304 | mas0 = _read_MAS0(); |
---|
305 | mas1 = _read_MAS1(); |
---|
306 | mas2 = _read_MAS2(); |
---|
307 | mas3 = _read_MAS3(); |
---|
308 | |
---|
309 | rtems_interrupt_enable(lvl); |
---|
310 | |
---|
311 | tlb = sel ? rtems_e500_tlb_va_cache + idx : &buf; |
---|
312 | |
---|
313 | if ( (tlb->att.v = (MAS1_V & mas1) ? 1 : 0) ) { |
---|
314 | tlb->va.va_epn = MAS2_EPN_GET(mas2); |
---|
315 | tlb->rpn = MAS3_RPN_GET(mas3); |
---|
316 | tlb->va.va_tid = MAS1_TID_GET(mas1); |
---|
317 | tlb->att.ts = (MAS1_TS & mas1) ? 1 : 0; |
---|
318 | tlb->att.sz = sel ? MAS1_TSIZE_GET(mas1) : 1 /* 4k size */; |
---|
319 | tlb->att.wimge = MAS2_ATTR_GET(mas2); |
---|
320 | tlb->att.perm = MAS3_PERM_GET(mas3); |
---|
321 | } |
---|
322 | |
---|
323 | if ( tlb->att.v ) { |
---|
324 | if ( !quiet ) { |
---|
325 | /* |
---|
326 | "TLB[1] Entry # 0 spans EA range 0x00000000 .. 0x00000000 |
---|
327 | "Mapping: VA [TS 0/TID 0x00/EPN 0x00000] -> RPN 0x00000" |
---|
328 | "Size: TSIZE 0x0 ( 4^ts KiB = 000000 KiB = 0x00000000 B) |
---|
329 | "Attributes: PERM 0x000 (ux/sx/uw/sw/ur/sr) WIMGE 0x00 IPROT 0" |
---|
330 | */ |
---|
331 | myprintf(f, |
---|
332 | "TLB[%i] Entry # %d spans EA range 0x%08x .. 0x%08x\r\n", |
---|
333 | sel, |
---|
334 | idx, |
---|
335 | (tlb->va.va_epn << 12), |
---|
336 | (tlb->va.va_epn << 12) + (1024<<(2*tlb->att.sz)) - 1 |
---|
337 | ); |
---|
338 | |
---|
339 | myprintf(f, |
---|
340 | "Mapping: VA [TS %d/TID 0x%02x/EPN 0x%05x] -> RPN 0x%05"PRIx32"\r\n", |
---|
341 | tlb->att.ts, tlb->va.va_tid, tlb->va.va_epn, tlb->rpn |
---|
342 | ); |
---|
343 | myprintf(f, |
---|
344 | "Size: TSIZE 0x%x ( 4^ts KiB = %6d KiB = 0x%08x B)\r\n", |
---|
345 | tlb->att.sz, (1<<(2*tlb->att.sz)), (1024<<(2*tlb->att.sz)) |
---|
346 | ); |
---|
347 | myprintf(f, |
---|
348 | "Attributes: PERM 0x%03x (ux/sx/uw/sw/ur/sr) WIMGE 0x%02x IPROT %i\r\n", |
---|
349 | tlb->att.perm, tlb->att.wimge, (sel && (mas1 & MAS1_IPROT) ? 1 : 0) |
---|
350 | ); |
---|
351 | myprintf(f, |
---|
352 | "EA range 0x%08x .. 0x%08x\r\n", |
---|
353 | (tlb->va.va_epn << 12), |
---|
354 | (tlb->va.va_epn << 12) + (1024<<(2*tlb->att.sz)) - 1 |
---|
355 | ); |
---|
356 | } |
---|
357 | } else { |
---|
358 | if ( !quiet ) { |
---|
359 | myprintf(f, "TLB[%i] Entry #%i <OFF> (size 0x%x = 0x%xb)\n", sel, idx, tlb->att.sz, (1024<<(2*tlb->att.sz))); |
---|
360 | } |
---|
361 | return 1; |
---|
362 | } |
---|
363 | return 0; |
---|
364 | } |
---|
365 | |
---|
366 | /* Initialize cache; verify that TLB0 is unused; |
---|
367 | * |
---|
368 | * RETURNS: zero on success, nonzero on error (TLB0 |
---|
369 | * seems to be in use); in this case the |
---|
370 | * driver will refuse to change TLB1 entries |
---|
371 | * (other than disabling them). |
---|
372 | */ |
---|
373 | int rtems_e500_initlb() |
---|
374 | { |
---|
375 | int i; |
---|
376 | int rval = 0; |
---|
377 | for (i=0; i<16; i++) |
---|
378 | rtems_e500_prtlb(E500_SELTLB_1 | i, 1, 0); |
---|
379 | for (i=0; i<256; i++) { |
---|
380 | /* refuse to enable operations that change TLB entries |
---|
381 | * if anything in TLB[0] is valid (because we currently |
---|
382 | * don't check against overlap with TLB[0] when we |
---|
383 | * write a new entry). |
---|
384 | */ |
---|
385 | if ( rtems_e500_prtlb(E500_SELTLB_0 | i, 1, 0) <=0 ) { |
---|
386 | myprintf(stderr,"WARNING: 4k TLB #%i seems to be valid; UNSUPPORTED configuration\n", i); |
---|
387 | rval = -1; |
---|
388 | } |
---|
389 | } |
---|
390 | if ( !rval ) |
---|
391 | initialized = 1; |
---|
392 | return rval; |
---|
393 | } |
---|
394 | |
---|
395 | /* |
---|
396 | * Write TLB1 entry (can also be used to disable an entry). |
---|
397 | * |
---|
398 | * The routine checks against the cached data in |
---|
399 | * rtems_e500_tlb_va[] to prevent the user from generating |
---|
400 | * overlapping entries. |
---|
401 | * |
---|
402 | * 'idx': TLB 1 entry # to manipulate |
---|
403 | * 'ea': Effective address (must be page aligned) |
---|
404 | * 'pa': Physical address (must be page aligned) |
---|
405 | * 'sz': Page size selector; page size is |
---|
406 | * 1024 * 2^(2*sz) bytes. |
---|
407 | * 'sz' may also be one of the following: |
---|
408 | * - page size in bytes ( >= 1024 ); the selector |
---|
409 | * value is then computed by this routine. |
---|
410 | * However, 'sz' must be a valid page size |
---|
411 | * or -1 will be returned. |
---|
412 | * - a value < 0 to invalidate/disable the |
---|
413 | * TLB entry. |
---|
414 | * 'attr': Page attributes; ORed combination of WIMGE, |
---|
415 | * PERMissions, TID and TS. Use ATTR_xxx macros |
---|
416 | * |
---|
417 | * RETURNS: 0 on success, nonzero on error: |
---|
418 | * |
---|
419 | * >0: requested mapping would overlap with |
---|
420 | * existing mapping in other entry. Return |
---|
421 | * value gives conflicting entry + 1; i.e., |
---|
422 | * if a value of 4 is returned then the request |
---|
423 | * conflicts with existing mapping in entry 3. |
---|
424 | * -1: invalid argument |
---|
425 | * -3: driver not initialized (or initialization |
---|
426 | * failed because TLB0 is in use). |
---|
427 | * <0: other error |
---|
428 | * |
---|
429 | */ |
---|
430 | #define E500_TLB_ATTR_WIMGE(x) ((x)&0x7f) /* includes user bits */ |
---|
431 | #define E500_TLB_ATTR_WIMGE_GET(x) ((x)&0x7f) |
---|
432 | #define E500_TLB_ATTR_TS (1<<7) |
---|
433 | #define E500_TLB_ATTR_PERM(x) (((x)&0x3ff)<<8) |
---|
434 | #define E500_TLB_ATTR_PERM_GET(x) (((x)>>8)&0x3ff) |
---|
435 | #define E500_TLB_ATTR_TID(x) (((x)&0xfff)<<20) |
---|
436 | #define E500_TLB_ATTR_TID_GET(x) (((x)>>20)&0xfff) |
---|
437 | |
---|
438 | int |
---|
439 | rtems_e500_wrtlb(int idx, uint32_t ea, uint32_t pa, int sz, uint32_t attr) |
---|
440 | { |
---|
441 | uint32_t mas1, mas2, mas3, mas4; |
---|
442 | uint32_t tid, msk; |
---|
443 | int lkup; |
---|
444 | rtems_interrupt_level lvl; |
---|
445 | |
---|
446 | if ( sz >= 1024 ) { |
---|
447 | /* Assume they literally specify a size */ |
---|
448 | msk = sz; |
---|
449 | sz = 0; |
---|
450 | while ( msk != (1024<<(2*sz)) ) { |
---|
451 | if ( ++sz > 15 ) { |
---|
452 | return -1; |
---|
453 | } |
---|
454 | } |
---|
455 | /* OK, acceptable */ |
---|
456 | } |
---|
457 | |
---|
458 | msk = sz > 0 ? (1024<<(2*sz)) - 1 : 0; |
---|
459 | |
---|
460 | if ( !initialized && sz > 0 ) { |
---|
461 | myprintf(stderr,"TLB driver not initialized; refuse to enable any entry\n"); |
---|
462 | return -3; |
---|
463 | } |
---|
464 | |
---|
465 | if ( (ea & msk) || (pa & msk) ) { |
---|
466 | myprintf(stderr,"Misaligned ea or pa\n"); |
---|
467 | return -1; |
---|
468 | } |
---|
469 | |
---|
470 | if ( idx < 0 || idx > 15 ) |
---|
471 | return -1; |
---|
472 | |
---|
473 | if ( sz > 15 ) { |
---|
474 | /* but e500v1 doesn't support all 16 sizes!! */ |
---|
475 | /* FIXME: we should inquire about this CPU's |
---|
476 | * capabilities... |
---|
477 | */ |
---|
478 | return -1; |
---|
479 | } |
---|
480 | |
---|
481 | tid = E500_TLB_ATTR_TID_GET(attr); |
---|
482 | |
---|
483 | mas1 = (attr & E500_TLB_ATTR_TS) ? MAS1_TS : 0; |
---|
484 | |
---|
485 | if ( sz >=0 ) { |
---|
486 | lkup = rtems_e500_matchtlb(ea, tid, mas1, sz); |
---|
487 | |
---|
488 | if ( lkup < -1 ) { |
---|
489 | /* some error */ |
---|
490 | return lkup; |
---|
491 | } |
---|
492 | |
---|
493 | if ( lkup >= 0 && lkup != idx ) { |
---|
494 | myprintf(stderr,"TLB[1] #%i overlaps with requested mapping\n", lkup); |
---|
495 | rtems_e500_prtlb( E500_SELTLB_1 | lkup, 0, stderr); |
---|
496 | return lkup+1; |
---|
497 | } |
---|
498 | } |
---|
499 | |
---|
500 | /* OK to proceed */ |
---|
501 | mas1 |= MAS1_IPROT | MAS1_TID(tid); |
---|
502 | |
---|
503 | if ( sz >= 0 ) |
---|
504 | mas1 |= MAS1_V | MAS1_TSIZE(sz); |
---|
505 | |
---|
506 | mas2 = MAS2_EPN( ea>>12 ) | E500_TLB_ATTR_WIMGE(attr); |
---|
507 | mas3 = MAS3_RPN( pa>>12 ) | E500_TLB_ATTR_PERM_GET(attr); |
---|
508 | /* mas4 is not really relevant; we don't use TLB replacement */ |
---|
509 | mas4 = MAS4_TLBSELD | MAS4_TIDSELD(0) | MAS4_TSIZED(9) | MAS4_ID | MAS4_GD; |
---|
510 | |
---|
511 | rtems_interrupt_disable(lvl); |
---|
512 | |
---|
513 | seltlb(idx | E500_SELTLB_1); |
---|
514 | |
---|
515 | _write_MAS1(mas1); |
---|
516 | _write_MAS2(mas2); |
---|
517 | _write_MAS3(mas3); |
---|
518 | _write_MAS4(mas4); |
---|
519 | |
---|
520 | asm volatile( |
---|
521 | " sync \n" |
---|
522 | " isync \n" |
---|
523 | " tlbwe \n" |
---|
524 | " sync \n" |
---|
525 | " isync \n" |
---|
526 | ); |
---|
527 | |
---|
528 | rtems_interrupt_enable(lvl); |
---|
529 | |
---|
530 | /* update cache */ |
---|
531 | rtems_e500_prtlb( E500_SELTLB_1 | idx, 1, 0); |
---|
532 | |
---|
533 | return 0; |
---|
534 | } |
---|
535 | |
---|
536 | /* |
---|
537 | * Check if a ts/tid/ea/sz mapping overlaps |
---|
538 | * with an existing entry. |
---|
539 | * |
---|
540 | * ASSUMPTION: all TLB0 (fixed 4k pages) are invalid and always unused. |
---|
541 | * |
---|
542 | * NOTE: 'sz' is the 'logarithmic' size selector; the page size |
---|
543 | * is 1024*2^(2*sz). |
---|
544 | * |
---|
545 | * RETURNS: |
---|
546 | * >= 0: index of TLB1 entry that already provides a mapping |
---|
547 | * which overlaps within the ea range. |
---|
548 | * -1: SUCCESS (no conflicting entry found) |
---|
549 | * <=-2: ERROR (invalid input) |
---|
550 | */ |
---|
551 | int rtems_e500_matchtlb(uint32_t ea, uint32_t tid, int ts, int sz) |
---|
552 | { |
---|
553 | int i; |
---|
554 | uint32_t m,a; |
---|
555 | E500_tlb_va_cache_t *tlb; |
---|
556 | |
---|
557 | if ( sz < 0 || sz > 15 ) |
---|
558 | return -4; |
---|
559 | |
---|
560 | sz = (1024<<(2*sz)); |
---|
561 | |
---|
562 | if ( !initialized ) { |
---|
563 | /* cache not initialized */ |
---|
564 | return -3; |
---|
565 | } |
---|
566 | |
---|
567 | if ( ea & (sz-1) ) { |
---|
568 | /* misaligned ea */ |
---|
569 | return -2; |
---|
570 | } |
---|
571 | |
---|
572 | if ( ts ) |
---|
573 | ts = 1; |
---|
574 | |
---|
575 | for ( i=0, tlb=rtems_e500_tlb_va_cache; i<16; i++, tlb++ ) { |
---|
576 | if ( ! tlb->att.v ) |
---|
577 | continue; |
---|
578 | if ( tlb->att.ts != ts ) |
---|
579 | continue; |
---|
580 | if ( tlb->va.va_tid && tlb->va.va_tid != tid ) |
---|
581 | continue; |
---|
582 | /* TID and TS match a valid entry */ |
---|
583 | m = (1024<<(2*tlb->att.sz)) - 1; |
---|
584 | /* calculate starting address of this entry */ |
---|
585 | a = tlb->va.va_epn<<12; |
---|
586 | if ( ea <= a + m && ea + sz -1 >= a ) { |
---|
587 | /* overlap */ |
---|
588 | return i; |
---|
589 | } |
---|
590 | } |
---|
591 | return -1; |
---|
592 | } |
---|
593 | |
---|
594 | /* Find TLB index that maps 'ea/as' combination |
---|
595 | * |
---|
596 | * RETURNS: index 'key'; i.e., the index number plus |
---|
597 | * a bit (E500_SELTLB_1) which indicates whether |
---|
598 | * the mapping was found in TLB0 (4k fixed page |
---|
599 | * size) or in TLB1 (variable page size). |
---|
600 | * |
---|
601 | * On error (no mapping) -1 is returned. |
---|
602 | */ |
---|
603 | rtems_e500_tlb_idx |
---|
604 | rtems_e500_ftlb(uint32_t ea, int as) |
---|
605 | { |
---|
606 | uint32_t pid, mas0, mas1; |
---|
607 | int i, rval = -1; |
---|
608 | rtems_interrupt_level lvl; |
---|
609 | |
---|
610 | rtems_interrupt_disable(lvl); |
---|
611 | |
---|
612 | for ( i=0; i<3; i++ ) { |
---|
613 | switch (i) { |
---|
614 | case 0: asm volatile("mfspr %0, %1":"=r"(pid):"i"(SPR_PID0)); break; |
---|
615 | case 1: asm volatile("mfspr %0, %1":"=r"(pid):"i"(SPR_PID1)); break; |
---|
616 | case 2: asm volatile("mfspr %0, %1":"=r"(pid):"i"(SPR_PID2)); break; |
---|
617 | default: |
---|
618 | goto bail; |
---|
619 | } |
---|
620 | |
---|
621 | _write_MAS6( MAS6_SPID0(pid) | (as ? MAS6_SAS : 0 ) ); |
---|
622 | |
---|
623 | asm volatile("tlbsx 0, %0"::"r"(ea)); |
---|
624 | |
---|
625 | mas1 = _read_MAS1(); |
---|
626 | |
---|
627 | if ( (MAS1_V & mas1) ) { |
---|
628 | mas0 = _read_MAS0(); |
---|
629 | if ( MAS0_TLBSEL & mas0 ) { |
---|
630 | /* TLB1 */ |
---|
631 | rval = MAS0_ESEL_RD(mas0) | E500_SELTLB_1; |
---|
632 | } else { |
---|
633 | rval = (ea >> (63-51)) | (( MAS0_NV & mas0 ) ? 180 : 0 ) ; |
---|
634 | } |
---|
635 | break; |
---|
636 | } |
---|
637 | } |
---|
638 | |
---|
639 | bail: |
---|
640 | rtems_interrupt_enable(lvl); |
---|
641 | return rval; |
---|
642 | } |
---|
643 | |
---|
644 | /* Mark TLB entry as invalid ('disabled'). Unlike |
---|
645 | * rtems_e500_wrtlb() with a negative size argument |
---|
646 | * this routine also can disable TLB0 entries. |
---|
647 | * |
---|
648 | * 'key': TLB entry (index) ORed with selector bit |
---|
649 | * (0 for TLB0, E500_SELTLB_1 for TLB1). |
---|
650 | * |
---|
651 | * RETURNS: zero on success, nonzero on error (TLB |
---|
652 | * unchanged). |
---|
653 | * |
---|
654 | * NOTE: If a TLB1 entry is disabled the associated |
---|
655 | * entry in rtems_e500_va_cache[] is also |
---|
656 | * marked as disabled. |
---|
657 | */ |
---|
658 | int |
---|
659 | rtems_e500_clrtlb(rtems_e500_tlb_idx key) |
---|
660 | { |
---|
661 | rtems_e500_tlb_idx k0; |
---|
662 | rtems_interrupt_level lvl; |
---|
663 | |
---|
664 | /* minimal guard against bad key */ |
---|
665 | if ( key < 0 ) |
---|
666 | return -1; |
---|
667 | |
---|
668 | if ( (key & E500_SELTLB_1) ) { |
---|
669 | if ( (key & ~E500_SELTLB_1) > 15 ) { |
---|
670 | myprintf(stderr,"Invalid TLB index; TLB1 index must be < 16\n"); |
---|
671 | return -1; |
---|
672 | } |
---|
673 | } else if ( key > 255 ) { |
---|
674 | myprintf(stderr,"Invalid TLB index; TLB0 index must be < 256\n"); |
---|
675 | return -1; |
---|
676 | } |
---|
677 | |
---|
678 | /* Must not invalidate page 0 which holds vectors, text etc... */ |
---|
679 | k0 = rtems_e500_ftlb(0, 0); |
---|
680 | if ( -1 == k0 ) { |
---|
681 | myprintf(stderr,"tlbivax; something's fishy - I don't find mapping for addr. 0\n"); |
---|
682 | return -1; |
---|
683 | } |
---|
684 | |
---|
685 | /* NOTE: we assume PID is ignored, and AS is 0 */ |
---|
686 | if ( k0 == key ) { |
---|
687 | myprintf(stderr,"Refuse to invalidate page holding addr 0 (always needed)\n"); |
---|
688 | return -1; |
---|
689 | } |
---|
690 | |
---|
691 | rtems_interrupt_disable(lvl); |
---|
692 | |
---|
693 | seltlb(key); |
---|
694 | |
---|
695 | asm volatile("tlbre"); |
---|
696 | |
---|
697 | /* read old entries */ |
---|
698 | _write_MAS1( _read_MAS1() & ~MAS1_V ); |
---|
699 | |
---|
700 | asm volatile( |
---|
701 | " sync \n" |
---|
702 | " isync \n" |
---|
703 | " tlbwe \n" |
---|
704 | " sync \n" |
---|
705 | " isync \n" |
---|
706 | ); |
---|
707 | |
---|
708 | /* update cache */ |
---|
709 | if ( E500_SELTLB_1 & key ) |
---|
710 | rtems_e500_tlb_va_cache[ (~E500_SELTLB_1 & key) ].att.v = 0; |
---|
711 | |
---|
712 | rtems_interrupt_enable(lvl); |
---|
713 | |
---|
714 | return 0; |
---|
715 | } |
---|