[S390] cleanup trap handling
Move the program interruption code and the translation exception identifier to the pt_regs structure as 'int_code' and 'int_parm_long' and make the first level interrupt handler in entry[64].S store the two values. That makes it possible to drop 'prot_addr' and 'trap_no' from the thread_struct and to reduce the number of arguments to a lot of functions. Finally un-inline do_trap. Overall this saves 5812 bytes in the .text section of the 64 bit kernel. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
679e2ea733
commit
aa33c8cbba
12 changed files with 158 additions and 210 deletions
|
@ -22,6 +22,6 @@ enum die_val {
|
|||
DIE_NMI_IPI,
|
||||
};
|
||||
|
||||
extern void die(const char *, struct pt_regs *, long);
|
||||
extern void die(struct pt_regs *, const char *);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -80,8 +80,6 @@ struct thread_struct {
|
|||
unsigned int acrs[NUM_ACRS];
|
||||
unsigned long ksp; /* kernel stack pointer */
|
||||
mm_segment_t mm_segment;
|
||||
unsigned long prot_addr; /* address of protection-excep. */
|
||||
unsigned int trap_no;
|
||||
unsigned long gmap_addr; /* address of last gmap fault. */
|
||||
struct per_regs per_user; /* User specified PER registers */
|
||||
struct per_event per_event; /* Cause of the last PER trap */
|
||||
|
|
|
@ -324,7 +324,8 @@ struct pt_regs
|
|||
psw_t psw;
|
||||
unsigned long gprs[NUM_GPRS];
|
||||
unsigned long orig_gpr2;
|
||||
unsigned int svc_code;
|
||||
unsigned int int_code;
|
||||
unsigned long int_parm_long;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -27,7 +27,7 @@ static inline long syscall_get_nr(struct task_struct *task,
|
|||
struct pt_regs *regs)
|
||||
{
|
||||
return test_tsk_thread_flag(task, TIF_SYSCALL) ?
|
||||
(regs->svc_code & 0xffff) : -1;
|
||||
(regs->int_code & 0xffff) : -1;
|
||||
}
|
||||
|
||||
static inline void syscall_rollback(struct task_struct *task,
|
||||
|
|
|
@ -45,7 +45,8 @@ int main(void)
|
|||
DEFINE(__PT_PSW, offsetof(struct pt_regs, psw));
|
||||
DEFINE(__PT_GPRS, offsetof(struct pt_regs, gprs));
|
||||
DEFINE(__PT_ORIG_GPR2, offsetof(struct pt_regs, orig_gpr2));
|
||||
DEFINE(__PT_SVC_CODE, offsetof(struct pt_regs, svc_code));
|
||||
DEFINE(__PT_INT_CODE, offsetof(struct pt_regs, int_code));
|
||||
DEFINE(__PT_INT_PARM_LONG, offsetof(struct pt_regs, int_parm_long));
|
||||
DEFINE(__PT_SIZE, sizeof(struct pt_regs));
|
||||
BLANK();
|
||||
DEFINE(__SF_BACKCHAIN, offsetof(struct stack_frame, back_chain));
|
||||
|
|
|
@ -501,8 +501,12 @@ static int setup_frame32(int sig, struct k_sigaction *ka,
|
|||
|
||||
/* We forgot to include these in the sigcontext.
|
||||
To avoid breaking binary compatibility, they are passed as args. */
|
||||
regs->gprs[4] = current->thread.trap_no;
|
||||
regs->gprs[5] = current->thread.prot_addr;
|
||||
if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL ||
|
||||
sig == SIGTRAP || sig == SIGFPE) {
|
||||
/* set extra registers only for synchronous signals */
|
||||
regs->gprs[4] = regs->int_code & 127;
|
||||
regs->gprs[5] = regs->int_parm_long;
|
||||
}
|
||||
|
||||
/* Place signal number on stack to allow backtrace from handler. */
|
||||
if (__put_user(regs->gprs[2], (int __force __user *) &frame->signo))
|
||||
|
|
|
@ -184,16 +184,16 @@ sysc_vtime:
|
|||
stm %r0,%r7,__PT_R0(%r11)
|
||||
mvc __PT_R8(32,%r11),__LC_SAVE_AREA_SYNC
|
||||
mvc __PT_PSW(8,%r11),__LC_SVC_OLD_PSW
|
||||
mvc __PT_SVC_CODE(4,%r11),__LC_SVC_ILC
|
||||
mvc __PT_INT_CODE(4,%r11),__LC_SVC_ILC
|
||||
sysc_do_svc:
|
||||
oi __TI_flags+3(%r12),_TIF_SYSCALL
|
||||
lh %r8,__PT_SVC_CODE+2(%r11)
|
||||
lh %r8,__PT_INT_CODE+2(%r11)
|
||||
sla %r8,2 # shift and test for svc0
|
||||
jnz sysc_nr_ok
|
||||
# svc 0: system call number in %r1
|
||||
cl %r1,BASED(.Lnr_syscalls)
|
||||
jnl sysc_nr_ok
|
||||
sth %r1,__PT_SVC_CODE+2(%r11)
|
||||
sth %r1,__PT_INT_CODE+2(%r11)
|
||||
lr %r8,%r1
|
||||
sla %r8,2
|
||||
sysc_nr_ok:
|
||||
|
@ -266,9 +266,9 @@ sysc_sigpending:
|
|||
jno sysc_return
|
||||
lm %r2,%r7,__PT_R2(%r11) # load svc arguments
|
||||
xr %r8,%r8 # svc 0 returns -ENOSYS
|
||||
clc __PT_SVC_CODE+2(2,%r11),BASED(.Lnr_syscalls+2)
|
||||
clc __PT_INT_CODE+2(2,%r11),BASED(.Lnr_syscalls+2)
|
||||
jnl sysc_nr_ok # invalid svc number -> do svc 0
|
||||
lh %r8,__PT_SVC_CODE+2(%r11) # load new svc number
|
||||
lh %r8,__PT_INT_CODE+2(%r11) # load new svc number
|
||||
sla %r8,2
|
||||
j sysc_nr_ok # restart svc
|
||||
|
||||
|
@ -300,7 +300,7 @@ sysc_tracesys:
|
|||
lr %r2,%r11 # pass pointer to pt_regs
|
||||
la %r3,0
|
||||
xr %r0,%r0
|
||||
icm %r0,3,__PT_SVC_CODE+2(%r11)
|
||||
icm %r0,3,__PT_INT_CODE+2(%r11)
|
||||
st %r0,__PT_R2(%r11)
|
||||
basr %r14,%r1 # call do_syscall_trace_enter
|
||||
cl %r2,BASED(.Lnr_syscalls)
|
||||
|
@ -396,6 +396,8 @@ ENTRY(pgm_check_handler)
|
|||
stm %r0,%r7,__PT_R0(%r11)
|
||||
mvc __PT_R8(32,%r11),__LC_SAVE_AREA_SYNC
|
||||
stm %r8,%r9,__PT_PSW(%r11)
|
||||
mvc __PT_INT_CODE(4,%r11),__LC_PGM_ILC
|
||||
mvc __PT_INT_PARM_LONG(4,%r11),__LC_TRANS_EXC_CODE
|
||||
tm __LC_PGM_ILC+3,0x80 # check for per exception
|
||||
jz 0f
|
||||
l %r1,__TI_task(%r12)
|
||||
|
@ -405,13 +407,11 @@ ENTRY(pgm_check_handler)
|
|||
mvc __THREAD_per_address(4,%r1),__LC_PER_ADDRESS
|
||||
mvc __THREAD_per_cause(2,%r1),__LC_PER_CAUSE
|
||||
mvc __THREAD_per_paid(1,%r1),__LC_PER_PAID
|
||||
0: l %r3,__LC_PGM_ILC # load program interruption code
|
||||
l %r4,__LC_TRANS_EXC_CODE
|
||||
REENABLE_IRQS
|
||||
0: REENABLE_IRQS
|
||||
xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15)
|
||||
l %r1,BASED(.Ljump_table)
|
||||
la %r10,0x7f
|
||||
nr %r10,%r3
|
||||
n %r10,__PT_INT_CODE(%r11)
|
||||
je sysc_return
|
||||
sll %r10,2
|
||||
l %r1,0(%r10,%r1) # load address of handler routine
|
||||
|
@ -858,7 +858,7 @@ cleanup_system_call:
|
|||
mvc __PT_R8(32,%r15),__LC_SAVE_AREA_SYNC
|
||||
stm %r0,%r7,__PT_R0(%r15)
|
||||
mvc __PT_PSW(8,%r15),__LC_SVC_OLD_PSW
|
||||
mvc __PT_SVC_CODE(4,%r15),__LC_SVC_ILC
|
||||
mvc __PT_INT_CODE(4,%r15),__LC_SVC_ILC
|
||||
# setup saved register 15
|
||||
ahi %r15,-STACK_FRAME_OVERHEAD
|
||||
st %r15,28(%r11) # r15 stack pointer
|
||||
|
|
|
@ -6,15 +6,15 @@
|
|||
#include <asm/ptrace.h>
|
||||
|
||||
|
||||
extern void (*pgm_check_table[128])(struct pt_regs *, long, unsigned long);
|
||||
extern void (*pgm_check_table[128])(struct pt_regs *);
|
||||
extern void *restart_stack;
|
||||
|
||||
asmlinkage long do_syscall_trace_enter(struct pt_regs *regs);
|
||||
asmlinkage void do_syscall_trace_exit(struct pt_regs *regs);
|
||||
|
||||
void do_protection_exception(struct pt_regs *, long, unsigned long);
|
||||
void do_dat_exception(struct pt_regs *, long, unsigned long);
|
||||
void do_asce_exception(struct pt_regs *, long, unsigned long);
|
||||
void do_protection_exception(struct pt_regs *regs);
|
||||
void do_dat_exception(struct pt_regs *regs);
|
||||
void do_asce_exception(struct pt_regs *regs);
|
||||
|
||||
void do_per_trap(struct pt_regs *regs);
|
||||
void syscall_trace(struct pt_regs *regs, int entryexit);
|
||||
|
@ -28,7 +28,7 @@ void do_extint(struct pt_regs *regs, unsigned int, unsigned int, unsigned long);
|
|||
void do_restart(void);
|
||||
int __cpuinit start_secondary(void *cpuvoid);
|
||||
void __init startup_init(void);
|
||||
void die(const char * str, struct pt_regs * regs, long err);
|
||||
void die(struct pt_regs *regs, const char *str);
|
||||
|
||||
void __init time_init(void);
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/*
|
||||
* arch/s390/kernel/entry64.S
|
||||
* S390 low-level entry points.
|
||||
|
@ -200,17 +199,17 @@ sysc_vtime:
|
|||
stmg %r0,%r7,__PT_R0(%r11)
|
||||
mvc __PT_R8(64,%r11),__LC_SAVE_AREA_SYNC
|
||||
mvc __PT_PSW(16,%r11),__LC_SVC_OLD_PSW
|
||||
mvc __PT_SVC_CODE(4,%r11),__LC_SVC_ILC
|
||||
mvc __PT_INT_CODE(4,%r11),__LC_SVC_ILC
|
||||
sysc_do_svc:
|
||||
oi __TI_flags+7(%r12),_TIF_SYSCALL
|
||||
llgh %r8,__PT_SVC_CODE+2(%r11)
|
||||
llgh %r8,__PT_INT_CODE+2(%r11)
|
||||
slag %r8,%r8,2 # shift and test for svc 0
|
||||
jnz sysc_nr_ok
|
||||
# svc 0: system call number in %r1
|
||||
llgfr %r1,%r1 # clear high word in r1
|
||||
cghi %r1,NR_syscalls
|
||||
jnl sysc_nr_ok
|
||||
sth %r1,__PT_SVC_CODE+2(%r11)
|
||||
sth %r1,__PT_INT_CODE+2(%r11)
|
||||
slag %r8,%r1,2
|
||||
sysc_nr_ok:
|
||||
larl %r10,sys_call_table # 64 bit system call table
|
||||
|
@ -288,7 +287,7 @@ sysc_sigpending:
|
|||
jno sysc_return
|
||||
lmg %r2,%r7,__PT_R2(%r11) # load svc arguments
|
||||
lghi %r8,0 # svc 0 returns -ENOSYS
|
||||
lh %r1,__PT_SVC_CODE+2(%r11) # load new svc number
|
||||
lh %r1,__PT_INT_CODE+2(%r11) # load new svc number
|
||||
cghi %r1,NR_syscalls
|
||||
jnl sysc_nr_ok # invalid svc number -> do svc 0
|
||||
slag %r8,%r1,2
|
||||
|
@ -318,7 +317,7 @@ sysc_singlestep:
|
|||
sysc_tracesys:
|
||||
lgr %r2,%r11 # pass pointer to pt_regs
|
||||
la %r3,0
|
||||
llgh %r0,__PT_SVC_CODE+2(%r11)
|
||||
llgh %r0,__PT_INT_CODE+2(%r11)
|
||||
stg %r0,__PT_R2(%r11)
|
||||
brasl %r14,do_syscall_trace_enter
|
||||
lghi %r0,NR_syscalls
|
||||
|
@ -411,6 +410,8 @@ ENTRY(pgm_check_handler)
|
|||
stmg %r0,%r7,__PT_R0(%r11)
|
||||
mvc __PT_R8(64,%r11),__LC_SAVE_AREA_SYNC
|
||||
stmg %r8,%r9,__PT_PSW(%r11)
|
||||
mvc __PT_INT_CODE(4,%r11),__LC_PGM_ILC
|
||||
mvc __PT_INT_PARM_LONG(8,%r11),__LC_TRANS_EXC_CODE
|
||||
stg %r10,__PT_ARGS(%r11)
|
||||
tm __LC_PGM_ILC+3,0x80 # check for per exception
|
||||
jz 0f
|
||||
|
@ -421,15 +422,13 @@ ENTRY(pgm_check_handler)
|
|||
mvc __THREAD_per_address(8,%r1),__LC_PER_ADDRESS
|
||||
mvc __THREAD_per_cause(2,%r1),__LC_PER_CAUSE
|
||||
mvc __THREAD_per_paid(1,%r1),__LC_PER_PAID
|
||||
0: lgf %r3,__LC_PGM_ILC # load program interruption code
|
||||
lg %r4,__LC_TRANS_EXC_CODE
|
||||
REENABLE_IRQS
|
||||
0: REENABLE_IRQS
|
||||
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
|
||||
lghi %r10,0x7f
|
||||
ngr %r10,%r3
|
||||
je sysc_return
|
||||
sll %r10,3
|
||||
larl %r1,pgm_check_table
|
||||
llgh %r10,__PT_INT_CODE+2(%r11)
|
||||
nill %r10,0x007f
|
||||
sll %r10,3
|
||||
je sysc_return
|
||||
lg %r1,0(%r10,%r1) # load address of handler routine
|
||||
lgr %r2,%r11 # pass pointer to pt_regs
|
||||
basr %r14,%r1 # branch to interrupt-handler
|
||||
|
@ -877,7 +876,7 @@ cleanup_system_call:
|
|||
mvc __PT_R8(64,%r15),__LC_SAVE_AREA_SYNC
|
||||
stmg %r0,%r7,__PT_R0(%r15)
|
||||
mvc __PT_PSW(16,%r15),__LC_SVC_OLD_PSW
|
||||
mvc __PT_SVC_CODE(4,%r15),__LC_SVC_ILC
|
||||
mvc __PT_INT_CODE(4,%r15),__LC_SVC_ILC
|
||||
# setup saved register r15
|
||||
aghi %r15,-STACK_FRAME_OVERHEAD
|
||||
stg %r15,56(%r11) # r15 stack pointer
|
||||
|
|
|
@ -302,9 +302,13 @@ static int setup_frame(int sig, struct k_sigaction *ka,
|
|||
|
||||
/* We forgot to include these in the sigcontext.
|
||||
To avoid breaking binary compatibility, they are passed as args. */
|
||||
regs->gprs[4] = current->thread.trap_no;
|
||||
regs->gprs[5] = current->thread.prot_addr;
|
||||
regs->gprs[6] = task_thread_info(current)->last_break;
|
||||
if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL ||
|
||||
sig == SIGTRAP || sig == SIGFPE) {
|
||||
/* set extra registers only for synchronous signals */
|
||||
regs->gprs[4] = regs->int_code & 127;
|
||||
regs->gprs[5] = regs->int_parm_long;
|
||||
regs->gprs[6] = task_thread_info(current)->last_break;
|
||||
}
|
||||
|
||||
/* Place signal number on stack to allow backtrace from handler. */
|
||||
if (__put_user(regs->gprs[2], (int __user *) &frame->signo))
|
||||
|
@ -434,13 +438,13 @@ void do_signal(struct pt_regs *regs)
|
|||
* call information.
|
||||
*/
|
||||
current_thread_info()->system_call =
|
||||
test_thread_flag(TIF_SYSCALL) ? regs->svc_code : 0;
|
||||
test_thread_flag(TIF_SYSCALL) ? regs->int_code : 0;
|
||||
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
|
||||
|
||||
if (signr > 0) {
|
||||
/* Whee! Actually deliver the signal. */
|
||||
if (current_thread_info()->system_call) {
|
||||
regs->svc_code = current_thread_info()->system_call;
|
||||
regs->int_code = current_thread_info()->system_call;
|
||||
/* Check for system call restarting. */
|
||||
switch (regs->gprs[2]) {
|
||||
case -ERESTART_RESTARTBLOCK:
|
||||
|
@ -457,7 +461,7 @@ void do_signal(struct pt_regs *regs)
|
|||
regs->gprs[2] = regs->orig_gpr2;
|
||||
regs->psw.addr =
|
||||
__rewind_psw(regs->psw,
|
||||
regs->svc_code >> 16);
|
||||
regs->int_code >> 16);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -488,11 +492,11 @@ void do_signal(struct pt_regs *regs)
|
|||
/* No handlers present - check for system call restart */
|
||||
clear_thread_flag(TIF_SYSCALL);
|
||||
if (current_thread_info()->system_call) {
|
||||
regs->svc_code = current_thread_info()->system_call;
|
||||
regs->int_code = current_thread_info()->system_call;
|
||||
switch (regs->gprs[2]) {
|
||||
case -ERESTART_RESTARTBLOCK:
|
||||
/* Restart with sys_restart_syscall */
|
||||
regs->svc_code = __NR_restart_syscall;
|
||||
regs->int_code = __NR_restart_syscall;
|
||||
/* fallthrough */
|
||||
case -ERESTARTNOHAND:
|
||||
case -ERESTARTSYS:
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
#include <asm/debug.h>
|
||||
#include "entry.h"
|
||||
|
||||
void (*pgm_check_table[128])(struct pt_regs *, long, unsigned long);
|
||||
void (*pgm_check_table[128])(struct pt_regs *regs);
|
||||
|
||||
int show_unhandled_signals;
|
||||
|
||||
|
@ -234,7 +234,7 @@ void show_regs(struct pt_regs *regs)
|
|||
|
||||
static DEFINE_SPINLOCK(die_lock);
|
||||
|
||||
void die(const char * str, struct pt_regs * regs, long err)
|
||||
void die(struct pt_regs *regs, const char *str)
|
||||
{
|
||||
static int die_counter;
|
||||
|
||||
|
@ -243,7 +243,7 @@ void die(const char * str, struct pt_regs * regs, long err)
|
|||
console_verbose();
|
||||
spin_lock_irq(&die_lock);
|
||||
bust_spinlocks(1);
|
||||
printk("%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter);
|
||||
printk("%s: %04x [#%d] ", str, regs->int_code & 0xffff, ++die_counter);
|
||||
#ifdef CONFIG_PREEMPT
|
||||
printk("PREEMPT ");
|
||||
#endif
|
||||
|
@ -254,7 +254,7 @@ void die(const char * str, struct pt_regs * regs, long err)
|
|||
printk("DEBUG_PAGEALLOC");
|
||||
#endif
|
||||
printk("\n");
|
||||
notify_die(DIE_OOPS, str, regs, err, current->thread.trap_no, SIGSEGV);
|
||||
notify_die(DIE_OOPS, str, regs, 0, regs->int_code & 0xffff, SIGSEGV);
|
||||
show_regs(regs);
|
||||
bust_spinlocks(0);
|
||||
add_taint(TAINT_DIE);
|
||||
|
@ -267,8 +267,7 @@ void die(const char * str, struct pt_regs * regs, long err)
|
|||
do_exit(SIGSEGV);
|
||||
}
|
||||
|
||||
static void inline report_user_fault(struct pt_regs *regs, long int_code,
|
||||
int signr)
|
||||
static inline void report_user_fault(struct pt_regs *regs, int signr)
|
||||
{
|
||||
if ((task_pid_nr(current) > 1) && !show_unhandled_signals)
|
||||
return;
|
||||
|
@ -276,7 +275,7 @@ static void inline report_user_fault(struct pt_regs *regs, long int_code,
|
|||
return;
|
||||
if (!printk_ratelimit())
|
||||
return;
|
||||
printk("User process fault: interruption code 0x%lX ", int_code);
|
||||
printk("User process fault: interruption code 0x%X ", regs->int_code);
|
||||
print_vma_addr("in ", regs->psw.addr & PSW_ADDR_INSN);
|
||||
printk("\n");
|
||||
show_regs(regs);
|
||||
|
@ -287,19 +286,28 @@ int is_valid_bugaddr(unsigned long addr)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static inline void __kprobes do_trap(long pgm_int_code, int signr, char *str,
|
||||
struct pt_regs *regs, siginfo_t *info)
|
||||
static inline void __user *get_psw_address(struct pt_regs *regs)
|
||||
{
|
||||
if (notify_die(DIE_TRAP, str, regs, pgm_int_code,
|
||||
pgm_int_code, signr) == NOTIFY_STOP)
|
||||
return (void __user *)
|
||||
((regs->psw.addr - (regs->int_code >> 16)) & PSW_ADDR_INSN);
|
||||
}
|
||||
|
||||
static void __kprobes do_trap(struct pt_regs *regs,
|
||||
int si_signo, int si_code, char *str)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
if (notify_die(DIE_TRAP, str, regs, 0,
|
||||
regs->int_code, si_signo) == NOTIFY_STOP)
|
||||
return;
|
||||
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
struct task_struct *tsk = current;
|
||||
|
||||
tsk->thread.trap_no = pgm_int_code & 0xffff;
|
||||
force_sig_info(signr, info, tsk);
|
||||
report_user_fault(regs, pgm_int_code, signr);
|
||||
info.si_signo = si_signo;
|
||||
info.si_errno = 0;
|
||||
info.si_code = si_code;
|
||||
info.si_addr = get_psw_address(regs);
|
||||
force_sig_info(si_signo, &info, current);
|
||||
report_user_fault(regs, si_signo);
|
||||
} else {
|
||||
const struct exception_table_entry *fixup;
|
||||
fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
|
||||
|
@ -311,18 +319,11 @@ static inline void __kprobes do_trap(long pgm_int_code, int signr, char *str,
|
|||
btt = report_bug(regs->psw.addr & PSW_ADDR_INSN, regs);
|
||||
if (btt == BUG_TRAP_TYPE_WARN)
|
||||
return;
|
||||
die(str, regs, pgm_int_code);
|
||||
die(regs, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void __user *get_psw_address(struct pt_regs *regs,
|
||||
long pgm_int_code)
|
||||
{
|
||||
return (void __user *)
|
||||
((regs->psw.addr - (pgm_int_code >> 16)) & PSW_ADDR_INSN);
|
||||
}
|
||||
|
||||
void __kprobes do_per_trap(struct pt_regs *regs)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
@ -339,26 +340,19 @@ void __kprobes do_per_trap(struct pt_regs *regs)
|
|||
force_sig_info(SIGTRAP, &info, current);
|
||||
}
|
||||
|
||||
static void default_trap_handler(struct pt_regs *regs, long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
static void default_trap_handler(struct pt_regs *regs)
|
||||
{
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
report_user_fault(regs, pgm_int_code, SIGSEGV);
|
||||
report_user_fault(regs, SIGSEGV);
|
||||
do_exit(SIGSEGV);
|
||||
} else
|
||||
die("Unknown program exception", regs, pgm_int_code);
|
||||
die(regs, "Unknown program exception");
|
||||
}
|
||||
|
||||
#define DO_ERROR_INFO(name, signr, sicode, str) \
|
||||
static void name(struct pt_regs *regs, long pgm_int_code, \
|
||||
unsigned long trans_exc_code) \
|
||||
static void name(struct pt_regs *regs) \
|
||||
{ \
|
||||
siginfo_t info; \
|
||||
info.si_signo = signr; \
|
||||
info.si_errno = 0; \
|
||||
info.si_code = sicode; \
|
||||
info.si_addr = get_psw_address(regs, pgm_int_code); \
|
||||
do_trap(pgm_int_code, signr, str, regs, &info); \
|
||||
do_trap(regs, signr, sicode, str); \
|
||||
}
|
||||
|
||||
DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR,
|
||||
|
@ -388,42 +382,34 @@ DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,
|
|||
DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN,
|
||||
"translation exception")
|
||||
|
||||
static inline void do_fp_trap(struct pt_regs *regs, void __user *location,
|
||||
int fpc, long pgm_int_code)
|
||||
static inline void do_fp_trap(struct pt_regs *regs, int fpc)
|
||||
{
|
||||
siginfo_t si;
|
||||
|
||||
si.si_signo = SIGFPE;
|
||||
si.si_errno = 0;
|
||||
si.si_addr = location;
|
||||
si.si_code = 0;
|
||||
int si_code = 0;
|
||||
/* FPC[2] is Data Exception Code */
|
||||
if ((fpc & 0x00000300) == 0) {
|
||||
/* bits 6 and 7 of DXC are 0 iff IEEE exception */
|
||||
if (fpc & 0x8000) /* invalid fp operation */
|
||||
si.si_code = FPE_FLTINV;
|
||||
si_code = FPE_FLTINV;
|
||||
else if (fpc & 0x4000) /* div by 0 */
|
||||
si.si_code = FPE_FLTDIV;
|
||||
si_code = FPE_FLTDIV;
|
||||
else if (fpc & 0x2000) /* overflow */
|
||||
si.si_code = FPE_FLTOVF;
|
||||
si_code = FPE_FLTOVF;
|
||||
else if (fpc & 0x1000) /* underflow */
|
||||
si.si_code = FPE_FLTUND;
|
||||
si_code = FPE_FLTUND;
|
||||
else if (fpc & 0x0800) /* inexact */
|
||||
si.si_code = FPE_FLTRES;
|
||||
si_code = FPE_FLTRES;
|
||||
}
|
||||
do_trap(pgm_int_code, SIGFPE,
|
||||
"floating point exception", regs, &si);
|
||||
do_trap(regs, SIGFPE, si_code, "floating point exception");
|
||||
}
|
||||
|
||||
static void __kprobes illegal_op(struct pt_regs *regs, long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
static void __kprobes illegal_op(struct pt_regs *regs)
|
||||
{
|
||||
siginfo_t info;
|
||||
__u8 opcode[6];
|
||||
__u16 __user *location;
|
||||
int signal = 0;
|
||||
|
||||
location = get_psw_address(regs, pgm_int_code);
|
||||
location = get_psw_address(regs);
|
||||
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
|
||||
|
@ -467,44 +453,31 @@ static void __kprobes illegal_op(struct pt_regs *regs, long pgm_int_code,
|
|||
* If we get an illegal op in kernel mode, send it through the
|
||||
* kprobes notifier. If kprobes doesn't pick it up, SIGILL
|
||||
*/
|
||||
if (notify_die(DIE_BPT, "bpt", regs, pgm_int_code,
|
||||
if (notify_die(DIE_BPT, "bpt", regs, 0,
|
||||
3, SIGTRAP) != NOTIFY_STOP)
|
||||
signal = SIGILL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MATHEMU
|
||||
if (signal == SIGFPE)
|
||||
do_fp_trap(regs, location,
|
||||
current->thread.fp_regs.fpc, pgm_int_code);
|
||||
else if (signal == SIGSEGV) {
|
||||
info.si_signo = signal;
|
||||
info.si_errno = 0;
|
||||
info.si_code = SEGV_MAPERR;
|
||||
info.si_addr = (void __user *) location;
|
||||
do_trap(pgm_int_code, signal,
|
||||
"user address fault", regs, &info);
|
||||
} else
|
||||
do_fp_trap(regs, current->thread.fp_regs.fpc);
|
||||
else if (signal == SIGSEGV)
|
||||
do_trap(regs, signal, SEGV_MAPERR, "user address fault");
|
||||
else
|
||||
#endif
|
||||
if (signal) {
|
||||
info.si_signo = signal;
|
||||
info.si_errno = 0;
|
||||
info.si_code = ILL_ILLOPC;
|
||||
info.si_addr = (void __user *) location;
|
||||
do_trap(pgm_int_code, signal,
|
||||
"illegal operation", regs, &info);
|
||||
}
|
||||
if (signal)
|
||||
do_trap(regs, signal, ILL_ILLOPC, "illegal operation");
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_MATHEMU
|
||||
void specification_exception(struct pt_regs *regs, long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
void specification_exception(struct pt_regs *regs)
|
||||
{
|
||||
__u8 opcode[6];
|
||||
__u16 __user *location = NULL;
|
||||
int signal = 0;
|
||||
|
||||
location = (__u16 __user *) get_psw_address(regs, pgm_int_code);
|
||||
location = (__u16 __user *) get_psw_address(regs);
|
||||
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
get_user(*((__u16 *) opcode), location);
|
||||
|
@ -539,30 +512,21 @@ void specification_exception(struct pt_regs *regs, long pgm_int_code,
|
|||
signal = SIGILL;
|
||||
|
||||
if (signal == SIGFPE)
|
||||
do_fp_trap(regs, location,
|
||||
current->thread.fp_regs.fpc, pgm_int_code);
|
||||
else if (signal) {
|
||||
siginfo_t info;
|
||||
info.si_signo = signal;
|
||||
info.si_errno = 0;
|
||||
info.si_code = ILL_ILLOPN;
|
||||
info.si_addr = location;
|
||||
do_trap(pgm_int_code, signal,
|
||||
"specification exception", regs, &info);
|
||||
}
|
||||
do_fp_trap(regs, current->thread.fp_regs.fpc);
|
||||
else if (signal)
|
||||
do_trap(regs, signal, ILL_ILLOPN, "specification exception");
|
||||
}
|
||||
#else
|
||||
DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN,
|
||||
"specification exception");
|
||||
#endif
|
||||
|
||||
static void data_exception(struct pt_regs *regs, long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
static void data_exception(struct pt_regs *regs)
|
||||
{
|
||||
__u16 __user *location;
|
||||
int signal = 0;
|
||||
|
||||
location = get_psw_address(regs, pgm_int_code);
|
||||
location = get_psw_address(regs);
|
||||
|
||||
if (MACHINE_HAS_IEEE)
|
||||
asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc));
|
||||
|
@ -627,32 +591,18 @@ static void data_exception(struct pt_regs *regs, long pgm_int_code,
|
|||
else
|
||||
signal = SIGILL;
|
||||
if (signal == SIGFPE)
|
||||
do_fp_trap(regs, location,
|
||||
current->thread.fp_regs.fpc, pgm_int_code);
|
||||
else if (signal) {
|
||||
siginfo_t info;
|
||||
info.si_signo = signal;
|
||||
info.si_errno = 0;
|
||||
info.si_code = ILL_ILLOPN;
|
||||
info.si_addr = location;
|
||||
do_trap(pgm_int_code, signal, "data exception", regs, &info);
|
||||
}
|
||||
do_fp_trap(regs, current->thread.fp_regs.fpc);
|
||||
else if (signal)
|
||||
do_trap(regs, signal, ILL_ILLOPN, "data exception");
|
||||
}
|
||||
|
||||
static void space_switch_exception(struct pt_regs *regs, long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
static void space_switch_exception(struct pt_regs *regs)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
/* Set user psw back to home space mode. */
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE)
|
||||
regs->psw.mask |= PSW_ASC_HOME;
|
||||
/* Send SIGILL. */
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_code = ILL_PRVOPC;
|
||||
info.si_addr = get_psw_address(regs, pgm_int_code);
|
||||
do_trap(pgm_int_code, SIGILL, "space switch event", regs, &info);
|
||||
do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event");
|
||||
}
|
||||
|
||||
void __kprobes kernel_stack_overflow(struct pt_regs * regs)
|
||||
|
|
|
@ -125,8 +125,7 @@ static inline int user_space_fault(unsigned long trans_exc_code)
|
|||
return trans_exc_code != 3;
|
||||
}
|
||||
|
||||
static inline void report_user_fault(struct pt_regs *regs, long int_code,
|
||||
int signr, unsigned long address)
|
||||
static inline void report_user_fault(struct pt_regs *regs, long signr)
|
||||
{
|
||||
if ((task_pid_nr(current) > 1) && !show_unhandled_signals)
|
||||
return;
|
||||
|
@ -134,10 +133,12 @@ static inline void report_user_fault(struct pt_regs *regs, long int_code,
|
|||
return;
|
||||
if (!printk_ratelimit())
|
||||
return;
|
||||
printk("User process fault: interruption code 0x%lX ", int_code);
|
||||
printk(KERN_ALERT "User process fault: interruption code 0x%X ",
|
||||
regs->int_code);
|
||||
print_vma_addr(KERN_CONT "in ", regs->psw.addr & PSW_ADDR_INSN);
|
||||
printk("\n");
|
||||
printk("failing address: %lX\n", address);
|
||||
printk(KERN_CONT "\n");
|
||||
printk(KERN_ALERT "failing address: %lX\n",
|
||||
regs->int_parm_long & __FAIL_ADDR_MASK);
|
||||
show_regs(regs);
|
||||
}
|
||||
|
||||
|
@ -145,24 +146,18 @@ static inline void report_user_fault(struct pt_regs *regs, long int_code,
|
|||
* Send SIGSEGV to task. This is an external routine
|
||||
* to keep the stack usage of do_page_fault small.
|
||||
*/
|
||||
static noinline void do_sigsegv(struct pt_regs *regs, long int_code,
|
||||
int si_code, unsigned long trans_exc_code)
|
||||
static noinline void do_sigsegv(struct pt_regs *regs, int si_code)
|
||||
{
|
||||
struct siginfo si;
|
||||
unsigned long address;
|
||||
|
||||
address = trans_exc_code & __FAIL_ADDR_MASK;
|
||||
current->thread.prot_addr = address;
|
||||
current->thread.trap_no = int_code;
|
||||
report_user_fault(regs, int_code, SIGSEGV, address);
|
||||
report_user_fault(regs, SIGSEGV);
|
||||
si.si_signo = SIGSEGV;
|
||||
si.si_code = si_code;
|
||||
si.si_addr = (void __user *) address;
|
||||
si.si_addr = (void __user *)(regs->int_parm_long & __FAIL_ADDR_MASK);
|
||||
force_sig_info(SIGSEGV, &si, current);
|
||||
}
|
||||
|
||||
static noinline void do_no_context(struct pt_regs *regs, long int_code,
|
||||
unsigned long trans_exc_code)
|
||||
static noinline void do_no_context(struct pt_regs *regs)
|
||||
{
|
||||
const struct exception_table_entry *fixup;
|
||||
unsigned long address;
|
||||
|
@ -178,55 +173,48 @@ static noinline void do_no_context(struct pt_regs *regs, long int_code,
|
|||
* Oops. The kernel tried to access some bad page. We'll have to
|
||||
* terminate things with extreme prejudice.
|
||||
*/
|
||||
address = trans_exc_code & __FAIL_ADDR_MASK;
|
||||
if (!user_space_fault(trans_exc_code))
|
||||
address = regs->int_parm_long & __FAIL_ADDR_MASK;
|
||||
if (!user_space_fault(regs->int_parm_long))
|
||||
printk(KERN_ALERT "Unable to handle kernel pointer dereference"
|
||||
" at virtual kernel address %p\n", (void *)address);
|
||||
else
|
||||
printk(KERN_ALERT "Unable to handle kernel paging request"
|
||||
" at virtual user address %p\n", (void *)address);
|
||||
|
||||
die("Oops", regs, int_code);
|
||||
die(regs, "Oops");
|
||||
do_exit(SIGKILL);
|
||||
}
|
||||
|
||||
static noinline void do_low_address(struct pt_regs *regs, long int_code,
|
||||
unsigned long trans_exc_code)
|
||||
static noinline void do_low_address(struct pt_regs *regs)
|
||||
{
|
||||
/* Low-address protection hit in kernel mode means
|
||||
NULL pointer write access in kernel mode. */
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
/* Low-address protection hit in user mode 'cannot happen'. */
|
||||
die ("Low-address protection", regs, int_code);
|
||||
die (regs, "Low-address protection");
|
||||
do_exit(SIGKILL);
|
||||
}
|
||||
|
||||
do_no_context(regs, int_code, trans_exc_code);
|
||||
do_no_context(regs);
|
||||
}
|
||||
|
||||
static noinline void do_sigbus(struct pt_regs *regs, long int_code,
|
||||
unsigned long trans_exc_code)
|
||||
static noinline void do_sigbus(struct pt_regs *regs)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
unsigned long address;
|
||||
struct siginfo si;
|
||||
|
||||
/*
|
||||
* Send a sigbus, regardless of whether we were in kernel
|
||||
* or user mode.
|
||||
*/
|
||||
address = trans_exc_code & __FAIL_ADDR_MASK;
|
||||
tsk->thread.prot_addr = address;
|
||||
tsk->thread.trap_no = int_code;
|
||||
si.si_signo = SIGBUS;
|
||||
si.si_errno = 0;
|
||||
si.si_code = BUS_ADRERR;
|
||||
si.si_addr = (void __user *) address;
|
||||
si.si_addr = (void __user *)(regs->int_parm_long & __FAIL_ADDR_MASK);
|
||||
force_sig_info(SIGBUS, &si, tsk);
|
||||
}
|
||||
|
||||
static noinline void do_fault_error(struct pt_regs *regs, long int_code,
|
||||
unsigned long trans_exc_code, int fault)
|
||||
static noinline void do_fault_error(struct pt_regs *regs, int fault)
|
||||
{
|
||||
int si_code;
|
||||
|
||||
|
@ -238,24 +226,24 @@ static noinline void do_fault_error(struct pt_regs *regs, long int_code,
|
|||
/* User mode accesses just cause a SIGSEGV */
|
||||
si_code = (fault == VM_FAULT_BADMAP) ?
|
||||
SEGV_MAPERR : SEGV_ACCERR;
|
||||
do_sigsegv(regs, int_code, si_code, trans_exc_code);
|
||||
do_sigsegv(regs, si_code);
|
||||
return;
|
||||
}
|
||||
case VM_FAULT_BADCONTEXT:
|
||||
do_no_context(regs, int_code, trans_exc_code);
|
||||
do_no_context(regs);
|
||||
break;
|
||||
default: /* fault & VM_FAULT_ERROR */
|
||||
if (fault & VM_FAULT_OOM) {
|
||||
if (!(regs->psw.mask & PSW_MASK_PSTATE))
|
||||
do_no_context(regs, int_code, trans_exc_code);
|
||||
do_no_context(regs);
|
||||
else
|
||||
pagefault_out_of_memory();
|
||||
} else if (fault & VM_FAULT_SIGBUS) {
|
||||
/* Kernel mode? Handle exceptions or die */
|
||||
if (!(regs->psw.mask & PSW_MASK_PSTATE))
|
||||
do_no_context(regs, int_code, trans_exc_code);
|
||||
do_no_context(regs);
|
||||
else
|
||||
do_sigbus(regs, int_code, trans_exc_code);
|
||||
do_sigbus(regs);
|
||||
} else
|
||||
BUG();
|
||||
break;
|
||||
|
@ -273,12 +261,12 @@ static noinline void do_fault_error(struct pt_regs *regs, long int_code,
|
|||
* 11 Page translation -> Not present (nullification)
|
||||
* 3b Region third trans. -> Not present (nullification)
|
||||
*/
|
||||
static inline int do_exception(struct pt_regs *regs, int access,
|
||||
unsigned long trans_exc_code)
|
||||
static inline int do_exception(struct pt_regs *regs, int access)
|
||||
{
|
||||
struct task_struct *tsk;
|
||||
struct mm_struct *mm;
|
||||
struct vm_area_struct *vma;
|
||||
unsigned long trans_exc_code;
|
||||
unsigned long address;
|
||||
unsigned int flags;
|
||||
int fault;
|
||||
|
@ -288,6 +276,7 @@ static inline int do_exception(struct pt_regs *regs, int access,
|
|||
|
||||
tsk = current;
|
||||
mm = tsk->mm;
|
||||
trans_exc_code = regs->int_parm_long;
|
||||
|
||||
/*
|
||||
* Verify that the fault happened in user space, that
|
||||
|
@ -387,45 +376,46 @@ static inline int do_exception(struct pt_regs *regs, int access,
|
|||
return fault;
|
||||
}
|
||||
|
||||
void __kprobes do_protection_exception(struct pt_regs *regs, long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
void __kprobes do_protection_exception(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long trans_exc_code;
|
||||
int fault;
|
||||
|
||||
trans_exc_code = regs->int_parm_long;
|
||||
/* Protection exception is suppressing, decrement psw address. */
|
||||
regs->psw.addr = __rewind_psw(regs->psw, pgm_int_code >> 16);
|
||||
regs->psw.addr = __rewind_psw(regs->psw, regs->int_code >> 16);
|
||||
/*
|
||||
* Check for low-address protection. This needs to be treated
|
||||
* as a special case because the translation exception code
|
||||
* field is not guaranteed to contain valid data in this case.
|
||||
*/
|
||||
if (unlikely(!(trans_exc_code & 4))) {
|
||||
do_low_address(regs, pgm_int_code, trans_exc_code);
|
||||
do_low_address(regs);
|
||||
return;
|
||||
}
|
||||
fault = do_exception(regs, VM_WRITE, trans_exc_code);
|
||||
fault = do_exception(regs, VM_WRITE);
|
||||
if (unlikely(fault))
|
||||
do_fault_error(regs, 4, trans_exc_code, fault);
|
||||
do_fault_error(regs, fault);
|
||||
}
|
||||
|
||||
void __kprobes do_dat_exception(struct pt_regs *regs, long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
void __kprobes do_dat_exception(struct pt_regs *regs)
|
||||
{
|
||||
int access, fault;
|
||||
|
||||
access = VM_READ | VM_EXEC | VM_WRITE;
|
||||
fault = do_exception(regs, access, trans_exc_code);
|
||||
fault = do_exception(regs, access);
|
||||
if (unlikely(fault))
|
||||
do_fault_error(regs, pgm_int_code & 255, trans_exc_code, fault);
|
||||
do_fault_error(regs, fault);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
void __kprobes do_asce_exception(struct pt_regs *regs, long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
void __kprobes do_asce_exception(struct pt_regs *regs)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
struct vm_area_struct *vma;
|
||||
unsigned long trans_exc_code;
|
||||
|
||||
trans_exc_code = regs->int_parm_long;
|
||||
if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm))
|
||||
goto no_context;
|
||||
|
||||
|
@ -440,12 +430,12 @@ void __kprobes do_asce_exception(struct pt_regs *regs, long pgm_int_code,
|
|||
|
||||
/* User mode accesses just cause a SIGSEGV */
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
do_sigsegv(regs, pgm_int_code, SEGV_MAPERR, trans_exc_code);
|
||||
do_sigsegv(regs, SEGV_MAPERR);
|
||||
return;
|
||||
}
|
||||
|
||||
no_context:
|
||||
do_no_context(regs, pgm_int_code, trans_exc_code);
|
||||
do_no_context(regs);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -459,14 +449,15 @@ int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write)
|
|||
regs.psw.mask |= PSW_MASK_IO | PSW_MASK_EXT;
|
||||
regs.psw.addr = (unsigned long) __builtin_return_address(0);
|
||||
regs.psw.addr |= PSW_ADDR_AMODE;
|
||||
uaddr &= PAGE_MASK;
|
||||
regs.int_code = pgm_int_code;
|
||||
regs.int_parm_long = (uaddr & PAGE_MASK) | 2;
|
||||
access = write ? VM_WRITE : VM_READ;
|
||||
fault = do_exception(®s, access, uaddr | 2);
|
||||
fault = do_exception(®s, access);
|
||||
if (unlikely(fault)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
return -EFAULT;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
do_sigbus(®s, pgm_int_code, uaddr);
|
||||
do_sigbus(®s);
|
||||
}
|
||||
return fault ? -EFAULT : 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue