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:
parent
9143a9359d
commit
081860b970
|
@ -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);
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -0,0 +1,4 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
|
||||
|
||||
#include <uapi/asm/unistd.h>
|
|
@ -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,
|
||||
};
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue