aboutsummaryrefslogtreecommitdiffstats
path: root/arch/xtensa/kernel/process.c
diff options
context:
space:
mode:
authorChris Zankel <chris@zankel.net>2008-02-12 16:17:07 -0500
committerChris Zankel <chris@zankel.net>2008-02-13 20:41:43 -0500
commitc658eac628aa8df040dfe614556d95e6da3a9ffb (patch)
treee2211e1d5c894c29e92d4c744f504b38410efe41 /arch/xtensa/kernel/process.c
parent71d28e6c285548106f551fde13ca6d589433d843 (diff)
[XTENSA] Add support for configurable registers and coprocessors
The Xtensa architecture allows to define custom instructions and registers. Registers that are bound to a coprocessor are only accessible if the corresponding enable bit is set, which allows to implement a 'lazy' context switch mechanism. Other registers needs to be saved and restore at the time of the context switch or during interrupt handling. This patch adds support for these additional states: - save and restore registers that are used by the compiler upon interrupt entry and exit. - context switch additional registers unbound to any coprocessor - 'lazy' context switch of registers bound to a coprocessor - ptrace interface to provide access to additional registers - update configuration files in include/asm-xtensa/variant-fsf Signed-off-by: Chris Zankel <chris@zankel.net>
Diffstat (limited to 'arch/xtensa/kernel/process.c')
-rw-r--r--arch/xtensa/kernel/process.c261
1 files changed, 102 insertions, 159 deletions
diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c
index 026138d641a4..9185597eb6a0 100644
--- a/arch/xtensa/kernel/process.c
+++ b/arch/xtensa/kernel/process.c
@@ -52,6 +52,55 @@ void (*pm_power_off)(void) = NULL;
52EXPORT_SYMBOL(pm_power_off); 52EXPORT_SYMBOL(pm_power_off);
53 53
54 54
55#if XTENSA_HAVE_COPROCESSORS
56
57void 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
83void 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
77void exit_thread(void) 125void 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 */
81void flush_thread(void) 136void 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 */
148void 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 &regs->areg[XCHAL_NUM_AREGS - len/4], len); 208 &regs->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,9 +265,20 @@ unsigned long get_wchan(struct task_struct *p)
191 * 265 *
192 */ 266 */
193 267
194void do_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs, 268void xtensa_elf_core_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs)
195 struct task_struct *tsk)
196{ 269{
270 unsigned long wb, ws, wm;
271 int live, last;
272
273 wb = regs->windowbase;
274 ws = regs->windowstart;
275 wm = regs->wmask;
276 ws = ((ws >> wb) | (ws << (WSBITS - wb))) & ((1 << WSBITS) - 1);
277
278 /* Don't leak any random bits. */
279
280 memset(elfregs, 0, sizeof (elfregs));
281
197 /* 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
198 * being set in regs->ps is for exception handling convenience. 283 * being set in regs->ps is for exception handling convenience.
199 */ 284 */
@@ -204,159 +289,18 @@ void do_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs,
204 elfregs->lend = regs->lend; 289 elfregs->lend = regs->lend;
205 elfregs->lcount = regs->lcount; 290 elfregs->lcount = regs->lcount;
206 elfregs->sar = regs->sar; 291 elfregs->sar = regs->sar;
292 elfregs->windowstart = ws;
207 293
208 memcpy (elfregs->a, regs->areg, sizeof(elfregs->a)); 294 live = (wm & 2) ? 4 : (wm & 4) ? 8 : (wm & 8) ? 12 : 16;
209} 295 last = XCHAL_NUM_AREGS - (wm >> 4) * 4;
210 296 memcpy(elfregs->a, regs->areg, live * 4);
211void xtensa_elf_core_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs) 297 memcpy(elfregs->a + last, regs->areg + last, (wm >> 4) * 16);
212{
213 do_copy_regs ((xtensa_gregset_t *)elfregs, regs, current);
214}
215
216
217/* The inverse of do_copy_regs(). No error or sanity checking. */
218
219void do_restore_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs,
220 struct task_struct *tsk)
221{
222 const unsigned long ps_mask = PS_CALLINC_MASK | PS_OWB_MASK;
223 unsigned long ps;
224
225 /* Note: PS.EXCM is not set while user task is running; it
226 * needs to be set in regs->ps is for exception handling convenience.
227 */
228
229 ps = (regs->ps & ~ps_mask) | (elfregs->ps & ps_mask) | (1<<PS_EXCM_BIT);
230 regs->ps = ps;
231 regs->pc = elfregs->pc;
232 regs->lbeg = elfregs->lbeg;
233 regs->lend = elfregs->lend;
234 regs->lcount = elfregs->lcount;
235 regs->sar = elfregs->sar;
236
237 memcpy (regs->areg, elfregs->a, sizeof(regs->areg));
238}
239
240/*
241 * do_save_fpregs() gathers information from 'struct pt_regs' and
242 * 'current->thread' to fill in the elf_fpregset_t structure.
243 *
244 * Core files and ptrace use elf_fpregset_t.
245 */
246
247void do_save_fpregs (elf_fpregset_t *fpregs, struct pt_regs *regs,
248 struct task_struct *tsk)
249{
250#if XCHAL_HAVE_CP
251
252 extern unsigned char _xtensa_reginfo_tables[];
253 extern unsigned _xtensa_reginfo_table_size;
254 int i;
255 unsigned long flags;
256
257 /* Before dumping coprocessor state from memory,
258 * ensure any live coprocessor contents for this
259 * task are first saved to memory:
260 */
261 local_irq_save(flags);
262
263 for (i = 0; i < XCHAL_CP_MAX; i++) {
264 if (tsk == coprocessor_info[i].owner) {
265 enable_coprocessor(i);
266 save_coprocessor_registers(
267 tsk->thread.cp_save+coprocessor_info[i].offset,i);
268 disable_coprocessor(i);
269 }
270 }
271
272 local_irq_restore(flags);
273
274 /* Now dump coprocessor & extra state: */
275 memcpy((unsigned char*)fpregs,
276 _xtensa_reginfo_tables, _xtensa_reginfo_table_size);
277 memcpy((unsigned char*)fpregs + _xtensa_reginfo_table_size,
278 tsk->thread.cp_save, XTENSA_CP_EXTRA_SIZE);
279#endif
280} 298}
281 299
282/* 300int dump_fpu(void)
283 * The inverse of do_save_fpregs().
284 * Copies coprocessor and extra state from fpregs into regs and tsk->thread.
285 * Returns 0 on success, non-zero if layout doesn't match.
286 */
287
288int do_restore_fpregs (elf_fpregset_t *fpregs, struct pt_regs *regs,
289 struct task_struct *tsk)
290{ 301{
291#if XCHAL_HAVE_CP
292
293 extern unsigned char _xtensa_reginfo_tables[];
294 extern unsigned _xtensa_reginfo_table_size;
295 int i;
296 unsigned long flags;
297
298 /* Make sure save area layouts match.
299 * FIXME: in the future we could allow restoring from
300 * a different layout of the same registers, by comparing
301 * fpregs' table with _xtensa_reginfo_tables and matching
302 * entries and copying registers one at a time.
303 * Not too sure yet whether that's very useful.
304 */
305
306 if( memcmp((unsigned char*)fpregs,
307 _xtensa_reginfo_tables, _xtensa_reginfo_table_size) ) {
308 return -1;
309 }
310
311 /* Before restoring coprocessor state from memory,
312 * ensure any live coprocessor contents for this
313 * task are first invalidated.
314 */
315
316 local_irq_save(flags);
317
318 for (i = 0; i < XCHAL_CP_MAX; i++) {
319 if (tsk == coprocessor_info[i].owner) {
320 enable_coprocessor(i);
321 save_coprocessor_registers(
322 tsk->thread.cp_save+coprocessor_info[i].offset,i);
323 coprocessor_info[i].owner = 0;
324 disable_coprocessor(i);
325 }
326 }
327
328 local_irq_restore(flags);
329
330 /* Now restore coprocessor & extra state: */
331
332 memcpy(tsk->thread.cp_save,
333 (unsigned char*)fpregs + _xtensa_reginfo_table_size,
334 XTENSA_CP_EXTRA_SIZE);
335#endif
336 return 0; 302 return 0;
337} 303}
338/*
339 * Fill in the CP structure for a core dump for a particular task.
340 */
341
342int
343dump_task_fpu(struct pt_regs *regs, struct task_struct *task, elf_fpregset_t *r)
344{
345 return 0; /* no coprocessors active on this processor */
346}
347
348/*
349 * Fill in the CP structure for a core dump.
350 * This includes any FPU coprocessor.
351 * Here, we dump all coprocessors, and other ("extra") custom state.
352 *
353 * This function is called by elf_core_dump() in fs/binfmt_elf.c
354 * (in which case 'regs' comes from calls to do_coredump, see signals.c).
355 */
356int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r)
357{
358 return dump_task_fpu(regs, current, r);
359}
360 304
361asmlinkage 305asmlinkage
362long xtensa_clone(unsigned long clone_flags, unsigned long newsp, 306long xtensa_clone(unsigned long clone_flags, unsigned long newsp,
@@ -370,8 +314,8 @@ long xtensa_clone(unsigned long clone_flags, unsigned long newsp,
370} 314}
371 315
372/* 316/*
373 * * xtensa_execve() executes a new program. 317 * xtensa_execve() executes a new program.
374 * */ 318 */
375 319
376asmlinkage 320asmlinkage
377long xtensa_execve(char __user *name, char __user * __user *argv, 321long xtensa_execve(char __user *name, char __user * __user *argv,
@@ -386,7 +330,6 @@ long xtensa_execve(char __user *name, char __user * __user *argv,
386 error = PTR_ERR(filename); 330 error = PTR_ERR(filename);
387 if (IS_ERR(filename)) 331 if (IS_ERR(filename))
388 goto out; 332 goto out;
389 // FIXME: release coprocessor??
390 error = do_execve(filename, argv, envp, regs); 333 error = do_execve(filename, argv, envp, regs);
391 if (error == 0) { 334 if (error == 0) {
392 task_lock(current); 335 task_lock(current);