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