patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / arch / ppc64 / kernel / stab.c
1 /*
2  * PowerPC64 Segment Translation Support.
3  *
4  * Dave Engebretsen and Mike Corrigan {engebret|mikejc}@us.ibm.com
5  *    Copyright (c) 2001 Dave Engebretsen
6  *
7  * Copyright (C) 2002 Anton Blanchard <anton@au.ibm.com>, IBM
8  * 
9  *      This program is free software; you can redistribute it and/or
10  *      modify it under the terms of the GNU General Public License
11  *      as published by the Free Software Foundation; either version
12  *      2 of the License, or (at your option) any later version.
13  */
14
15 #include <linux/config.h>
16 #include <asm/pgtable.h>
17 #include <asm/mmu.h>
18 #include <asm/mmu_context.h>
19 #include <asm/paca.h>
20 #include <asm/naca.h>
21 #include <asm/cputable.h>
22
23 static int make_ste(unsigned long stab, unsigned long esid, unsigned long vsid);
24 static void make_slbe(unsigned long esid, unsigned long vsid, int large,
25                       int kernel_segment);
26
27 static inline void slb_add_bolted(void)
28 {
29 #ifndef CONFIG_PPC_ISERIES
30         unsigned long esid = GET_ESID(VMALLOCBASE);
31         unsigned long vsid = get_kernel_vsid(VMALLOCBASE);
32
33         WARN_ON(!irqs_disabled());
34
35         /*
36          * Bolt in the first vmalloc segment. Since modules end
37          * up there it gets hit very heavily.
38          */
39         get_paca()->xStab_data.next_round_robin = 1;
40         make_slbe(esid, vsid, 0, 1);
41 #endif
42 }
43
44 /*
45  * Build an entry for the base kernel segment and put it into
46  * the segment table or SLB.  All other segment table or SLB
47  * entries are faulted in.
48  */
49 void stab_initialize(unsigned long stab)
50 {
51         unsigned long esid, vsid; 
52         int seg0_largepages = 0;
53
54         esid = GET_ESID(KERNELBASE);
55         vsid = get_kernel_vsid(esid << SID_SHIFT); 
56
57         if (cur_cpu_spec->cpu_features & CPU_FTR_16M_PAGE)
58                 seg0_largepages = 1;
59
60         if (cur_cpu_spec->cpu_features & CPU_FTR_SLB) {
61                 /* Invalidate the entire SLB & all the ERATS */
62 #ifdef CONFIG_PPC_ISERIES
63                 asm volatile("isync; slbia; isync":::"memory");
64 #else
65                 asm volatile("isync":::"memory");
66                 asm volatile("slbmte  %0,%0"::"r" (0) : "memory");
67                 asm volatile("isync; slbia; isync":::"memory");
68                 get_paca()->xStab_data.next_round_robin = 0;
69                 make_slbe(esid, vsid, seg0_largepages, 1);
70                 asm volatile("isync":::"memory");
71 #endif
72
73                 slb_add_bolted();
74         } else {
75                 asm volatile("isync; slbia; isync":::"memory");
76                 make_ste(stab, esid, vsid);
77
78                 /* Order update */
79                 asm volatile("sync":::"memory"); 
80         }
81 }
82
83 /* Both the segment table and SLB code uses the following cache */
84 #define NR_STAB_CACHE_ENTRIES 8
85 DEFINE_PER_CPU(long, stab_cache_ptr);
86 DEFINE_PER_CPU(long, stab_cache[NR_STAB_CACHE_ENTRIES]);
87
88 /*
89  * Segment table stuff
90  */
91
92 /*
93  * Create a segment table entry for the given esid/vsid pair.
94  */
95 static int make_ste(unsigned long stab, unsigned long esid, unsigned long vsid)
96 {
97         unsigned long entry, group, old_esid, castout_entry, i;
98         unsigned int global_entry;
99         STE *ste, *castout_ste;
100         unsigned long kernel_segment = (REGION_ID(esid << SID_SHIFT) != 
101                                         USER_REGION_ID);
102
103         /* Search the primary group first. */
104         global_entry = (esid & 0x1f) << 3;
105         ste = (STE *)(stab | ((esid & 0x1f) << 7)); 
106
107         /* Find an empty entry, if one exists. */
108         for (group = 0; group < 2; group++) {
109                 for (entry = 0; entry < 8; entry++, ste++) {
110                         if (!(ste->dw0.dw0.v)) {
111                                 ste->dw0.dword0 = 0;
112                                 ste->dw1.dword1 = 0;
113                                 ste->dw1.dw1.vsid = vsid;
114                                 ste->dw0.dw0.esid = esid;
115                                 ste->dw0.dw0.kp = 1;
116                                 if (!kernel_segment)
117                                         ste->dw0.dw0.ks = 1;
118                                 asm volatile("eieio":::"memory");
119                                 ste->dw0.dw0.v = 1;
120                                 return (global_entry | entry);
121                         }
122                 }
123                 /* Now search the secondary group. */
124                 global_entry = ((~esid) & 0x1f) << 3;
125                 ste = (STE *)(stab | (((~esid) & 0x1f) << 7)); 
126         }
127
128         /*
129          * Could not find empty entry, pick one with a round robin selection.
130          * Search all entries in the two groups.
131          */
132         castout_entry = get_paca()->xStab_data.next_round_robin;
133         for (i = 0; i < 16; i++) {
134                 if (castout_entry < 8) {
135                         global_entry = (esid & 0x1f) << 3;
136                         ste = (STE *)(stab | ((esid & 0x1f) << 7)); 
137                         castout_ste = ste + castout_entry;
138                 } else {
139                         global_entry = ((~esid) & 0x1f) << 3;
140                         ste = (STE *)(stab | (((~esid) & 0x1f) << 7)); 
141                         castout_ste = ste + (castout_entry - 8);
142                 }
143
144                 /* Dont cast out the first kernel segment */
145                 if (castout_ste->dw0.dw0.esid != GET_ESID(KERNELBASE))
146                         break;
147
148                 castout_entry = (castout_entry + 1) & 0xf;
149         }
150
151         get_paca()->xStab_data.next_round_robin = (castout_entry + 1) & 0xf;
152
153         /* Modify the old entry to the new value. */
154
155         /* Force previous translations to complete. DRENG */
156         asm volatile("isync" : : : "memory");
157
158         castout_ste->dw0.dw0.v = 0;
159         asm volatile("sync" : : : "memory");    /* Order update */
160
161         castout_ste->dw0.dword0 = 0;
162         castout_ste->dw1.dword1 = 0;
163         castout_ste->dw1.dw1.vsid = vsid;
164         old_esid = castout_ste->dw0.dw0.esid;
165         castout_ste->dw0.dw0.esid = esid;
166         castout_ste->dw0.dw0.kp = 1;
167         if (!kernel_segment)
168                 castout_ste->dw0.dw0.ks = 1;
169         asm volatile("eieio" : : : "memory");   /* Order update */
170         castout_ste->dw0.dw0.v  = 1;
171         asm volatile("slbie  %0" : : "r" (old_esid << SID_SHIFT)); 
172         /* Ensure completion of slbie */
173         asm volatile("sync" : : : "memory");
174
175         return (global_entry | (castout_entry & 0x7));
176 }
177
178 static inline void __ste_allocate(unsigned long esid, unsigned long vsid)
179 {
180         unsigned char stab_entry;
181         unsigned long offset;
182         int region_id = REGION_ID(esid << SID_SHIFT);
183
184         stab_entry = make_ste(get_paca()->xStab_data.virt, esid, vsid);
185
186         if (region_id != USER_REGION_ID)
187                 return;
188
189         offset = __get_cpu_var(stab_cache_ptr);
190         if (offset < NR_STAB_CACHE_ENTRIES)
191                 __get_cpu_var(stab_cache[offset++]) = stab_entry;
192         else
193                 offset = NR_STAB_CACHE_ENTRIES+1;
194         __get_cpu_var(stab_cache_ptr) = offset;
195 }
196
197 /*
198  * Allocate a segment table entry for the given ea.
199  */
200 int ste_allocate(unsigned long ea)
201 {
202         unsigned long vsid, esid;
203         mm_context_t context;
204
205         /* Check for invalid effective addresses. */
206         if (!IS_VALID_EA(ea))
207                 return 1;
208
209         /* Kernel or user address? */
210         if (REGION_ID(ea) >= KERNEL_REGION_ID) {
211                 vsid = get_kernel_vsid(ea);
212                 context = KERNEL_CONTEXT(ea);
213         } else {
214                 if (!current->mm)
215                         return 1;
216
217                 context = current->mm->context;
218                 vsid = get_vsid(context.id, ea);
219         }
220
221         esid = GET_ESID(ea);
222         __ste_allocate(esid, vsid);
223         /* Order update */
224         asm volatile("sync":::"memory");
225
226         return 0;
227 }
228
229 /*
230  * preload some userspace segments into the segment table.
231  */
232 static void preload_stab(struct task_struct *tsk, struct mm_struct *mm)
233 {
234         unsigned long pc = KSTK_EIP(tsk);
235         unsigned long stack = KSTK_ESP(tsk);
236         unsigned long unmapped_base;
237         unsigned long pc_esid = GET_ESID(pc);
238         unsigned long stack_esid = GET_ESID(stack);
239         unsigned long unmapped_base_esid;
240         unsigned long vsid;
241
242         if (test_tsk_thread_flag(tsk, TIF_32BIT))
243                 unmapped_base = TASK_UNMAPPED_BASE_USER32;
244         else
245                 unmapped_base = TASK_UNMAPPED_BASE_USER64;
246
247         unmapped_base_esid = GET_ESID(unmapped_base);
248
249         if (!IS_VALID_EA(pc) || (REGION_ID(pc) >= KERNEL_REGION_ID))
250                 return;
251         vsid = get_vsid(mm->context.id, pc);
252         __ste_allocate(pc_esid, vsid);
253
254         if (pc_esid == stack_esid)
255                 return;
256
257         if (!IS_VALID_EA(stack) || (REGION_ID(stack) >= KERNEL_REGION_ID))
258                 return;
259         vsid = get_vsid(mm->context.id, stack);
260         __ste_allocate(stack_esid, vsid);
261
262         if (pc_esid == unmapped_base_esid || stack_esid == unmapped_base_esid)
263                 return;
264
265         if (!IS_VALID_EA(unmapped_base) ||
266             (REGION_ID(unmapped_base) >= KERNEL_REGION_ID))
267                 return;
268         vsid = get_vsid(mm->context.id, unmapped_base);
269         __ste_allocate(unmapped_base_esid, vsid);
270
271         /* Order update */
272         asm volatile("sync" : : : "memory");
273 }
274
275 /* Flush all user entries from the segment table of the current processor. */
276 void flush_stab(struct task_struct *tsk, struct mm_struct *mm)
277 {
278         STE *stab = (STE *) get_paca()->xStab_data.virt;
279         STE *ste;
280         unsigned long offset = __get_cpu_var(stab_cache_ptr);
281
282         /* Force previous translations to complete. DRENG */
283         asm volatile("isync" : : : "memory");
284
285         if (offset <= NR_STAB_CACHE_ENTRIES) {
286                 int i;
287
288                 for (i = 0; i < offset; i++) {
289                         ste = stab + __get_cpu_var(stab_cache[i]);
290                         ste->dw0.dw0.v = 0;
291                 }
292         } else {
293                 unsigned long entry;
294
295                 /* Invalidate all entries. */
296                 ste = stab;
297
298                 /* Never flush the first entry. */
299                 ste += 1;
300                 for (entry = 1;
301                      entry < (PAGE_SIZE / sizeof(STE));
302                      entry++, ste++) {
303                         unsigned long ea;
304                         ea = ste->dw0.dw0.esid << SID_SHIFT;
305                         if (ea < KERNELBASE) {
306                                 ste->dw0.dw0.v = 0;
307                         }
308                 }
309         }
310
311         asm volatile("sync; slbia; sync":::"memory");
312
313         __get_cpu_var(stab_cache_ptr) = 0;
314
315         preload_stab(tsk, mm);
316 }
317
318 /*
319  * SLB stuff
320  */
321
322 /*
323  * Create a segment buffer entry for the given esid/vsid pair.
324  *
325  * NOTE: A context syncronising instruction is required before and after
326  * this, in the common case we use exception entry and rfid.
327  */
328 static void make_slbe(unsigned long esid, unsigned long vsid, int large,
329                       int kernel_segment)
330 {
331         unsigned long entry, castout_entry;
332         union {
333                 unsigned long word0;
334                 slb_dword0    data;
335         } esid_data;
336         union {
337                 unsigned long word0;
338                 slb_dword1    data;
339         } vsid_data;
340         struct paca_struct *lpaca = get_paca();
341
342         /*
343          * We take the next entry, round robin. Previously we tried
344          * to find a free slot first but that took too long. Unfortunately
345          * we dont have any LRU information to help us choose a slot.
346          */
347
348         /* 
349          * Never cast out the segment for our kernel stack. Since we
350          * dont invalidate the ERAT we could have a valid translation
351          * for the kernel stack during the first part of exception exit 
352          * which gets invalidated due to a tlbie from another cpu at a
353          * non recoverable point (after setting srr0/1) - Anton
354          *
355          * paca Ksave is always valid (even when on the interrupt stack)
356          * so we use that.
357          */
358         castout_entry = lpaca->xStab_data.next_round_robin;
359         do {
360                 entry = castout_entry;
361                 castout_entry++; 
362                 /*
363                  * We bolt in the first kernel segment and the first
364                  * vmalloc segment.
365                  */
366                 if (castout_entry >= SLB_NUM_ENTRIES)
367                         castout_entry = 2;
368                 asm volatile("slbmfee  %0,%1" : "=r" (esid_data) : "r" (entry));
369         } while (esid_data.data.v &&
370                  esid_data.data.esid == GET_ESID(lpaca->xKsave));
371
372         lpaca->xStab_data.next_round_robin = castout_entry;
373
374         /* slbie not needed as the previous mapping is still valid. */
375
376         /* 
377          * Write the new SLB entry.
378          */
379         vsid_data.word0 = 0;
380         vsid_data.data.vsid = vsid;
381         vsid_data.data.kp = 1;
382         if (large)
383                 vsid_data.data.l = 1;
384         if (kernel_segment)
385                 vsid_data.data.c = 1;
386         else
387                 vsid_data.data.ks = 1;
388
389         esid_data.word0 = 0;
390         esid_data.data.esid = esid;
391         esid_data.data.v = 1;
392         esid_data.data.index = entry;
393
394         /*
395          * No need for an isync before or after this slbmte. The exception
396          * we enter with and the rfid we exit with are context synchronizing.
397          */
398         asm volatile("slbmte  %0,%1" : : "r" (vsid_data), "r" (esid_data)); 
399 }
400
401 static inline void __slb_allocate(unsigned long esid, unsigned long vsid,
402                                   mm_context_t context)
403 {
404         int large = 0;
405         int region_id = REGION_ID(esid << SID_SHIFT);
406         unsigned long offset;
407
408         if (cur_cpu_spec->cpu_features & CPU_FTR_16M_PAGE) {
409                 if (region_id == KERNEL_REGION_ID)
410                         large = 1;
411                 else if (region_id == USER_REGION_ID)
412                         large = in_hugepage_area(context, esid << SID_SHIFT);
413         }
414
415         make_slbe(esid, vsid, large, region_id != USER_REGION_ID);
416
417         if (region_id != USER_REGION_ID)
418                 return;
419
420         offset = __get_cpu_var(stab_cache_ptr);
421         if (offset < NR_STAB_CACHE_ENTRIES)
422                 __get_cpu_var(stab_cache[offset++]) = esid;
423         else
424                 offset = NR_STAB_CACHE_ENTRIES+1;
425         __get_cpu_var(stab_cache_ptr) = offset;
426 }
427
428 /*
429  * Allocate a segment table entry for the given ea.
430  */
431 int slb_allocate(unsigned long ea)
432 {
433         unsigned long vsid, esid;
434         mm_context_t context;
435
436         /* Check for invalid effective addresses. */
437         if (unlikely(!IS_VALID_EA(ea)))
438                 return 1;
439
440         /* Kernel or user address? */
441         if (REGION_ID(ea) >= KERNEL_REGION_ID) {
442                 context = KERNEL_CONTEXT(ea);
443                 vsid = get_kernel_vsid(ea);
444         } else {
445                 if (unlikely(!current->mm))
446                         return 1;
447
448                 context = current->mm->context;
449                 vsid = get_vsid(context.id, ea);
450         }
451
452         esid = GET_ESID(ea);
453 #ifndef CONFIG_PPC_ISERIES
454         BUG_ON((esid << SID_SHIFT) == VMALLOCBASE);
455 #endif
456         __slb_allocate(esid, vsid, context);
457
458         return 0;
459 }
460
461 /*
462  * preload some userspace segments into the SLB.
463  */
464 static void preload_slb(struct task_struct *tsk, struct mm_struct *mm)
465 {
466         unsigned long pc = KSTK_EIP(tsk);
467         unsigned long stack = KSTK_ESP(tsk);
468         unsigned long unmapped_base;
469         unsigned long pc_esid = GET_ESID(pc);
470         unsigned long stack_esid = GET_ESID(stack);
471         unsigned long unmapped_base_esid;
472         unsigned long vsid;
473
474         if (test_tsk_thread_flag(tsk, TIF_32BIT))
475                 unmapped_base = TASK_UNMAPPED_BASE_USER32;
476         else
477                 unmapped_base = TASK_UNMAPPED_BASE_USER64;
478
479         unmapped_base_esid = GET_ESID(unmapped_base);
480
481         if (!IS_VALID_EA(pc) || (REGION_ID(pc) >= KERNEL_REGION_ID))
482                 return;
483         vsid = get_vsid(mm->context.id, pc);
484         __slb_allocate(pc_esid, vsid, mm->context);
485
486         if (pc_esid == stack_esid)
487                 return;
488
489         if (!IS_VALID_EA(stack) || (REGION_ID(stack) >= KERNEL_REGION_ID))
490                 return;
491         vsid = get_vsid(mm->context.id, stack);
492         __slb_allocate(stack_esid, vsid, mm->context);
493
494         if (pc_esid == unmapped_base_esid || stack_esid == unmapped_base_esid)
495                 return;
496
497         if (!IS_VALID_EA(unmapped_base) ||
498             (REGION_ID(unmapped_base) >= KERNEL_REGION_ID))
499                 return;
500         vsid = get_vsid(mm->context.id, unmapped_base);
501         __slb_allocate(unmapped_base_esid, vsid, mm->context);
502 }
503
504 /* Flush all user entries from the segment table of the current processor. */
505 void flush_slb(struct task_struct *tsk, struct mm_struct *mm)
506 {
507         unsigned long offset = __get_cpu_var(stab_cache_ptr);
508         union {
509                 unsigned long word0;
510                 slb_dword0 data;
511         } esid_data;
512
513         if (offset <= NR_STAB_CACHE_ENTRIES) {
514                 int i;
515                 asm volatile("isync" : : : "memory");
516                 for (i = 0; i < offset; i++) {
517                         esid_data.word0 = 0;
518                         esid_data.data.esid = __get_cpu_var(stab_cache[i]);
519                         BUG_ON(esid_data.data.esid == GET_ESID(VMALLOCBASE));
520                         asm volatile("slbie %0" : : "r" (esid_data));
521                 }
522                 asm volatile("isync" : : : "memory");
523         } else {
524                 asm volatile("isync; slbia; isync" : : : "memory");
525                 slb_add_bolted();
526         }
527
528         /* Workaround POWER5 < DD2.1 issue */
529         if (offset == 1 || offset > NR_STAB_CACHE_ENTRIES) {
530                 /* 
531                  * flush segment in EEH region, we dont normally access
532                  * addresses in this region.
533                  */
534                 esid_data.word0 = 0;
535                 esid_data.data.esid = EEH_REGION_ID;
536                 asm volatile("slbie %0" : : "r" (esid_data));
537         }
538
539         __get_cpu_var(stab_cache_ptr) = 0;
540
541         preload_slb(tsk, mm);
542 }