linux/arch/sh/kernel/process_32.c
<<
>>
Prefs
   1/*
   2 * arch/sh/kernel/process.c
   3 *
   4 * This file handles the architecture-dependent parts of process handling..
   5 *
   6 *  Copyright (C) 1995  Linus Torvalds
   7 *
   8 *  SuperH version:  Copyright (C) 1999, 2000  Niibe Yutaka & Kaz Kojima
   9 *                   Copyright (C) 2006 Lineo Solutions Inc. support SH4A UBC
  10 *                   Copyright (C) 2002 - 2008  Paul Mundt
  11 *
  12 * This file is subject to the terms and conditions of the GNU General Public
  13 * License.  See the file "COPYING" in the main directory of this archive
  14 * for more details.
  15 */
  16#include <linux/module.h>
  17#include <linux/mm.h>
  18#include <linux/elfcore.h>
  19#include <linux/pm.h>
  20#include <linux/kallsyms.h>
  21#include <linux/kexec.h>
  22#include <linux/kdebug.h>
  23#include <linux/tick.h>
  24#include <linux/reboot.h>
  25#include <linux/fs.h>
  26#include <linux/preempt.h>
  27#include <asm/uaccess.h>
  28#include <asm/mmu_context.h>
  29#include <asm/pgalloc.h>
  30#include <asm/system.h>
  31#include <asm/ubc.h>
  32#include <asm/fpu.h>
  33#include <asm/syscalls.h>
  34
  35int ubc_usercnt = 0;
  36
  37void machine_restart(char * __unused)
  38{
  39        /* SR.BL=1 and invoke address error to let CPU reset (manual reset) */
  40        asm volatile("ldc %0, sr\n\t"
  41                     "mov.l @%1, %0" : : "r" (0x10000000), "r" (0x80000001));
  42}
  43
  44void machine_halt(void)
  45{
  46        local_irq_disable();
  47
  48        while (1)
  49                cpu_sleep();
  50}
  51
  52void machine_power_off(void)
  53{
  54        if (pm_power_off)
  55                pm_power_off();
  56}
  57
  58void show_regs(struct pt_regs * regs)
  59{
  60        printk("\n");
  61        printk("Pid : %d, Comm: \t\t%s\n", task_pid_nr(current), current->comm);
  62        printk("CPU : %d        \t\t%s  (%s %.*s)\n\n",
  63               smp_processor_id(), print_tainted(), init_utsname()->release,
  64               (int)strcspn(init_utsname()->version, " "),
  65               init_utsname()->version);
  66
  67        print_symbol("PC is at %s\n", instruction_pointer(regs));
  68        print_symbol("PR is at %s\n", regs->pr);
  69
  70        printk("PC  : %08lx SP  : %08lx SR  : %08lx ",
  71               regs->pc, regs->regs[15], regs->sr);
  72#ifdef CONFIG_MMU
  73        printk("TEA : %08x\n", ctrl_inl(MMU_TEA));
  74#else
  75        printk("\n");
  76#endif
  77
  78        printk("R0  : %08lx R1  : %08lx R2  : %08lx R3  : %08lx\n",
  79               regs->regs[0],regs->regs[1],
  80               regs->regs[2],regs->regs[3]);
  81        printk("R4  : %08lx R5  : %08lx R6  : %08lx R7  : %08lx\n",
  82               regs->regs[4],regs->regs[5],
  83               regs->regs[6],regs->regs[7]);
  84        printk("R8  : %08lx R9  : %08lx R10 : %08lx R11 : %08lx\n",
  85               regs->regs[8],regs->regs[9],
  86               regs->regs[10],regs->regs[11]);
  87        printk("R12 : %08lx R13 : %08lx R14 : %08lx\n",
  88               regs->regs[12],regs->regs[13],
  89               regs->regs[14]);
  90        printk("MACH: %08lx MACL: %08lx GBR : %08lx PR  : %08lx\n",
  91               regs->mach, regs->macl, regs->gbr, regs->pr);
  92
  93        show_trace(NULL, (unsigned long *)regs->regs[15], regs);
  94        show_code(regs);
  95}
  96
  97/*
  98 * Create a kernel thread
  99 */
 100ATTRIB_NORET void kernel_thread_helper(void *arg, int (*fn)(void *))
 101{
 102        do_exit(fn(arg));
 103}
 104
 105/* Don't use this in BL=1(cli).  Or else, CPU resets! */
 106int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 107{
 108        struct pt_regs regs;
 109        int pid;
 110
 111        memset(&regs, 0, sizeof(regs));
 112        regs.regs[4] = (unsigned long)arg;
 113        regs.regs[5] = (unsigned long)fn;
 114
 115        regs.pc = (unsigned long)kernel_thread_helper;
 116        regs.sr = (1 << 30);
 117
 118        /* Ok, create the new process.. */
 119        pid = do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0,
 120                      &regs, 0, NULL, NULL);
 121
 122        return pid;
 123}
 124
 125/*
 126 * Free current thread data structures etc..
 127 */
 128void exit_thread(void)
 129{
 130        if (current->thread.ubc_pc) {
 131                current->thread.ubc_pc = 0;
 132                ubc_usercnt -= 1;
 133        }
 134}
 135
 136void flush_thread(void)
 137{
 138#if defined(CONFIG_SH_FPU)
 139        struct task_struct *tsk = current;
 140        /* Forget lazy FPU state */
 141        clear_fpu(tsk, task_pt_regs(tsk));
 142        clear_used_math();
 143#endif
 144}
 145
 146void release_thread(struct task_struct *dead_task)
 147{
 148        /* do nothing */
 149}
 150
 151/* Fill in the fpu structure for a core dump.. */
 152int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
 153{
 154        int fpvalid = 0;
 155
 156#if defined(CONFIG_SH_FPU)
 157        struct task_struct *tsk = current;
 158
 159        fpvalid = !!tsk_used_math(tsk);
 160        if (fpvalid)
 161                fpvalid = !fpregs_get(tsk, NULL, 0,
 162                                      sizeof(struct user_fpu_struct),
 163                                      fpu, NULL);
 164#endif
 165
 166        return fpvalid;
 167}
 168
 169asmlinkage void ret_from_fork(void);
 170
 171int copy_thread(unsigned long clone_flags, unsigned long usp,
 172                unsigned long unused,
 173                struct task_struct *p, struct pt_regs *regs)
 174{
 175        struct thread_info *ti = task_thread_info(p);
 176        struct pt_regs *childregs;
 177#if defined(CONFIG_SH_FPU) || defined(CONFIG_SH_DSP)
 178        struct task_struct *tsk = current;
 179#endif
 180
 181#if defined(CONFIG_SH_FPU)
 182        unlazy_fpu(tsk, regs);
 183        p->thread.fpu = tsk->thread.fpu;
 184        copy_to_stopped_child_used_math(p);
 185#endif
 186
 187#if defined(CONFIG_SH_DSP)
 188        if (is_dsp_enabled(tsk)) {
 189                /* We can use the __save_dsp or just copy the struct:
 190                 * __save_dsp(p);
 191                 * p->thread.dsp_status.status |= SR_DSP
 192                 */
 193                p->thread.dsp_status = tsk->thread.dsp_status;
 194        }
 195#endif
 196
 197        childregs = task_pt_regs(p);
 198        *childregs = *regs;
 199
 200        if (user_mode(regs)) {
 201                childregs->regs[15] = usp;
 202                ti->addr_limit = USER_DS;
 203        } else {
 204                childregs->regs[15] = (unsigned long)childregs;
 205                ti->addr_limit = KERNEL_DS;
 206        }
 207
 208        if (clone_flags & CLONE_SETTLS)
 209                childregs->gbr = childregs->regs[0];
 210
 211        childregs->regs[0] = 0; /* Set return value for child */
 212
 213        p->thread.sp = (unsigned long) childregs;
 214        p->thread.pc = (unsigned long) ret_from_fork;
 215
 216        p->thread.ubc_pc = 0;
 217
 218        return 0;
 219}
 220
 221/* Tracing by user break controller.  */
 222static void ubc_set_tracing(int asid, unsigned long pc)
 223{
 224#if defined(CONFIG_CPU_SH4A)
 225        unsigned long val;
 226
 227        val = (UBC_CBR_ID_INST | UBC_CBR_RW_READ | UBC_CBR_CE);
 228        val |= (UBC_CBR_AIE | UBC_CBR_AIV_SET(asid));
 229
 230        ctrl_outl(val, UBC_CBR0);
 231        ctrl_outl(pc,  UBC_CAR0);
 232        ctrl_outl(0x0, UBC_CAMR0);
 233        ctrl_outl(0x0, UBC_CBCR);
 234
 235        val = (UBC_CRR_RES | UBC_CRR_PCB | UBC_CRR_BIE);
 236        ctrl_outl(val, UBC_CRR0);
 237
 238        /* Read UBC register that we wrote last, for checking update */
 239        val = ctrl_inl(UBC_CRR0);
 240
 241#else   /* CONFIG_CPU_SH4A */
 242        ctrl_outl(pc, UBC_BARA);
 243
 244#ifdef CONFIG_MMU
 245        ctrl_outb(asid, UBC_BASRA);
 246#endif
 247
 248        ctrl_outl(0, UBC_BAMRA);
 249
 250        if (current_cpu_data.type == CPU_SH7729 ||
 251            current_cpu_data.type == CPU_SH7710 ||
 252            current_cpu_data.type == CPU_SH7712 ||
 253            current_cpu_data.type == CPU_SH7203){
 254                ctrl_outw(BBR_INST | BBR_READ | BBR_CPU, UBC_BBRA);
 255                ctrl_outl(BRCR_PCBA | BRCR_PCTE, UBC_BRCR);
 256        } else {
 257                ctrl_outw(BBR_INST | BBR_READ, UBC_BBRA);
 258                ctrl_outw(BRCR_PCBA, UBC_BRCR);
 259        }
 260#endif  /* CONFIG_CPU_SH4A */
 261}
 262
 263/*
 264 *      switch_to(x,y) should switch tasks from x to y.
 265 *
 266 */
 267struct task_struct *__switch_to(struct task_struct *prev,
 268                                struct task_struct *next)
 269{
 270#if defined(CONFIG_SH_FPU)
 271        unlazy_fpu(prev, task_pt_regs(prev));
 272#endif
 273
 274#ifdef CONFIG_MMU
 275        /*
 276         * Restore the kernel mode register
 277         *      k7 (r7_bank1)
 278         */
 279        asm volatile("ldc       %0, r7_bank"
 280                     : /* no output */
 281                     : "r" (task_thread_info(next)));
 282#endif
 283
 284        /* If no tasks are using the UBC, we're done */
 285        if (ubc_usercnt == 0)
 286                /* If no tasks are using the UBC, we're done */;
 287        else if (next->thread.ubc_pc && next->mm) {
 288                int asid = 0;
 289#ifdef CONFIG_MMU
 290                asid |= cpu_asid(smp_processor_id(), next->mm);
 291#endif
 292                ubc_set_tracing(asid, next->thread.ubc_pc);
 293        } else {
 294#if defined(CONFIG_CPU_SH4A)
 295                ctrl_outl(UBC_CBR_INIT, UBC_CBR0);
 296                ctrl_outl(UBC_CRR_INIT, UBC_CRR0);
 297#else
 298                ctrl_outw(0, UBC_BBRA);
 299                ctrl_outw(0, UBC_BBRB);
 300#endif
 301        }
 302
 303        return prev;
 304}
 305
 306asmlinkage int sys_fork(unsigned long r4, unsigned long r5,
 307                        unsigned long r6, unsigned long r7,
 308                        struct pt_regs __regs)
 309{
 310#ifdef CONFIG_MMU
 311        struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
 312        return do_fork(SIGCHLD, regs->regs[15], regs, 0, NULL, NULL);
 313#else
 314        /* fork almost works, enough to trick you into looking elsewhere :-( */
 315        return -EINVAL;
 316#endif
 317}
 318
 319asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
 320                         unsigned long parent_tidptr,
 321                         unsigned long child_tidptr,
 322                         struct pt_regs __regs)
 323{
 324        struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
 325        if (!newsp)
 326                newsp = regs->regs[15];
 327        return do_fork(clone_flags, newsp, regs, 0,
 328                        (int __user *)parent_tidptr,
 329                        (int __user *)child_tidptr);
 330}
 331
 332/*
 333 * This is trivial, and on the face of it looks like it
 334 * could equally well be done in user mode.
 335 *
 336 * Not so, for quite unobvious reasons - register pressure.
 337 * In user mode vfork() cannot have a stack frame, and if
 338 * done by calling the "clone()" system call directly, you
 339 * do not have enough call-clobbered registers to hold all
 340 * the information you need.
 341 */
 342asmlinkage int sys_vfork(unsigned long r4, unsigned long r5,
 343                         unsigned long r6, unsigned long r7,
 344                         struct pt_regs __regs)
 345{
 346        struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
 347        return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->regs[15], regs,
 348                       0, NULL, NULL);
 349}
 350
 351/*
 352 * sys_execve() executes a new program.
 353 */
 354asmlinkage int sys_execve(char __user *ufilename, char __user * __user *uargv,
 355                          char __user * __user *uenvp, unsigned long r7,
 356                          struct pt_regs __regs)
 357{
 358        struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
 359        int error;
 360        char *filename;
 361
 362        filename = getname(ufilename);
 363        error = PTR_ERR(filename);
 364        if (IS_ERR(filename))
 365                goto out;
 366
 367        error = do_execve(filename, uargv, uenvp, regs);
 368        putname(filename);
 369out:
 370        return error;
 371}
 372
 373unsigned long get_wchan(struct task_struct *p)
 374{
 375        unsigned long pc;
 376
 377        if (!p || p == current || p->state == TASK_RUNNING)
 378                return 0;
 379
 380        /*
 381         * The same comment as on the Alpha applies here, too ...
 382         */
 383        pc = thread_saved_pc(p);
 384
 385#ifdef CONFIG_FRAME_POINTER
 386        if (in_sched_functions(pc)) {
 387                unsigned long schedule_frame = (unsigned long)p->thread.sp;
 388                return ((unsigned long *)schedule_frame)[21];
 389        }
 390#endif
 391
 392        return pc;
 393}
 394
 395asmlinkage void break_point_trap(void)
 396{
 397        /* Clear tracing.  */
 398#if defined(CONFIG_CPU_SH4A)
 399        ctrl_outl(UBC_CBR_INIT, UBC_CBR0);
 400        ctrl_outl(UBC_CRR_INIT, UBC_CRR0);
 401#else
 402        ctrl_outw(0, UBC_BBRA);
 403        ctrl_outw(0, UBC_BBRB);
 404        ctrl_outl(0, UBC_BRCR);
 405#endif
 406        current->thread.ubc_pc = 0;
 407        ubc_usercnt -= 1;
 408
 409        force_sig(SIGTRAP, current);
 410}
 411
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.