linux/arch/m68knommu/kernel/process.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/m68knommu/kernel/process.c
   3 *
   4 *  Copyright (C) 1995  Hamish Macdonald
   5 *
   6 *  68060 fixes by Jesper Skov
   7 *
   8 *  uClinux changes
   9 *  Copyright (C) 2000-2002, David McCullough <davidm@snapgear.com>
  10 */
  11
  12/*
  13 * This file handles the architecture-dependent parts of process handling..
  14 */
  15
  16#include <linux/module.h>
  17#include <linux/errno.h>
  18#include <linux/sched.h>
  19#include <linux/kernel.h>
  20#include <linux/mm.h>
  21#include <linux/smp.h>
  22#include <linux/stddef.h>
  23#include <linux/unistd.h>
  24#include <linux/ptrace.h>
  25#include <linux/user.h>
  26#include <linux/interrupt.h>
  27#include <linux/reboot.h>
  28#include <linux/fs.h>
  29#include <linux/slab.h>
  30
  31#include <asm/uaccess.h>
  32#include <asm/system.h>
  33#include <asm/traps.h>
  34#include <asm/machdep.h>
  35#include <asm/setup.h>
  36#include <asm/pgtable.h>
  37
  38asmlinkage void ret_from_fork(void);
  39
  40/*
  41 * The following aren't currently used.
  42 */
  43void (*pm_idle)(void);
  44EXPORT_SYMBOL(pm_idle);
  45
  46void (*pm_power_off)(void);
  47EXPORT_SYMBOL(pm_power_off);
  48
  49/*
  50 * The idle loop on an m68knommu..
  51 */
  52static void default_idle(void)
  53{
  54        local_irq_disable();
  55        while (!need_resched()) {
  56                /* This stop will re-enable interrupts */
  57                __asm__("stop #0x2000" : : : "cc");
  58                local_irq_disable();
  59        }
  60        local_irq_enable();
  61}
  62
  63void (*idle)(void) = default_idle;
  64
  65/*
  66 * The idle thread. There's no useful work to be
  67 * done, so just try to conserve power and have a
  68 * low exit latency (ie sit in a loop waiting for
  69 * somebody to say that they'd like to reschedule)
  70 */
  71void cpu_idle(void)
  72{
  73        /* endless idle loop with no priority at all */
  74        while (1) {
  75                idle();
  76                preempt_enable_no_resched();
  77                schedule();
  78                preempt_disable();
  79        }
  80}
  81
  82void machine_restart(char * __unused)
  83{
  84        if (mach_reset)
  85                mach_reset();
  86        for (;;);
  87}
  88
  89void machine_halt(void)
  90{
  91        if (mach_halt)
  92                mach_halt();
  93        for (;;);
  94}
  95
  96void machine_power_off(void)
  97{
  98        if (mach_power_off)
  99                mach_power_off();
 100        for (;;);
 101}
 102
 103void show_regs(struct pt_regs * regs)
 104{
 105        printk(KERN_NOTICE "\n");
 106        printk(KERN_NOTICE "Format %02x  Vector: %04x  PC: %08lx  Status: %04x    %s\n",
 107               regs->format, regs->vector, regs->pc, regs->sr, print_tainted());
 108        printk(KERN_NOTICE "ORIG_D0: %08lx  D0: %08lx  A2: %08lx  A1: %08lx\n",
 109               regs->orig_d0, regs->d0, regs->a2, regs->a1);
 110        printk(KERN_NOTICE "A0: %08lx  D5: %08lx  D4: %08lx\n",
 111               regs->a0, regs->d5, regs->d4);
 112        printk(KERN_NOTICE "D3: %08lx  D2: %08lx  D1: %08lx\n",
 113               regs->d3, regs->d2, regs->d1);
 114        if (!(regs->sr & PS_S))
 115                printk(KERN_NOTICE "USP: %08lx\n", rdusp());
 116}
 117
 118/*
 119 * Create a kernel thread
 120 */
 121int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 122{
 123        int retval;
 124        long clone_arg = flags | CLONE_VM;
 125        mm_segment_t fs;
 126
 127        fs = get_fs();
 128        set_fs(KERNEL_DS);
 129
 130        __asm__ __volatile__ (
 131                        "movel  %%sp, %%d2\n\t"
 132                        "movel  %5, %%d1\n\t"
 133                        "movel  %1, %%d0\n\t"
 134                        "trap   #0\n\t"
 135                        "cmpl   %%sp, %%d2\n\t"
 136                        "jeq    1f\n\t"
 137                        "movel  %3, %%sp@-\n\t"
 138                        "jsr    %4@\n\t"
 139                        "movel  %2, %%d0\n\t"
 140                        "trap   #0\n"
 141                        "1:\n\t"
 142                        "movel  %%d0, %0\n"
 143                : "=d" (retval)
 144                : "i" (__NR_clone),
 145                  "i" (__NR_exit),
 146                  "a" (arg),
 147                  "a" (fn),
 148                  "a" (clone_arg)
 149                : "cc", "%d0", "%d1", "%d2");
 150
 151        set_fs(fs);
 152        return retval;
 153}
 154
 155void flush_thread(void)
 156{
 157#ifdef CONFIG_FPU
 158        unsigned long zero = 0;
 159#endif
 160        set_fs(USER_DS);
 161        current->thread.fs = __USER_DS;
 162#ifdef CONFIG_FPU
 163        if (!FPU_IS_EMU)
 164                asm volatile (".chip 68k/68881\n\t"
 165                              "frestore %0@\n\t"
 166                              ".chip 68k" : : "a" (&zero));
 167#endif
 168}
 169
 170/*
 171 * "m68k_fork()".. By the time we get here, the
 172 * non-volatile registers have also been saved on the
 173 * stack. We do some ugly pointer stuff here.. (see
 174 * also copy_thread)
 175 */
 176
 177asmlinkage int m68k_fork(struct pt_regs *regs)
 178{
 179        /* fork almost works, enough to trick you into looking elsewhere :-( */
 180        return(-EINVAL);
 181}
 182
 183asmlinkage int m68k_vfork(struct pt_regs *regs)
 184{
 185        return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL, NULL);
 186}
 187
 188asmlinkage int m68k_clone(struct pt_regs *regs)
 189{
 190        unsigned long clone_flags;
 191        unsigned long newsp;
 192
 193        /* syscall2 puts clone_flags in d1 and usp in d2 */
 194        clone_flags = regs->d1;
 195        newsp = regs->d2;
 196        if (!newsp)
 197                newsp = rdusp();
 198        return do_fork(clone_flags, newsp, regs, 0, NULL, NULL);
 199}
 200
 201int copy_thread(unsigned long clone_flags,
 202                unsigned long usp, unsigned long topstk,
 203                struct task_struct * p, struct pt_regs * regs)
 204{
 205        struct pt_regs * childregs;
 206        struct switch_stack * childstack, *stack;
 207        unsigned long *retp;
 208
 209        childregs = (struct pt_regs *) (task_stack_page(p) + THREAD_SIZE) - 1;
 210
 211        *childregs = *regs;
 212        childregs->d0 = 0;
 213
 214        retp = ((unsigned long *) regs);
 215        stack = ((struct switch_stack *) retp) - 1;
 216
 217        childstack = ((struct switch_stack *) childregs) - 1;
 218        *childstack = *stack;
 219        childstack->retpc = (unsigned long)ret_from_fork;
 220
 221        p->thread.usp = usp;
 222        p->thread.ksp = (unsigned long)childstack;
 223
 224        if (clone_flags & CLONE_SETTLS)
 225                task_thread_info(p)->tp_value = regs->d5;
 226
 227        /*
 228         * Must save the current SFC/DFC value, NOT the value when
 229         * the parent was last descheduled - RGH  10-08-96
 230         */
 231        p->thread.fs = get_fs().seg;
 232
 233#ifdef CONFIG_FPU
 234        if (!FPU_IS_EMU) {
 235                /* Copy the current fpu state */
 236                asm volatile ("fsave %0" : : "m" (p->thread.fpstate[0]) : "memory");
 237
 238                if (p->thread.fpstate[0])
 239                  asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t"
 240                                "fmoveml %/fpiar/%/fpcr/%/fpsr,%1"
 241                                : : "m" (p->thread.fp[0]), "m" (p->thread.fpcntl[0])
 242                                : "memory");
 243                /* Restore the state in case the fpu was busy */
 244                asm volatile ("frestore %0" : : "m" (p->thread.fpstate[0]));
 245        }
 246#endif
 247
 248        return 0;
 249}
 250
 251/* Fill in the fpu structure for a core dump.  */
 252
 253int dump_fpu(struct pt_regs *regs, struct user_m68kfp_struct *fpu)
 254{
 255#ifdef CONFIG_FPU
 256        char fpustate[216];
 257
 258        if (FPU_IS_EMU) {
 259                int i;
 260
 261                memcpy(fpu->fpcntl, current->thread.fpcntl, 12);
 262                memcpy(fpu->fpregs, current->thread.fp, 96);
 263                /* Convert internal fpu reg representation
 264                 * into long double format
 265                 */
 266                for (i = 0; i < 24; i += 3)
 267                        fpu->fpregs[i] = ((fpu->fpregs[i] & 0xffff0000) << 15) |
 268                                         ((fpu->fpregs[i] & 0x0000ffff) << 16);
 269                return 1;
 270        }
 271
 272        /* First dump the fpu context to avoid protocol violation.  */
 273        asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory");
 274        if (!fpustate[0])
 275                return 0;
 276
 277        asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0"
 278                :: "m" (fpu->fpcntl[0])
 279                : "memory");
 280        asm volatile ("fmovemx %/fp0-%/fp7,%0"
 281                :: "m" (fpu->fpregs[0])
 282                : "memory");
 283#endif
 284        return 1;
 285}
 286
 287/*
 288 *      Generic dumping code. Used for panic and debug.
 289 */
 290void dump(struct pt_regs *fp)
 291{
 292        unsigned long   *sp;
 293        unsigned char   *tp;
 294        int             i;
 295
 296        printk(KERN_EMERG "\nCURRENT PROCESS:\n\n");
 297        printk(KERN_EMERG "COMM=%s PID=%d\n", current->comm, current->pid);
 298
 299        if (current->mm) {
 300                printk(KERN_EMERG "TEXT=%08x-%08x DATA=%08x-%08x BSS=%08x-%08x\n",
 301                        (int) current->mm->start_code,
 302                        (int) current->mm->end_code,
 303                        (int) current->mm->start_data,
 304                        (int) current->mm->end_data,
 305                        (int) current->mm->end_data,
 306                        (int) current->mm->brk);
 307                printk(KERN_EMERG "USER-STACK=%08x KERNEL-STACK=%08x\n\n",
 308                        (int) current->mm->start_stack,
 309                        (int)(((unsigned long) current) + THREAD_SIZE));
 310        }
 311
 312        printk(KERN_EMERG "PC: %08lx\n", fp->pc);
 313        printk(KERN_EMERG "SR: %08lx    SP: %08lx\n", (long) fp->sr, (long) fp);
 314        printk(KERN_EMERG "d0: %08lx    d1: %08lx    d2: %08lx    d3: %08lx\n",
 315                fp->d0, fp->d1, fp->d2, fp->d3);
 316        printk(KERN_EMERG "d4: %08lx    d5: %08lx    a0: %08lx    a1: %08lx\n",
 317                fp->d4, fp->d5, fp->a0, fp->a1);
 318        printk(KERN_EMERG "\nUSP: %08x   TRAPFRAME: %p\n",
 319                (unsigned int) rdusp(), fp);
 320
 321        printk(KERN_EMERG "\nCODE:");
 322        tp = ((unsigned char *) fp->pc) - 0x20;
 323        for (sp = (unsigned long *) tp, i = 0; (i < 0x40);  i += 4) {
 324                if ((i % 0x10) == 0)
 325                        printk(KERN_EMERG "%p: ", tp + i);
 326                printk("%08x ", (int) *sp++);
 327        }
 328        printk(KERN_EMERG "\n");
 329
 330        printk(KERN_EMERG "KERNEL STACK:");
 331        tp = ((unsigned char *) fp) - 0x40;
 332        for (sp = (unsigned long *) tp, i = 0; (i < 0xc0); i += 4) {
 333                if ((i % 0x10) == 0)
 334                        printk(KERN_EMERG "%p: ", tp + i);
 335                printk("%08x ", (int) *sp++);
 336        }
 337        printk(KERN_EMERG "\n");
 338
 339        printk(KERN_EMERG "USER STACK:");
 340        tp = (unsigned char *) (rdusp() - 0x10);
 341        for (sp = (unsigned long *) tp, i = 0; (i < 0x80); i += 4) {
 342                if ((i % 0x10) == 0)
 343                        printk(KERN_EMERG "%p: ", tp + i);
 344                printk("%08x ", (int) *sp++);
 345        }
 346        printk(KERN_EMERG "\n");
 347}
 348
 349/*
 350 * sys_execve() executes a new program.
 351 */
 352asmlinkage int sys_execve(const char *name,
 353                          const char *const *argv,
 354                          const char *const *envp)
 355{
 356        int error;
 357        char * filename;
 358        struct pt_regs *regs = (struct pt_regs *) &name;
 359
 360        filename = getname(name);
 361        error = PTR_ERR(filename);
 362        if (IS_ERR(filename))
 363                return error;
 364        error = do_execve(filename, argv, envp, regs);
 365        putname(filename);
 366        return error;
 367}
 368
 369unsigned long get_wchan(struct task_struct *p)
 370{
 371        unsigned long fp, pc;
 372        unsigned long stack_page;
 373        int count = 0;
 374        if (!p || p == current || p->state == TASK_RUNNING)
 375                return 0;
 376
 377        stack_page = (unsigned long)p;
 378        fp = ((struct switch_stack *)p->thread.ksp)->a6;
 379        do {
 380                if (fp < stack_page+sizeof(struct thread_info) ||
 381                    fp >= THREAD_SIZE-8+stack_page)
 382                        return 0;
 383                pc = ((unsigned long *)fp)[1];
 384                if (!in_sched_functions(pc))
 385                        return pc;
 386                fp = *(unsigned long *) fp;
 387        } while (count++ < 16);
 388        return 0;
 389}
 390
 391/*
 392 * Return saved PC of a blocked thread.
 393 */
 394unsigned long thread_saved_pc(struct task_struct *tsk)
 395{
 396        struct switch_stack *sw = (struct switch_stack *)tsk->thread.ksp;
 397
 398        /* Check whether the thread is blocked in resume() */
 399        if (in_sched_functions(sw->retpc))
 400                return ((unsigned long *)sw->a6)[1];
 401        else
 402                return sw->retpc;
 403}
 404
 405