File: /usr/src/linux/arch/mips/kernel/process.c

1     /*
2      * This file is subject to the terms and conditions of the GNU General Public
3      * License.  See the file "COPYING" in the main directory of this archive
4      * for more details.
5      *
6      * Copyright (C) 1994 - 2000 by Ralf Baechle and others.
7      * Copyright (C) 1999 Silicon Graphics, Inc.
8      */
9     #include <linux/config.h>
10     #include <linux/errno.h>
11     #include <linux/sched.h>
12     #include <linux/kernel.h>
13     #include <linux/mm.h>
14     #include <linux/stddef.h>
15     #include <linux/unistd.h>
16     #include <linux/ptrace.h>
17     #include <linux/slab.h>
18     #include <linux/mman.h>
19     #include <linux/sys.h>
20     #include <linux/user.h>
21     #include <linux/a.out.h>
22     
23     #include <asm/bootinfo.h>
24     #include <asm/cpu.h>
25     #include <asm/pgtable.h>
26     #include <asm/system.h>
27     #include <asm/mipsregs.h>
28     #include <asm/processor.h>
29     #include <asm/stackframe.h>
30     #include <asm/uaccess.h>
31     #include <asm/io.h>
32     #include <asm/elf.h>
33     #include <asm/isadep.h>
34     
35     void cpu_idle(void)
36     {
37     	/* endless idle loop with no priority at all */
38     	current->nice = 20;
39     	current->counter = -100;
40     	init_idle();
41     
42     	while (1) {
43     		while (!current->need_resched)
44     			if (cpu_wait)
45     				(*cpu_wait)();
46     		schedule();
47     		check_pgt_cache();
48     	}
49     }
50     
51     struct task_struct *last_task_used_math = NULL;
52     
53     asmlinkage void ret_from_fork(void);
54     
55     void exit_thread(void)
56     {
57     	/* Forget lazy fpu state */
58     	if (last_task_used_math == current) {
59     		set_cp0_status(ST0_CU1);
60     		__asm__ __volatile__("cfc1\t$0,$31");
61     		last_task_used_math = NULL;
62     	}
63     }
64     
65     void flush_thread(void)
66     {
67     	/* Forget lazy fpu state */
68     	if (last_task_used_math == current) {
69     		set_cp0_status(ST0_CU1);
70     		__asm__ __volatile__("cfc1\t$0,$31");
71     		last_task_used_math = NULL;
72     	}
73     }
74     
75     int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
76     		 unsigned long unused,
77                      struct task_struct * p, struct pt_regs * regs)
78     {
79     	struct pt_regs * childregs;
80     	long childksp;
81     	extern void save_fp(void*);
82     
83     	childksp = (unsigned long)p + KERNEL_STACK_SIZE - 32;
84     
85     	if (last_task_used_math == current)
86     		if (mips_cpu.options & MIPS_CPU_FPU) {
87     			set_cp0_status(ST0_CU1);
88     			save_fp(p);
89     		}
90     	/* set up new TSS. */
91     	childregs = (struct pt_regs *) childksp - 1;
92     	*childregs = *regs;
93     	childregs->regs[7] = 0;	/* Clear error flag */
94     	if(current->personality == PER_LINUX) {
95     		childregs->regs[2] = 0;	/* Child gets zero as return value */
96     		regs->regs[2] = p->pid;
97     	} else {
98     		/* Under IRIX things are a little different. */
99     		childregs->regs[2] = 0;
100     		childregs->regs[3] = 1;
101     		regs->regs[2] = p->pid;
102     		regs->regs[3] = 0;
103     	}
104     	if (childregs->cp0_status & ST0_CU0) {
105     		childregs->regs[28] = (unsigned long) p;
106     		childregs->regs[29] = childksp;
107     		p->thread.current_ds = KERNEL_DS;
108     	} else {
109     		childregs->regs[29] = usp;
110     		p->thread.current_ds = USER_DS;
111     	}
112     	p->thread.reg29 = (unsigned long) childregs;
113     	p->thread.reg31 = (unsigned long) ret_from_fork;
114     
115     	/*
116     	 * New tasks loose permission to use the fpu. This accelerates context
117     	 * switching for most programs since they don't use the fpu.
118     	 */
119     	p->thread.cp0_status = read_32bit_cp0_register(CP0_STATUS) &
120                                 ~(ST0_CU2|ST0_CU1|KU_MASK);
121     	childregs->cp0_status &= ~(ST0_CU2|ST0_CU1);
122     
123     	return 0;
124     }
125     
126     /* Fill in the fpu structure for a core dump.. */
127     int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r)
128     {
129     	/* We actually store the FPU info in the task->thread
130     	 * area.
131     	 */
132     	if(regs->cp0_status & ST0_CU1) {
133     		memcpy(r, &current->thread.fpu, sizeof(current->thread.fpu));
134     		return 1;
135     	}
136     	return 0; /* Task didn't use the fpu at all. */
137     }
138     
139     /* Fill in the user structure for a core dump.. */
140     void dump_thread(struct pt_regs *regs, struct user *dump)
141     {
142     	dump->magic = CMAGIC;
143     	dump->start_code  = current->mm->start_code;
144     	dump->start_data  = current->mm->start_data;
145     	dump->start_stack = regs->regs[29] & ~(PAGE_SIZE - 1);
146     	dump->u_tsize = (current->mm->end_code - dump->start_code) >> PAGE_SHIFT;
147     	dump->u_dsize = (current->mm->brk + (PAGE_SIZE - 1) - dump->start_data) >> PAGE_SHIFT;
148     	dump->u_ssize =
149     		(current->mm->start_stack - dump->start_stack + PAGE_SIZE - 1) >> PAGE_SHIFT;
150     	memcpy(&dump->regs[0], regs, sizeof(struct pt_regs));
151     	memcpy(&dump->regs[EF_SIZE/4], &current->thread.fpu, sizeof(current->thread.fpu));
152     }
153     
154     /*
155      * Create a kernel thread
156      */
157     int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
158     {
159     	long retval;
160     
161     	__asm__ __volatile__(
162     		".set noreorder               \n"
163     		"    move    $6,$sp           \n"
164     		"    move    $4,%5            \n"
165     		"    li      $2,%1            \n"
166     		"    syscall                  \n"
167     		"    beq     $6,$sp,1f        \n"
168     		"    subu    $sp,32           \n"	/* delay slot */
169     		"    jalr    %4               \n"
170     		"    move    $4,%3            \n"	/* delay slot */
171     		"    move    $4,$2            \n"
172     		"    li      $2,%2            \n"
173     		"    syscall                  \n"
174     		"1:  addiu   $sp,32           \n"
175     		"    move    %0,$2            \n"
176     		".set reorder"
177     		:"=r" (retval)
178     		:"i" (__NR_clone), "i" (__NR_exit),
179     		 "r" (arg), "r" (fn),
180     		 "r" (flags | CLONE_VM)
181     		 /*
182     		  * The called subroutine might have destroyed any of the
183     		  * at, result, argument or temporary registers ...
184     		  */
185     		:"$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8",
186     		 "$9","$10","$11","$12","$13","$14","$15","$24","$25");
187     
188     	return retval;
189     }
190     
191     /*
192      * These bracket the sleeping functions..
193      */
194     extern void scheduling_functions_start_here(void);
195     extern void scheduling_functions_end_here(void);
196     #define first_sched	((unsigned long) scheduling_functions_start_here)
197     #define last_sched	((unsigned long) scheduling_functions_end_here)
198     
199     /* get_wchan - a maintenance nightmare ...  */
200     unsigned long get_wchan(struct task_struct *p)
201     {
202     	unsigned long frame, pc;
203     
204     	if (!p || p == current || p->state == TASK_RUNNING)
205     		return 0;
206     
207     	pc = thread_saved_pc(&p->thread);
208     	if (pc < first_sched || pc >= last_sched) {
209     		return pc;
210     	}
211     
212     	if (pc >= (unsigned long) sleep_on_timeout)
213     		goto schedule_timeout_caller;
214     	if (pc >= (unsigned long) sleep_on)
215     		goto schedule_caller;
216     	if (pc >= (unsigned long) interruptible_sleep_on_timeout)
217     		goto schedule_timeout_caller;
218     	if (pc >= (unsigned long)interruptible_sleep_on)
219     		goto schedule_caller;
220     	goto schedule_timeout_caller;
221     
222     schedule_caller:
223     	frame = ((unsigned long *)p->thread.reg30)[9];
224     	pc    = ((unsigned long *)frame)[11];
225     	return pc;
226     
227     schedule_timeout_caller:
228     	/* Must be schedule_timeout ...  */
229     	pc    = ((unsigned long *)p->thread.reg30)[10];
230     	frame = ((unsigned long *)p->thread.reg30)[9];
231     
232     	/* The schedule_timeout frame ...  */
233     	pc    = ((unsigned long *)frame)[14];
234     	frame = ((unsigned long *)frame)[13];
235     
236     	if (pc >= first_sched && pc < last_sched) {
237     		/* schedule_timeout called by interruptible_sleep_on_timeout */
238     		pc    = ((unsigned long *)frame)[11];
239     		frame = ((unsigned long *)frame)[10];
240     	}
241     
242     	return pc;
243     }
244