X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fsh%2Fkernel%2Fprocess.c;h=9f6aa80613ab24d396365cca4d4f8f5f6fb340eb;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=3d024590c24e8f34c185ecd49a13b5ec547ba6ae;hpb=6a77f38946aaee1cd85eeec6cf4229b204c15071;p=linux-2.6.git diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 3d024590c..9f6aa8061 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c @@ -5,6 +5,7 @@ * Copyright (C) 1995 Linus Torvalds * * SuperH version: Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima + * Copyright (C) 2006 Lineo Solutions Inc. support SH4A UBC */ /* @@ -15,21 +16,18 @@ #include #include #include -#include #include +#include +#include #include -#include #include +#include #include #include #include #include -#if defined(CONFIG_SH_HS7751RVOIP) -#include -#elif defined(CONFIG_SH_RTS7751R2D) -#include -#endif +#include static int hlt_counter=0; @@ -37,6 +35,11 @@ int ubc_usercnt = 0; #define HARD_IDLE_TIMEOUT (HZ / 3) +void (*pm_idle)(void); + +void (*pm_power_off)(void); +EXPORT_SYMBOL(pm_power_off); + void disable_hlt(void) { hlt_counter++; @@ -52,27 +55,31 @@ void enable_hlt(void) EXPORT_SYMBOL(enable_hlt); void default_idle(void) +{ + if (!hlt_counter) + cpu_sleep(); + else + cpu_relax(); +} + +void cpu_idle(void) { /* endless idle loop with no priority at all */ while (1) { - if (hlt_counter) { - while (1) - if (need_resched()) - break; - } else { - while (!need_resched()) - cpu_sleep(); - } + void (*idle)(void) = pm_idle; + + if (!idle) + idle = default_idle; + while (!need_resched()) + idle(); + + preempt_enable_no_resched(); schedule(); + preempt_disable(); } } -void cpu_idle(void) -{ - default_idle(); -} - void machine_restart(char * __unused) { /* SR.BL=1 and invoke address error to let CPU reset (manual reset) */ @@ -80,43 +87,26 @@ void machine_restart(char * __unused) "mov.l @%1, %0" : : "r" (0x10000000), "r" (0x80000001)); } -EXPORT_SYMBOL(machine_restart); - void machine_halt(void) { -#if defined(CONFIG_SH_HS7751RVOIP) - unsigned short value; + local_irq_disable(); - value = ctrl_inw(PA_OUTPORTR); - ctrl_outw((value & 0xffdf), PA_OUTPORTR); -#elif defined(CONFIG_SH_RTS7751R2D) - ctrl_outw(0x0001, PA_POWOFF); -#endif while (1) cpu_sleep(); } -EXPORT_SYMBOL(machine_halt); - void machine_power_off(void) { -#if defined(CONFIG_SH_HS7751RVOIP) - unsigned short value; - - value = ctrl_inw(PA_OUTPORTR); - ctrl_outw((value & 0xffdf), PA_OUTPORTR); -#elif defined(CONFIG_SH_RTS7751R2D) - ctrl_outw(0x0001, PA_POWOFF); -#endif + if (pm_power_off) + pm_power_off(); } -EXPORT_SYMBOL(machine_power_off); - void show_regs(struct pt_regs * regs) { printk("\n"); - printk("Pid : %d, Comm: %20s\n", current->pid, current->comm); - print_symbol("PC is at %s\n", regs->pc); + printk("Pid : %d:#%u, Comm: %20s\n", + current->pid, current->xid, current->comm); + print_symbol("PC is at %s\n", instruction_pointer(regs)); printk("PC : %08lx SP : %08lx SR : %08lx ", regs->pc, regs->regs[15], regs->sr); #ifdef CONFIG_MMU @@ -141,15 +131,7 @@ void show_regs(struct pt_regs * regs) printk("MACH: %08lx MACL: %08lx GBR : %08lx PR : %08lx\n", regs->mach, regs->macl, regs->gbr, regs->pr); - /* - * If we're in kernel mode, dump the stack too.. - */ - if (!user_mode(regs)) { - extern void show_task(unsigned long *sp); - unsigned long sp = regs->regs[15]; - - show_task((unsigned long *)sp); - } + show_trace(NULL, (unsigned long *)regs->regs[15], regs); } /* @@ -183,7 +165,8 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) regs.sr = (1 << 30); /* Ok, create the new process.. */ - return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); + return do_fork(flags | CLONE_VM | CLONE_UNTRACED | CLONE_KTHREAD, + 0, ®s, 0, NULL, NULL); } /* @@ -201,13 +184,8 @@ void flush_thread(void) { #if defined(CONFIG_SH_FPU) struct task_struct *tsk = current; - struct pt_regs *regs = (struct pt_regs *) - ((unsigned long)tsk->thread_info - + THREAD_SIZE - sizeof(struct pt_regs) - - sizeof(unsigned long)); - /* Forget lazy FPU state */ - clear_fpu(tsk, regs); + clear_fpu(tsk, task_pt_regs(tsk)); clear_used_math(); #endif } @@ -242,13 +220,7 @@ int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs) { struct pt_regs ptregs; - ptregs = *(struct pt_regs *) - ((unsigned long)tsk->thread_info + THREAD_SIZE - - sizeof(struct pt_regs) -#ifdef CONFIG_SH_DSP - - sizeof(struct pt_dspregs) -#endif - - sizeof(unsigned long)); + ptregs = *task_pt_regs(tsk); elf_core_copy_regs(regs, &ptregs); return 1; @@ -262,11 +234,7 @@ dump_task_fpu (struct task_struct *tsk, elf_fpregset_t *fpu) #if defined(CONFIG_SH_FPU) fpvalid = !!tsk_used_math(tsk); if (fpvalid) { - struct pt_regs *regs = (struct pt_regs *) - ((unsigned long)tsk->thread_info - + THREAD_SIZE - sizeof(struct pt_regs) - - sizeof(unsigned long)); - unlazy_fpu(tsk, regs); + unlazy_fpu(tsk, task_pt_regs(tsk)); memcpy(fpu, &tsk->thread.fpu.hard, sizeof(*fpu)); } #endif @@ -280,6 +248,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, unsigned long unused, struct task_struct *p, struct pt_regs *regs) { + struct thread_info *ti = task_thread_info(p); struct pt_regs *childregs; #if defined(CONFIG_SH_FPU) struct task_struct *tsk = current; @@ -289,18 +258,15 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, copy_to_stopped_child_used_math(p); #endif - childregs = ((struct pt_regs *) - (THREAD_SIZE + (unsigned long) p->thread_info) -#ifdef CONFIG_SH_DSP - - sizeof(struct pt_dspregs) -#endif - - sizeof(unsigned long)) - 1; + childregs = task_pt_regs(p); *childregs = *regs; if (user_mode(regs)) { childregs->regs[15] = usp; + ti->addr_limit = USER_DS; } else { - childregs->regs[15] = (unsigned long)p->thread_info + THREAD_SIZE; + childregs->regs[15] = (unsigned long)task_stack_page(p) + THREAD_SIZE; + ti->addr_limit = KERNEL_DS; } if (clone_flags & CLONE_SETTLS) { childregs->gbr = childregs->regs[0]; @@ -315,45 +281,46 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, return 0; } -/* - * fill in the user structure for a core dump.. - */ -void dump_thread(struct pt_regs * regs, struct user * dump) -{ - dump->magic = CMAGIC; - dump->start_code = current->mm->start_code; - dump->start_data = current->mm->start_data; - dump->start_stack = regs->regs[15] & ~(PAGE_SIZE - 1); - dump->u_tsize = (current->mm->end_code - dump->start_code) >> PAGE_SHIFT; - dump->u_dsize = (current->mm->brk + (PAGE_SIZE-1) - dump->start_data) >> PAGE_SHIFT; - dump->u_ssize = (current->mm->start_stack - dump->start_stack + - PAGE_SIZE - 1) >> PAGE_SHIFT; - /* Debug registers will come here. */ - - dump->regs = *regs; - - dump->u_fpvalid = dump_fpu(regs, &dump->fpu); -} - /* Tracing by user break controller. */ static void ubc_set_tracing(int asid, unsigned long pc) { +#if defined(CONFIG_CPU_SH4A) + unsigned long val; + + val = (UBC_CBR_ID_INST | UBC_CBR_RW_READ | UBC_CBR_CE); + val |= (UBC_CBR_AIE | UBC_CBR_AIV_SET(asid)); + + ctrl_outl(val, UBC_CBR0); + ctrl_outl(pc, UBC_CAR0); + ctrl_outl(0x0, UBC_CAMR0); + ctrl_outl(0x0, UBC_CBCR); + + val = (UBC_CRR_RES | UBC_CRR_PCB | UBC_CRR_BIE); + ctrl_outl(val, UBC_CRR0); + + /* Read UBC register that we writed last. For chekking UBC Register changed */ + val = ctrl_inl(UBC_CRR0); + +#else /* CONFIG_CPU_SH4A */ ctrl_outl(pc, UBC_BARA); +#ifdef CONFIG_MMU /* We don't have any ASID settings for the SH-2! */ if (cpu_data->type != CPU_SH7604) ctrl_outb(asid, UBC_BASRA); +#endif ctrl_outl(0, UBC_BAMRA); - if (cpu_data->type == CPU_SH7729) { + if (cpu_data->type == CPU_SH7729 || cpu_data->type == CPU_SH7710) { ctrl_outw(BBR_INST | BBR_READ | BBR_CPU, UBC_BBRA); ctrl_outl(BRCR_PCBA | BRCR_PCTE, UBC_BRCR); } else { ctrl_outw(BBR_INST | BBR_READ, UBC_BBRA); ctrl_outw(BRCR_PCBA, UBC_BRCR); } +#endif /* CONFIG_CPU_SH4A */ } /* @@ -363,11 +330,7 @@ ubc_set_tracing(int asid, unsigned long pc) struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *next) { #if defined(CONFIG_SH_FPU) - struct pt_regs *regs = (struct pt_regs *) - ((unsigned long)prev->thread_info - + THREAD_SIZE - sizeof(struct pt_regs) - - sizeof(unsigned long)); - unlazy_fpu(prev, regs); + unlazy_fpu(prev, task_pt_regs(prev)); #endif #ifdef CONFIG_PREEMPT @@ -376,13 +339,7 @@ struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *ne struct pt_regs *regs; local_irq_save(flags); - regs = (struct pt_regs *) - ((unsigned long)prev->thread_info - + THREAD_SIZE - sizeof(struct pt_regs) -#ifdef CONFIG_SH_DSP - - sizeof(struct pt_dspregs) -#endif - - sizeof(unsigned long)); + regs = task_pt_regs(prev); if (user_mode(regs) && regs->regs[15] >= 0xc0000000) { int offset = (int)regs->regs[15]; @@ -396,36 +353,45 @@ struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *ne } #endif +#ifdef CONFIG_MMU /* * Restore the kernel mode register * k7 (r7_bank1) */ asm volatile("ldc %0, r7_bank" : /* no output */ - : "r" (next->thread_info)); + : "r" (task_thread_info(next))); +#endif -#ifdef CONFIG_MMU /* If no tasks are using the UBC, we're done */ if (ubc_usercnt == 0) /* If no tasks are using the UBC, we're done */; else if (next->thread.ubc_pc && next->mm) { - ubc_set_tracing(next->mm->context & MMU_CONTEXT_ASID_MASK, - next->thread.ubc_pc); + int asid = 0; +#ifdef CONFIG_MMU + asid |= next->mm->context.id & MMU_CONTEXT_ASID_MASK; +#endif + ubc_set_tracing(asid, next->thread.ubc_pc); } else { +#if defined(CONFIG_CPU_SH4A) + ctrl_outl(UBC_CBR_INIT, UBC_CBR0); + ctrl_outl(UBC_CRR_INIT, UBC_CRR0); +#else ctrl_outw(0, UBC_BBRA); ctrl_outw(0, UBC_BBRB); - } #endif + } return prev; } asmlinkage int sys_fork(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7, - struct pt_regs regs) + struct pt_regs __regs) { + struct pt_regs *regs = RELOC_HIDE(&__regs, 0); #ifdef CONFIG_MMU - return do_fork(SIGCHLD, regs.regs[15], ®s, 0, NULL, NULL); + return do_fork(SIGCHLD, regs->regs[15], regs, 0, NULL, NULL); #else /* fork almost works, enough to trick you into looking elsewhere :-( */ return -EINVAL; @@ -435,11 +401,12 @@ asmlinkage int sys_fork(unsigned long r4, unsigned long r5, asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, unsigned long parent_tidptr, unsigned long child_tidptr, - struct pt_regs regs) + struct pt_regs __regs) { + struct pt_regs *regs = RELOC_HIDE(&__regs, 0); if (!newsp) - newsp = regs.regs[15]; - return do_fork(clone_flags, newsp, ®s, 0, + newsp = regs->regs[15]; + return do_fork(clone_flags, newsp, regs, 0, (int __user *)parent_tidptr, (int __user *)child_tidptr); } @@ -455,9 +422,10 @@ asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, */ asmlinkage int sys_vfork(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7, - struct pt_regs regs) + struct pt_regs __regs) { - return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.regs[15], ®s, + struct pt_regs *regs = RELOC_HIDE(&__regs, 0); + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->regs[15], regs, 0, NULL, NULL); } @@ -466,8 +434,9 @@ asmlinkage int sys_vfork(unsigned long r4, unsigned long r5, */ asmlinkage int sys_execve(char *ufilename, char **uargv, char **uenvp, unsigned long r7, - struct pt_regs regs) + struct pt_regs __regs) { + struct pt_regs *regs = RELOC_HIDE(&__regs, 0); int error; char *filename; @@ -479,7 +448,7 @@ asmlinkage int sys_execve(char *ufilename, char **uargv, error = do_execve(filename, (char __user * __user *)uargv, (char __user * __user *)uenvp, - ®s); + regs); if (error == 0) { task_lock(current); current->ptrace &= ~PT_DTRACE; @@ -503,19 +472,23 @@ unsigned long get_wchan(struct task_struct *p) */ pc = thread_saved_pc(p); if (in_sched_functions(pc)) { - schedule_frame = ((unsigned long *)(long)p->thread.sp)[1]; - return (unsigned long)((unsigned long *)schedule_frame)[1]; + schedule_frame = (unsigned long)p->thread.sp; + return ((unsigned long *)schedule_frame)[21]; } + return pc; } -asmlinkage void break_point_trap(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs regs) +asmlinkage void break_point_trap(void) { /* Clear tracing. */ +#if defined(CONFIG_CPU_SH4A) + ctrl_outl(UBC_CBR_INIT, UBC_CBR0); + ctrl_outl(UBC_CRR_INIT, UBC_CRR0); +#else ctrl_outw(0, UBC_BBRA); ctrl_outw(0, UBC_BBRB); +#endif current->thread.ubc_pc = 0; ubc_usercnt -= 1; @@ -524,8 +497,20 @@ asmlinkage void break_point_trap(unsigned long r4, unsigned long r5, asmlinkage void break_point_trap_software(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7, - struct pt_regs regs) + struct pt_regs __regs) { - regs.pc -= 2; + struct pt_regs *regs = RELOC_HIDE(&__regs, 0); + + /* Rewind */ + regs->pc -= 2; + +#ifdef CONFIG_BUG + if (__kernel_text_address(instruction_pointer(regs))) { + u16 insn = *(u16 *)instruction_pointer(regs); + if (insn == TRAPA_BUG_OPCODE) + handle_BUG(regs); + } +#endif + force_sig(SIGTRAP, current); }