diff options
Diffstat (limited to 'arch/cris/arch-v32/kernel/process.c')
-rw-r--r-- | arch/cris/arch-v32/kernel/process.c | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/arch/cris/arch-v32/kernel/process.c b/arch/cris/arch-v32/kernel/process.c new file mode 100644 index 000000000000..882be42114f7 --- /dev/null +++ b/arch/cris/arch-v32/kernel/process.c | |||
@@ -0,0 +1,270 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000-2003 Axis Communications AB | ||
3 | * | ||
4 | * Authors: Bjorn Wesen (bjornw@axis.com) | ||
5 | * Mikael Starvik (starvik@axis.com) | ||
6 | * Tobias Anderberg (tobiasa@axis.com), CRISv32 port. | ||
7 | * | ||
8 | * This file handles the architecture-dependent parts of process handling.. | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/err.h> | ||
14 | #include <linux/fs.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <asm/arch/hwregs/reg_rdwr.h> | ||
17 | #include <asm/arch/hwregs/reg_map.h> | ||
18 | #include <asm/arch/hwregs/timer_defs.h> | ||
19 | #include <asm/arch/hwregs/intr_vect_defs.h> | ||
20 | |||
21 | extern void stop_watchdog(void); | ||
22 | |||
23 | #ifdef CONFIG_ETRAX_GPIO | ||
24 | extern void etrax_gpio_wake_up_check(void); /* Defined in drivers/gpio.c. */ | ||
25 | #endif | ||
26 | |||
27 | extern int cris_hlt_counter; | ||
28 | |||
29 | /* We use this if we don't have any better idle routine. */ | ||
30 | void default_idle(void) | ||
31 | { | ||
32 | local_irq_disable(); | ||
33 | if (!need_resched() && !cris_hlt_counter) { | ||
34 | /* Halt until exception. */ | ||
35 | __asm__ volatile("ei \n\t" | ||
36 | "halt "); | ||
37 | } | ||
38 | local_irq_enable(); | ||
39 | } | ||
40 | |||
41 | /* | ||
42 | * Free current thread data structures etc.. | ||
43 | */ | ||
44 | |||
45 | extern void deconfigure_bp(long pid); | ||
46 | void exit_thread(void) | ||
47 | { | ||
48 | deconfigure_bp(current->pid); | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * If the watchdog is enabled, disable interrupts and enter an infinite loop. | ||
53 | * The watchdog will reset the CPU after 0.1s. If the watchdog isn't enabled | ||
54 | * then enable it and wait. | ||
55 | */ | ||
56 | extern void arch_enable_nmi(void); | ||
57 | |||
58 | void | ||
59 | hard_reset_now(void) | ||
60 | { | ||
61 | /* | ||
62 | * Don't declare this variable elsewhere. We don't want any other | ||
63 | * code to know about it than the watchdog handler in entry.S and | ||
64 | * this code, implementing hard reset through the watchdog. | ||
65 | */ | ||
66 | #if defined(CONFIG_ETRAX_WATCHDOG) | ||
67 | extern int cause_of_death; | ||
68 | #endif | ||
69 | |||
70 | printk("*** HARD RESET ***\n"); | ||
71 | local_irq_disable(); | ||
72 | |||
73 | #if defined(CONFIG_ETRAX_WATCHDOG) | ||
74 | cause_of_death = 0xbedead; | ||
75 | #else | ||
76 | { | ||
77 | reg_timer_rw_wd_ctrl wd_ctrl = {0}; | ||
78 | |||
79 | stop_watchdog(); | ||
80 | |||
81 | wd_ctrl.key = 16; /* Arbitrary key. */ | ||
82 | wd_ctrl.cnt = 1; /* Minimum time. */ | ||
83 | wd_ctrl.cmd = regk_timer_start; | ||
84 | |||
85 | arch_enable_nmi(); | ||
86 | REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl); | ||
87 | } | ||
88 | #endif | ||
89 | |||
90 | while (1) | ||
91 | ; /* Wait for reset. */ | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * Return saved PC of a blocked thread. | ||
96 | */ | ||
97 | unsigned long thread_saved_pc(struct task_struct *t) | ||
98 | { | ||
99 | return (unsigned long)user_regs(t->thread_info)->erp; | ||
100 | } | ||
101 | |||
102 | static void | ||
103 | kernel_thread_helper(void* dummy, int (*fn)(void *), void * arg) | ||
104 | { | ||
105 | fn(arg); | ||
106 | do_exit(-1); /* Should never be called, return bad exit value. */ | ||
107 | } | ||
108 | |||
109 | /* Create a kernel thread. */ | ||
110 | int | ||
111 | kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) | ||
112 | { | ||
113 | struct pt_regs regs; | ||
114 | |||
115 | memset(®s, 0, sizeof(regs)); | ||
116 | |||
117 | /* Don't use r10 since that is set to 0 in copy_thread. */ | ||
118 | regs.r11 = (unsigned long) fn; | ||
119 | regs.r12 = (unsigned long) arg; | ||
120 | regs.erp = (unsigned long) kernel_thread_helper; | ||
121 | regs.ccs = 1 << (I_CCS_BITNR + CCS_SHIFT); | ||
122 | |||
123 | /* Create the new process. */ | ||
124 | return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * Setup the child's kernel stack with a pt_regs and call switch_stack() on it. | ||
129 | * It will be unnested during _resume and _ret_from_sys_call when the new thread | ||
130 | * is scheduled. | ||
131 | * | ||
132 | * Also setup the thread switching structure which is used to keep | ||
133 | * thread-specific data during _resumes. | ||
134 | */ | ||
135 | |||
136 | extern asmlinkage void ret_from_fork(void); | ||
137 | |||
138 | int | ||
139 | copy_thread(int nr, unsigned long clone_flags, unsigned long usp, | ||
140 | unsigned long unused, | ||
141 | struct task_struct *p, struct pt_regs *regs) | ||
142 | { | ||
143 | struct pt_regs *childregs; | ||
144 | struct switch_stack *swstack; | ||
145 | |||
146 | /* | ||
147 | * Put the pt_regs structure at the end of the new kernel stack page and | ||
148 | * fix it up. Note: the task_struct doubles as the kernel stack for the | ||
149 | * task. | ||
150 | */ | ||
151 | childregs = user_regs(p->thread_info); | ||
152 | *childregs = *regs; /* Struct copy of pt_regs. */ | ||
153 | p->set_child_tid = p->clear_child_tid = NULL; | ||
154 | childregs->r10 = 0; /* Child returns 0 after a fork/clone. */ | ||
155 | |||
156 | /* Set a new TLS ? | ||
157 | * The TLS is in $mof beacuse it is the 5th argument to sys_clone. | ||
158 | */ | ||
159 | if (p->mm && (clone_flags & CLONE_SETTLS)) { | ||
160 | p->thread_info->tls = regs->mof; | ||
161 | } | ||
162 | |||
163 | /* Put the switch stack right below the pt_regs. */ | ||
164 | swstack = ((struct switch_stack *) childregs) - 1; | ||
165 | |||
166 | /* Paramater to ret_from_sys_call. 0 is don't restart the syscall. */ | ||
167 | swstack->r9 = 0; | ||
168 | |||
169 | /* | ||
170 | * We want to return into ret_from_sys_call after the _resume. | ||
171 | * ret_from_fork will call ret_from_sys_call. | ||
172 | */ | ||
173 | swstack->return_ip = (unsigned long) ret_from_fork; | ||
174 | |||
175 | /* Fix the user-mode and kernel-mode stackpointer. */ | ||
176 | p->thread.usp = usp; | ||
177 | p->thread.ksp = (unsigned long) swstack; | ||
178 | |||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * Be aware of the "magic" 7th argument in the four system-calls below. | ||
184 | * They need the latest stackframe, which is put as the 7th argument by | ||
185 | * entry.S. The previous arguments are dummies or actually used, but need | ||
186 | * to be defined to reach the 7th argument. | ||
187 | * | ||
188 | * N.B.: Another method to get the stackframe is to use current_regs(). But | ||
189 | * it returns the latest stack-frame stacked when going from _user mode_ and | ||
190 | * some of these (at least sys_clone) are called from kernel-mode sometimes | ||
191 | * (for example during kernel_thread, above) and thus cannot use it. Thus, | ||
192 | * to be sure not to get any surprises, we use the method for the other calls | ||
193 | * as well. | ||
194 | */ | ||
195 | asmlinkage int | ||
196 | sys_fork(long r10, long r11, long r12, long r13, long mof, long srp, | ||
197 | struct pt_regs *regs) | ||
198 | { | ||
199 | return do_fork(SIGCHLD, rdusp(), regs, 0, NULL, NULL); | ||
200 | } | ||
201 | |||
202 | /* FIXME: Is parent_tid/child_tid really third/fourth argument? Update lib? */ | ||
203 | asmlinkage int | ||
204 | sys_clone(unsigned long newusp, unsigned long flags, int *parent_tid, int *child_tid, | ||
205 | unsigned long tls, long srp, struct pt_regs *regs) | ||
206 | { | ||
207 | if (!newusp) | ||
208 | newusp = rdusp(); | ||
209 | |||
210 | return do_fork(flags, newusp, regs, 0, parent_tid, child_tid); | ||
211 | } | ||
212 | |||
213 | /* | ||
214 | * vfork is a system call in i386 because of register-pressure - maybe | ||
215 | * we can remove it and handle it in libc but we put it here until then. | ||
216 | */ | ||
217 | asmlinkage int | ||
218 | sys_vfork(long r10, long r11, long r12, long r13, long mof, long srp, | ||
219 | struct pt_regs *regs) | ||
220 | { | ||
221 | return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL, NULL); | ||
222 | } | ||
223 | |||
224 | /* sys_execve() executes a new program. */ | ||
225 | asmlinkage int | ||
226 | sys_execve(const char *fname, char **argv, char **envp, long r13, long mof, long srp, | ||
227 | struct pt_regs *regs) | ||
228 | { | ||
229 | int error; | ||
230 | char *filename; | ||
231 | |||
232 | filename = getname(fname); | ||
233 | error = PTR_ERR(filename); | ||
234 | |||
235 | if (IS_ERR(filename)) | ||
236 | goto out; | ||
237 | |||
238 | error = do_execve(filename, argv, envp, regs); | ||
239 | putname(filename); | ||
240 | out: | ||
241 | return error; | ||
242 | } | ||
243 | |||
244 | unsigned long | ||
245 | get_wchan(struct task_struct *p) | ||
246 | { | ||
247 | /* TODO */ | ||
248 | return 0; | ||
249 | } | ||
250 | #undef last_sched | ||
251 | #undef first_sched | ||
252 | |||
253 | void show_regs(struct pt_regs * regs) | ||
254 | { | ||
255 | unsigned long usp = rdusp(); | ||
256 | printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n", | ||
257 | regs->erp, regs->srp, regs->ccs, usp, regs->mof); | ||
258 | |||
259 | printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", | ||
260 | regs->r0, regs->r1, regs->r2, regs->r3); | ||
261 | |||
262 | printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", | ||
263 | regs->r4, regs->r5, regs->r6, regs->r7); | ||
264 | |||
265 | printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", | ||
266 | regs->r8, regs->r9, regs->r10, regs->r11); | ||
267 | |||
268 | printk("r12: %08lx r13: %08lx oR10: %08lx\n", | ||
269 | regs->r12, regs->r13, regs->orig_r10); | ||
270 | } | ||