Blame view

arch/s390/kernel/process.c 10.1 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  /*
   *  arch/s390/kernel/process.c
   *
   *  S390 version
   *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
   *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
   *               Hartmut Penner (hp@de.ibm.com),
   *               Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
   *
   *  Derived from "arch/i386/kernel/process.c"
   *    Copyright (C) 1995, Linus Torvalds
   */
  
  /*
   * This file handles the architecture-dependent parts of process handling..
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  #include <linux/compiler.h>
  #include <linux/cpu.h>
  #include <linux/errno.h>
  #include <linux/sched.h>
  #include <linux/kernel.h>
  #include <linux/mm.h>
  #include <linux/smp.h>
  #include <linux/smp_lock.h>
  #include <linux/stddef.h>
  #include <linux/unistd.h>
  #include <linux/ptrace.h>
  #include <linux/slab.h>
  #include <linux/vmalloc.h>
  #include <linux/user.h>
  #include <linux/a.out.h>
  #include <linux/interrupt.h>
  #include <linux/delay.h>
  #include <linux/reboot.h>
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/notifier.h>
  
  #include <asm/uaccess.h>
  #include <asm/pgtable.h>
  #include <asm/system.h>
  #include <asm/io.h>
  #include <asm/processor.h>
  #include <asm/irq.h>
  #include <asm/timer.h>
94c12cc7d   Martin Schwidefsky   [S390] Inline ass...
46
  asmlinkage void ret_from_fork(void) asm ("ret_from_fork");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
50
51
52
53
54
55
56
57
  
  /*
   * Return saved PC of a blocked thread. used in kernel/sched.
   * resume in entry.S does not create a new stack frame, it
   * just stores the registers %r6-%r15 to the frame given by
   * schedule. We want to return the address of the caller of
   * schedule, so we have to walk the backchain one time to
   * find the frame schedule() store its return address.
   */
  unsigned long thread_saved_pc(struct task_struct *tsk)
  {
eb33c190c   Heiko Carstens   [PATCH] s390: sho...
58
  	struct stack_frame *sf, *low, *high;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59

eb33c190c   Heiko Carstens   [PATCH] s390: sho...
60
61
62
63
64
65
66
67
68
69
  	if (!tsk || !task_stack_page(tsk))
  		return 0;
  	low = task_stack_page(tsk);
  	high = (struct stack_frame *) task_pt_regs(tsk);
  	sf = (struct stack_frame *) (tsk->thread.ksp & PSW_ADDR_INSN);
  	if (sf <= low || sf > high)
  		return 0;
  	sf = (struct stack_frame *) (sf->back_chain & PSW_ADDR_INSN);
  	if (sf <= low || sf > high)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
73
74
75
  	return sf->gprs[8];
  }
  
  /*
   * Need to know about CPUs going idle?
   */
