diff options
Diffstat (limited to 'arch/xtensa/kernel/process.c')
-rw-r--r-- | arch/xtensa/kernel/process.c | 306 |
1 files changed, 99 insertions, 207 deletions
diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c index f53d7bd9dfb2..9185597eb6a0 100644 --- a/arch/xtensa/kernel/process.c +++ b/arch/xtensa/kernel/process.c | |||
@@ -52,6 +52,55 @@ void (*pm_power_off)(void) = NULL; | |||
52 | EXPORT_SYMBOL(pm_power_off); | 52 | EXPORT_SYMBOL(pm_power_off); |
53 | 53 | ||
54 | 54 | ||
55 | #if XTENSA_HAVE_COPROCESSORS | ||
56 | |||
57 | void coprocessor_release_all(struct thread_info *ti) | ||
58 | { | ||
59 | unsigned long cpenable; | ||
60 | int i; | ||
61 | |||
62 | /* Make sure we don't switch tasks during this operation. */ | ||
63 | |||
64 | preempt_disable(); | ||
65 | |||
66 | /* Walk through all cp owners and release it for the requested one. */ | ||
67 | |||
68 | cpenable = ti->cpenable; | ||
69 | |||
70 | for (i = 0; i < XCHAL_CP_MAX; i++) { | ||
71 | if (coprocessor_owner[i] == ti) { | ||
72 | coprocessor_owner[i] = 0; | ||
73 | cpenable &= ~(1 << i); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | ti->cpenable = cpenable; | ||
78 | coprocessor_clear_cpenable(); | ||
79 | |||
80 | preempt_enable(); | ||
81 | } | ||
82 | |||
83 | void coprocessor_flush_all(struct thread_info *ti) | ||
84 | { | ||
85 | unsigned long cpenable; | ||
86 | int i; | ||
87 | |||
88 | preempt_disable(); | ||
89 | |||
90 | cpenable = ti->cpenable; | ||
91 | |||
92 | for (i = 0; i < XCHAL_CP_MAX; i++) { | ||
93 | if ((cpenable & 1) != 0 && coprocessor_owner[i] == ti) | ||
94 | coprocessor_flush(ti, i); | ||
95 | cpenable >>= 1; | ||
96 | } | ||
97 | |||
98 | preempt_enable(); | ||
99 | } | ||
100 | |||
101 | #endif | ||
102 | |||
103 | |||
55 | /* | 104 | /* |
56 | * Powermanagement idle function, if any is provided by the platform. | 105 | * Powermanagement idle function, if any is provided by the platform. |
57 | */ | 106 | */ |
@@ -71,15 +120,36 @@ void cpu_idle(void) | |||
71 | } | 120 | } |
72 | 121 | ||
73 | /* | 122 | /* |
74 | * Free current thread data structures etc.. | 123 | * This is called when the thread calls exit(). |
75 | */ | 124 | */ |
76 | |||
77 | void exit_thread(void) | 125 | void exit_thread(void) |
78 | { | 126 | { |
127 | #if XTENSA_HAVE_COPROCESSORS | ||
128 | coprocessor_release_all(current_thread_info()); | ||
129 | #endif | ||
79 | } | 130 | } |
80 | 131 | ||
132 | /* | ||
133 | * Flush thread state. This is called when a thread does an execve() | ||
134 | * Note that we flush coprocessor registers for the case execve fails. | ||
135 | */ | ||
81 | void flush_thread(void) | 136 | void flush_thread(void) |
82 | { | 137 | { |
138 | #if XTENSA_HAVE_COPROCESSORS | ||
139 | struct thread_info *ti = current_thread_info(); | ||
140 | coprocessor_flush_all(ti); | ||
141 | coprocessor_release_all(ti); | ||
142 | #endif | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * This is called before the thread is copied. | ||
147 | */ | ||
148 | void prepare_to_copy(struct task_struct *tsk) | ||
149 | { | ||
150 | #if XTENSA_HAVE_COPROCESSORS | ||
151 | coprocessor_flush_all(task_thread_info(tsk)); | ||
152 | #endif | ||
83 | } | 153 | } |
84 | 154 | ||
85 | /* | 155 | /* |
@@ -107,6 +177,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, | |||
107 | struct task_struct * p, struct pt_regs * regs) | 177 | struct task_struct * p, struct pt_regs * regs) |
108 | { | 178 | { |
109 | struct pt_regs *childregs; | 179 | struct pt_regs *childregs; |
180 | struct thread_info *ti; | ||
110 | unsigned long tos; | 181 | unsigned long tos; |
111 | int user_mode = user_mode(regs); | 182 | int user_mode = user_mode(regs); |
112 | 183 | ||
@@ -128,13 +199,14 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, | |||
128 | p->set_child_tid = p->clear_child_tid = NULL; | 199 | p->set_child_tid = p->clear_child_tid = NULL; |
129 | p->thread.ra = MAKE_RA_FOR_CALL((unsigned long)ret_from_fork, 0x1); | 200 | p->thread.ra = MAKE_RA_FOR_CALL((unsigned long)ret_from_fork, 0x1); |
130 | p->thread.sp = (unsigned long)childregs; | 201 | p->thread.sp = (unsigned long)childregs; |
202 | |||
131 | if (user_mode(regs)) { | 203 | if (user_mode(regs)) { |
132 | 204 | ||
133 | int len = childregs->wmask & ~0xf; | 205 | int len = childregs->wmask & ~0xf; |
134 | childregs->areg[1] = usp; | 206 | childregs->areg[1] = usp; |
135 | memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4], | 207 | memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4], |
136 | ®s->areg[XCHAL_NUM_AREGS - len/4], len); | 208 | ®s->areg[XCHAL_NUM_AREGS - len/4], len); |
137 | 209 | // FIXME: we need to set THREADPTR in thread_info... | |
138 | if (clone_flags & CLONE_SETTLS) | 210 | if (clone_flags & CLONE_SETTLS) |
139 | childregs->areg[2] = childregs->areg[6]; | 211 | childregs->areg[2] = childregs->areg[6]; |
140 | 212 | ||
@@ -142,6 +214,12 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, | |||
142 | /* In kernel space, we start a new thread with a new stack. */ | 214 | /* In kernel space, we start a new thread with a new stack. */ |
143 | childregs->wmask = 1; | 215 | childregs->wmask = 1; |
144 | } | 216 | } |
217 | |||
218 | #if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS) | ||
219 | ti = task_thread_info(p); | ||
220 | ti->cpenable = 0; | ||
221 | #endif | ||
222 | |||
145 | return 0; | 223 | return 0; |
146 | } | 224 | } |
147 | 225 | ||
@@ -179,10 +257,6 @@ unsigned long get_wchan(struct task_struct *p) | |||
179 | } | 257 | } |
180 | 258 | ||
181 | /* | 259 | /* |
182 | * do_copy_regs() gathers information from 'struct pt_regs' and | ||
183 | * 'current->thread.areg[]' to fill in the xtensa_gregset_t | ||
184 | * structure. | ||
185 | * | ||
186 | * xtensa_gregset_t and 'struct pt_regs' are vastly different formats | 260 | * xtensa_gregset_t and 'struct pt_regs' are vastly different formats |
187 | * of processor registers. Besides different ordering, | 261 | * of processor registers. Besides different ordering, |
188 | * xtensa_gregset_t contains non-live register information that | 262 | * xtensa_gregset_t contains non-live register information that |
@@ -191,18 +265,19 @@ unsigned long get_wchan(struct task_struct *p) | |||
191 | * | 265 | * |
192 | */ | 266 | */ |
193 | 267 | ||
194 | void do_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs, | 268 | void xtensa_elf_core_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs) |
195 | struct task_struct *tsk) | ||
196 | { | 269 | { |
197 | int i, n, wb_offset; | 270 | unsigned long wb, ws, wm; |
271 | int live, last; | ||
198 | 272 | ||
199 | elfregs->xchal_config_id0 = XCHAL_HW_CONFIGID0; | 273 | wb = regs->windowbase; |
200 | elfregs->xchal_config_id1 = XCHAL_HW_CONFIGID1; | 274 | ws = regs->windowstart; |
275 | wm = regs->wmask; | ||
276 | ws = ((ws >> wb) | (ws << (WSBITS - wb))) & ((1 << WSBITS) - 1); | ||
201 | 277 | ||
202 | __asm__ __volatile__ ("rsr %0, 176\n" : "=a" (i)); | 278 | /* Don't leak any random bits. */ |
203 | elfregs->cpux = i; | 279 | |
204 | __asm__ __volatile__ ("rsr %0, 208\n" : "=a" (i)); | 280 | memset(elfregs, 0, sizeof (elfregs)); |
205 | elfregs->cpuy = i; | ||
206 | 281 | ||
207 | /* Note: PS.EXCM is not set while user task is running; its | 282 | /* Note: PS.EXCM is not set while user task is running; its |
208 | * being set in regs->ps is for exception handling convenience. | 283 | * being set in regs->ps is for exception handling convenience. |
@@ -210,204 +285,22 @@ void do_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs, | |||
210 | 285 | ||
211 | elfregs->pc = regs->pc; | 286 | elfregs->pc = regs->pc; |
212 | elfregs->ps = (regs->ps & ~(1 << PS_EXCM_BIT)); | 287 | elfregs->ps = (regs->ps & ~(1 << PS_EXCM_BIT)); |
213 | elfregs->exccause = regs->exccause; | ||
214 | elfregs->excvaddr = regs->excvaddr; | ||
215 | elfregs->windowbase = regs->windowbase; | ||
216 | elfregs->windowstart = regs->windowstart; | ||
217 | elfregs->lbeg = regs->lbeg; | 288 | elfregs->lbeg = regs->lbeg; |
218 | elfregs->lend = regs->lend; | 289 | elfregs->lend = regs->lend; |
219 | elfregs->lcount = regs->lcount; | 290 | elfregs->lcount = regs->lcount; |
220 | elfregs->sar = regs->sar; | 291 | elfregs->sar = regs->sar; |
221 | elfregs->syscall = regs->syscall; | 292 | elfregs->windowstart = ws; |
222 | |||
223 | /* Copy register file. | ||
224 | * The layout looks like this: | ||
225 | * | ||
226 | * | a0 ... a15 | Z ... Z | arX ... arY | | ||
227 | * current window unused saved frames | ||
228 | */ | ||
229 | |||
230 | memset (elfregs->ar, 0, sizeof(elfregs->ar)); | ||
231 | 293 | ||
232 | wb_offset = regs->windowbase * 4; | 294 | live = (wm & 2) ? 4 : (wm & 4) ? 8 : (wm & 8) ? 12 : 16; |
233 | n = (regs->wmask&1)? 4 : (regs->wmask&2)? 8 : (regs->wmask&4)? 12 : 16; | 295 | last = XCHAL_NUM_AREGS - (wm >> 4) * 4; |
234 | 296 | memcpy(elfregs->a, regs->areg, live * 4); | |
235 | for (i = 0; i < n; i++) | 297 | memcpy(elfregs->a + last, regs->areg + last, (wm >> 4) * 16); |
236 | elfregs->ar[(wb_offset + i) % XCHAL_NUM_AREGS] = regs->areg[i]; | ||
237 | |||
238 | n = (regs->wmask >> 4) * 4; | ||
239 | |||
240 | for (i = XCHAL_NUM_AREGS - n; n > 0; i++, n--) | ||
241 | elfregs->ar[(wb_offset + i) % XCHAL_NUM_AREGS] = regs->areg[i]; | ||
242 | } | 298 | } |
243 | 299 | ||
244 | void xtensa_elf_core_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs) | 300 | int dump_fpu(void) |
245 | { | 301 | { |
246 | do_copy_regs ((xtensa_gregset_t *)elfregs, regs, current); | ||
247 | } | ||
248 | |||
249 | |||
250 | /* The inverse of do_copy_regs(). No error or sanity checking. */ | ||
251 | |||
252 | void do_restore_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs, | ||
253 | struct task_struct *tsk) | ||
254 | { | ||
255 | int i, n, wb_offset; | ||
256 | |||
257 | /* Note: PS.EXCM is not set while user task is running; it | ||
258 | * needs to be set in regs->ps is for exception handling convenience. | ||
259 | */ | ||
260 | |||
261 | regs->pc = elfregs->pc; | ||
262 | regs->ps = (elfregs->ps | (1 << PS_EXCM_BIT)); | ||
263 | regs->exccause = elfregs->exccause; | ||
264 | regs->excvaddr = elfregs->excvaddr; | ||
265 | regs->windowbase = elfregs->windowbase; | ||
266 | regs->windowstart = elfregs->windowstart; | ||
267 | regs->lbeg = elfregs->lbeg; | ||
268 | regs->lend = elfregs->lend; | ||
269 | regs->lcount = elfregs->lcount; | ||
270 | regs->sar = elfregs->sar; | ||
271 | regs->syscall = elfregs->syscall; | ||
272 | |||
273 | /* Clear everything. */ | ||
274 | |||
275 | memset (regs->areg, 0, sizeof(regs->areg)); | ||
276 | |||
277 | /* Copy regs from live window frame. */ | ||
278 | |||
279 | wb_offset = regs->windowbase * 4; | ||
280 | n = (regs->wmask&1)? 4 : (regs->wmask&2)? 8 : (regs->wmask&4)? 12 : 16; | ||
281 | |||
282 | for (i = 0; i < n; i++) | ||
283 | regs->areg[(wb_offset+i) % XCHAL_NUM_AREGS] = elfregs->ar[i]; | ||
284 | |||
285 | n = (regs->wmask >> 4) * 4; | ||
286 | |||
287 | for (i = XCHAL_NUM_AREGS - n; n > 0; i++, n--) | ||
288 | regs->areg[(wb_offset+i) % XCHAL_NUM_AREGS] = elfregs->ar[i]; | ||
289 | } | ||
290 | |||
291 | /* | ||
292 | * do_save_fpregs() gathers information from 'struct pt_regs' and | ||
293 | * 'current->thread' to fill in the elf_fpregset_t structure. | ||
294 | * | ||
295 | * Core files and ptrace use elf_fpregset_t. | ||
296 | */ | ||
297 | |||
298 | void do_save_fpregs (elf_fpregset_t *fpregs, struct pt_regs *regs, | ||
299 | struct task_struct *tsk) | ||
300 | { | ||
301 | #if XCHAL_HAVE_CP | ||
302 | |||
303 | extern unsigned char _xtensa_reginfo_tables[]; | ||
304 | extern unsigned _xtensa_reginfo_table_size; | ||
305 | int i; | ||
306 | unsigned long flags; | ||
307 | |||
308 | /* Before dumping coprocessor state from memory, | ||
309 | * ensure any live coprocessor contents for this | ||
310 | * task are first saved to memory: | ||
311 | */ | ||
312 | local_irq_save(flags); | ||
313 | |||
314 | for (i = 0; i < XCHAL_CP_MAX; i++) { | ||
315 | if (tsk == coprocessor_info[i].owner) { | ||
316 | enable_coprocessor(i); | ||
317 | save_coprocessor_registers( | ||
318 | tsk->thread.cp_save+coprocessor_info[i].offset,i); | ||
319 | disable_coprocessor(i); | ||
320 | } | ||
321 | } | ||
322 | |||
323 | local_irq_restore(flags); | ||
324 | |||
325 | /* Now dump coprocessor & extra state: */ | ||
326 | memcpy((unsigned char*)fpregs, | ||
327 | _xtensa_reginfo_tables, _xtensa_reginfo_table_size); | ||
328 | memcpy((unsigned char*)fpregs + _xtensa_reginfo_table_size, | ||
329 | tsk->thread.cp_save, XTENSA_CP_EXTRA_SIZE); | ||
330 | #endif | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * The inverse of do_save_fpregs(). | ||
335 | * Copies coprocessor and extra state from fpregs into regs and tsk->thread. | ||
336 | * Returns 0 on success, non-zero if layout doesn't match. | ||
337 | */ | ||
338 | |||
339 | int do_restore_fpregs (elf_fpregset_t *fpregs, struct pt_regs *regs, | ||
340 | struct task_struct *tsk) | ||
341 | { | ||
342 | #if XCHAL_HAVE_CP | ||
343 | |||
344 | extern unsigned char _xtensa_reginfo_tables[]; | ||
345 | extern unsigned _xtensa_reginfo_table_size; | ||
346 | int i; | ||
347 | unsigned long flags; | ||
348 | |||
349 | /* Make sure save area layouts match. | ||
350 | * FIXME: in the future we could allow restoring from | ||
351 | * a different layout of the same registers, by comparing | ||
352 | * fpregs' table with _xtensa_reginfo_tables and matching | ||
353 | * entries and copying registers one at a time. | ||
354 | * Not too sure yet whether that's very useful. | ||
355 | */ | ||
356 | |||
357 | if( memcmp((unsigned char*)fpregs, | ||
358 | _xtensa_reginfo_tables, _xtensa_reginfo_table_size) ) { | ||
359 | return -1; | ||
360 | } | ||
361 | |||
362 | /* Before restoring coprocessor state from memory, | ||
363 | * ensure any live coprocessor contents for this | ||
364 | * task are first invalidated. | ||
365 | */ | ||
366 | |||
367 | local_irq_save(flags); | ||
368 | |||
369 | for (i = 0; i < XCHAL_CP_MAX; i++) { | ||
370 | if (tsk == coprocessor_info[i].owner) { | ||
371 | enable_coprocessor(i); | ||
372 | save_coprocessor_registers( | ||
373 | tsk->thread.cp_save+coprocessor_info[i].offset,i); | ||
374 | coprocessor_info[i].owner = 0; | ||
375 | disable_coprocessor(i); | ||
376 | } | ||
377 | } | ||
378 | |||
379 | local_irq_restore(flags); | ||
380 | |||
381 | /* Now restore coprocessor & extra state: */ | ||
382 | |||
383 | memcpy(tsk->thread.cp_save, | ||
384 | (unsigned char*)fpregs + _xtensa_reginfo_table_size, | ||
385 | XTENSA_CP_EXTRA_SIZE); | ||
386 | #endif | ||
387 | return 0; | 302 | return 0; |
388 | } | 303 | } |
389 | /* | ||
390 | * Fill in the CP structure for a core dump for a particular task. | ||
391 | */ | ||
392 | |||
393 | int | ||
394 | dump_task_fpu(struct pt_regs *regs, struct task_struct *task, elf_fpregset_t *r) | ||
395 | { | ||
396 | return 0; /* no coprocessors active on this processor */ | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | * Fill in the CP structure for a core dump. | ||
401 | * This includes any FPU coprocessor. | ||
402 | * Here, we dump all coprocessors, and other ("extra") custom state. | ||
403 | * | ||
404 | * This function is called by elf_core_dump() in fs/binfmt_elf.c | ||
405 | * (in which case 'regs' comes from calls to do_coredump, see signals.c). | ||
406 | */ | ||
407 | int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r) | ||
408 | { | ||
409 | return dump_task_fpu(regs, current, r); | ||
410 | } | ||
411 | 304 | ||
412 | asmlinkage | 305 | asmlinkage |
413 | long xtensa_clone(unsigned long clone_flags, unsigned long newsp, | 306 | long xtensa_clone(unsigned long clone_flags, unsigned long newsp, |
@@ -421,8 +314,8 @@ long xtensa_clone(unsigned long clone_flags, unsigned long newsp, | |||
421 | } | 314 | } |
422 | 315 | ||
423 | /* | 316 | /* |
424 | * * xtensa_execve() executes a new program. | 317 | * xtensa_execve() executes a new program. |
425 | * */ | 318 | */ |
426 | 319 | ||
427 | asmlinkage | 320 | asmlinkage |
428 | long xtensa_execve(char __user *name, char __user * __user *argv, | 321 | long xtensa_execve(char __user *name, char __user * __user *argv, |
@@ -437,7 +330,6 @@ long xtensa_execve(char __user *name, char __user * __user *argv, | |||
437 | error = PTR_ERR(filename); | 330 | error = PTR_ERR(filename); |
438 | if (IS_ERR(filename)) | 331 | if (IS_ERR(filename)) |
439 | goto out; | 332 | goto out; |
440 | // FIXME: release coprocessor?? | ||
441 | error = do_execve(filename, argv, envp, regs); | 333 | error = do_execve(filename, argv, envp, regs); |
442 | if (error == 0) { | 334 | if (error == 0) { |
443 | task_lock(current); | 335 | task_lock(current); |