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