Blame view

arch/xtensa/kernel/process.c 7.74 KB
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  /*
   * arch/xtensa/kernel/process.c
   *
   * Xtensa Processor version.
   *
   * This file is subject to the terms and conditions of the GNU General Public
   * License.  See the file "COPYING" in the main directory of this archive
   * for more details.
   *
   * Copyright (C) 2001 - 2005 Tensilica Inc.
   *
   * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
   * Chris Zankel <chris@zankel.net>
   * Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
   * Kevin Chea
   */
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
17
18
19
20
21
  #include <linux/errno.h>
  #include <linux/sched.h>
  #include <linux/kernel.h>
  #include <linux/mm.h>
  #include <linux/smp.h>
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
22
23
24
  #include <linux/stddef.h>
  #include <linux/unistd.h>
  #include <linux/ptrace.h>
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
25
26
27
28
29
30
  #include <linux/elf.h>
  #include <linux/init.h>
  #include <linux/prctl.h>
  #include <linux/init_task.h>
  #include <linux/module.h>
  #include <linux/mqueue.h>
73089cbfd   Chris Zankel   [XTENSA] Move pre...
31
  #include <linux/fs.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
32
  #include <linux/slab.h>
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
33
34
35
36
37
38
39
40
41
  
  #include <asm/pgtable.h>
  #include <asm/uaccess.h>
  #include <asm/system.h>
  #include <asm/io.h>
  #include <asm/processor.h>
  #include <asm/platform.h>
  #include <asm/mmu.h>
  #include <asm/irq.h>
60063497a   Arun Sharma   atomic: use <linu...
42
  #include <linux/atomic.h>
0013a8545   Sam Ravnborg   kbuild: m68k,pari...
43
  #include <asm/asm-offsets.h>
173d66813   Chris Zankel   [PATCH] xtensa: r...
44
  #include <asm/regs.h>
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
45
46
  
  extern void ret_from_fork(void);
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
47
  struct task_struct *current_set[NR_CPUS] = {&init_task, };
47f3fc94c   Adrian Bunk   [PATCH] add missi...
48
49
  void (*pm_power_off)(void) = NULL;
  EXPORT_SYMBOL(pm_power_off);
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
50

c658eac62   Chris Zankel   [XTENSA] Add supp...
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  #if XTENSA_HAVE_COPROCESSORS
  
  void coprocessor_release_all(struct thread_info *ti)
  {
  	unsigned long cpenable;
  	int i;
  
  	/* Make sure we don't switch tasks during this operation. */
  
  	preempt_disable();
  
  	/* Walk through all cp owners and release it for the requested one. */
  
  	cpenable = ti->cpenable;
  
  	for (i = 0; i < XCHAL_CP_MAX; i++) {
  		if (coprocessor_owner[i] == ti) {
  			coprocessor_owner[i] = 0;
  			cpenable &= ~(1 << i);
  		}
  	}
  
  	ti->cpenable = cpenable;
  	coprocessor_clear_cpenable();
  
  	preempt_enable();
  }
  
  void coprocessor_flush_all(struct thread_info *ti)
  {
  	unsigned long cpenable;
  	int i;
  
  	preempt_disable();
  
  	cpenable = ti->cpenable;
  
  	for (i = 0; i < XCHAL_CP_MAX; i++) {
  		if ((cpenable & 1) != 0 && coprocessor_owner[i] == ti)
  			coprocessor_flush(ti, i);
  		cpenable >>= 1;
  	}
  
  	preempt_enable();
  }
  
  #endif
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
98
99
100
101
102
103
104
105
106
107
108
109
  /*
   * Powermanagement idle function, if any is provided by the platform.
   */
  
  void cpu_idle(void)
  {
    	local_irq_enable();
  
  	/* endless idle loop with no priority at all */
  	while (1) {
  		while (!need_resched())
  			platform_idle();
5bfb5d690   Nick Piggin   [PATCH] sched: di...
110
  		preempt_enable_no_resched();
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
111
  		schedule();
5bfb5d690   Nick Piggin   [PATCH] sched: di...
112
  		preempt_disable();
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
113
114
115
116
  	}
  }
  
  /*
c658eac62   Chris Zankel   [XTENSA] Add supp...
117
   * This is called when the thread calls exit().
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
118
   */
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
119
120
  void exit_thread(void)
  {
c658eac62   Chris Zankel   [XTENSA] Add supp...
121
122
123
  #if XTENSA_HAVE_COPROCESSORS
  	coprocessor_release_all(current_thread_info());
  #endif
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
124
  }
