File: /usr/src/linux/arch/s390x/kernel/traps.c

1     /*
2      *  arch/s390/kernel/traps.c
3      *
4      *  S390 version
5      *    Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
6      *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
7      *               Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
8      *
9      *  Derived from "arch/i386/kernel/traps.c"
10      *    Copyright (C) 1991, 1992 Linus Torvalds
11      */
12     
13     /*
14      * 'Traps.c' handles hardware traps and faults after we have saved some
15      * state in 'asm.s'.
16      */
17     #include <linux/config.h>
18     #include <linux/sched.h>
19     #include <linux/kernel.h>
20     #include <linux/string.h>
21     #include <linux/errno.h>
22     #include <linux/ptrace.h>
23     #include <linux/timer.h>
24     #include <linux/mm.h>
25     #include <linux/smp.h>
26     #include <linux/smp_lock.h>
27     #include <linux/init.h>
28     #include <linux/delay.h>
29     
30     #include <asm/system.h>
31     #include <asm/uaccess.h>
32     #include <asm/io.h>
33     #include <asm/atomic.h>
34     #include <asm/mathemu.h>
35     #if CONFIG_REMOTE_DEBUG
36     #include <asm/gdb-stub.h>
37     #endif
38     #include <asm/cpcmd.h>
39     #include <asm/s390_ext.h>
40     
41     /* Called from entry.S only */
42     extern void handle_per_exception(struct pt_regs *regs);
43     
44     typedef void pgm_check_handler_t(struct pt_regs *, long);
45     pgm_check_handler_t *pgm_check_table[128];
46     
47     #ifdef CONFIG_SYSCTL
48     #ifdef CONFIG_PROCESS_DEBUG
49     int sysctl_userprocess_debug = 1;
50     #else
51     int sysctl_userprocess_debug = 0;
52     #endif
53     #endif
54     
55     extern pgm_check_handler_t do_page_fault;
56     #ifdef CONFIG_PFAULT
57     extern int pfault_init(void);
58     extern void pfault_fini(void);
59     extern void pfault_interrupt(struct pt_regs *regs, __u16 error_code);
60     #endif
61     
62     spinlock_t die_lock;
63     
64     void die(const char * str, struct pt_regs * regs, long err)
65     {
66             console_verbose();
67             spin_lock_irq(&die_lock);
68             printk("%s: %04lx\n", str, err & 0xffff);
69             show_regs(regs);
70             spin_unlock_irq(&die_lock);
71             do_exit(SIGSEGV);
72     }
73     
74     #define DO_ERROR(signr, str, name) \
75     asmlinkage void name(struct pt_regs * regs, long interruption_code) \
76     { \
77     	do_trap(interruption_code, signr, str, regs, NULL); \
78     }
79     
80     #define DO_ERROR_INFO(signr, str, name, sicode, siaddr) \
81     asmlinkage void name(struct pt_regs * regs, long interruption_code) \
82     { \
83             siginfo_t info; \
84             info.si_signo = signr; \
85             info.si_errno = 0; \
86             info.si_code = sicode; \
87             info.si_addr = (void *)siaddr; \
88             do_trap(interruption_code, signr, str, regs, &info); \
89     }
90     
91     static void inline do_trap(long interruption_code, int signr, char *str,
92                                struct pt_regs *regs, siginfo_t *info)
93     {
94             if (regs->psw.mask & PSW_PROBLEM_STATE) {
95                     struct task_struct *tsk = current;
96                     tsk->thread.trap_no = interruption_code;
97     		if (info)
98     			force_sig_info(signr, info, tsk);
99     		else
100                     	force_sig(signr, tsk);
101     #ifndef CONFIG_SYSCTL
102     #ifdef CONFIG_PROCESS_DEBUG
103                     printk("User process fault: interruption code 0x%lX\n",
104                            interruption_code);
105                     show_regs(regs);
106     #endif
107     #else
108     		if (sysctl_userprocess_debug) {
109     			printk("User process fault: interruption code 0x%lX\n",
110     			       interruption_code);
111     			show_regs(regs);
112     		}
113     #endif
114             } else {
115                     unsigned long fixup = search_exception_table(regs->psw.addr);
116                     if (fixup)
117                             regs->psw.addr = fixup;
118                     else
119                             die(str, regs, interruption_code);
120             }
121     }
122     
123     int do_debugger_trap(struct pt_regs *regs,int signal)
124     {
125     	if(regs->psw.mask&PSW_PROBLEM_STATE)
126     	{
127     		if(current->ptrace & PT_PTRACED)
128     			force_sig(signal,current);
129     		else
130     			return 1;
131     	}
132     	else
133     	{
134     #if CONFIG_REMOTE_DEBUG
135     		if(gdb_stub_initialised)
136     		{
137     			gdb_stub_handle_exception((gdb_pt_regs *)regs,signal);
138     			return 0;
139     		}
140     #endif
141     		return 1;
142     	}
143     	return 0;
144     }
145     
146     DO_ERROR(SIGSEGV, "Unknown program exception", default_trap_handler)
147     DO_ERROR(SIGILL,  "privileged operation", privileged_op)
148     DO_ERROR(SIGILL,  "execute exception", execute_exception)
149     DO_ERROR(SIGSEGV, "addressing exception", addressing_exception)
150     DO_ERROR(SIGFPE,  "fixpoint divide exception", divide_exception)
151     DO_ERROR(SIGILL,  "translation exception", translation_exception)
152     DO_ERROR(SIGILL,  "special operand exception", special_op_exception)
153     DO_ERROR(SIGILL,  "operand exception", operand_exception)
154     DO_ERROR(SIGILL,  "specification exception", specification_exception);
155     
156     asmlinkage void illegal_op(struct pt_regs * regs, long interruption_code)
157     {
158             __u8 opcode[6];
159     	__u16 *location;
160     	int do_sig = 0;
161     
162     	location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc);
163     	/* WARNING don't change this check back to */
164     	/* int problem_state=(regs->psw.mask & PSW_PROBLEM_STATE); */
165     	/* & then doing if(problem_state) an int is too small for this */
166     	/* check on 64 bit. */
167     	if(regs->psw.mask & PSW_PROBLEM_STATE)
168     		get_user(*((__u16 *) opcode), location);
169     	else
170     		*((__u16 *)opcode)=*((__u16 *)location);
171     	if(*((__u16 *)opcode)==S390_BREAKPOINT_U16)
172             {
173     		if(do_debugger_trap(regs,SIGTRAP))
174     			do_sig=1;
175     	}
176     	else
177     		do_sig = 1;
178     	if (do_sig)
179     		do_trap(interruption_code, SIGILL, "illegal operation", regs, NULL);
180     }
181     
182     asmlinkage void data_exception(struct pt_regs * regs, long interruption_code)
183     {
184     	__u16 *location;
185     	int do_sig = 0;
186     
187     	location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc);
188     	__asm__ volatile ("stfpc %0\n\t" 
189     			  : "=m" (current->thread.fp_regs.fpc));
190     	/* Same code should work when we implement fpu emulation */
191     	/* provided we call data exception from the fpu emulator */
192     	if(current->thread.fp_regs.fpc&FPC_DXC_MASK)
193     	{
194     		current->thread.ieee_instruction_pointer=(addr_t)location;
195     		force_sig(SIGFPE, current);
196     	}
197     	else
198     		do_sig = 1;
199             if (do_sig)
200                     do_trap(interruption_code, SIGILL, "data exception", regs, NULL);
201     }
202     
203     
204     
205     /* init is done in lowcore.S and head.S */
206     
207     void __init trap_init(void)
208     {
209             int i;
210     
211             for (i = 0; i < 128; i++)
212               pgm_check_table[i] = &default_trap_handler;
213             pgm_check_table[1] = &illegal_op;
214             pgm_check_table[2] = &privileged_op;
215             pgm_check_table[3] = &execute_exception;
216             pgm_check_table[5] = &addressing_exception;
217             pgm_check_table[6] = &specification_exception;
218             pgm_check_table[7] = &data_exception;
219             pgm_check_table[9] = &divide_exception;
220             pgm_check_table[0x12] = &translation_exception;
221             pgm_check_table[0x13] = &special_op_exception;
222             pgm_check_table[0x15] = &operand_exception;
223             pgm_check_table[4] = &do_page_fault;
224             pgm_check_table[0x10] = &do_page_fault;
225             pgm_check_table[0x11] = &do_page_fault;
226             pgm_check_table[0x1C] = &privileged_op;
227             pgm_check_table[0x38] = &addressing_exception;
228             pgm_check_table[0x3B] = &do_page_fault;
229     #ifdef CONFIG_PFAULT
230     	if (MACHINE_IS_VM) {
231     		/* request the 0x2603 external interrupt */
232     		if (register_external_interrupt(0x2603, pfault_interrupt) != 0)
233     			panic("Couldn't request external interrupt 0x2603");
234     		/*
235     		 * Try to get pfault pseudo page faults going.
236     		 */
237     		if (pfault_init() != 0) {
238     			/* Tough luck, no pfault. */
239     			unregister_external_interrupt(0x2603,
240     						      pfault_interrupt);
241     		}
242     	}
243     #endif
244     }
245     
246     
247     void handle_per_exception(struct pt_regs *regs)
248     {
249     	if(regs->psw.mask&PSW_PROBLEM_STATE)
250     	{
251     		per_struct *per_info=&current->thread.per_info;
252     		per_info->lowcore.words.perc_atmid=S390_lowcore.per_perc_atmid;
253     		per_info->lowcore.words.address=S390_lowcore.per_address;
254     		per_info->lowcore.words.access_id=S390_lowcore.per_access_id;
255     	}
256     	if(do_debugger_trap(regs,SIGTRAP))
257     	{
258     		/* I've seen this possibly a task structure being reused ? */
259     		printk("Spurious per exception detected\n");
260     		printk("switching off per tracing for this task.\n");
261     		show_regs(regs);
262     		/* Hopefully switching off per tracing will help us survive */
263     		regs->psw.mask &= ~PSW_PER_MASK;
264     	}
265     }
266     
267