diff options
Diffstat (limited to 'arch/openrisc/kernel/entry.S')
-rw-r--r-- | arch/openrisc/kernel/entry.S | 1128 |
1 files changed, 1128 insertions, 0 deletions
diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S new file mode 100644 index 000000000000..d5f9c35a583f --- /dev/null +++ b/arch/openrisc/kernel/entry.S | |||
@@ -0,0 +1,1128 @@ | |||
1 | /* | ||
2 | * OpenRISC entry.S | ||
3 | * | ||
4 | * Linux architectural port borrowing liberally from similar works of | ||
5 | * others. All original copyrights apply as per the original source | ||
6 | * declaration. | ||
7 | * | ||
8 | * Modifications for the OpenRISC architecture: | ||
9 | * Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com> | ||
10 | * Copyright (C) 2005 Gyorgy Jeney <nog@bsemi.com> | ||
11 | * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se> | ||
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 | #include <linux/linkage.h> | ||
20 | |||
21 | #include <asm/processor.h> | ||
22 | #include <asm/unistd.h> | ||
23 | #include <asm/thread_info.h> | ||
24 | #include <asm/errno.h> | ||
25 | #include <asm/spr_defs.h> | ||
26 | #include <asm/page.h> | ||
27 | #include <asm/mmu.h> | ||
28 | #include <asm/pgtable.h> | ||
29 | #include <asm/asm-offsets.h> | ||
30 | |||
31 | #define DISABLE_INTERRUPTS(t1,t2) \ | ||
32 | l.mfspr t2,r0,SPR_SR ;\ | ||
33 | l.movhi t1,hi(~(SPR_SR_IEE|SPR_SR_TEE)) ;\ | ||
34 | l.ori t1,t1,lo(~(SPR_SR_IEE|SPR_SR_TEE)) ;\ | ||
35 | l.and t2,t2,t1 ;\ | ||
36 | l.mtspr r0,t2,SPR_SR | ||
37 | |||
38 | #define ENABLE_INTERRUPTS(t1) \ | ||
39 | l.mfspr t1,r0,SPR_SR ;\ | ||
40 | l.ori t1,t1,lo(SPR_SR_IEE|SPR_SR_TEE) ;\ | ||
41 | l.mtspr r0,t1,SPR_SR | ||
42 | |||
43 | /* =========================================================[ macros ]=== */ | ||
44 | |||
45 | /* | ||
46 | * We need to disable interrupts at beginning of RESTORE_ALL | ||
47 | * since interrupt might come in after we've loaded EPC return address | ||
48 | * and overwrite EPC with address somewhere in RESTORE_ALL | ||
49 | * which is of course wrong! | ||
50 | */ | ||
51 | |||
52 | #define RESTORE_ALL \ | ||
53 | DISABLE_INTERRUPTS(r3,r4) ;\ | ||
54 | l.lwz r3,PT_PC(r1) ;\ | ||
55 | l.mtspr r0,r3,SPR_EPCR_BASE ;\ | ||
56 | l.lwz r3,PT_SR(r1) ;\ | ||
57 | l.mtspr r0,r3,SPR_ESR_BASE ;\ | ||
58 | l.lwz r2,PT_GPR2(r1) ;\ | ||
59 | l.lwz r3,PT_GPR3(r1) ;\ | ||
60 | l.lwz r4,PT_GPR4(r1) ;\ | ||
61 | l.lwz r5,PT_GPR5(r1) ;\ | ||
62 | l.lwz r6,PT_GPR6(r1) ;\ | ||
63 | l.lwz r7,PT_GPR7(r1) ;\ | ||
64 | l.lwz r8,PT_GPR8(r1) ;\ | ||
65 | l.lwz r9,PT_GPR9(r1) ;\ | ||
66 | l.lwz r10,PT_GPR10(r1) ;\ | ||
67 | l.lwz r11,PT_GPR11(r1) ;\ | ||
68 | l.lwz r12,PT_GPR12(r1) ;\ | ||
69 | l.lwz r13,PT_GPR13(r1) ;\ | ||
70 | l.lwz r14,PT_GPR14(r1) ;\ | ||
71 | l.lwz r15,PT_GPR15(r1) ;\ | ||
72 | l.lwz r16,PT_GPR16(r1) ;\ | ||
73 | l.lwz r17,PT_GPR17(r1) ;\ | ||
74 | l.lwz r18,PT_GPR18(r1) ;\ | ||
75 | l.lwz r19,PT_GPR19(r1) ;\ | ||
76 | l.lwz r20,PT_GPR20(r1) ;\ | ||
77 | l.lwz r21,PT_GPR21(r1) ;\ | ||
78 | l.lwz r22,PT_GPR22(r1) ;\ | ||
79 | l.lwz r23,PT_GPR23(r1) ;\ | ||
80 | l.lwz r24,PT_GPR24(r1) ;\ | ||
81 | l.lwz r25,PT_GPR25(r1) ;\ | ||
82 | l.lwz r26,PT_GPR26(r1) ;\ | ||
83 | l.lwz r27,PT_GPR27(r1) ;\ | ||
84 | l.lwz r28,PT_GPR28(r1) ;\ | ||
85 | l.lwz r29,PT_GPR29(r1) ;\ | ||
86 | l.lwz r30,PT_GPR30(r1) ;\ | ||
87 | l.lwz r31,PT_GPR31(r1) ;\ | ||
88 | l.lwz r1,PT_SP(r1) ;\ | ||
89 | l.rfe | ||
90 | |||
91 | |||
92 | #define EXCEPTION_ENTRY(handler) \ | ||
93 | .global handler ;\ | ||
94 | handler: ;\ | ||
95 | /* r1, EPCR, ESR a already saved */ ;\ | ||
96 | l.sw PT_GPR2(r1),r2 ;\ | ||
97 | l.sw PT_GPR3(r1),r3 ;\ | ||
98 | l.sw PT_ORIG_GPR11(r1),r11 ;\ | ||
99 | /* r4 already save */ ;\ | ||
100 | l.sw PT_GPR5(r1),r5 ;\ | ||
101 | l.sw PT_GPR6(r1),r6 ;\ | ||
102 | l.sw PT_GPR7(r1),r7 ;\ | ||
103 | l.sw PT_GPR8(r1),r8 ;\ | ||
104 | l.sw PT_GPR9(r1),r9 ;\ | ||
105 | /* r10 already saved */ ;\ | ||
106 | l.sw PT_GPR11(r1),r11 ;\ | ||
107 | /* r12 already saved */ ;\ | ||
108 | l.sw PT_GPR13(r1),r13 ;\ | ||
109 | l.sw PT_GPR14(r1),r14 ;\ | ||
110 | l.sw PT_GPR15(r1),r15 ;\ | ||
111 | l.sw PT_GPR16(r1),r16 ;\ | ||
112 | l.sw PT_GPR17(r1),r17 ;\ | ||
113 | l.sw PT_GPR18(r1),r18 ;\ | ||
114 | l.sw PT_GPR19(r1),r19 ;\ | ||
115 | l.sw PT_GPR20(r1),r20 ;\ | ||
116 | l.sw PT_GPR21(r1),r21 ;\ | ||
117 | l.sw PT_GPR22(r1),r22 ;\ | ||
118 | l.sw PT_GPR23(r1),r23 ;\ | ||
119 | l.sw PT_GPR24(r1),r24 ;\ | ||
120 | l.sw PT_GPR25(r1),r25 ;\ | ||
121 | l.sw PT_GPR26(r1),r26 ;\ | ||
122 | l.sw PT_GPR27(r1),r27 ;\ | ||
123 | l.sw PT_GPR28(r1),r28 ;\ | ||
124 | l.sw PT_GPR29(r1),r29 ;\ | ||
125 | /* r30 already save */ ;\ | ||
126 | /* l.sw PT_GPR30(r1),r30*/ ;\ | ||
127 | l.sw PT_GPR31(r1),r31 ;\ | ||
128 | l.sw PT_SYSCALLNO(r1),r0 | ||
129 | |||
130 | #define UNHANDLED_EXCEPTION(handler,vector) \ | ||
131 | .global handler ;\ | ||
132 | handler: ;\ | ||
133 | /* r1, EPCR, ESR already saved */ ;\ | ||
134 | l.sw PT_GPR2(r1),r2 ;\ | ||
135 | l.sw PT_GPR3(r1),r3 ;\ | ||
136 | l.sw PT_ORIG_GPR11(r1),r11 ;\ | ||
137 | l.sw PT_GPR5(r1),r5 ;\ | ||
138 | l.sw PT_GPR6(r1),r6 ;\ | ||
139 | l.sw PT_GPR7(r1),r7 ;\ | ||
140 | l.sw PT_GPR8(r1),r8 ;\ | ||
141 | l.sw PT_GPR9(r1),r9 ;\ | ||
142 | /* r10 already saved */ ;\ | ||
143 | l.sw PT_GPR11(r1),r11 ;\ | ||
144 | /* r12 already saved */ ;\ | ||
145 | l.sw PT_GPR13(r1),r13 ;\ | ||
146 | l.sw PT_GPR14(r1),r14 ;\ | ||
147 | l.sw PT_GPR15(r1),r15 ;\ | ||
148 | l.sw PT_GPR16(r1),r16 ;\ | ||
149 | l.sw PT_GPR17(r1),r17 ;\ | ||
150 | l.sw PT_GPR18(r1),r18 ;\ | ||
151 | l.sw PT_GPR19(r1),r19 ;\ | ||
152 | l.sw PT_GPR20(r1),r20 ;\ | ||
153 | l.sw PT_GPR21(r1),r21 ;\ | ||
154 | l.sw PT_GPR22(r1),r22 ;\ | ||
155 | l.sw PT_GPR23(r1),r23 ;\ | ||
156 | l.sw PT_GPR24(r1),r24 ;\ | ||
157 | l.sw PT_GPR25(r1),r25 ;\ | ||
158 | l.sw PT_GPR26(r1),r26 ;\ | ||
159 | l.sw PT_GPR27(r1),r27 ;\ | ||
160 | l.sw PT_GPR28(r1),r28 ;\ | ||
161 | l.sw PT_GPR29(r1),r29 ;\ | ||
162 | /* r31 already saved */ ;\ | ||
163 | l.sw PT_GPR30(r1),r30 ;\ | ||
164 | /* l.sw PT_GPR31(r1),r31 */ ;\ | ||
165 | l.sw PT_SYSCALLNO(r1),r0 ;\ | ||
166 | l.addi r3,r1,0 ;\ | ||
167 | /* r4 is exception EA */ ;\ | ||
168 | l.addi r5,r0,vector ;\ | ||
169 | l.jal unhandled_exception ;\ | ||
170 | l.nop ;\ | ||
171 | l.j _ret_from_exception ;\ | ||
172 | l.nop | ||
173 | |||
174 | /* | ||
175 | * NOTE: one should never assume that SPR_EPC, SPR_ESR, SPR_EEAR | ||
176 | * contain the same values as when exception we're handling | ||
177 | * occured. in fact they never do. if you need them use | ||
178 | * values saved on stack (for SPR_EPC, SPR_ESR) or content | ||
179 | * of r4 (for SPR_EEAR). for details look at EXCEPTION_HANDLE() | ||
180 | * in 'arch/or32/kernel/head.S' | ||
181 | */ | ||
182 | |||
183 | /* =====================================================[ exceptions] === */ | ||
184 | |||
185 | /* ---[ 0x100: RESET exception ]----------------------------------------- */ | ||
186 | |||
187 | EXCEPTION_ENTRY(_tng_kernel_start) | ||
188 | l.jal _start | ||
189 | l.andi r0,r0,0 | ||
190 | |||
191 | /* ---[ 0x200: BUS exception ]------------------------------------------- */ | ||
192 | |||
193 | EXCEPTION_ENTRY(_bus_fault_handler) | ||
194 | /* r4: EA of fault (set by EXCEPTION_HANDLE) */ | ||
195 | l.jal do_bus_fault | ||
196 | l.addi r3,r1,0 /* pt_regs */ | ||
197 | |||
198 | l.j _ret_from_exception | ||
199 | l.nop | ||
200 | |||
201 | /* ---[ 0x300: Data Page Fault exception ]------------------------------- */ | ||
202 | |||
203 | EXCEPTION_ENTRY(_data_page_fault_handler) | ||
204 | /* set up parameters for do_page_fault */ | ||
205 | l.addi r3,r1,0 // pt_regs | ||
206 | /* r4 set be EXCEPTION_HANDLE */ // effective address of fault | ||
207 | l.ori r5,r0,0x300 // exception vector | ||
208 | |||
209 | /* | ||
210 | * __PHX__: TODO | ||
211 | * | ||
212 | * all this can be written much simpler. look at | ||
213 | * DTLB miss handler in the CONFIG_GUARD_PROTECTED_CORE part | ||
214 | */ | ||
215 | #ifdef CONFIG_OPENRISC_NO_SPR_SR_DSX | ||
216 | l.lwz r6,PT_PC(r3) // address of an offending insn | ||
217 | l.lwz r6,0(r6) // instruction that caused pf | ||
218 | |||
219 | l.srli r6,r6,26 // check opcode for jump insn | ||
220 | l.sfeqi r6,0 // l.j | ||
221 | l.bf 8f | ||
222 | l.sfeqi r6,1 // l.jal | ||
223 | l.bf 8f | ||
224 | l.sfeqi r6,3 // l.bnf | ||
225 | l.bf 8f | ||
226 | l.sfeqi r6,4 // l.bf | ||
227 | l.bf 8f | ||
228 | l.sfeqi r6,0x11 // l.jr | ||
229 | l.bf 8f | ||
230 | l.sfeqi r6,0x12 // l.jalr | ||
231 | l.bf 8f | ||
232 | |||
233 | l.nop | ||
234 | |||
235 | l.j 9f | ||
236 | l.nop | ||
237 | 8: | ||
238 | |||
239 | l.lwz r6,PT_PC(r3) // address of an offending insn | ||
240 | l.addi r6,r6,4 | ||
241 | l.lwz r6,0(r6) // instruction that caused pf | ||
242 | l.srli r6,r6,26 // get opcode | ||
243 | 9: | ||
244 | |||
245 | #else | ||
246 | |||
247 | l.mfspr r6,r0,SPR_SR // SR | ||
248 | // l.lwz r6,PT_SR(r3) // ESR | ||
249 | l.andi r6,r6,SPR_SR_DSX // check for delay slot exception | ||
250 | l.sfeqi r6,0x1 // exception happened in delay slot | ||
251 | l.bnf 7f | ||
252 | l.lwz r6,PT_PC(r3) // address of an offending insn | ||
253 | |||
254 | l.addi r6,r6,4 // offending insn is in delay slot | ||
255 | 7: | ||
256 | l.lwz r6,0(r6) // instruction that caused pf | ||
257 | l.srli r6,r6,26 // check opcode for write access | ||
258 | #endif | ||
259 | |||
260 | l.sfgeui r6,0x34 // check opcode for write access | ||
261 | l.bnf 1f | ||
262 | l.sfleui r6,0x37 | ||
263 | l.bnf 1f | ||
264 | l.ori r6,r0,0x1 // write access | ||
265 | l.j 2f | ||
266 | l.nop | ||
267 | 1: l.ori r6,r0,0x0 // !write access | ||
268 | 2: | ||
269 | |||
270 | /* call fault.c handler in or32/mm/fault.c */ | ||
271 | l.jal do_page_fault | ||
272 | l.nop | ||
273 | l.j _ret_from_exception | ||
274 | l.nop | ||
275 | |||
276 | /* ---[ 0x400: Insn Page Fault exception ]------------------------------- */ | ||
277 | |||
278 | EXCEPTION_ENTRY(_insn_page_fault_handler) | ||
279 | /* set up parameters for do_page_fault */ | ||
280 | l.addi r3,r1,0 // pt_regs | ||
281 | /* r4 set be EXCEPTION_HANDLE */ // effective address of fault | ||
282 | l.ori r5,r0,0x400 // exception vector | ||
283 | l.ori r6,r0,0x0 // !write access | ||
284 | |||
285 | /* call fault.c handler in or32/mm/fault.c */ | ||
286 | l.jal do_page_fault | ||
287 | l.nop | ||
288 | l.j _ret_from_exception | ||
289 | l.nop | ||
290 | |||
291 | |||
292 | /* ---[ 0x500: Timer exception ]----------------------------------------- */ | ||
293 | |||
294 | EXCEPTION_ENTRY(_timer_handler) | ||
295 | l.jal timer_interrupt | ||
296 | l.addi r3,r1,0 /* pt_regs */ | ||
297 | |||
298 | l.j _ret_from_intr | ||
299 | l.nop | ||
300 | |||
301 | /* ---[ 0x600: Aligment exception ]-------------------------------------- */ | ||
302 | |||
303 | EXCEPTION_ENTRY(_alignment_handler) | ||
304 | /* r4: EA of fault (set by EXCEPTION_HANDLE) */ | ||
305 | l.jal do_unaligned_access | ||
306 | l.addi r3,r1,0 /* pt_regs */ | ||
307 | |||
308 | l.j _ret_from_exception | ||
309 | l.nop | ||
310 | |||
311 | #if 0 | ||
312 | EXCEPTION_ENTRY(_aligment_handler) | ||
313 | // l.mfspr r2,r0,SPR_EEAR_BASE /* Load the efective addres */ | ||
314 | l.addi r2,r4,0 | ||
315 | // l.mfspr r5,r0,SPR_EPCR_BASE /* Load the insn address */ | ||
316 | l.lwz r5,PT_PC(r1) | ||
317 | |||
318 | l.lwz r3,0(r5) /* Load insn */ | ||
319 | l.srli r4,r3,26 /* Shift left to get the insn opcode */ | ||
320 | |||
321 | l.sfeqi r4,0x00 /* Check if the load/store insn is in delay slot */ | ||
322 | l.bf jmp | ||
323 | l.sfeqi r4,0x01 | ||
324 | l.bf jmp | ||
325 | l.sfeqi r4,0x03 | ||
326 | l.bf jmp | ||
327 | l.sfeqi r4,0x04 | ||
328 | l.bf jmp | ||
329 | l.sfeqi r4,0x11 | ||
330 | l.bf jr | ||
331 | l.sfeqi r4,0x12 | ||
332 | l.bf jr | ||
333 | l.nop | ||
334 | l.j 1f | ||
335 | l.addi r5,r5,4 /* Increment PC to get return insn address */ | ||
336 | |||
337 | jmp: | ||
338 | l.slli r4,r3,6 /* Get the signed extended jump length */ | ||
339 | l.srai r4,r4,4 | ||
340 | |||
341 | l.lwz r3,4(r5) /* Load the real load/store insn */ | ||
342 | |||
343 | l.add r5,r5,r4 /* Calculate jump target address */ | ||
344 | |||
345 | l.j 1f | ||
346 | l.srli r4,r3,26 /* Shift left to get the insn opcode */ | ||
347 | |||
348 | jr: | ||
349 | l.slli r4,r3,9 /* Shift to get the reg nb */ | ||
350 | l.andi r4,r4,0x7c | ||
351 | |||
352 | l.lwz r3,4(r5) /* Load the real load/store insn */ | ||
353 | |||
354 | l.add r4,r4,r1 /* Load the jump register value from the stack */ | ||
355 | l.lwz r5,0(r4) | ||
356 | |||
357 | l.srli r4,r3,26 /* Shift left to get the insn opcode */ | ||
358 | |||
359 | |||
360 | 1: | ||
361 | // l.mtspr r0,r5,SPR_EPCR_BASE | ||
362 | l.sw PT_PC(r1),r5 | ||
363 | |||
364 | l.sfeqi r4,0x26 | ||
365 | l.bf lhs | ||
366 | l.sfeqi r4,0x25 | ||
367 | l.bf lhz | ||
368 | l.sfeqi r4,0x22 | ||
369 | l.bf lws | ||
370 | l.sfeqi r4,0x21 | ||
371 | l.bf lwz | ||
372 | l.sfeqi r4,0x37 | ||
373 | l.bf sh | ||
374 | l.sfeqi r4,0x35 | ||
375 | l.bf sw | ||
376 | l.nop | ||
377 | |||
378 | 1: l.j 1b /* I don't know what to do */ | ||
379 | l.nop | ||
380 | |||
381 | lhs: l.lbs r5,0(r2) | ||
382 | l.slli r5,r5,8 | ||
383 | l.lbz r6,1(r2) | ||
384 | l.or r5,r5,r6 | ||
385 | l.srli r4,r3,19 | ||
386 | l.andi r4,r4,0x7c | ||
387 | l.add r4,r4,r1 | ||
388 | l.j align_end | ||
389 | l.sw 0(r4),r5 | ||
390 | |||
391 | lhz: l.lbz r5,0(r2) | ||
392 | l.slli r5,r5,8 | ||
393 | l.lbz r6,1(r2) | ||
394 | l.or r5,r5,r6 | ||
395 | l.srli r4,r3,19 | ||
396 | l.andi r4,r4,0x7c | ||
397 | l.add r4,r4,r1 | ||
398 | l.j align_end | ||
399 | l.sw 0(r4),r5 | ||
400 | |||
401 | lws: l.lbs r5,0(r2) | ||
402 | l.slli r5,r5,24 | ||
403 | l.lbz r6,1(r2) | ||
404 | l.slli r6,r6,16 | ||
405 | l.or r5,r5,r6 | ||
406 | l.lbz r6,2(r2) | ||
407 | l.slli r6,r6,8 | ||
408 | l.or r5,r5,r6 | ||
409 | l.lbz r6,3(r2) | ||
410 | l.or r5,r5,r6 | ||
411 | l.srli r4,r3,19 | ||
412 | l.andi r4,r4,0x7c | ||
413 | l.add r4,r4,r1 | ||
414 | l.j align_end | ||
415 | l.sw 0(r4),r5 | ||
416 | |||
417 | lwz: l.lbz r5,0(r2) | ||
418 | l.slli r5,r5,24 | ||
419 | l.lbz r6,1(r2) | ||
420 | l.slli r6,r6,16 | ||
421 | l.or r5,r5,r6 | ||
422 | l.lbz r6,2(r2) | ||
423 | l.slli r6,r6,8 | ||
424 | l.or r5,r5,r6 | ||
425 | l.lbz r6,3(r2) | ||
426 | l.or r5,r5,r6 | ||
427 | l.srli r4,r3,19 | ||
428 | l.andi r4,r4,0x7c | ||
429 | l.add r4,r4,r1 | ||
430 | l.j align_end | ||
431 | l.sw 0(r4),r5 | ||
432 | |||
433 | sh: | ||
434 | l.srli r4,r3,9 | ||
435 | l.andi r4,r4,0x7c | ||
436 | l.add r4,r4,r1 | ||
437 | l.lwz r5,0(r4) | ||
438 | l.sb 1(r2),r5 | ||
439 | l.srli r5,r5,8 | ||
440 | l.j align_end | ||
441 | l.sb 0(r2),r5 | ||
442 | |||
443 | sw: | ||
444 | l.srli r4,r3,9 | ||
445 | l.andi r4,r4,0x7c | ||
446 | l.add r4,r4,r1 | ||
447 | l.lwz r5,0(r4) | ||
448 | l.sb 3(r2),r5 | ||
449 | l.srli r5,r5,8 | ||
450 | l.sb 2(r2),r5 | ||
451 | l.srli r5,r5,8 | ||
452 | l.sb 1(r2),r5 | ||
453 | l.srli r5,r5,8 | ||
454 | l.j align_end | ||
455 | l.sb 0(r2),r5 | ||
456 | |||
457 | align_end: | ||
458 | l.j _ret_from_intr | ||
459 | l.nop | ||
460 | #endif | ||
461 | |||
462 | /* ---[ 0x700: Illegal insn exception ]---------------------------------- */ | ||
463 | |||
464 | EXCEPTION_ENTRY(_illegal_instruction_handler) | ||
465 | /* r4: EA of fault (set by EXCEPTION_HANDLE) */ | ||
466 | l.jal do_illegal_instruction | ||
467 | l.addi r3,r1,0 /* pt_regs */ | ||
468 | |||
469 | l.j _ret_from_exception | ||
470 | l.nop | ||
471 | |||
472 | /* ---[ 0x800: External interrupt exception ]---------------------------- */ | ||
473 | |||
474 | EXCEPTION_ENTRY(_external_irq_handler) | ||
475 | #ifdef CONFIG_OPENRISC_ESR_EXCEPTION_BUG_CHECK | ||
476 | l.lwz r4,PT_SR(r1) // were interrupts enabled ? | ||
477 | l.andi r4,r4,SPR_SR_IEE | ||
478 | l.sfeqi r4,0 | ||
479 | l.bnf 1f // ext irq enabled, all ok. | ||
480 | l.nop | ||
481 | |||
482 | l.addi r1,r1,-0x8 | ||
483 | l.movhi r3,hi(42f) | ||
484 | l.ori r3,r3,lo(42f) | ||
485 | l.sw 0x0(r1),r3 | ||
486 | l.jal printk | ||
487 | l.sw 0x4(r1),r4 | ||
488 | l.addi r1,r1,0x8 | ||
489 | |||
490 | .section .rodata, "a" | ||
491 | 42: | ||
492 | .string "\n\rESR interrupt bug: in _external_irq_handler (ESR %x)\n\r" | ||
493 | .align 4 | ||
494 | .previous | ||
495 | |||
496 | l.ori r4,r4,SPR_SR_IEE // fix the bug | ||
497 | // l.sw PT_SR(r1),r4 | ||
498 | 1: | ||
499 | #endif | ||
500 | l.addi r3,r1,0 | ||
501 | l.movhi r8,hi(do_IRQ) | ||
502 | l.ori r8,r8,lo(do_IRQ) | ||
503 | l.jalr r8 | ||
504 | l.nop | ||
505 | l.j _ret_from_intr | ||
506 | l.nop | ||
507 | |||
508 | /* ---[ 0x900: DTLB miss exception ]------------------------------------- */ | ||
509 | |||
510 | |||
511 | /* ---[ 0xa00: ITLB miss exception ]------------------------------------- */ | ||
512 | |||
513 | |||
514 | /* ---[ 0xb00: Range exception ]----------------------------------------- */ | ||
515 | |||
516 | UNHANDLED_EXCEPTION(_vector_0xb00,0xb00) | ||
517 | |||
518 | /* ---[ 0xc00: Syscall exception ]--------------------------------------- */ | ||
519 | |||
520 | /* | ||
521 | * Syscalls are a special type of exception in that they are | ||
522 | * _explicitly_ invoked by userspace and can therefore be | ||
523 | * held to conform to the same ABI as normal functions with | ||
524 | * respect to whether registers are preserved across the call | ||
525 | * or not. | ||
526 | */ | ||
527 | |||
528 | /* Upon syscall entry we just save the callee-saved registers | ||
529 | * and not the call-clobbered ones. | ||
530 | */ | ||
531 | |||
532 | _string_syscall_return: | ||
533 | .string "syscall return %ld \n\r\0" | ||
534 | .align 4 | ||
535 | |||
536 | ENTRY(_sys_call_handler) | ||
537 | /* syscalls run with interrupts enabled */ | ||
538 | ENABLE_INTERRUPTS(r29) // enable interrupts, r29 is temp | ||
539 | |||
540 | /* r1, EPCR, ESR a already saved */ | ||
541 | l.sw PT_GPR2(r1),r2 | ||
542 | /* r3-r8 must be saved because syscall restart relies | ||
543 | * on us being able to restart the syscall args... technically | ||
544 | * they should be clobbered, otherwise | ||
545 | */ | ||
546 | l.sw PT_GPR3(r1),r3 | ||
547 | /* r4 already saved */ | ||
548 | /* r4 holds the EEAR address of the fault, load the original r4 */ | ||
549 | l.lwz r4,PT_GPR4(r1) | ||
550 | l.sw PT_GPR5(r1),r5 | ||
551 | l.sw PT_GPR6(r1),r6 | ||
552 | l.sw PT_GPR7(r1),r7 | ||
553 | l.sw PT_GPR8(r1),r8 | ||
554 | l.sw PT_GPR9(r1),r9 | ||
555 | /* r10 already saved */ | ||
556 | l.sw PT_GPR11(r1),r11 | ||
557 | l.sw PT_ORIG_GPR11(r1),r11 | ||
558 | /* r12,r13 already saved */ | ||
559 | |||
560 | /* r14-r28 (even) aren't touched by the syscall fast path below | ||
561 | * so we don't need to save them. However, the functions that return | ||
562 | * to userspace via a call to switch() DO need to save these because | ||
563 | * switch() effectively clobbers them... saving these registers for | ||
564 | * such functions is handled in their syscall wrappers (see fork, vfork, | ||
565 | * and clone, below). | ||
566 | |||
567 | /* r30 is the only register we clobber in the fast path */ | ||
568 | /* r30 already saved */ | ||
569 | /* l.sw PT_GPR30(r1),r30 */ | ||
570 | /* This is used by do_signal to determine whether to check for | ||
571 | * syscall restart or not */ | ||
572 | l.sw PT_SYSCALLNO(r1),r11 | ||
573 | |||
574 | _syscall_check_trace_enter: | ||
575 | /* If TIF_SYSCALL_TRACE is set, then we want to do syscall tracing */ | ||
576 | l.lwz r30,TI_FLAGS(r10) | ||
577 | l.andi r30,r30,_TIF_SYSCALL_TRACE | ||
578 | l.sfne r30,r0 | ||
579 | l.bf _syscall_trace_enter | ||
580 | l.nop | ||
581 | |||
582 | _syscall_check: | ||
583 | /* Ensure that the syscall number is reasonable */ | ||
584 | l.sfgeui r11,__NR_syscalls | ||
585 | l.bf _syscall_badsys | ||
586 | l.nop | ||
587 | |||
588 | _syscall_call: | ||
589 | l.movhi r29,hi(sys_call_table) | ||
590 | l.ori r29,r29,lo(sys_call_table) | ||
591 | l.slli r11,r11,2 | ||
592 | l.add r29,r29,r11 | ||
593 | l.lwz r29,0(r29) | ||
594 | |||
595 | l.jalr r29 | ||
596 | l.nop | ||
597 | |||
598 | _syscall_return: | ||
599 | /* All syscalls return here... just pay attention to ret_from_fork | ||
600 | * which does it in a round-about way. | ||
601 | */ | ||
602 | l.sw PT_GPR11(r1),r11 // save return value | ||
603 | |||
604 | #if 0 | ||
605 | _syscall_debug: | ||
606 | l.movhi r3,hi(_string_syscall_return) | ||
607 | l.ori r3,r3,lo(_string_syscall_return) | ||
608 | l.ori r27,r0,1 | ||
609 | l.sw -4(r1),r27 | ||
610 | l.sw -8(r1),r11 | ||
611 | l.addi r1,r1,-8 | ||
612 | l.movhi r27,hi(printk) | ||
613 | l.ori r27,r27,lo(printk) | ||
614 | l.jalr r27 | ||
615 | l.nop | ||
616 | l.addi r1,r1,8 | ||
617 | #endif | ||
618 | |||
619 | _syscall_check_trace_leave: | ||
620 | /* r30 is a callee-saved register so this should still hold the | ||
621 | * _TIF_SYSCALL_TRACE flag from _syscall_check_trace_enter above... | ||
622 | * _syscall_trace_leave expects syscall result to be in pt_regs->r11. | ||
623 | */ | ||
624 | l.sfne r30,r0 | ||
625 | l.bf _syscall_trace_leave | ||
626 | l.nop | ||
627 | |||
628 | /* This is where the exception-return code begins... interrupts need to be | ||
629 | * disabled the rest of the way here because we can't afford to miss any | ||
630 | * interrupts that set NEED_RESCHED or SIGNALPENDING... really true? */ | ||
631 | |||
632 | _syscall_check_work: | ||
633 | /* Here we need to disable interrupts */ | ||
634 | DISABLE_INTERRUPTS(r27,r29) | ||
635 | l.lwz r30,TI_FLAGS(r10) | ||
636 | l.andi r30,r30,_TIF_WORK_MASK | ||
637 | l.sfne r30,r0 | ||
638 | |||
639 | l.bnf _syscall_resume_userspace | ||
640 | l.nop | ||
641 | |||
642 | /* Work pending follows a different return path, so we need to | ||
643 | * make sure that all the call-saved registers get into pt_regs | ||
644 | * before branching... | ||
645 | */ | ||
646 | l.sw PT_GPR14(r1),r14 | ||
647 | l.sw PT_GPR16(r1),r16 | ||
648 | l.sw PT_GPR18(r1),r18 | ||
649 | l.sw PT_GPR20(r1),r20 | ||
650 | l.sw PT_GPR22(r1),r22 | ||
651 | l.sw PT_GPR24(r1),r24 | ||
652 | l.sw PT_GPR26(r1),r26 | ||
653 | l.sw PT_GPR28(r1),r28 | ||
654 | |||
655 | /* _work_pending needs to be called with interrupts disabled */ | ||
656 | l.j _work_pending | ||
657 | l.nop | ||
658 | |||
659 | _syscall_resume_userspace: | ||
660 | // ENABLE_INTERRUPTS(r29) | ||
661 | |||
662 | |||
663 | /* This is the hot path for returning to userspace from a syscall. If there's | ||
664 | * work to be done and the branch to _work_pending was taken above, then the | ||
665 | * return to userspace will be done via the normal exception return path... | ||
666 | * that path restores _all_ registers and will overwrite the "clobbered" | ||
667 | * registers with whatever garbage is in pt_regs -- that's OK because those | ||
668 | * registers are clobbered anyway and because the extra work is insignificant | ||
669 | * in the context of the extra work that _work_pending is doing. | ||
670 | |||
671 | /* Once again, syscalls are special and only guarantee to preserve the | ||
672 | * same registers as a normal function call */ | ||
673 | |||
674 | /* The assumption here is that the registers r14-r28 (even) are untouched and | ||
675 | * don't need to be restored... be sure that that's really the case! | ||
676 | */ | ||
677 | |||
678 | /* This is still too much... we should only be restoring what we actually | ||
679 | * clobbered... we should even be using 'scratch' (odd) regs above so that | ||
680 | * we don't need to restore anything, hardly... | ||
681 | */ | ||
682 | |||
683 | l.lwz r2,PT_GPR2(r1) | ||
684 | |||
685 | /* Restore args */ | ||
686 | /* r3-r8 are technically clobbered, but syscall restart needs these | ||
687 | * to be restored... | ||
688 | */ | ||
689 | l.lwz r3,PT_GPR3(r1) | ||
690 | l.lwz r4,PT_GPR4(r1) | ||
691 | l.lwz r5,PT_GPR5(r1) | ||
692 | l.lwz r6,PT_GPR6(r1) | ||
693 | l.lwz r7,PT_GPR7(r1) | ||
694 | l.lwz r8,PT_GPR8(r1) | ||
695 | |||
696 | l.lwz r9,PT_GPR9(r1) | ||
697 | l.lwz r10,PT_GPR10(r1) | ||
698 | l.lwz r11,PT_GPR11(r1) | ||
699 | |||
700 | /* r30 is the only register we clobber in the fast path */ | ||
701 | l.lwz r30,PT_GPR30(r1) | ||
702 | |||
703 | /* Here we use r13-r19 (odd) as scratch regs */ | ||
704 | l.lwz r13,PT_PC(r1) | ||
705 | l.lwz r15,PT_SR(r1) | ||
706 | l.lwz r1,PT_SP(r1) | ||
707 | /* Interrupts need to be disabled for setting EPCR and ESR | ||
708 | * so that another interrupt doesn't come in here and clobber | ||
709 | * them before we can use them for our l.rfe */ | ||
710 | DISABLE_INTERRUPTS(r17,r19) | ||
711 | l.mtspr r0,r13,SPR_EPCR_BASE | ||
712 | l.mtspr r0,r15,SPR_ESR_BASE | ||
713 | l.rfe | ||
714 | |||
715 | /* End of hot path! | ||
716 | * Keep the below tracing and error handling out of the hot path... | ||
717 | */ | ||
718 | |||
719 | _syscall_trace_enter: | ||
720 | /* Here we pass pt_regs to do_syscall_trace_enter. Make sure | ||
721 | * that function is really getting all the info it needs as | ||
722 | * pt_regs isn't a complete set of userspace regs, just the | ||
723 | * ones relevant to the syscall... | ||
724 | * | ||
725 | * Note use of delay slot for setting argument. | ||
726 | */ | ||
727 | l.jal do_syscall_trace_enter | ||
728 | l.addi r3,r1,0 | ||
729 | |||
730 | /* Restore arguments (not preserved across do_syscall_trace_enter) | ||
731 | * so that we can do the syscall for real and return to the syscall | ||
732 | * hot path. | ||
733 | */ | ||
734 | l.lwz r11,PT_SYSCALLNO(r1) | ||
735 | l.lwz r3,PT_GPR3(r1) | ||
736 | l.lwz r4,PT_GPR4(r1) | ||
737 | l.lwz r5,PT_GPR5(r1) | ||
738 | l.lwz r6,PT_GPR6(r1) | ||
739 | l.lwz r7,PT_GPR7(r1) | ||
740 | |||
741 | l.j _syscall_check | ||
742 | l.lwz r8,PT_GPR8(r1) | ||
743 | |||
744 | _syscall_trace_leave: | ||
745 | l.jal do_syscall_trace_leave | ||
746 | l.addi r3,r1,0 | ||
747 | |||
748 | l.j _syscall_check_work | ||
749 | l.nop | ||
750 | |||
751 | _syscall_badsys: | ||
752 | /* Here we effectively pretend to have executed an imaginary | ||
753 | * syscall that returns -ENOSYS and then return to the regular | ||
754 | * syscall hot path. | ||
755 | * Note that "return value" is set in the delay slot... | ||
756 | */ | ||
757 | l.j _syscall_return | ||
758 | l.addi r11,r0,-ENOSYS | ||
759 | |||
760 | /******* END SYSCALL HANDLING *******/ | ||
761 | |||
762 | /* ---[ 0xd00: Trap exception ]------------------------------------------ */ | ||
763 | |||
764 | UNHANDLED_EXCEPTION(_vector_0xd00,0xd00) | ||
765 | |||
766 | /* ---[ 0xe00: Trap exception ]------------------------------------------ */ | ||
767 | |||
768 | EXCEPTION_ENTRY(_trap_handler) | ||
769 | /* r4: EA of fault (set by EXCEPTION_HANDLE) */ | ||
770 | l.jal do_trap | ||
771 | l.addi r3,r1,0 /* pt_regs */ | ||
772 | |||
773 | l.j _ret_from_exception | ||
774 | l.nop | ||
775 | |||
776 | /* ---[ 0xf00: Reserved exception ]-------------------------------------- */ | ||
777 | |||
778 | UNHANDLED_EXCEPTION(_vector_0xf00,0xf00) | ||
779 | |||
780 | /* ---[ 0x1000: Reserved exception ]------------------------------------- */ | ||
781 | |||
782 | UNHANDLED_EXCEPTION(_vector_0x1000,0x1000) | ||
783 | |||
784 | /* ---[ 0x1100: Reserved exception ]------------------------------------- */ | ||
785 | |||
786 | UNHANDLED_EXCEPTION(_vector_0x1100,0x1100) | ||
787 | |||
788 | /* ---[ 0x1200: Reserved exception ]------------------------------------- */ | ||
789 | |||
790 | UNHANDLED_EXCEPTION(_vector_0x1200,0x1200) | ||
791 | |||
792 | /* ---[ 0x1300: Reserved exception ]------------------------------------- */ | ||
793 | |||
794 | UNHANDLED_EXCEPTION(_vector_0x1300,0x1300) | ||
795 | |||
796 | /* ---[ 0x1400: Reserved exception ]------------------------------------- */ | ||
797 | |||
798 | UNHANDLED_EXCEPTION(_vector_0x1400,0x1400) | ||
799 | |||
800 | /* ---[ 0x1500: Reserved exception ]------------------------------------- */ | ||
801 | |||
802 | UNHANDLED_EXCEPTION(_vector_0x1500,0x1500) | ||
803 | |||
804 | /* ---[ 0x1600: Reserved exception ]------------------------------------- */ | ||
805 | |||
806 | UNHANDLED_EXCEPTION(_vector_0x1600,0x1600) | ||
807 | |||
808 | /* ---[ 0x1700: Reserved exception ]------------------------------------- */ | ||
809 | |||
810 | UNHANDLED_EXCEPTION(_vector_0x1700,0x1700) | ||
811 | |||
812 | /* ---[ 0x1800: Reserved exception ]------------------------------------- */ | ||
813 | |||
814 | UNHANDLED_EXCEPTION(_vector_0x1800,0x1800) | ||
815 | |||
816 | /* ---[ 0x1900: Reserved exception ]------------------------------------- */ | ||
817 | |||
818 | UNHANDLED_EXCEPTION(_vector_0x1900,0x1900) | ||
819 | |||
820 | /* ---[ 0x1a00: Reserved exception ]------------------------------------- */ | ||
821 | |||
822 | UNHANDLED_EXCEPTION(_vector_0x1a00,0x1a00) | ||
823 | |||
824 | /* ---[ 0x1b00: Reserved exception ]------------------------------------- */ | ||
825 | |||
826 | UNHANDLED_EXCEPTION(_vector_0x1b00,0x1b00) | ||
827 | |||
828 | /* ---[ 0x1c00: Reserved exception ]------------------------------------- */ | ||
829 | |||
830 | UNHANDLED_EXCEPTION(_vector_0x1c00,0x1c00) | ||
831 | |||
832 | /* ---[ 0x1d00: Reserved exception ]------------------------------------- */ | ||
833 | |||
834 | UNHANDLED_EXCEPTION(_vector_0x1d00,0x1d00) | ||
835 | |||
836 | /* ---[ 0x1e00: Reserved exception ]------------------------------------- */ | ||
837 | |||
838 | UNHANDLED_EXCEPTION(_vector_0x1e00,0x1e00) | ||
839 | |||
840 | /* ---[ 0x1f00: Reserved exception ]------------------------------------- */ | ||
841 | |||
842 | UNHANDLED_EXCEPTION(_vector_0x1f00,0x1f00) | ||
843 | |||
844 | /* ========================================================[ return ] === */ | ||
845 | |||
846 | _work_pending: | ||
847 | /* | ||
848 | * if (current_thread_info->flags & _TIF_NEED_RESCHED) | ||
849 | * schedule(); | ||
850 | */ | ||
851 | l.lwz r5,TI_FLAGS(r10) | ||
852 | l.andi r3,r5,_TIF_NEED_RESCHED | ||
853 | l.sfnei r3,0 | ||
854 | l.bnf _work_notifysig | ||
855 | l.nop | ||
856 | l.jal schedule | ||
857 | l.nop | ||
858 | l.j _resume_userspace | ||
859 | l.nop | ||
860 | |||
861 | /* Handle pending signals and notify-resume requests. | ||
862 | * do_notify_resume must be passed the latest pushed pt_regs, not | ||
863 | * necessarily the "userspace" ones. Also, pt_regs->syscallno | ||
864 | * must be set so that the syscall restart functionality works. | ||
865 | */ | ||
866 | _work_notifysig: | ||
867 | l.jal do_notify_resume | ||
868 | l.ori r3,r1,0 /* pt_regs */ | ||
869 | |||
870 | _resume_userspace: | ||
871 | DISABLE_INTERRUPTS(r3,r4) | ||
872 | l.lwz r3,TI_FLAGS(r10) | ||
873 | l.andi r3,r3,_TIF_WORK_MASK | ||
874 | l.sfnei r3,0 | ||
875 | l.bf _work_pending | ||
876 | l.nop | ||
877 | |||
878 | _restore_all: | ||
879 | RESTORE_ALL | ||
880 | /* This returns to userspace code */ | ||
881 | |||
882 | |||
883 | ENTRY(_ret_from_intr) | ||
884 | ENTRY(_ret_from_exception) | ||
885 | l.lwz r4,PT_SR(r1) | ||
886 | l.andi r3,r4,SPR_SR_SM | ||
887 | l.sfeqi r3,0 | ||
888 | l.bnf _restore_all | ||
889 | l.nop | ||
890 | l.j _resume_userspace | ||
891 | l.nop | ||
892 | |||
893 | ENTRY(ret_from_fork) | ||
894 | l.jal schedule_tail | ||
895 | l.nop | ||
896 | |||
897 | /* _syscall_returns expect r11 to contain return value */ | ||
898 | l.lwz r11,PT_GPR11(r1) | ||
899 | |||
900 | /* The syscall fast path return expects call-saved registers | ||
901 | * r12-r28 to be untouched, so we restore them here as they | ||
902 | * will have been effectively clobbered when arriving here | ||
903 | * via the call to switch() | ||
904 | */ | ||
905 | l.lwz r12,PT_GPR12(r1) | ||
906 | l.lwz r14,PT_GPR14(r1) | ||
907 | l.lwz r16,PT_GPR16(r1) | ||
908 | l.lwz r18,PT_GPR18(r1) | ||
909 | l.lwz r20,PT_GPR20(r1) | ||
910 | l.lwz r22,PT_GPR22(r1) | ||
911 | l.lwz r24,PT_GPR24(r1) | ||
912 | l.lwz r26,PT_GPR26(r1) | ||
913 | l.lwz r28,PT_GPR28(r1) | ||
914 | |||
915 | l.j _syscall_return | ||
916 | l.nop | ||
917 | |||
918 | /* Since syscalls don't save call-clobbered registers, the args to | ||
919 | * kernel_thread_helper will need to be passed through callee-saved | ||
920 | * registers and copied to the parameter registers when the thread | ||
921 | * begins running. | ||
922 | * | ||
923 | * See arch/openrisc/kernel/process.c: | ||
924 | * The args are passed as follows: | ||
925 | * arg1 (r3) : passed in r20 | ||
926 | * arg2 (r4) : passed in r22 | ||
927 | */ | ||
928 | |||
929 | ENTRY(_kernel_thread_helper) | ||
930 | l.or r3,r20,r0 | ||
931 | l.or r4,r22,r0 | ||
932 | l.movhi r31,hi(kernel_thread_helper) | ||
933 | l.ori r31,r31,lo(kernel_thread_helper) | ||
934 | l.jr r31 | ||
935 | l.nop | ||
936 | |||
937 | |||
938 | /* ========================================================[ switch ] === */ | ||
939 | |||
940 | /* | ||
941 | * This routine switches between two different tasks. The process | ||
942 | * state of one is saved on its kernel stack. Then the state | ||
943 | * of the other is restored from its kernel stack. The memory | ||
944 | * management hardware is updated to the second process's state. | ||
945 | * Finally, we can return to the second process, via the 'return'. | ||
946 | * | ||
947 | * Note: there are two ways to get to the "going out" portion | ||
948 | * of this code; either by coming in via the entry (_switch) | ||
949 | * or via "fork" which must set up an environment equivalent | ||
950 | * to the "_switch" path. If you change this (or in particular, the | ||
951 | * SAVE_REGS macro), you'll have to change the fork code also. | ||
952 | */ | ||
953 | |||
954 | |||
955 | /* _switch MUST never lay on page boundry, cause it runs from | ||
956 | * effective addresses and beeing interrupted by iTLB miss would kill it. | ||
957 | * dTLB miss seams to never accour in the bad place since data accesses | ||
958 | * are from task structures which are always page aligned. | ||
959 | * | ||
960 | * The problem happens in RESTORE_ALL_NO_R11 where we first set the EPCR | ||
961 | * register, then load the previous register values and only at the end call | ||
962 | * the l.rfe instruction. If get TLB miss in beetwen the EPCR register gets | ||
963 | * garbled and we end up calling l.rfe with the wrong EPCR. (same probably | ||
964 | * holds for ESR) | ||
965 | * | ||
966 | * To avoid this problems it is sufficient to align _switch to | ||
967 | * some nice round number smaller than it's size... | ||
968 | */ | ||
969 | |||
970 | /* ABI rules apply here... we either enter _switch via schedule() or via | ||
971 | * an imaginary call to which we shall return at return_from_fork. Either | ||
972 | * way, we are a function call and only need to preserve the callee-saved | ||
973 | * registers when we return. As such, we don't need to save the registers | ||
974 | * on the stack that we won't be returning as they were... | ||
975 | */ | ||
976 | |||
977 | .align 0x400 | ||
978 | ENTRY(_switch) | ||
979 | /* We don't store SR as _switch only gets called in a context where | ||
980 | * the SR will be the same going in and coming out... */ | ||
981 | |||
982 | /* Set up new pt_regs struct for saving task state */ | ||
983 | l.addi r1,r1,-(INT_FRAME_SIZE) | ||
984 | |||
985 | /* No need to store r1/PT_SP as it goes into KSP below */ | ||
986 | l.sw PT_GPR2(r1),r2 | ||
987 | l.sw PT_GPR9(r1),r9 | ||
988 | /* This is wrong, r12 shouldn't be here... but GCC is broken for the time being | ||
989 | * and expects r12 to be callee-saved... */ | ||
990 | l.sw PT_GPR12(r1),r12 | ||
991 | l.sw PT_GPR14(r1),r14 | ||
992 | l.sw PT_GPR16(r1),r16 | ||
993 | l.sw PT_GPR18(r1),r18 | ||
994 | l.sw PT_GPR20(r1),r20 | ||
995 | l.sw PT_GPR22(r1),r22 | ||
996 | l.sw PT_GPR24(r1),r24 | ||
997 | l.sw PT_GPR26(r1),r26 | ||
998 | l.sw PT_GPR28(r1),r28 | ||
999 | l.sw PT_GPR30(r1),r30 | ||
1000 | |||
1001 | l.addi r11,r10,0 /* Save old 'current' to 'last' return value*/ | ||
1002 | |||
1003 | /* We use thread_info->ksp for storing the address of the above | ||
1004 | * structure so that we can get back to it later... we don't want | ||
1005 | * to lose the value of thread_info->ksp, though, so store it as | ||
1006 | * pt_regs->sp so that we can easily restore it when we are made | ||
1007 | * live again... | ||
1008 | */ | ||
1009 | |||
1010 | /* Save the old value of thread_info->ksp as pt_regs->sp */ | ||
1011 | l.lwz r29,TI_KSP(r10) | ||
1012 | l.sw PT_SP(r1),r29 | ||
1013 | |||
1014 | /* Swap kernel stack pointers */ | ||
1015 | l.sw TI_KSP(r10),r1 /* Save old stack pointer */ | ||
1016 | l.or r10,r4,r0 /* Set up new current_thread_info */ | ||
1017 | l.lwz r1,TI_KSP(r10) /* Load new stack pointer */ | ||
1018 | |||
1019 | /* Restore the old value of thread_info->ksp */ | ||
1020 | l.lwz r29,PT_SP(r1) | ||
1021 | l.sw TI_KSP(r10),r29 | ||
1022 | |||
1023 | /* ...and restore the registers, except r11 because the return value | ||
1024 | * has already been set above. | ||
1025 | */ | ||
1026 | l.lwz r2,PT_GPR2(r1) | ||
1027 | l.lwz r9,PT_GPR9(r1) | ||
1028 | /* No need to restore r10 */ | ||
1029 | /* ...and do not restore r11 */ | ||
1030 | |||
1031 | /* This is wrong, r12 shouldn't be here... but GCC is broken for the time being | ||
1032 | * and expects r12 to be callee-saved... */ | ||
1033 | l.lwz r12,PT_GPR12(r1) | ||
1034 | l.lwz r14,PT_GPR14(r1) | ||
1035 | l.lwz r16,PT_GPR16(r1) | ||
1036 | l.lwz r18,PT_GPR18(r1) | ||
1037 | l.lwz r20,PT_GPR20(r1) | ||
1038 | l.lwz r22,PT_GPR22(r1) | ||
1039 | l.lwz r24,PT_GPR24(r1) | ||
1040 | l.lwz r26,PT_GPR26(r1) | ||
1041 | l.lwz r28,PT_GPR28(r1) | ||
1042 | l.lwz r30,PT_GPR30(r1) | ||
1043 | |||
1044 | /* Unwind stack to pre-switch state */ | ||
1045 | l.addi r1,r1,(INT_FRAME_SIZE) | ||
1046 | |||
1047 | /* Return via the link-register back to where we 'came from', where that can be | ||
1048 | * either schedule() or return_from_fork()... */ | ||
1049 | l.jr r9 | ||
1050 | l.nop | ||
1051 | |||
1052 | /* ==================================================================== */ | ||
1053 | |||
1054 | /* These all use the delay slot for setting the argument register, so the | ||
1055 | * jump is always happening after the l.addi instruction. | ||
1056 | * | ||
1057 | * These are all just wrappers that don't touch the link-register r9, so the | ||
1058 | * return from the "real" syscall function will return back to the syscall | ||
1059 | * code that did the l.jal that brought us here. | ||
1060 | */ | ||
1061 | |||
1062 | /* fork requires that we save all the callee-saved registers because they | ||
1063 | * are all effectively clobbered by the call to _switch. Here we store | ||
1064 | * all the registers that aren't touched by the syscall fast path and thus | ||
1065 | * weren't saved there. | ||
1066 | */ | ||
1067 | |||
1068 | _fork_save_extra_regs_and_call: | ||
1069 | l.sw PT_GPR14(r1),r14 | ||
1070 | l.sw PT_GPR16(r1),r16 | ||
1071 | l.sw PT_GPR18(r1),r18 | ||
1072 | l.sw PT_GPR20(r1),r20 | ||
1073 | l.sw PT_GPR22(r1),r22 | ||
1074 | l.sw PT_GPR24(r1),r24 | ||
1075 | l.sw PT_GPR26(r1),r26 | ||
1076 | l.jr r29 | ||
1077 | l.sw PT_GPR28(r1),r28 | ||
1078 | |||
1079 | ENTRY(sys_clone) | ||
1080 | l.movhi r29,hi(_sys_clone) | ||
1081 | l.ori r29,r29,lo(_sys_clone) | ||
1082 | l.j _fork_save_extra_regs_and_call | ||
1083 | l.addi r7,r1,0 | ||
1084 | |||
1085 | ENTRY(sys_fork) | ||
1086 | l.movhi r29,hi(_sys_fork) | ||
1087 | l.ori r29,r29,lo(_sys_fork) | ||
1088 | l.j _fork_save_extra_regs_and_call | ||
1089 | l.addi r3,r1,0 | ||
1090 | |||
1091 | ENTRY(sys_execve) | ||
1092 | l.j _sys_execve | ||
1093 | l.addi r6,r1,0 | ||
1094 | |||
1095 | ENTRY(sys_sigaltstack) | ||
1096 | l.j _sys_sigaltstack | ||
1097 | l.addi r5,r1,0 | ||
1098 | |||
1099 | ENTRY(sys_rt_sigreturn) | ||
1100 | l.j _sys_rt_sigreturn | ||
1101 | l.addi r3,r1,0 | ||
1102 | |||
1103 | /* This is a catch-all syscall for atomic instructions for the OpenRISC 1000. | ||
1104 | * The functions takes a variable number of parameters depending on which | ||
1105 | * particular flavour of atomic you want... parameter 1 is a flag identifying | ||
1106 | * the atomic in question. Currently, this function implements the | ||
1107 | * following variants: | ||
1108 | * | ||
1109 | * XCHG: | ||
1110 | * @flag: 1 | ||
1111 | * @ptr1: | ||
1112 | * @ptr2: | ||
1113 | * Atomically exchange the values in pointers 1 and 2. | ||
1114 | * | ||
1115 | */ | ||
1116 | |||
1117 | ENTRY(sys_or1k_atomic) | ||
1118 | /* FIXME: This ignores r3 and always does an XCHG */ | ||
1119 | DISABLE_INTERRUPTS(r17,r19) | ||
1120 | l.lwz r30,0(r4) | ||
1121 | l.lwz r28,0(r5) | ||
1122 | l.sw 0(r4),r28 | ||
1123 | l.sw 0(r5),r30 | ||
1124 | ENABLE_INTERRUPTS(r17) | ||
1125 | l.jr r9 | ||
1126 | l.or r11,r0,r0 | ||
1127 | |||
1128 | /* ============================================================[ EOF ]=== */ | ||