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 | } |
