Commit 1a8e5712 authored by Mao Minkai's avatar Mao Minkai Committed by guzitao
Browse files

sw64: rewrite syscall invocation in C

Sunway inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I8CCP8



--------------------------------

Rewrite syscall invocation in C, leave only the minimum required
assembly code in entry.S, just like interrupts and exceptions.

Signed-off-by: default avatarMao Minkai <maominkai@wxiat.com>
Reviewed-by: default avatarHe Sheng <hesheng@wxiat.com>
Signed-off-by: default avatarGu Zitao <guzitao@wxiat.com>
parent 985e00fe
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -50,6 +50,9 @@ struct pt_regs {

extern short regoffsets[];

extern unsigned long syscall_trace_enter(void);
extern void syscall_trace_leave(void);

/**
 * regs_get_register() - get register value from its offset
 * @regs:       pt_regs from which register value is gotten
+8 −1
Original line number Diff line number Diff line
@@ -4,7 +4,12 @@

#include <uapi/linux/audit.h>

extern void *sys_call_table[];
#ifndef __ASSEMBLY__

typedef long (*syscall_fn_t)(ulong, ulong, ulong, ulong, ulong, ulong);

extern syscall_fn_t sys_call_table[];

static inline int syscall_get_nr(struct task_struct *task,
				 struct pt_regs *regs)
{
@@ -72,4 +77,6 @@ static inline int syscall_get_arch(struct task_struct *task)
	return AUDIT_ARCH_SW64;
}

#endif /* !__ASSEMBLY__ */

#endif /* _ASM_SW64_SYSCALL_H */
+11 −158
Original line number Diff line number Diff line
@@ -199,40 +199,20 @@ entUna:

	.align 4
	.globl entSys
	.globl ret_from_sys_call
	.ent entSys
entSys:

	SAVE_ALL
	ldl	$0, PT_REGS_R0($sp)
	stl	$0, PT_REGS_ORIG_R0($sp)
	stl	$19, PT_REGS_ORIG_R19($sp)
	ldi	$4, NR_SYSCALLS($31)
	stl	$16, PT_REGS_R16($sp)
	ldi	$5, sys_call_table
	ldi	$27, sys_ni_syscall
	cmpult	$0, $4, $4
	ldw	$3, TI_FLAGS($8)
	stl	$17, PT_REGS_R17($sp)
	s8addl	$0, $5, $5
	stl	$18, PT_REGS_R18($sp)
	ldi	$6, _TIF_SYSCALL_WORK
	and	$3, $6, $3
	bne	$3, strace

	beq	$4, 1f
	ldl	$27, 0($5)
1:	call	$26, ($27), ni_syscall
	ldgp	$gp, 0($26)
	blt	$0, $syscall_error	/* the call failed */

	ldi	$1, NO_SYSCALL
	stl	$1, PT_REGS_ORIG_R0($sp)
$syscall_success:
	stl	$0, PT_REGS_R0($sp)
	stl	$31, PT_REGS_R19($sp)	/* a3=0 => no error */
	mov	$sp, $16
	call	$26, do_entSys
	br	ret_from_sys_call
	.end entSys

	.align 4
	.globl ret_from_sys_call
	.ent ret_from_sys_call
ret_from_sys_call:
#ifdef CONFIG_SUBARCH_C3B
	fillcs	0($sp)			/* prefetch */
@@ -240,18 +220,14 @@ ret_from_sys_call:
#endif
	br	$27, 1f
1:	ldgp	$29, 0($27)
	ldl	$0, PT_REGS_PS($sp)
	and	$0, 8, $0
	beq	$0, ret_to_kernel
ret_to_user:
#ifdef CONFIG_DEBUG_RSEQ
	mov	$sp, $16
	call	$26, rseq_syscall
#endif
	/* Make sure need_resched and sigpending don't change between
		sampling and the rti.  */
	ldi	$16, 7
	sys_call HMC_swpipl
	ldl	$0, PT_REGS_PS($sp)
	and	$0, 8, $0
	beq	$0, restore_all
ret_to_user:
	ldw	$17, TI_FLAGS($8)
	and	$17, _TIF_WORK_MASK, $2
	beq	$2, restore_all
@@ -260,89 +236,7 @@ ret_to_user:
restore_all:
	RESTORE_ALL
	sys_call HMC_rti

ret_to_kernel:
	ldi	$16, 7
	sys_call HMC_swpipl
	br restore_all

	.align 3
$syscall_error:
	/*
	 * Some system calls (e.g., ptrace) can return arbitrary
	 * values which might normally be mistaken as error numbers.
	 * Those functions must zero $0 (v0) directly in the stack
	 * frame to indicate that a negative return value wasn't an
	 * error number..
	 */
	ldl	$1, PT_REGS_ORIG_R0($sp)	/* old syscall nr (-1 if success) */
	blt	$1, $syscall_success

	subl	$31, $0, $0	/* with error in v0 */
	addl	$31, 1, $1	/* set a3 for errno return */
	stl	$0, PT_REGS_R0($sp)
	stl	$1, PT_REGS_R19($sp)	/* a3 for return */
	br	ret_from_sys_call
	.end entSys

/*
 * PTRACE syscall handler
 */

	.align 4
	.ent strace
strace:
	/* set up signal stack, call syscall_trace */
	call	$26, syscall_trace_enter
	blt	$0, $syscall_trace_failed

	/* get the system call number and the arguments back.. */
	stl	$0, PT_REGS_ORIG_R0($sp)
	ldl	$16, PT_REGS_R16($sp)
	ldl	$17, PT_REGS_R17($sp)
	ldl	$18, PT_REGS_R18($sp)
	ldl	$19, PT_REGS_R19($sp)
	ldl	$20, PT_REGS_R20($sp)
	ldl	$21, PT_REGS_R21($sp)

	/* get the system call pointer.. */
	ldi	$1, NR_SYSCALLS($31)
	ldi	$2, sys_call_table
	ldi	$27, ni_syscall

	cmpult	$0, $1, $1
	s8addl	$0, $2, $2
	beq	$1, 1f
	ldl	$27, 0($2)
1:	call	$26, ($27), sys_gettimeofday
ret_from_straced:
	ldgp	$gp, 0($26)

	/* check return.. */
	blt	$0, $strace_error	/* the call failed */

	ldi	$1, NO_SYSCALL
	stl	$1, PT_REGS_ORIG_R0($sp)
$strace_success:
	stl	$0, PT_REGS_R0($sp)	/* save return value */
	stl	$31, PT_REGS_R19($sp)	/* a3=0 => no error */
	call	$26, syscall_trace_leave
	br	ret_from_sys_call

	.align 3
$strace_error:
	ldl	$1, PT_REGS_ORIG_R0($sp)	/* old syscall nr (-1 if success) */
	blt	$1, $strace_success

	subl	$31, $0, $0	/* with error in v0 */
	addl	$31, 1, $1	/* set a3 for errno return */
	stl	$0, PT_REGS_R0($sp)
	stl	$1, PT_REGS_R19($sp)	/* a3 for return */

$syscall_trace_failed:
	call	$26, syscall_trace_leave
	br	ret_from_sys_call
	.end strace
	.end ret_from_sys_call

/*
 * Integer register context switch
@@ -410,44 +304,3 @@ ret_from_kernel_thread:
	call	$26, ($9)
	br	ret_to_user
	.end ret_from_kernel_thread

	.align 4
	.globl sys_sigreturn
	.ent sys_sigreturn
sys_sigreturn:
	.prologue 0
	ldi	$9, ret_from_straced
	cmpult	$26, $9, $9
	call	$26, do_sigreturn
	bne	$9, 1f
	call	$26, syscall_trace_leave
1:	br	ret_from_sys_call
	.end sys_sigreturn

	.align 4
	.globl sys_rt_sigreturn
	.ent sys_rt_sigreturn
sys_rt_sigreturn:
	.prologue 0
	ldi	$9, ret_from_straced
	cmpult	$26, $9, $9
	call	$26, do_rt_sigreturn
	bne	$9, 1f
	call	$26, syscall_trace_leave
1:	br	ret_from_sys_call
	.end sys_rt_sigreturn

	.align 4
	.globl ni_syscall
	.ent ni_syscall
ni_syscall:
	.prologue 0
	/* Special because it also implements overflow handling via
	 * syscall number 0.  And if you recall, zero is a special
	 * trigger for "not an error".  Store large non-zero there.
	 */
	ldi	$0, -ENOSYS
	unop
	stl	$0, PT_REGS_R0($sp)
	ret
	.end ni_syscall
+6 −4
Original line number Diff line number Diff line
@@ -114,7 +114,7 @@ restore_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs)
 * registers and transfer control from userland.
 */

