diff options
Diffstat (limited to 'arch/sparc/kernel/wuf.S')
-rw-r--r-- | arch/sparc/kernel/wuf.S | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/arch/sparc/kernel/wuf.S b/arch/sparc/kernel/wuf.S new file mode 100644 index 000000000000..d1a266bf103a --- /dev/null +++ b/arch/sparc/kernel/wuf.S | |||
@@ -0,0 +1,360 @@ | |||
1 | /* $Id: wuf.S,v 1.39 2000/01/08 16:38:18 anton Exp $ | ||
2 | * wuf.S: Window underflow trap handler for the Sparc. | ||
3 | * | ||
4 | * Copyright (C) 1995 David S. Miller | ||
5 | */ | ||
6 | |||
7 | #include <asm/contregs.h> | ||
8 | #include <asm/page.h> | ||
9 | #include <asm/ptrace.h> | ||
10 | #include <asm/psr.h> | ||
11 | #include <asm/smp.h> | ||
12 | #include <asm/asi.h> | ||
13 | #include <asm/winmacro.h> | ||
14 | #include <asm/asmmacro.h> | ||
15 | #include <asm/thread_info.h> | ||
16 | |||
17 | /* Just like the overflow handler we define macros for registers | ||
18 | * with fixed meanings in this routine. | ||
19 | */ | ||
20 | #define t_psr l0 | ||
21 | #define t_pc l1 | ||
22 | #define t_npc l2 | ||
23 | #define t_wim l3 | ||
24 | /* Don't touch the above registers or else you die horribly... */ | ||
25 | |||
26 | /* Now macros for the available scratch registers in this routine. */ | ||
27 | #define twin_tmp1 l4 | ||
28 | #define twin_tmp2 l5 | ||
29 | |||
30 | #define curptr g6 | ||
31 | |||
32 | .text | ||
33 | .align 4 | ||
34 | |||
35 | /* The trap entry point has executed the following: | ||
36 | * | ||
37 | * rd %psr, %l0 | ||
38 | * rd %wim, %l3 | ||
39 | * b fill_window_entry | ||
40 | * andcc %l0, PSR_PS, %g0 | ||
41 | */ | ||
42 | |||
43 | /* Datum current_thread_info->uwinmask contains at all times a bitmask | ||
44 | * where if any user windows are active, at least one bit will | ||
45 | * be set in to mask. If no user windows are active, the bitmask | ||
46 | * will be all zeroes. | ||
47 | */ | ||
48 | |||
49 | /* To get an idea of what has just happened to cause this | ||
50 | * trap take a look at this diagram: | ||
51 | * | ||
52 | * 1 2 3 4 <-- Window number | ||
53 | * ---------- | ||
54 | * T O W I <-- Symbolic name | ||
55 | * | ||
56 | * O == the window that execution was in when | ||
57 | * the restore was attempted | ||
58 | * | ||
59 | * T == the trap itself has save'd us into this | ||
60 | * window | ||
61 | * | ||
62 | * W == this window is the one which is now invalid | ||
63 | * and must be made valid plus loaded from the | ||
64 | * stack | ||
65 | * | ||
66 | * I == this window will be the invalid one when we | ||
67 | * are done and return from trap if successful | ||
68 | */ | ||
69 | |||
70 | /* BEGINNING OF PATCH INSTRUCTIONS */ | ||
71 | |||
72 | /* On 7-window Sparc the boot code patches fnwin_patch1 | ||
73 | * with the following instruction. | ||
74 | */ | ||
75 | .globl fnwin_patch1_7win, fnwin_patch2_7win | ||
76 | fnwin_patch1_7win: srl %t_wim, 6, %twin_tmp2 | ||
77 | fnwin_patch2_7win: and %twin_tmp1, 0x7f, %twin_tmp1 | ||
78 | /* END OF PATCH INSTRUCTIONS */ | ||
79 | |||
80 | .globl fill_window_entry, fnwin_patch1, fnwin_patch2 | ||
81 | fill_window_entry: | ||
82 | /* LOCATION: Window 'T' */ | ||
83 | |||
84 | /* Compute what the new %wim is going to be if we retrieve | ||
85 | * the proper window off of the stack. | ||
86 | */ | ||
87 | sll %t_wim, 1, %twin_tmp1 | ||
88 | fnwin_patch1: srl %t_wim, 7, %twin_tmp2 | ||
89 | or %twin_tmp1, %twin_tmp2, %twin_tmp1 | ||
90 | fnwin_patch2: and %twin_tmp1, 0xff, %twin_tmp1 | ||
91 | |||
92 | wr %twin_tmp1, 0x0, %wim /* Make window 'I' invalid */ | ||
93 | |||
94 | andcc %t_psr, PSR_PS, %g0 | ||
95 | be fwin_from_user | ||
96 | restore %g0, %g0, %g0 /* Restore to window 'O' */ | ||
97 | |||
98 | /* Trapped from kernel, we trust that the kernel does not | ||
99 | * 'over restore' sorta speak and just grab the window | ||
100 | * from the stack and return. Easy enough. | ||
101 | */ | ||
102 | fwin_from_kernel: | ||
103 | /* LOCATION: Window 'O' */ | ||
104 | |||
105 | restore %g0, %g0, %g0 | ||
106 | |||
107 | /* LOCATION: Window 'W' */ | ||
108 | |||
109 | LOAD_WINDOW(sp) /* Load it up */ | ||
110 | |||
111 | /* Spin the wheel... */ | ||
112 | save %g0, %g0, %g0 | ||
113 | save %g0, %g0, %g0 | ||
114 | /* I'd like to buy a vowel please... */ | ||
115 | |||
116 | /* LOCATION: Window 'T' */ | ||
117 | |||
118 | /* Now preserve the condition codes in %psr, pause, and | ||
119 | * return from trap. This is the simplest case of all. | ||
120 | */ | ||
121 | wr %t_psr, 0x0, %psr | ||
122 | WRITE_PAUSE | ||
123 | |||
124 | jmp %t_pc | ||
125 | rett %t_npc | ||
126 | |||
127 | fwin_from_user: | ||
128 | /* LOCATION: Window 'O' */ | ||
129 | |||
130 | restore %g0, %g0, %g0 /* Restore to window 'W' */ | ||
131 | |||
132 | /* LOCATION: Window 'W' */ | ||
133 | |||
134 | /* Branch to the architecture specific stack validation | ||
135 | * routine. They can be found below... | ||
136 | */ | ||
137 | .globl fwin_mmu_patchme | ||
138 | fwin_mmu_patchme: b sun4c_fwin_stackchk | ||
139 | andcc %sp, 0x7, %g0 | ||
140 | |||
141 | #define STACK_OFFSET (THREAD_SIZE - TRACEREG_SZ - STACKFRAME_SZ) | ||
142 | |||
143 | fwin_user_stack_is_bolixed: | ||
144 | /* LOCATION: Window 'W' */ | ||
145 | |||
146 | /* Place a pt_regs frame on the kernel stack, save back | ||
147 | * to the trap window and call c-code to deal with this. | ||
148 | */ | ||
149 | LOAD_CURRENT(l4, l5) | ||
150 | |||
151 | sethi %hi(STACK_OFFSET), %l5 | ||
152 | or %l5, %lo(STACK_OFFSET), %l5 | ||
153 | add %l4, %l5, %l5 | ||
154 | |||
155 | /* Store globals into pt_regs frame. */ | ||
156 | STORE_PT_GLOBALS(l5) | ||
157 | STORE_PT_YREG(l5, g3) | ||
158 | |||
159 | /* Save current in a global while we change windows. */ | ||
160 | mov %l4, %curptr | ||
161 | |||
162 | save %g0, %g0, %g0 | ||
163 | |||
164 | /* LOCATION: Window 'O' */ | ||
165 | |||
166 | rd %psr, %g3 /* Read %psr in live user window */ | ||
167 | mov %fp, %g4 /* Save bogus frame pointer. */ | ||
168 | |||
169 | save %g0, %g0, %g0 | ||
170 | |||
171 | /* LOCATION: Window 'T' */ | ||
172 | |||
173 | sethi %hi(STACK_OFFSET), %l5 | ||
174 | or %l5, %lo(STACK_OFFSET), %l5 | ||
175 | add %curptr, %l5, %sp | ||
176 | |||
177 | /* Build rest of pt_regs. */ | ||
178 | STORE_PT_INS(sp) | ||
179 | STORE_PT_PRIV(sp, t_psr, t_pc, t_npc) | ||
180 | |||
181 | /* re-set trap time %wim value */ | ||
182 | wr %t_wim, 0x0, %wim | ||
183 | |||
184 | /* Fix users window mask and buffer save count. */ | ||
185 | mov 0x1, %g5 | ||
186 | sll %g5, %g3, %g5 | ||
187 | st %g5, [%curptr + TI_UWINMASK] ! one live user window still | ||
188 | st %g0, [%curptr + TI_W_SAVED] ! no windows in the buffer | ||
189 | |||
190 | wr %t_psr, PSR_ET, %psr ! enable traps | ||
191 | nop | ||
192 | call window_underflow_fault | ||
193 | mov %g4, %o0 | ||
194 | |||
195 | b ret_trap_entry | ||
196 | clr %l6 | ||
197 | |||
198 | fwin_user_stack_is_ok: | ||
199 | /* LOCATION: Window 'W' */ | ||
200 | |||
201 | /* The users stack area is kosher and mapped, load the | ||
202 | * window and fall through to the finish up routine. | ||
203 | */ | ||
204 | LOAD_WINDOW(sp) | ||
205 | |||
206 | /* Round and round she goes... */ | ||
207 | save %g0, %g0, %g0 /* Save to window 'O' */ | ||
208 | save %g0, %g0, %g0 /* Save to window 'T' */ | ||
209 | /* Where she'll trap nobody knows... */ | ||
210 | |||
211 | /* LOCATION: Window 'T' */ | ||
212 | |||
213 | fwin_user_finish_up: | ||
214 | /* LOCATION: Window 'T' */ | ||
215 | |||
216 | wr %t_psr, 0x0, %psr | ||
217 | WRITE_PAUSE | ||
218 | |||
219 | jmp %t_pc | ||
220 | rett %t_npc | ||
221 | |||
222 | /* Here come the architecture specific checks for stack. | ||
223 | * mappings. Note that unlike the window overflow handler | ||
224 | * we only need to check whether the user can read from | ||
225 | * the appropriate addresses. Also note that we are in | ||
226 | * an invalid window which will be loaded, and this means | ||
227 | * that until we actually load the window up we are free | ||
228 | * to use any of the local registers contained within. | ||
229 | * | ||
230 | * On success these routine branch to fwin_user_stack_is_ok | ||
231 | * if the area at %sp is user readable and the window still | ||
232 | * needs to be loaded, else fwin_user_finish_up if the | ||
233 | * routine has done the loading itself. On failure (bogus | ||
234 | * user stack) the routine shall branch to the label called | ||
235 | * fwin_user_stack_is_bolixed. | ||
236 | * | ||
237 | * Contrary to the arch-specific window overflow stack | ||
238 | * check routines in wof.S, these routines are free to use | ||
239 | * any of the local registers they want to as this window | ||
240 | * does not belong to anyone at this point, however the | ||
241 | * outs and ins are still verboten as they are part of | ||
242 | * 'someone elses' window possibly. | ||
243 | */ | ||
244 | |||
245 | .align 4 | ||
246 | .globl sun4c_fwin_stackchk | ||
247 | sun4c_fwin_stackchk: | ||
248 | /* LOCATION: Window 'W' */ | ||
249 | |||
250 | /* Caller did 'andcc %sp, 0x7, %g0' */ | ||
251 | be 1f | ||
252 | and %sp, 0xfff, %l0 ! delay slot | ||
253 | |||
254 | b,a fwin_user_stack_is_bolixed | ||
255 | |||
256 | /* See if we have to check the sanity of one page or two */ | ||
257 | 1: | ||
258 | add %l0, 0x38, %l0 | ||
259 | sra %sp, 29, %l5 | ||
260 | add %l5, 0x1, %l5 | ||
261 | andncc %l5, 0x1, %g0 | ||
262 | be 1f | ||
263 | andncc %l0, 0xff8, %g0 | ||
264 | |||
265 | b,a fwin_user_stack_is_bolixed /* %sp is in vma hole, yuck */ | ||
266 | |||
267 | 1: | ||
268 | be sun4c_fwin_onepage /* Only one page to check */ | ||
269 | lda [%sp] ASI_PTE, %l1 | ||
270 | sun4c_fwin_twopages: | ||
271 | add %sp, 0x38, %l0 | ||
272 | sra %l0, 29, %l5 | ||
273 | add %l5, 0x1, %l5 | ||
274 | andncc %l5, 0x1, %g0 | ||
275 | be 1f | ||
276 | lda [%l0] ASI_PTE, %l1 | ||
277 | |||
278 | b,a fwin_user_stack_is_bolixed /* Second page in vma hole */ | ||
279 | |||
280 | 1: | ||
281 | srl %l1, 29, %l1 | ||
282 | andcc %l1, 0x4, %g0 | ||
283 | bne sun4c_fwin_onepage | ||
284 | lda [%sp] ASI_PTE, %l1 | ||
285 | |||
286 | b,a fwin_user_stack_is_bolixed /* Second page has bad perms */ | ||
287 | |||
288 | sun4c_fwin_onepage: | ||
289 | srl %l1, 29, %l1 | ||
290 | andcc %l1, 0x4, %g0 | ||
291 | bne fwin_user_stack_is_ok | ||
292 | nop | ||
293 | |||
294 | /* A page had bad page permissions, losing... */ | ||
295 | b,a fwin_user_stack_is_bolixed | ||
296 | |||
297 | .globl srmmu_fwin_stackchk | ||
298 | srmmu_fwin_stackchk: | ||
299 | /* LOCATION: Window 'W' */ | ||
300 | |||
301 | /* Caller did 'andcc %sp, 0x7, %g0' */ | ||
302 | bne fwin_user_stack_is_bolixed | ||
303 | sethi %hi(PAGE_OFFSET), %l5 | ||
304 | |||
305 | /* Check if the users stack is in kernel vma, then our | ||
306 | * trial and error technique below would succeed for | ||
307 | * the 'wrong' reason. | ||
308 | */ | ||
309 | mov AC_M_SFSR, %l4 | ||
310 | cmp %l5, %sp | ||
311 | bleu fwin_user_stack_is_bolixed | ||
312 | lda [%l4] ASI_M_MMUREGS, %g0 ! clear fault status | ||
313 | |||
314 | /* The technique is, turn off faults on this processor, | ||
315 | * just let the load rip, then check the sfsr to see if | ||
316 | * a fault did occur. Then we turn on fault traps again | ||
317 | * and branch conditionally based upon what happened. | ||
318 | */ | ||
319 | lda [%g0] ASI_M_MMUREGS, %l5 ! read mmu-ctrl reg | ||
320 | or %l5, 0x2, %l5 ! turn on no-fault bit | ||
321 | sta %l5, [%g0] ASI_M_MMUREGS ! store it | ||
322 | |||
323 | /* Cross fingers and go for it. */ | ||
324 | LOAD_WINDOW(sp) | ||
325 | |||
326 | /* A penny 'saved'... */ | ||
327 | save %g0, %g0, %g0 | ||
328 | save %g0, %g0, %g0 | ||
329 | /* Is a BADTRAP earned... */ | ||
330 | |||
331 | /* LOCATION: Window 'T' */ | ||
332 | |||
333 | lda [%g0] ASI_M_MMUREGS, %twin_tmp1 ! load mmu-ctrl again | ||
334 | andn %twin_tmp1, 0x2, %twin_tmp1 ! clear no-fault bit | ||
335 | sta %twin_tmp1, [%g0] ASI_M_MMUREGS ! store it | ||
336 | |||
337 | mov AC_M_SFAR, %twin_tmp2 | ||
338 | lda [%twin_tmp2] ASI_M_MMUREGS, %g0 ! read fault address | ||
339 | |||
340 | mov AC_M_SFSR, %twin_tmp2 | ||
341 | lda [%twin_tmp2] ASI_M_MMUREGS, %twin_tmp2 ! read fault status | ||
342 | andcc %twin_tmp2, 0x2, %g0 ! did fault occur? | ||
343 | |||
344 | bne 1f ! yep, cleanup | ||
345 | nop | ||
346 | |||
347 | wr %t_psr, 0x0, %psr | ||
348 | nop | ||
349 | b fwin_user_finish_up + 0x4 | ||
350 | nop | ||
351 | |||
352 | /* Did I ever tell you about my window lobotomy? | ||
353 | * anyways... fwin_user_stack_is_bolixed expects | ||
354 | * to be in window 'W' so make it happy or else | ||
355 | * we watchdog badly. | ||
356 | */ | ||
357 | 1: | ||
358 | restore %g0, %g0, %g0 | ||
359 | b fwin_user_stack_is_bolixed ! oh well | ||
360 | restore %g0, %g0, %g0 | ||