diff options
Diffstat (limited to 'arch/hexagon/kernel/process.c')
-rw-r--r-- | arch/hexagon/kernel/process.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/arch/hexagon/kernel/process.c b/arch/hexagon/kernel/process.c new file mode 100644 index 000000000000..18c4f0b0f4ba --- /dev/null +++ b/arch/hexagon/kernel/process.c | |||
@@ -0,0 +1,279 @@ | |||
1 | /* | ||
2 | * Process creation support for Hexagon | ||
3 | * | ||
4 | * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 and | ||
8 | * only version 2 as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/sched.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/tick.h> | ||
25 | #include <linux/uaccess.h> | ||
26 | #include <linux/slab.h> | ||
27 | |||
28 | /* | ||
29 | * Kernel thread creation. The desired kernel function is "wrapped" | ||
30 | * in the kernel_thread_helper function, which does cleanup | ||
31 | * afterwards. | ||
32 | */ | ||
33 | static void __noreturn kernel_thread_helper(void *arg, int (*fn)(void *)) | ||
34 | { | ||
35 | do_exit(fn(arg)); | ||
36 | } | ||
37 | |||
38 | int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) | ||
39 | { | ||
40 | struct pt_regs regs; | ||
41 | |||
42 | memset(®s, 0, sizeof(regs)); | ||
43 | /* | ||
44 | * Yes, we're exploting illicit knowledge of the ABI here. | ||
45 | */ | ||
46 | regs.r00 = (unsigned long) arg; | ||
47 | regs.r01 = (unsigned long) fn; | ||
48 | pt_set_elr(®s, (unsigned long)kernel_thread_helper); | ||
49 | pt_set_kmode(®s); | ||
50 | |||
51 | return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); | ||
52 | } | ||
53 | EXPORT_SYMBOL(kernel_thread); | ||
54 | |||
55 | /* | ||
56 | * Program thread launch. Often defined as a macro in processor.h, | ||
57 | * but we're shooting for a small footprint and it's not an inner-loop | ||
58 | * performance-critical operation. | ||
59 | * | ||
60 | * The Hexagon ABI specifies that R28 is zero'ed before program launch, | ||
61 | * so that gets automatically done here. If we ever stop doing that here, | ||
62 | * we'll probably want to define the ELF_PLAT_INIT macro. | ||
63 | */ | ||
64 | void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) | ||
65 | { | ||
66 | /* Set to run with user-mode data segmentation */ | ||
67 | set_fs(USER_DS); | ||
68 | /* We want to zero all data-containing registers. Is this overkill? */ | ||
69 | memset(regs, 0, sizeof(*regs)); | ||
70 | /* We might want to also zero all Processor registers here */ | ||
71 | pt_set_usermode(regs); | ||
72 | pt_set_elr(regs, pc); | ||
73 | pt_set_rte_sp(regs, sp); | ||
74 | } | ||
75 | |||
76 | /* | ||
77 | * Spin, or better still, do a hardware or VM wait instruction | ||
78 | * If hardware or VM offer wait termination even though interrupts | ||
79 | * are disabled. | ||
80 | */ | ||
81 | static void default_idle(void) | ||
82 | { | ||
83 | __vmwait(); | ||
84 | } | ||
85 | |||
86 | void (*idle_sleep)(void) = default_idle; | ||
87 | |||
88 | void cpu_idle(void) | ||
89 | { | ||
90 | while (1) { | ||
91 | tick_nohz_stop_sched_tick(1); | ||
92 | local_irq_disable(); | ||
93 | while (!need_resched()) { | ||
94 | idle_sleep(); | ||
95 | /* interrupts wake us up, but aren't serviced */ | ||
96 | local_irq_enable(); /* service interrupt */ | ||
97 | local_irq_disable(); | ||
98 | } | ||
99 | local_irq_enable(); | ||
100 | tick_nohz_restart_sched_tick(); | ||
101 | schedule(); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * Return saved PC of a blocked thread | ||
107 | */ | ||
108 | unsigned long thread_saved_pc(struct task_struct *tsk) | ||
109 | { | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * Copy architecture-specific thread state | ||
115 | */ | ||
116 | int copy_thread(unsigned long clone_flags, unsigned long usp, | ||
117 | unsigned long unused, struct task_struct *p, | ||
118 | struct pt_regs *regs) | ||
119 | { | ||
120 | struct thread_info *ti = task_thread_info(p); | ||
121 | struct hexagon_switch_stack *ss; | ||
122 | struct pt_regs *childregs; | ||
123 | asmlinkage void ret_from_fork(void); | ||
124 | |||
125 | childregs = (struct pt_regs *) (((unsigned long) ti + THREAD_SIZE) - | ||
126 | sizeof(*childregs)); | ||
127 | |||
128 | memcpy(childregs, regs, sizeof(*childregs)); | ||
129 | ti->regs = childregs; | ||
130 | |||
131 | /* | ||
132 | * Establish kernel stack pointer and initial PC for new thread | ||
133 | */ | ||
134 | ss = (struct hexagon_switch_stack *) ((unsigned long) childregs - | ||
135 | sizeof(*ss)); | ||
136 | ss->lr = (unsigned long)ret_from_fork; | ||
137 | p->thread.switch_sp = ss; | ||
138 | |||
139 | /* If User mode thread, set pt_reg stack pointer as per parameter */ | ||
140 | if (user_mode(childregs)) { | ||
141 | pt_set_rte_sp(childregs, usp); | ||
142 | |||
143 | /* Child sees zero return value */ | ||
144 | childregs->r00 = 0; | ||
145 | |||
146 | /* | ||
147 | * The clone syscall has the C signature: | ||
148 | * int [r0] clone(int flags [r0], | ||
149 | * void *child_frame [r1], | ||
150 | * void *parent_tid [r2], | ||
151 | * void *child_tid [r3], | ||
152 | * void *thread_control_block [r4]); | ||
153 | * ugp is used to provide TLS support. | ||
154 | */ | ||
155 | if (clone_flags & CLONE_SETTLS) | ||
156 | childregs->ugp = childregs->r04; | ||
157 | |||
158 | /* | ||
159 | * Parent sees new pid -- not necessary, not even possible at | ||
160 | * this point in the fork process | ||
161 | * Might also want to set things like ti->addr_limit | ||
162 | */ | ||
163 | } else { | ||
164 | /* | ||
165 | * If kernel thread, resume stack is kernel stack base. | ||
166 | * Note that this is pointer arithmetic on pt_regs * | ||
167 | */ | ||
168 | pt_set_rte_sp(childregs, (unsigned long)(childregs + 1)); | ||
169 | /* | ||
170 | * We need the current thread_info fast path pointer | ||
171 | * set up in pt_regs. The register to be used is | ||
172 | * parametric for assembler code, but the mechanism | ||
173 | * doesn't drop neatly into C. Needs to be fixed. | ||
174 | */ | ||
175 | childregs->THREADINFO_REG = (unsigned long) ti; | ||
176 | } | ||
177 | |||
178 | /* | ||
179 | * thread_info pointer is pulled out of task_struct "stack" | ||
180 | * field on switch_to. | ||
181 | */ | ||
182 | p->stack = (void *)ti; | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | /* | ||
188 | * Release any architecture-specific resources locked by thread | ||
189 | */ | ||
190 | void release_thread(struct task_struct *dead_task) | ||
191 | { | ||
192 | } | ||
193 | |||
194 | /* | ||
195 | * Free any architecture-specific thread data structures, etc. | ||
196 | */ | ||
197 | void exit_thread(void) | ||
198 | { | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * Some archs flush debug and FPU info here | ||
203 | */ | ||
204 | void flush_thread(void) | ||
205 | { | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * The "wait channel" terminology is archaic, but what we want | ||
210 | * is an identification of the point at which the scheduler | ||
211 | * was invoked by a blocked thread. | ||
212 | */ | ||
213 | unsigned long get_wchan(struct task_struct *p) | ||
214 | { | ||
215 | unsigned long fp, pc; | ||
216 | unsigned long stack_page; | ||
217 | int count = 0; | ||
218 | if (!p || p == current || p->state == TASK_RUNNING) | ||
219 | return 0; | ||
220 | |||
221 | stack_page = (unsigned long)task_stack_page(p); | ||
222 | fp = ((struct hexagon_switch_stack *)p->thread.switch_sp)->fp; | ||
223 | do { | ||
224 | if (fp < (stack_page + sizeof(struct thread_info)) || | ||
225 | fp >= (THREAD_SIZE - 8 + stack_page)) | ||
226 | return 0; | ||
227 | pc = ((unsigned long *)fp)[1]; | ||
228 | if (!in_sched_functions(pc)) | ||
229 | return pc; | ||
230 | fp = *(unsigned long *) fp; | ||
231 | } while (count++ < 16); | ||
232 | |||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | /* | ||
237 | * Borrowed from PowerPC -- basically allow smaller kernel stacks if we | ||
238 | * go crazy with the page sizes. | ||
239 | */ | ||
240 | #if THREAD_SHIFT < PAGE_SHIFT | ||
241 | |||
242 | static struct kmem_cache *thread_info_cache; | ||
243 | |||
244 | struct thread_info *alloc_thread_info_node(struct task_struct *tsk, int node) | ||
245 | { | ||
246 | struct thread_info *ti; | ||
247 | |||
248 | ti = kmem_cache_alloc_node(thread_info_cache, GFP_KERNEL, node); | ||
249 | if (unlikely(ti == NULL)) | ||
250 | return NULL; | ||
251 | #ifdef CONFIG_DEBUG_STACK_USAGE | ||
252 | memset(ti, 0, THREAD_SIZE); | ||
253 | #endif | ||
254 | return ti; | ||
255 | } | ||
256 | |||
257 | void free_thread_info(struct thread_info *ti) | ||
258 | { | ||
259 | kmem_cache_free(thread_info_cache, ti); | ||
260 | } | ||
261 | |||
262 | /* Weak symbol; called by init/main.c */ | ||
263 | |||
264 | void thread_info_cache_init(void) | ||
265 | { | ||
266 | thread_info_cache = kmem_cache_create("thread_info", THREAD_SIZE, | ||
267 | THREAD_SIZE, 0, NULL); | ||
268 | BUG_ON(thread_info_cache == NULL); | ||
269 | } | ||
270 | |||
271 | #endif /* THREAD_SHIFT < PAGE_SHIFT */ | ||
272 | |||
273 | /* | ||
274 | * Required placeholder. | ||
275 | */ | ||
276 | int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu) | ||
277 | { | ||
278 | return 0; | ||
279 | } | ||