[PATCH 02/19] csky: Exception handling and syscall

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Guo Ren <ren_guo@xxxxxxxxx>
---
 arch/csky/abiv1/inc/abi/entry.h     | 171 ++++++++++++
 arch/csky/abiv1/src/alignment.c     | 513 ++++++++++++++++++++++++++++++++++++
 arch/csky/abiv2/inc/abi/entry.h     | 154 +++++++++++
 arch/csky/include/asm/syscalls.h    |  14 +
 arch/csky/include/asm/traps.h       |  39 +++
 arch/csky/include/asm/unistd.h      |   4 +
 arch/csky/include/uapi/asm/unistd.h | 100 +++++++
 arch/csky/kernel/cpu-probe.c        |  63 +++++
 arch/csky/kernel/entry.S            | 408 ++++++++++++++++++++++++++++
 arch/csky/kernel/syscall.c          |  65 +++++
 arch/csky/kernel/syscall_table.c    |  12 +
 arch/csky/kernel/traps.c            | 152 +++++++++++
 arch/csky/mm/fault.c                | 246 +++++++++++++++++
 13 files changed, 1941 insertions(+)
 create mode 100644 arch/csky/abiv1/inc/abi/entry.h
 create mode 100644 arch/csky/abiv1/src/alignment.c
 create mode 100644 arch/csky/abiv2/inc/abi/entry.h
 create mode 100644 arch/csky/include/asm/syscalls.h
 create mode 100644 arch/csky/include/asm/traps.h
 create mode 100644 arch/csky/include/asm/unistd.h
 create mode 100644 arch/csky/include/uapi/asm/unistd.h
 create mode 100644 arch/csky/kernel/cpu-probe.c
 create mode 100644 arch/csky/kernel/entry.S
 create mode 100644 arch/csky/kernel/syscall.c
 create mode 100644 arch/csky/kernel/syscall_table.c
 create mode 100644 arch/csky/kernel/traps.c
 create mode 100644 arch/csky/mm/fault.c

