diff options
Diffstat (limited to 'arch/s390/kernel/process.c')
| -rw-r--r-- | arch/s390/kernel/process.c | 275 | 
1 files changed, 110 insertions, 165 deletions
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index ec2e03b22ea..93b9ca42e5c 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -1,7 +1,7 @@  /*   * This file handles the architecture dependent parts of process handling.   * - *    Copyright IBM Corp. 1999,2009 + *    Copyright IBM Corp. 1999, 2009   *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>,   *		 Hartmut Penner <hp@de.ibm.com>,   *		 Denis Joseph Barrow, @@ -9,38 +9,29 @@  #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/fs.h> +#include <linux/elfcore.h>  #include <linux/smp.h> -#include <linux/stddef.h>  #include <linux/slab.h> -#include <linux/unistd.h> -#include <linux/ptrace.h> -#include <linux/vmalloc.h> -#include <linux/user.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 <linux/tick.h> -#include <linux/elfcore.h> -#include <linux/kernel_stat.h> +#include <linux/personality.h>  #include <linux/syscalls.h>  #include <linux/compat.h> -#include <asm/compat.h> -#include <asm/uaccess.h> -#include <asm/pgtable.h> -#include <asm/system.h> +#include <linux/kprobes.h> +#include <linux/random.h> +#include <linux/module.h>  #include <asm/io.h>  #include <asm/processor.h> +#include <asm/vtimer.h> +#include <asm/exec.h>  #include <asm/irq.h> -#include <asm/timer.h>  #include <asm/nmi.h> +#include <asm/smp.h> +#include <asm/switch_to.h> +#include <asm/runtime_instr.h>  #include "entry.h"  asmlinkage void ret_from_fork(void) asm ("ret_from_fork"); @@ -70,85 +61,38 @@ unsigned long thread_saved_pc(struct task_struct *tsk)  	return sf->gprs[8];  } -/* - * The idle loop on a S390... - */ -static void default_idle(void) +void arch_cpu_idle(void)  { -	/* CPU is going idle. */ -#ifdef CONFIG_HOTPLUG_CPU -	if (cpu_is_offline(smp_processor_id())) { -		preempt_enable_no_resched(); -		cpu_die(); -	} -#endif -	local_irq_disable(); -	if (need_resched()) { -		local_irq_enable(); -		return; -	}  	local_mcck_disable(); -	if (test_thread_flag(TIF_MCCK_PENDING)) { +	if (test_cpu_flag(CIF_MCCK_PENDING)) {  		local_mcck_enable();  		local_irq_enable(); -		s390_handle_mcck();  		return;  	} -	trace_hardirqs_on(); -	/* Don't trace preempt off for idle. */ -	stop_critical_timings(); -	/* Stop virtual timer and halt the cpu. */ +	/* Halt the cpu and keep track of cpu time accounting. */  	vtime_stop_cpu(); -	/* Reenable preemption tracer. */ -	start_critical_timings(); +	local_irq_enable();  } -void cpu_idle(void) +void arch_cpu_idle_exit(void)  { -	for (;;) { -		tick_nohz_stop_sched_tick(1); -		while (!need_resched()) -			default_idle(); -		tick_nohz_restart_sched_tick(); -		preempt_enable_no_resched(); -		schedule(); -		preempt_disable(); -	} +	if (test_cpu_flag(CIF_MCCK_PENDING)) +		s390_handle_mcck();  } -extern void kernel_thread_starter(void); - -asm( -	".align 4\n" -	"kernel_thread_starter:\n" -	"    la    2,0(10)\n" -	"    basr  14,9\n" -	"    la    2,0\n" -	"    br    11\n"); - -int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +void arch_cpu_idle_dead(void)  { -	struct pt_regs regs; - -	memset(®s, 0, sizeof(regs)); -	regs.psw.mask = psw_kernel_bits | PSW_MASK_IO | PSW_MASK_EXT; -	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, ®s, 0, NULL, NULL); +	cpu_die();  } -EXPORT_SYMBOL(kernel_thread); + +extern void __kprobes kernel_thread_starter(void);  /*   * Free current thread data structures etc..   */  void exit_thread(void)  { +	exit_thread_runtime_instr();  }  void flush_thread(void) @@ -160,8 +104,7 @@ void release_thread(struct task_struct *dead_task)  }  int copy_thread(unsigned long clone_flags, unsigned long new_stackp, -		unsigned long unused, -		struct task_struct *p, struct pt_regs *regs) +		unsigned long arg, struct task_struct *p)  {  	struct thread_info *ti;  	struct fake_frame @@ -172,91 +115,82 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,  	frame = container_of(task_pt_regs(p), struct fake_frame, childregs);  	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; +	/* Save access registers to new thread structure. */ +	save_access_regs(&p->thread.acrs[0]); +	/* 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_user, 0, sizeof(p->thread.per_user)); +	memset(&p->thread.per_event, 0, sizeof(p->thread.per_event)); +	clear_tsk_thread_flag(p, TIF_SINGLE_STEP); +	/* Initialize per thread user and system timer values */ +	ti = task_thread_info(p); +	ti->user_timer = 0; +	ti->system_timer = 0; +	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]); +	/* Store access registers to kernel stack of new process. */ +	if (unlikely(p->flags & PF_KTHREAD)) { +		/* kernel thread */ +		memset(&frame->childregs, 0, sizeof(struct pt_regs)); +		frame->childregs.psw.mask = PSW_KERNEL_BITS | PSW_MASK_DAT | +				PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; +		frame->childregs.psw.addr = PSW_ADDR_AMODE | +				(unsigned long) kernel_thread_starter; +		frame->childregs.gprs[9] = new_stackp; /* function */ +		frame->childregs.gprs[10] = arg; +		frame->childregs.gprs[11] = (unsigned long) do_exit; +		frame->childregs.orig_gpr2 = -1; + +		return 0; +	} +	frame->childregs = *current_pt_regs(); +	frame->childregs.gprs[2] = 0;	/* child returns 0 on fork. */ +	frame->childregs.flags = 0; +	if (new_stackp) +		frame->childregs.gprs[15] = new_stackp; + +	/* Don't copy runtime instrumentation info */ +	p->thread.ri_cb = NULL; +	p->thread.ri_signum = 0; +	frame->childregs.psw.mask &= ~PSW_MASK_RI;  #ifndef CONFIG_64BIT  	/*  	 * 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(¤t->thread.fp_regs); +	save_fp_ctl(¤t->thread.fp_regs.fpc); +	save_fp_regs(current->thread.fp_regs.fprs);  	memcpy(&p->thread.fp_regs, ¤t->thread.fp_regs,  	       sizeof(s390_fp_regs));  	/* Set a new TLS ?  */  	if (clone_flags & CLONE_SETTLS) -		p->thread.acrs[0] = regs->gprs[6]; +		p->thread.acrs[0] = frame->childregs.gprs[6];  #else /* CONFIG_64BIT */  	/* Save the fpu registers to new thread structure. */ -	save_fp_regs(&p->thread.fp_regs); +	save_fp_ctl(&p->thread.fp_regs.fpc); +	save_fp_regs(p->thread.fp_regs.fprs); +	p->thread.fp_regs.pad = 0;  	/* Set a new TLS ?  */  	if (clone_flags & CLONE_SETTLS) { +		unsigned long tls = frame->childregs.gprs[6];  		if (is_compat_task()) { -			p->thread.acrs[0] = (unsigned int) regs->gprs[6]; +			p->thread.acrs[0] = (unsigned int)tls;  		} else { -			p->thread.acrs[0] = (unsigned int)(regs->gprs[6] >> 32); -			p->thread.acrs[1] = (unsigned int) regs->gprs[6]; +			p->thread.acrs[0] = (unsigned int)(tls >> 32); +			p->thread.acrs[1] = (unsigned int)tls;  		}  	}  #endif /* CONFIG_64BIT */ -	/* 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)); -	clear_tsk_thread_flag(p, TIF_SINGLE_STEP); -	/* Initialize per thread user and system timer values */ -	ti = task_thread_info(p); -	ti->user_timer = 0; -	ti->system_timer = 0;  	return 0;  } -SYSCALL_DEFINE0(fork) -{ -	struct pt_regs *regs = task_pt_regs(current); -	return do_fork(SIGCHLD, regs->gprs[15], regs, 0, NULL, NULL); -} - -SYSCALL_DEFINE4(clone, unsigned long, newsp, unsigned long, clone_flags, -		int __user *, parent_tidptr, int __user *, child_tidptr) -{ -	struct pt_regs *regs = task_pt_regs(current); - -	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. - */ -SYSCALL_DEFINE0(vfork) -{ -	struct pt_regs *regs = task_pt_regs(current); -	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, -		       regs->gprs[15], regs, 0, NULL, NULL); -} -  asmlinkage void execve_tail(void)  {  	current->thread.fp_regs.fpc = 0; @@ -265,31 +199,6 @@ asmlinkage void execve_tail(void)  }  /* - * sys_execve() executes a new program. - */ -SYSCALL_DEFINE3(execve, const char __user *, name, -		const char __user *const __user *, argv, -		const char __user *const __user *, envp) -{ -	struct pt_regs *regs = task_pt_regs(current); -	char *filename; -	long rc; - -	filename = getname(name); -	rc = PTR_ERR(filename); -	if (IS_ERR(filename)) -		return rc; -	rc = do_execve(filename, argv, envp, regs); -	if (rc) -		goto out; -	execve_tail(); -	rc = regs->gprs[2]; -out: -	putname(filename); -	return rc; -} - -/*   * fill in the FPU structure for a core dump.   */  int dump_fpu (struct pt_regs * regs, s390_fp_regs *fpregs) @@ -299,10 +208,12 @@ int dump_fpu (struct pt_regs * regs, s390_fp_regs *fpregs)  	 * 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(¤t->thread.fp_regs); +	save_fp_ctl(¤t->thread.fp_regs.fpc); +	save_fp_regs(current->thread.fp_regs.fprs);  	memcpy(fpregs, ¤t->thread.fp_regs, sizeof(s390_fp_regs));  #else /* CONFIG_64BIT */ -	save_fp_regs(fpregs); +	save_fp_ctl(&fpregs->fpc); +	save_fp_regs(fpregs->fprs);  #endif /* CONFIG_64BIT */  	return 1;  } @@ -331,3 +242,37 @@ unsigned long get_wchan(struct task_struct *p)  	}  	return 0;  } + +unsigned long arch_align_stack(unsigned long sp) +{ +	if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space) +		sp -= get_random_int() & ~PAGE_MASK; +	return sp & ~0xf; +} + +static inline unsigned long brk_rnd(void) +{ +	/* 8MB for 32bit, 1GB for 64bit */ +	if (is_32bit_task()) +		return (get_random_int() & 0x7ffUL) << PAGE_SHIFT; +	else +		return (get_random_int() & 0x3ffffUL) << PAGE_SHIFT; +} + +unsigned long arch_randomize_brk(struct mm_struct *mm) +{ +	unsigned long ret; + +	ret = PAGE_ALIGN(mm->brk + brk_rnd()); +	return (ret > mm->brk) ? ret : mm->brk; +} + +unsigned long randomize_et_dyn(unsigned long base) +{ +	unsigned long ret; + +	if (!(current->flags & PF_RANDOMIZE)) +		return base; +	ret = PAGE_ALIGN(base + brk_rnd()); +	return (ret > base) ? ret : base; +}  | 
