forked from Minki/linux
[S390] cleanup lowcore access from program checks
Read all required fields for program checks from the lowcore in the first level interrupt handler in entry[64].S. If the context that caused the fault was enabled for interrupts we can now re-enable the irqs in entry[64].S. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
84afdcee62
commit
1e54622e04
@ -84,6 +84,7 @@ int main(void)
|
||||
DEFINE(__LC_SVC_INT_CODE, offsetof(struct _lowcore, svc_code));
|
||||
DEFINE(__LC_PGM_ILC, offsetof(struct _lowcore, pgm_ilc));
|
||||
DEFINE(__LC_PGM_INT_CODE, offsetof(struct _lowcore, pgm_code));
|
||||
DEFINE(__LC_TRANS_EXC_CODE, offsetof(struct _lowcore, trans_exc_code));
|
||||
DEFINE(__LC_PER_ATMID, offsetof(struct _lowcore, per_perc_atmid));
|
||||
DEFINE(__LC_PER_ADDRESS, offsetof(struct _lowcore, per_address));
|
||||
DEFINE(__LC_PER_ACCESS_ID, offsetof(struct _lowcore, per_access_id));
|
||||
|
@ -72,25 +72,9 @@ STACK_SIZE = 1 << STACK_SHIFT
|
||||
l %r1,BASED(.Ltrace_irq_off_caller)
|
||||
basr %r14,%r1
|
||||
.endm
|
||||
|
||||
.macro TRACE_IRQS_CHECK_ON
|
||||
tm SP_PSW(%r15),0x03 # irqs enabled?
|
||||
bz BASED(0f)
|
||||
TRACE_IRQS_ON
|
||||
0:
|
||||
.endm
|
||||
|
||||
.macro TRACE_IRQS_CHECK_OFF
|
||||
tm SP_PSW(%r15),0x03 # irqs enabled?
|
||||
bz BASED(0f)
|
||||
TRACE_IRQS_OFF
|
||||
0:
|
||||
.endm
|
||||
#else
|
||||
#define TRACE_IRQS_ON
|
||||
#define TRACE_IRQS_OFF
|
||||
#define TRACE_IRQS_CHECK_ON
|
||||
#define TRACE_IRQS_CHECK_OFF
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
@ -198,6 +182,12 @@ STACK_SIZE = 1 << STACK_SHIFT
|
||||
lpsw \psworg # back to caller
|
||||
.endm
|
||||
|
||||
.macro REENABLE_IRQS
|
||||
mvc __SF_EMPTY(1,%r15),SP_PSW(%r15)
|
||||
ni __SF_EMPTY(%r15),0xbf
|
||||
ssm __SF_EMPTY(%r15)
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Scheduler resume function, called by switch_to
|
||||
* gpr2 = (task_struct *) prev
|
||||
@ -440,13 +430,11 @@ kernel_execve:
|
||||
br %r14
|
||||
# execve succeeded.
|
||||
0: stnsm __SF_EMPTY(%r15),0xfc # disable interrupts
|
||||
TRACE_IRQS_OFF
|
||||
l %r15,__LC_KERNEL_STACK # load ksp
|
||||
s %r15,BASED(.Lc_spsize) # make room for registers & psw
|
||||
l %r9,__LC_THREAD_INFO
|
||||
mvc SP_PTREGS(__PT_SIZE,%r15),0(%r12) # copy pt_regs
|
||||
xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15)
|
||||
TRACE_IRQS_ON
|
||||
stosm __SF_EMPTY(%r15),0x03 # reenable interrupts
|
||||
l %r1,BASED(.Lexecve_tail)
|
||||
basr %r14,%r1
|
||||
@ -483,9 +471,10 @@ pgm_check_handler:
|
||||
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
|
||||
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
|
||||
pgm_no_vtime:
|
||||
TRACE_IRQS_CHECK_OFF
|
||||
l %r9,__LC_THREAD_INFO # load pointer to thread_info struct
|
||||
l %r3,__LC_PGM_ILC # load program interruption code
|
||||
l %r4,__LC_TRANS_EXC_CODE
|
||||
REENABLE_IRQS
|
||||
la %r8,0x7f
|
||||
nr %r8,%r3
|
||||
pgm_do_call:
|
||||
@ -495,7 +484,6 @@ pgm_do_call:
|
||||
la %r2,SP_PTREGS(%r15) # address of register-save area
|
||||
basr %r14,%r7 # branch to interrupt-handler
|
||||
pgm_exit:
|
||||
TRACE_IRQS_CHECK_ON
|
||||
b BASED(sysc_return)
|
||||
|
||||
#
|
||||
@ -523,7 +511,6 @@ pgm_per_std:
|
||||
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
|
||||
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
|
||||
pgm_no_vtime2:
|
||||
TRACE_IRQS_CHECK_OFF
|
||||
l %r9,__LC_THREAD_INFO # load pointer to thread_info struct
|
||||
l %r1,__TI_task(%r9)
|
||||
tm SP_PSW+1(%r15),0x01 # kernel per event ?
|
||||
@ -533,6 +520,8 @@ pgm_no_vtime2:
|
||||
mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID
|
||||
oi __TI_flags+3(%r9),_TIF_SINGLE_STEP # set TIF_SINGLE_STEP
|
||||
l %r3,__LC_PGM_ILC # load program interruption code
|
||||
l %r4,__LC_TRANS_EXC_CODE
|
||||
REENABLE_IRQS
|
||||
la %r8,0x7f
|
||||
nr %r8,%r3 # clear per-event-bit and ilc
|
||||
be BASED(pgm_exit2) # only per or per+check ?
|
||||
@ -542,8 +531,6 @@ pgm_no_vtime2:
|
||||
la %r2,SP_PTREGS(%r15) # address of register-save area
|
||||
basr %r14,%r7 # branch to interrupt-handler
|
||||
pgm_exit2:
|
||||
TRACE_IRQS_ON
|
||||
stosm __SF_EMPTY(%r15),0x03 # reenable interrupts
|
||||
b BASED(sysc_return)
|
||||
|
||||
#
|
||||
@ -557,13 +544,11 @@ pgm_svcper:
|
||||
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
|
||||
lh %r7,0x8a # get svc number from lowcore
|
||||
l %r9,__LC_THREAD_INFO # load pointer to thread_info struct
|
||||
TRACE_IRQS_OFF
|
||||
l %r8,__TI_task(%r9)
|
||||
mvc __THREAD_per+__PER_atmid(2,%r8),__LC_PER_ATMID
|
||||
mvc __THREAD_per+__PER_address(4,%r8),__LC_PER_ADDRESS
|
||||
mvc __THREAD_per+__PER_access_id(1,%r8),__LC_PER_ACCESS_ID
|
||||
oi __TI_flags+3(%r9),_TIF_SINGLE_STEP # set TIF_SINGLE_STEP
|
||||
TRACE_IRQS_ON
|
||||
stosm __SF_EMPTY(%r15),0x03 # reenable interrupts
|
||||
lm %r2,%r6,SP_R2(%r15) # load svc arguments
|
||||
b BASED(sysc_do_svc)
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <linux/signal.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
typedef void pgm_check_handler_t(struct pt_regs *, long);
|
||||
typedef void pgm_check_handler_t(struct pt_regs *, long, unsigned long);
|
||||
extern pgm_check_handler_t *pgm_check_table[128];
|
||||
pgm_check_handler_t do_protection_exception;
|
||||
pgm_check_handler_t do_dat_exception;
|
||||
|
@ -79,25 +79,9 @@ _TIF_SYSCALL = (_TIF_SYSCALL_TRACE>>8 | _TIF_SYSCALL_AUDIT>>8 | \
|
||||
basr %r2,%r0
|
||||
brasl %r14,trace_hardirqs_off_caller
|
||||
.endm
|
||||
|
||||
.macro TRACE_IRQS_CHECK_ON
|
||||
tm SP_PSW(%r15),0x03 # irqs enabled?
|
||||
jz 0f
|
||||
TRACE_IRQS_ON
|
||||
0:
|
||||
.endm
|
||||
|
||||
.macro TRACE_IRQS_CHECK_OFF
|
||||
tm SP_PSW(%r15),0x03 # irqs enabled?
|
||||
jz 0f
|
||||
TRACE_IRQS_OFF
|
||||
0:
|
||||
.endm
|
||||
#else
|
||||
#define TRACE_IRQS_ON
|
||||
#define TRACE_IRQS_OFF
|
||||
#define TRACE_IRQS_CHECK_ON
|
||||
#define TRACE_IRQS_CHECK_OFF
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
@ -207,6 +191,12 @@ _TIF_SYSCALL = (_TIF_SYSCALL_TRACE>>8 | _TIF_SYSCALL_AUDIT>>8 | \
|
||||
0:
|
||||
.endm
|
||||
|
||||
.macro REENABLE_IRQS
|
||||
mvc __SF_EMPTY(1,%r15),SP_PSW(%r15)
|
||||
ni __SF_EMPTY(%r15),0xbf
|
||||
ssm __SF_EMPTY(%r15)
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Scheduler resume function, called by switch_to
|
||||
* gpr2 = (task_struct *) prev
|
||||
@ -443,14 +433,12 @@ kernel_execve:
|
||||
br %r14
|
||||
# execve succeeded.
|
||||
0: stnsm __SF_EMPTY(%r15),0xfc # disable interrupts
|
||||
# TRACE_IRQS_OFF
|
||||
lg %r15,__LC_KERNEL_STACK # load ksp
|
||||
aghi %r15,-SP_SIZE # make room for registers & psw
|
||||
lg %r13,__LC_SVC_NEW_PSW+8
|
||||
mvc SP_PTREGS(__PT_SIZE,%r15),0(%r12) # copy pt_regs
|
||||
lg %r12,__LC_THREAD_INFO
|
||||
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
|
||||
# TRACE_IRQS_ON
|
||||
stosm __SF_EMPTY(%r15),0x03 # reenable interrupts
|
||||
brasl %r14,execve_tail
|
||||
j sysc_return
|
||||
@ -490,19 +478,18 @@ pgm_check_handler:
|
||||
LAST_BREAK
|
||||
pgm_no_vtime:
|
||||
HANDLE_SIE_INTERCEPT
|
||||
TRACE_IRQS_CHECK_OFF
|
||||
stg %r11,SP_ARGS(%r15)
|
||||
lgf %r3,__LC_PGM_ILC # load program interruption code
|
||||
lg %r4,__LC_TRANS_EXC_CODE
|
||||
REENABLE_IRQS
|
||||
lghi %r8,0x7f
|
||||
ngr %r8,%r3
|
||||
pgm_do_call:
|
||||
sll %r8,3
|
||||
larl %r1,pgm_check_table
|
||||
lg %r1,0(%r8,%r1) # load address of handler routine
|
||||
la %r2,SP_PTREGS(%r15) # address of register-save area
|
||||
basr %r14,%r1 # branch to interrupt-handler
|
||||
pgm_exit:
|
||||
TRACE_IRQS_CHECK_ON
|
||||
j sysc_return
|
||||
|
||||
#
|
||||
@ -533,7 +520,6 @@ pgm_per_std:
|
||||
LAST_BREAK
|
||||
pgm_no_vtime2:
|
||||
HANDLE_SIE_INTERCEPT
|
||||
TRACE_IRQS_CHECK_OFF
|
||||
lg %r1,__TI_task(%r12)
|
||||
tm SP_PSW+1(%r15),0x01 # kernel per event ?
|
||||
jz kernel_per
|
||||
@ -542,6 +528,8 @@ pgm_no_vtime2:
|
||||
mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID
|
||||
oi __TI_flags+7(%r12),_TIF_SINGLE_STEP # set TIF_SINGLE_STEP
|
||||
lgf %r3,__LC_PGM_ILC # load program interruption code
|
||||
lg %r4,__LC_TRANS_EXC_CODE
|
||||
REENABLE_IRQS
|
||||
lghi %r8,0x7f
|
||||
ngr %r8,%r3 # clear per-event-bit and ilc
|
||||
je pgm_exit2
|
||||
@ -551,8 +539,6 @@ pgm_no_vtime2:
|
||||
la %r2,SP_PTREGS(%r15) # address of register-save area
|
||||
basr %r14,%r1 # branch to interrupt-handler
|
||||
pgm_exit2:
|
||||
TRACE_IRQS_ON
|
||||
stosm __SF_EMPTY(%r15),0x03 # reenable interrupts
|
||||
j sysc_return
|
||||
|
||||
#
|
||||
@ -568,13 +554,11 @@ pgm_svcper:
|
||||
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
|
||||
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
|
||||
LAST_BREAK
|
||||
TRACE_IRQS_OFF
|
||||
lg %r8,__TI_task(%r12)
|
||||
mvc __THREAD_per+__PER_atmid(2,%r8),__LC_PER_ATMID
|
||||
mvc __THREAD_per+__PER_address(8,%r8),__LC_PER_ADDRESS
|
||||
mvc __THREAD_per+__PER_access_id(1,%r8),__LC_PER_ACCESS_ID
|
||||
oi __TI_flags+7(%r12),_TIF_SINGLE_STEP # set TIF_SINGLE_STEP
|
||||
TRACE_IRQS_ON
|
||||
stosm __SF_EMPTY(%r15),0x03 # reenable interrupts
|
||||
lmg %r2,%r6,SP_R2(%r15) # load svc arguments
|
||||
j sysc_do_svc
|
||||
|
@ -329,27 +329,19 @@ int is_valid_bugaddr(unsigned long addr)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void __kprobes inline do_trap(long interruption_code, int signr,
|
||||
char *str, struct pt_regs *regs,
|
||||
siginfo_t *info)
|
||||
static inline void __kprobes do_trap(long pgm_int_code, int signr, char *str,
|
||||
struct pt_regs *regs, siginfo_t *info)
|
||||
{
|
||||
/*
|
||||
* We got all needed information from the lowcore and can
|
||||
* now safely switch on interrupts.
|
||||
*/
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE)
|
||||
local_irq_enable();
|
||||
|
||||
if (notify_die(DIE_TRAP, str, regs, interruption_code,
|
||||
interruption_code, signr) == NOTIFY_STOP)
|
||||
if (notify_die(DIE_TRAP, str, regs, pgm_int_code,
|
||||
pgm_int_code, signr) == NOTIFY_STOP)
|
||||
return;
|
||||
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
struct task_struct *tsk = current;
|
||||
|
||||
tsk->thread.trap_no = interruption_code & 0xffff;
|
||||
tsk->thread.trap_no = pgm_int_code & 0xffff;
|
||||
force_sig_info(signr, info, tsk);
|
||||
report_user_fault(regs, interruption_code, signr);
|
||||
report_user_fault(regs, pgm_int_code, signr);
|
||||
} else {
|
||||
const struct exception_table_entry *fixup;
|
||||
fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
|
||||
@ -361,14 +353,16 @@ static void __kprobes inline do_trap(long interruption_code, int signr,
|
||||
btt = report_bug(regs->psw.addr & PSW_ADDR_INSN, regs);
|
||||
if (btt == BUG_TRAP_TYPE_WARN)
|
||||
return;
|
||||
die(str, regs, interruption_code);
|
||||
die(str, regs, pgm_int_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void __user *get_check_address(struct pt_regs *regs)
|
||||
static inline void __user *get_psw_address(struct pt_regs *regs,
|
||||
long pgm_int_code)
|
||||
{
|
||||
return (void __user *)((regs->psw.addr-S390_lowcore.pgm_ilc) & PSW_ADDR_INSN);
|
||||
return (void __user *)
|
||||
((regs->psw.addr - (pgm_int_code >> 16)) & PSW_ADDR_INSN);
|
||||
}
|
||||
|
||||
void __kprobes do_single_step(struct pt_regs *regs)
|
||||
@ -381,57 +375,57 @@ void __kprobes do_single_step(struct pt_regs *regs)
|
||||
force_sig(SIGTRAP, current);
|
||||
}
|
||||
|
||||
static void default_trap_handler(struct pt_regs * regs, long interruption_code)
|
||||
static void default_trap_handler(struct pt_regs *regs, long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
{
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
local_irq_enable();
|
||||
report_user_fault(regs, interruption_code, SIGSEGV);
|
||||
report_user_fault(regs, pgm_int_code, SIGSEGV);
|
||||
do_exit(SIGSEGV);
|
||||
} else
|
||||
die("Unknown program exception", regs, interruption_code);
|
||||
die("Unknown program exception", regs, pgm_int_code);
|
||||
}
|
||||
|
||||
#define DO_ERROR_INFO(signr, str, name, sicode, siaddr) \
|
||||
static void name(struct pt_regs * regs, long interruption_code) \
|
||||
#define DO_ERROR_INFO(name, signr, sicode, str) \
|
||||
static void name(struct pt_regs *regs, long pgm_int_code, \
|
||||
unsigned long trans_exc_code) \
|
||||
{ \
|
||||
siginfo_t info; \
|
||||
info.si_signo = signr; \
|
||||
info.si_errno = 0; \
|
||||
info.si_code = sicode; \
|
||||
info.si_addr = siaddr; \
|
||||
do_trap(interruption_code, signr, str, regs, &info); \
|
||||
info.si_addr = get_psw_address(regs, pgm_int_code); \
|
||||
do_trap(pgm_int_code, signr, str, regs, &info); \
|
||||
}
|
||||
|
||||
DO_ERROR_INFO(SIGILL, "addressing exception", addressing_exception,
|
||||
ILL_ILLADR, get_check_address(regs))
|
||||
DO_ERROR_INFO(SIGILL, "execute exception", execute_exception,
|
||||
ILL_ILLOPN, get_check_address(regs))
|
||||
DO_ERROR_INFO(SIGFPE, "fixpoint divide exception", divide_exception,
|
||||
FPE_INTDIV, get_check_address(regs))
|
||||
DO_ERROR_INFO(SIGFPE, "fixpoint overflow exception", overflow_exception,
|
||||
FPE_INTOVF, get_check_address(regs))
|
||||
DO_ERROR_INFO(SIGFPE, "HFP overflow exception", hfp_overflow_exception,
|
||||
FPE_FLTOVF, get_check_address(regs))
|
||||
DO_ERROR_INFO(SIGFPE, "HFP underflow exception", hfp_underflow_exception,
|
||||
FPE_FLTUND, get_check_address(regs))
|
||||
DO_ERROR_INFO(SIGFPE, "HFP significance exception", hfp_significance_exception,
|
||||
FPE_FLTRES, get_check_address(regs))
|
||||
DO_ERROR_INFO(SIGFPE, "HFP divide exception", hfp_divide_exception,
|
||||
FPE_FLTDIV, get_check_address(regs))
|
||||
DO_ERROR_INFO(SIGFPE, "HFP square root exception", hfp_sqrt_exception,
|
||||
FPE_FLTINV, get_check_address(regs))
|
||||
DO_ERROR_INFO(SIGILL, "operand exception", operand_exception,
|
||||
ILL_ILLOPN, get_check_address(regs))
|
||||
DO_ERROR_INFO(SIGILL, "privileged operation", privileged_op,
|
||||
ILL_PRVOPC, get_check_address(regs))
|
||||
DO_ERROR_INFO(SIGILL, "special operation exception", special_op_exception,
|
||||
ILL_ILLOPN, get_check_address(regs))
|
||||
DO_ERROR_INFO(SIGILL, "translation exception", translation_exception,
|
||||
ILL_ILLOPN, get_check_address(regs))
|
||||
DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR,
|
||||
"addressing exception")
|
||||
DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN,
|
||||
"execute exception")
|
||||
DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV,
|
||||
"fixpoint divide exception")
|
||||
DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF,
|
||||
"fixpoint overflow exception")
|
||||
DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF,
|
||||
"HFP overflow exception")
|
||||
DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND,
|
||||
"HFP underflow exception")
|
||||
DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES,
|
||||
"HFP significance exception")
|
||||
DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV,
|
||||
"HFP divide exception")
|
||||
DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV,
|
||||
"HFP square root exception")
|
||||
DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN,
|
||||
"operand exception")
|
||||
DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC,
|
||||
"privileged operation")
|
||||
DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,
|
||||
"special operation exception")
|
||||
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 interruption_code)
|
||||
static inline void do_fp_trap(struct pt_regs *regs, void __user *location,
|
||||
int fpc, long pgm_int_code)
|
||||
{
|
||||
siginfo_t si;
|
||||
|
||||
@ -454,25 +448,19 @@ do_fp_trap(struct pt_regs *regs, void __user *location,
|
||||
si.si_code = FPE_FLTRES;
|
||||
}
|
||||
current->thread.ieee_instruction_pointer = (addr_t) location;
|
||||
do_trap(interruption_code, SIGFPE,
|
||||
do_trap(pgm_int_code, SIGFPE,
|
||||
"floating point exception", regs, &si);
|
||||
}
|
||||
|
||||
static void illegal_op(struct pt_regs * regs, long interruption_code)
|
||||
static void illegal_op(struct pt_regs *regs, long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
{
|
||||
siginfo_t info;
|
||||
__u8 opcode[6];
|
||||
__u16 __user *location;
|
||||
int signal = 0;
|
||||
|
||||
location = get_check_address(regs);
|
||||
|
||||
/*
|
||||
* We got all needed information from the lowcore and can
|
||||
* now safely switch on interrupts.
|
||||
*/
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE)
|
||||
local_irq_enable();
|
||||
location = get_psw_address(regs, pgm_int_code);
|
||||
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
|
||||
@ -512,7 +500,7 @@ static void illegal_op(struct pt_regs * regs, long interruption_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, interruption_code,
|
||||
if (notify_die(DIE_BPT, "bpt", regs, pgm_int_code,
|
||||
3, SIGTRAP) != NOTIFY_STOP)
|
||||
signal = SIGILL;
|
||||
}
|
||||
@ -520,13 +508,13 @@ static void illegal_op(struct pt_regs * regs, long interruption_code)
|
||||
#ifdef CONFIG_MATHEMU
|
||||
if (signal == SIGFPE)
|
||||
do_fp_trap(regs, location,
|
||||
current->thread.fp_regs.fpc, interruption_code);
|
||||
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(interruption_code, signal,
|
||||
do_trap(pgm_int_code, signal,
|
||||
"user address fault", regs, &info);
|
||||
} else
|
||||
#endif
|
||||
@ -535,28 +523,22 @@ static void illegal_op(struct pt_regs * regs, long interruption_code)
|
||||
info.si_errno = 0;
|
||||
info.si_code = ILL_ILLOPC;
|
||||
info.si_addr = (void __user *) location;
|
||||
do_trap(interruption_code, signal,
|
||||
do_trap(pgm_int_code, signal,
|
||||
"illegal operation", regs, &info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_MATHEMU
|
||||
asmlinkage void
|
||||
specification_exception(struct pt_regs * regs, long interruption_code)
|
||||
asmlinkage void specification_exception(struct pt_regs *regs,
|
||||
long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
{
|
||||
__u8 opcode[6];
|
||||
__u16 __user *location = NULL;
|
||||
int signal = 0;
|
||||
|
||||
location = (__u16 __user *) get_check_address(regs);
|
||||
|
||||
/*
|
||||
* We got all needed information from the lowcore and can
|
||||
* now safely switch on interrupts.
|
||||
*/
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE)
|
||||
local_irq_enable();
|
||||
location = (__u16 __user *) get_psw_address(regs, pgm_int_code);
|
||||
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
get_user(*((__u16 *) opcode), location);
|
||||
@ -592,35 +574,29 @@ specification_exception(struct pt_regs * regs, long interruption_code)
|
||||
|
||||
if (signal == SIGFPE)
|
||||
do_fp_trap(regs, location,
|
||||
current->thread.fp_regs.fpc, interruption_code);
|
||||
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(interruption_code, signal,
|
||||
do_trap(pgm_int_code, signal,
|
||||
"specification exception", regs, &info);
|
||||
}
|
||||
}
|
||||
#else
|
||||
DO_ERROR_INFO(SIGILL, "specification exception", specification_exception,
|
||||
ILL_ILLOPN, get_check_address(regs));
|
||||
DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN,
|
||||
"specification exception");
|
||||
#endif
|
||||
|
||||
static void data_exception(struct pt_regs * regs, long interruption_code)
|
||||
static void data_exception(struct pt_regs *regs, long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
{
|
||||
__u16 __user *location;
|
||||
int signal = 0;
|
||||
|
||||
location = get_check_address(regs);
|
||||
|
||||
/*
|
||||
* We got all needed information from the lowcore and can
|
||||
* now safely switch on interrupts.
|
||||
*/
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE)
|
||||
local_irq_enable();
|
||||
location = get_psw_address(regs, pgm_int_code);
|
||||
|
||||
if (MACHINE_HAS_IEEE)
|
||||
asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc));
|
||||
@ -686,19 +662,19 @@ static void data_exception(struct pt_regs * regs, long interruption_code)
|
||||
signal = SIGILL;
|
||||
if (signal == SIGFPE)
|
||||
do_fp_trap(regs, location,
|
||||
current->thread.fp_regs.fpc, interruption_code);
|
||||
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(interruption_code, signal,
|
||||
"data exception", regs, &info);
|
||||
do_trap(pgm_int_code, signal, "data exception", regs, &info);
|
||||
}
|
||||
}
|
||||
|
||||
static void space_switch_exception(struct pt_regs * regs, long int_code)
|
||||
static void space_switch_exception(struct pt_regs *regs, long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
@ -709,8 +685,8 @@ static void space_switch_exception(struct pt_regs * regs, long int_code)
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_code = ILL_PRVOPC;
|
||||
info.si_addr = get_check_address(regs);
|
||||
do_trap(int_code, SIGILL, "space switch event", regs, &info);
|
||||
info.si_addr = get_psw_address(regs, pgm_int_code);
|
||||
do_trap(pgm_int_code, SIGILL, "space switch event", regs, &info);
|
||||
}
|
||||
|
||||
asmlinkage void kernel_stack_overflow(struct pt_regs * regs)
|
||||
|
@ -333,12 +333,6 @@ static inline int do_exception(struct pt_regs *regs, int access,
|
||||
goto out;
|
||||
|
||||
address = trans_exc_code & __FAIL_ADDR_MASK;
|
||||
/*
|
||||
* When we get here, the fault happened in the current
|
||||
* task's user address space, so we can switch on the
|
||||
* interrupts again and then search the VMAs
|
||||
*/
|
||||
local_irq_enable();
|
||||
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address);
|
||||
down_read(&mm->mmap_sem);
|
||||
|
||||
@ -397,20 +391,20 @@ out:
|
||||
return fault;
|
||||
}
|
||||
|
||||
void __kprobes do_protection_exception(struct pt_regs *regs, long int_code)
|
||||
void __kprobes do_protection_exception(struct pt_regs *regs, long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
{
|
||||
unsigned long trans_exc_code = S390_lowcore.trans_exc_code;
|
||||
int fault;
|
||||
|
||||
/* Protection exception is supressing, decrement psw address. */
|
||||
regs->psw.addr -= (int_code >> 16);
|
||||
regs->psw.addr -= (pgm_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, int_code, trans_exc_code);
|
||||
do_low_address(regs, pgm_int_code, trans_exc_code);
|
||||
return;
|
||||
}
|
||||
fault = do_exception(regs, VM_WRITE, trans_exc_code);
|
||||
@ -418,9 +412,9 @@ void __kprobes do_protection_exception(struct pt_regs *regs, long int_code)
|
||||
do_fault_error(regs, 4, trans_exc_code, fault);
|
||||
}
|
||||
|
||||
void __kprobes do_dat_exception(struct pt_regs *regs, long int_code)
|
||||
void __kprobes do_dat_exception(struct pt_regs *regs, long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
{
|
||||
unsigned long trans_exc_code = S390_lowcore.trans_exc_code;
|
||||
int access, fault;
|
||||
|
||||
access = VM_READ | VM_EXEC | VM_WRITE;
|
||||
@ -431,21 +425,19 @@ void __kprobes do_dat_exception(struct pt_regs *regs, long int_code)
|
||||
#endif
|
||||
fault = do_exception(regs, access, trans_exc_code);
|
||||
if (unlikely(fault))
|
||||
do_fault_error(regs, int_code & 255, trans_exc_code, fault);
|
||||
do_fault_error(regs, pgm_int_code & 255, trans_exc_code, fault);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
void __kprobes do_asce_exception(struct pt_regs *regs, long int_code)
|
||||
void __kprobes do_asce_exception(struct pt_regs *regs, long pgm_int_code,
|
||||
unsigned long trans_exc_code)
|
||||
{
|
||||
unsigned long trans_exc_code = S390_lowcore.trans_exc_code;
|
||||
struct mm_struct *mm = current->mm;
|
||||
struct vm_area_struct *vma;
|
||||
|
||||
if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm))
|
||||
goto no_context;
|
||||
|
||||
local_irq_enable();
|
||||
|
||||
down_read(&mm->mmap_sem);
|
||||
vma = find_vma(mm, trans_exc_code & __FAIL_ADDR_MASK);
|
||||
up_read(&mm->mmap_sem);
|
||||
@ -457,16 +449,16 @@ void __kprobes do_asce_exception(struct pt_regs *regs, long int_code)
|
||||
|
||||
/* User mode accesses just cause a SIGSEGV */
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
do_sigsegv(regs, int_code, SEGV_MAPERR, trans_exc_code);
|
||||
do_sigsegv(regs, pgm_int_code, SEGV_MAPERR, trans_exc_code);
|
||||
return;
|
||||
}
|
||||
|
||||
no_context:
|
||||
do_no_context(regs, int_code, trans_exc_code);
|
||||
do_no_context(regs, pgm_int_code, trans_exc_code);
|
||||
}
|
||||
#endif
|
||||
|
||||
int __handle_fault(unsigned long uaddr, unsigned long int_code, int write_user)
|
||||
int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
int access, fault;
|
||||
@ -477,14 +469,14 @@ int __handle_fault(unsigned long uaddr, unsigned long int_code, int write_user)
|
||||
regs.psw.addr = (unsigned long) __builtin_return_address(0);
|
||||
regs.psw.addr |= PSW_ADDR_AMODE;
|
||||
uaddr &= PAGE_MASK;
|
||||
access = write_user ? VM_WRITE : VM_READ;
|
||||
access = write ? VM_WRITE : VM_READ;
|
||||
fault = do_exception(®s, access, uaddr | 2);
|
||||
if (unlikely(fault)) {
|
||||
if (fault & VM_FAULT_OOM) {
|
||||
pagefault_out_of_memory();
|
||||
fault = 0;
|
||||
} else if (fault & VM_FAULT_SIGBUS)
|
||||
do_sigbus(®s, int_code, uaddr);
|
||||
do_sigbus(®s, pgm_int_code, uaddr);
|
||||
}
|
||||
return fault ? -EFAULT : 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user