c658eac62   Chris Zankel   [XTENSA] Add supp...
125
126
127
128
  /*
   * Flush thread state. This is called when a thread does an execve()
   * Note that we flush coprocessor registers for the case execve fails.
   */
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
129
130
  void flush_thread(void)
  {
c658eac62   Chris Zankel   [XTENSA] Add supp...
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
  #if XTENSA_HAVE_COPROCESSORS
  	struct thread_info *ti = current_thread_info();
  	coprocessor_flush_all(ti);
  	coprocessor_release_all(ti);
  #endif
  }
  
  /*
   * This is called before the thread is copied. 
   */
  void prepare_to_copy(struct task_struct *tsk)
  {
  #if XTENSA_HAVE_COPROCESSORS
  	coprocessor_flush_all(task_thread_info(tsk));
  #endif
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  }
  
  /*
   * Copy thread.
   *
   * The stack layout for the new thread looks like this:
   *
   *	+------------------------+ <- sp in childregs (= tos)
   *	|       childregs        |
   *	+------------------------+ <- thread.sp = sp in dummy-frame
   *	|      dummy-frame       |    (saved in dummy-frame spill-area)
   *	+------------------------+
   *
   * We create a dummy frame to return to ret_from_fork:
   *   a0 points to ret_from_fork (simulating a call4)
   *   sp points to itself (thread.sp)
   *   a2, a3 are unused.
   *
   * Note: This is a pristine frame, so we don't need any spill region on top of
   *       childregs.
   */
6f2c55b84   Alexey Dobriyan   Simplify copy_thr...
167
  int copy_thread(unsigned long clone_flags, unsigned long usp,
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
168
169
170
171
  		unsigned long unused,
                  struct task_struct * p, struct pt_regs * regs)
  {
  	struct pt_regs *childregs;
c658eac62   Chris Zankel   [XTENSA] Add supp...
172
  	struct thread_info *ti;
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
173
174
175
176
  	unsigned long tos;
  	int user_mode = user_mode(regs);
  
  	/* Set up new TSS. */
04fe6faf1   Al Viro   [PATCH] xtensa: t...
177
  	tos = (unsigned long)task_stack_page(p) + THREAD_SIZE;
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
  	if (user_mode)
  		childregs = (struct pt_regs*)(tos - PT_USER_SIZE);
  	else
  		childregs = (struct pt_regs*)tos - 1;
  
  	*childregs = *regs;
  
  	/* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */
  	*((int*)childregs - 3) = (unsigned long)childregs;
  	*((int*)childregs - 4) = 0;
  
  	childregs->areg[1] = tos;
  	childregs->areg[2] = 0;
  	p->set_child_tid = p->clear_child_tid = NULL;
  	p->thread.ra = MAKE_RA_FOR_CALL((unsigned long)ret_from_fork, 0x1);
  	p->thread.sp = (unsigned long)childregs;
c658eac62   Chris Zankel   [XTENSA] Add supp...
194

5a0015d62   Chris Zankel   [PATCH] xtensa: A...
195
196
197
198
199
200
  	if (user_mode(regs)) {
  
  		int len = childregs->wmask & ~0xf;
  		childregs->areg[1] = usp;
  		memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4],
  		       &regs->areg[XCHAL_NUM_AREGS - len/4], len);
c658eac62   Chris Zankel   [XTENSA] Add supp...
201
  // FIXME: we need to set THREADPTR in thread_info...
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
202
203
204
205
206
207
208
  		if (clone_flags & CLONE_SETTLS)
  			childregs->areg[2] = childregs->areg[6];
  
  	} else {
  		/* In kernel space, we start a new thread with a new stack. */
  		childregs->wmask = 1;
  	}
