diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/v850/kernel/process.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/v850/kernel/process.c')
-rw-r--r-- | arch/v850/kernel/process.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/arch/v850/kernel/process.c b/arch/v850/kernel/process.c new file mode 100644 index 000000000000..9c708c32c1f0 --- /dev/null +++ b/arch/v850/kernel/process.c | |||
@@ -0,0 +1,236 @@ | |||
1 | /* | ||
2 | * arch/v850/kernel/process.c -- Arch-dependent process handling | ||
3 | * | ||
4 | * Copyright (C) 2001,02,03 NEC Electronics Corporation | ||
5 | * Copyright (C) 2001,02,03 Miles Bader <miles@gnu.org> | ||
6 | * | ||
7 | * This file is subject to the terms and conditions of the GNU General | ||
8 | * Public License. See the file COPYING in the main directory of this | ||
9 | * archive for more details. | ||
10 | * | ||
11 | * Written by Miles Bader <miles@gnu.org> | ||
12 | */ | ||
13 | |||
14 | #include <linux/config.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/smp.h> | ||
20 | #include <linux/smp_lock.h> | ||
21 | #include <linux/stddef.h> | ||
22 | #include <linux/unistd.h> | ||
23 | #include <linux/ptrace.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/user.h> | ||
26 | #include <linux/a.out.h> | ||
27 | #include <linux/reboot.h> | ||
28 | |||
29 | #include <asm/uaccess.h> | ||
30 | #include <asm/system.h> | ||
31 | #include <asm/pgtable.h> | ||
32 | |||
33 | extern void ret_from_fork (void); | ||
34 | |||
35 | |||
36 | /* The idle loop. */ | ||
37 | void default_idle (void) | ||
38 | { | ||
39 | while (1) { | ||
40 | while (! need_resched ()) | ||
41 | asm ("halt; nop; nop; nop; nop; nop" ::: "cc"); | ||
42 | schedule (); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | void (*idle)(void) = default_idle; | ||
47 | |||
48 | /* | ||
49 | * The idle thread. There's no useful work to be | ||
50 | * done, so just try to conserve power and have a | ||
51 | * low exit latency (ie sit in a loop waiting for | ||
52 | * somebody to say that they'd like to reschedule) | ||
53 | */ | ||
54 | void cpu_idle (void) | ||
55 | { | ||
56 | /* endless idle loop with no priority at all */ | ||
57 | (*idle) (); | ||
58 | } | ||
59 | |||
60 | /* | ||
61 | * This is the mechanism for creating a new kernel thread. | ||
62 | * | ||
63 | * NOTE! Only a kernel-only process (ie the swapper or direct descendants who | ||
64 | * haven't done an "execve()") should use this: it will work within a system | ||
65 | * call from a "real" process, but the process memory space will not be free'd | ||
66 | * until both the parent and the child have exited. | ||
67 | */ | ||
68 | int kernel_thread (int (*fn)(void *), void *arg, unsigned long flags) | ||
69 | { | ||
70 | register mm_segment_t fs = get_fs (); | ||
71 | register unsigned long syscall asm (SYSCALL_NUM); | ||
72 | register unsigned long arg0 asm (SYSCALL_ARG0); | ||
73 | register unsigned long ret asm (SYSCALL_RET); | ||
74 | |||
75 | set_fs (KERNEL_DS); | ||
76 | |||
77 | /* Clone this thread. Note that we don't pass the clone syscall's | ||
78 | second argument -- it's ignored for calls from kernel mode (the | ||
79 | child's SP is always set to the top of the kernel stack). */ | ||
80 | arg0 = flags | CLONE_VM; | ||
81 | syscall = __NR_clone; | ||
82 | asm volatile ("trap " SYSCALL_SHORT_TRAP | ||
83 | : "=r" (ret), "=r" (syscall) | ||
84 | : "1" (syscall), "r" (arg0) | ||
85 | : SYSCALL_SHORT_CLOBBERS); | ||
86 | |||
87 | if (ret == 0) { | ||
88 | /* In child thread, call FN and exit. */ | ||
89 | arg0 = (*fn) (arg); | ||
90 | syscall = __NR_exit; | ||
91 | asm volatile ("trap " SYSCALL_SHORT_TRAP | ||
92 | : "=r" (ret), "=r" (syscall) | ||
93 | : "1" (syscall), "r" (arg0) | ||
94 | : SYSCALL_SHORT_CLOBBERS); | ||
95 | } | ||
96 | |||
97 | /* In parent. */ | ||
98 | set_fs (fs); | ||
99 | |||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | void flush_thread (void) | ||
104 | { | ||
105 | set_fs (USER_DS); | ||
106 | } | ||
107 | |||
108 | int copy_thread (int nr, unsigned long clone_flags, | ||
109 | unsigned long stack_start, unsigned long stack_size, | ||
110 | struct task_struct *p, struct pt_regs *regs) | ||
111 | { | ||
112 | /* Start pushing stuff from the top of the child's kernel stack. */ | ||
113 | unsigned long orig_ksp = (unsigned long)p->thread_info + THREAD_SIZE; | ||
114 | unsigned long ksp = orig_ksp; | ||
115 | /* We push two `state save' stack fames (see entry.S) on the new | ||
116 | kernel stack: | ||
117 | 1) The innermost one is what switch_thread would have | ||
118 | pushed, and is used when we context switch to the child | ||
119 | thread for the first time. It's set up to return to | ||
120 | ret_from_fork in entry.S. | ||
121 | 2) The outermost one (nearest the top) is what a syscall | ||
122 | trap would have pushed, and is set up to return to the | ||
123 | same location as the parent thread, but with a return | ||
124 | value of 0. */ | ||
125 | struct pt_regs *child_switch_regs, *child_trap_regs; | ||
126 | |||
127 | /* Trap frame. */ | ||
128 | ksp -= STATE_SAVE_SIZE; | ||
129 | child_trap_regs = (struct pt_regs *)(ksp + STATE_SAVE_PT_OFFSET); | ||
130 | /* Switch frame. */ | ||
131 | ksp -= STATE_SAVE_SIZE; | ||
132 | child_switch_regs = (struct pt_regs *)(ksp + STATE_SAVE_PT_OFFSET); | ||
133 | |||
134 | /* First copy parent's register state to child. */ | ||
135 | *child_switch_regs = *regs; | ||
136 | *child_trap_regs = *regs; | ||
137 | |||
138 | /* switch_thread returns to the restored value of the lp | ||
139 | register (r31), so we make that the place where we want to | ||
140 | jump when the child thread begins running. */ | ||
141 | child_switch_regs->gpr[GPR_LP] = (v850_reg_t)ret_from_fork; | ||
142 | |||
143 | if (regs->kernel_mode) | ||
144 | /* Since we're returning to kernel-mode, make sure the child's | ||
145 | stored kernel stack pointer agrees with what the actual | ||
146 | stack pointer will be at that point (the trap return code | ||
147 | always restores the SP, even when returning to | ||
148 | kernel-mode). */ | ||
149 | child_trap_regs->gpr[GPR_SP] = orig_ksp; | ||
150 | else | ||
151 | /* Set the child's user-mode stack-pointer (the name | ||
152 | `stack_start' is a misnomer, it's just the initial SP | ||
153 | value). */ | ||
154 | child_trap_regs->gpr[GPR_SP] = stack_start; | ||
155 | |||
156 | /* Thread state for the child (everything else is on the stack). */ | ||
157 | p->thread.ksp = ksp; | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | /* | ||
163 | * fill in the user structure for a core dump.. | ||
164 | */ | ||
165 | void dump_thread (struct pt_regs *regs, struct user *dump) | ||
166 | { | ||
167 | #if 0 /* Later. XXX */ | ||
168 | dump->magic = CMAGIC; | ||
169 | dump->start_code = 0; | ||
170 | dump->start_stack = regs->gpr[GPR_SP]; | ||
171 | dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; | ||
172 | dump->u_dsize = ((unsigned long) (current->mm->brk + | ||
173 | (PAGE_SIZE-1))) >> PAGE_SHIFT; | ||
174 | dump->u_dsize -= dump->u_tsize; | ||
175 | dump->u_ssize = 0; | ||
176 | |||
177 | if (dump->start_stack < TASK_SIZE) | ||
178 | dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; | ||
179 | |||
180 | dump->u_ar0 = (struct user_regs_struct *)((int)&dump->regs - (int)dump); | ||
181 | dump->regs = *regs; | ||
182 | dump->u_fpvalid = 0; | ||
183 | #endif | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * sys_execve() executes a new program. | ||
188 | */ | ||
189 | int sys_execve (char *name, char **argv, char **envp, struct pt_regs *regs) | ||
190 | { | ||
191 | char *filename = getname (name); | ||
192 | int error = PTR_ERR (filename); | ||
193 | |||
194 | if (! IS_ERR (filename)) { | ||
195 | error = do_execve (filename, argv, envp, regs); | ||
196 | putname (filename); | ||
197 | } | ||
198 | |||
199 | return error; | ||
200 | } | ||
201 | |||
202 | |||
203 | /* | ||
204 | * These bracket the sleeping functions.. | ||
205 | */ | ||
206 | #define first_sched ((unsigned long)__sched_text_start) | ||
207 | #define last_sched ((unsigned long)__sched_text_end) | ||
208 | |||
209 | unsigned long get_wchan (struct task_struct *p) | ||
210 | { | ||
211 | #if 0 /* Barf. Figure out the stack-layout later. XXX */ | ||
212 | unsigned long fp, pc; | ||
213 | int count = 0; | ||
214 | |||
215 | if (!p || p == current || p->state == TASK_RUNNING) | ||
216 | return 0; | ||
217 | |||
218 | pc = thread_saved_pc (p); | ||
219 | |||
220 | /* This quite disgusting function walks up the stack, following | ||
221 | saved return address, until it something that's out of bounds | ||
222 | (as defined by `first_sched' and `last_sched'). It then | ||
223 | returns the last PC that was in-bounds. */ | ||
224 | do { | ||
225 | if (fp < stack_page + sizeof (struct task_struct) || | ||
226 | fp >= 8184+stack_page) | ||
227 | return 0; | ||
228 | pc = ((unsigned long *)fp)[1]; | ||
229 | if (pc < first_sched || pc >= last_sched) | ||
230 | return pc; | ||
231 | fp = *(unsigned long *) fp; | ||
232 | } while (count++ < 16); | ||
233 | #endif | ||
234 | |||
235 | return 0; | ||
236 | } | ||