diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /include/asm-ppc64/mmu_context.h |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'include/asm-ppc64/mmu_context.h')
-rw-r--r-- | include/asm-ppc64/mmu_context.h | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/include/asm-ppc64/mmu_context.h b/include/asm-ppc64/mmu_context.h new file mode 100644 index 000000000000..c2e8e0466383 --- /dev/null +++ b/include/asm-ppc64/mmu_context.h | |||
@@ -0,0 +1,169 @@ | |||
1 | #ifndef __PPC64_MMU_CONTEXT_H | ||
2 | #define __PPC64_MMU_CONTEXT_H | ||
3 | |||
4 | #include <linux/config.h> | ||
5 | #include <linux/kernel.h> | ||
6 | #include <linux/mm.h> | ||
7 | #include <asm/mmu.h> | ||
8 | #include <asm/cputable.h> | ||
9 | |||
10 | /* | ||
11 | * Copyright (C) 2001 PPC 64 Team, IBM Corp | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version | ||
16 | * 2 of the License, or (at your option) any later version. | ||
17 | */ | ||
18 | |||
19 | /* | ||
20 | * Every architecture must define this function. It's the fastest | ||
21 | * way of searching a 140-bit bitmap where the first 100 bits are | ||
22 | * unlikely to be set. It's guaranteed that at least one of the 140 | ||
23 | * bits is cleared. | ||
24 | */ | ||
25 | static inline int sched_find_first_bit(unsigned long *b) | ||
26 | { | ||
27 | if (unlikely(b[0])) | ||
28 | return __ffs(b[0]); | ||
29 | if (unlikely(b[1])) | ||
30 | return __ffs(b[1]) + 64; | ||
31 | return __ffs(b[2]) + 128; | ||
32 | } | ||
33 | |||
34 | static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) | ||
35 | { | ||
36 | } | ||
37 | |||
38 | #define NO_CONTEXT 0 | ||
39 | #define MAX_CONTEXT (0x100000-1) | ||
40 | |||
41 | extern int init_new_context(struct task_struct *tsk, struct mm_struct *mm); | ||
42 | extern void destroy_context(struct mm_struct *mm); | ||
43 | |||
44 | extern void switch_stab(struct task_struct *tsk, struct mm_struct *mm); | ||
45 | extern void switch_slb(struct task_struct *tsk, struct mm_struct *mm); | ||
46 | |||
47 | /* | ||
48 | * switch_mm is the entry point called from the architecture independent | ||
49 | * code in kernel/sched.c | ||
50 | */ | ||
51 | static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, | ||
52 | struct task_struct *tsk) | ||
53 | { | ||
54 | if (!cpu_isset(smp_processor_id(), next->cpu_vm_mask)) | ||
55 | cpu_set(smp_processor_id(), next->cpu_vm_mask); | ||
56 | |||
57 | /* No need to flush userspace segments if the mm doesnt change */ | ||
58 | if (prev == next) | ||
59 | return; | ||
60 | |||
61 | #ifdef CONFIG_ALTIVEC | ||
62 | if (cpu_has_feature(CPU_FTR_ALTIVEC)) | ||
63 | asm volatile ("dssall"); | ||
64 | #endif /* CONFIG_ALTIVEC */ | ||
65 | |||
66 | if (cpu_has_feature(CPU_FTR_SLB)) | ||
67 | switch_slb(tsk, next); | ||
68 | else | ||
69 | switch_stab(tsk, next); | ||
70 | } | ||
71 | |||
72 | #define deactivate_mm(tsk,mm) do { } while (0) | ||
73 | |||
74 | /* | ||
75 | * After we have set current->mm to a new value, this activates | ||
76 | * the context for the new mm so we see the new mappings. | ||
77 | */ | ||
78 | static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next) | ||
79 | { | ||
80 | unsigned long flags; | ||
81 | |||
82 | local_irq_save(flags); | ||
83 | switch_mm(prev, next, current); | ||
84 | local_irq_restore(flags); | ||
85 | } | ||
86 | |||
87 | /* VSID allocation | ||
88 | * =============== | ||
89 | * | ||
90 | * We first generate a 36-bit "proto-VSID". For kernel addresses this | ||
91 | * is equal to the ESID, for user addresses it is: | ||
92 | * (context << 15) | (esid & 0x7fff) | ||
93 | * | ||
94 | * The two forms are distinguishable because the top bit is 0 for user | ||
95 | * addresses, whereas the top two bits are 1 for kernel addresses. | ||
96 | * Proto-VSIDs with the top two bits equal to 0b10 are reserved for | ||
97 | * now. | ||
98 | * | ||
99 | * The proto-VSIDs are then scrambled into real VSIDs with the | ||
100 | * multiplicative hash: | ||
101 | * | ||
102 | * VSID = (proto-VSID * VSID_MULTIPLIER) % VSID_MODULUS | ||
103 | * where VSID_MULTIPLIER = 268435399 = 0xFFFFFC7 | ||
104 | * VSID_MODULUS = 2^36-1 = 0xFFFFFFFFF | ||
105 | * | ||
106 | * This scramble is only well defined for proto-VSIDs below | ||
107 | * 0xFFFFFFFFF, so both proto-VSID and actual VSID 0xFFFFFFFFF are | ||
108 | * reserved. VSID_MULTIPLIER is prime, so in particular it is | ||
109 | * co-prime to VSID_MODULUS, making this a 1:1 scrambling function. | ||
110 | * Because the modulus is 2^n-1 we can compute it efficiently without | ||
111 | * a divide or extra multiply (see below). | ||
112 | * | ||
113 | * This scheme has several advantages over older methods: | ||
114 | * | ||
115 | * - We have VSIDs allocated for every kernel address | ||
116 | * (i.e. everything above 0xC000000000000000), except the very top | ||
117 | * segment, which simplifies several things. | ||
118 | * | ||
119 | * - We allow for 15 significant bits of ESID and 20 bits of | ||
120 | * context for user addresses. i.e. 8T (43 bits) of address space for | ||
121 | * up to 1M contexts (although the page table structure and context | ||
122 | * allocation will need changes to take advantage of this). | ||
123 | * | ||
124 | * - The scramble function gives robust scattering in the hash | ||
125 | * table (at least based on some initial results). The previous | ||
126 | * method was more susceptible to pathological cases giving excessive | ||
127 | * hash collisions. | ||
128 | */ | ||
129 | |||
130 | /* | ||
131 | * WARNING - If you change these you must make sure the asm | ||
132 | * implementations in slb_allocate(), do_stab_bolted and mmu.h | ||
133 | * (ASM_VSID_SCRAMBLE macro) are changed accordingly. | ||
134 | * | ||
135 | * You'll also need to change the precomputed VSID values in head.S | ||
136 | * which are used by the iSeries firmware. | ||
137 | */ | ||
138 | |||
139 | static inline unsigned long vsid_scramble(unsigned long protovsid) | ||
140 | { | ||
141 | #if 0 | ||
142 | /* The code below is equivalent to this function for arguments | ||
143 | * < 2^VSID_BITS, which is all this should ever be called | ||
144 | * with. However gcc is not clever enough to compute the | ||
145 | * modulus (2^n-1) without a second multiply. */ | ||
146 | return ((protovsid * VSID_MULTIPLIER) % VSID_MODULUS); | ||
147 | #else /* 1 */ | ||
148 | unsigned long x; | ||
149 | |||
150 | x = protovsid * VSID_MULTIPLIER; | ||
151 | x = (x >> VSID_BITS) + (x & VSID_MODULUS); | ||
152 | return (x + ((x+1) >> VSID_BITS)) & VSID_MODULUS; | ||
153 | #endif /* 1 */ | ||
154 | } | ||
155 | |||
156 | /* This is only valid for addresses >= KERNELBASE */ | ||
157 | static inline unsigned long get_kernel_vsid(unsigned long ea) | ||
158 | { | ||
159 | return vsid_scramble(ea >> SID_SHIFT); | ||
160 | } | ||
161 | |||
162 | /* This is only valid for user addresses (which are below 2^41) */ | ||
163 | static inline unsigned long get_vsid(unsigned long context, unsigned long ea) | ||
164 | { | ||
165 | return vsid_scramble((context << USER_ESID_BITS) | ||
166 | | (ea >> SID_SHIFT)); | ||
167 | } | ||
168 | |||
169 | #endif /* __PPC64_MMU_CONTEXT_H */ | ||