asmlinkage void do_sigreturn(void)
SYSCALL_DEFINE0(sigreturn)
{
	struct pt_regs *regs = current_pt_regs();
	struct sigcontext __user *sc;
@@ -140,13 +140,14 @@ asmlinkage void do_sigreturn(void)
		force_sig_fault(SIGTRAP, TRAP_BRKPT,
				(void __user *)regs->pc);
	}
	return;
	return regs->regs[0];

give_sigsegv:
	force_sig(SIGSEGV);
	return 0;
}

asmlinkage void do_rt_sigreturn(void)
SYSCALL_DEFINE0(rt_sigreturn)
{
	struct pt_regs *regs = current_pt_regs();
	struct rt_sigframe __user *frame;
@@ -175,10 +176,11 @@ asmlinkage void do_rt_sigreturn(void)
		force_sig_fault(SIGTRAP, TRAP_BRKPT,
				(void __user *)regs->pc);
	}
	return;
	return regs->regs[0];

give_sigsegv:
	force_sig(SIGSEGV);
	return 0;
}


+42 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/sched/debug.h>
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/syscalls.h>

#include <asm/gentrap.h>
#include <asm/mmu_context.h>
@@ -30,6 +31,8 @@
#include <asm/ptrace.h>
#include <asm/debug.h>
#include <asm/efi.h>
#include <asm/syscall.h>
#include <asm/unistd.h>

