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