diff options
Diffstat (limited to 'arch/sparc/kernel')
| -rw-r--r-- | arch/sparc/kernel/entry.h | 7 | ||||
| -rw-r--r-- | arch/sparc/kernel/leon_kernel.c | 6 | ||||
| -rw-r--r-- | arch/sparc/kernel/perf_event.c | 22 | ||||
| -rw-r--r-- | arch/sparc/kernel/process_64.c | 42 | ||||
| -rw-r--r-- | arch/sparc/kernel/ptrace_64.c | 4 | ||||
| -rw-r--r-- | arch/sparc/kernel/setup_64.c | 21 | ||||
| -rw-r--r-- | arch/sparc/kernel/sys_sparc_64.c | 5 | ||||
| -rw-r--r-- | arch/sparc/kernel/systbls_32.S | 1 | ||||
| -rw-r--r-- | arch/sparc/kernel/systbls_64.S | 2 | ||||
| -rw-r--r-- | arch/sparc/kernel/unaligned_64.c | 36 | ||||
| -rw-r--r-- | arch/sparc/kernel/visemul.c | 23 | ||||
| -rw-r--r-- | arch/sparc/kernel/vmlinux.lds.S | 5 | ||||
| -rw-r--r-- | arch/sparc/kernel/winfixup.S | 2 | 
13 files changed, 125 insertions, 51 deletions
diff --git a/arch/sparc/kernel/entry.h b/arch/sparc/kernel/entry.h index 0c218e4c088..cc3c5cb47cd 100644 --- a/arch/sparc/kernel/entry.h +++ b/arch/sparc/kernel/entry.h @@ -59,6 +59,13 @@ struct popc_6insn_patch_entry {  extern struct popc_6insn_patch_entry __popc_6insn_patch,  	__popc_6insn_patch_end; +struct pause_patch_entry { +	unsigned int	addr; +	unsigned int	insns[3]; +}; +extern struct pause_patch_entry __pause_3insn_patch, +	__pause_3insn_patch_end; +  extern void __init per_cpu_patch(void);  extern void sun4v_patch_1insn_range(struct sun4v_1insn_patch_entry *,  				    struct sun4v_1insn_patch_entry *); diff --git a/arch/sparc/kernel/leon_kernel.c b/arch/sparc/kernel/leon_kernel.c index f8b6eee40bd..87f60ee6543 100644 --- a/arch/sparc/kernel/leon_kernel.c +++ b/arch/sparc/kernel/leon_kernel.c @@ -56,11 +56,13 @@ static inline unsigned int leon_eirq_get(int cpu)  static void leon_handle_ext_irq(unsigned int irq, struct irq_desc *desc)  {  	unsigned int eirq; +	struct irq_bucket *p;  	int cpu = sparc_leon3_cpuid();  	eirq = leon_eirq_get(cpu); -	if ((eirq & 0x10) && irq_map[eirq]->irq) /* bit4 tells if IRQ happened */ -		generic_handle_irq(irq_map[eirq]->irq); +	p = irq_map[eirq]; +	if ((eirq & 0x10) && p && p->irq) /* bit4 tells if IRQ happened */ +		generic_handle_irq(p->irq);  }  /* The extended IRQ controller has been found, this function registers it */ diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index 885a8af7406..b5c38faa4ea 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -1762,15 +1762,25 @@ static void perf_callchain_user_32(struct perf_callchain_entry *entry,  	ufp = regs->u_regs[UREG_I6] & 0xffffffffUL;  	do { -		struct sparc_stackf32 *usf, sf;  		unsigned long pc; -		usf = (struct sparc_stackf32 *) ufp; -		if (__copy_from_user_inatomic(&sf, usf, sizeof(sf))) -			break; +		if (thread32_stack_is_64bit(ufp)) { +			struct sparc_stackf *usf, sf; -		pc = sf.callers_pc; -		ufp = (unsigned long)sf.fp; +			ufp += STACK_BIAS; +			usf = (struct sparc_stackf *) ufp; +			if (__copy_from_user_inatomic(&sf, usf, sizeof(sf))) +				break; +			pc = sf.callers_pc & 0xffffffff; +			ufp = ((unsigned long) sf.fp) & 0xffffffff; +		} else { +			struct sparc_stackf32 *usf, sf; +			usf = (struct sparc_stackf32 *) ufp; +			if (__copy_from_user_inatomic(&sf, usf, sizeof(sf))) +				break; +			pc = sf.callers_pc; +			ufp = (unsigned long)sf.fp; +		}  		perf_callchain_store(entry, pc);  	} while (entry->nr < PERF_MAX_STACK_DEPTH);  } diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c index d778248ef3f..c6e0c291004 100644 --- a/arch/sparc/kernel/process_64.c +++ b/arch/sparc/kernel/process_64.c @@ -452,13 +452,16 @@ void flush_thread(void)  /* It's a bit more tricky when 64-bit tasks are involved... */  static unsigned long clone_stackframe(unsigned long csp, unsigned long psp)  { +	bool stack_64bit = test_thread_64bit_stack(psp);  	unsigned long fp, distance, rval; -	if (!(test_thread_flag(TIF_32BIT))) { +	if (stack_64bit) {  		csp += STACK_BIAS;  		psp += STACK_BIAS;  		__get_user(fp, &(((struct reg_window __user *)psp)->ins[6]));  		fp += STACK_BIAS; +		if (test_thread_flag(TIF_32BIT)) +			fp &= 0xffffffff;  	} else  		__get_user(fp, &(((struct reg_window32 __user *)psp)->ins[6])); @@ -472,7 +475,7 @@ static unsigned long clone_stackframe(unsigned long csp, unsigned long psp)  	rval = (csp - distance);  	if (copy_in_user((void __user *) rval, (void __user *) psp, distance))  		rval = 0; -	else if (test_thread_flag(TIF_32BIT)) { +	else if (!stack_64bit) {  		if (put_user(((u32)csp),  			     &(((struct reg_window32 __user *)rval)->ins[6])))  			rval = 0; @@ -507,18 +510,18 @@ void synchronize_user_stack(void)  	flush_user_windows();  	if ((window = get_thread_wsaved()) != 0) { -		int winsize = sizeof(struct reg_window); -		int bias = 0; - -		if (test_thread_flag(TIF_32BIT)) -			winsize = sizeof(struct reg_window32); -		else -			bias = STACK_BIAS; -  		window -= 1;  		do { -			unsigned long sp = (t->rwbuf_stkptrs[window] + bias);  			struct reg_window *rwin = &t->reg_window[window]; +			int winsize = sizeof(struct reg_window); +			unsigned long sp; + +			sp = t->rwbuf_stkptrs[window]; + +			if (test_thread_64bit_stack(sp)) +				sp += STACK_BIAS; +			else +				winsize = sizeof(struct reg_window32);  			if (!copy_to_user((char __user *)sp, rwin, winsize)) {  				shift_window_buffer(window, get_thread_wsaved() - 1, t); @@ -544,13 +547,6 @@ void fault_in_user_windows(void)  {  	struct thread_info *t = current_thread_info();  	unsigned long window; -	int winsize = sizeof(struct reg_window); -	int bias = 0; - -	if (test_thread_flag(TIF_32BIT)) -		winsize = sizeof(struct reg_window32); -	else -		bias = STACK_BIAS;  	flush_user_windows();  	window = get_thread_wsaved(); @@ -558,8 +554,16 @@ void fault_in_user_windows(void)  	if (likely(window != 0)) {  		window -= 1;  		do { -			unsigned long sp = (t->rwbuf_stkptrs[window] + bias);  			struct reg_window *rwin = &t->reg_window[window]; +			int winsize = sizeof(struct reg_window); +			unsigned long sp; + +			sp = t->rwbuf_stkptrs[window]; + +			if (test_thread_64bit_stack(sp)) +				sp += STACK_BIAS; +			else +				winsize = sizeof(struct reg_window32);  			if (unlikely(sp & 0x7UL))  				stack_unaligned(sp); diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index 484dabac704..7ff45e4ba68 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -151,7 +151,7 @@ static int regwindow64_get(struct task_struct *target,  {  	unsigned long rw_addr = regs->u_regs[UREG_I6]; -	if (test_tsk_thread_flag(current, TIF_32BIT)) { +	if (!test_thread_64bit_stack(rw_addr)) {  		struct reg_window32 win32;  		int i; @@ -176,7 +176,7 @@ static int regwindow64_set(struct task_struct *target,  {  	unsigned long rw_addr = regs->u_regs[UREG_I6]; -	if (test_tsk_thread_flag(current, TIF_32BIT)) { +	if (!test_thread_64bit_stack(rw_addr)) {  		struct reg_window32 win32;  		int i; diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c index 0800e71d8a8..0eaf0059aae 100644 --- a/arch/sparc/kernel/setup_64.c +++ b/arch/sparc/kernel/setup_64.c @@ -316,6 +316,25 @@ static void __init popc_patch(void)  	}  } +static void __init pause_patch(void) +{ +	struct pause_patch_entry *p; + +	p = &__pause_3insn_patch; +	while (p < &__pause_3insn_patch_end) { +		unsigned long i, addr = p->addr; + +		for (i = 0; i < 3; i++) { +			*(unsigned int *) (addr +  (i * 4)) = p->insns[i]; +			wmb(); +			__asm__ __volatile__("flush	%0" +					     : : "r" (addr +  (i * 4))); +		} + +		p++; +	} +} +  #ifdef CONFIG_SMP  void __init boot_cpu_id_too_large(int cpu)  { @@ -528,6 +547,8 @@ static void __init init_sparc64_elf_hwcap(void)  	if (sparc64_elf_hwcap & AV_SPARC_POPC)  		popc_patch(); +	if (sparc64_elf_hwcap & AV_SPARC_PAUSE) +		pause_patch();  }  void __init setup_arch(char **cmdline_p) diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c index 11c6c9603e7..878ef3d5fec 100644 --- a/arch/sparc/kernel/sys_sparc_64.c +++ b/arch/sparc/kernel/sys_sparc_64.c @@ -751,3 +751,8 @@ int kernel_execve(const char *filename,  		      : "cc");  	return __res;  } + +asmlinkage long sys_kern_features(void) +{ +	return KERN_FEATURE_MIXED_MODE_STACK; +} diff --git a/arch/sparc/kernel/systbls_32.S b/arch/sparc/kernel/systbls_32.S index 63402f9e9f5..5147f574f12 100644 --- a/arch/sparc/kernel/systbls_32.S +++ b/arch/sparc/kernel/systbls_32.S @@ -85,3 +85,4 @@ sys_call_table:  /*325*/	.long sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_event_open, sys_recvmmsg, sys_fanotify_init  /*330*/	.long sys_fanotify_mark, sys_prlimit64, sys_name_to_handle_at, sys_open_by_handle_at, sys_clock_adjtime  /*335*/	.long sys_syncfs, sys_sendmmsg, sys_setns, sys_process_vm_readv, sys_process_vm_writev +/*340*/	.long sys_ni_syscall, sys_kcmp diff --git a/arch/sparc/kernel/systbls_64.S b/arch/sparc/kernel/systbls_64.S index 3a58e0d66f5..1c9af9fa38e 100644 --- a/arch/sparc/kernel/systbls_64.S +++ b/arch/sparc/kernel/systbls_64.S @@ -86,6 +86,7 @@ sys_call_table32:  	.word compat_sys_pwritev, compat_sys_rt_tgsigqueueinfo, sys_perf_event_open, compat_sys_recvmmsg, sys_fanotify_init  /*330*/	.word sys32_fanotify_mark, sys_prlimit64, sys_name_to_handle_at, compat_sys_open_by_handle_at, compat_sys_clock_adjtime  	.word sys_syncfs, compat_sys_sendmmsg, sys_setns, compat_sys_process_vm_readv, compat_sys_process_vm_writev +/*340*/	.word sys_kern_features, sys_kcmp  #endif /* CONFIG_COMPAT */ @@ -163,3 +164,4 @@ sys_call_table:  	.word sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_event_open, sys_recvmmsg, sys_fanotify_init  /*330*/	.word sys_fanotify_mark, sys_prlimit64, sys_name_to_handle_at, sys_open_by_handle_at, sys_clock_adjtime  	.word sys_syncfs, sys_sendmmsg, sys_setns, sys_process_vm_readv, sys_process_vm_writev +/*340*/	.word sys_kern_features, sys_kcmp diff --git a/arch/sparc/kernel/unaligned_64.c b/arch/sparc/kernel/unaligned_64.c index f81d038f734..8201c25e766 100644 --- a/arch/sparc/kernel/unaligned_64.c +++ b/arch/sparc/kernel/unaligned_64.c @@ -113,21 +113,24 @@ static inline long sign_extend_imm13(long imm)  static unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)  { -	unsigned long value; +	unsigned long value, fp;  	if (reg < 16)  		return (!reg ? 0 : regs->u_regs[reg]); + +	fp = regs->u_regs[UREG_FP]; +  	if (regs->tstate & TSTATE_PRIV) {  		struct reg_window *win; -		win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS); +		win = (struct reg_window *)(fp + STACK_BIAS);  		value = win->locals[reg - 16]; -	} else if (test_thread_flag(TIF_32BIT)) { +	} else if (!test_thread_64bit_stack(fp)) {  		struct reg_window32 __user *win32; -		win32 = (struct reg_window32 __user *)((unsigned long)((u32)regs->u_regs[UREG_FP])); +		win32 = (struct reg_window32 __user *)((unsigned long)((u32)fp));  		get_user(value, &win32->locals[reg - 16]);  	} else {  		struct reg_window __user *win; -		win = (struct reg_window __user *)(regs->u_regs[UREG_FP] + STACK_BIAS); +		win = (struct reg_window __user *)(fp + STACK_BIAS);  		get_user(value, &win->locals[reg - 16]);  	}  	return value; @@ -135,19 +138,24 @@ static unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)  static unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs)  { +	unsigned long fp; +  	if (reg < 16)  		return ®s->u_regs[reg]; + +	fp = regs->u_regs[UREG_FP]; +  	if (regs->tstate & TSTATE_PRIV) {  		struct reg_window *win; -		win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS); +		win = (struct reg_window *)(fp + STACK_BIAS);  		return &win->locals[reg - 16]; -	} else if (test_thread_flag(TIF_32BIT)) { +	} else if (!test_thread_64bit_stack(fp)) {  		struct reg_window32 *win32; -		win32 = (struct reg_window32 *)((unsigned long)((u32)regs->u_regs[UREG_FP])); +		win32 = (struct reg_window32 *)((unsigned long)((u32)fp));  		return (unsigned long *)&win32->locals[reg - 16];  	} else {  		struct reg_window *win; -		win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS); +		win = (struct reg_window *)(fp + STACK_BIAS);  		return &win->locals[reg - 16];  	}  } @@ -392,13 +400,15 @@ int handle_popc(u32 insn, struct pt_regs *regs)  		if (rd)  			regs->u_regs[rd] = ret;  	} else { -		if (test_thread_flag(TIF_32BIT)) { +		unsigned long fp = regs->u_regs[UREG_FP]; + +		if (!test_thread_64bit_stack(fp)) {  			struct reg_window32 __user *win32; -			win32 = (struct reg_window32 __user *)((unsigned long)((u32)regs->u_regs[UREG_FP])); +			win32 = (struct reg_window32 __user *)((unsigned long)((u32)fp));  			put_user(ret, &win32->locals[rd - 16]);  		} else {  			struct reg_window __user *win; -			win = (struct reg_window __user *)(regs->u_regs[UREG_FP] + STACK_BIAS); +			win = (struct reg_window __user *)(fp + STACK_BIAS);  			put_user(ret, &win->locals[rd - 16]);  		}  	} @@ -554,7 +564,7 @@ void handle_ld_nf(u32 insn, struct pt_regs *regs)  		reg[0] = 0;  		if ((insn & 0x780000) == 0x180000)  			reg[1] = 0; -	} else if (test_thread_flag(TIF_32BIT)) { +	} else if (!test_thread_64bit_stack(regs->u_regs[UREG_FP])) {  		put_user(0, (int __user *) reg);  		if ((insn & 0x780000) == 0x180000)  			put_user(0, ((int __user *) reg) + 1); diff --git a/arch/sparc/kernel/visemul.c b/arch/sparc/kernel/visemul.c index 08e074b7eb6..c096c624ac4 100644 --- a/arch/sparc/kernel/visemul.c +++ b/arch/sparc/kernel/visemul.c @@ -149,21 +149,24 @@ static inline void maybe_flush_windows(unsigned int rs1, unsigned int rs2,  static unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)  { -	unsigned long value; +	unsigned long value, fp;  	if (reg < 16)  		return (!reg ? 0 : regs->u_regs[reg]); + +	fp = regs->u_regs[UREG_FP]; +  	if (regs->tstate & TSTATE_PRIV) {  		struct reg_window *win; -		win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS); +		win = (struct reg_window *)(fp + STACK_BIAS);  		value = win->locals[reg - 16]; -	} else if (test_thread_flag(TIF_32BIT)) { +	} else if (!test_thread_64bit_stack(fp)) {  		struct reg_window32 __user *win32; -		win32 = (struct reg_window32 __user *)((unsigned long)((u32)regs->u_regs[UREG_FP])); +		win32 = (struct reg_window32 __user *)((unsigned long)((u32)fp));  		get_user(value, &win32->locals[reg - 16]);  	} else {  		struct reg_window __user *win; -		win = (struct reg_window __user *)(regs->u_regs[UREG_FP] + STACK_BIAS); +		win = (struct reg_window __user *)(fp + STACK_BIAS);  		get_user(value, &win->locals[reg - 16]);  	}  	return value; @@ -172,16 +175,18 @@ static unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)  static inline unsigned long __user *__fetch_reg_addr_user(unsigned int reg,  							  struct pt_regs *regs)  { +	unsigned long fp = regs->u_regs[UREG_FP]; +  	BUG_ON(reg < 16);  	BUG_ON(regs->tstate & TSTATE_PRIV); -	if (test_thread_flag(TIF_32BIT)) { +	if (!test_thread_64bit_stack(fp)) {  		struct reg_window32 __user *win32; -		win32 = (struct reg_window32 __user *)((unsigned long)((u32)regs->u_regs[UREG_FP])); +		win32 = (struct reg_window32 __user *)((unsigned long)((u32)fp));  		return (unsigned long __user *)&win32->locals[reg - 16];  	} else {  		struct reg_window __user *win; -		win = (struct reg_window __user *)(regs->u_regs[UREG_FP] + STACK_BIAS); +		win = (struct reg_window __user *)(fp + STACK_BIAS);  		return &win->locals[reg - 16];  	}  } @@ -204,7 +209,7 @@ static void store_reg(struct pt_regs *regs, unsigned long val, unsigned long rd)  	} else {  		unsigned long __user *rd_user = __fetch_reg_addr_user(rd, regs); -		if (test_thread_flag(TIF_32BIT)) +		if (!test_thread_64bit_stack(regs->u_regs[UREG_FP]))  			__put_user((u32)val, (u32 __user *)rd_user);  		else  			__put_user(val, rd_user); diff --git a/arch/sparc/kernel/vmlinux.lds.S b/arch/sparc/kernel/vmlinux.lds.S index 89c2c29f154..0bacceb1915 100644 --- a/arch/sparc/kernel/vmlinux.lds.S +++ b/arch/sparc/kernel/vmlinux.lds.S @@ -132,6 +132,11 @@ SECTIONS  		*(.popc_6insn_patch)  		__popc_6insn_patch_end = .;  	} +	.pause_3insn_patch : { +		__pause_3insn_patch = .; +		*(.pause_3insn_patch) +		__pause_3insn_patch_end = .; +	}  	PERCPU_SECTION(SMP_CACHE_BYTES)  	. = ALIGN(PAGE_SIZE); diff --git a/arch/sparc/kernel/winfixup.S b/arch/sparc/kernel/winfixup.S index a6b0863c27d..1e67ce95836 100644 --- a/arch/sparc/kernel/winfixup.S +++ b/arch/sparc/kernel/winfixup.S @@ -43,6 +43,8 @@ spill_fixup_mna:  spill_fixup_dax:  	TRAP_LOAD_THREAD_REG(%g6, %g1)  	ldx	[%g6 + TI_FLAGS], %g1 +	andcc	%sp, 0x1, %g0 +	movne	%icc, 0, %g1  	andcc	%g1, _TIF_32BIT, %g0  	ldub	[%g6 + TI_WSAVED], %g1  	sll	%g1, 3, %g3  |