e041c6834   Alan Stern   [PATCH] Notifier ...
76
  static ATOMIC_NOTIFIER_HEAD(idle_chain);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
  
  int register_idle_notifier(struct notifier_block *nb)
  {
e041c6834   Alan Stern   [PATCH] Notifier ...
80
  	return atomic_notifier_chain_register(&idle_chain, nb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
83
84
85
  }
  EXPORT_SYMBOL(register_idle_notifier);
  
  int unregister_idle_notifier(struct notifier_block *nb)
  {
e041c6834   Alan Stern   [PATCH] Notifier ...
86
  	return atomic_notifier_chain_unregister(&idle_chain, nb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
88
89
90
91
92
93
  }
  EXPORT_SYMBOL(unregister_idle_notifier);
  
  void do_monitor_call(struct pt_regs *regs, long interruption_code)
  {
  	/* disable monitor call class 0 */
  	__ctl_clear_bit(8, 15);
e041c6834   Alan Stern   [PATCH] Notifier ...
94
  	atomic_notifier_call_chain(&idle_chain, CPU_NOT_IDLE,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
  			    (void *)(long) smp_processor_id());
  }
77fa22450   Heiko Carstens   [PATCH] s390: imp...
97
  extern void s390_handle_mcck(void);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
100
  /*
   * The idle loop on a S390...
   */
cdb045278   Adrian Bunk   [PATCH] kill incl...
101
  static void default_idle(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
  	int cpu, rc;
64c7c8f88   Nick Piggin   [PATCH] sched: re...
104
105
  	/* CPU is going idle. */
  	cpu = smp_processor_id();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  	local_irq_disable();
64c7c8f88   Nick Piggin   [PATCH] sched: re...
107
  	if (need_resched()) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
  		local_irq_enable();
64c7c8f88   Nick Piggin   [PATCH] sched: re...
109
110
  		return;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111

e041c6834   Alan Stern   [PATCH] Notifier ...
112
113
  	rc = atomic_notifier_call_chain(&idle_chain,
  			CPU_IDLE, (void *)(long) cpu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
115
116
117
118
119
120
121
122
123
124
  	if (rc != NOTIFY_OK && rc != NOTIFY_DONE)
  		BUG();
  	if (rc != NOTIFY_OK) {
  		local_irq_enable();
  		return;
  	}
  
  	/* enable monitor call class 0 */
  	__ctl_set_bit(8, 15);
  
  #ifdef CONFIG_HOTPLUG_CPU
1fca251f3   Heiko Carstens   [PATCH] s390: fix...
125
126
  	if (cpu_is_offline(cpu)) {
  		preempt_enable_no_resched();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
  		cpu_die();
1fca251f3   Heiko Carstens   [PATCH] s390: fix...
128
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
  #endif
77fa22450   Heiko Carstens   [PATCH] s390: imp...
130
131
132
133
134
135
136
  	local_mcck_disable();
  	if (test_thread_flag(TIF_MCCK_PENDING)) {
  		local_mcck_enable();
  		local_irq_enable();
  		s390_handle_mcck();
  		return;
  	}
1f194a4c3   Heiko Carstens   [PATCH] lockdep: ...
137
  	trace_hardirqs_on();
77fa22450   Heiko Carstens   [PATCH] s390: imp...
138
  	/* Wait for external, I/O or machine check interrupt. */
c1821c2e9   Gerald Schaefer   [S390] noexec pro...
139
  	__load_psw_mask(psw_kernel_bits | PSW_MASK_WAIT |
77fa22450   Heiko Carstens   [PATCH] s390: imp...
140
  			PSW_MASK_IO | PSW_MASK_EXT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
144
  }
  
  void cpu_idle(void)
  {
5bfb5d690   Nick Piggin   [PATCH] sched: di...
145
146
147
148
149
150
151
152
  	for (;;) {
  		while (!need_resched())
  			default_idle();
  
  		preempt_enable_no_resched();
  		schedule();
  		preempt_disable();
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
  }
  
  void show_regs(struct pt_regs *regs)
  {
  	struct task_struct *tsk = current;
30af7120f   Al Viro   [PATCH] s390: tas...
158
159
          printk("CPU:    %d    %s
  ", task_thread_info(tsk)->cpu, print_tainted());
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
162
163
164
165
166
167
          printk("Process %s (pid: %d, task: %p, ksp: %p)
  ",
  	       current->comm, current->pid, (void *) tsk,
  	       (void *) tsk->thread.ksp);
  
  	show_registers(regs);
  	/* Show stack backtrace if pt_regs is from kernel mode */
  	if (!(regs->psw.mask & PSW_MASK_PSTATE))
d2c993d84   Heiko Carstens   [S390] Fix sparse...
168
  		show_trace(NULL, (unsigned long *) regs->gprs[15]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
170
171
  }
  
  extern void kernel_thread_starter(void);
94c12cc7d   Martin Schwidefsky   [S390] Inline ass...
172
173
174
  asm(
  	".align 4
  "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
  	"kernel_thread_starter:
  "
  	"    la    2,0(10)
  "
  	"    basr  14,9
  "
  	"    la    2,0
  "
  	"    br    11
  ");
  
  int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
  {
  	struct pt_regs regs;
  
  	memset(&regs, 0, sizeof(regs));
c1821c2e9   Gerald Schaefer   [S390] noexec pro...
191
  	regs.psw.mask = psw_kernel_bits | PSW_MASK_IO | PSW_MASK_EXT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
  	regs.psw.addr = (unsigned long) kernel_thread_starter | PSW_ADDR_AMODE;
  	regs.gprs[9] = (unsigned long) fn;
  	regs.gprs[10] = (unsigned long) arg;
  	regs.gprs[11] = (unsigned long) do_exit;
  	regs.orig_gpr2 = -1;
  
  	/* Ok, create the new process.. */
  	return do_fork(flags | CLONE_VM | CLONE_UNTRACED,
  		       0, &regs, 0, NULL, NULL);
  }
  
  /*
   * Free current thread data structures etc..
   */
  void exit_thread(void)
  {
  }
  
  void flush_thread(void)
  {
  	clear_used_math();
  	clear_tsk_thread_flag(current, TIF_USEDFPU);
  }
  
  void release_thread(struct task_struct *dead_task)
  {
  }
  
  int copy_thread(int nr, unsigned long clone_flags, unsigned long new_stackp,
  	unsigned long unused,
          struct task_struct * p, struct pt_regs * regs)
  {
          struct fake_frame
            {
  	    struct stack_frame sf;
              struct pt_regs childregs;
            } *frame;
c7584fb6b   Al Viro   [PATCH] s390: tas...
229
          frame = container_of(task_pt_regs(p), struct fake_frame, childregs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
          p->thread.ksp = (unsigned long) frame;
  	/* Store access registers to kernel stack of new process. */
          frame->childregs = *regs;
  	frame->childregs.gprs[2] = 0;	/* child returns 0 on fork. */
          frame->childregs.gprs[15] = new_stackp;
          frame->sf.back_chain = 0;
  
          /* new return point is ret_from_fork */
          frame->sf.gprs[8] = (unsigned long) ret_from_fork;
  
          /* fake return stack for resume(), don't go back to schedule */
          frame->sf.gprs[9] = (unsigned long) frame;
  
  	/* Save access registers to new thread structure. */
  	save_access_regs(&p->thread.acrs[0]);
347a8dc3b   Martin Schwidefsky   [PATCH] s390: cle...
245
  #ifndef CONFIG_64BIT
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
248
249
250
251
252
253
254
255
256
          /*
  	 * save fprs to current->thread.fp_regs to merge them with
  	 * the emulated registers and then copy the result to the child.
  	 */
  	save_fp_regs(&current->thread.fp_regs);
  	memcpy(&p->thread.fp_regs, &current->thread.fp_regs,
  	       sizeof(s390_fp_regs));
          p->thread.user_seg = __pa((unsigned long) p->mm->pgd) | _SEGMENT_TABLE;
  	/* Set a new TLS ?  */
  	if (clone_flags & CLONE_SETTLS)
  		p->thread.acrs[0] = regs->gprs[6];
347a8dc3b   Martin Schwidefsky   [PATCH] s390: cle...
257
  #else /* CONFIG_64BIT */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
259
260
261
262
263
264
265
266
267
268
269
  	/* Save the fpu registers to new thread structure. */
  	save_fp_regs(&p->thread.fp_regs);
          p->thread.user_seg = __pa((unsigned long) p->mm->pgd) | _REGION_TABLE;
  	/* Set a new TLS ?  */
  	if (clone_flags & CLONE_SETTLS) {
  		if (test_thread_flag(TIF_31BIT)) {
  			p->thread.acrs[0] = (unsigned int) regs->gprs[6];
  		} else {
  			p->thread.acrs[0] = (unsigned int)(regs->gprs[6] >> 32);
  			p->thread.acrs[1] = (unsigned int) regs->gprs[6];
  		}
  	}
347a8dc3b   Martin Schwidefsky   [PATCH] s390: cle...
270
  #endif /* CONFIG_64BIT */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
  	/* start new process with ar4 pointing to the correct address space */
  	p->thread.mm_segment = get_fs();
          /* Don't copy debug registers */
          memset(&p->thread.per_info,0,sizeof(p->thread.per_info));
  
          return 0;
  }
  
  asmlinkage long sys_fork(struct pt_regs regs)
  {
  	return do_fork(SIGCHLD, regs.gprs[15], &regs, 0, NULL, NULL);
  }
  
  asmlinkage long sys_clone(struct pt_regs regs)
  {
          unsigned long clone_flags;
          unsigned long newsp;
  	int __user *parent_tidptr, *child_tidptr;
  
          clone_flags = regs.gprs[3];
          newsp = regs.orig_gpr2;
  	parent_tidptr = (int __user *) regs.gprs[4];
  	child_tidptr = (int __user *) regs.gprs[5];
          if (!newsp)
                  newsp = regs.gprs[15];
          return do_fork(clone_flags, newsp, &regs, 0,
  		       parent_tidptr, child_tidptr);
  }
  
  /*
   * This is trivial, and on the face of it looks like it
   * could equally well be done in user mode.
   *
   * Not so, for quite unobvious reasons - register pressure.
   * In user mode vfork() cannot have a stack frame, and if
   * done by calling the "clone()" system call directly, you
   * do not have enough call-clobbered registers to hold all
   * the information you need.
   */
  asmlinkage long sys_vfork(struct pt_regs regs)
  {
  	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
  		       regs.gprs[15], &regs, 0, NULL, NULL);
  }
  
  /*
   * sys_execve() executes a new program.
   */
  asmlinkage long sys_execve(struct pt_regs regs)
  {
          int error;
          char * filename;
  
          filename = getname((char __user *) regs.orig_gpr2);
          error = PTR_ERR(filename);
          if (IS_ERR(filename))
                  goto out;
          error = do_execve(filename, (char __user * __user *) regs.gprs[3],
  			  (char __user * __user *) regs.gprs[4], &regs);
  	if (error == 0) {
  		task_lock(current);
  		current->ptrace &= ~PT_DTRACE;
  		task_unlock(current);
  		current->thread.fp_regs.fpc = 0;
  		if (MACHINE_HAS_IEEE)
  			asm volatile("sfpc %0,%0" : : "d" (0));
  	}
          putname(filename);
  out:
          return error;
  }
  
  
  /*
   * fill in the FPU structure for a core dump.
   */
  int dump_fpu (struct pt_regs * regs, s390_fp_regs *fpregs)
  {
347a8dc3b   Martin Schwidefsky   [PATCH] s390: cle...
349
  #ifndef CONFIG_64BIT
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
351
352
353
354
355
          /*
  	 * save fprs to current->thread.fp_regs to merge them with
  	 * the emulated registers and then copy the result to the dump.
  	 */
  	save_fp_regs(&current->thread.fp_regs);
  	memcpy(fpregs, &current->thread.fp_regs, sizeof(s390_fp_regs));
347a8dc3b   Martin Schwidefsky   [PATCH] s390: cle...
356
  #else /* CONFIG_64BIT */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
  	save_fp_regs(fpregs);
347a8dc3b   Martin Schwidefsky   [PATCH] s390: cle...
358
  #endif /* CONFIG_64BIT */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
  	return 1;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
363
364
365
  unsigned long get_wchan(struct task_struct *p)
  {
  	struct stack_frame *sf, *low, *high;
  	unsigned long return_address;
  	int count;
30af7120f   Al Viro   [PATCH] s390: tas...
366
  	if (!p || p == current || p->state == TASK_RUNNING || !task_stack_page(p))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
  		return 0;
30af7120f   Al Viro   [PATCH] s390: tas...
368
369
  	low = task_stack_page(p);
  	high = (struct stack_frame *) task_pt_regs(p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370
371
372
373
374
375
376
377
378
379
380
381
382
  	sf = (struct stack_frame *) (p->thread.ksp & PSW_ADDR_INSN);
  	if (sf <= low || sf > high)
  		return 0;
  	for (count = 0; count < 16; count++) {
  		sf = (struct stack_frame *) (sf->back_chain & PSW_ADDR_INSN);
  		if (sf <= low || sf > high)
  			return 0;
  		return_address = sf->gprs[8] & PSW_ADDR_INSN;
  		if (!in_sched_functions(return_address))
  			return return_address;
  	}
  	return 0;
  }