diff options
Diffstat (limited to 'arch/mips/kernel/process.c')
-rw-r--r-- | arch/mips/kernel/process.c | 213 |
1 files changed, 145 insertions, 68 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index e4f2f8011387..4fe3d5715c41 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c | |||
@@ -25,8 +25,10 @@ | |||
25 | #include <linux/init.h> | 25 | #include <linux/init.h> |
26 | #include <linux/completion.h> | 26 | #include <linux/completion.h> |
27 | 27 | ||
28 | #include <asm/abi.h> | ||
28 | #include <asm/bootinfo.h> | 29 | #include <asm/bootinfo.h> |
29 | #include <asm/cpu.h> | 30 | #include <asm/cpu.h> |
31 | #include <asm/dsp.h> | ||
30 | #include <asm/fpu.h> | 32 | #include <asm/fpu.h> |
31 | #include <asm/pgtable.h> | 33 | #include <asm/pgtable.h> |
32 | #include <asm/system.h> | 34 | #include <asm/system.h> |
@@ -39,14 +41,6 @@ | |||
39 | #include <asm/inst.h> | 41 | #include <asm/inst.h> |
40 | 42 | ||
41 | /* | 43 | /* |
42 | * We use this if we don't have any better idle routine.. | ||
43 | * (This to kill: kernel/platform.c. | ||
44 | */ | ||
45 | void default_idle (void) | ||
46 | { | ||
47 | } | ||
48 | |||
49 | /* | ||
50 | * The idle thread. There's no useful work to be done, so just try to conserve | 44 | * The idle thread. There's no useful work to be done, so just try to conserve |
51 | * power and have a low exit latency (ie sit in a loop waiting for somebody to | 45 | * power and have a low exit latency (ie sit in a loop waiting for somebody to |
52 | * say that they'd like to reschedule) | 46 | * say that they'd like to reschedule) |
@@ -62,6 +56,54 @@ ATTRIB_NORET void cpu_idle(void) | |||
62 | } | 56 | } |
63 | } | 57 | } |
64 | 58 | ||
59 | extern int do_signal(sigset_t *oldset, struct pt_regs *regs); | ||
60 | extern int do_signal32(sigset_t *oldset, struct pt_regs *regs); | ||
61 | |||
62 | /* | ||
63 | * Native o32 and N64 ABI without DSP ASE | ||
64 | */ | ||
65 | extern int setup_frame(struct k_sigaction * ka, struct pt_regs *regs, | ||
66 | int signr, sigset_t *set); | ||
67 | extern int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, | ||
68 | int signr, sigset_t *set, siginfo_t *info); | ||
69 | |||
70 | struct mips_abi mips_abi = { | ||
71 | .do_signal = do_signal, | ||
72 | #ifdef CONFIG_TRAD_SIGNALS | ||
73 | .setup_frame = setup_frame, | ||
74 | #endif | ||
75 | .setup_rt_frame = setup_rt_frame | ||
76 | }; | ||
77 | |||
78 | #ifdef CONFIG_MIPS32_O32 | ||
79 | /* | ||
80 | * o32 compatibility on 64-bit kernels, without DSP ASE | ||
81 | */ | ||
82 | extern int setup_frame_32(struct k_sigaction * ka, struct pt_regs *regs, | ||
83 | int signr, sigset_t *set); | ||
84 | extern int setup_rt_frame_32(struct k_sigaction * ka, struct pt_regs *regs, | ||
85 | int signr, sigset_t *set, siginfo_t *info); | ||
86 | |||
87 | struct mips_abi mips_abi_32 = { | ||
88 | .do_signal = do_signal32, | ||
89 | .setup_frame = setup_frame_32, | ||
90 | .setup_rt_frame = setup_rt_frame_32 | ||
91 | }; | ||
92 | #endif /* CONFIG_MIPS32_O32 */ | ||
93 | |||
94 | #ifdef CONFIG_MIPS32_N32 | ||
95 | /* | ||
96 | * N32 on 64-bit kernels, without DSP ASE | ||
97 | */ | ||
98 | extern int setup_rt_frame_n32(struct k_sigaction * ka, struct pt_regs *regs, | ||
99 | int signr, sigset_t *set, siginfo_t *info); | ||
100 | |||
101 | struct mips_abi mips_abi_n32 = { | ||
102 | .do_signal = do_signal, | ||
103 | .setup_rt_frame = setup_rt_frame_n32 | ||
104 | }; | ||
105 | #endif /* CONFIG_MIPS32_N32 */ | ||
106 | |||
65 | asmlinkage void ret_from_fork(void); | 107 | asmlinkage void ret_from_fork(void); |
66 | 108 | ||
67 | void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp) | 109 | void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp) |
@@ -78,6 +120,8 @@ void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp) | |||
78 | regs->cp0_status = status; | 120 | regs->cp0_status = status; |
79 | clear_used_math(); | 121 | clear_used_math(); |
80 | lose_fpu(); | 122 | lose_fpu(); |
123 | if (cpu_has_dsp) | ||
124 | __init_dsp(); | ||
81 | regs->cp0_epc = pc; | 125 | regs->cp0_epc = pc; |
82 | regs->regs[29] = sp; | 126 | regs->regs[29] = sp; |
83 | current_thread_info()->addr_limit = USER_DS; | 127 | current_thread_info()->addr_limit = USER_DS; |
@@ -97,14 +141,17 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, | |||
97 | struct thread_info *ti = p->thread_info; | 141 | struct thread_info *ti = p->thread_info; |
98 | struct pt_regs *childregs; | 142 | struct pt_regs *childregs; |
99 | long childksp; | 143 | long childksp; |
144 | p->set_child_tid = p->clear_child_tid = NULL; | ||
100 | 145 | ||
101 | childksp = (unsigned long)ti + THREAD_SIZE - 32; | 146 | childksp = (unsigned long)ti + THREAD_SIZE - 32; |
102 | 147 | ||
103 | preempt_disable(); | 148 | preempt_disable(); |
104 | 149 | ||
105 | if (is_fpu_owner()) { | 150 | if (is_fpu_owner()) |
106 | save_fp(p); | 151 | save_fp(p); |
107 | } | 152 | |
153 | if (cpu_has_dsp) | ||
154 | save_dsp(p); | ||
108 | 155 | ||
109 | preempt_enable(); | 156 | preempt_enable(); |
110 | 157 | ||
@@ -142,6 +189,9 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, | |||
142 | childregs->cp0_status &= ~(ST0_CU2|ST0_CU1); | 189 | childregs->cp0_status &= ~(ST0_CU2|ST0_CU1); |
143 | clear_tsk_thread_flag(p, TIF_USEDFPU); | 190 | clear_tsk_thread_flag(p, TIF_USEDFPU); |
144 | 191 | ||
192 | if (clone_flags & CLONE_SETTLS) | ||
193 | ti->tp_value = regs->regs[7]; | ||
194 | |||
145 | return 0; | 195 | return 0; |
146 | } | 196 | } |
147 | 197 | ||
@@ -175,6 +225,14 @@ void dump_regs(elf_greg_t *gp, struct pt_regs *regs) | |||
175 | #endif | 225 | #endif |
176 | } | 226 | } |
177 | 227 | ||
228 | int dump_task_regs (struct task_struct *tsk, elf_gregset_t *regs) | ||
229 | { | ||
230 | struct thread_info *ti = tsk->thread_info; | ||
231 | long ksp = (unsigned long)ti + THREAD_SIZE - 32; | ||
232 | dump_regs(&(*regs)[0], (struct pt_regs *) ksp - 1); | ||
233 | return 1; | ||
234 | } | ||
235 | |||
178 | int dump_task_fpu (struct task_struct *t, elf_fpregset_t *fpr) | 236 | int dump_task_fpu (struct task_struct *t, elf_fpregset_t *fpr) |
179 | { | 237 | { |
180 | memcpy(fpr, &t->thread.fpu, sizeof(current->thread.fpu)); | 238 | memcpy(fpr, &t->thread.fpu, sizeof(current->thread.fpu)); |
@@ -211,22 +269,48 @@ long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) | |||
211 | return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); | 269 | return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); |
212 | } | 270 | } |
213 | 271 | ||
214 | struct mips_frame_info { | 272 | static struct mips_frame_info { |
273 | void *func; | ||
274 | int omit_fp; /* compiled without fno-omit-frame-pointer */ | ||
215 | int frame_offset; | 275 | int frame_offset; |
216 | int pc_offset; | 276 | int pc_offset; |
277 | } schedule_frame, mfinfo[] = { | ||
278 | { schedule, 0 }, /* must be first */ | ||
279 | /* arch/mips/kernel/semaphore.c */ | ||
280 | { __down, 1 }, | ||
281 | { __down_interruptible, 1 }, | ||
282 | /* kernel/sched.c */ | ||
283 | #ifdef CONFIG_PREEMPT | ||
284 | { preempt_schedule, 0 }, | ||
285 | #endif | ||
286 | { wait_for_completion, 0 }, | ||
287 | { interruptible_sleep_on, 0 }, | ||
288 | { interruptible_sleep_on_timeout, 0 }, | ||
289 | { sleep_on, 0 }, | ||
290 | { sleep_on_timeout, 0 }, | ||
291 | { yield, 0 }, | ||
292 | { io_schedule, 0 }, | ||
293 | { io_schedule_timeout, 0 }, | ||
294 | #if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) | ||
295 | { __preempt_spin_lock, 0 }, | ||
296 | { __preempt_write_lock, 0 }, | ||
297 | #endif | ||
298 | /* kernel/timer.c */ | ||
299 | { schedule_timeout, 1 }, | ||
300 | /* { nanosleep_restart, 1 }, */ | ||
301 | /* lib/rwsem-spinlock.c */ | ||
302 | { __down_read, 1 }, | ||
303 | { __down_write, 1 }, | ||
217 | }; | 304 | }; |
218 | static struct mips_frame_info schedule_frame; | 305 | |
219 | static struct mips_frame_info schedule_timeout_frame; | ||
220 | static struct mips_frame_info sleep_on_frame; | ||
221 | static struct mips_frame_info sleep_on_timeout_frame; | ||
222 | static struct mips_frame_info wait_for_completion_frame; | ||
223 | static int mips_frame_info_initialized; | 306 | static int mips_frame_info_initialized; |
224 | static int __init get_frame_info(struct mips_frame_info *info, void *func) | 307 | static int __init get_frame_info(struct mips_frame_info *info) |
225 | { | 308 | { |
226 | int i; | 309 | int i; |
310 | void *func = info->func; | ||
227 | union mips_instruction *ip = (union mips_instruction *)func; | 311 | union mips_instruction *ip = (union mips_instruction *)func; |
228 | info->pc_offset = -1; | 312 | info->pc_offset = -1; |
229 | info->frame_offset = -1; | 313 | info->frame_offset = info->omit_fp ? 0 : -1; |
230 | for (i = 0; i < 128; i++, ip++) { | 314 | for (i = 0; i < 128; i++, ip++) { |
231 | /* if jal, jalr, jr, stop. */ | 315 | /* if jal, jalr, jr, stop. */ |
232 | if (ip->j_format.opcode == jal_op || | 316 | if (ip->j_format.opcode == jal_op || |
@@ -247,14 +331,16 @@ static int __init get_frame_info(struct mips_frame_info *info, void *func) | |||
247 | /* sw / sd $ra, offset($sp) */ | 331 | /* sw / sd $ra, offset($sp) */ |
248 | if (ip->i_format.rt == 31) { | 332 | if (ip->i_format.rt == 31) { |
249 | if (info->pc_offset != -1) | 333 | if (info->pc_offset != -1) |
250 | break; | 334 | continue; |
251 | info->pc_offset = | 335 | info->pc_offset = |
252 | ip->i_format.simmediate / sizeof(long); | 336 | ip->i_format.simmediate / sizeof(long); |
253 | } | 337 | } |
254 | /* sw / sd $s8, offset($sp) */ | 338 | /* sw / sd $s8, offset($sp) */ |
255 | if (ip->i_format.rt == 30) { | 339 | if (ip->i_format.rt == 30) { |
340 | //#if 0 /* gcc 3.4 does aggressive optimization... */ | ||
256 | if (info->frame_offset != -1) | 341 | if (info->frame_offset != -1) |
257 | break; | 342 | continue; |
343 | //#endif | ||
258 | info->frame_offset = | 344 | info->frame_offset = |
259 | ip->i_format.simmediate / sizeof(long); | 345 | ip->i_format.simmediate / sizeof(long); |
260 | } | 346 | } |
@@ -272,13 +358,25 @@ static int __init get_frame_info(struct mips_frame_info *info, void *func) | |||
272 | 358 | ||
273 | static int __init frame_info_init(void) | 359 | static int __init frame_info_init(void) |
274 | { | 360 | { |
275 | mips_frame_info_initialized = | 361 | int i, found; |
276 | !get_frame_info(&schedule_frame, schedule) && | 362 | for (i = 0; i < ARRAY_SIZE(mfinfo); i++) |
277 | !get_frame_info(&schedule_timeout_frame, schedule_timeout) && | 363 | if (get_frame_info(&mfinfo[i])) |
278 | !get_frame_info(&sleep_on_frame, sleep_on) && | 364 | return -1; |
279 | !get_frame_info(&sleep_on_timeout_frame, sleep_on_timeout) && | 365 | schedule_frame = mfinfo[0]; |
280 | !get_frame_info(&wait_for_completion_frame, wait_for_completion); | 366 | /* bubble sort */ |
281 | 367 | do { | |
368 | struct mips_frame_info tmp; | ||
369 | found = 0; | ||
370 | for (i = 1; i < ARRAY_SIZE(mfinfo); i++) { | ||
371 | if (mfinfo[i-1].func > mfinfo[i].func) { | ||
372 | tmp = mfinfo[i]; | ||
373 | mfinfo[i] = mfinfo[i-1]; | ||
374 | mfinfo[i-1] = tmp; | ||
375 | found = 1; | ||
376 | } | ||
377 | } | ||
378 | } while (found); | ||
379 | mips_frame_info_initialized = 1; | ||
282 | return 0; | 380 | return 0; |
283 | } | 381 | } |
284 | 382 | ||
@@ -303,60 +401,39 @@ unsigned long thread_saved_pc(struct task_struct *tsk) | |||
303 | /* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ | 401 | /* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ |
304 | unsigned long get_wchan(struct task_struct *p) | 402 | unsigned long get_wchan(struct task_struct *p) |
305 | { | 403 | { |
404 | unsigned long stack_page; | ||
306 | unsigned long frame, pc; | 405 | unsigned long frame, pc; |
307 | 406 | ||
308 | if (!p || p == current || p->state == TASK_RUNNING) | 407 | if (!p || p == current || p->state == TASK_RUNNING) |
309 | return 0; | 408 | return 0; |
310 | 409 | ||
311 | if (!mips_frame_info_initialized) | 410 | stack_page = (unsigned long)p->thread_info; |
411 | if (!stack_page || !mips_frame_info_initialized) | ||
312 | return 0; | 412 | return 0; |
413 | |||
313 | pc = thread_saved_pc(p); | 414 | pc = thread_saved_pc(p); |
314 | if (!in_sched_functions(pc)) | 415 | if (!in_sched_functions(pc)) |
315 | goto out; | 416 | return pc; |
316 | |||
317 | if (pc >= (unsigned long) sleep_on_timeout) | ||
318 | goto schedule_timeout_caller; | ||
319 | if (pc >= (unsigned long) sleep_on) | ||
320 | goto schedule_caller; | ||
321 | if (pc >= (unsigned long) interruptible_sleep_on_timeout) | ||
322 | goto schedule_timeout_caller; | ||
323 | if (pc >= (unsigned long)interruptible_sleep_on) | ||
324 | goto schedule_caller; | ||
325 | if (pc >= (unsigned long)wait_for_completion) | ||
326 | goto schedule_caller; | ||
327 | goto schedule_timeout_caller; | ||
328 | |||
329 | schedule_caller: | ||
330 | frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; | ||
331 | if (pc >= (unsigned long) sleep_on) | ||
332 | pc = ((unsigned long *)frame)[sleep_on_frame.pc_offset]; | ||
333 | else | ||
334 | pc = ((unsigned long *)frame)[wait_for_completion_frame.pc_offset]; | ||
335 | goto out; | ||
336 | 417 | ||
337 | schedule_timeout_caller: | ||
338 | /* | ||
339 | * The schedule_timeout frame | ||
340 | */ | ||
341 | frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; | 418 | frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; |
419 | do { | ||
420 | int i; | ||
342 | 421 | ||
343 | /* | 422 | if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32) |
344 | * frame now points to sleep_on_timeout's frame | 423 | return 0; |
345 | */ | ||
346 | pc = ((unsigned long *)frame)[schedule_timeout_frame.pc_offset]; | ||
347 | |||
348 | if (in_sched_functions(pc)) { | ||
349 | /* schedule_timeout called by [interruptible_]sleep_on_timeout */ | ||
350 | frame = ((unsigned long *)frame)[schedule_timeout_frame.frame_offset]; | ||
351 | pc = ((unsigned long *)frame)[sleep_on_timeout_frame.pc_offset]; | ||
352 | } | ||
353 | 424 | ||
354 | out: | 425 | for (i = ARRAY_SIZE(mfinfo) - 1; i >= 0; i--) { |
426 | if (pc >= (unsigned long) mfinfo[i].func) | ||
427 | break; | ||
428 | } | ||
429 | if (i < 0) | ||
430 | break; | ||
355 | 431 | ||
356 | #ifdef CONFIG_64BIT | 432 | if (mfinfo[i].omit_fp) |
357 | if (current->thread.mflags & MF_32BIT_REGS) /* Kludge for 32-bit ps */ | 433 | break; |
358 | pc &= 0xffffffffUL; | 434 | pc = ((unsigned long *)frame)[mfinfo[i].pc_offset]; |
359 | #endif | 435 | frame = ((unsigned long *)frame)[mfinfo[i].frame_offset]; |
436 | } while (in_sched_functions(pc)); | ||
360 | 437 | ||
361 | return pc; | 438 | return pc; |
362 | } | 439 | } |