c658eac62   Chris Zankel   [XTENSA] Add supp...
209
210
211
212
213
  
  #if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
  	ti = task_thread_info(p);
  	ti->cpenable = 0;
  #endif
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
214
215
216
217
218
  	return 0;
  }
  
  
  /*
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
219
220
221
222
223
224
   * These bracket the sleeping functions..
   */
  
  unsigned long get_wchan(struct task_struct *p)
  {
  	unsigned long sp, pc;
04fe6faf1   Al Viro   [PATCH] xtensa: t...
225
  	unsigned long stack_page = (unsigned long) task_stack_page(p);
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
  	int count = 0;
  
  	if (!p || p == current || p->state == TASK_RUNNING)
  		return 0;
  
  	sp = p->thread.sp;
  	pc = MAKE_PC_FROM_RA(p->thread.ra, p->thread.sp);
  
  	do {
  		if (sp < stack_page + sizeof(struct task_struct) ||
  		    sp >= (stack_page + THREAD_SIZE) ||
  		    pc == 0)
  			return 0;
  		if (!in_sched_functions(pc))
  			return pc;
  
  		/* Stack layout: sp-4: ra, sp-3: sp' */
  
  		pc = MAKE_PC_FROM_RA(*(unsigned long*)sp - 4, sp);
  		sp = *(unsigned long *)sp - 3;
  	} while (count++ < 16);
  	return 0;
  }
  
  /*
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
251
252
253
254
255
256
257
   * xtensa_gregset_t and 'struct pt_regs' are vastly different formats
   * of processor registers.  Besides different ordering,
   * xtensa_gregset_t contains non-live register information that
   * 'struct pt_regs' does not.  Exception handling (primarily) uses
   * 'struct pt_regs'.  Core files and ptrace use xtensa_gregset_t.
   *
   */
c658eac62   Chris Zankel   [XTENSA] Add supp...
258
  void xtensa_elf_core_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs)
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
259
  {
c658eac62   Chris Zankel   [XTENSA] Add supp...
260
261
262
263
264
265
266
267
268
269
270
  	unsigned long wb, ws, wm;
  	int live, last;
  
  	wb = regs->windowbase;
  	ws = regs->windowstart;
  	wm = regs->wmask;
  	ws = ((ws >> wb) | (ws << (WSBITS - wb))) & ((1 << WSBITS) - 1);
  
  	/* Don't leak any random bits. */
  
  	memset(elfregs, 0, sizeof (elfregs));
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
271
272
273
274
275
  	/* Note:  PS.EXCM is not set while user task is running; its
  	 * being set in regs->ps is for exception handling convenience.
  	 */
  
  	elfregs->pc		= regs->pc;
173d66813   Chris Zankel   [PATCH] xtensa: r...
276
  	elfregs->ps		= (regs->ps & ~(1 << PS_EXCM_BIT));
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
277
278
279
280
  	elfregs->lbeg		= regs->lbeg;
  	elfregs->lend		= regs->lend;
  	elfregs->lcount		= regs->lcount;
  	elfregs->sar		= regs->sar;
c658eac62   Chris Zankel   [XTENSA] Add supp...
281
  	elfregs->windowstart	= ws;
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
282

c658eac62   Chris Zankel   [XTENSA] Add supp...
283
284
285
286
  	live = (wm & 2) ? 4 : (wm & 4) ? 8 : (wm & 8) ? 12 : 16;
  	last = XCHAL_NUM_AREGS - (wm >> 4) * 4;
  	memcpy(elfregs->a, regs->areg, live * 4);
  	memcpy(elfregs->a + last, regs->areg + last, (wm >> 4) * 16);
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
287
  }
c658eac62   Chris Zankel   [XTENSA] Add supp...
288
  int dump_fpu(void)
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
289
  {
5a0015d62   Chris Zankel   [PATCH] xtensa: A...
290
291
  	return 0;
  }
fc4fb2adf   Chris Zankel   [PATCH] xtensa: f...
292
293
294
295
296
297
298
299
300
301
302
303
304
  
  asmlinkage
  long xtensa_clone(unsigned long clone_flags, unsigned long newsp,
                    void __user *parent_tid, void *child_tls,
                    void __user *child_tid, long a5,
                    struct pt_regs *regs)
  {
          if (!newsp)
                  newsp = regs->areg[1];
          return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid);
  }
  
  /*
c658eac62   Chris Zankel   [XTENSA] Add supp...
305
306
   * xtensa_execve() executes a new program.
   */
fc4fb2adf   Chris Zankel   [PATCH] xtensa: f...
307
308
  
  asmlinkage
d7627467b   David Howells   Make do_execve() ...
309
310
311
  long xtensa_execve(const char __user *name,
  		   const char __user *const __user *argv,
                     const char __user *const __user *envp,
fc4fb2adf   Chris Zankel   [PATCH] xtensa: f...
312
313
314
315
316
317
318
319
320
321
                     long a3, long a4, long a5,
                     struct pt_regs *regs)
  {
  	long error;
  	char * filename;
  
  	filename = getname(name);
  	error = PTR_ERR(filename);
  	if (IS_ERR(filename))
  		goto out;
fc4fb2adf   Chris Zankel   [PATCH] xtensa: f...
322
  	error = do_execve(filename, argv, envp, regs);
fc4fb2adf   Chris Zankel   [PATCH] xtensa: f...
323
324
325
326
  	putname(filename);
  out:
  	return error;
  }