csky: Exception handling and mm-fault

This patch adds exception handling code, cpuinfo and mm-fault code.

Signed-off-by: Guo Ren <ren_guo@c-sky.com>
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Guo Ren 2018-09-05 14:25:08 +08:00
parent 9143a9359d
commit 081860b970
9 changed files with 1546 additions and 0 deletions

326
arch/csky/abiv1/alignment.c Normal file
View File

@ -0,0 +1,326 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/ptrace.h>
static int align_enable = 1;
static int align_count;
static inline uint32_t get_ptreg(struct pt_regs *regs, uint32_t rx)
{
return rx == 15 ? regs->lr : *((uint32_t *)&(regs->a0) - 2 + rx);
}
static inline void put_ptreg(struct pt_regs *regs, uint32_t rx, uint32_t val)
{
if (rx == 15)
regs->lr = val;
else
*((uint32_t *)&(regs->a0) - 2 + rx) = val;
}
/*
* Get byte-value from addr and set it to *valp.
*
* Success: return 0
* Failure: return 1
*/
static int ldb_asm(uint32_t addr, uint32_t *valp)
{
uint32_t val;
int err;
if (!access_ok(VERIFY_READ, (void *)addr, 1))
return 1;
asm volatile (
"movi %0, 0\n"
"1:\n"
"ldb %1, (%2)\n"
"br 3f\n"
"2:\n"
"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)
);
*valp = val;
return err;
}
/*
* Put byte-value to addr.
*
* Success: return 0
* Failure: return 1
*/
static int stb_asm(uint32_t addr, uint32_t val)
{
int err;
if (!access_ok(VERIFY_WRITE, (void *)addr, 1))
return 1;
asm volatile (
"movi %0, 0\n"
"1:\n"
"stb %1, (%2)\n"
"br 3f\n"
"2:\n"
"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)
);
return err;
}
/*
* Get half-word from [rx + imm]
*
* Success: return 0
* Failure: return 1
*/
static int ldh_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
{
uint32_t byte0, byte1;
if (ldb_asm(addr, &byte0))
return 1;
addr += 1;
if (ldb_asm(addr, &byte1))
return 1;
byte0 |= byte1 << 8;
put_ptreg(regs, rz, byte0);
return 0;
}
/*
* Store half-word to [rx + imm]
*
* Success: return 0
* Failure: return 1
*/
static int sth_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
{
uint32_t byte0, byte1;
byte0 = byte1 = get_ptreg(regs, rz);
byte0 &= 0xff;
if (stb_asm(addr, byte0))
return 1;
addr += 1;
byte1 = (byte1 >> 8) & 0xff;
if (stb_asm(addr, byte1))
return 1;
return 0;
}
/*
* Get word from [rx + imm]
*
* Success: return 0
* Failure: return 1
*/
static int ldw_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
{
uint32_t byte0, byte1, byte2, byte3;
if (ldb_asm(addr, &byte0))
return 1;
addr += 1;
if (ldb_asm(addr, &byte1))
return 1;
addr += 1;
if (ldb_asm(addr, &byte2))
return 1;
addr += 1;
if (ldb_asm(addr, &byte3))
return 1;
byte0 |= byte1 << 8;
byte0 |= byte2 << 16;
byte0 |= byte3 << 24;
put_ptreg(regs, rz, byte0);
return 0;
}
/*
* Store word to [rx + imm]
*
* Success: return 0
* Failure: return 1
*/
static int stw_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
{
uint32_t byte0, byte1, byte2, byte3;
byte0 = byte1 = byte2 = byte3 = get_ptreg(regs, rz);
byte0 &= 0xff;
if (stb_asm(addr, byte0))
return 1;
addr += 1;
byte1 = (byte1 >> 8) & 0xff;
if (stb_asm(addr, byte1))
return 1;
addr += 1;
byte2 = (byte2 >> 16) & 0xff;
if (stb_asm(addr, byte2))
return 1;
addr += 1;
byte3 = (byte3 >> 24) & 0xff;
if (stb_asm(addr, byte3))
return 1;
align_count++;
return 0;
}
extern int fixup_exception(struct pt_regs *regs);
#define OP_LDH 0xc000
#define OP_STH 0xd000
#define OP_LDW 0x8000
#define OP_STW 0x9000
void csky_alignment(struct pt_regs *regs)
{
int ret;
uint16_t tmp;
uint32_t opcode = 0;
uint32_t rx = 0;
uint32_t rz = 0;
uint32_t imm = 0;
uint32_t addr = 0;
if (!user_mode(regs))
goto bad_area;
ret = get_user(tmp, (uint16_t *)instruction_pointer(regs));
if (ret) {
pr_err("%s get_user failed.\n", __func__);
goto bad_area;
}
opcode = (uint32_t)tmp;
rx = opcode & 0xf;
imm = (opcode >> 4) & 0xf;
rz = (opcode >> 8) & 0xf;
opcode &= 0xf000;
if (rx == 0 || rx == 1 || rz == 0 || rz == 1)
goto bad_area;
switch (opcode) {
case OP_LDH:
addr = get_ptreg(regs, rx) + (imm << 1);
ret = ldh_c(regs, rz, addr);
break;
case OP_LDW:
addr = get_ptreg(regs, rx) + (imm << 2);
ret = ldw_c(regs, rz, addr);
break;
case OP_STH:
addr = get_ptreg(regs, rx) + (imm << 1);
ret = sth_c(regs, rz, addr);
break;
case OP_STW:
addr = get_ptreg(regs, rx) + (imm << 2);
ret = stw_c(regs, rz, addr);
break;
}
if (ret)
goto bad_area;
regs->pc += 2;
return;
bad_area:
if (!user_mode(regs)) {
if (fixup_exception(regs))
return;
bust_spinlocks(1);
pr_alert("%s opcode: %x, rz: %d, rx: %d, imm: %d, addr: %x.\n",
__func__, opcode, rz, rx, imm, addr);
show_regs(regs);
bust_spinlocks(0);
do_exit(SIGKILL);
}
force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)addr, current);
}
static struct ctl_table alignment_tbl[4] = {
{
.procname = "enable",
.data = &align_enable,
.maxlen = sizeof(align_enable),
.mode = 0666,
.proc_handler = &proc_dointvec
},
{
.procname = "count",
.data = &align_count,
.maxlen = sizeof(align_count),
.mode = 0666,
.proc_handler = &proc_dointvec
},
{}
};
static struct ctl_table sysctl_table[2] = {
{
.procname = "csky_alignment",
.mode = 0555,
.child = alignment_tbl},
{}
};
static struct ctl_path sysctl_path[2] = {
{.procname = "csky"},
{}
};
static int __init csky_alignment_init(void)
{
register_sysctl_paths(sysctl_path, sysctl_table);
return 0;
}
arch_initcall(csky_alignment_init);

