diff options
Diffstat (limited to 'arch/csky/kernel')
| -rw-r--r-- | arch/csky/kernel/Makefile | 2 | ||||
| -rw-r--r-- | arch/csky/kernel/atomic.S | 26 | ||||
| -rw-r--r-- | arch/csky/kernel/entry.S | 77 | ||||
| -rw-r--r-- | arch/csky/kernel/ftrace.c | 148 | ||||
| -rw-r--r-- | arch/csky/kernel/head.S | 60 | ||||
| -rw-r--r-- | arch/csky/kernel/perf_callchain.c | 119 | ||||
| -rw-r--r-- | arch/csky/kernel/perf_regs.c | 40 | ||||
| -rw-r--r-- | arch/csky/kernel/ptrace.c | 51 | ||||
| -rw-r--r-- | arch/csky/kernel/setup.c | 12 | ||||
| -rw-r--r-- | arch/csky/kernel/signal.c | 348 | 
10 files changed, 513 insertions, 370 deletions
| diff --git a/arch/csky/kernel/Makefile b/arch/csky/kernel/Makefile index 484e6d3a3647..1624b04bffb5 100644 --- a/arch/csky/kernel/Makefile +++ b/arch/csky/kernel/Makefile @@ -9,6 +9,8 @@ obj-$(CONFIG_SMP)			+= smp.o  obj-$(CONFIG_FUNCTION_TRACER)		+= ftrace.o  obj-$(CONFIG_STACKTRACE)		+= stacktrace.o  obj-$(CONFIG_CSKY_PMU_V1)		+= perf_event.o +obj-$(CONFIG_PERF_EVENTS)		+= perf_callchain.o +obj-$(CONFIG_HAVE_PERF_REGS)            += perf_regs.o  ifdef CONFIG_FUNCTION_TRACER  CFLAGS_REMOVE_ftrace.o = $(CC_FLAGS_FTRACE) diff --git a/arch/csky/kernel/atomic.S b/arch/csky/kernel/atomic.S index d2357c8f85bd..5b84f11485ae 100644 --- a/arch/csky/kernel/atomic.S +++ b/arch/csky/kernel/atomic.S @@ -12,11 +12,10 @@   * If *ptr != oldval && return 1,   * else *ptr = newval return 0.   */ -#ifdef CONFIG_CPU_HAS_LDSTEX  ENTRY(csky_cmpxchg)  	USPTOKSP  	mfcr	a3, epc -	INCTRAP	a3 +	addi	a3, TRAP0_SIZE  	subi    sp, 8  	stw     a3, (sp, 0) @@ -24,6 +23,7 @@ ENTRY(csky_cmpxchg)  	stw     a3, (sp, 4)  	psrset	ee +#ifdef CONFIG_CPU_HAS_LDSTEX  1:  	ldex	a3, (a2)  	cmpne	a0, a3 @@ -33,27 +33,7 @@ ENTRY(csky_cmpxchg)  	bez	a3, 1b  2:  	sync.is -	mvc	a0 -	ldw	a3, (sp, 0) -	mtcr	a3, epc -	ldw     a3, (sp, 4) -	mtcr	a3, epsr -	addi	sp, 8 -	KSPTOUSP -	rte -END(csky_cmpxchg)  #else -ENTRY(csky_cmpxchg) -	USPTOKSP -	mfcr	a3, epc -	INCTRAP	a3 - -	subi    sp, 8 -	stw     a3, (sp, 0) -	mfcr    a3, epsr -	stw     a3, (sp, 4) - -	psrset	ee  1:  	ldw	a3, (a2)  	cmpne	a0, a3 @@ -61,6 +41,7 @@ ENTRY(csky_cmpxchg)  2:  	stw	a1, (a2)  3: +#endif  	mvc	a0  	ldw	a3, (sp, 0)  	mtcr	a3, epc @@ -71,6 +52,7 @@ ENTRY(csky_cmpxchg)  	rte  END(csky_cmpxchg) +#ifndef CONFIG_CPU_HAS_LDSTEX  /*   * Called from tlbmodified exception   */ diff --git a/arch/csky/kernel/entry.S b/arch/csky/kernel/entry.S index 5137ed9062bd..a7e84ccccbd8 100644 --- a/arch/csky/kernel/entry.S +++ b/arch/csky/kernel/entry.S @@ -40,7 +40,8 @@ ENTRY(csky_\name)  	WR_MCIR	a2  #endif  	bclri   r6, 0 -	lrw	a2, PHYS_OFFSET +	lrw	a2, va_pa_offset +	ld.w	a2, (a2, 0)  	subu	r6, a2  	bseti	r6, 31 @@ -50,7 +51,8 @@ ENTRY(csky_\name)  	addu    r6, a2  	ldw     r6, (r6) -	lrw	a2, PHYS_OFFSET +	lrw	a2, va_pa_offset +	ld.w	a2, (a2, 0)  	subu	r6, a2  	bseti	r6, 31 @@ -91,7 +93,7 @@ ENTRY(csky_\name)  	mfcr    a3, ss2  	mfcr    r6, ss3  	mfcr    a2, ss4 -	SAVE_ALL EPC_KEEP +	SAVE_ALL 0  .endm  .macro tlbop_end is_write  	RD_MEH	a2 @@ -99,7 +101,6 @@ ENTRY(csky_\name)  	mov     a0, sp  	movi    a1, \is_write  	jbsr    do_page_fault -	movi    r11_sig, 0             /* r11 = 0, Not a syscall. */  	jmpi    ret_from_exception  .endm @@ -118,7 +119,7 @@ jbsr csky_cmpxchg_fixup  tlbop_end 1  ENTRY(csky_systemcall) -	SAVE_ALL EPC_INCREASE +	SAVE_ALL TRAP0_SIZE  	psrset  ee, ie @@ -136,8 +137,9 @@ ENTRY(csky_systemcall)  	bmaski  r10, THREAD_SHIFT  	andn    r9, r10  	ldw     r8, (r9, TINFO_FLAGS) -	btsti   r8, TIF_SYSCALL_TRACE -	bt      1f +	ANDI_R3	r8, (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_TRACEPOINT | _TIF_SYSCALL_AUDIT) +	cmpnei	r8, 0 +	bt      csky_syscall_trace  #if defined(__CSKYABIV2__)  	subi    sp, 8  	stw  	r5, (sp, 0x4) @@ -150,10 +152,9 @@ ENTRY(csky_systemcall)  	stw     a0, (sp, LSAVE_A0)      /* Save return value */  	jmpi    ret_from_exception -1: -	movi	a0, 0                   /* enter system call */ -	mov	a1, sp                  /* sp = pt_regs pointer */ -	jbsr	syscall_trace +csky_syscall_trace: +	mov	a0, sp                  /* sp = pt_regs pointer */ +	jbsr	syscall_trace_enter  	/* Prepare args before do system call */  	ldw	a0, (sp, LSAVE_A0)  	ldw	a1, (sp, LSAVE_A1) @@ -173,9 +174,8 @@ ENTRY(csky_systemcall)  #endif  	stw	a0, (sp, LSAVE_A0)	/* Save return value */ -	movi    a0, 1                   /* leave system call */ -	mov     a1, sp                  /* right now, sp --> pt_regs */ -	jbsr    syscall_trace +	mov     a0, sp                  /* right now, sp --> pt_regs */ +	jbsr    syscall_trace_exit  	br	ret_from_exception  ENTRY(ret_from_kernel_thread) @@ -190,14 +190,11 @@ ENTRY(ret_from_fork)  	bmaski	r10, THREAD_SHIFT  	andn	r9, r10  	ldw	r8, (r9, TINFO_FLAGS) -	movi	r11_sig, 1 -	btsti	r8, TIF_SYSCALL_TRACE -	bf	3f -	movi	a0, 1 -	mov	a1, sp			/* sp = pt_regs pointer */ -	jbsr	syscall_trace -3: -	jbsr	ret_from_exception +	ANDI_R3	r8, (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_TRACEPOINT | _TIF_SYSCALL_AUDIT) +	cmpnei	r8, 0 +	bf	ret_from_exception +	mov	a0, sp			/* sp = pt_regs pointer */ +	jbsr	syscall_trace_exit  ret_from_exception:  	ld	syscallid, (sp, LSAVE_PSR) @@ -212,41 +209,30 @@ ret_from_exception:  	bmaski	r10, THREAD_SHIFT  	andn	r9, r10 -resume_userspace:  	ldw	r8, (r9, TINFO_FLAGS)  	andi	r8, (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED)  	cmpnei	r8, 0  	bt	exit_work -1:  RESTORE_ALL +1: +	RESTORE_ALL  exit_work: +	lrw	syscallid, ret_from_exception +	mov	lr, syscallid +  	btsti	r8, TIF_NEED_RESCHED  	bt	work_resched -	/* If thread_info->flag is empty, RESTORE_ALL */ -	cmpnei	r8, 0 -	bf	1b -	mov	a1, sp -	mov	a0, r8 -	mov	a2, r11_sig		/* syscall? */ -	btsti	r8, TIF_SIGPENDING	/* delivering a signal? */ -	/* prevent further restarts(set r11 = 0) */ -	clrt	r11_sig -	jbsr	do_notify_resume	/* do signals */ -	br	resume_userspace + +	mov	a0, sp +	mov	a1, r8 +	jmpi	do_notify_resume  work_resched: -	lrw	syscallid, ret_from_exception -	mov	r15, syscallid		/* Return address in link */  	jmpi	schedule -ENTRY(sys_rt_sigreturn) -	movi	r11_sig, 0 -	jmpi	do_rt_sigreturn -  ENTRY(csky_trap) -	SAVE_ALL EPC_KEEP +	SAVE_ALL 0  	psrset	ee -	movi	r11_sig, 0             /* r11 = 0, Not a syscall. */  	mov	a0, sp                 /* Push Stack pointer arg */  	jbsr	trap_c                 /* Call C-level trap handler */  	jmpi	ret_from_exception @@ -261,7 +247,7 @@ ENTRY(csky_get_tls)  	/* increase epc for continue */  	mfcr	a0, epc -	INCTRAP	a0 +	addi	a0, TRAP0_SIZE  	mtcr	a0, epc  	/* get current task thread_info with kernel 8K stack */ @@ -278,9 +264,8 @@ ENTRY(csky_get_tls)  	rte  ENTRY(csky_irq) -	SAVE_ALL EPC_KEEP +	SAVE_ALL 0  	psrset	ee -	movi	r11_sig, 0		/* r11 = 0, Not a syscall. */  #ifdef CONFIG_PREEMPT  	mov	r9, sp			/* Get current stack  pointer */ diff --git a/arch/csky/kernel/ftrace.c b/arch/csky/kernel/ftrace.c index 274c431f1810..44f4880179b7 100644 --- a/arch/csky/kernel/ftrace.c +++ b/arch/csky/kernel/ftrace.c @@ -3,6 +3,137 @@  #include <linux/ftrace.h>  #include <linux/uaccess.h> +#include <asm/cacheflush.h> + +#ifdef CONFIG_DYNAMIC_FTRACE + +#define NOP		0x4000 +#define NOP32_HI	0xc400 +#define NOP32_LO	0x4820 +#define PUSH_LR		0x14d0 +#define MOVIH_LINK	0xea3a +#define ORI_LINK	0xef5a +#define JSR_LINK	0xe8fa +#define BSR_LINK	0xe000 + +/* + * Gcc-csky with -pg will insert stub in function prologue: + *	push	lr + *	jbsr	_mcount + *	nop32 + *	nop32 + * + * If the (callee - current_pc) is less then 64MB, we'll use bsr: + *	push	lr + *	bsr	_mcount + *	nop32 + *	nop32 + * else we'll use (movih + ori + jsr): + *	push	lr + *	movih	r26, ... + *	ori	r26, ... + *	jsr	r26 + * + * (r26 is our reserved link-reg) + * + */ +static inline void make_jbsr(unsigned long callee, unsigned long pc, +			     uint16_t *call, bool nolr) +{ +	long offset; + +	call[0]	= nolr ? NOP : PUSH_LR; + +	offset = (long) callee - (long) pc; + +	if (unlikely(offset < -67108864 || offset > 67108864)) { +		call[1] = MOVIH_LINK; +		call[2] = callee >> 16; +		call[3] = ORI_LINK; +		call[4] = callee & 0xffff; +		call[5] = JSR_LINK; +		call[6] = 0; +	} else { +		offset = offset >> 1; + +		call[1] = BSR_LINK | +			 ((uint16_t)((unsigned long) offset >> 16) & 0x3ff); +		call[2] = (uint16_t)((unsigned long) offset & 0xffff); +		call[3] = call[5] = NOP32_HI; +		call[4] = call[6] = NOP32_LO; +	} +} + +static uint16_t nops[7] = {NOP, NOP32_HI, NOP32_LO, NOP32_HI, NOP32_LO, +				NOP32_HI, NOP32_LO}; +static int ftrace_check_current_nop(unsigned long hook) +{ +	uint16_t olds[7]; +	unsigned long hook_pos = hook - 2; + +	if (probe_kernel_read((void *)olds, (void *)hook_pos, sizeof(nops))) +		return -EFAULT; + +	if (memcmp((void *)nops, (void *)olds, sizeof(nops))) { +		pr_err("%p: nop but get (%04x %04x %04x %04x %04x %04x %04x)\n", +			(void *)hook_pos, +			olds[0], olds[1], olds[2], olds[3], olds[4], olds[5], +			olds[6]); + +		return -EINVAL; +	} + +	return 0; +} + +static int ftrace_modify_code(unsigned long hook, unsigned long target, +			      bool enable, bool nolr) +{ +	uint16_t call[7]; + +	unsigned long hook_pos = hook - 2; +	int ret = 0; + +	make_jbsr(target, hook, call, nolr); + +	ret = probe_kernel_write((void *)hook_pos, enable ? call : nops, +				 sizeof(nops)); +	if (ret) +		return -EPERM; + +	flush_icache_range(hook_pos, hook_pos + MCOUNT_INSN_SIZE); + +	return 0; +} + +int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{ +	int ret = ftrace_check_current_nop(rec->ip); + +	if (ret) +		return ret; + +	return ftrace_modify_code(rec->ip, addr, true, false); +} + +int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, +		    unsigned long addr) +{ +	return ftrace_modify_code(rec->ip, addr, false, false); +} + +int ftrace_update_ftrace_func(ftrace_func_t func) +{ +	int ret = ftrace_modify_code((unsigned long)&ftrace_call, +				(unsigned long)func, true, true); +	return ret; +} + +int __init ftrace_dyn_arch_init(void) +{ +	return 0; +} +#endif /* CONFIG_DYNAMIC_FTRACE */  #ifdef CONFIG_FUNCTION_GRAPH_TRACER  void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, @@ -43,8 +174,21 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,  			*(unsigned long *)frame_pointer = return_hooker;  	}  } -#endif + +#ifdef CONFIG_DYNAMIC_FTRACE +int ftrace_enable_ftrace_graph_caller(void) +{ +	return ftrace_modify_code((unsigned long)&ftrace_graph_call, +			(unsigned long)&ftrace_graph_caller, true, true); +} + +int ftrace_disable_ftrace_graph_caller(void) +{ +	return ftrace_modify_code((unsigned long)&ftrace_graph_call, +			(unsigned long)&ftrace_graph_caller, false, true); +} +#endif /* CONFIG_DYNAMIC_FTRACE */ +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */  /* _mcount is defined in abi's mcount.S */ -extern void _mcount(void);  EXPORT_SYMBOL(_mcount); diff --git a/arch/csky/kernel/head.S b/arch/csky/kernel/head.S index 9c4ec473b76b..61989f9241c0 100644 --- a/arch/csky/kernel/head.S +++ b/arch/csky/kernel/head.S @@ -7,16 +7,11 @@  __HEAD  ENTRY(_start) -	/* set super user mode */ -	lrw	a3, DEFAULT_PSR_VALUE -	mtcr    a3, psr -	psrset  ee - -	SETUP_MMU a3 +	SETUP_MMU  	/* set stack point */ -	lrw     a3, init_thread_union + THREAD_SIZE -	mov	sp, a3 +	lrw     r6, init_thread_union + THREAD_SIZE +	mov	sp, r6  	jmpi	csky_start  END(_start) @@ -24,53 +19,12 @@ END(_start)  #ifdef CONFIG_SMP  .align 10  ENTRY(_start_smp_secondary) -	/* Invalid I/Dcache BTB BHT */ -	movi	a3, 7 -	lsli	a3, 16 -	addi	a3, (1<<4) | 3 -	mtcr	a3, cr17 - -	tlbi.alls - -	/* setup PAGEMASK */ -	movi	a3, 0 -	mtcr	a3, cr<6, 15> - -	/* setup MEL0/MEL1 */ -	grs	a0, _start_smp_pc -_start_smp_pc: -	bmaski  a1, 13 -	andn    a0, a1 -	movi    a1, 0x00000006 -	movi    a2, 0x00001006 -	or      a1, a0 -	or      a2, a0 -	mtcr	a1, cr<2, 15> -	mtcr	a2, cr<3, 15> - -	/* setup MEH */ -	mtcr	a0, cr<4, 15> - -	/* write TLB */ -	bgeni   a3, 28 -	mtcr	a3, cr<8, 15> - -	SETUP_MMU a3 - -	/* enable MMU */ -	movi	a3, 1 -	mtcr    a3, cr18 - -	jmpi	_goto_mmu_on -_goto_mmu_on: -	lrw	a3, DEFAULT_PSR_VALUE -	mtcr    a3, psr -	psrset  ee +	SETUP_MMU  	/* set stack point */ -	lrw     a3, secondary_stack -	ld.w	a3, (a3, 0) -	mov	sp, a3 +	lrw     r6, secondary_stack +	ld.w	r6, (r6, 0) +	mov	sp, r6  	jmpi	csky_start_secondary  END(_start_smp_secondary) diff --git a/arch/csky/kernel/perf_callchain.c b/arch/csky/kernel/perf_callchain.c new file mode 100644 index 000000000000..e68ff375c8f8 --- /dev/null +++ b/arch/csky/kernel/perf_callchain.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. + +#include <linux/perf_event.h> +#include <linux/uaccess.h> + +/* Kernel callchain */ +struct stackframe { +	unsigned long fp; +	unsigned long lr; +}; + +static int unwind_frame_kernel(struct stackframe *frame) +{ +	if (kstack_end((void *)frame->fp)) +		return -EPERM; +	if (frame->fp & 0x3 || frame->fp < TASK_SIZE) +		return -EPERM; + +	*frame = *(struct stackframe *)frame->fp; +	if (__kernel_text_address(frame->lr)) { +		int graph = 0; + +		frame->lr = ftrace_graph_ret_addr(NULL, &graph, frame->lr, +				NULL); +	} +	return 0; +} + +static void notrace walk_stackframe(struct stackframe *fr, +			struct perf_callchain_entry_ctx *entry) +{ +	do { +		perf_callchain_store(entry, fr->lr); +	} while (unwind_frame_kernel(fr) >= 0); +} + +/* + * Get the return address for a single stackframe and return a pointer to the + * next frame tail. + */ +static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry, +			unsigned long fp, unsigned long reg_lr) +{ +	struct stackframe buftail; +	unsigned long lr = 0; +	unsigned long *user_frame_tail = (unsigned long *)fp; + +	/* Check accessibility of one struct frame_tail beyond */ +	if (!access_ok(user_frame_tail, sizeof(buftail))) +		return 0; +	if (__copy_from_user_inatomic(&buftail, user_frame_tail, +				      sizeof(buftail))) +		return 0; + +	if (reg_lr != 0) +		lr = reg_lr; +	else +		lr = buftail.lr; + +	fp = buftail.fp; +	perf_callchain_store(entry, lr); + +	return fp; +} + +/* + * This will be called when the target is in user mode + * This function will only be called when we use + * "PERF_SAMPLE_CALLCHAIN" in + * kernel/events/core.c:perf_prepare_sample() + * + * How to trigger perf_callchain_[user/kernel] : + * $ perf record -e cpu-clock --call-graph fp ./program + * $ perf report --call-graph + * + * On C-SKY platform, the program being sampled and the C library + * need to be compiled with * -mbacktrace, otherwise the user + * stack will not contain function frame. + */ +void perf_callchain_user(struct perf_callchain_entry_ctx *entry, +			 struct pt_regs *regs) +{ +	unsigned long fp = 0; + +	/* C-SKY does not support virtualization. */ +	if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) +		return; + +	fp = regs->regs[4]; +	perf_callchain_store(entry, regs->pc); + +	/* +	 * While backtrace from leaf function, lr is normally +	 * not saved inside frame on C-SKY, so get lr from pt_regs +	 * at the sample point. However, lr value can be incorrect if +	 * lr is used as temp register +	 */ +	fp = user_backtrace(entry, fp, regs->lr); + +	while (fp && !(fp & 0x3) && entry->nr < entry->max_stack) +		fp = user_backtrace(entry, fp, 0); +} + +void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, +			   struct pt_regs *regs) +{ +	struct stackframe fr; + +	/* C-SKY does not support virtualization. */ +	if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { +		pr_warn("C-SKY does not support perf in guest mode!"); +		return; +	} + +	fr.fp = regs->regs[4]; +	fr.lr = regs->lr; +	walk_stackframe(&fr, entry); +} diff --git a/arch/csky/kernel/perf_regs.c b/arch/csky/kernel/perf_regs.c new file mode 100644 index 000000000000..eb32838b8210 --- /dev/null +++ b/arch/csky/kernel/perf_regs.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/perf_event.h> +#include <linux/bug.h> +#include <asm/perf_regs.h> +#include <asm/ptrace.h> + +u64 perf_reg_value(struct pt_regs *regs, int idx) +{ +	if (WARN_ON_ONCE((u32)idx >= PERF_REG_CSKY_MAX)) +		return 0; + +	return (u64)*((u32 *)regs + idx); +} + +#define REG_RESERVED (~((1ULL << PERF_REG_CSKY_MAX) - 1)) + +int perf_reg_validate(u64 mask) +{ +	if (!mask || mask & REG_RESERVED) +		return -EINVAL; + +	return 0; +} + +u64 perf_reg_abi(struct task_struct *task) +{ +	return PERF_SAMPLE_REGS_ABI_32; +} + +void perf_get_regs_user(struct perf_regs *regs_user, +			struct pt_regs *regs, +			struct pt_regs *regs_user_copy) +{ +	regs_user->regs = task_pt_regs(current); +	regs_user->abi = perf_reg_abi(current); +} diff --git a/arch/csky/kernel/ptrace.c b/arch/csky/kernel/ptrace.c index f2f12fff36f7..313623a19ecb 100644 --- a/arch/csky/kernel/ptrace.c +++ b/arch/csky/kernel/ptrace.c @@ -1,6 +1,7 @@  // SPDX-License-Identifier: GPL-2.0  // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. +#include <linux/audit.h>  #include <linux/elf.h>  #include <linux/errno.h>  #include <linux/kernel.h> @@ -11,6 +12,7 @@  #include <linux/sched/task_stack.h>  #include <linux/signal.h>  #include <linux/smp.h> +#include <linux/tracehook.h>  #include <linux/uaccess.h>  #include <linux/user.h> @@ -22,6 +24,9 @@  #include <abi/regdef.h> +#define CREATE_TRACE_POINTS +#include <trace/events/syscalls.h> +  /* sets the trace bits. */  #define TRACE_MODE_SI      (1 << 14)  #define TRACE_MODE_RUN     0 @@ -207,35 +212,27 @@ long arch_ptrace(struct task_struct *child, long request,  	return ret;  } -/* - * If process's system calls is traces, do some corresponding handles in this - * function before entering system call function and after exiting system call - * function. - */ -asmlinkage void syscall_trace(int why, struct pt_regs *regs) +asmlinkage void syscall_trace_enter(struct pt_regs *regs)  { -	long saved_why; -	/* -	 * Save saved_why, why is used to denote syscall entry/exit; -	 * why = 0:entry, why = 1: exit -	 */ -	saved_why = regs->regs[SYSTRACE_SAVENUM]; -	regs->regs[SYSTRACE_SAVENUM] = why; - -	ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) -					? 0x80 : 0)); - -	/* -	 * this isn't the same as continuing with a signal, but it will do -	 * for normal use.  strace only continues with a signal if the -	 * stopping signal is not SIGTRAP.  -brl -	 */ -	if (current->exit_code) { -		send_sig(current->exit_code, current, 1); -		current->exit_code = 0; -	} +	if (test_thread_flag(TIF_SYSCALL_TRACE)) +		if (tracehook_report_syscall_entry(regs)) +			syscall_set_nr(current, regs, -1); + +	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) +		trace_sys_enter(regs, syscall_get_nr(current, regs)); + +	audit_syscall_entry(regs_syscallid(regs), regs->a0, regs->a1, regs->a2, regs->a3); +} + +asmlinkage void syscall_trace_exit(struct pt_regs *regs) +{ +	audit_syscall_exit(regs); + +	if (test_thread_flag(TIF_SYSCALL_TRACE)) +		tracehook_report_syscall_exit(regs, 0); -	regs->regs[SYSTRACE_SAVENUM] = saved_why; +	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) +		trace_sys_exit(regs, syscall_get_return_value(current, regs));  }  extern void show_stack(struct task_struct *task, unsigned long *stack); diff --git a/arch/csky/kernel/setup.c b/arch/csky/kernel/setup.c index dff8b89444ec..23ee604aafdb 100644 --- a/arch/csky/kernel/setup.c +++ b/arch/csky/kernel/setup.c @@ -142,18 +142,24 @@ void __init setup_arch(char **cmdline_p)  #endif  } -asmlinkage __visible void __init csky_start(unsigned int unused, void *param) +unsigned long va_pa_offset; +EXPORT_SYMBOL(va_pa_offset); + +asmlinkage __visible void __init csky_start(unsigned int unused, +					    void *dtb_start)  {  	/* Clean up bss section */  	memset(__bss_start, 0, __bss_stop - __bss_start); +	va_pa_offset = read_mmu_msa0() & ~(SSEG_SIZE - 1); +  	pre_trap_init();  	pre_mmu_init(); -	if (param == NULL) +	if (dtb_start == NULL)  		early_init_dt_scan(__dtb_start);  	else -		early_init_dt_scan(param); +		early_init_dt_scan(dtb_start);  	start_kernel(); diff --git a/arch/csky/kernel/signal.c b/arch/csky/kernel/signal.c index 207a891479d2..04a43cfd4e09 100644 --- a/arch/csky/kernel/signal.c +++ b/arch/csky/kernel/signal.c @@ -1,26 +1,10 @@  // SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. -#include <linux/sched.h> -#include <linux/mm.h> -#include <linux/kernel.h>  #include <linux/signal.h> +#include <linux/uaccess.h>  #include <linux/syscalls.h> -#include <linux/errno.h> -#include <linux/wait.h> -#include <linux/ptrace.h> -#include <linux/unistd.h> -#include <linux/stddef.h> -#include <linux/highuid.h> -#include <linux/personality.h> -#include <linux/tty.h> -#include <linux/binfmts.h>  #include <linux/tracehook.h> -#include <linux/freezer.h> -#include <linux/uaccess.h> -#include <asm/setup.h> -#include <asm/pgtable.h>  #include <asm/traps.h>  #include <asm/ucontext.h>  #include <asm/vdso.h> @@ -29,110 +13,117 @@  #ifdef CONFIG_CPU_HAS_FPU  #include <abi/fpu.h> - -static int restore_fpu_state(struct sigcontext *sc) +static int restore_fpu_state(struct sigcontext __user *sc)  {  	int err = 0;  	struct user_fp user_fp; -	err = copy_from_user(&user_fp, &sc->sc_user_fp, sizeof(user_fp)); +	err = __copy_from_user(&user_fp, &sc->sc_user_fp, sizeof(user_fp));  	restore_from_user_fp(&user_fp);  	return err;  } -static int save_fpu_state(struct sigcontext *sc) +static int save_fpu_state(struct sigcontext __user *sc)  {  	struct user_fp user_fp;  	save_to_user_fp(&user_fp); -	return copy_to_user(&sc->sc_user_fp, &user_fp, sizeof(user_fp)); +	return __copy_to_user(&sc->sc_user_fp, &user_fp, sizeof(user_fp));  }  #else -static inline int restore_fpu_state(struct sigcontext *sc) { return 0; } -static inline int save_fpu_state(struct sigcontext *sc) { return 0; } +#define restore_fpu_state(sigcontext)	(0) +#define save_fpu_state(sigcontext)	(0)  #endif  struct rt_sigframe { -	int sig; -	struct siginfo *pinfo; -	void *puc;  	struct siginfo info;  	struct ucontext uc;  }; -static int -restore_sigframe(struct pt_regs *regs, -		 struct sigcontext *sc, int *pr2) +static long restore_sigcontext(struct pt_regs *regs, +	struct sigcontext __user *sc)  {  	int err = 0; -	/* Always make any pending restarted system calls return -EINTR */ -	current_thread_info()->task->restart_block.fn = do_no_restart_syscall; - -	err |= copy_from_user(regs, &sc->sc_pt_regs, sizeof(struct pt_regs)); +	/* sc_pt_regs is structured the same as the start of pt_regs */ +	err |= __copy_from_user(regs, &sc->sc_pt_regs, sizeof(struct pt_regs)); +	/* Restore the floating-point state. */  	err |= restore_fpu_state(sc); -	*pr2 = regs->a0;  	return err;  } -asmlinkage int -do_rt_sigreturn(void) +SYSCALL_DEFINE0(rt_sigreturn)  { -	sigset_t set; -	int a0;  	struct pt_regs *regs = current_pt_regs(); -	struct rt_sigframe *frame = (struct rt_sigframe *)(regs->usp); +	struct rt_sigframe __user *frame; +	struct task_struct *task; +	sigset_t set; + +	/* Always make any pending restarted system calls return -EINTR */ +	current->restart_block.fn = do_no_restart_syscall; + +	frame = (struct rt_sigframe __user *)regs->usp;  	if (!access_ok(frame, sizeof(*frame)))  		goto badframe; +  	if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))  		goto badframe; -	sigdelsetmask(&set, (sigmask(SIGKILL) | sigmask(SIGSTOP))); -	spin_lock_irq(¤t->sighand->siglock); -	current->blocked = set; -	recalc_sigpending(); -	spin_unlock_irq(¤t->sighand->siglock); +	set_current_blocked(&set); -	if (restore_sigframe(regs, &frame->uc.uc_mcontext, &a0)) +	if (restore_sigcontext(regs, &frame->uc.uc_mcontext))  		goto badframe; -	return a0; +	if (restore_altstack(&frame->uc.uc_stack)) +		goto badframe; + +	return regs->a0;  badframe: -	force_sig(SIGSEGV, current); +	task = current; +	force_sig(SIGSEGV, task);  	return 0;  } -static int setup_sigframe(struct sigcontext *sc, struct pt_regs *regs) +static int setup_sigcontext(struct rt_sigframe __user *frame, +	struct pt_regs *regs)  { +	struct sigcontext __user *sc = &frame->uc.uc_mcontext;  	int err = 0; -	err |= copy_to_user(&sc->sc_pt_regs, regs, sizeof(struct pt_regs)); +	err |= __copy_to_user(&sc->sc_pt_regs, regs, sizeof(struct pt_regs));  	err |= save_fpu_state(sc);  	return err;  } -static inline void * -get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) +static inline void __user *get_sigframe(struct ksignal *ksig, +	struct pt_regs *regs, size_t framesize)  { -	unsigned long usp; +	unsigned long sp; +	/* Default to using normal stack */ +	sp = regs->usp; + +	/* +	 * If we are on the alternate signal stack and would overflow it, don't. +	 * Return an always-bogus address instead so we will die with SIGSEGV. +	 */ +	if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize))) +		return (void __user __force *)(-1UL); -	/* Default to using normal stack.  */ -	usp = regs->usp; +	/* This is the X/Open sanctioned signal stack switching. */ +	sp = sigsp(sp, ksig) - framesize; -	/* This is the X/Open sanctioned signal stack switching.  */ -	if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(usp)) { -		if (!on_sig_stack(usp)) -			usp = current->sas_ss_sp + current->sas_ss_size; -	} -	return (void *)((usp - frame_size) & -8UL); +	/* Align the stack frame. */ +	sp &= -8UL; + +	return (void __user *)sp;  }  static int @@ -140,205 +131,128 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs)  {  	struct rt_sigframe *frame;  	int err = 0; -  	struct csky_vdso *vdso = current->mm->context.vdso; -	frame = get_sigframe(&ksig->ka, regs, sizeof(*frame)); -	if (!frame) -		return 1; +	frame = get_sigframe(ksig, regs, sizeof(*frame)); +	if (!access_ok(frame, sizeof(*frame))) +		return -EFAULT; -	err |= __put_user(ksig->sig, &frame->sig); -	err |= __put_user(&frame->info, &frame->pinfo); -	err |= __put_user(&frame->uc, &frame->puc);  	err |= copy_siginfo_to_user(&frame->info, &ksig->info); -	/* Create the ucontext.  */ +	/* Create the ucontext. */  	err |= __put_user(0, &frame->uc.uc_flags); -	err |= __put_user(0, &frame->uc.uc_link); -	err |= __put_user((void *)current->sas_ss_sp, -			&frame->uc.uc_stack.ss_sp); -	err |= __put_user(sas_ss_flags(regs->usp), -			&frame->uc.uc_stack.ss_flags); -	err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); -	err |= setup_sigframe(&frame->uc.uc_mcontext, regs); -	err |= copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); - +	err |= __put_user(NULL, &frame->uc.uc_link); +	err |= __save_altstack(&frame->uc.uc_stack, regs->usp); +	err |= setup_sigcontext(frame, regs); +	err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));  	if (err) -		goto give_sigsegv; +		return -EFAULT; -	/* Set up registers for signal handler */ -	regs->usp = (unsigned long)frame; -	regs->pc = (unsigned long)ksig->ka.sa.sa_handler; -	regs->lr = (unsigned long)vdso->rt_signal_retcode; +	/* Set up to return from userspace. */ +	regs->lr = (unsigned long)(vdso->rt_signal_retcode); -adjust_stack: -	regs->a0 = ksig->sig; /* first arg is signo */ -	regs->a1 = (unsigned long)(&(frame->info)); -	regs->a2 = (unsigned long)(&(frame->uc)); -	return err; +	/* +	 * Set up registers for signal handler. +	 * Registers that we don't modify keep the value they had from +	 * user-space at the time we took the signal. +	 * We always pass siginfo and mcontext, regardless of SA_SIGINFO, +	 * since some things rely on this (e.g. glibc's debug/segfault.c). +	 */ +	regs->pc  = (unsigned long)ksig->ka.sa.sa_handler; +	regs->usp = (unsigned long)frame; +	regs->a0  = ksig->sig;				/* a0: signal number */ +	regs->a1  = (unsigned long)(&(frame->info));	/* a1: siginfo pointer */ +	regs->a2  = (unsigned long)(&(frame->uc));	/* a2: ucontext pointer */ -give_sigsegv: -	if (ksig->sig == SIGSEGV) -		ksig->ka.sa.sa_handler = SIG_DFL; -	force_sig(SIGSEGV, current); -	goto adjust_stack; +	return 0;  } -/* - * OK, we're invoking a handler - */ -static int -handle_signal(struct ksignal *ksig, struct pt_regs *regs) +static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)  { -	int ret;  	sigset_t *oldset = sigmask_to_save(); +	int ret; -	/* -	 * set up the stack frame, regardless of SA_SIGINFO, -	 * and pass info anyway. -	 */ -	ret = setup_rt_frame(ksig, oldset, regs); +	/* Are we from a system call? */ +	if (in_syscall(regs)) { +		/* Avoid additional syscall restarting via ret_from_exception */ +		forget_syscall(regs); + +		/* If so, check system call restarting.. */ +		switch (regs->a0) { +		case -ERESTART_RESTARTBLOCK: +		case -ERESTARTNOHAND: +			regs->a0 = -EINTR; +			break; -	if (ret != 0) { -		force_sigsegv(ksig->sig, current); -		return ret; +		case -ERESTARTSYS: +			if (!(ksig->ka.sa.sa_flags & SA_RESTART)) { +				regs->a0 = -EINTR; +				break; +			} +			/* fallthrough */ +		case -ERESTARTNOINTR: +			regs->a0 = regs->orig_a0; +			regs->pc -= TRAP0_SIZE; +			break; +		}  	} -	/* Block the signal if we were successful. */ -	spin_lock_irq(¤t->sighand->siglock); -	sigorsets(¤t->blocked, ¤t->blocked, &ksig->ka.sa.sa_mask); -	if (!(ksig->ka.sa.sa_flags & SA_NODEFER)) -		sigaddset(¤t->blocked, ksig->sig); -	recalc_sigpending(); -	spin_unlock_irq(¤t->sighand->siglock); +	/* Set up the stack frame */ +	ret = setup_rt_frame(ksig, oldset, regs); -	return 0; +	signal_setup_done(ret, ksig, 0);  } -/* - * Note that 'init' is a special process: it doesn't get signals it doesn't - * want to handle. Thus you cannot kill init even with a SIGKILL even by - * mistake. - * - * Note that we go through the signals twice: once to check the signals - * that the kernel can handle, and then we build all the user-level signal - * handling stack-frames in one go after that. - */ -static void do_signal(struct pt_regs *regs, int syscall) +static void do_signal(struct pt_regs *regs)  { -	unsigned int retval = 0, continue_addr = 0, restart_addr = 0;  	struct ksignal ksig; -	/* -	 * We want the common case to go fast, which -	 * is why we may in certain cases get here from -	 * kernel mode. Just return without doing anything -	 * if so. -	 */ -	if (!user_mode(regs)) +	if (get_signal(&ksig)) { +		/* Actually deliver the signal */ +		handle_signal(&ksig, regs);  		return; +	} -	/* -	 * If we were from a system call, check for system call restarting... -	 */ -	if (syscall) { -		continue_addr = regs->pc; -#if defined(__CSKYABIV2__) -		restart_addr = continue_addr - 4; -#else -		restart_addr = continue_addr - 2; -#endif -		retval = regs->a0; +	/* Did we come from a system call? */ +	if (in_syscall(regs)) { +		/* Avoid additional syscall restarting via ret_from_exception */ +		forget_syscall(regs); -		/* -		 * Prepare for system call restart.  We do this here so that a -		 * debugger will see the already changed. -		 */ -		switch (retval) { +		/* Restart the system call - no handlers present */ +		switch (regs->a0) {  		case -ERESTARTNOHAND:  		case -ERESTARTSYS:  		case -ERESTARTNOINTR:  			regs->a0 = regs->orig_a0; -			regs->pc = restart_addr; +			regs->pc -= TRAP0_SIZE;  			break;  		case -ERESTART_RESTARTBLOCK: -			regs->a0 = -EINTR; +			regs->a0 = regs->orig_a0; +			regs_syscallid(regs) = __NR_restart_syscall; +			regs->pc -= TRAP0_SIZE;  			break;  		}  	} -	if (try_to_freeze()) -		goto no_signal; -  	/* -	 * Get the signal to deliver.  When running under ptrace, at this -	 * point the debugger may change all our registers ... +	 * If there is no signal to deliver, we just put the saved +	 * sigmask back.  	 */ -	if (get_signal(&ksig)) { -		/* -		 * Depending on the signal settings we may need to revert the -		 * decision to restart the system call.  But skip this if a -		 * debugger has chosen to restart at a different PC. -		 */ -		if (regs->pc == restart_addr) { -			if (retval == -ERESTARTNOHAND || -			    (retval == -ERESTARTSYS && -			     !(ksig.ka.sa.sa_flags & SA_RESTART))) { -				regs->a0 = -EINTR; -				regs->pc = continue_addr; -			} -		} - -		/* Whee!  Actually deliver the signal.  */ -		if (handle_signal(&ksig, regs) == 0) { -			/* -			 * A signal was successfully delivered; the saved -			 * sigmask will have been stored in the signal frame, -			 * and will be restored by sigreturn, so we can simply -			 * clear the TIF_RESTORE_SIGMASK flag. -			 */ -			if (test_thread_flag(TIF_RESTORE_SIGMASK)) -				clear_thread_flag(TIF_RESTORE_SIGMASK); -		} -		return; -	} - -no_signal: -	if (syscall) { -		/* -		 * Handle restarting a different system call.  As above, -		 * if a debugger has chosen to restart at a different PC, -		 * ignore the restart. -		 */ -		if (retval == -ERESTART_RESTARTBLOCK -				&& regs->pc == continue_addr) { -#if defined(__CSKYABIV2__) -			regs->regs[3] = __NR_restart_syscall; -			regs->pc -= 4; -#else -			regs->regs[9] = __NR_restart_syscall; -			regs->pc -= 2; -#endif -		} - -		/* -		 * If there's no signal to deliver, we just put the saved -		 * sigmask back. -		 */ -		if (test_thread_flag(TIF_RESTORE_SIGMASK)) { -			clear_thread_flag(TIF_RESTORE_SIGMASK); -			sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); -		} -	} +	restore_saved_sigmask();  } -asmlinkage void -do_notify_resume(unsigned int thread_flags, struct pt_regs *regs, int syscall) +/* + * notification of userspace execution resumption + * - triggered by the _TIF_WORK_MASK flags + */ +asmlinkage void do_notify_resume(struct pt_regs *regs, +	unsigned long thread_info_flags)  { -	if (thread_flags & _TIF_SIGPENDING) -		do_signal(regs, syscall); +	/* Handle pending signal delivery */ +	if (thread_info_flags & _TIF_SIGPENDING) +		do_signal(regs); -	if (thread_flags & _TIF_NOTIFY_RESUME) { +	if (thread_info_flags & _TIF_NOTIFY_RESUME) {  		clear_thread_flag(TIF_NOTIFY_RESUME);  		tracehook_notify_resume(regs);  	} |