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, ¤t->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], ¤t->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