View File

@ -0,0 +1,160 @@
/* 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_PC 8
#define LSAVE_PSR 12
#define LSAVE_A0 24
#define LSAVE_A1 28
#define LSAVE_A2 32
#define LSAVE_A3 36
#define LSAVE_A4 40
#define LSAVE_A5 44
#define EPC_INCREASE 2
#define EPC_KEEP 0
.macro USPTOKSP
mtcr sp, ss1
mfcr sp, ss0
.endm
.macro KSPTOUSP
mtcr sp, ss0
mfcr sp, ss1
.endm
.macro INCTRAP rx
addi \rx, EPC_INCREASE
.endm
.macro SAVE_ALL epc_inc
mtcr r13, ss2
mfcr r13, epsr
btsti r13, 31
bt 1f
USPTOKSP
1:
subi sp, 32
subi sp, 32
subi sp, 16
stw r13, (sp, 12)
stw lr, (sp, 4)
mfcr lr, epc
movi r13, \epc_inc
add lr, r13
stw lr, (sp, 8)
mfcr lr, ss1
stw lr, (sp, 16)
stw a0, (sp, 20)
stw a0, (sp, 24)
stw a1, (sp, 28)
stw a2, (sp, 32)
stw a3, (sp, 36)
addi sp, 32
addi sp, 8
mfcr r13, ss2
stw r6, (sp)
stw r7, (sp, 4)
stw r8, (sp, 8)
stw r9, (sp, 12)
stw r10, (sp, 16)
stw r11, (sp, 20)
stw r12, (sp, 24)
stw r13, (sp, 28)
stw r14, (sp, 32)
stw r1, (sp, 36)
subi sp, 32
subi sp, 8
.endm
.macro RESTORE_ALL
psrclr ie
ldw lr, (sp, 4)
ldw a0, (sp, 8)
mtcr a0, epc
ldw a0, (sp, 12)
mtcr a0, epsr
btsti a0, 31
ldw a0, (sp, 16)
mtcr a0, ss1
ldw a0, (sp, 24)
ldw a1, (sp, 28)
ldw a2, (sp, 32)
ldw a3, (sp, 36)
addi sp, 32
addi sp, 8
ldw r6, (sp)
ldw r7, (sp, 4)
ldw r8, (sp, 8)
ldw r9, (sp, 12)
ldw r10, (sp, 16)
ldw r11, (sp, 20)
ldw r12, (sp, 24)
ldw r13, (sp, 28)
ldw r14, (sp, 32)
ldw r1, (sp, 36)
addi sp, 32
addi sp, 8
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
.macro SETUP_MMU rx
lrw \rx, PHYS_OFFSET | 0xe
cpwcr \rx, cpcr30
lrw \rx, (PHYS_OFFSET + 0x20000000) | 0xe
cpwcr \rx, cpcr31
.endm
#endif /* __ASM_CSKY_ENTRY_H */

