提交 cc26ac5d 编写于 作者: H He Sheng 提交者: guzitao

sw64: fix instruction fault handler

Sunway inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I5PNCX

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

An issue says that when parent traces a child, child does not terminate
after signal SIGILL is delivered. This is because BPT/GENTRAP/OPDEC make
regs->pc = exc_pc + 4 in hmcode. It used to send SIGILL to child with
incorrect regs->pc. As a result, after PTRACE_CONT request is made on
child, it will skip the exception instruction and go on.

This patch makes `regs->pc = exc_pc` for SIGILL to fix this issue, then
restructures code to make it clear.
Signed-off-by: NHe Sheng <hesheng@wxiat.com>
Signed-off-by: NGu Zitao <guzitao@wxiat.com>
上级 f92db6f8
...@@ -31,6 +31,14 @@ ...@@ -31,6 +31,14 @@
#include "proto.h" #include "proto.h"
enum SW64_IF_TYPES {
IF_BREAKPOINT = 0,
IF_RESERVED,
IF_GENTRAP,
IF_FEN,
IF_OPDEC,
};
void show_regs(struct pt_regs *regs) void show_regs(struct pt_regs *regs)
{ {
show_regs_print_info(KERN_DEFAULT); show_regs_print_info(KERN_DEFAULT);
...@@ -155,6 +163,10 @@ do_entArith(unsigned long summary, unsigned long write_mask, ...@@ -155,6 +163,10 @@ do_entArith(unsigned long summary, unsigned long write_mask,
force_sig_fault(SIGFPE, si_code, (void __user *)regs->pc, 0); force_sig_fault(SIGFPE, si_code, (void __user *)regs->pc, 0);
} }
/*
* BPT/GENTRAP/OPDEC make regs->pc = exc_pc + 4. debugger should
* do something necessary to handle it correctly.
*/
asmlinkage void asmlinkage void
do_entIF(unsigned long inst_type, struct pt_regs *regs) do_entIF(unsigned long inst_type, struct pt_regs *regs)
{ {
...@@ -164,35 +176,23 @@ do_entIF(unsigned long inst_type, struct pt_regs *regs) ...@@ -164,35 +176,23 @@ do_entIF(unsigned long inst_type, struct pt_regs *regs)
type = inst_type & 0xffffffff; type = inst_type & 0xffffffff;
inst = inst_type >> 32; inst = inst_type >> 32;
if (!user_mode(regs) && type != 4) { if (!user_mode(regs) && type != IF_OPDEC) {
if (type == 1) { if (type == IF_BREAKPOINT) {
const unsigned int *data
= (const unsigned int *) regs->pc;
printk("Kernel bug at %s:%d\n",
(const char *)(data[1] | (long)data[2] << 32),
data[0]);
} else if (type == 0) {
/* support kgdb */ /* support kgdb */
notify_die(0, "kgdb trap", regs, 0, 0, SIGTRAP); notify_die(0, "kgdb trap", regs, 0, 0, SIGTRAP);
return; return;
} }
die((type == 1 ? "Kernel Bug" : "Instruction fault"), die((type == IF_RESERVED ? "Kernel Bug" : "Instruction fault"),
regs, type); regs, type);
} }
switch (type) { switch (type) {
case 0: /* breakpoint */ case IF_BREAKPOINT: /* gdb do pc-4 for sigtrap */
if (ptrace_cancel_bpt(current))
regs->pc -= 4; /* make pc point to former bpt */
force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->pc, 0); force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->pc, 0);
return; return;
case 1: /* bugcheck */ case IF_GENTRAP:
force_sig_fault(SIGTRAP, TRAP_UNK, (void __user *)regs->pc, 0); regs->pc -= 4;
return;
case 2: /* gentrap */
switch ((long)regs->r16) { switch ((long)regs->r16) {
case GEN_INTOVF: case GEN_INTOVF:
signo = SIGFPE; signo = SIGFPE;
...@@ -245,6 +245,7 @@ do_entIF(unsigned long inst_type, struct pt_regs *regs) ...@@ -245,6 +245,7 @@ do_entIF(unsigned long inst_type, struct pt_regs *regs)
case GEN_SUBRNG6: case GEN_SUBRNG6:
case GEN_SUBRNG7: case GEN_SUBRNG7:
default: default:
regs->pc += 4;
signo = SIGTRAP; signo = SIGTRAP;
code = TRAP_UNK; code = TRAP_UNK;
break; break;
...@@ -253,7 +254,11 @@ do_entIF(unsigned long inst_type, struct pt_regs *regs) ...@@ -253,7 +254,11 @@ do_entIF(unsigned long inst_type, struct pt_regs *regs)
force_sig_fault(signo, code, (void __user *)regs->pc, regs->r16); force_sig_fault(signo, code, (void __user *)regs->pc, regs->r16);
return; return;
case 4: /* opDEC */ case IF_FEN:
fpu_enable();
return;
case IF_OPDEC:
switch (inst) { switch (inst) {
case BREAK_KPROBE: case BREAK_KPROBE:
if (notify_die(DIE_BREAK, "kprobe", regs, 0, 0, SIGTRAP) == NOTIFY_STOP) if (notify_die(DIE_BREAK, "kprobe", regs, 0, 0, SIGTRAP) == NOTIFY_STOP)
...@@ -268,26 +273,15 @@ do_entIF(unsigned long inst_type, struct pt_regs *regs) ...@@ -268,26 +273,15 @@ do_entIF(unsigned long inst_type, struct pt_regs *regs)
if (notify_die(DIE_UPROBE_XOL, "uprobe_xol", regs, 0, 0, SIGTRAP) == NOTIFY_STOP) if (notify_die(DIE_UPROBE_XOL, "uprobe_xol", regs, 0, 0, SIGTRAP) == NOTIFY_STOP)
return; return;
} }
if (!user_mode(regs))
if (user_mode(regs))
regs->pc -= 4;
else
die("Instruction fault", regs, type); die("Instruction fault", regs, type);
break; break;
case 3: /* FEN fault */
/*
* Irritating users can call HMC_clrfen to disable the
* FPU for the process. The kernel will then trap to
* save and restore the FP registers.
* Given that GCC by default generates code that uses the
* FP registers, HMC_clrfen is not useful except for DoS
* attacks. So turn the bleeding FPU back on and be done
* with it.
*/
fpu_enable();
return;
case 5: /* illoc */
default: /* unexpected instruction-fault type */ default: /* unexpected instruction-fault type */
regs->pc -= 4;
break; break;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册