diff options
Diffstat (limited to 'arch/powerpc/mm/slb.c')
-rw-r--r-- | arch/powerpc/mm/slb.c | 35 |
1 files changed, 32 insertions, 3 deletions
diff --git a/arch/powerpc/mm/slb.c b/arch/powerpc/mm/slb.c index bbd2c512ee05..27922dff8b94 100644 --- a/arch/powerpc/mm/slb.c +++ b/arch/powerpc/mm/slb.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <asm/smp.h> | 25 | #include <asm/smp.h> |
26 | #include <asm/firmware.h> | 26 | #include <asm/firmware.h> |
27 | #include <linux/compiler.h> | 27 | #include <linux/compiler.h> |
28 | #include <asm/udbg.h> | ||
28 | 29 | ||
29 | #ifdef DEBUG | 30 | #ifdef DEBUG |
30 | #define DBG(fmt...) udbg_printf(fmt) | 31 | #define DBG(fmt...) udbg_printf(fmt) |
@@ -148,6 +149,35 @@ void slb_vmalloc_update(void) | |||
148 | slb_flush_and_rebolt(); | 149 | slb_flush_and_rebolt(); |
149 | } | 150 | } |
150 | 151 | ||
152 | /* Helper function to compare esids. There are four cases to handle. | ||
153 | * 1. The system is not 1T segment size capable. Use the GET_ESID compare. | ||
154 | * 2. The system is 1T capable, both addresses are < 1T, use the GET_ESID compare. | ||
155 | * 3. The system is 1T capable, only one of the two addresses is > 1T. This is not a match. | ||
156 | * 4. The system is 1T capable, both addresses are > 1T, use the GET_ESID_1T macro to compare. | ||
157 | */ | ||
158 | static inline int esids_match(unsigned long addr1, unsigned long addr2) | ||
159 | { | ||
160 | int esid_1t_count; | ||
161 | |||
162 | /* System is not 1T segment size capable. */ | ||
163 | if (!cpu_has_feature(CPU_FTR_1T_SEGMENT)) | ||
164 | return (GET_ESID(addr1) == GET_ESID(addr2)); | ||
165 | |||
166 | esid_1t_count = (((addr1 >> SID_SHIFT_1T) != 0) + | ||
167 | ((addr2 >> SID_SHIFT_1T) != 0)); | ||
168 | |||
169 | /* both addresses are < 1T */ | ||
170 | if (esid_1t_count == 0) | ||
171 | return (GET_ESID(addr1) == GET_ESID(addr2)); | ||
172 | |||
173 | /* One address < 1T, the other > 1T. Not a match */ | ||
174 | if (esid_1t_count == 1) | ||
175 | return 0; | ||
176 | |||
177 | /* Both addresses are > 1T. */ | ||
178 | return (GET_ESID_1T(addr1) == GET_ESID_1T(addr2)); | ||
179 | } | ||
180 | |||
151 | /* Flush all user entries from the segment table of the current processor. */ | 181 | /* Flush all user entries from the segment table of the current processor. */ |
152 | void switch_slb(struct task_struct *tsk, struct mm_struct *mm) | 182 | void switch_slb(struct task_struct *tsk, struct mm_struct *mm) |
153 | { | 183 | { |
@@ -193,15 +223,14 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm) | |||
193 | return; | 223 | return; |
194 | slb_allocate(pc); | 224 | slb_allocate(pc); |
195 | 225 | ||
196 | if (GET_ESID(pc) == GET_ESID(stack)) | 226 | if (esids_match(pc,stack)) |
197 | return; | 227 | return; |
198 | 228 | ||
199 | if (is_kernel_addr(stack)) | 229 | if (is_kernel_addr(stack)) |
200 | return; | 230 | return; |
201 | slb_allocate(stack); | 231 | slb_allocate(stack); |
202 | 232 | ||
203 | if ((GET_ESID(pc) == GET_ESID(unmapped_base)) | 233 | if (esids_match(pc,unmapped_base) || esids_match(stack,unmapped_base)) |
204 | || (GET_ESID(stack) == GET_ESID(unmapped_base))) | ||
205 | return; | 234 | return; |
206 | 235 | ||
207 | if (is_kernel_addr(unmapped_base)) | 236 | if (is_kernel_addr(unmapped_base)) |