Commit 141c943fd4b323bae2b47f67743dba96134afb1f
1 parent
79725df578
Exists in
master
and in
7 other branches
unicore32 core architecture: low level entry and setup codes
This patch implements low level entry and setup codes. Signed-off-by: Guan Xuetao <gxt@mprc.pku.edu.cn> Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Showing 6 changed files with 1820 additions and 0 deletions Side-by-side Diff
arch/unicore32/include/asm/traps.h
1 | +/* | |
2 | + * linux/arch/unicore32/include/asm/traps.h | |
3 | + * | |
4 | + * Code specific to PKUnity SoC and UniCore ISA | |
5 | + * | |
6 | + * Copyright (C) 2001-2010 GUAN Xue-tao | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License version 2 as | |
10 | + * published by the Free Software Foundation. | |
11 | + */ | |
12 | +#ifndef __UNICORE_TRAP_H__ | |
13 | +#define __UNICORE_TRAP_H__ | |
14 | + | |
15 | +extern void __init early_trap_init(void); | |
16 | +extern void dump_backtrace_entry(unsigned long where, | |
17 | + unsigned long from, unsigned long frame); | |
18 | + | |
19 | +extern void do_DataAbort(unsigned long addr, unsigned int fsr, | |
20 | + struct pt_regs *regs); | |
21 | +#endif |
arch/unicore32/kernel/entry.S
1 | +/* | |
2 | + * linux/arch/unicore32/kernel/entry.S | |
3 | + * | |
4 | + * Code specific to PKUnity SoC and UniCore ISA | |
5 | + * | |
6 | + * Copyright (C) 2001-2010 GUAN Xue-tao | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License version 2 as | |
10 | + * published by the Free Software Foundation. | |
11 | + * | |
12 | + * Low-level vector interface routines | |
13 | + */ | |
14 | +#include <linux/init.h> | |
15 | +#include <linux/linkage.h> | |
16 | +#include <asm/assembler.h> | |
17 | +#include <asm/errno.h> | |
18 | +#include <asm/thread_info.h> | |
19 | +#include <asm/memory.h> | |
20 | +#include <asm/unistd.h> | |
21 | +#include <generated/asm-offsets.h> | |
22 | +#include "debug-macro.S" | |
23 | + | |
24 | +@ | |
25 | +@ Most of the stack format comes from struct pt_regs, but with | |
26 | +@ the addition of 8 bytes for storing syscall args 5 and 6. | |
27 | +@ | |
28 | +#define S_OFF 8 | |
29 | + | |
30 | +/* | |
31 | + * The SWI code relies on the fact that R0 is at the bottom of the stack | |
32 | + * (due to slow/fast restore user regs). | |
33 | + */ | |
34 | +#if S_R0 != 0 | |
35 | +#error "Please fix" | |
36 | +#endif | |
37 | + | |
38 | + .macro zero_fp | |
39 | +#ifdef CONFIG_FRAME_POINTER | |
40 | + mov fp, #0 | |
41 | +#endif | |
42 | + .endm | |
43 | + | |
44 | + .macro alignment_trap, rtemp | |
45 | +#ifdef CONFIG_ALIGNMENT_TRAP | |
46 | + ldw \rtemp, .LCcralign | |
47 | + ldw \rtemp, [\rtemp] | |
48 | + movc p0.c1, \rtemp, #0 | |
49 | +#endif | |
50 | + .endm | |
51 | + | |
52 | + .macro load_user_sp_lr, rd, rtemp, offset = 0 | |
53 | + mov \rtemp, asr | |
54 | + xor \rtemp, \rtemp, #(PRIV_MODE ^ SUSR_MODE) | |
55 | + mov.a asr, \rtemp @ switch to the SUSR mode | |
56 | + | |
57 | + ldw sp, [\rd+], #\offset @ load sp_user | |
58 | + ldw lr, [\rd+], #\offset + 4 @ load lr_user | |
59 | + | |
60 | + xor \rtemp, \rtemp, #(PRIV_MODE ^ SUSR_MODE) | |
61 | + mov.a asr, \rtemp @ switch back to the PRIV mode | |
62 | + .endm | |
63 | + | |
64 | + .macro priv_exit, rpsr | |
65 | + mov.a bsr, \rpsr | |
66 | + ldm.w (r0 - r15), [sp]+ | |
67 | + ldm.b (r16 - pc), [sp]+ @ load r0 - pc, asr | |
68 | + .endm | |
69 | + | |
70 | + .macro restore_user_regs, fast = 0, offset = 0 | |
71 | + ldw r1, [sp+], #\offset + S_PSR @ get calling asr | |
72 | + ldw lr, [sp+], #\offset + S_PC @ get pc | |
73 | + mov.a bsr, r1 @ save in bsr_priv | |
74 | + .if \fast | |
75 | + add sp, sp, #\offset + S_R1 @ r0 is syscall return value | |
76 | + ldm.w (r1 - r15), [sp]+ @ get calling r1 - r15 | |
77 | + ldur (r16 - lr), [sp]+ @ get calling r16 - lr | |
78 | + .else | |
79 | + ldm.w (r0 - r15), [sp]+ @ get calling r0 - r15 | |
80 | + ldur (r16 - lr), [sp]+ @ get calling r16 - lr | |
81 | + .endif | |
82 | + nop | |
83 | + add sp, sp, #S_FRAME_SIZE - S_R16 | |
84 | + mov.a pc, lr @ return | |
85 | + @ and move bsr_priv into asr | |
86 | + .endm | |
87 | + | |
88 | + .macro get_thread_info, rd | |
89 | + mov \rd, sp >> #13 | |
90 | + mov \rd, \rd << #13 | |
91 | + .endm | |
92 | + | |
93 | + .macro get_irqnr_and_base, irqnr, irqstat, base, tmp | |
94 | + ldw \base, =(io_p2v(PKUNITY_INTC_BASE)) | |
95 | + ldw \irqstat, [\base+], #0xC @ INTC_ICIP | |
96 | + ldw \tmp, [\base+], #0x4 @ INTC_ICMR | |
97 | + and.a \irqstat, \irqstat, \tmp | |
98 | + beq 1001f | |
99 | + cntlz \irqnr, \irqstat | |
100 | + rsub \irqnr, \irqnr, #31 | |
101 | +1001: /* EQ will be set if no irqs pending */ | |
102 | + .endm | |
103 | + | |
104 | +#ifdef CONFIG_DEBUG_LL | |
105 | + .macro printreg, reg, temp | |
106 | + adr \temp, 901f | |
107 | + stm (r0-r3), [\temp]+ | |
108 | + stw lr, [\temp+], #0x10 | |
109 | + mov r0, \reg | |
110 | + b.l printhex8 | |
111 | + mov r0, #':' | |
112 | + b.l printch | |
113 | + mov r0, pc | |
114 | + b.l printhex8 | |
115 | + adr r0, 902f | |
116 | + b.l printascii | |
117 | + adr \temp, 901f | |
118 | + ldm (r0-r3), [\temp]+ | |
119 | + ldw lr, [\temp+], #0x10 | |
120 | + b 903f | |
121 | +901: .word 0, 0, 0, 0, 0 @ r0-r3, lr | |
122 | +902: .asciz ": epip4d\n" | |
123 | + .align | |
124 | +903: | |
125 | + .endm | |
126 | +#endif | |
127 | + | |
128 | +/* | |
129 | + * These are the registers used in the syscall handler, and allow us to | |
130 | + * have in theory up to 7 arguments to a function - r0 to r6. | |
131 | + * | |
132 | + * Note that tbl == why is intentional. | |
133 | + * | |
134 | + * We must set at least "tsk" and "why" when calling ret_with_reschedule. | |
135 | + */ | |
136 | +scno .req r21 @ syscall number | |
137 | +tbl .req r22 @ syscall table pointer | |
138 | +why .req r22 @ Linux syscall (!= 0) | |
139 | +tsk .req r23 @ current thread_info | |
140 | + | |
141 | +/* | |
142 | + * Interrupt handling. Preserves r17, r18, r19 | |
143 | + */ | |
144 | + .macro intr_handler | |
145 | +1: get_irqnr_and_base r0, r6, r5, lr | |
146 | + beq 2f | |
147 | + mov r1, sp | |
148 | + @ | |
149 | + @ routine called with r0 = irq number, r1 = struct pt_regs * | |
150 | + @ | |
151 | + adr lr, 1b | |
152 | + b asm_do_IRQ | |
153 | +2: | |
154 | + .endm | |
155 | + | |
156 | +/* | |
157 | + * PRIV mode handlers | |
158 | + */ | |
159 | + .macro priv_entry | |
160 | + sub sp, sp, #(S_FRAME_SIZE - 4) | |
161 | + stm (r1 - r15), [sp]+ | |
162 | + add r5, sp, #S_R15 | |
163 | + stm (r16 - r28), [r5]+ | |
164 | + | |
165 | + ldm (r1 - r3), [r0]+ | |
166 | + add r5, sp, #S_SP - 4 @ here for interlock avoidance | |
167 | + mov r4, #-1 @ "" "" "" "" | |
168 | + add r0, sp, #(S_FRAME_SIZE - 4) | |
169 | + stw.w r1, [sp+], #-4 @ save the "real" r0 copied | |
170 | + @ from the exception stack | |
171 | + | |
172 | + mov r1, lr | |
173 | + | |
174 | + @ | |
175 | + @ We are now ready to fill in the remaining blanks on the stack: | |
176 | + @ | |
177 | + @ r0 - sp_priv | |
178 | + @ r1 - lr_priv | |
179 | + @ r2 - lr_<exception>, already fixed up for correct return/restart | |
180 | + @ r3 - bsr_<exception> | |
181 | + @ r4 - orig_r0 (see pt_regs definition in ptrace.h) | |
182 | + @ | |
183 | + stm (r0 - r4), [r5]+ | |
184 | + .endm | |
185 | + | |
186 | +/* | |
187 | + * User mode handlers | |
188 | + * | |
189 | + */ | |
190 | + .macro user_entry | |
191 | + sub sp, sp, #S_FRAME_SIZE | |
192 | + stm (r1 - r15), [sp+] | |
193 | + add r4, sp, #S_R16 | |
194 | + stm (r16 - r28), [r4]+ | |
195 | + | |
196 | + ldm (r1 - r3), [r0]+ | |
197 | + add r0, sp, #S_PC @ here for interlock avoidance | |
198 | + mov r4, #-1 @ "" "" "" "" | |
199 | + | |
200 | + stw r1, [sp] @ save the "real" r0 copied | |
201 | + @ from the exception stack | |
202 | + | |
203 | + @ | |
204 | + @ We are now ready to fill in the remaining blanks on the stack: | |
205 | + @ | |
206 | + @ r2 - lr_<exception>, already fixed up for correct return/restart | |
207 | + @ r3 - bsr_<exception> | |
208 | + @ r4 - orig_r0 (see pt_regs definition in ptrace.h) | |
209 | + @ | |
210 | + @ Also, separately save sp_user and lr_user | |
211 | + @ | |
212 | + stm (r2 - r4), [r0]+ | |
213 | + stur (sp, lr), [r0-] | |
214 | + | |
215 | + @ | |
216 | + @ Enable the alignment trap while in kernel mode | |
217 | + @ | |
218 | + alignment_trap r0 | |
219 | + | |
220 | + @ | |
221 | + @ Clear FP to mark the first stack frame | |
222 | + @ | |
223 | + zero_fp | |
224 | + .endm | |
225 | + | |
226 | + .text | |
227 | + | |
228 | +@ | |
229 | +@ __invalid - generic code for failed exception | |
230 | +@ (re-entrant version of handlers) | |
231 | +@ | |
232 | +__invalid: | |
233 | + sub sp, sp, #S_FRAME_SIZE | |
234 | + stm (r1 - r15), [sp+] | |
235 | + add r1, sp, #S_R16 | |
236 | + stm (r16 - r28, sp, lr), [r1]+ | |
237 | + | |
238 | + zero_fp | |
239 | + | |
240 | + ldm (r4 - r6), [r0]+ | |
241 | + add r0, sp, #S_PC @ here for interlock avoidance | |
242 | + mov r7, #-1 @ "" "" "" "" | |
243 | + stw r4, [sp] @ save preserved r0 | |
244 | + stm (r5 - r7), [r0]+ @ lr_<exception>, | |
245 | + @ asr_<exception>, "old_r0" | |
246 | + | |
247 | + mov r0, sp | |
248 | + mov r1, asr | |
249 | + b bad_mode | |
250 | +ENDPROC(__invalid) | |
251 | + | |
252 | + .align 5 | |
253 | +__dabt_priv: | |
254 | + priv_entry | |
255 | + | |
256 | + @ | |
257 | + @ get ready to re-enable interrupts if appropriate | |
258 | + @ | |
259 | + mov r17, asr | |
260 | + cand.a r3, #PSR_I_BIT | |
261 | + bne 1f | |
262 | + andn r17, r17, #PSR_I_BIT | |
263 | +1: | |
264 | + | |
265 | + @ | |
266 | + @ Call the processor-specific abort handler: | |
267 | + @ | |
268 | + @ r2 - aborted context pc | |
269 | + @ r3 - aborted context asr | |
270 | + @ | |
271 | + @ The abort handler must return the aborted address in r0, and | |
272 | + @ the fault status register in r1. | |
273 | + @ | |
274 | + movc r1, p0.c3, #0 @ get FSR | |
275 | + movc r0, p0.c4, #0 @ get FAR | |
276 | + | |
277 | + @ | |
278 | + @ set desired INTR state, then call main handler | |
279 | + @ | |
280 | + mov.a asr, r17 | |
281 | + mov r2, sp | |
282 | + b.l do_DataAbort | |
283 | + | |
284 | + @ | |
285 | + @ INTRs off again before pulling preserved data off the stack | |
286 | + @ | |
287 | + disable_irq r0 | |
288 | + | |
289 | + @ | |
290 | + @ restore BSR and restart the instruction | |
291 | + @ | |
292 | + ldw r2, [sp+], #S_PSR | |
293 | + priv_exit r2 @ return from exception | |
294 | +ENDPROC(__dabt_priv) | |
295 | + | |
296 | + .align 5 | |
297 | +__intr_priv: | |
298 | + priv_entry | |
299 | + | |
300 | + intr_handler | |
301 | + | |
302 | + mov r0, #0 @ epip4d | |
303 | + movc p0.c5, r0, #14 | |
304 | + nop; nop; nop; nop; nop; nop; nop; nop | |
305 | + | |
306 | + ldw r4, [sp+], #S_PSR @ irqs are already disabled | |
307 | + | |
308 | + priv_exit r4 @ return from exception | |
309 | +ENDPROC(__intr_priv) | |
310 | + | |
311 | + .ltorg | |
312 | + | |
313 | + .align 5 | |
314 | +__extn_priv: | |
315 | + priv_entry | |
316 | + | |
317 | + mov r0, sp @ struct pt_regs *regs | |
318 | + mov r1, asr | |
319 | + b bad_mode @ not supported | |
320 | +ENDPROC(__extn_priv) | |
321 | + | |
322 | + .align 5 | |
323 | +__pabt_priv: | |
324 | + priv_entry | |
325 | + | |
326 | + @ | |
327 | + @ re-enable interrupts if appropriate | |
328 | + @ | |
329 | + mov r17, asr | |
330 | + cand.a r3, #PSR_I_BIT | |
331 | + bne 1f | |
332 | + andn r17, r17, #PSR_I_BIT | |
333 | +1: | |
334 | + | |
335 | + @ | |
336 | + @ set args, then call main handler | |
337 | + @ | |
338 | + @ r0 - address of faulting instruction | |
339 | + @ r1 - pointer to registers on stack | |
340 | + @ | |
341 | + mov r0, r2 @ pass address of aborted instruction | |
342 | + mov r1, #5 | |
343 | + mov.a asr, r17 | |
344 | + mov r2, sp @ regs | |
345 | + b.l do_PrefetchAbort @ call abort handler | |
346 | + | |
347 | + @ | |
348 | + @ INTRs off again before pulling preserved data off the stack | |
349 | + @ | |
350 | + disable_irq r0 | |
351 | + | |
352 | + @ | |
353 | + @ restore BSR and restart the instruction | |
354 | + @ | |
355 | + ldw r2, [sp+], #S_PSR | |
356 | + priv_exit r2 @ return from exception | |
357 | +ENDPROC(__pabt_priv) | |
358 | + | |
359 | + .align 5 | |
360 | +.LCcralign: | |
361 | + .word cr_alignment | |
362 | + | |
363 | + .align 5 | |
364 | +__dabt_user: | |
365 | + user_entry | |
366 | + | |
367 | +#ifdef CONFIG_UNICORE_FPU_F64 | |
368 | + cff ip, s31 | |
369 | + cand.a ip, #0x08000000 @ FPU execption traps? | |
370 | + beq 209f | |
371 | + | |
372 | + ldw ip, [sp+], #S_PC | |
373 | + add ip, ip, #4 | |
374 | + stw ip, [sp+], #S_PC | |
375 | + @ | |
376 | + @ fall through to the emulation code, which returns using r19 if | |
377 | + @ it has emulated the instruction, or the more conventional lr | |
378 | + @ if we are to treat this as a real extended instruction | |
379 | + @ | |
380 | + @ r0 - instruction | |
381 | + @ | |
382 | +1: ldw.u r0, [r2] | |
383 | + adr r19, ret_from_exception | |
384 | + adr lr, 209f | |
385 | + @ | |
386 | + @ fallthrough to call do_uc_f64 | |
387 | + @ | |
388 | +/* | |
389 | + * Check whether the instruction is a co-processor instruction. | |
390 | + * If yes, we need to call the relevant co-processor handler. | |
391 | + * | |
392 | + * Note that we don't do a full check here for the co-processor | |
393 | + * instructions; all instructions with bit 27 set are well | |
394 | + * defined. The only instructions that should fault are the | |
395 | + * co-processor instructions. | |
396 | + * | |
397 | + * Emulators may wish to make use of the following registers: | |
398 | + * r0 = instruction opcode. | |
399 | + * r2 = PC | |
400 | + * r19 = normal "successful" return address | |
401 | + * r20 = this threads thread_info structure. | |
402 | + * lr = unrecognised instruction return address | |
403 | + */ | |
404 | + get_thread_info r20 @ get current thread | |
405 | + and r8, r0, #0x00003c00 @ mask out CP number | |
406 | + mov r7, #1 | |
407 | + stb r7, [r20+], #TI_USED_CP + 2 @ set appropriate used_cp[] | |
408 | + | |
409 | + @ F64 hardware support entry point. | |
410 | + @ r0 = faulted instruction | |
411 | + @ r19 = return address | |
412 | + @ r20 = fp_state | |
413 | + enable_irq r4 | |
414 | + add r20, r20, #TI_FPSTATE @ r20 = workspace | |
415 | + cff r1, s31 @ get fpu FPSCR | |
416 | + andn r2, r1, #0x08000000 | |
417 | + ctf r2, s31 @ clear 27 bit | |
418 | + mov r2, sp @ nothing stacked - regdump is at TOS | |
419 | + mov lr, r19 @ setup for a return to the user code | |
420 | + | |
421 | + @ Now call the C code to package up the bounce to the support code | |
422 | + @ r0 holds the trigger instruction | |
423 | + @ r1 holds the FPSCR value | |
424 | + @ r2 pointer to register dump | |
425 | + b ucf64_exchandler | |
426 | +209: | |
427 | +#endif | |
428 | + @ | |
429 | + @ Call the processor-specific abort handler: | |
430 | + @ | |
431 | + @ r2 - aborted context pc | |
432 | + @ r3 - aborted context asr | |
433 | + @ | |
434 | + @ The abort handler must return the aborted address in r0, and | |
435 | + @ the fault status register in r1. | |
436 | + @ | |
437 | + movc r1, p0.c3, #0 @ get FSR | |
438 | + movc r0, p0.c4, #0 @ get FAR | |
439 | + | |
440 | + @ | |
441 | + @ INTRs on, then call the main handler | |
442 | + @ | |
443 | + enable_irq r2 | |
444 | + mov r2, sp | |
445 | + adr lr, ret_from_exception | |
446 | + b do_DataAbort | |
447 | +ENDPROC(__dabt_user) | |
448 | + | |
449 | + .align 5 | |
450 | +__intr_user: | |
451 | + user_entry | |
452 | + | |
453 | + get_thread_info tsk | |
454 | + | |
455 | + intr_handler | |
456 | + | |
457 | + mov why, #0 | |
458 | + b ret_to_user | |
459 | +ENDPROC(__intr_user) | |
460 | + | |
461 | + .ltorg | |
462 | + | |
463 | + .align 5 | |
464 | +__extn_user: | |
465 | + user_entry | |
466 | + | |
467 | + mov r0, sp | |
468 | + mov r1, asr | |
469 | + b bad_mode | |
470 | +ENDPROC(__extn_user) | |
471 | + | |
472 | + .align 5 | |
473 | +__pabt_user: | |
474 | + user_entry | |
475 | + | |
476 | + mov r0, r2 @ pass address of aborted instruction. | |
477 | + mov r1, #5 | |
478 | + enable_irq r1 @ Enable interrupts | |
479 | + mov r2, sp @ regs | |
480 | + b.l do_PrefetchAbort @ call abort handler | |
481 | + /* fall through */ | |
482 | +/* | |
483 | + * This is the return code to user mode for abort handlers | |
484 | + */ | |
485 | +ENTRY(ret_from_exception) | |
486 | + get_thread_info tsk | |
487 | + mov why, #0 | |
488 | + b ret_to_user | |
489 | +ENDPROC(__pabt_user) | |
490 | +ENDPROC(ret_from_exception) | |
491 | + | |
492 | +/* | |
493 | + * Register switch for UniCore V2 processors | |
494 | + * r0 = previous task_struct, r1 = previous thread_info, r2 = next thread_info | |
495 | + * previous and next are guaranteed not to be the same. | |
496 | + */ | |
497 | +ENTRY(__switch_to) | |
498 | + add ip, r1, #TI_CPU_SAVE | |
499 | + stm.w (r4 - r15), [ip]+ | |
500 | + stm.w (r16 - r27, sp, lr), [ip]+ | |
501 | + | |
502 | +#ifdef CONFIG_UNICORE_FPU_F64 | |
503 | + add ip, r1, #TI_FPSTATE | |
504 | + sfm.w (f0 - f7 ), [ip]+ | |
505 | + sfm.w (f8 - f15), [ip]+ | |
506 | + sfm.w (f16 - f23), [ip]+ | |
507 | + sfm.w (f24 - f31), [ip]+ | |
508 | + cff r4, s31 | |
509 | + stw r4, [ip] | |
510 | + | |
511 | + add ip, r2, #TI_FPSTATE | |
512 | + lfm.w (f0 - f7 ), [ip]+ | |
513 | + lfm.w (f8 - f15), [ip]+ | |
514 | + lfm.w (f16 - f23), [ip]+ | |
515 | + lfm.w (f24 - f31), [ip]+ | |
516 | + ldw r4, [ip] | |
517 | + ctf r4, s31 | |
518 | +#endif | |
519 | + add ip, r2, #TI_CPU_SAVE | |
520 | + ldm.w (r4 - r15), [ip]+ | |
521 | + ldm (r16 - r27, sp, pc), [ip]+ @ Load all regs saved previously | |
522 | +ENDPROC(__switch_to) | |
523 | + | |
524 | + .align 5 | |
525 | +/* | |
526 | + * This is the fast syscall return path. We do as little as | |
527 | + * possible here, and this includes saving r0 back into the PRIV | |
528 | + * stack. | |
529 | + */ | |
530 | +ret_fast_syscall: | |
531 | + disable_irq r1 @ disable interrupts | |
532 | + ldw r1, [tsk+], #TI_FLAGS | |
533 | + cand.a r1, #_TIF_WORK_MASK | |
534 | + bne fast_work_pending | |
535 | + | |
536 | + @ fast_restore_user_regs | |
537 | + restore_user_regs fast = 1, offset = S_OFF | |
538 | + | |
539 | +/* | |
540 | + * Ok, we need to do extra processing, enter the slow path. | |
541 | + */ | |
542 | +fast_work_pending: | |
543 | + stw.w r0, [sp+], #S_R0+S_OFF @ returned r0 | |
544 | +work_pending: | |
545 | + cand.a r1, #_TIF_NEED_RESCHED | |
546 | + bne work_resched | |
547 | + cand.a r1, #_TIF_SIGPENDING|_TIF_NOTIFY_RESUME | |
548 | + beq no_work_pending | |
549 | + mov r0, sp @ 'regs' | |
550 | + mov r2, why @ 'syscall' | |
551 | + cand.a r1, #_TIF_SIGPENDING @ delivering a signal? | |
552 | + cmovne why, #0 @ prevent further restarts | |
553 | + b.l do_notify_resume | |
554 | + b ret_slow_syscall @ Check work again | |
555 | + | |
556 | +work_resched: | |
557 | + b.l schedule | |
558 | +/* | |
559 | + * "slow" syscall return path. "why" tells us if this was a real syscall. | |
560 | + */ | |
561 | +ENTRY(ret_to_user) | |
562 | +ret_slow_syscall: | |
563 | + disable_irq r1 @ disable interrupts | |
564 | + get_thread_info tsk @ epip4d, one path error?! | |
565 | + ldw r1, [tsk+], #TI_FLAGS | |
566 | + cand.a r1, #_TIF_WORK_MASK | |
567 | + bne work_pending | |
568 | +no_work_pending: | |
569 | + @ slow_restore_user_regs | |
570 | + restore_user_regs fast = 0, offset = 0 | |
571 | +ENDPROC(ret_to_user) | |
572 | + | |
573 | +/* | |
574 | + * This is how we return from a fork. | |
575 | + */ | |
576 | +ENTRY(ret_from_fork) | |
577 | + b.l schedule_tail | |
578 | + get_thread_info tsk | |
579 | + ldw r1, [tsk+], #TI_FLAGS @ check for syscall tracing | |
580 | + mov why, #1 | |
581 | + cand.a r1, #_TIF_SYSCALL_TRACE @ are we tracing syscalls? | |
582 | + beq ret_slow_syscall | |
583 | + mov r1, sp | |
584 | + mov r0, #1 @ trace exit [IP = 1] | |
585 | + b.l syscall_trace | |
586 | + b ret_slow_syscall | |
587 | +ENDPROC(ret_from_fork) | |
588 | + | |
589 | +/*============================================================================= | |
590 | + * SWI handler | |
591 | + *----------------------------------------------------------------------------- | |
592 | + */ | |
593 | + .align 5 | |
594 | +ENTRY(vector_swi) | |
595 | + sub sp, sp, #S_FRAME_SIZE | |
596 | + stm (r0 - r15), [sp]+ @ Calling r0 - r15 | |
597 | + add r8, sp, #S_R16 | |
598 | + stm (r16 - r28), [r8]+ @ Calling r16 - r28 | |
599 | + add r8, sp, #S_PC | |
600 | + stur (sp, lr), [r8-] @ Calling sp, lr | |
601 | + mov r8, bsr @ called from non-REAL mode | |
602 | + stw lr, [sp+], #S_PC @ Save calling PC | |
603 | + stw r8, [sp+], #S_PSR @ Save ASR | |
604 | + stw r0, [sp+], #S_OLD_R0 @ Save OLD_R0 | |
605 | + zero_fp | |
606 | + | |
607 | + /* | |
608 | + * Get the system call number. | |
609 | + */ | |
610 | + sub ip, lr, #4 | |
611 | + ldw.u scno, [ip] @ get SWI instruction | |
612 | + | |
613 | +#ifdef CONFIG_ALIGNMENT_TRAP | |
614 | + ldw ip, __cr_alignment | |
615 | + ldw ip, [ip] | |
616 | + movc p0.c1, ip, #0 @ update control register | |
617 | +#endif | |
618 | + enable_irq ip | |
619 | + | |
620 | + get_thread_info tsk | |
621 | + ldw tbl, =sys_call_table @ load syscall table pointer | |
622 | + | |
623 | + andn scno, scno, #0xff000000 @ mask off SWI op-code | |
624 | + andn scno, scno, #0x00ff0000 @ mask off SWI op-code | |
625 | + | |
626 | + stm.w (r4, r5), [sp-] @ push fifth and sixth args | |
627 | + ldw ip, [tsk+], #TI_FLAGS @ check for syscall tracing | |
628 | + cand.a ip, #_TIF_SYSCALL_TRACE @ are we tracing syscalls? | |
629 | + bne __sys_trace | |
630 | + | |
631 | + csub.a scno, #__NR_syscalls @ check upper syscall limit | |
632 | + adr lr, ret_fast_syscall @ return address | |
633 | + bea 1f | |
634 | + ldw pc, [tbl+], scno << #2 @ call sys_* routine | |
635 | +1: | |
636 | + add r1, sp, #S_OFF | |
637 | +2: mov why, #0 @ no longer a real syscall | |
638 | + b sys_ni_syscall @ not private func | |
639 | + | |
640 | + /* | |
641 | + * This is the really slow path. We're going to be doing | |
642 | + * context switches, and waiting for our parent to respond. | |
643 | + */ | |
644 | +__sys_trace: | |
645 | + mov r2, scno | |
646 | + add r1, sp, #S_OFF | |
647 | + mov r0, #0 @ trace entry [IP = 0] | |
648 | + b.l syscall_trace | |
649 | + | |
650 | + adr lr, __sys_trace_return @ return address | |
651 | + mov scno, r0 @ syscall number (possibly new) | |
652 | + add r1, sp, #S_R0 + S_OFF @ pointer to regs | |
653 | + csub.a scno, #__NR_syscalls @ check upper syscall limit | |
654 | + bea 2b | |
655 | + ldm (r0 - r3), [r1]+ @ have to reload r0 - r3 | |
656 | + ldw pc, [tbl+], scno << #2 @ call sys_* routine | |
657 | + | |
658 | +__sys_trace_return: | |
659 | + stw.w r0, [sp+], #S_R0 + S_OFF @ save returned r0 | |
660 | + mov r2, scno | |
661 | + mov r1, sp | |
662 | + mov r0, #1 @ trace exit [IP = 1] | |
663 | + b.l syscall_trace | |
664 | + b ret_slow_syscall | |
665 | + | |
666 | + .align 5 | |
667 | +#ifdef CONFIG_ALIGNMENT_TRAP | |
668 | + .type __cr_alignment, #object | |
669 | +__cr_alignment: | |
670 | + .word cr_alignment | |
671 | +#endif | |
672 | + .ltorg | |
673 | + | |
674 | +ENTRY(sys_execve) | |
675 | + add r3, sp, #S_OFF | |
676 | + b __sys_execve | |
677 | +ENDPROC(sys_execve) | |
678 | + | |
679 | +ENTRY(sys_clone) | |
680 | + add ip, sp, #S_OFF | |
681 | + stw ip, [sp+], #4 | |
682 | + b __sys_clone | |
683 | +ENDPROC(sys_clone) | |
684 | + | |
685 | +ENTRY(sys_rt_sigreturn) | |
686 | + add r0, sp, #S_OFF | |
687 | + mov why, #0 @ prevent syscall restart handling | |
688 | + b __sys_rt_sigreturn | |
689 | +ENDPROC(sys_rt_sigreturn) | |
690 | + | |
691 | +ENTRY(sys_sigaltstack) | |
692 | + ldw r2, [sp+], #S_OFF + S_SP | |
693 | + b do_sigaltstack | |
694 | +ENDPROC(sys_sigaltstack) | |
695 | + | |
696 | + __INIT | |
697 | + | |
698 | +/* | |
699 | + * Vector stubs. | |
700 | + * | |
701 | + * This code is copied to 0xffff0200 so we can use branches in the | |
702 | + * vectors, rather than ldr's. Note that this code must not | |
703 | + * exceed 0x300 bytes. | |
704 | + * | |
705 | + * Common stub entry macro: | |
706 | + * Enter in INTR mode, bsr = PRIV/USER ASR, lr = PRIV/USER PC | |
707 | + * | |
708 | + * SP points to a minimal amount of processor-private memory, the address | |
709 | + * of which is copied into r0 for the mode specific abort handler. | |
710 | + */ | |
711 | + .macro vector_stub, name, mode | |
712 | + .align 5 | |
713 | + | |
714 | +vector_\name: | |
715 | + @ | |
716 | + @ Save r0, lr_<exception> (parent PC) and bsr_<exception> | |
717 | + @ (parent ASR) | |
718 | + @ | |
719 | + stw r0, [sp] | |
720 | + stw lr, [sp+], #4 @ save r0, lr | |
721 | + mov lr, bsr | |
722 | + stw lr, [sp+], #8 @ save bsr | |
723 | + | |
724 | + @ | |
725 | + @ Prepare for PRIV mode. INTRs remain disabled. | |
726 | + @ | |
727 | + mov r0, asr | |
728 | + xor r0, r0, #(\mode ^ PRIV_MODE) | |
729 | + mov.a bsr, r0 | |
730 | + | |
731 | + @ | |
732 | + @ the branch table must immediately follow this code | |
733 | + @ | |
734 | + and lr, lr, #0x03 | |
735 | + add lr, lr, #1 | |
736 | + mov r0, sp | |
737 | + ldw lr, [pc+], lr << #2 | |
738 | + mov.a pc, lr @ branch to handler in PRIV mode | |
739 | +ENDPROC(vector_\name) | |
740 | + .align 2 | |
741 | + @ handler addresses follow this label | |
742 | + .endm | |
743 | + | |
744 | + .globl __stubs_start | |
745 | +__stubs_start: | |
746 | +/* | |
747 | + * Interrupt dispatcher | |
748 | + */ | |
749 | + vector_stub intr, INTR_MODE | |
750 | + | |
751 | + .long __intr_user @ 0 (USER) | |
752 | + .long __invalid @ 1 | |
753 | + .long __invalid @ 2 | |
754 | + .long __intr_priv @ 3 (PRIV) | |
755 | + | |
756 | +/* | |
757 | + * Data abort dispatcher | |
758 | + * Enter in ABT mode, bsr = USER ASR, lr = USER PC | |
759 | + */ | |
760 | + vector_stub dabt, ABRT_MODE | |
761 | + | |
762 | + .long __dabt_user @ 0 (USER) | |
763 | + .long __invalid @ 1 | |
764 | + .long __invalid @ 2 (INTR) | |
765 | + .long __dabt_priv @ 3 (PRIV) | |
766 | + | |
767 | +/* | |
768 | + * Prefetch abort dispatcher | |
769 | + * Enter in ABT mode, bsr = USER ASR, lr = USER PC | |
770 | + */ | |
771 | + vector_stub pabt, ABRT_MODE | |
772 | + | |
773 | + .long __pabt_user @ 0 (USER) | |
774 | + .long __invalid @ 1 | |
775 | + .long __invalid @ 2 (INTR) | |
776 | + .long __pabt_priv @ 3 (PRIV) | |
777 | + | |
778 | +/* | |
779 | + * Undef instr entry dispatcher | |
780 | + * Enter in EXTN mode, bsr = PRIV/USER ASR, lr = PRIV/USER PC | |
781 | + */ | |
782 | + vector_stub extn, EXTN_MODE | |
783 | + | |
784 | + .long __extn_user @ 0 (USER) | |
785 | + .long __invalid @ 1 | |
786 | + .long __invalid @ 2 (INTR) | |
787 | + .long __extn_priv @ 3 (PRIV) | |
788 | + | |
789 | +/* | |
790 | + * We group all the following data together to optimise | |
791 | + * for CPUs with separate I & D caches. | |
792 | + */ | |
793 | + .align 5 | |
794 | + | |
795 | +.LCvswi: | |
796 | + .word vector_swi | |
797 | + | |
798 | + .globl __stubs_end | |
799 | +__stubs_end: | |
800 | + | |
801 | + .equ stubs_offset, __vectors_start + 0x200 - __stubs_start | |
802 | + | |
803 | + .globl __vectors_start | |
804 | +__vectors_start: | |
805 | + jepriv SYS_ERROR0 | |
806 | + b vector_extn + stubs_offset | |
807 | + ldw pc, .LCvswi + stubs_offset | |
808 | + b vector_pabt + stubs_offset | |
809 | + b vector_dabt + stubs_offset | |
810 | + jepriv SYS_ERROR0 | |
811 | + b vector_intr + stubs_offset | |
812 | + jepriv SYS_ERROR0 | |
813 | + | |
814 | + .globl __vectors_end | |
815 | +__vectors_end: | |
816 | + | |
817 | + .data | |
818 | + | |
819 | + .globl cr_alignment | |
820 | + .globl cr_no_alignment | |
821 | +cr_alignment: | |
822 | + .space 4 | |
823 | +cr_no_alignment: | |
824 | + .space 4 |
arch/unicore32/kernel/head.S
1 | +/* | |
2 | + * linux/arch/unicore32/kernel/head.S | |
3 | + * | |
4 | + * Code specific to PKUnity SoC and UniCore ISA | |
5 | + * | |
6 | + * Copyright (C) 2001-2010 GUAN Xue-tao | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License version 2 as | |
10 | + * published by the Free Software Foundation. | |
11 | + */ | |
12 | +#include <linux/linkage.h> | |
13 | +#include <linux/init.h> | |
14 | + | |
15 | +#include <asm/assembler.h> | |
16 | +#include <asm/ptrace.h> | |
17 | +#include <generated/asm-offsets.h> | |
18 | +#include <asm/memory.h> | |
19 | +#include <asm/thread_info.h> | |
20 | +#include <asm/system.h> | |
21 | +#include <asm/pgtable-hwdef.h> | |
22 | + | |
23 | +#if (PHYS_OFFSET & 0x003fffff) | |
24 | +#error "PHYS_OFFSET must be at an even 4MiB boundary!" | |
25 | +#endif | |
26 | + | |
27 | +#define KERNEL_RAM_VADDR (PAGE_OFFSET + KERNEL_IMAGE_START) | |
28 | +#define KERNEL_RAM_PADDR (PHYS_OFFSET + KERNEL_IMAGE_START) | |
29 | + | |
30 | +#define KERNEL_PGD_PADDR (KERNEL_RAM_PADDR - 0x1000) | |
31 | +#define KERNEL_PGD_VADDR (KERNEL_RAM_VADDR - 0x1000) | |
32 | + | |
33 | +#define KERNEL_START KERNEL_RAM_VADDR | |
34 | +#define KERNEL_END _end | |
35 | + | |
36 | +/* | |
37 | + * swapper_pg_dir is the virtual address of the initial page table. | |
38 | + * We place the page tables 4K below KERNEL_RAM_VADDR. Therefore, we must | |
39 | + * make sure that KERNEL_RAM_VADDR is correctly set. Currently, we expect | |
40 | + * the least significant 16 bits to be 0x8000, but we could probably | |
41 | + * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x1000. | |
42 | + */ | |
43 | +#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000 | |
44 | +#error KERNEL_RAM_VADDR must start at 0xXXXX8000 | |
45 | +#endif | |
46 | + | |
47 | + .globl swapper_pg_dir | |
48 | + .equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x1000 | |
49 | + | |
50 | +/* | |
51 | + * Kernel startup entry point. | |
52 | + * --------------------------- | |
53 | + * | |
54 | + * This is normally called from the decompressor code. The requirements | |
55 | + * are: MMU = off, D-cache = off, I-cache = dont care | |
56 | + * | |
57 | + * This code is mostly position independent, so if you link the kernel at | |
58 | + * 0xc0008000, you call this at __pa(0xc0008000). | |
59 | + */ | |
60 | + __HEAD | |
61 | +ENTRY(stext) | |
62 | + @ set asr | |
63 | + mov r0, #PRIV_MODE @ ensure priv mode | |
64 | + or r0, #PSR_R_BIT | PSR_I_BIT @ disable irqs | |
65 | + mov.a asr, r0 | |
66 | + | |
67 | + @ process identify | |
68 | + movc r0, p0.c0, #0 @ cpuid | |
69 | + movl r1, 0xff00ffff @ mask | |
70 | + movl r2, 0x4d000863 @ value | |
71 | + and r0, r1, r0 | |
72 | + cxor.a r0, r2 | |
73 | + bne __error_p @ invalid processor id | |
74 | + | |
75 | + /* | |
76 | + * Clear the 4K level 1 swapper page table | |
77 | + */ | |
78 | + movl r0, #KERNEL_PGD_PADDR @ page table address | |
79 | + mov r1, #0 | |
80 | + add r2, r0, #0x1000 | |
81 | +101: stw.w r1, [r0]+, #4 | |
82 | + stw.w r1, [r0]+, #4 | |
83 | + stw.w r1, [r0]+, #4 | |
84 | + stw.w r1, [r0]+, #4 | |
85 | + cxor.a r0, r2 | |
86 | + bne 101b | |
87 | + | |
88 | + movl r4, #KERNEL_PGD_PADDR @ page table address | |
89 | + mov r7, #PMD_TYPE_SECT | PMD_PRESENT @ page size: section | |
90 | + or r7, r7, #PMD_SECT_CACHEABLE @ cacheable | |
91 | + or r7, r7, #PMD_SECT_READ | PMD_SECT_WRITE | PMD_SECT_EXEC | |
92 | + | |
93 | + /* | |
94 | + * Create identity mapping for first 4MB of kernel to | |
95 | + * cater for the MMU enable. This identity mapping | |
96 | + * will be removed by paging_init(). We use our current program | |
97 | + * counter to determine corresponding section base address. | |
98 | + */ | |
99 | + mov r6, pc | |
100 | + mov r6, r6 >> #22 @ start of kernel section | |
101 | + or r1, r7, r6 << #22 @ flags + kernel base | |
102 | + stw r1, [r4+], r6 << #2 @ identity mapping | |
103 | + | |
104 | + /* | |
105 | + * Now setup the pagetables for our kernel direct | |
106 | + * mapped region. | |
107 | + */ | |
108 | + add r0, r4, #(KERNEL_START & 0xff000000) >> 20 | |
109 | + stw.w r1, [r0+], #(KERNEL_START & 0x00c00000) >> 20 | |
110 | + movl r6, #(KERNEL_END - 1) | |
111 | + add r0, r0, #4 | |
112 | + add r6, r4, r6 >> #20 | |
113 | +102: csub.a r0, r6 | |
114 | + add r1, r1, #1 << 22 | |
115 | + bua 103f | |
116 | + stw.w r1, [r0]+, #4 | |
117 | + b 102b | |
118 | +103: | |
119 | + /* | |
120 | + * Then map first 4MB of ram in case it contains our boot params. | |
121 | + */ | |
122 | + add r0, r4, #PAGE_OFFSET >> 20 | |
123 | + or r6, r7, #(PHYS_OFFSET & 0xffc00000) | |
124 | + stw r6, [r0] | |
125 | + | |
126 | + ldw r15, __switch_data @ address to jump to after | |
127 | + | |
128 | + /* | |
129 | + * Initialise TLB, Caches, and MMU state ready to switch the MMU | |
130 | + * on. | |
131 | + */ | |
132 | + mov r0, #0 | |
133 | + movc p0.c5, r0, #28 @ cache invalidate all | |
134 | + nop8 | |
135 | + movc p0.c6, r0, #6 @ TLB invalidate all | |
136 | + nop8 | |
137 | + | |
138 | + /* | |
139 | + * ..V. .... ..TB IDAM | |
140 | + * ..1. .... ..01 1111 | |
141 | + */ | |
142 | + movl r0, #0x201f @ control register setting | |
143 | + | |
144 | + /* | |
145 | + * Setup common bits before finally enabling the MMU. Essentially | |
146 | + * this is just loading the page table pointer and domain access | |
147 | + * registers. | |
148 | + */ | |
149 | + #ifndef CONFIG_ALIGNMENT_TRAP | |
150 | + andn r0, r0, #CR_A | |
151 | + #endif | |
152 | + #ifdef CONFIG_CPU_DCACHE_DISABLE | |
153 | + andn r0, r0, #CR_D | |
154 | + #endif | |
155 | + #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | |
156 | + andn r0, r0, #CR_B | |
157 | + #endif | |
158 | + #ifdef CONFIG_CPU_ICACHE_DISABLE | |
159 | + andn r0, r0, #CR_I | |
160 | + #endif | |
161 | + | |
162 | + movc p0.c2, r4, #0 @ set pgd | |
163 | + b __turn_mmu_on | |
164 | +ENDPROC(stext) | |
165 | + | |
166 | +/* | |
167 | + * Enable the MMU. This completely changes the stucture of the visible | |
168 | + * memory space. You will not be able to trace execution through this. | |
169 | + * | |
170 | + * r0 = cp#0 control register | |
171 | + * r15 = *virtual* address to jump to upon completion | |
172 | + */ | |
173 | + .align 5 | |
174 | +__turn_mmu_on: | |
175 | + mov r0, r0 | |
176 | + movc p0.c1, r0, #0 @ write control reg | |
177 | + nop @ fetch inst by phys addr | |
178 | + mov pc, r15 | |
179 | + nop8 @ fetch inst by phys addr | |
180 | +ENDPROC(__turn_mmu_on) | |
181 | + | |
182 | +/* | |
183 | + * Setup the initial page tables. We only setup the barest | |
184 | + * amount which are required to get the kernel running, which | |
185 | + * generally means mapping in the kernel code. | |
186 | + * | |
187 | + * r9 = cpuid | |
188 | + * r10 = procinfo | |
189 | + * | |
190 | + * Returns: | |
191 | + * r0, r3, r6, r7 corrupted | |
192 | + * r4 = physical page table address | |
193 | + */ | |
194 | + .ltorg | |
195 | + | |
196 | + .align 2 | |
197 | + .type __switch_data, %object | |
198 | +__switch_data: | |
199 | + .long __mmap_switched | |
200 | + .long __bss_start @ r6 | |
201 | + .long _end @ r7 | |
202 | + .long cr_alignment @ r8 | |
203 | + .long init_thread_union + THREAD_START_SP @ sp | |
204 | + | |
205 | +/* | |
206 | + * The following fragment of code is executed with the MMU on in MMU mode, | |
207 | + * and uses absolute addresses; this is not position independent. | |
208 | + * | |
209 | + * r0 = cp#0 control register | |
210 | + */ | |
211 | +__mmap_switched: | |
212 | + adr r3, __switch_data + 4 | |
213 | + | |
214 | + ldm.w (r6, r7, r8), [r3]+ | |
215 | + ldw sp, [r3] | |
216 | + | |
217 | + mov fp, #0 @ Clear BSS (and zero fp) | |
218 | +203: csub.a r6, r7 | |
219 | + bea 204f | |
220 | + stw.w fp, [r6]+,#4 | |
221 | + b 203b | |
222 | +204: | |
223 | + andn r1, r0, #CR_A @ Clear 'A' bit | |
224 | + stm (r0, r1), [r8]+ @ Save control register values | |
225 | + b start_kernel | |
226 | +ENDPROC(__mmap_switched) | |
227 | + | |
228 | +/* | |
229 | + * Exception handling. Something went wrong and we can't proceed. We | |
230 | + * ought to tell the user, but since we don't have any guarantee that | |
231 | + * we're even running on the right architecture, we do virtually nothing. | |
232 | + * | |
233 | + * If CONFIG_DEBUG_LL is set we try to print out something about the error | |
234 | + * and hope for the best (useful if bootloader fails to pass a proper | |
235 | + * machine ID for example). | |
236 | + */ | |
237 | +__error_p: | |
238 | +#ifdef CONFIG_DEBUG_LL | |
239 | + adr r0, str_p1 | |
240 | + b.l printascii | |
241 | + mov r0, r9 | |
242 | + b.l printhex8 | |
243 | + adr r0, str_p2 | |
244 | + b.l printascii | |
245 | +901: nop8 | |
246 | + b 901b | |
247 | +str_p1: .asciz "\nError: unrecognized processor variant (0x" | |
248 | +str_p2: .asciz ").\n" | |
249 | + .align | |
250 | +#endif | |
251 | +ENDPROC(__error_p) |
arch/unicore32/kernel/setup.c
1 | +/* | |
2 | + * linux/arch/unicore32/kernel/setup.c | |
3 | + * | |
4 | + * Code specific to PKUnity SoC and UniCore ISA | |
5 | + * | |
6 | + * Copyright (C) 2001-2010 GUAN Xue-tao | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License version 2 as | |
10 | + * published by the Free Software Foundation. | |
11 | + */ | |
12 | +#include <linux/module.h> | |
13 | +#include <linux/kernel.h> | |
14 | +#include <linux/stddef.h> | |
15 | +#include <linux/ioport.h> | |
16 | +#include <linux/delay.h> | |
17 | +#include <linux/utsname.h> | |
18 | +#include <linux/initrd.h> | |
19 | +#include <linux/console.h> | |
20 | +#include <linux/bootmem.h> | |
21 | +#include <linux/seq_file.h> | |
22 | +#include <linux/screen_info.h> | |
23 | +#include <linux/init.h> | |
24 | +#include <linux/root_dev.h> | |
25 | +#include <linux/cpu.h> | |
26 | +#include <linux/interrupt.h> | |
27 | +#include <linux/smp.h> | |
28 | +#include <linux/fs.h> | |
29 | +#include <linux/proc_fs.h> | |
30 | +#include <linux/memblock.h> | |
31 | +#include <linux/elf.h> | |
32 | +#include <linux/io.h> | |
33 | + | |
34 | +#include <asm/cputype.h> | |
35 | +#include <asm/sections.h> | |
36 | +#include <asm/setup.h> | |
37 | +#include <asm/cacheflush.h> | |
38 | +#include <asm/tlbflush.h> | |
39 | +#include <asm/traps.h> | |
40 | + | |
41 | +#include "setup.h" | |
42 | + | |
43 | +#ifndef MEM_SIZE | |
44 | +#define MEM_SIZE (16*1024*1024) | |
45 | +#endif | |
46 | + | |
47 | +struct stack { | |
48 | + u32 irq[3]; | |
49 | + u32 abt[3]; | |
50 | + u32 und[3]; | |
51 | +} ____cacheline_aligned; | |
52 | + | |
53 | +static struct stack stacks[NR_CPUS]; | |
54 | + | |
55 | +char elf_platform[ELF_PLATFORM_SIZE]; | |
56 | +EXPORT_SYMBOL(elf_platform); | |
57 | + | |
58 | +static char __initdata cmd_line[COMMAND_LINE_SIZE]; | |
59 | + | |
60 | +static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE; | |
61 | + | |
62 | +/* | |
63 | + * Standard memory resources | |
64 | + */ | |
65 | +static struct resource mem_res[] = { | |
66 | + { | |
67 | + .name = "Video RAM", | |
68 | + .start = 0, | |
69 | + .end = 0, | |
70 | + .flags = IORESOURCE_MEM | |
71 | + }, | |
72 | + { | |
73 | + .name = "Kernel text", | |
74 | + .start = 0, | |
75 | + .end = 0, | |
76 | + .flags = IORESOURCE_MEM | |
77 | + }, | |
78 | + { | |
79 | + .name = "Kernel data", | |
80 | + .start = 0, | |
81 | + .end = 0, | |
82 | + .flags = IORESOURCE_MEM | |
83 | + } | |
84 | +}; | |
85 | + | |
86 | +#define video_ram mem_res[0] | |
87 | +#define kernel_code mem_res[1] | |
88 | +#define kernel_data mem_res[2] | |
89 | + | |
90 | +/* | |
91 | + * These functions re-use the assembly code in head.S, which | |
92 | + * already provide the required functionality. | |
93 | + */ | |
94 | +static void __init setup_processor(void) | |
95 | +{ | |
96 | + printk(KERN_DEFAULT "CPU: UniCore-II [%08x] revision %d, cr=%08lx\n", | |
97 | + uc32_cpuid, (int)(uc32_cpuid >> 16) & 15, cr_alignment); | |
98 | + | |
99 | + sprintf(init_utsname()->machine, "puv3"); | |
100 | + sprintf(elf_platform, "ucv2"); | |
101 | +} | |
102 | + | |
103 | +/* | |
104 | + * cpu_init - initialise one CPU. | |
105 | + * | |
106 | + * cpu_init sets up the per-CPU stacks. | |
107 | + */ | |
108 | +void cpu_init(void) | |
109 | +{ | |
110 | + unsigned int cpu = smp_processor_id(); | |
111 | + struct stack *stk = &stacks[cpu]; | |
112 | + | |
113 | + /* | |
114 | + * setup stacks for re-entrant exception handlers | |
115 | + */ | |
116 | + __asm__ ( | |
117 | + "mov.a asr, %1\n\t" | |
118 | + "add sp, %0, %2\n\t" | |
119 | + "mov.a asr, %3\n\t" | |
120 | + "add sp, %0, %4\n\t" | |
121 | + "mov.a asr, %5\n\t" | |
122 | + "add sp, %0, %6\n\t" | |
123 | + "mov.a asr, %7" | |
124 | + : | |
125 | + : "r" (stk), | |
126 | + "r" (PSR_R_BIT | PSR_I_BIT | INTR_MODE), | |
127 | + "I" (offsetof(struct stack, irq[0])), | |
128 | + "r" (PSR_R_BIT | PSR_I_BIT | ABRT_MODE), | |
129 | + "I" (offsetof(struct stack, abt[0])), | |
130 | + "r" (PSR_R_BIT | PSR_I_BIT | EXTN_MODE), | |
131 | + "I" (offsetof(struct stack, und[0])), | |
132 | + "r" (PSR_R_BIT | PSR_I_BIT | PRIV_MODE) | |
133 | + : "r30", "cc"); | |
134 | +} | |
135 | + | |
136 | +static int __init uc32_add_memory(unsigned long start, unsigned long size) | |
137 | +{ | |
138 | + struct membank *bank = &meminfo.bank[meminfo.nr_banks]; | |
139 | + | |
140 | + if (meminfo.nr_banks >= NR_BANKS) { | |
141 | + printk(KERN_CRIT "NR_BANKS too low, " | |
142 | + "ignoring memory at %#lx\n", start); | |
143 | + return -EINVAL; | |
144 | + } | |
145 | + | |
146 | + /* | |
147 | + * Ensure that start/size are aligned to a page boundary. | |
148 | + * Size is appropriately rounded down, start is rounded up. | |
149 | + */ | |
150 | + size -= start & ~PAGE_MASK; | |
151 | + | |
152 | + bank->start = PAGE_ALIGN(start); | |
153 | + bank->size = size & PAGE_MASK; | |
154 | + | |
155 | + /* | |
156 | + * Check whether this memory region has non-zero size or | |
157 | + * invalid node number. | |
158 | + */ | |
159 | + if (bank->size == 0) | |
160 | + return -EINVAL; | |
161 | + | |
162 | + meminfo.nr_banks++; | |
163 | + return 0; | |
164 | +} | |
165 | + | |
166 | +/* | |
167 | + * Pick out the memory size. We look for mem=size@start, | |
168 | + * where start and size are "size[KkMm]" | |
169 | + */ | |
170 | +static int __init early_mem(char *p) | |
171 | +{ | |
172 | + static int usermem __initdata = 1; | |
173 | + unsigned long size, start; | |
174 | + char *endp; | |
175 | + | |
176 | + /* | |
177 | + * If the user specifies memory size, we | |
178 | + * blow away any automatically generated | |
179 | + * size. | |
180 | + */ | |
181 | + if (usermem) { | |
182 | + usermem = 0; | |
183 | + meminfo.nr_banks = 0; | |
184 | + } | |
185 | + | |
186 | + start = PHYS_OFFSET; | |
187 | + size = memparse(p, &endp); | |
188 | + if (*endp == '@') | |
189 | + start = memparse(endp + 1, NULL); | |
190 | + | |
191 | + uc32_add_memory(start, size); | |
192 | + | |
193 | + return 0; | |
194 | +} | |
195 | +early_param("mem", early_mem); | |
196 | + | |
197 | +static void __init | |
198 | +request_standard_resources(struct meminfo *mi) | |
199 | +{ | |
200 | + struct resource *res; | |
201 | + int i; | |
202 | + | |
203 | + kernel_code.start = virt_to_phys(_stext); | |
204 | + kernel_code.end = virt_to_phys(_etext - 1); | |
205 | + kernel_data.start = virt_to_phys(_sdata); | |
206 | + kernel_data.end = virt_to_phys(_end - 1); | |
207 | + | |
208 | + for (i = 0; i < mi->nr_banks; i++) { | |
209 | + if (mi->bank[i].size == 0) | |
210 | + continue; | |
211 | + | |
212 | + res = alloc_bootmem_low(sizeof(*res)); | |
213 | + res->name = "System RAM"; | |
214 | + res->start = mi->bank[i].start; | |
215 | + res->end = mi->bank[i].start + mi->bank[i].size - 1; | |
216 | + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; | |
217 | + | |
218 | + request_resource(&iomem_resource, res); | |
219 | + | |
220 | + if (kernel_code.start >= res->start && | |
221 | + kernel_code.end <= res->end) | |
222 | + request_resource(res, &kernel_code); | |
223 | + if (kernel_data.start >= res->start && | |
224 | + kernel_data.end <= res->end) | |
225 | + request_resource(res, &kernel_data); | |
226 | + } | |
227 | + | |
228 | + video_ram.start = PKUNITY_UNIGFX_MMAP_BASE; | |
229 | + video_ram.end = PKUNITY_UNIGFX_MMAP_BASE + PKUNITY_UNIGFX_MMAP_SIZE; | |
230 | + request_resource(&iomem_resource, &video_ram); | |
231 | +} | |
232 | + | |
233 | +static void (*init_machine)(void) __initdata; | |
234 | + | |
235 | +static int __init customize_machine(void) | |
236 | +{ | |
237 | + /* customizes platform devices, or adds new ones */ | |
238 | + if (init_machine) | |
239 | + init_machine(); | |
240 | + return 0; | |
241 | +} | |
242 | +arch_initcall(customize_machine); | |
243 | + | |
244 | +void __init setup_arch(char **cmdline_p) | |
245 | +{ | |
246 | + char *from = default_command_line; | |
247 | + | |
248 | + setup_processor(); | |
249 | + | |
250 | + init_mm.start_code = (unsigned long) _stext; | |
251 | + init_mm.end_code = (unsigned long) _etext; | |
252 | + init_mm.end_data = (unsigned long) _edata; | |
253 | + init_mm.brk = (unsigned long) _end; | |
254 | + | |
255 | + /* parse_early_param needs a boot_command_line */ | |
256 | + strlcpy(boot_command_line, from, COMMAND_LINE_SIZE); | |
257 | + | |
258 | + /* populate cmd_line too for later use, preserving boot_command_line */ | |
259 | + strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE); | |
260 | + *cmdline_p = cmd_line; | |
261 | + | |
262 | + parse_early_param(); | |
263 | + | |
264 | + uc32_memblock_init(&meminfo); | |
265 | + | |
266 | + paging_init(); | |
267 | + request_standard_resources(&meminfo); | |
268 | + | |
269 | + cpu_init(); | |
270 | + | |
271 | + /* | |
272 | + * Set up various architecture-specific pointers | |
273 | + */ | |
274 | + init_machine = puv3_core_init; | |
275 | + | |
276 | +#ifdef CONFIG_VT | |
277 | +#if defined(CONFIG_VGA_CONSOLE) | |
278 | + conswitchp = &vga_con; | |
279 | +#elif defined(CONFIG_DUMMY_CONSOLE) | |
280 | + conswitchp = &dummy_con; | |
281 | +#endif | |
282 | +#endif | |
283 | + early_trap_init(); | |
284 | +} | |
285 | + | |
286 | +static struct cpu cpuinfo_unicore; | |
287 | + | |
288 | +static int __init topology_init(void) | |
289 | +{ | |
290 | + int i; | |
291 | + | |
292 | + for_each_possible_cpu(i) | |
293 | + register_cpu(&cpuinfo_unicore, i); | |
294 | + | |
295 | + return 0; | |
296 | +} | |
297 | +subsys_initcall(topology_init); | |
298 | + | |
299 | +#ifdef CONFIG_HAVE_PROC_CPU | |
300 | +static int __init proc_cpu_init(void) | |
301 | +{ | |
302 | + struct proc_dir_entry *res; | |
303 | + | |
304 | + res = proc_mkdir("cpu", NULL); | |
305 | + if (!res) | |
306 | + return -ENOMEM; | |
307 | + return 0; | |
308 | +} | |
309 | +fs_initcall(proc_cpu_init); | |
310 | +#endif | |
311 | + | |
312 | +static int c_show(struct seq_file *m, void *v) | |
313 | +{ | |
314 | + seq_printf(m, "Processor\t: UniCore-II rev %d (%s)\n", | |
315 | + (int)(uc32_cpuid >> 16) & 15, elf_platform); | |
316 | + | |
317 | + seq_printf(m, "BogoMIPS\t: %lu.%02lu\n", | |
318 | + loops_per_jiffy / (500000/HZ), | |
319 | + (loops_per_jiffy / (5000/HZ)) % 100); | |
320 | + | |
321 | + /* dump out the processor features */ | |
322 | + seq_puts(m, "Features\t: CMOV UC-F64"); | |
323 | + | |
324 | + seq_printf(m, "\nCPU implementer\t: 0x%02x\n", uc32_cpuid >> 24); | |
325 | + seq_printf(m, "CPU architecture: 2\n"); | |
326 | + seq_printf(m, "CPU revision\t: %d\n", (uc32_cpuid >> 16) & 15); | |
327 | + | |
328 | + seq_printf(m, "Cache type\t: write-back\n" | |
329 | + "Cache clean\t: cp0 c5 ops\n" | |
330 | + "Cache lockdown\t: not support\n" | |
331 | + "Cache format\t: Harvard\n"); | |
332 | + | |
333 | + seq_puts(m, "\n"); | |
334 | + | |
335 | + seq_printf(m, "Hardware\t: PKUnity v3\n"); | |
336 | + | |
337 | + return 0; | |
338 | +} | |
339 | + | |
340 | +static void *c_start(struct seq_file *m, loff_t *pos) | |
341 | +{ | |
342 | + return *pos < 1 ? (void *)1 : NULL; | |
343 | +} | |
344 | + | |
345 | +static void *c_next(struct seq_file *m, void *v, loff_t *pos) | |
346 | +{ | |
347 | + ++*pos; | |
348 | + return NULL; | |
349 | +} | |
350 | + | |
351 | +static void c_stop(struct seq_file *m, void *v) | |
352 | +{ | |
353 | +} | |
354 | + | |
355 | +const struct seq_operations cpuinfo_op = { | |
356 | + .start = c_start, | |
357 | + .next = c_next, | |
358 | + .stop = c_stop, | |
359 | + .show = c_show | |
360 | +}; |
arch/unicore32/kernel/setup.h
1 | +/* | |
2 | + * linux/arch/unicore32/kernel/setup.h | |
3 | + * | |
4 | + * Code specific to PKUnity SoC and UniCore ISA | |
5 | + * | |
6 | + * Copyright (C) 2001-2010 GUAN Xue-tao | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License version 2 as | |
10 | + * published by the Free Software Foundation. | |
11 | + */ | |
12 | +#ifndef __UNICORE_KERNEL_SETUP_H__ | |
13 | +#define __UNICORE_KERNEL_SETUP_H__ | |
14 | + | |
15 | +extern void paging_init(void); | |
16 | +extern void puv3_core_init(void); | |
17 | + | |
18 | +extern void puv3_ps2_init(void); | |
19 | +extern void pci_puv3_preinit(void); | |
20 | +extern void __init puv3_init_gpio(void); | |
21 | + | |
22 | +extern void setup_mm_for_reboot(char mode); | |
23 | + | |
24 | +extern char __stubs_start[], __stubs_end[]; | |
25 | +extern char __vectors_start[], __vectors_end[]; | |
26 | + | |
27 | +extern void kernel_thread_helper(void); | |
28 | + | |
29 | +extern void __init early_signal_init(void); | |
30 | +#endif |
arch/unicore32/kernel/traps.c
1 | +/* | |
2 | + * linux/arch/unicore32/kernel/traps.c | |
3 | + * | |
4 | + * Code specific to PKUnity SoC and UniCore ISA | |
5 | + * | |
6 | + * Copyright (C) 2001-2010 GUAN Xue-tao | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License version 2 as | |
10 | + * published by the Free Software Foundation. | |
11 | + * | |
12 | + * 'traps.c' handles hardware exceptions after we have saved some state. | |
13 | + * Mostly a debugging aid, but will probably kill the offending process. | |
14 | + */ | |
15 | +#include <linux/module.h> | |
16 | +#include <linux/signal.h> | |
17 | +#include <linux/spinlock.h> | |
18 | +#include <linux/personality.h> | |
19 | +#include <linux/kallsyms.h> | |
20 | +#include <linux/kdebug.h> | |
21 | +#include <linux/uaccess.h> | |
22 | +#include <linux/delay.h> | |
23 | +#include <linux/hardirq.h> | |
24 | +#include <linux/init.h> | |
25 | +#include <linux/uaccess.h> | |
26 | +#include <linux/atomic.h> | |
27 | +#include <linux/unistd.h> | |
28 | + | |
29 | +#include <asm/cacheflush.h> | |
30 | +#include <asm/system.h> | |
31 | +#include <asm/traps.h> | |
32 | + | |
33 | +#include "setup.h" | |
34 | + | |
35 | +static void dump_mem(const char *, const char *, unsigned long, unsigned long); | |
36 | + | |
37 | +void dump_backtrace_entry(unsigned long where, | |
38 | + unsigned long from, unsigned long frame) | |
39 | +{ | |
40 | +#ifdef CONFIG_KALLSYMS | |
41 | + printk(KERN_DEFAULT "[<%08lx>] (%pS) from [<%08lx>] (%pS)\n", | |
42 | + where, (void *)where, from, (void *)from); | |
43 | +#else | |
44 | + printk(KERN_DEFAULT "Function entered at [<%08lx>] from [<%08lx>]\n", | |
45 | + where, from); | |
46 | +#endif | |
47 | +} | |
48 | + | |
49 | +/* | |
50 | + * Stack pointers should always be within the kernels view of | |
51 | + * physical memory. If it is not there, then we can't dump | |
52 | + * out any information relating to the stack. | |
53 | + */ | |
54 | +static int verify_stack(unsigned long sp) | |
55 | +{ | |
56 | + if (sp < PAGE_OFFSET || | |
57 | + (sp > (unsigned long)high_memory && high_memory != NULL)) | |
58 | + return -EFAULT; | |
59 | + | |
60 | + return 0; | |
61 | +} | |
62 | + | |
63 | +/* | |
64 | + * Dump out the contents of some memory nicely... | |
65 | + */ | |
66 | +static void dump_mem(const char *lvl, const char *str, unsigned long bottom, | |
67 | + unsigned long top) | |
68 | +{ | |
69 | + unsigned long first; | |
70 | + mm_segment_t fs; | |
71 | + int i; | |
72 | + | |
73 | + /* | |
74 | + * We need to switch to kernel mode so that we can use __get_user | |
75 | + * to safely read from kernel space. Note that we now dump the | |
76 | + * code first, just in case the backtrace kills us. | |
77 | + */ | |
78 | + fs = get_fs(); | |
79 | + set_fs(KERNEL_DS); | |
80 | + | |
81 | + printk(KERN_DEFAULT "%s%s(0x%08lx to 0x%08lx)\n", | |
82 | + lvl, str, bottom, top); | |
83 | + | |
84 | + for (first = bottom & ~31; first < top; first += 32) { | |
85 | + unsigned long p; | |
86 | + char str[sizeof(" 12345678") * 8 + 1]; | |
87 | + | |
88 | + memset(str, ' ', sizeof(str)); | |
89 | + str[sizeof(str) - 1] = '\0'; | |
90 | + | |
91 | + for (p = first, i = 0; i < 8 && p < top; i++, p += 4) { | |
92 | + if (p >= bottom && p < top) { | |
93 | + unsigned long val; | |
94 | + if (__get_user(val, (unsigned long *)p) == 0) | |
95 | + sprintf(str + i * 9, " %08lx", val); | |
96 | + else | |
97 | + sprintf(str + i * 9, " ????????"); | |
98 | + } | |
99 | + } | |
100 | + printk(KERN_DEFAULT "%s%04lx:%s\n", lvl, first & 0xffff, str); | |
101 | + } | |
102 | + | |
103 | + set_fs(fs); | |
104 | +} | |
105 | + | |
106 | +static void dump_instr(const char *lvl, struct pt_regs *regs) | |
107 | +{ | |
108 | + unsigned long addr = instruction_pointer(regs); | |
109 | + const int width = 8; | |
110 | + mm_segment_t fs; | |
111 | + char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str; | |
112 | + int i; | |
113 | + | |
114 | + /* | |
115 | + * We need to switch to kernel mode so that we can use __get_user | |
116 | + * to safely read from kernel space. Note that we now dump the | |
117 | + * code first, just in case the backtrace kills us. | |
118 | + */ | |
119 | + fs = get_fs(); | |
120 | + set_fs(KERNEL_DS); | |
121 | + | |
122 | + for (i = -4; i < 1; i++) { | |
123 | + unsigned int val, bad; | |
124 | + | |
125 | + bad = __get_user(val, &((u32 *)addr)[i]); | |
126 | + | |
127 | + if (!bad) | |
128 | + p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ", | |
129 | + width, val); | |
130 | + else { | |
131 | + p += sprintf(p, "bad PC value"); | |
132 | + break; | |
133 | + } | |
134 | + } | |
135 | + printk(KERN_DEFAULT "%sCode: %s\n", lvl, str); | |
136 | + | |
137 | + set_fs(fs); | |
138 | +} | |
139 | + | |
140 | +static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) | |
141 | +{ | |
142 | + unsigned int fp, mode; | |
143 | + int ok = 1; | |
144 | + | |
145 | + printk(KERN_DEFAULT "Backtrace: "); | |
146 | + | |
147 | + if (!tsk) | |
148 | + tsk = current; | |
149 | + | |
150 | + if (regs) { | |
151 | + fp = regs->UCreg_fp; | |
152 | + mode = processor_mode(regs); | |
153 | + } else if (tsk != current) { | |
154 | + fp = thread_saved_fp(tsk); | |
155 | + mode = 0x10; | |
156 | + } else { | |
157 | + asm("mov %0, fp" : "=r" (fp) : : "cc"); | |
158 | + mode = 0x10; | |
159 | + } | |
160 | + | |
161 | + if (!fp) { | |
162 | + printk("no frame pointer"); | |
163 | + ok = 0; | |
164 | + } else if (verify_stack(fp)) { | |
165 | + printk("invalid frame pointer 0x%08x", fp); | |
166 | + ok = 0; | |
167 | + } else if (fp < (unsigned long)end_of_stack(tsk)) | |
168 | + printk("frame pointer underflow"); | |
169 | + printk("\n"); | |
170 | + | |
171 | + if (ok) | |
172 | + c_backtrace(fp, mode); | |
173 | +} | |
174 | + | |
175 | +void dump_stack(void) | |
176 | +{ | |
177 | + dump_backtrace(NULL, NULL); | |
178 | +} | |
179 | +EXPORT_SYMBOL(dump_stack); | |
180 | + | |
181 | +void show_stack(struct task_struct *tsk, unsigned long *sp) | |
182 | +{ | |
183 | + dump_backtrace(NULL, tsk); | |
184 | + barrier(); | |
185 | +} | |
186 | + | |
187 | +static int __die(const char *str, int err, struct thread_info *thread, | |
188 | + struct pt_regs *regs) | |
189 | +{ | |
190 | + struct task_struct *tsk = thread->task; | |
191 | + static int die_counter; | |
192 | + int ret; | |
193 | + | |
194 | + printk(KERN_EMERG "Internal error: %s: %x [#%d]\n", | |
195 | + str, err, ++die_counter); | |
196 | + sysfs_printk_last_file(); | |
197 | + | |
198 | + /* trap and error numbers are mostly meaningless on UniCore */ | |
199 | + ret = notify_die(DIE_OOPS, str, regs, err, tsk->thread.trap_no, \ | |
200 | + SIGSEGV); | |
201 | + if (ret == NOTIFY_STOP) | |
202 | + return ret; | |
203 | + | |
204 | + print_modules(); | |
205 | + __show_regs(regs); | |
206 | + printk(KERN_EMERG "Process %.*s (pid: %d, stack limit = 0x%p)\n", | |
207 | + TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1); | |
208 | + | |
209 | + if (!user_mode(regs) || in_interrupt()) { | |
210 | + dump_mem(KERN_EMERG, "Stack: ", regs->UCreg_sp, | |
211 | + THREAD_SIZE + (unsigned long)task_stack_page(tsk)); | |
212 | + dump_backtrace(regs, tsk); | |
213 | + dump_instr(KERN_EMERG, regs); | |
214 | + } | |
215 | + | |
216 | + return ret; | |
217 | +} | |
218 | + | |
219 | +DEFINE_SPINLOCK(die_lock); | |
220 | + | |
221 | +/* | |
222 | + * This function is protected against re-entrancy. | |
223 | + */ | |
224 | +void die(const char *str, struct pt_regs *regs, int err) | |
225 | +{ | |
226 | + struct thread_info *thread = current_thread_info(); | |
227 | + int ret; | |
228 | + | |
229 | + oops_enter(); | |
230 | + | |
231 | + spin_lock_irq(&die_lock); | |
232 | + console_verbose(); | |
233 | + bust_spinlocks(1); | |
234 | + ret = __die(str, err, thread, regs); | |
235 | + | |
236 | + bust_spinlocks(0); | |
237 | + add_taint(TAINT_DIE); | |
238 | + spin_unlock_irq(&die_lock); | |
239 | + oops_exit(); | |
240 | + | |
241 | + if (in_interrupt()) | |
242 | + panic("Fatal exception in interrupt"); | |
243 | + if (panic_on_oops) | |
244 | + panic("Fatal exception"); | |
245 | + if (ret != NOTIFY_STOP) | |
246 | + do_exit(SIGSEGV); | |
247 | +} | |
248 | + | |
249 | +void uc32_notify_die(const char *str, struct pt_regs *regs, | |
250 | + struct siginfo *info, unsigned long err, unsigned long trap) | |
251 | +{ | |
252 | + if (user_mode(regs)) { | |
253 | + current->thread.error_code = err; | |
254 | + current->thread.trap_no = trap; | |
255 | + | |
256 | + force_sig_info(info->si_signo, info, current); | |
257 | + } else | |
258 | + die(str, regs, err); | |
259 | +} | |
260 | + | |
261 | +/* | |
262 | + * bad_mode handles the impossible case in the vectors. If you see one of | |
263 | + * these, then it's extremely serious, and could mean you have buggy hardware. | |
264 | + * It never returns, and never tries to sync. We hope that we can at least | |
265 | + * dump out some state information... | |
266 | + */ | |
267 | +asmlinkage void bad_mode(struct pt_regs *regs, unsigned int reason) | |
268 | +{ | |
269 | + console_verbose(); | |
270 | + | |
271 | + printk(KERN_CRIT "Bad mode detected with reason 0x%x\n", reason); | |
272 | + | |
273 | + die("Oops - bad mode", regs, 0); | |
274 | + local_irq_disable(); | |
275 | + panic("bad mode"); | |
276 | +} | |
277 | + | |
278 | +void __pte_error(const char *file, int line, unsigned long val) | |
279 | +{ | |
280 | + printk(KERN_DEFAULT "%s:%d: bad pte %08lx.\n", file, line, val); | |
281 | +} | |
282 | + | |
283 | +void __pmd_error(const char *file, int line, unsigned long val) | |
284 | +{ | |
285 | + printk(KERN_DEFAULT "%s:%d: bad pmd %08lx.\n", file, line, val); | |
286 | +} | |
287 | + | |
288 | +void __pgd_error(const char *file, int line, unsigned long val) | |
289 | +{ | |
290 | + printk(KERN_DEFAULT "%s:%d: bad pgd %08lx.\n", file, line, val); | |
291 | +} | |
292 | + | |
293 | +asmlinkage void __div0(void) | |
294 | +{ | |
295 | + printk(KERN_DEFAULT "Division by zero in kernel.\n"); | |
296 | + dump_stack(); | |
297 | +} | |
298 | +EXPORT_SYMBOL(__div0); | |
299 | + | |
300 | +void abort(void) | |
301 | +{ | |
302 | + BUG(); | |
303 | + | |
304 | + /* if that doesn't kill us, halt */ | |
305 | + panic("Oops failed to kill thread"); | |
306 | +} | |
307 | +EXPORT_SYMBOL(abort); | |
308 | + | |
309 | +void __init trap_init(void) | |
310 | +{ | |
311 | + return; | |
312 | +} | |
313 | + | |
314 | +void __init early_trap_init(void) | |
315 | +{ | |
316 | + unsigned long vectors = VECTORS_BASE; | |
317 | + | |
318 | + /* | |
319 | + * Copy the vectors, stubs (in entry-unicore.S) | |
320 | + * into the vector page, mapped at 0xffff0000, and ensure these | |
321 | + * are visible to the instruction stream. | |
322 | + */ | |
323 | + memcpy((void *)vectors, | |
324 | + __vectors_start, | |
325 | + __vectors_end - __vectors_start); | |
326 | + memcpy((void *)vectors + 0x200, | |
327 | + __stubs_start, | |
328 | + __stubs_end - __stubs_start); | |
329 | + | |
330 | + early_signal_init(); | |
331 | + | |
332 | + flush_icache_range(vectors, vectors + PAGE_SIZE); | |
333 | +} |