#include "proto.h"

@@ -1481,6 +1484,45 @@ do_entUnaUser(void __user *va, unsigned long opcode,
	force_sig_fault(SIGBUS, BUS_ADRALN, va);
}

asmlinkage void do_entSys(struct pt_regs *regs)
{
	long ret = -ENOSYS;
	unsigned long nr;
	unsigned long ti_flags = current_thread_info()->flags;

	regs->orig_r0 = regs->regs[0];
	regs->orig_r19 = regs->regs[19];
	nr = regs->regs[0];

	if (ti_flags & _TIF_SYSCALL_WORK) {
		nr = syscall_trace_enter();
		if (nr == NO_SYSCALL)
			goto syscall_out;
		regs->orig_r0 = regs->regs[0];
		regs->orig_r19 = regs->regs[19];
	}

	if (nr < __NR_syscalls) {
		syscall_fn_t syscall_fn = sys_call_table[nr];

		ret = syscall_fn(regs->regs[16], regs->regs[17], regs->regs[18],
				regs->regs[19], regs->regs[20], regs->regs[21]);
	}

	if ((nr != __NR_sigreturn) && (nr != __NR_rt_sigreturn)) {
		if (likely((ret >= 0) || regs->orig_r0 == NO_SYSCALL))
			syscall_set_return_value(current, regs, 0, ret);
		else
			syscall_set_return_value(current, regs, ret, 0);
	}

syscall_out:
	rseq_syscall(regs);

	if (ti_flags & _TIF_SYSCALL_WORK)
		syscall_trace_leave();
}

void
trap_init(void)
{