aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel/process.c')
-rw-r--r--arch/mips/kernel/process.c213
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 */
45void 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
59extern int do_signal(sigset_t *oldset, struct pt_regs *regs);
60extern int do_signal32(sigset_t *oldset, struct pt_regs *regs);
61
62/*
63 * Native o32 and N64 ABI without DSP ASE
64 */
65extern int setup_frame(struct k_sigaction * ka, struct pt_regs *regs,
66 int signr, sigset_t *set);
67extern int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs,
68 int signr, sigset_t *set, siginfo_t *info);
69
70struct 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 */
82extern int setup_frame_32(struct k_sigaction * ka, struct pt_regs *regs,
83 int signr, sigset_t *set);
84extern int setup_rt_frame_32(struct k_sigaction * ka, struct pt_regs *regs,
85 int signr, sigset_t *set, siginfo_t *info);
86
87struct 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 */
98extern int setup_rt_frame_n32(struct k_sigaction * ka, struct pt_regs *regs,
99 int signr, sigset_t *set, siginfo_t *info);
100
101struct 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
65asmlinkage void ret_from_fork(void); 107asmlinkage void ret_from_fork(void);
66 108
67void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp) 109void 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
228int 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
178int dump_task_fpu (struct task_struct *t, elf_fpregset_t *fpr) 236int 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, &regs, 0, NULL, NULL); 269 return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
212} 270}
213 271
214struct mips_frame_info { 272static 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};
218static struct mips_frame_info schedule_frame; 305
219static struct mips_frame_info schedule_timeout_frame;
220static struct mips_frame_info sleep_on_frame;
221static struct mips_frame_info sleep_on_timeout_frame;
222static struct mips_frame_info wait_for_completion_frame;
223static int mips_frame_info_initialized; 306static int mips_frame_info_initialized;
224static int __init get_frame_info(struct mips_frame_info *info, void *func) 307static 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
273static int __init frame_info_init(void) 359static 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 ... */
304unsigned long get_wchan(struct task_struct *p) 402unsigned 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
329schedule_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
337schedule_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
354out: 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}