diff --git a/arch/csky/abiv1/inc/abi/entry.h b/arch/csky/abiv1/inc/abi/entry.h
new file mode 100644
index 0000000..bff3ff2
--- /dev/null
+++ b/arch/csky/abiv1/inc/abi/entry.h
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
+#ifndef __ASM_CSKY_ENTRY_H
+#define __ASM_CSKY_ENTRY_H
+
+#include <asm/setup.h>
+#include <abi/regdef.h>
+
+/*
+ * Stack layout for exception (sp=r0):
+ *
+ *	 0(sp) - pc
+ *	 4(sp) - orig_a0
+ *	 8(sp) - sr
+ *	 C(sp) - a0/r2
+ *	10(sp) - a1/r3
+ *	14(sp) - a2/r4
+ *	18(sp) - a3/r5
+ *	1C(sp) - r6
+ *	20(sp) - r7
+ *	24(sp) - r8
+ *	28(sp) - r9
+ *	2C(sp) - r10
+ *	30(sp) - r11
+ *	34(sp) - r12
+ *	38(sp) - r13
+ *	3C(sp) - r14
+ *	40(sp) - r1
+ *	44(sp) - r15
+ */
+
+#define LSAVE_A0	0xc
+#define LSAVE_A1	0x10
+#define LSAVE_A2	0x14
+#define LSAVE_A3	0x18
+#define LSAVE_A4	0x1C
+#define LSAVE_A5	0x20
+
+.macro USPTOKSP
+	mtcr	sp, ss1
+	mfcr	sp, ss0
+.endm
+
+.macro KSPTOUSP
+	mtcr	sp, ss0
+	mfcr	sp, ss1
+.endm
+
+.macro GET_USP rx
+	mfcr	\rx, ss1
+.endm
+
+.macro SET_USP rx
+	mtcr	\rx, ss1
+.endm
+
+.macro INCTRAP	rx
+	addi	\rx, 2
+.endm
+
+/*
+ * SAVE_ALL: save the pt_regs to the stack.
+ */
+.macro	SAVE_ALL
+	mtcr    r13, ss2
+	mfcr    r13, epsr
+	btsti   r13, 31
+	bt      1f
+	USPTOKSP
+1:
+	subi    sp, 32
+	subi    sp, 32
+	stw     r13,	(sp, 0)
+	mfcr    r13,	ss2
+	stw     a0,	(sp, 4)
+	stw     a1,	(sp, 8)
+	stw     a2,	(sp, 12)
+	stw     a3,	(sp, 16)
+	stw     r6,	(sp, 20)
+	stw     r7,	(sp, 24)
+	stw     r8,	(sp, 28)
+	stw     r9,	(sp, 32)
+	stw     r10,	(sp, 36)
+	stw     r11,	(sp, 40)
+	stw     r12,	(sp, 44)
+	stw     r13,	(sp, 48)
+	stw     r14,	(sp, 52)
+	stw     r1,	(sp, 56)
+	stw     r15,	(sp, 60)
+
+	subi    sp,	8
+	stw     a0,	(sp, 4)
+	mfcr    r13,	epc
+	stw     r13,	(sp)
+.endm
+
+.macro SAVE_ALL_TRAP
+	SAVE_ALL
+	INCTRAP r13
+	stw     r13,	(sp)
+.endm
+
+.macro	RESTORE_ALL
+	psrclr  ie
+	ldw     a0, (sp)
+	mtcr    a0, epc
+	ldw     a0, (sp, 8)
+	mtcr    a0, epsr
+	btsti   a0, 31
+
+	addi    sp, 12
+	ldw     a0, (sp, 0)
+	ldw     a1, (sp, 4)
+	ldw     a2, (sp, 8)
+	ldw     a3, (sp, 12)
+	ldw     r6, (sp, 16)
+	ldw     r7, (sp, 20)
+	ldw     r8, (sp, 24)
+	ldw     r9, (sp, 28)
+	ldw     r10, (sp, 32)
+	ldw     r11, (sp, 36)
+	ldw     r12, (sp, 40)
+	ldw     r13, (sp, 44)
+	ldw     r14, (sp, 48)
+	ldw     r1, (sp, 52)
+	ldw     r15, (sp, 56)
+	addi    sp, 32
+	addi    sp, 28
+
+	bt      1f
+	KSPTOUSP
+1:
+	rte
+.endm
+
+.macro SAVE_SWITCH_STACK
+	subi    sp, 32
+	stm     r8-r15,(sp)
+.endm
+
+.macro RESTORE_SWITCH_STACK
+        ldm     r8-r15,(sp)
+        addi    sp, 32
+.endm
+
+/* MMU registers operators. */
+.macro RD_MIR	rx
+	cprcr   \rx, cpcr0
+.endm
+
+.macro RD_MEH	rx
+	cprcr   \rx, cpcr4
+.endm
+
+.macro RD_MCIR	rx
+	cprcr   \rx, cpcr8
+.endm
+
+.macro RD_PGDR  rx
+        cprcr   \rx, cpcr29
+.endm
+
+.macro WR_MEH	rx
+	cpwcr   \rx, cpcr4
+.endm
+
+.macro WR_MCIR	rx
+	cpwcr   \rx, cpcr8
+.endm
+
+#endif /* __ASM_CSKY_ENTRY_H */
diff --git a/arch/csky/abiv1/src/alignment.c b/arch/csky/abiv1/src/alignment.c
new file mode 100644
index 0000000..436c389
--- /dev/null
+++ b/arch/csky/abiv1/src/alignment.c
@@ -0,0 +1,513 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/ptrace.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+
+#include <asm/siginfo.h>
+#include <asm/unaligned.h>
+
+extern void die_if_kernel(char *, struct pt_regs *, long);
+
+#define HANDLER_SUCCESS 0
+#define HANDLER_FAILURE	1
+#define SP_NUM 0
+#define R4_NUM 4
+#define R15_NUM 4
+#define R16_NUM 16
+#define R28_NUM 28
+
+#define CODING_BITS(i)  (i & 0xFC000000)
+#define LDST_TYPE(i)    (i & 0xf000)
+
+static unsigned long ai_user;
+static unsigned long ai_sys;
+static unsigned long ai_skipped;
+static unsigned long ai_half;
+static unsigned long ai_word;
+static unsigned long ai_qword;
+static int ai_usermode;
+
+#define UM_WARN		(1 << 0)
+#define UM_FIXUP	(1 << 1)
+#define UM_SIGNAL	(1 << 2)
+
+static const char *usermode_action[] = {
+	"ignored",
+	"warn",
+	"fixup",
+	"fixup+warn",
+	"signal",
+	"signal+warn"
+};
+
+static int alignment_proc_show(struct seq_file *m, void *v)
+{
+	seq_printf(m, "User:\t\t%lu\n", ai_user);
+	seq_printf(m, "System:\t\t%lu\n", ai_sys);
+	seq_printf(m, "Skipped:\t%lu\n", ai_skipped);
+	seq_printf(m, "Half:\t\t%lu\n", ai_half);
+	seq_printf(m, "Word:\t\t%lu\n", ai_word);
+	seq_printf(m, "Qword:\t\t%lu\n", ai_qword);
+	seq_printf(m, "User faults:\t%i (%s)\n", ai_usermode,
+			usermode_action[ai_usermode]);
+
+	return 0;
+}
+
+static int alignment_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, alignment_proc_show, NULL);
+}
+
+static int proc_alignment_write(struct file *file, const char __user *buffer,
+				size_t count, loff_t *pos)
+{
+	char mode;
+
+	if (count > 0) {
+		if (get_user(mode, buffer))
+			return -EFAULT;
+		if (mode >= '0' && mode <= '5')
+			ai_usermode = mode - '0';
+	}
+	return count;
+}
+
+static const struct file_operations alignment_proc_fops = {
+	.open		= alignment_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+	.write		= proc_alignment_write,
+};
+
+#ifdef  __cskyBE__
+#define BE		1
+#define FIRST_BYTE_16	"rotri	%1, 8\n"
+#define FIRST_BYTE_32	"rotri	%1, 24\n"
+#define NEXT_BYTE	"rotri  %1, 24\n"
+#else
+#define BE		0
+#define FIRST_BYTE_16
+#define FIRST_BYTE_32
+#define NEXT_BYTE	"lsri   %1, 8\n"
+#endif
+
+#define __get8_unaligned_check(val,addr,err)		\
+	asm(					\
+	"1:	ldb	%1, (%2)\n"			\
+	"	addi	%2, 1\n"			\
+	"	br	3f\n"				\
+	"2:	movi	%0, 1\n"			\
+	"	br	3f\n"				\
+	"	.section __ex_table,\"a\"\n"		\
+	"	.align	2\n"				\
+	"	.long	1b, 2b\n"			\
+	"	.previous\n"				\
+	"3:\n"						\
+	: "=r" (err), "=r" (val), "=r" (addr)		\
+	: "0" (err), "2" (addr))
+
+#define get16_unaligned_check(val,addr)				\
+	do {							\
+		unsigned int err = 0, v, a = addr;		\
+		__get8_unaligned_check(v,a,err);		\
+		val =  v << ((BE) ? 8 : 0);			\
+		__get8_unaligned_check(v,a,err);		\
+		val |= v << ((BE) ? 0 : 8);			\
+		if (err)					\
+			goto fault;				\
+	} while (0)
+
+#define get32_unaligned_check(val,addr)				\
+	do {							\
+		unsigned int err = 0, v, a = addr;		\
+		__get8_unaligned_check(v,a,err);		\
+		val =  v << ((BE) ? 24 :  0);			\
+		__get8_unaligned_check(v,a,err);		\
+		val |= v << ((BE) ? 16 :  8);			\
+		__get8_unaligned_check(v,a,err);		\
+		val |= v << ((BE) ?  8 : 16);			\
+		__get8_unaligned_check(v,a,err);		\
+		val |= v << ((BE) ?  0 : 24);			\
+		if (err)					\
+			goto fault;				\
+	} while (0)
+
+#define put16_unaligned_check(val,addr)				\
+	do {							\
+		unsigned int err = 0, v = val, a = addr;	\
+		asm( FIRST_BYTE_16				\
+		"1:	stb	%1, (%2)\n"			\
+		"	addi	%2, 1\n"			\
+			NEXT_BYTE				\
+		"2:	stb	%1, (%2)\n"			\
+		"	br	4f\n"				\
+		"3:	movi	%0, 1\n"			\
+		"	br	4f\n"				\
+		"	.section __ex_table,\"a\"\n"		\
+		"	.align	2\n"				\
+		"	.long	1b, 3b\n"			\
+		"	.long	2b, 3b\n"			\
+		"	.previous\n"				\
+		"4:\n"						\
+		: "=r" (err), "=r" (v), "=r" (a)		\
+		: "0" (err), "1" (v), "2" (a));			\
+		if (err)					\
+			goto fault;				\
+	} while (0)
+
+#define put32_unaligned_check(val,addr)				\
+	do {							\
+		unsigned int err = 0, v = val, a = addr;	\
+		asm( FIRST_BYTE_32				\
+		"1:	stb	%1, (%2)\n"			\
+		"	addi	%2, 1\n"			\
+			NEXT_BYTE				\
+		"2:	stb	%1, (%2)\n"			\
+		"	addi	%2, 1\n"			\
+			NEXT_BYTE				\
+		"3:	stb	%1, (%2)\n"			\
+		"	addi	%2, 1\n"			\
+			NEXT_BYTE				\
+		"4:	stb	%1, (%2)\n"			\
+		"	br	6f\n"				\
+		"5:	movi	%0, 1\n"			\
+		"	br	6f\n"				\
+		"	.section __ex_table,\"a\"\n"		\
+		"	.align	2\n"				\
+		"	.long	1b, 5b\n"			\
+		"	.long	2b, 5b\n"			\
+		"	.long	3b, 5b\n"			\
+		"	.long	4b, 5b\n"			\
+		"	.previous\n"				\
+		"6:\n"						\
+		: "=r" (err), "=r" (v), "=r" (a)		\
+		: "0" (err), "1" (v), "2" (a));			\
+		if (err)					\
+			goto fault;				\
+	} while (0)
+
+inline static unsigned int
+get_regs_value(unsigned int rx, struct pt_regs *regs)
+{
+	unsigned int value;
+
+	if(rx == 0){
+		if(user_mode(regs)){
+			asm volatile("mfcr %0, ss1\n":"=r"(value));
+		}else{
+			value = sizeof(struct pt_regs) + ((unsigned int)regs);
+		}
+	}else if(rx == 1){
+		value = regs->regs[9];
+	}else if(rx == 15){
+		value = regs->r15;
+	}else{
+		value = *((int *)regs + rx + 1);
+	}
+
+	return value;
+}
+
+inline static int
+put_regs_value(unsigned int value, unsigned int rx, struct pt_regs *regs){
+	if(rx == 0){
+		printk("alignment handler trying to write sp.\n");
+		goto fault;
+	}else if(rx == 1){
+		regs->regs[9] = value;
+	}else if(rx == 15){
+		regs->r15 = value;
+	}else{
+		*((int *)regs + rx + 1) = value;
+	}
+	return 0;
+fault:
+	return 1;
+}
+
+static int
+handle_ldh_ldw_v1(unsigned long instr, struct pt_regs *regs){
+	unsigned int regx = instr & 0xf;
+	unsigned int regz = (instr >> 8) & 0xf;
+	unsigned int imm4 = (instr >> 4) & 0xf;
+	unsigned int destaddr, ldh_ldw;
+	unsigned int dataregx, tmpval32;
+	unsigned short tmpval16;
+
+	dataregx = get_regs_value(regx, regs);
+
+	ldh_ldw = instr & 0x6000;
+	if(ldh_ldw == 0x4000){ // ldh
+		destaddr = dataregx + (imm4 << 1);
+		get16_unaligned_check(tmpval16, destaddr);
+		if(put_regs_value((unsigned int)tmpval16, regz, regs) != 0){
+			goto fault;
+		}
+		ai_half += 1;
+	}else if(ldh_ldw == 0x0000){ // ldw
+		destaddr = dataregx + (imm4 << 2);
+		get32_unaligned_check(tmpval32, destaddr);
+		if(put_regs_value(tmpval32, regz, regs) != 0){
+			goto fault;
+		}
+		ai_word += 1;
+	}else{
+		goto fault;
+	}
+
+	return HANDLER_SUCCESS;
+fault:
+	return HANDLER_FAILURE;
+}
+
+static int
+handle_ldm_v1(unsigned long instr, struct pt_regs *regs){
+	unsigned int regf = instr & 0xf;
+	unsigned int datasp;
+	unsigned int tmpval32, i;
+
+	// regf can not be r0 or r15.
+	if(regf == 0 || regf == 15){
+		goto fault;
+	}
+
+	datasp = get_regs_value(SP_NUM, regs);
+	for(i = regf; i <= R15_NUM; i++){
+		get32_unaligned_check(tmpval32, datasp + (i - regf) * 4);
+		if(put_regs_value(tmpval32, i, regs) != 0){
+			goto fault;
+		}
+	}
+	ai_qword += 1;
+
+	return HANDLER_SUCCESS;
+fault:
+	return HANDLER_FAILURE;
+}
+
+static int
+handle_ldq_v1(unsigned long instr, struct pt_regs *regs){
+	unsigned int regf = instr & 0xf;
+	unsigned int datarf;
+	unsigned int tmpval32, i;
+
+	// regf can not be r4 - r7.
+	if(regf > 3 && regf < 8){
+		goto fault;
+	}
+
+	datarf = get_regs_value(regf, regs);
+	for(i = 4; i <= 8; i++){
+		get32_unaligned_check(tmpval32, datarf + (i - 4) * 4);
+		if(put_regs_value(tmpval32, i, regs) != 0){
+			goto fault;
+		}
+	}
+	ai_qword += 1;
+
+	return HANDLER_SUCCESS;
+fault:
+	return HANDLER_FAILURE;
+}
+
+static int
+handle_sth_stw_v1(unsigned long instr, struct pt_regs *regs){
+	unsigned int regx = instr & 0xf;
+	unsigned int regz = (instr >> 8) & 0xf;
+	unsigned int imm4 = (instr >> 4) & 0xf;
+	unsigned int destaddr, sth_stw;
+	unsigned int dataregx, dataregz;
+
+	dataregx = get_regs_value(regx, regs);
+	dataregz = get_regs_value(regz, regs);
+
+	sth_stw = instr & 0x6000;
+	if(sth_stw == 0x4000){ // sth
+		destaddr = dataregx + (imm4 << 1);
+		put16_unaligned_check(dataregz, destaddr);
+		ai_half += 1;
+	}else if(sth_stw == 0x0000){ //stw
+		destaddr = dataregx + (imm4 << 2);
+		put32_unaligned_check(dataregz, destaddr);
+		ai_word += 1;
+	}else{
+		goto fault;
+	}
+
+	return HANDLER_SUCCESS;
+fault:
+	return HANDLER_FAILURE;
+}
+
+static int
+handle_stq_v1(unsigned long instr, struct pt_regs *regs){
+	unsigned int regf = instr & 0xf;
+	unsigned int datarf;
+	unsigned int tmpval32, i;
+
+	// regf can not be r4 - r7.
+	if(regf > 3 && regf < 8){
+		goto fault;
+	}
+
+	datarf = get_regs_value(regf, regs);
+	for(i = 4; i <= 7; i++){
+		tmpval32 = get_regs_value(i, regs);
+		put32_unaligned_check(tmpval32, datarf + (i - 4) * 4);
+	}
+	ai_qword += 1;
+
+	return HANDLER_SUCCESS;
+fault:
+	return HANDLER_FAILURE;
+}
+
+static int
+handle_stm_v1(unsigned long instr, struct pt_regs *regs){
+	unsigned int regf = instr & 0xf;
+	unsigned int datasp;
+	unsigned int tmpval32, i;
+
+	// regf can not be r0 or r15.
+	if(regf == 0 || regf == 15){
+		goto fault;
+	}
+
+	datasp = get_regs_value(SP_NUM, regs);
+	for(i = regf; i <= R15_NUM; i++){
+		tmpval32 = get_regs_value(i, regs);
+		put32_unaligned_check(tmpval32, datasp + (i - regf) * 4);
+	}
+	ai_qword += 1;
+
+	return HANDLER_SUCCESS;
+fault:
+	return HANDLER_FAILURE;
+}
+
+void csky_alignment(struct pt_regs *regs)
+{
+	int err;
+	unsigned long instr = 0, instrptr;
+	unsigned int fault;
+	u16 tinstr = 0;
+	int (*handler)(unsigned long inst, struct pt_regs *regs) = NULL;
+	int isize = 2;
+	siginfo_t info;
+
+
+	mm_segment_t fs;
+
+	instrptr = instruction_pointer(regs);
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	fault = __get_user(tinstr, (u16 *)(instrptr & ~1));
+	instr = (unsigned long)tinstr;
+
+	set_fs(fs);
+	if (fault) {
+		goto bad_or_fault;
+	}
+
+	if (user_mode(regs)) {
+		goto user;
+	}
+
+	ai_sys += 1;
+fixup:
+	regs->pc += isize;
+
+	if((instr & 0x9000) == 0x9000){ // sth, stw
+		handler = handle_sth_stw_v1;
+	}else if((instr & 0x9000) == 0x8000){ // ldh, ldw
+		handler = handle_ldh_ldw_v1;
+	}else if((instr & 0xfff0) == 0x0070){ // stm
+		handler = handle_stm_v1;
+	}else if((instr & 0xfff0) == 0x0060){ // ldm
+		handler = handle_ldm_v1;
+	}else if((instr & 0xfff0) == 0x0050){ // stq
+		handler = handle_stq_v1;
+	}else if((instr & 0xfff0) == 0x0040){ // ldq
+		handler = handle_ldq_v1;
+	}else{
+		goto bad_or_fault;
+	}
+
+	if (!handler)
+		goto bad_or_fault;
+
+	err = handler(instr, regs);
+	if (err != HANDLER_SUCCESS)
+	{
+		regs->pc -=2;
+		goto bad_or_fault;
+	}
+
+	return;
+
+bad_or_fault:
+	if(fixup_exception(regs)) {
+		ai_skipped += 1;
+		return;
+	}
+
+	die_if_kernel("Alignment trap: not handle this instruction", regs, 0);
+	return;
+
+user:
+	ai_user += 1;
+
+	if (ai_usermode & UM_WARN)
+		printk("Alignment trap: %s(pid=%d) PC=0x%x Ins=0x%x\n",
+			current->comm, current->pid,
+			(unsigned int)regs->pc, (unsigned int)instr);
+
+	if (ai_usermode & UM_FIXUP)
+		goto fixup;
+
+	if (ai_usermode & UM_SIGNAL) {
+		info.si_code = NSIGBUS;
+		info.si_signo = SIGBUS;
+		info.si_errno = 0;
+		force_sig_info(SIGBUS, &info, current);
+	}
+
+	return;
+}
+
+/*
+ * This needs to be done after sysctl_init, otherwise sys/ will be
+ * overwritten.  Actually, this shouldn't be in sys/ at all since
+ * it isn't a sysctl, and it doesn't contain sysctl information.
+ * We now locate it in /proc/cpu/alignment instead.
+ */
+static int __init alignment_init(void)
+{
+	struct proc_dir_entry *res;
+
+	res = proc_mkdir("cpu", NULL);
+	if (!res)
+		return -ENOMEM;
+
+	res = proc_create("alignment", S_IWUSR | S_IRUGO, res, &alignment_proc_fops);
+	if (!res)
+		return -ENOMEM;
+
+	ai_usermode = UM_FIXUP;
+
+	return 0;
+}
+fs_initcall(alignment_init);
diff --git a/arch/csky/abiv2/inc/abi/entry.h b/arch/csky/abiv2/inc/abi/entry.h
new file mode 100644
index 0000000..fc6deab
--- /dev/null
+++ b/arch/csky/abiv2/inc/abi/entry.h
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
+#ifndef __ASM_CSKY_ENTRY_H
+#define __ASM_CSKY_ENTRY_H
+
+#include <asm/setup.h>
+#include <abi/regdef.h>
+
+#define LSAVE_A0       0xc
+#define LSAVE_A1       0x10
+#define LSAVE_A2       0x14
+#define LSAVE_A3       0x18
+
+#define KSPTOUSP
+#define USPTOKSP
+
+.macro GET_USP rx
+	mfcr	\rx, cr<14, 1>
+.endm
+
+.macro SET_USP rx
+	mtcr	\rx, cr<14, 1>
+.endm
+
+.macro INCTRAP	rx
+	addi	\rx, 4
+.endm
+
+.macro SAVE_ALL
+	subi    sp,  144
+	stw     a0, (sp, 4)
+	stw     a0, (sp, 12)
+	stw     a1, (sp, 16)
+	stw     a2, (sp, 20)
+	stw     a3, (sp, 24)
+	stw     r4, (sp, 28)
+	stw     r5, (sp, 32)
+	stw     r6, (sp, 36)
+	stw     r7, (sp, 40)
+	stw     r8, (sp, 44)
+	stw     r9, (sp, 48)
+	stw     r10, (sp, 52)
+	stw     r11, (sp, 56)
+	stw     r12, (sp, 60)
+	stw     r13, (sp, 64)
+	stw     r15, (sp, 68)
+	addi    sp, 72
+	stm     r16-r31,(sp)
+#ifdef CONFIG_CPU_HAS_HILO
+	mfhi    r22
+	mflo    r23
+	stw     r22, (sp, 64)
+        stw     r23, (sp, 68)
+#endif
+	subi    sp,  72
+
+	mfcr    r22, epsr
+	stw     r22, (sp, 8)
+	mfcr    r22, epc
+	stw     r22, (sp)
+.endm
+.macro SAVE_ALL_TRAP
+	SAVE_ALL
+	INCTRAP	r22
+	stw     r22, (sp)
+.endm
+
+.macro	RESTORE_ALL
+	psrclr  ie
+	ldw     a0, (sp)
+	mtcr    a0, epc
+	ldw     a0, (sp, 8)
+	mtcr    a0, epsr
+	addi    sp, 12
+#ifdef CONFIG_CPU_HAS_HILO
+	ldw     a0, (sp, 124)
+	ldw     a1, (sp, 128)
+	mthi    a0
+	mtlo    a1
+#endif
+	ldw     a0, (sp, 0)
+	ldw     a1, (sp, 4)
+	ldw     a2, (sp, 8)
+	ldw     a3, (sp, 12)
+	ldw     r4, (sp, 16)
+	ldw     r5, (sp, 20)
+	ldw     r6, (sp, 24)
+	ldw     r7, (sp, 28)
+	ldw     r8, (sp, 32)
+	ldw     r9, (sp, 36)
+	ldw     r10, (sp, 40)
+	ldw     r11, (sp, 44)
+	ldw     r12, (sp, 48)
+	ldw     r13, (sp, 52)
+	ldw     r15, (sp, 56)
+	addi    sp, 60
+	ldm     r16-r31,(sp)
+	addi    sp,  72
+1:
+	rte
+.endm
+
+.macro SAVE_SWITCH_STACK
+        subi    sp, 64
+        stm     r4-r11,(sp)
+        stw     r15, (sp, 32)
+        stw     r16, (sp, 36)
+        stw     r17, (sp, 40)
+        stw     r26, (sp, 44)
+        stw     r27, (sp, 48)
+        stw     r28, (sp, 52)
+        stw     r29, (sp, 56)
+        stw     r30, (sp, 60)
+.endm
+
+.macro RESTORE_SWITCH_STACK
+        ldm     r4-r11,(sp)
+        ldw     r15, (sp, 32)
+        ldw     r16, (sp, 36)
+        ldw     r17, (sp, 40)
+        ldw     r26, (sp, 44)
+        ldw     r27, (sp, 48)
+        ldw     r28, (sp, 52)
+        ldw     r29, (sp, 56)
+        ldw     r30, (sp, 60)
+        addi    sp, 64
+.endm
+
+/* MMU registers operators. */
+.macro RD_MIR	rx
+	mfcr    \rx, cr<0, 15>
+.endm
+
+.macro RD_MEH	rx
+	mfcr    \rx, cr<4, 15>
+.endm
+
+.macro RD_MCIR	rx
+	mfcr    \rx, cr<8, 15>
+.endm
+
+.macro RD_PGDR  rx
+        mfcr    \rx, cr<29, 15>
+.endm
+
+.macro WR_MEH	rx
+	mtcr    \rx, cr<4, 15>
+.endm
+
+.macro WR_MCIR	rx
+	mtcr    \rx, cr<8, 15>
+.endm
+
+#endif /* __ASM_CSKY_ENTRY_H */
diff --git a/arch/csky/include/asm/syscalls.h b/arch/csky/include/asm/syscalls.h
new file mode 100644
index 0000000..c478830
--- /dev/null
+++ b/arch/csky/include/asm/syscalls.h
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
+#ifndef __ASM_CSKY_SYSCALLS_H
+#define __ASM_CSKY_SYSCALLS_H
+
+#include <asm-generic/syscalls.h>
+
+long sys_cacheflush(void __user *, unsigned long, int);
+
+long sys_set_thread_area(unsigned long addr);
+
+long sys_csky_fadvise64_64(int fd, int advice, loff_t offset, loff_t len);
+
+#endif /* __ASM_CSKY_SYSCALLS_H */
diff --git a/arch/csky/include/asm/traps.h b/arch/csky/include/asm/traps.h
new file mode 100644
index 0000000..ca82b19
--- /dev/null
+++ b/arch/csky/include/asm/traps.h
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
+#ifndef __ASM_CSKY_TRAPS_H
+#define __ASM_CSKY_TRAPS_H
+
+#define VEC_RESET	0
+#define VEC_ALIGN	1
+#define VEC_ACCESS	2
+#define VEC_ZERODIV	3
+#define VEC_ILLEGAL	4
+#define VEC_PRIV	5
+#define VEC_TRACE	6
+#define VEC_BREAKPOINT	7
+#define VEC_UNRECOVER	8
+#define VEC_SOFTRESET	9
+#define VEC_AUTOVEC	10
+#define VEC_FAUTOVEC	11
+#define VEC_HWACCEL	12
+
+#define	VEC_TLBMISS	14
+#define	VEC_TLBMODIFIED	15
+
+#define VEC_TRAP0	16
+#define VEC_TRAP1	17
+#define VEC_TRAP2	18
+#define VEC_TRAP3	19
+
+#define	VEC_TLBINVALIDL	20
+#define	VEC_TLBINVALIDS	21
+
+#define VEC_PRFL	29
+#define VEC_FPE		30
+
+extern void * vec_base[];
+#define VEC_INIT(i, func) vec_base[i] = (void *)func
+
+void csky_alignment(struct pt_regs*);
+
+#endif /* __ASM_CSKY_TRAPS_H */
diff --git a/arch/csky/include/asm/unistd.h b/arch/csky/include/asm/unistd.h
new file mode 100644
index 0000000..704526c
--- /dev/null
+++ b/arch/csky/include/asm/unistd.h
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
+#include <uapi/asm/unistd.h>
+
diff --git a/arch/csky/include/uapi/asm/unistd.h b/arch/csky/include/uapi/asm/unistd.h
new file mode 100644
index 0000000..12ebbba
--- /dev/null
+++ b/arch/csky/include/uapi/asm/unistd.h
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
+#define __ARCH_WANT_IPC_PARSE_VERSION
+#define __ARCH_WANT_OLD_READDIR
+#define __ARCH_WANT_RENAMEAT
+#define __ARCH_WANT_STAT64
+#define __ARCH_WANT_SYNC_FILE_RANGE2
+#define __ARCH_WANT_SYS_ALARM
+#define __ARCH_WANT_SYS_CLONE
+#define __ARCH_WANT_SYS_FORK
+#define __ARCH_WANT_SYS_GETHOSTNAME
+#define __ARCH_WANT_SYS_GETPGRP
+#define __ARCH_WANT_SYS_IPC
+#define __ARCH_WANT_SYS_LLSEEK
+#define __ARCH_WANT_SYS_NICE
+#define __ARCH_WANT_SYS_OLD_GETRLIMIT
+#define __ARCH_WANT_SYS_OLD_SELECT
+#define __ARCH_WANT_SYS_OLDUMOUNT
+#define __ARCH_WANT_SYS_PAUSE
+#define __ARCH_WANT_SYS_RT_SIGACTION
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
+#define __ARCH_WANT_SYS_SGETMASK
+#define __ARCH_WANT_SYS_SIGNAL
+#define __ARCH_WANT_SYS_SIGPENDING
+#define __ARCH_WANT_SYS_SIGPROCMASK
+#define __ARCH_WANT_SYS_SOCKETCALL
+#define __ARCH_WANT_SYS_TIME
+#define __ARCH_WANT_SYS_UTIME
+#define __ARCH_WANT_SYS_VFORK
+#define __ARCH_WANT_SYS_WAITPID
+#define __ARCH_WANT_SYSCALL_DEPRECATED
+#define __ARCH_WANT_SYSCALL_OFF_T
+#define __ARCH_WANT_SYSCALL_NO_AT
+#define __ARCH_WANT_SYSCALL_NO_FLAGS
+
+#undef	__NR_rt_sigreturn
+#undef	__NR_getppid
+
+#include <asm-generic/unistd.h>
+
+/*
+ * __NR_rt_sigreturn must be 173
+ * Because gcc/config/csky/linux-unwind.h use hard-code 173
+ * to parse rt_sigframe.
+ */
+#if __NR_rt_sigreturn != 139
+#error __NR_rt_sigreturn has changed.
+#endif
+
+#if __NR_getppid != 173
+#error __NR_getppid has changed.
+#endif
+
+#undef	__NR_rt_sigreturn
+#define	__NR_rt_sigreturn 173
+__SC_COMP(__NR_rt_sigreturn, sys_rt_sigreturn, compat_sys_rt_sigreturn)
+
+#undef	__NR_getppid
+#define	__NR_getppid 139
+__SYSCALL(__NR_getppid, sys_getppid)
+
+
+/*
+ * other define
+ */
+#define __NR_set_thread_area	(__NR_arch_specific_syscall + 0)
+__SYSCALL(__NR_set_thread_area, sys_set_thread_area)
+#define __NR_ipc		(__NR_arch_specific_syscall + 1)
+__SYSCALL(__NR_ipc, sys_ipc)
+#define __NR_socketcall		(__NR_arch_specific_syscall + 2)
+__SYSCALL(__NR_socketcall, sys_socketcall)
+#define __NR_ugetrlimit		(__NR_arch_specific_syscall + 3)
+__SYSCALL(__NR_ugetrlimit, sys_getrlimit)
+#define __NR_cacheflush		(__NR_arch_specific_syscall + 4)
+__SYSCALL(__NR_cacheflush, sys_cacheflush)
+#define __NR_sysfs		(__NR_arch_specific_syscall + 5)
+__SYSCALL(__NR_sysfs, sys_sysfs)
+
+__SYSCALL(__NR_fadvise64_64, sys_csky_fadvise64_64)
+
+#define __NR_setgroups32	__NR_setgroups
+#define __NR_getgid32		__NR_getgid
+#define __NR_getgroups32	__NR_getgroups
+#define __NR_setuid32		__NR_setuid
+#define __NR_setgid32		__NR_setgid
+#define __NR_getresgid32	__NR_getresgid
+#define __NR_chown32		__NR_chown
+#define __NR_setfsuid32		__NR_setfsuid
+#define __NR_setfsgid32		__NR_setfsgid
+#define __NR_lchown32		__NR_lchown
+#define __NR_fchown32		__NR_fchown
+#define __NR_geteuid32		__NR_geteuid
+#define __NR_getegid32		__NR_getegid
+#define __NR_getresuid32	__NR_getresuid
+#define __NR_setresuid32	__NR_setresuid
+#define __NR_setresgid32	__NR_setresgid
+#define __NR_setreuid32		__NR_setreuid
+#define __NR_setregid32		__NR_setregid
+#define __NR__llseek		__NR_llseek
+
diff --git a/arch/csky/kernel/cpu-probe.c b/arch/csky/kernel/cpu-probe.c
new file mode 100644
index 0000000..8934583
--- /dev/null
+++ b/arch/csky/kernel/cpu-probe.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
+#include <linux/of.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <abi/reg_ops.h>
+#include <linux/memblock.h>
+
+static __init void setup_cpu_msa(void)
+{
+	if (memblock_start_of_DRAM() != (PHYS_OFFSET + CONFIG_RAM_BASE)) {
+		pr_err("C-SKY: dts-DRAM doesn't fit .config: %x-%x.\n",
+			memblock_start_of_DRAM(),
+			PHYS_OFFSET + CONFIG_RAM_BASE);
+		return;
+	}
+
+	mtcr_msa0(PHYS_OFFSET | 0xe);
+	mtcr_msa1(PHYS_OFFSET | 0x6);
+}
+
+__init void cpu_dt_probe(void)
+{
+	setup_cpu_msa();
+}
+
+static int c_show(struct seq_file *m, void *v)
+{
+	seq_printf(m, "C-SKY CPU : %s\n", CSKYCPU_DEF_NAME);
+	seq_printf(m, "revision  : 0x%08x\n", mfcr_cpuidrr());
+	seq_printf(m, "ccr reg   : 0x%08x\n", mfcr_ccr());
+	seq_printf(m, "ccr2 reg  : 0x%08x\n", mfcr_ccr2());
+	seq_printf(m, "hint reg  : 0x%08x\n", mfcr_hint());
+	seq_printf(m, "msa0 reg  : 0x%08x\n", mfcr_msa0());
+	seq_printf(m, "msa1 reg  : 0x%08x\n", mfcr_msa1());
+	seq_printf(m, "\n");
+#ifdef CSKY_ARCH_VERSION
+	seq_printf(m, "arch-version : %s\n", CSKY_ARCH_VERSION);
+	seq_printf(m, "\n");
+#endif
+	return 0;
+}
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+	return *pos < 1 ? (void *)1 : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	++*pos;
+	return NULL;
+}
+
+static void c_stop(struct seq_file *m, void *v) {}
+
+const struct seq_operations cpuinfo_op = {
+	.start	= c_start,
+	.next	= c_next,
+	.stop	= c_stop,
+	.show	= c_show,
+};
+
diff --git a/arch/csky/kernel/entry.S b/arch/csky/kernel/entry.S
new file mode 100644
index 0000000..f0ab7d7
--- /dev/null
+++ b/arch/csky/kernel/entry.S
@@ -0,0 +1,408 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
+#include <linux/linkage.h>
+#include <abi/entry.h>
+#include <abi/pgtable-bits.h>
+#include <asm/errno.h>
+#include <asm/setup.h>
+#include <asm/unistd.h>
+#include <asm/asm-offsets.h>
+#include <linux/threads.h>
+#include <asm/setup.h>
+#include <asm/page.h>
+#include <asm/thread_info.h>
+#ifdef CONFIG_CPU_HAS_FPU
+#include <abi/fpu.h>
+#endif
+
+#define PTE_INDX_MSK    0xffc
+#define PTE_INDX_SHIFT  10
+#define _PGDIR_SHIFT    22
+#define THREADSIZE_MASK_BIT 13
+
+.macro tlbop_begin name, val0, val1, val2
+ENTRY(csky_\name)
+	mtcr    a3, ss2
+	mtcr    r6, ss3
+	mtcr    a2, ss4
+
+	RD_MEH	a3
+#ifdef CONFIG_CPU_HAS_TLBI
+	tlbi.va	a3
+#else
+	bgeni	a2, 31
+	WR_MCIR	a2
+	bgeni	a2, 25
+	WR_MCIR	a2
+#endif
+
+	RD_PGDR	r6
+	bclri   r6, 0
+	lrw	a2, PHYS_OFFSET
+	subu	r6, a2
+	bseti	r6, 31
+
+	mov     a2, a3
+   	lsri    a2, _PGDIR_SHIFT
+	lsli    a2, 2
+	addu    r6, a2
+	ldw     r6, (r6)
+
+	lrw	a2, PHYS_OFFSET
+	subu	r6, a2
+	bseti	r6, 31
+
+	lsri    a3, PTE_INDX_SHIFT
+	lrw     a2, PTE_INDX_MSK
+	and     a3, a2
+	addu    r6, a3
+	ldw     a3, (r6)
+
+	movi	a2, (_PAGE_PRESENT | \val0)
+	and     a3, a2
+	cmpne   a3, a2
+	bt	\name
+
+	/* First read/write the page, just update the flags */
+	ldw     a3, (r6)
+	bgeni   a2, PAGE_VALID_BIT
+	bseti   a2, PAGE_ACCESSED_BIT
+	bseti   a2, \val1
+	bseti   a2, \val2
+	or      a3, a2
+	stw     a3, (r6)
+
+	/* Some cpu tlb-hardrefill bypass the cache */
+#ifdef CONFIG_CPU_NEED_TLBSYNC
+	movi	a2, 0x22
+	bseti	a2, 6
+	mtcr	r6, cr22
+	mtcr	a2, cr17
+	sync
+#endif
+
+	mfcr    a3, ss2
+	mfcr    r6, ss3
+	mfcr    a2, ss4
+	rte
+\name:
+	mfcr    a3, ss2
+	mfcr    r6, ss3
+	mfcr    a2, ss4
+	SAVE_ALL
+.endm
+.macro tlbop_end is_write
+	RD_MEH	a2
+	psrset  ee, ie
+	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
+
+.text
+
+tlbop_begin tlbinvalidl, _PAGE_READ, PAGE_VALID_BIT, PAGE_ACCESSED_BIT
+tlbop_end 0
+
+tlbop_begin tlbinvalids, _PAGE_WRITE, PAGE_DIRTY_BIT, PAGE_MODIFIED_BIT
+tlbop_end 1
+
+tlbop_begin tlbmodified, _PAGE_WRITE, PAGE_DIRTY_BIT, PAGE_MODIFIED_BIT
+jbsr csky_cmpxchg_fixup
+tlbop_end 1
+
+ENTRY(csky_systemcall)
+	SAVE_ALL_TRAP
+
+	psrset  ee, ie
+
+	/* Stack frame for syscall, origin call set_esp0 */
+	mov     r12, sp
+
+	bmaski  r11, 13
+	andn    r12, r11
+	bgeni   r11, 9
+	addi    r11, 32
+	addu    r12, r11
+	st      sp, (r12, 0)
+
+	lrw     r11, __NR_syscalls
+	cmphs   syscallid, r11                 /* Check nr of syscall */
+	bt      ret_from_exception
+
+	lrw     r13, sys_call_table
+	ixw     r13, syscallid                 /* Index into syscall table */
+	ldw     r11, (r13)               /* Get syscall function */
+	cmpnei  r11, 0                  /* Check for not null */
+	bf      ret_from_exception
+
+	mov     r9, sp				 /* Get task pointer */
+	bmaski  r10, THREADSIZE_MASK_BIT
+	andn    r9, r10                      /* Get thread_info */
+	ldw     r8, (r9, TINFO_FLAGS)       /* Get thread_info.flags value */
+	btsti   r8, TIF_SYSCALL_TRACE       /* Check if TIF_SYSCALL_TRACE set */
+	bt      1f
+#if defined(__CSKYABIV2__)
+	subi    sp, 8
+	stw  	r5, (sp, 0x4)
+	stw  	r4, (sp, 0x0)
+	jsr     r11                      /* Do system call */
+	addi 	sp, 8
+#else
+	jsr     r11
+#endif
+	stw     a0, (sp, LSAVE_A0)      /* Save return value */
+	jmpi    ret_from_exception
+
+1:
+	movi    a0, 0                   /* enter system call */
+	mov     a1, sp                  /* right now, sp --> pt_regs */
+	jbsr    syscall_trace
+	/* Prepare args before do system call */
+	ldw     a0, (sp, LSAVE_A0)
+	ldw     a1, (sp, LSAVE_A1)
+	ldw     a2, (sp, LSAVE_A2)
+	ldw     a3, (sp, LSAVE_A3)
+#if defined(__CSKYABIV2__)
+	subi    sp, 8
+	stw     r5, (sp, 0x4)
+	stw     r4, (sp, 0x0)
+#else
+	ldw     r6, (sp, LSAVE_A4)
+	ldw     r7, (sp, LSAVE_A5)
+#endif
+	jsr     r11                     /* Do system call */
+#if defined(__CSKYABIV2__)
+	addi    sp, 8
+#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
+
+syscall_exit_work:
+	ld       syscallid, (sp, 8)     /* get psr, is user mode? */
+	btsti    syscallid, 31
+	bt       2f
+
+	jmpi     resume_userspace
+
+2:      RESTORE_ALL
+
+ENTRY(ret_from_kernel_thread)
+	jbsr     schedule_tail
+	mov	 a0, r8
+	jsr	 r9
+	jbsr     ret_from_exception
+
+ENTRY(ret_from_fork)
+	jbsr     schedule_tail
+	mov      r9, sp				 /* Get task pointer */
+	bmaski   r10, THREADSIZE_MASK_BIT
+	andn     r9, r10                     /* Get thread_info */
+	ldw      r8, (r9, TINFO_FLAGS)       /* Get thread_info.flags value */
+	movi     r11_sig, 1                  /* is a syscall */
+	btsti    r8, TIF_SYSCALL_TRACE       /* Check if TIF_SYSCALL_TRACE set */
+	bf       3f
+	movi     a0, 1                       /* leave system call */
+	mov      a1, sp                      /* right now, sp --> pt_regs */
+	jbsr     syscall_trace
+3:
+	jbsr     ret_from_exception
+
+ret_from_exception:
+	ld       syscallid, (sp,8)     /* get psr, is user mode? */
+	btsti    syscallid, 31
+	bt       1f
+	/*
+	 * Load address of current->thread_info, Then get address of task_struct
+	 * Get task_needreshed in task_struct
+	 */
+	mov     r9, sp     					 /* Get current stack  pointer */
+	bmaski  r10, THREADSIZE_MASK_BIT
+	andn    r9, r10                      /* Get task_struct */
+
+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
+
+exit_work:
+	mov      a0, sp                 /* Stack address is arg[0] */
+	jbsr     set_esp0               /* Call C level */
+	btsti    r8, TIF_NEED_RESCHED
+	bt       work_resched
+	cmpnei   r8, 0		/* If thread_info->flag is empty, RESTORE_ALL. */
+	bf       1b
+	mov      a1, sp
+	mov      a0, r8
+	mov      a2, r11_sig        /* syscall? */
+	btsti    r8, TIF_SIGPENDING /* delivering a signal? */
+	clrt     r11_sig            /* prevent further restarts(set r11 = 0) */
+	jbsr     do_notify_resume	/* do signals */
+	br       resume_userspace
+
+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
+
+/*
+ * Common trap handler. Standard traps come through here first
+ */
+
+ENTRY(csky_trap)
+	SAVE_ALL
+	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
+
+/*
+ * Prototype from libc:
+ * register unsigned int __result asm("a0");
+ * asm( "trap 3" :"=r"(__result)::);
+ */
+ENTRY(csky_get_tls)
+	USPTOKSP
+
+	/* increase epc for continue */
+	mfcr	a0, epc
+	INCTRAP	a0
+	mtcr	a0, epc
+
+	/* get current task thread_info with kernel 8K stack */
+	bmaski   a0, (PAGE_SHIFT + 1)
+	not      a0
+	subi     sp, 1
+	and      a0, sp
+	addi     sp, 1
+
+	/* get tls */
+	ldw      a0, (a0, TINFO_TP_VALUE)
+
+	KSPTOUSP
+	rte
+
+ENTRY(csky_irq)
+	SAVE_ALL
+	psrset  ee       // enable exception
+	movi    r11_sig, 0                   /* r11 = 0, Not a syscall. */
+
+#ifdef CONFIG_PREEMPT
+	mov     r9, sp                       /* Get current stack  pointer */
+	bmaski  r10, THREADSIZE_MASK_BIT
+	andn    r9, r10                      /* Get thread_info */
+
+	/*
+	 * Get task_struct->stack.preempt_count for current,
+	 * and increase 1.
+	 */
+	ldw      r8, (r9, TINFO_PREEMPT)
+	addi     r8, 1
+	stw      r8, (r9, TINFO_PREEMPT)
+#endif
+
+	mov      a0, sp                      /* arg[0] is stack pointer */
+	jbsr     csky_do_auto_IRQ          /* Call handler */
+
+#ifdef CONFIG_PREEMPT
+	subi     r8, 1
+	stw      r8, (r9, TINFO_PREEMPT)
+	cmpnei   r8, 0
+	bt       2f
+	ldw      r8, (r9, TINFO_FLAGS)
+	btsti    r8, TIF_NEED_RESCHED
+	bf       2f
+1:
+	jbsr     preempt_schedule_irq   /* irq en/disable is done inside */
+	ldw      r7, (r9, TINFO_FLAGS)  /* get new tasks TI_FLAGS */
+	btsti    r7, TIF_NEED_RESCHED
+	bt       1b                     /* go again */
+#endif
+2:
+	jmpi     ret_from_exception
+
+/*
+ * a0 =  prev task_struct *
+ * a1 =  next task_struct *
+ * a0 =  return next
+ */
+ENTRY(__switch_to)
+	lrw      a3, TASK_THREAD        /* struct_thread offset in task_struct */
+	addu     a3, a0                 /* a3 point to thread in prev task_struct */
+
+	mfcr     a2, psr                /* Save PSR value */
+	stw      a2, (a3, THREAD_SR)    /* Save PSR in task struct */
+	bclri    a2, 6                  /* Disable interrupts */
+	mtcr     a2, psr
+
+	SAVE_SWITCH_STACK
+
+	GET_USP	 r6
+
+	stw      r6, (a3, THREAD_USP)   /* Save usp in task struct */
+	stw      sp, (a3, THREAD_KSP)   /* Save ksp in task struct */
+
+#ifdef CONFIG_CPU_HAS_FPU
+	FPU_SAVE_REGS
+#endif
+
+#ifdef CONFIG_CPU_HAS_HILO
+	lrw      r10, THREAD_DSPHI
+	add      r10, a3
+	mfhi     r6
+	mflo     r7
+	stw      r6, (r10, 0)           /* THREAD_DSPHI */
+	stw      r7, (r10, 4)           /* THREAD_DSPLO */
+	mfcr     r6, cr14
+	stw      r6, (r10, 8)           /* THREAD_DSPCSR */
+#endif
+
+	/* Set up next process to run */
+	lrw      a3, TASK_THREAD	/* struct_thread offset in task_struct */
+	addu     a3, a1			/* a3 point to thread in next task_struct */
+
+	ldw      sp, (a3, THREAD_KSP)	/* Set next ksp */
+	ldw      r6, (a3, THREAD_USP)	/* Set next usp */
+
+	SET_USP	 r6
+
+#ifdef CONFIG_CPU_HAS_FPU
+	FPU_RESTORE_REGS
+#endif
+
+#ifdef CONFIG_CPU_HAS_HILO
+	lrw      r10, THREAD_DSPHI
+	add      r10, a3
+	ldw      r6, (r10, 8)   /* THREAD_DSPCSR */
+	mtcr     r6, cr14
+	ldw      r6, (r10, 0)    /* THREAD_DSPHI */
+	ldw      r7, (r10, 4)    /* THREAD_DSPLO */
+	mthi     r6
+	mtlo     r7
+#endif
+
+	ldw      a2, (a3, THREAD_SR)    /* Set next PSR */
+	mtcr     a2, psr
+
+#if  defined(__CSKYABIV2__)
+	/* set TLS register (r31) */
+	addi     r7, a1, TASK_THREAD_INFO
+	ldw      r31, (r7, TINFO_TP_VALUE)
+#endif
+
+	RESTORE_SWITCH_STACK
+
+	rts
+ENDPROC(__switch_to)
diff --git a/arch/csky/kernel/syscall.c b/arch/csky/kernel/syscall.c
new file mode 100644
index 0000000..b45f497
--- /dev/null
+++ b/arch/csky/kernel/syscall.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
+#include <linux/syscalls.h>
+
+SYSCALL_DEFINE1(set_thread_area, unsigned long, addr)
+{
+	struct thread_info *ti = task_thread_info(current);
+
+#if defined(__CSKYABIV2__)
+	struct pt_regs *reg = current_pt_regs();
+	reg->exregs[15] = (long)addr;
+#endif
+	ti->tp_value = addr;
+
+	return 0;
+}
+
+SYSCALL_DEFINE6(mmap2,
+	unsigned long, addr,
+	unsigned long, len,
+	unsigned long, prot,
+	unsigned long, flags,
+	unsigned long, fd,
+	off_t, offset)
+{
+	if (unlikely(offset & (~PAGE_MASK >> 12)))
+		return -EINVAL;
+	return sys_mmap_pgoff(addr, len, prot, flags, fd,
+		offset >> (PAGE_SHIFT - 12));
+}
+
+struct mmap_arg_struct {
+	unsigned long addr;
+	unsigned long len;
+	unsigned long prot;
+	unsigned long flags;
+	unsigned long fd;
+	unsigned long offset;
+};
+
+SYSCALL_DEFINE1(mmap,
+	struct mmap_arg_struct *, arg)
+{
+	struct mmap_arg_struct a;
+
+	if (copy_from_user(&a, arg, sizeof(a)))
+		return -EINVAL;
+
+	if (unlikely(a.offset & ~PAGE_MASK))
+		return -EINVAL;
+
+	return sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
+}
+
+/*
+ * for abiv1 the 64bits args should be even th, So we need mov the advice forward.
+ */
+SYSCALL_DEFINE4(csky_fadvise64_64,
+	int, fd,
+	int, advice,
+	loff_t, offset,
+	loff_t, len)
+{
+	return sys_fadvise64_64(fd, offset, len, advice);
+}
diff --git a/arch/csky/kernel/syscall_table.c b/arch/csky/kernel/syscall_table.c
new file mode 100644
index 0000000..bea8558
--- /dev/null
+++ b/arch/csky/kernel/syscall_table.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
+#include <linux/syscalls.h>
+#include <asm/syscalls.h>
+
+#undef __SYSCALL
+#define __SYSCALL(nr, call) [nr] = (call),
+
+void * const sys_call_table[__NR_syscalls] __page_aligned_data = {
+	[0 ... __NR_syscalls - 1] = sys_ni_syscall,
+#include <asm/unistd.h>
+};
diff --git a/arch/csky/kernel/traps.c b/arch/csky/kernel/traps.c
new file mode 100644
index 0000000..17bcb08
--- /dev/null
+++ b/arch/csky/kernel/traps.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/user.h>
+#include <linux/string.h>
+#include <linux/linkage.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/kallsyms.h>
+#include <linux/rtc.h>
+#include <linux/uaccess.h>
+
+#include <asm/setup.h>
+#include <asm/traps.h>
+#include <asm/pgalloc.h>
+#include <asm/siginfo.h>
+
+#include <asm/mmu_context.h>
+
+#ifdef CONFIG_CPU_HAS_FPU
+#include <abi/fpu.h>
+#endif
+
+/* Defined in entry.S */
+asmlinkage void csky_trap(void);
+
+asmlinkage void csky_systemcall(void);
+asmlinkage void csky_cmpxchg(void);
+asmlinkage void csky_get_tls(void);
+asmlinkage void csky_irq(void);
+
+asmlinkage void csky_tlbinvalidl(void);
+asmlinkage void csky_tlbinvalids(void);
+asmlinkage void csky_tlbmodified(void);
+
+void __init pre_trap_init(void)
+{
+	int i;
+
+	asm volatile(
+		"mtcr %0, vbr\n"
+		::"r"(vec_base));
+
+	for(i=1;i<128;i++) VEC_INIT(i, csky_trap);
+}
+
+void __init trap_init (void)
+{
+	int i;
+
+	/* setup irq */
+	for(i=32;i<128;i++)
+		  VEC_INIT(i, csky_irq);
+	VEC_INIT(VEC_AUTOVEC, csky_irq);
+
+	/* setup trap0 trap2 trap3 */
+	VEC_INIT(VEC_TRAP0, csky_systemcall);
+	VEC_INIT(VEC_TRAP2, csky_cmpxchg);
+	VEC_INIT(VEC_TRAP3, csky_get_tls);
+
+	/* setup MMU TLB exception */
+	VEC_INIT(VEC_TLBINVALIDL, csky_tlbinvalidl);
+	VEC_INIT(VEC_TLBINVALIDS, csky_tlbinvalids);
+	VEC_INIT(VEC_TLBMODIFIED, csky_tlbmodified);
+
+
+#ifdef CONFIG_CPU_HAS_FPU
+	init_fpu();
+#endif
+}
+
+void die_if_kernel (char *str, struct pt_regs *regs, int nr)
+{
+	if (user_mode(regs)) return;
+
+	console_verbose();
+	pr_err("%s: %08x\n",str,nr);
+        show_regs(regs);
+        add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
+	do_exit(SIGSEGV);
+}
+
+void buserr(struct pt_regs *regs)
+{
+	siginfo_t info;
+
+	die_if_kernel("Kernel mode BUS error", regs, 0);
+
+	pr_err("User mode Bus Error\n");
+	show_regs(regs);
+
+	current->thread.esp0 = (unsigned long) regs;
+	info.si_signo = SIGSEGV;
+	info.si_errno = 0;
+	force_sig_info(SIGSEGV, &info, current);
+}
+
+asmlinkage void trap_c(struct pt_regs *regs)
+{
+	int sig;
+	unsigned long vector;
+	siginfo_t info;
+
+	asm volatile("mfcr %0, psr":"=r"(vector));
+
+	vector = (vector >> 16) & 0xff;
+
+	switch (vector) {
+		case VEC_ZERODIV:
+			sig = SIGFPE;
+			break;
+		/* ptrace */
+		case VEC_TRACE:
+			info.si_code = TRAP_TRACE;
+			sig = SIGTRAP;
+			break;
+
+		/* gdbserver  breakpoint */
+		case VEC_TRAP1:
+		/* jtagserver breakpoint */
+		case VEC_BREAKPOINT:
+			info.si_code = TRAP_BRKPT;
+			sig = SIGTRAP;
+			break;
+		case VEC_ACCESS:
+			return buserr(regs);
+#ifdef CONFIG_CPU_NEED_SOFTALIGN
+		case VEC_ALIGN:
+			return csky_alignment(regs);
+#endif
+#ifdef CONFIG_CPU_HAS_FPU
+		case VEC_FPE:
+			return fpu_fpe(regs);
+		case VEC_PRIV:
+			if(fpu_libc_helper(regs)) return;
+#endif
+		default:
+			sig = SIGILL;
+			break;
+	}
+	send_sig(sig, current, 0);
+}
+
+asmlinkage void set_esp0 (unsigned long ssp)
+{
+	current->thread.esp0 = ssp;
+}
+
diff --git a/arch/csky/mm/fault.c b/arch/csky/mm/fault.c
new file mode 100644
index 0000000..fe85404
--- /dev/null
+++ b/arch/csky/mm/fault.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
+#include <linux/signal.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/version.h>
+#include <linux/vt_kern.h>
+#include <linux/kernel.h>
+#include <linux/extable.h>
+#include <linux/uaccess.h>
+
+#include <asm/hardirq.h>
+#include <asm/mmu_context.h>
+#include <asm/traps.h>
+#include <asm/page.h>
+
+extern void die_if_kernel(char *, struct pt_regs *, long);
+
+static inline int delay_slot(struct pt_regs *regs)
+{
+        return 0;
+}
+
+static inline unsigned long exception_epc(struct pt_regs *regs)
+{
+        if (!delay_slot(regs))
+                return regs->pc;
+
+        return regs->pc + 4;
+}
+
+int fixup_exception(struct pt_regs *regs)
+{
+        const struct exception_table_entry *fixup;
+
+        fixup = search_exception_tables(exception_epc(regs));
+        if (fixup) {
+                regs->pc = fixup->nextinsn;
+
+                return 1;
+        }
+
+        return 0;
+}
+
+/*
+ * This routine handles page faults.  It determines the address,
+ * and the problem, and then passes it off to one of the appropriate
+ * routines.
+ */
+asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,
+                              unsigned long mmu_meh)
+{
+        struct vm_area_struct * vma = NULL;
+        struct task_struct *tsk = current;
+        struct mm_struct *mm = tsk->mm;
+        siginfo_t info;
+	int fault;
+	unsigned long address = mmu_meh & PAGE_MASK;
+
+        info.si_code = SEGV_MAPERR;
+
+        /*
+         * We fault-in kernel-space virtual memory on-demand. The
+         * 'reference' page table is init_mm.pgd.
+         *
+         * NOTE! We MUST NOT take any locks for this case. We may
+         * be in an interrupt or a critical region, and should
+         * only copy the information from the master page table,
+         * nothing more.
+         */
+        if (unlikely(address >= VMALLOC_START && address <= VMALLOC_END))
+                goto vmalloc_fault;
+
+        /*
+         * If we're in an interrupt or have no user
+         * context, we must not take the fault..
+         */
+        if (in_atomic() || !mm)
+                goto bad_area_nosemaphore;
+
+        down_read(&mm->mmap_sem);
+        vma = find_vma(mm, address);
+        if (!vma)
+                goto bad_area;
+        if (vma->vm_start <= address)
+                goto good_area;
+        if (!(vma->vm_flags & VM_GROWSDOWN))
+                goto bad_area;
+        if (expand_stack(vma, address))
+                goto bad_area;
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+good_area:
+info.si_code = SEGV_ACCERR;
+
+        if (write) {
+                if (!(vma->vm_flags & VM_WRITE))
+                        goto bad_area;
+        } else {
+                if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)))
+                        goto bad_area;
+        }
+
+        /*
+         * If for any reason at all we couldn't handle the fault,
+         * make sure we exit gracefully rather than endlessly redo
+         * the fault.
+         */
+        fault = handle_mm_fault(vma, address, write ? FAULT_FLAG_WRITE : 0);
+        if (unlikely(fault & VM_FAULT_ERROR)) {
+                if (fault & VM_FAULT_OOM)
+                        goto out_of_memory;
+                else if (fault & VM_FAULT_SIGBUS)
+                        goto do_sigbus;
+                else if (fault & VM_FAULT_SIGSEGV)
+                        goto bad_area;
+                BUG();
+        }
+        if (fault & VM_FAULT_MAJOR)
+                tsk->maj_flt++;
+        else
+                tsk->min_flt++;
+
+        up_read(&mm->mmap_sem);
+        return;
+
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+bad_area:
+        up_read(&mm->mmap_sem);
+
+bad_area_nosemaphore:
+        /* User mode accesses just cause a SIGSEGV */
+        if (user_mode(regs)) {
+                tsk->thread.address = address;
+                tsk->thread.error_code = write;
+#if 0
+                printk(KERN_ERR "do_page_fault() #2: sending SIGSEGV to %s for"
+                       "invalid %s\n%08lx (epc == %08lx)\n",
+                       tsk->comm,
+                       write ? "write access to" : "read access from",
+                       address,
+                       regs->pc);
+#endif
+                info.si_signo = SIGSEGV;
+                info.si_errno = 0;
+                /* info.si_code has been set above */
+                info.si_addr = (void __user *) address;
+                force_sig_info(SIGSEGV, &info, tsk);
+                return;
+        }
+
+no_context:
+	/* Are we prepared to handle this kernel fault? */
+        if (fixup_exception(regs)) return;
+
+	/*
+         * Oops. The kernel tried to access some bad page. We'll have to
+         * terminate things with extreme prejudice.
+         */
+        bust_spinlocks(1);
+
+        printk(KERN_ALERT "Unable to handle kernel paging request at virtual "
+               "address %08lx, epc == %08lx\n",
+               address, regs->pc);
+	die_if_kernel("Oops", regs, write);
+
+out_of_memory:
+        /*
+         * We ran out of memory, call the OOM killer, and return the userspace
+         * (which will retry the fault, or kill us if we got oom-killed).
+         */
+        pagefault_out_of_memory();
+        return;
+
+do_sigbus:
+        up_read(&mm->mmap_sem);
+
+        /* Kernel mode? Handle exceptions or die */
+        if (!user_mode(regs))
+                goto no_context;
+
+        tsk->thread.address = address;
+        info.si_signo = SIGBUS;
+        info.si_errno = 0;
+        info.si_code = BUS_ADRERR;
+        info.si_addr = (void __user *) address;
+        force_sig_info(SIGBUS, &info, tsk);
+
+        return;
+vmalloc_fault:
+        {
+                /*
+                 * Synchronize this task's top level page-table
+                 * with the 'reference' page table.
+                 *
+                 * Do _not_ use "tsk" here. We might be inside
+                 * an interrupt in the middle of a task switch..
+                 */
+                int offset = __pgd_offset(address);
+                pgd_t *pgd, *pgd_k;
+                pud_t *pud, *pud_k;
+                pmd_t *pmd, *pmd_k;
+                pte_t *pte_k;
+
+                unsigned long pgd_base;
+		pgd_base = tlb_get_pgd();
+                pgd = (pgd_t *)pgd_base + offset;
+                pgd_k = init_mm.pgd + offset;
+
+                if (!pgd_present(*pgd_k))
+                        goto no_context;
+                set_pgd(pgd, *pgd_k);
+
+                pud = (pud_t *)pgd;
+                pud_k = (pud_t *)pgd_k;
+                if (!pud_present(*pud_k))
+                        goto no_context;
+
+                pmd = pmd_offset(pud, address);
+                pmd_k = pmd_offset(pud_k, address);
+                if (!pmd_present(*pmd_k))
+                        goto no_context;
+                set_pmd(pmd, *pmd_k);
+
+                pte_k = pte_offset_kernel(pmd_k, address);
+                if (!pte_present(*pte_k))
+                        goto no_context;
+                return;
+        }
+}
+
-- 
2.7.4





[Index of Archives]     [Linux Kernel]     [Kernel Newbies]     [x86 Platform Driver]     [Netdev]     [Linux Wireless]     [Netfilter]     [Bugtraq]     [Linux Filesystems]     [Yosemite Discussion]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]

  Powered by Linux