diff options
author | Paul Mackerras <paulus@samba.org> | 2005-10-10 08:29:05 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2005-10-10 08:29:05 -0400 |
commit | 06d67d54741a5bfefa31945ef195dfa748c29025 (patch) | |
tree | ee47a8b2f86d927c930da5120659c086f9b5dc55 /arch/powerpc/kernel/process.c | |
parent | daec962e27490be4fae9ab5a51d0c17f6e638715 (diff) |
powerpc: make process.c suitable for both 32-bit and 64-bit
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/kernel/process.c')
-rw-r--r-- | arch/powerpc/kernel/process.c | 436 |
1 files changed, 309 insertions, 127 deletions
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index ae316e9ed581..f09908a0beea 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c | |||
@@ -36,6 +36,8 @@ | |||
36 | #include <linux/kallsyms.h> | 36 | #include <linux/kallsyms.h> |
37 | #include <linux/mqueue.h> | 37 | #include <linux/mqueue.h> |
38 | #include <linux/hardirq.h> | 38 | #include <linux/hardirq.h> |
39 | #include <linux/utsname.h> | ||
40 | #include <linux/kprobes.h> | ||
39 | 41 | ||
40 | #include <asm/pgtable.h> | 42 | #include <asm/pgtable.h> |
41 | #include <asm/uaccess.h> | 43 | #include <asm/uaccess.h> |
@@ -44,6 +46,11 @@ | |||
44 | #include <asm/processor.h> | 46 | #include <asm/processor.h> |
45 | #include <asm/mmu.h> | 47 | #include <asm/mmu.h> |
46 | #include <asm/prom.h> | 48 | #include <asm/prom.h> |
49 | #ifdef CONFIG_PPC64 | ||
50 | #include <asm/firmware.h> | ||
51 | #include <asm/plpar_wrappers.h> | ||
52 | #include <asm/time.h> | ||
53 | #endif | ||
47 | 54 | ||
48 | extern unsigned long _get_SP(void); | 55 | extern unsigned long _get_SP(void); |
49 | 56 | ||
@@ -53,26 +60,6 @@ struct task_struct *last_task_used_altivec = NULL; | |||
53 | struct task_struct *last_task_used_spe = NULL; | 60 | struct task_struct *last_task_used_spe = NULL; |
54 | #endif | 61 | #endif |
55 | 62 | ||
56 | static struct fs_struct init_fs = INIT_FS; | ||
57 | static struct files_struct init_files = INIT_FILES; | ||
58 | static struct signal_struct init_signals = INIT_SIGNALS(init_signals); | ||
59 | static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); | ||
60 | struct mm_struct init_mm = INIT_MM(init_mm); | ||
61 | EXPORT_SYMBOL(init_mm); | ||
62 | |||
63 | /* this is 8kB-aligned so we can get to the thread_info struct | ||
64 | at the base of it from the stack pointer with 1 integer instruction. */ | ||
65 | union thread_union init_thread_union | ||
66 | __attribute__((__section__(".data.init_task"))) = | ||
67 | { INIT_THREAD_INFO(init_task) }; | ||
68 | |||
69 | /* initial task structure */ | ||
70 | struct task_struct init_task = INIT_TASK(init_task); | ||
71 | EXPORT_SYMBOL(init_task); | ||
72 | |||
73 | /* only used to get secondary processor up */ | ||
74 | struct task_struct *current_set[NR_CPUS] = {&init_task, }; | ||
75 | |||
76 | /* | 63 | /* |
77 | * Make sure the floating-point register state in the | 64 | * Make sure the floating-point register state in the |
78 | * the thread_struct is up to date for task tsk. | 65 | * the thread_struct is up to date for task tsk. |
@@ -237,7 +224,10 @@ int set_dabr(unsigned long dabr) | |||
237 | return ret; | 224 | return ret; |
238 | } | 225 | } |
239 | 226 | ||
227 | #ifdef CONFIG_PPC64 | ||
228 | DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array); | ||
240 | static DEFINE_PER_CPU(unsigned long, current_dabr); | 229 | static DEFINE_PER_CPU(unsigned long, current_dabr); |
230 | #endif | ||
241 | 231 | ||
242 | struct task_struct *__switch_to(struct task_struct *prev, | 232 | struct task_struct *__switch_to(struct task_struct *prev, |
243 | struct task_struct *new) | 233 | struct task_struct *new) |
@@ -308,10 +298,27 @@ struct task_struct *__switch_to(struct task_struct *prev, | |||
308 | set_dabr(new->thread.dabr); | 298 | set_dabr(new->thread.dabr); |
309 | __get_cpu_var(current_dabr) = new->thread.dabr; | 299 | __get_cpu_var(current_dabr) = new->thread.dabr; |
310 | } | 300 | } |
301 | |||
302 | flush_tlb_pending(); | ||
311 | #endif | 303 | #endif |
312 | 304 | ||
313 | new_thread = &new->thread; | 305 | new_thread = &new->thread; |
314 | old_thread = ¤t->thread; | 306 | old_thread = ¤t->thread; |
307 | |||
308 | #ifdef CONFIG_PPC64 | ||
309 | /* | ||
310 | * Collect processor utilization data per process | ||
311 | */ | ||
312 | if (firmware_has_feature(FW_FEATURE_SPLPAR)) { | ||
313 | struct cpu_usage *cu = &__get_cpu_var(cpu_usage_array); | ||
314 | long unsigned start_tb, current_tb; | ||
315 | start_tb = old_thread->start_tb; | ||
316 | cu->current_tb = current_tb = mfspr(SPRN_PURR); | ||
317 | old_thread->accum_tb += (current_tb - start_tb); | ||
318 | new_thread->start_tb = current_tb; | ||
319 | } | ||
320 | #endif | ||
321 | |||
315 | local_irq_save(flags); | 322 | local_irq_save(flags); |
316 | last = _switch(old_thread, new_thread); | 323 | last = _switch(old_thread, new_thread); |
317 | 324 | ||
@@ -320,37 +327,106 @@ struct task_struct *__switch_to(struct task_struct *prev, | |||
320 | return last; | 327 | return last; |
321 | } | 328 | } |
322 | 329 | ||
330 | static int instructions_to_print = 16; | ||
331 | |||
332 | #ifdef CONFIG_PPC64 | ||
333 | #define BAD_PC(pc) ((REGION_ID(pc) != KERNEL_REGION_ID) && \ | ||
334 | (REGION_ID(pc) != VMALLOC_REGION_ID)) | ||
335 | #else | ||
336 | #define BAD_PC(pc) ((pc) < KERNELBASE) | ||
337 | #endif | ||
338 | |||
339 | static void show_instructions(struct pt_regs *regs) | ||
340 | { | ||
341 | int i; | ||
342 | unsigned long pc = regs->nip - (instructions_to_print * 3 / 4 * | ||
343 | sizeof(int)); | ||
344 | |||
345 | printk("Instruction dump:"); | ||
346 | |||
347 | for (i = 0; i < instructions_to_print; i++) { | ||
348 | int instr; | ||
349 | |||
350 | if (!(i % 8)) | ||
351 | printk("\n"); | ||
352 | |||
353 | if (BAD_PC(pc) || __get_user(instr, (unsigned int *)pc)) { | ||
354 | printk("XXXXXXXX "); | ||
355 | } else { | ||
356 | if (regs->nip == pc) | ||
357 | printk("<%08x> ", instr); | ||
358 | else | ||
359 | printk("%08x ", instr); | ||
360 | } | ||
361 | |||
362 | pc += sizeof(int); | ||
363 | } | ||
364 | |||
365 | printk("\n"); | ||
366 | } | ||
367 | |||
368 | static struct regbit { | ||
369 | unsigned long bit; | ||
370 | const char *name; | ||
371 | } msr_bits[] = { | ||
372 | {MSR_EE, "EE"}, | ||
373 | {MSR_PR, "PR"}, | ||
374 | {MSR_FP, "FP"}, | ||
375 | {MSR_ME, "ME"}, | ||
376 | {MSR_IR, "IR"}, | ||
377 | {MSR_DR, "DR"}, | ||
378 | {0, NULL} | ||
379 | }; | ||
380 | |||
381 | static void printbits(unsigned long val, struct regbit *bits) | ||
382 | { | ||
383 | const char *sep = ""; | ||
384 | |||
385 | printk("<"); | ||
386 | for (; bits->bit; ++bits) | ||
387 | if (val & bits->bit) { | ||
388 | printk("%s%s", sep, bits->name); | ||
389 | sep = ","; | ||
390 | } | ||
391 | printk(">"); | ||
392 | } | ||
393 | |||
394 | #ifdef CONFIG_PPC64 | ||
395 | #define REG "%016lX" | ||
396 | #define REGS_PER_LINE 4 | ||
397 | #define LAST_VOLATILE 13 | ||
398 | #else | ||
399 | #define REG "%08lX" | ||
400 | #define REGS_PER_LINE 8 | ||
401 | #define LAST_VOLATILE 12 | ||
402 | #endif | ||
403 | |||
323 | void show_regs(struct pt_regs * regs) | 404 | void show_regs(struct pt_regs * regs) |
324 | { | 405 | { |
325 | int i, trap; | 406 | int i, trap; |
326 | 407 | ||
327 | printk("NIP: %08lX LR: %08lX SP: %08lX REGS: %p TRAP: %04lx %s\n", | 408 | printk("NIP: "REG" LR: "REG" CTR: "REG"\n", |
328 | regs->nip, regs->link, regs->gpr[1], regs, regs->trap, | 409 | regs->nip, regs->link, regs->ctr); |
329 | print_tainted()); | 410 | printk("REGS: %p TRAP: %04lx %s (%s)\n", |
330 | printk("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", | 411 | regs, regs->trap, print_tainted(), system_utsname.release); |
331 | regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, | 412 | printk("MSR: "REG" ", regs->msr); |
332 | regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, | 413 | printbits(regs->msr, msr_bits); |
333 | regs->msr&MSR_IR ? 1 : 0, | 414 | printk(" CR: %08lX XER: %08lX\n", regs->ccr, regs->xer); |
334 | regs->msr&MSR_DR ? 1 : 0); | ||
335 | trap = TRAP(regs); | 415 | trap = TRAP(regs); |
336 | if (trap == 0x300 || trap == 0x600) | 416 | if (trap == 0x300 || trap == 0x600) |
337 | printk("DAR: %08lX, DSISR: %08lX\n", regs->dar, regs->dsisr); | 417 | printk("DAR: "REG", DSISR: "REG"\n", regs->dar, regs->dsisr); |
338 | printk("TASK = %p[%d] '%s' THREAD: %p\n", | 418 | printk("TASK = %p[%d] '%s' THREAD: %p", |
339 | current, current->pid, current->comm, current->thread_info); | 419 | current, current->pid, current->comm, current->thread_info); |
340 | printk("Last syscall: %ld ", current->thread.last_syscall); | ||
341 | 420 | ||
342 | #ifdef CONFIG_SMP | 421 | #ifdef CONFIG_SMP |
343 | printk(" CPU: %d", smp_processor_id()); | 422 | printk(" CPU: %d", smp_processor_id()); |
344 | #endif /* CONFIG_SMP */ | 423 | #endif /* CONFIG_SMP */ |
345 | 424 | ||
346 | for (i = 0; i < 32; i++) { | 425 | for (i = 0; i < 32; i++) { |
347 | long r; | 426 | if ((i % REGS_PER_LINE) == 0) |
348 | if ((i % 8) == 0) | ||
349 | printk("\n" KERN_INFO "GPR%02d: ", i); | 427 | printk("\n" KERN_INFO "GPR%02d: ", i); |
350 | if (__get_user(r, ®s->gpr[i])) | 428 | printk(REG " ", regs->gpr[i]); |
351 | break; | 429 | if (i == LAST_VOLATILE && !FULL_REGS(regs)) |
352 | printk("%08lX ", r); | ||
353 | if (i == 12 && !FULL_REGS(regs)) | ||
354 | break; | 430 | break; |
355 | } | 431 | } |
356 | printk("\n"); | 432 | printk("\n"); |
@@ -359,16 +435,20 @@ void show_regs(struct pt_regs * regs) | |||
359 | * Lookup NIP late so we have the best change of getting the | 435 | * Lookup NIP late so we have the best change of getting the |
360 | * above info out without failing | 436 | * above info out without failing |
361 | */ | 437 | */ |
362 | printk("NIP [%08lx] ", regs->nip); | 438 | printk("NIP ["REG"] ", regs->nip); |
363 | print_symbol("%s\n", regs->nip); | 439 | print_symbol("%s\n", regs->nip); |
364 | printk("LR [%08lx] ", regs->link); | 440 | printk("LR ["REG"] ", regs->link); |
365 | print_symbol("%s\n", regs->link); | 441 | print_symbol("%s\n", regs->link); |
366 | #endif | 442 | #endif |
367 | show_stack(current, (unsigned long *) regs->gpr[1]); | 443 | show_stack(current, (unsigned long *) regs->gpr[1]); |
444 | if (!user_mode(regs)) | ||
445 | show_instructions(regs); | ||
368 | } | 446 | } |
369 | 447 | ||
370 | void exit_thread(void) | 448 | void exit_thread(void) |
371 | { | 449 | { |
450 | kprobe_flush_task(current); | ||
451 | |||
372 | #ifndef CONFIG_SMP | 452 | #ifndef CONFIG_SMP |
373 | if (last_task_used_math == current) | 453 | if (last_task_used_math == current) |
374 | last_task_used_math = NULL; | 454 | last_task_used_math = NULL; |
@@ -385,6 +465,14 @@ void exit_thread(void) | |||
385 | 465 | ||
386 | void flush_thread(void) | 466 | void flush_thread(void) |
387 | { | 467 | { |
468 | #ifdef CONFIG_PPC64 | ||
469 | struct thread_info *t = current_thread_info(); | ||
470 | |||
471 | if (t->flags & _TIF_ABI_PENDING) | ||
472 | t->flags ^= (_TIF_ABI_PENDING | _TIF_32BIT); | ||
473 | #endif | ||
474 | kprobe_flush_task(current); | ||
475 | |||
388 | #ifndef CONFIG_SMP | 476 | #ifndef CONFIG_SMP |
389 | if (last_task_used_math == current) | 477 | if (last_task_used_math == current) |
390 | last_task_used_math = NULL; | 478 | last_task_used_math = NULL; |
@@ -425,15 +513,13 @@ void prepare_to_copy(struct task_struct *tsk) | |||
425 | /* | 513 | /* |
426 | * Copy a thread.. | 514 | * Copy a thread.. |
427 | */ | 515 | */ |
428 | int | 516 | int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, |
429 | copy_thread(int nr, unsigned long clone_flags, unsigned long usp, | 517 | unsigned long unused, struct task_struct *p, |
430 | unsigned long unused, | 518 | struct pt_regs *regs) |
431 | struct task_struct *p, struct pt_regs *regs) | ||
432 | { | 519 | { |
433 | struct pt_regs *childregs, *kregs; | 520 | struct pt_regs *childregs, *kregs; |
434 | extern void ret_from_fork(void); | 521 | extern void ret_from_fork(void); |
435 | unsigned long sp = (unsigned long)p->thread_info + THREAD_SIZE; | 522 | unsigned long sp = (unsigned long)p->thread_info + THREAD_SIZE; |
436 | unsigned long childframe; | ||
437 | 523 | ||
438 | CHECK_FULL_REGS(regs); | 524 | CHECK_FULL_REGS(regs); |
439 | /* Copy registers */ | 525 | /* Copy registers */ |
@@ -443,17 +529,26 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, | |||
443 | if ((childregs->msr & MSR_PR) == 0) { | 529 | if ((childregs->msr & MSR_PR) == 0) { |
444 | /* for kernel thread, set `current' and stackptr in new task */ | 530 | /* for kernel thread, set `current' and stackptr in new task */ |
445 | childregs->gpr[1] = sp + sizeof(struct pt_regs); | 531 | childregs->gpr[1] = sp + sizeof(struct pt_regs); |
532 | #ifdef CONFIG_PPC32 | ||
446 | childregs->gpr[2] = (unsigned long) p; | 533 | childregs->gpr[2] = (unsigned long) p; |
534 | #else | ||
535 | clear_ti_thread_flag(p->thread_info, TIF_32BIT); | ||
536 | #endif | ||
447 | p->thread.regs = NULL; /* no user register state */ | 537 | p->thread.regs = NULL; /* no user register state */ |
448 | } else { | 538 | } else { |
449 | childregs->gpr[1] = usp; | 539 | childregs->gpr[1] = usp; |
450 | p->thread.regs = childregs; | 540 | p->thread.regs = childregs; |
451 | if (clone_flags & CLONE_SETTLS) | 541 | if (clone_flags & CLONE_SETTLS) { |
452 | childregs->gpr[2] = childregs->gpr[6]; | 542 | #ifdef CONFIG_PPC64 |
543 | if (!test_thread_flag(TIF_32BIT)) | ||
544 | childregs->gpr[13] = childregs->gpr[6]; | ||
545 | else | ||
546 | #endif | ||
547 | childregs->gpr[2] = childregs->gpr[6]; | ||
548 | } | ||
453 | } | 549 | } |
454 | childregs->gpr[3] = 0; /* Result from fork() */ | 550 | childregs->gpr[3] = 0; /* Result from fork() */ |
455 | sp -= STACK_FRAME_OVERHEAD; | 551 | sp -= STACK_FRAME_OVERHEAD; |
456 | childframe = sp; | ||
457 | 552 | ||
458 | /* | 553 | /* |
459 | * The way this works is that at some point in the future | 554 | * The way this works is that at some point in the future |
@@ -467,9 +562,30 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, | |||
467 | kregs = (struct pt_regs *) sp; | 562 | kregs = (struct pt_regs *) sp; |
468 | sp -= STACK_FRAME_OVERHEAD; | 563 | sp -= STACK_FRAME_OVERHEAD; |
469 | p->thread.ksp = sp; | 564 | p->thread.ksp = sp; |
470 | kregs->nip = (unsigned long)ret_from_fork; | ||
471 | 565 | ||
566 | #ifdef CONFIG_PPC64 | ||
567 | if (cpu_has_feature(CPU_FTR_SLB)) { | ||
568 | unsigned long sp_vsid = get_kernel_vsid(sp); | ||
569 | |||
570 | sp_vsid <<= SLB_VSID_SHIFT; | ||
571 | sp_vsid |= SLB_VSID_KERNEL; | ||
572 | if (cpu_has_feature(CPU_FTR_16M_PAGE)) | ||
573 | sp_vsid |= SLB_VSID_L; | ||
574 | |||
575 | p->thread.ksp_vsid = sp_vsid; | ||
576 | } | ||
577 | |||
578 | /* | ||
579 | * The PPC64 ABI makes use of a TOC to contain function | ||
580 | * pointers. The function (ret_from_except) is actually a pointer | ||
581 | * to the TOC entry. The first entry is a pointer to the actual | ||
582 | * function. | ||
583 | */ | ||
584 | kregs->nip = *((unsigned long *)ret_from_fork); | ||
585 | #else | ||
586 | kregs->nip = (unsigned long)ret_from_fork; | ||
472 | p->thread.last_syscall = -1; | 587 | p->thread.last_syscall = -1; |
588 | #endif | ||
473 | 589 | ||
474 | return 0; | 590 | return 0; |
475 | } | 591 | } |
@@ -477,18 +593,61 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, | |||
477 | /* | 593 | /* |
478 | * Set up a thread for executing a new program | 594 | * Set up a thread for executing a new program |
479 | */ | 595 | */ |
480 | void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) | 596 | void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp) |
481 | { | 597 | { |
482 | set_fs(USER_DS); | 598 | set_fs(USER_DS); |
599 | |||
600 | /* | ||
601 | * If we exec out of a kernel thread then thread.regs will not be | ||
602 | * set. Do it now. | ||
603 | */ | ||
604 | if (!current->thread.regs) { | ||
605 | unsigned long childregs = (unsigned long)current->thread_info + | ||
606 | THREAD_SIZE; | ||
607 | childregs -= sizeof(struct pt_regs); | ||
608 | current->thread.regs = (struct pt_regs *)childregs; | ||
609 | } | ||
610 | |||
483 | memset(regs->gpr, 0, sizeof(regs->gpr)); | 611 | memset(regs->gpr, 0, sizeof(regs->gpr)); |
484 | regs->ctr = 0; | 612 | regs->ctr = 0; |
485 | regs->link = 0; | 613 | regs->link = 0; |
486 | regs->xer = 0; | 614 | regs->xer = 0; |
487 | regs->ccr = 0; | 615 | regs->ccr = 0; |
488 | regs->mq = 0; | ||
489 | regs->nip = nip; | ||
490 | regs->gpr[1] = sp; | 616 | regs->gpr[1] = sp; |
617 | |||
618 | #ifdef CONFIG_PPC32 | ||
619 | regs->mq = 0; | ||
620 | regs->nip = start; | ||
491 | regs->msr = MSR_USER; | 621 | regs->msr = MSR_USER; |
622 | #else | ||
623 | if (test_thread_flag(TIF_32BIT)) { | ||
624 | unsigned long entry, toc, load_addr = regs->gpr[2]; | ||
625 | |||
626 | /* start is a relocated pointer to the function descriptor for | ||
627 | * the elf _start routine. The first entry in the function | ||
628 | * descriptor is the entry address of _start and the second | ||
629 | * entry is the TOC value we need to use. | ||
630 | */ | ||
631 | __get_user(entry, (unsigned long __user *)start); | ||
632 | __get_user(toc, (unsigned long __user *)start+1); | ||
633 | |||
634 | /* Check whether the e_entry function descriptor entries | ||
635 | * need to be relocated before we can use them. | ||
636 | */ | ||
637 | if (load_addr != 0) { | ||
638 | entry += load_addr; | ||
639 | toc += load_addr; | ||
640 | } | ||
641 | regs->nip = entry; | ||
642 | regs->gpr[2] = toc; | ||
643 | regs->msr = MSR_USER64; | ||
644 | } else { | ||
645 | regs->nip = start; | ||
646 | regs->gpr[2] = 0; | ||
647 | regs->msr = MSR_USER32; | ||
648 | } | ||
649 | #endif | ||
650 | |||
492 | #ifndef CONFIG_SMP | 651 | #ifndef CONFIG_SMP |
493 | if (last_task_used_math == current) | 652 | if (last_task_used_math == current) |
494 | last_task_used_math = NULL; | 653 | last_task_used_math = NULL; |
@@ -506,6 +665,7 @@ void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) | |||
506 | #ifdef CONFIG_ALTIVEC | 665 | #ifdef CONFIG_ALTIVEC |
507 | memset(current->thread.vr, 0, sizeof(current->thread.vr)); | 666 | memset(current->thread.vr, 0, sizeof(current->thread.vr)); |
508 | memset(¤t->thread.vscr, 0, sizeof(current->thread.vscr)); | 667 | memset(¤t->thread.vscr, 0, sizeof(current->thread.vscr)); |
668 | current->thread.vscr.u[3] = 0x00010000; /* Java mode disabled */ | ||
509 | current->thread.vrsave = 0; | 669 | current->thread.vrsave = 0; |
510 | current->thread.used_vr = 0; | 670 | current->thread.used_vr = 0; |
511 | #endif /* CONFIG_ALTIVEC */ | 671 | #endif /* CONFIG_ALTIVEC */ |
@@ -532,22 +692,23 @@ int set_fpexc_mode(struct task_struct *tsk, unsigned int val) | |||
532 | #ifdef CONFIG_SPE | 692 | #ifdef CONFIG_SPE |
533 | tsk->thread.fpexc_mode = val & | 693 | tsk->thread.fpexc_mode = val & |
534 | (PR_FP_EXC_SW_ENABLE | PR_FP_ALL_EXCEPT); | 694 | (PR_FP_EXC_SW_ENABLE | PR_FP_ALL_EXCEPT); |
695 | return 0; | ||
535 | #else | 696 | #else |
536 | return -EINVAL; | 697 | return -EINVAL; |
537 | #endif | 698 | #endif |
538 | } else { | ||
539 | /* on a CONFIG_SPE this does not hurt us. The bits that | ||
540 | * __pack_fe01 use do not overlap with bits used for | ||
541 | * PR_FP_EXC_SW_ENABLE. Additionally, the MSR[FE0,FE1] bits | ||
542 | * on CONFIG_SPE implementations are reserved so writing to | ||
543 | * them does not change anything */ | ||
544 | if (val > PR_FP_EXC_PRECISE) | ||
545 | return -EINVAL; | ||
546 | tsk->thread.fpexc_mode = __pack_fe01(val); | ||
547 | if (regs != NULL && (regs->msr & MSR_FP) != 0) | ||
548 | regs->msr = (regs->msr & ~(MSR_FE0|MSR_FE1)) | ||
549 | | tsk->thread.fpexc_mode; | ||
550 | } | 699 | } |
700 | |||
701 | /* on a CONFIG_SPE this does not hurt us. The bits that | ||
702 | * __pack_fe01 use do not overlap with bits used for | ||
703 | * PR_FP_EXC_SW_ENABLE. Additionally, the MSR[FE0,FE1] bits | ||
704 | * on CONFIG_SPE implementations are reserved so writing to | ||
705 | * them does not change anything */ | ||
706 | if (val > PR_FP_EXC_PRECISE) | ||
707 | return -EINVAL; | ||
708 | tsk->thread.fpexc_mode = __pack_fe01(val); | ||
709 | if (regs != NULL && (regs->msr & MSR_FP) != 0) | ||
710 | regs->msr = (regs->msr & ~(MSR_FE0|MSR_FE1)) | ||
711 | | tsk->thread.fpexc_mode; | ||
551 | return 0; | 712 | return 0; |
552 | } | 713 | } |
553 | 714 | ||
@@ -566,6 +727,8 @@ int get_fpexc_mode(struct task_struct *tsk, unsigned long adr) | |||
566 | return put_user(val, (unsigned int __user *) adr); | 727 | return put_user(val, (unsigned int __user *) adr); |
567 | } | 728 | } |
568 | 729 | ||
730 | #define TRUNC_PTR(x) ((typeof(x))(((unsigned long)(x)) & 0xffffffff)) | ||
731 | |||
569 | int sys_clone(unsigned long clone_flags, unsigned long usp, | 732 | int sys_clone(unsigned long clone_flags, unsigned long usp, |
570 | int __user *parent_tidp, void __user *child_threadptr, | 733 | int __user *parent_tidp, void __user *child_threadptr, |
571 | int __user *child_tidp, int p6, | 734 | int __user *child_tidp, int p6, |
@@ -574,6 +737,12 @@ int sys_clone(unsigned long clone_flags, unsigned long usp, | |||
574 | CHECK_FULL_REGS(regs); | 737 | CHECK_FULL_REGS(regs); |
575 | if (usp == 0) | 738 | if (usp == 0) |
576 | usp = regs->gpr[1]; /* stack pointer for child */ | 739 | usp = regs->gpr[1]; /* stack pointer for child */ |
740 | #ifdef CONFIG_PPC64 | ||
741 | if (test_thread_flag(TIF_32BIT)) { | ||
742 | parent_tidp = TRUNC_PTR(parent_tidp); | ||
743 | child_tidp = TRUNC_PTR(child_tidp); | ||
744 | } | ||
745 | #endif | ||
577 | return do_fork(clone_flags, usp, regs, 0, parent_tidp, child_tidp); | 746 | return do_fork(clone_flags, usp, regs, 0, parent_tidp, child_tidp); |
578 | } | 747 | } |
579 | 748 | ||
@@ -599,7 +768,7 @@ int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, | |||
599 | struct pt_regs *regs) | 768 | struct pt_regs *regs) |
600 | { | 769 | { |
601 | int error; | 770 | int error; |
602 | char * filename; | 771 | char *filename; |
603 | 772 | ||
604 | filename = getname((char __user *) a0); | 773 | filename = getname((char __user *) a0); |
605 | error = PTR_ERR(filename); | 774 | error = PTR_ERR(filename); |
@@ -644,67 +813,19 @@ static int validate_sp(unsigned long sp, struct task_struct *p, | |||
644 | return 0; | 813 | return 0; |
645 | } | 814 | } |
646 | 815 | ||
647 | void dump_stack(void) | 816 | #ifdef CONFIG_PPC64 |
648 | { | 817 | #define MIN_STACK_FRAME 112 /* same as STACK_FRAME_OVERHEAD, in fact */ |
649 | show_stack(current, NULL); | 818 | #define FRAME_LR_SAVE 2 |
650 | } | 819 | #define INT_FRAME_SIZE (sizeof(struct pt_regs) + STACK_FRAME_OVERHEAD + 288) |
651 | 820 | #define REGS_MARKER 0x7265677368657265ul | |
652 | EXPORT_SYMBOL(dump_stack); | 821 | #define FRAME_MARKER 12 |
653 | 822 | #else | |
654 | void show_stack(struct task_struct *tsk, unsigned long *stack) | 823 | #define MIN_STACK_FRAME 16 |
655 | { | 824 | #define FRAME_LR_SAVE 1 |
656 | unsigned long sp, stack_top, prev_sp, ret; | 825 | #define INT_FRAME_SIZE (sizeof(struct pt_regs) + STACK_FRAME_OVERHEAD) |
657 | int count = 0; | 826 | #define REGS_MARKER 0x72656773ul |
658 | unsigned long next_exc = 0; | 827 | #define FRAME_MARKER 2 |
659 | struct pt_regs *regs; | ||
660 | extern char ret_from_except, ret_from_except_full, ret_from_syscall; | ||
661 | |||
662 | sp = (unsigned long) stack; | ||
663 | if (tsk == NULL) | ||
664 | tsk = current; | ||
665 | if (sp == 0) { | ||
666 | if (tsk == current) | ||
667 | asm("mr %0,1" : "=r" (sp)); | ||
668 | else | ||
669 | sp = tsk->thread.ksp; | ||
670 | } | ||
671 | |||
672 | prev_sp = (unsigned long) (tsk->thread_info + 1); | ||
673 | stack_top = (unsigned long) tsk->thread_info + THREAD_SIZE; | ||
674 | while (count < 16 && sp > prev_sp && sp < stack_top && (sp & 3) == 0) { | ||
675 | if (count == 0) { | ||
676 | printk("Call trace:"); | ||
677 | #ifdef CONFIG_KALLSYMS | ||
678 | printk("\n"); | ||
679 | #endif | ||
680 | } else { | ||
681 | if (next_exc) { | ||
682 | ret = next_exc; | ||
683 | next_exc = 0; | ||
684 | } else | ||
685 | ret = *(unsigned long *)(sp + 4); | ||
686 | printk(" [%08lx] ", ret); | ||
687 | #ifdef CONFIG_KALLSYMS | ||
688 | print_symbol("%s", ret); | ||
689 | printk("\n"); | ||
690 | #endif | ||
691 | if (ret == (unsigned long) &ret_from_except | ||
692 | || ret == (unsigned long) &ret_from_except_full | ||
693 | || ret == (unsigned long) &ret_from_syscall) { | ||
694 | /* sp + 16 points to an exception frame */ | ||
695 | regs = (struct pt_regs *) (sp + 16); | ||
696 | if (sp + 16 + sizeof(*regs) <= stack_top) | ||
697 | next_exc = regs->nip; | ||
698 | } | ||
699 | } | ||
700 | ++count; | ||
701 | sp = *(unsigned long *)sp; | ||
702 | } | ||
703 | #ifndef CONFIG_KALLSYMS | ||
704 | if (count > 0) | ||
705 | printk("\n"); | ||
706 | #endif | 828 | #endif |
707 | } | ||
708 | 829 | ||
709 | unsigned long get_wchan(struct task_struct *p) | 830 | unsigned long get_wchan(struct task_struct *p) |
710 | { | 831 | { |
@@ -715,15 +836,15 @@ unsigned long get_wchan(struct task_struct *p) | |||
715 | return 0; | 836 | return 0; |
716 | 837 | ||
717 | sp = p->thread.ksp; | 838 | sp = p->thread.ksp; |
718 | if (!validate_sp(sp, p, 16)) | 839 | if (!validate_sp(sp, p, MIN_STACK_FRAME)) |
719 | return 0; | 840 | return 0; |
720 | 841 | ||
721 | do { | 842 | do { |
722 | sp = *(unsigned long *)sp; | 843 | sp = *(unsigned long *)sp; |
723 | if (!validate_sp(sp, p, 16)) | 844 | if (!validate_sp(sp, p, MIN_STACK_FRAME)) |
724 | return 0; | 845 | return 0; |
725 | if (count > 0) { | 846 | if (count > 0) { |
726 | ip = *(unsigned long *)(sp + 4); | 847 | ip = ((unsigned long *)sp)[FRAME_LR_SAVE]; |
727 | if (!in_sched_functions(ip)) | 848 | if (!in_sched_functions(ip)) |
728 | return ip; | 849 | return ip; |
729 | } | 850 | } |
@@ -731,3 +852,64 @@ unsigned long get_wchan(struct task_struct *p) | |||
731 | return 0; | 852 | return 0; |
732 | } | 853 | } |
733 | EXPORT_SYMBOL(get_wchan); | 854 | EXPORT_SYMBOL(get_wchan); |
855 | |||
856 | static int kstack_depth_to_print = 64; | ||
857 | |||
858 | void show_stack(struct task_struct *tsk, unsigned long *stack) | ||
859 | { | ||
860 | unsigned long sp, ip, lr, newsp; | ||
861 | int count = 0; | ||
862 | int firstframe = 1; | ||
863 | |||
864 | sp = (unsigned long) stack; | ||
865 | if (tsk == NULL) | ||
866 | tsk = current; | ||
867 | if (sp == 0) { | ||
868 | if (tsk == current) | ||
869 | asm("mr %0,1" : "=r" (sp)); | ||
870 | else | ||
871 | sp = tsk->thread.ksp; | ||
872 | } | ||
873 | |||
874 | lr = 0; | ||
875 | printk("Call Trace:\n"); | ||
876 | do { | ||
877 | if (!validate_sp(sp, tsk, MIN_STACK_FRAME)) | ||
878 | return; | ||
879 | |||
880 | stack = (unsigned long *) sp; | ||
881 | newsp = stack[0]; | ||
882 | ip = stack[FRAME_LR_SAVE]; | ||
883 | if (!firstframe || ip != lr) { | ||
884 | printk("["REG"] ["REG"] ", sp, ip); | ||
885 | print_symbol("%s", ip); | ||
886 | if (firstframe) | ||
887 | printk(" (unreliable)"); | ||
888 | printk("\n"); | ||
889 | } | ||
890 | firstframe = 0; | ||
891 | |||
892 | /* | ||
893 | * See if this is an exception frame. | ||
894 | * We look for the "regshere" marker in the current frame. | ||
895 | */ | ||
896 | if (validate_sp(sp, tsk, INT_FRAME_SIZE) | ||
897 | && stack[FRAME_MARKER] == REGS_MARKER) { | ||
898 | struct pt_regs *regs = (struct pt_regs *) | ||
899 | (sp + STACK_FRAME_OVERHEAD); | ||
900 | printk("--- Exception: %lx", regs->trap); | ||
901 | print_symbol(" at %s\n", regs->nip); | ||
902 | lr = regs->link; | ||
903 | print_symbol(" LR = %s\n", lr); | ||
904 | firstframe = 1; | ||
905 | } | ||
906 | |||
907 | sp = newsp; | ||
908 | } while (count++ < kstack_depth_to_print); | ||
909 | } | ||
910 | |||
911 | void dump_stack(void) | ||
912 | { | ||
913 | show_stack(current, NULL); | ||
914 | } | ||
915 | EXPORT_SYMBOL(dump_stack); | ||