View File

@ -0,0 +1,156 @@
/* 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_PC 8
#define LSAVE_PSR 12
#define LSAVE_A0 24
#define LSAVE_A1 28
#define LSAVE_A2 32
#define LSAVE_A3 36
#define EPC_INCREASE 4
#define EPC_KEEP 0
#define KSPTOUSP
#define USPTOKSP
#define usp cr<14, 1>
.macro INCTRAP rx
addi \rx, EPC_INCREASE
.endm
.macro SAVE_ALL epc_inc
subi sp, 152
stw tls, (sp, 0)
stw lr, (sp, 4)
mfcr lr, epc
movi tls, \epc_inc
add lr, tls
stw lr, (sp, 8)
mfcr lr, epsr
stw lr, (sp, 12)
mfcr lr, usp
stw lr, (sp, 16)
stw a0, (sp, 20)
stw a0, (sp, 24)
stw a1, (sp, 28)
stw a2, (sp, 32)
stw a3, (sp, 36)
addi sp, 40
stm r4-r13, (sp)
addi sp, 40
stm r16-r30, (sp)
#ifdef CONFIG_CPU_HAS_HILO
mfhi lr
stw lr, (sp, 60)
mflo lr
stw lr, (sp, 64)
#endif
subi sp, 80
.endm
.macro RESTORE_ALL
psrclr ie
ldw tls, (sp, 0)
ldw lr, (sp, 4)
ldw a0, (sp, 8)
mtcr a0, epc
ldw a0, (sp, 12)
mtcr a0, epsr
ldw a0, (sp, 16)
mtcr a0, usp
#ifdef CONFIG_CPU_HAS_HILO
ldw a0, (sp, 140)
mthi a0
ldw a0, (sp, 144)
mtlo a0
#endif
ldw a0, (sp, 24)
ldw a1, (sp, 28)
ldw a2, (sp, 32)
ldw a3, (sp, 36)
addi sp, 40
ldm r4-r13, (sp)
addi sp, 40
ldm r16-r30, (sp)
addi sp, 72
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 RD_PGDR_K rx
mfcr \rx, cr<28, 15>
.endm
.macro WR_MEH rx
mtcr \rx, cr<4, 15>
.endm
.macro WR_MCIR rx
mtcr \rx, cr<8, 15>
.endm
.macro SETUP_MMU rx
lrw \rx, PHYS_OFFSET | 0xe
mtcr \rx, cr<30, 15>
lrw \rx, (PHYS_OFFSET + 0x20000000) | 0xe
mtcr \rx, cr<31, 15>
.endm
#endif /* __ASM_CSKY_ENTRY_H */

View File

@ -0,0 +1,44 @@
/* 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) \
do { \
vec_base[i] = (void *)func; \
} while (0)
void csky_alignment(struct pt_regs *regs);
#endif /* __ASM_CSKY_TRAPS_H */

View File

@ -0,0 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 */
// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
#include <uapi/asm/unistd.h>

View File

@ -0,0 +1,79 @@
// 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 <linux/memblock.h>
#include <abi/reg_ops.h>
static void percpu_print(void *arg)
{
struct seq_file *m = (struct seq_file *)arg;
unsigned int cur, next, i;
seq_printf(m, "processor : %d\n", smp_processor_id());
seq_printf(m, "C-SKY CPU model : %s\n", CSKYCPU_DEF_NAME);
/* read processor id, max is 100 */
cur = mfcr("cr13");
for (i = 0; i < 100; i++) {
seq_printf(m, "product info[%d] : 0x%08x\n", i, cur);
next = mfcr("cr13");
/* some CPU only has one id reg */
if (cur == next)
break;
cur = next;
/* cpid index is 31-28, reset */
if (!(next >> 28)) {
while ((mfcr("cr13") >> 28) != i);
break;
}
}
/* CPU feature regs, setup by bootloader or gdbinit */
seq_printf(m, "hint (CPU funcs): 0x%08x\n", mfcr_hint());
seq_printf(m, "ccr (L1C & MMU): 0x%08x\n", mfcr("cr18"));
seq_printf(m, "ccr2 (L2C) : 0x%08x\n", mfcr_ccr2());
seq_printf(m, "\n");
}
static int c_show(struct seq_file *m, void *v)
{
int cpu;
for_each_online_cpu(cpu)
smp_call_function_single(cpu, percpu_print, m, true);
#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,
};

396
arch/csky/kernel/entry.S Normal file
View File

