[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 @@ out:
 | 
			
		|||
	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…
	
	Add table
		Add a link
		
	
		Reference in a new issue