@ -0,0 +1,396 @@
/* 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>
#define PTE_INDX_MSK 0xffc
#define PTE_INDX_SHIFT 10
#define _PGDIR_SHIFT 22
.macro tlbop_begin name, val0, val1, val2
ENTRY(csky_\name)
mtcr a3, ss2
mtcr r6, ss3
mtcr a2, ss4
RD_PGDR r6
RD_MEH a3
#ifdef CONFIG_CPU_HAS_TLBI
tlbi.vaas a3
sync.is
btsti a3, 31
bf 1f
RD_PGDR_K r6
1:
#else
bgeni a2, 31
WR_MCIR a2
bgeni a2, 25
WR_MCIR a2
#endif
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 EPC_KEEP
.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
#ifndef CONFIG_CPU_HAS_LDSTEX
jbsr csky_cmpxchg_fixup
#endif
tlbop_end 1
ENTRY(csky_systemcall)
SAVE_ALL EPC_INCREASE
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
ldw r11, (r13)
cmpnei r11, 0
bf ret_from_exception
mov r9, sp
bmaski r10, THREAD_SHIFT
andn r9, r10
ldw r8, (r9, TINFO_FLAGS)
btsti r8, TIF_SYSCALL_TRACE
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 /* sp = pt_regs pointer */
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 /* sp = pt_regs pointer */
jbsr syscall_trace
syscall_exit_work:
ld syscallid, (sp, LSAVE_PSR)
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
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
ret_from_exception:
ld syscallid, (sp, LSAVE_PSR)
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
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
exit_work:
mov a0, sp /* Stack address is arg[0] */
jbsr set_esp0 /* Call C level */
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
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
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 for abiv1:
 * 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, THREAD_SHIFT
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 EPC_KEEP
psrset ee
movi r11_sig, 0 /* r11 = 0, Not a syscall. */
#ifdef CONFIG_PREEMPT
mov r9, sp /* Get current stack pointer */
bmaski r10, THREAD_SHIFT
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
jbsr csky_do_IRQ
#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
addu a3, a0
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
stw sp, (a3, THREAD_KSP)
#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
addu a3, a1
ldw sp, (a3, THREAD_KSP) /* Set next kernel sp */
#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__)
addi r7, a1, TASK_THREAD_INFO
ldw tls, (r7, TINFO_TP_VALUE)
#endif
RESTORE_SWITCH_STACK
rts
ENDPROC(__switch_to)

169
arch/csky/kernel/traps.c Normal file
View File

@ -0,0 +1,169 @@
// 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);
/* Defined in head.S */
asmlinkage void _start_smp_secondary(void);
void __init pre_trap_init(void)
{
int i;
mtcr("vbr", vec_base);
for (i = 1; i < 128; i++)
VEC_INIT(i, csky_trap);
}
void __init trap_init(void)
{
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
#ifdef CONFIG_SMP
mtcr("cr<28, 0>", virt_to_phys(vec_base));
VEC_INIT(VEC_RESET, (void *)virt_to_phys(_start_smp_secondary));
#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)
{
#ifdef CONFIG_CPU_CK810
static unsigned long prev_pc;
if ((regs->pc == prev_pc) && prev_pc != 0) {
prev_pc = 0;
} else {
prev_pc = regs->pc;
return;
}
#endif
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;
force_sig_fault(SIGSEGV, 0, (void __user *)regs->pc, current);
}
#define USR_BKPT 0x1464
asmlinkage void trap_c(struct pt_regs *regs)
{
int sig;
unsigned long vector;
siginfo_t info;
vector = (mfcr("psr") >> 16) & 0xff;
switch (vector) {
case VEC_ZERODIV:
sig = SIGFPE;
break;
/* ptrace */
case VEC_TRACE:
info.si_code = TRAP_TRACE;
sig = SIGTRAP;
break;
case VEC_ILLEGAL:
#ifndef CONFIG_CPU_NO_USER_BKPT
if (*(uint16_t *)instruction_pointer(regs) != USR_BKPT)
#endif
{
sig = SIGILL;
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 = SIGSEGV;
break;
}
send_sig(sig, current, 0);
}
asmlinkage void set_esp0(unsigned long ssp)
{
current->thread.esp0 = ssp;
}

212
arch/csky/mm/fault.c Normal file
View File

@ -0,0 +1,212 @@
// 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>
int fixup_exception(struct pt_regs *regs)
{
const struct exception_table_entry *fixup;
fixup = search_exception_tables(instruction_pointer(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;
int si_code;
int fault;
unsigned long address = mmu_meh & PAGE_MASK;
si_code = SEGV_MAPERR;
#ifndef CONFIG_CPU_HAS_TLBI
/*
* 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) &&
unlikely(address <= VMALLOC_END)) {
/*
* 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;
}
#endif
/*
* 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:
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;
force_sig_fault(SIGSEGV, si_code, (void __user *)address, current);
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);
pr_alert("Unable to %s at vaddr: %08lx, epc: %08lx\n",
__func__, 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;
force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)address, current);
}