File: /usr/src/linux/arch/s390/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     extern pgm_check_handler_t do_pseudo_page_fault;
57     #ifdef CONFIG_PFAULT
58     extern int pfault_init(void);
59     extern void pfault_fini(void);
60     extern void pfault_interrupt(struct pt_regs *regs, __u16 error_code);
61     #endif
62     
63     spinlock_t die_lock;
64     
65     void die(const char * str, struct pt_regs * regs, long err)
66     {
67             console_verbose();
68             spin_lock_irq(&die_lock);
69             printk("%s: %04lx\n", str, err & 0xffff);
70             show_regs(regs);
71             spin_unlock_irq(&die_lock);
72             do_exit(SIGSEGV);
73     }
74     
75     #define DO_ERROR(signr, str, name) \
76     asmlinkage void name(struct pt_regs * regs, long interruption_code) \
77     { \
78     	do_trap(interruption_code, signr, str, regs, NULL); \
79     }
80     
81     #define DO_ERROR_INFO(signr, str, name, sicode, siaddr) \
82     asmlinkage void name(struct pt_regs * regs, long interruption_code) \
83     { \
84             siginfo_t info; \
85             info.si_signo = signr; \
86             info.si_errno = 0; \
87             info.si_code = sicode; \
88             info.si_addr = (void *)siaddr; \
89             do_trap(interruption_code, signr, str, regs, &info); \
90     }
91     
92     static void inline do_trap(long interruption_code, int signr, char *str,
93                                struct pt_regs *regs, siginfo_t *info)
94     {
95             if (regs->psw.mask & PSW_PROBLEM_STATE) {
96                     struct task_struct *tsk = current;
97                     tsk->thread.trap_no = interruption_code;
98     		if (info)
99     			force_sig_info(signr, info, tsk);
100     		else
101                     	force_sig(signr, tsk);
102     #ifndef CONFIG_SYSCTL
103     #ifdef CONFIG_PROCESS_DEBUG
104                     printk("User process fault: interruption code 0x%lX\n",
105                            interruption_code);
106                     show_regs(regs);
107     #endif
108     #else
109     		if (sysctl_userprocess_debug) {
110     			printk("User process fault: interruption code 0x%lX\n",
111     			       interruption_code);
112     			show_regs(regs);
113     		}
114     #endif
115             } else {
116                     unsigned long fixup = search_exception_table(regs->psw.addr);
117                     if (fixup)
118                             regs->psw.addr = fixup;
119                     else
120                             die(str, regs, interruption_code);
121             }
122     }
123     
124     int do_debugger_trap(struct pt_regs *regs,int signal)
125     {
126     	if(regs->psw.mask&PSW_PROBLEM_STATE)
127     	{
128     		if(current->ptrace & PT_PTRACED)
129     			force_sig(signal,current);
130     		else
131     			return 1;
132     	}
133     	else
134     	{
135     #if CONFIG_REMOTE_DEBUG
136     		if(gdb_stub_initialised)
137     		{
138     			gdb_stub_handle_exception((gdb_pt_regs *)regs,signal);
139     			return 0;
140     		}
141     #endif
142     		return 1;
143     	}
144     	return 0;
145     }
146     
147     DO_ERROR(SIGSEGV, "Unknown program exception", default_trap_handler)
148     DO_ERROR(SIGILL,  "privileged operation", privileged_op)
149     DO_ERROR(SIGILL,  "execute exception", execute_exception)
150     DO_ERROR(SIGSEGV, "addressing exception", addressing_exception)
151     DO_ERROR(SIGFPE,  "fixpoint divide exception", divide_exception)
152     DO_ERROR(SIGILL,  "translation exception", translation_exception)
153     DO_ERROR(SIGILL,  "special operand exception", special_op_exception)
154     DO_ERROR(SIGILL,  "operand exception", operand_exception)
155     
156     asmlinkage void illegal_op(struct pt_regs * regs, long interruption_code)
157     {
158             __u8 opcode[6];
159     	__u16 *location;
160     	int signal = 0;
161     	int problem_state=(regs->psw.mask & PSW_PROBLEM_STATE);
162     
163     	location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc);
164     	if(problem_state)
165     		get_user(*((__u16 *) opcode), location);
166     	else
167     		*((__u16 *)opcode)=*((__u16 *)location);
168     	if(*((__u16 *)opcode)==S390_BREAKPOINT_U16)
169             {
170     		if(do_debugger_trap(regs,SIGTRAP))
171     			signal = SIGILL;
172     	}
173     #ifdef CONFIG_MATHEMU
174             else if (problem_state)
175     	{
176     		if (opcode[0] == 0xb3) {
177     			get_user(*((__u16 *) (opcode+2)), location+1);
178     			signal = math_emu_b3(opcode, regs);
179                     } else if (opcode[0] == 0xed) {
180     			get_user(*((__u32 *) (opcode+2)),
181     				 (__u32 *)(location+1));
182     			signal = math_emu_ed(opcode, regs);
183     		} else if (*((__u16 *) opcode) == 0xb299) {
184     			get_user(*((__u16 *) (opcode+2)), location+1);
185     			signal = math_emu_srnm(opcode, regs);
186     		} else if (*((__u16 *) opcode) == 0xb29c) {
187     			get_user(*((__u16 *) (opcode+2)), location+1);
188     			signal = math_emu_stfpc(opcode, regs);
189     		} else if (*((__u16 *) opcode) == 0xb29d) {
190     			get_user(*((__u16 *) (opcode+2)), location+1);
191     			signal = math_emu_lfpc(opcode, regs);
192     		} else
193     			signal = SIGILL;
194             }
195     #endif 
196     	else
197     		signal = SIGILL;
198             if (signal == SIGFPE) {
199     		current->thread.ieee_instruction_pointer = (addr_t) location;
200     		do_trap(interruption_code, signal,
201     			"floating point exception", regs, NULL);
202             } else if (signal)
203     		do_trap(interruption_code, signal,
204     			"illegal operation", regs, NULL);
205     }
206     
207     
208     
209     #ifdef CONFIG_MATHEMU
210     asmlinkage void 
211     specification_exception(struct pt_regs * regs, long interruption_code)
212     {
213             __u8 opcode[6];
214     	__u16 *location;
215     	int signal = 0;
216     
217             if (regs->psw.mask & PSW_PROBLEM_STATE) {
218     		location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc);
219     		get_user(*((__u16 *) opcode), location);
220     		switch (opcode[0]) {
221     		case 0x28: /* LDR Rx,Ry   */
222     			signal = math_emu_ldr(opcode);
223     			break;
224     		case 0x38: /* LER Rx,Ry   */
225     			signal = math_emu_ler(opcode);
226     			break;
227     		case 0x60: /* STD R,D(X,B) */
228     			get_user(*((__u16 *) (opcode+2)), location+1);
229     			signal = math_emu_std(opcode, regs);
230     			break;
231     		case 0x68: /* LD R,D(X,B) */
232     			get_user(*((__u16 *) (opcode+2)), location+1);
233     			signal = math_emu_ld(opcode, regs);
234     			break;
235     		case 0x70: /* STE R,D(X,B) */
236     			get_user(*((__u16 *) (opcode+2)), location+1);
237     			signal = math_emu_ste(opcode, regs);
238     			break;
239     		case 0x78: /* LE R,D(X,B) */
240     			get_user(*((__u16 *) (opcode+2)), location+1);
241     			signal = math_emu_le(opcode, regs);
242     			break;
243     		default:
244     			signal = SIGILL;
245     			break;
246                     }
247             } else
248     		signal = SIGILL;
249             if (signal == SIGFPE) {
250     		current->thread.ieee_instruction_pointer = (addr_t) location;
251     		do_trap(interruption_code, signal,
252     			"floating point exception", regs, NULL);
253             } else if (signal)
254                     do_trap(interruption_code, signal,
255     			"specification exception", regs, NULL);
256     }
257     #else
258     DO_ERROR(SIGILL, "specification exception", specification_exception)
259     #endif
260     
261     asmlinkage void data_exception(struct pt_regs * regs, long interruption_code)
262     {
263             __u8 opcode[6];
264     	__u16 *location;
265     	int signal = 0;
266     
267     	location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc);
268     	if (MACHINE_HAS_IEEE)
269     		__asm__ volatile ("stfpc %0\n\t" 
270     				  : "=m" (current->thread.fp_regs.fpc));
271     
272     #ifdef CONFIG_MATHEMU
273             else if (regs->psw.mask & PSW_PROBLEM_STATE) {
274     		get_user(*((__u16 *) opcode), location);
275     		switch (opcode[0]) {
276     		case 0x28: /* LDR Rx,Ry   */
277     			signal = math_emu_ldr(opcode);
278     			break;
279     		case 0x38: /* LER Rx,Ry   */
280     			signal = math_emu_ler(opcode);
281     			break;
282     		case 0x60: /* STD R,D(X,B) */
283     			get_user(*((__u16 *) (opcode+2)), location+1);
284     			signal = math_emu_std(opcode, regs);
285     			break;
286     		case 0x68: /* LD R,D(X,B) */
287     			get_user(*((__u16 *) (opcode+2)), location+1);
288     			signal = math_emu_ld(opcode, regs);
289     			break;
290     		case 0x70: /* STE R,D(X,B) */
291     			get_user(*((__u16 *) (opcode+2)), location+1);
292     			signal = math_emu_ste(opcode, regs);
293     			break;
294     		case 0x78: /* LE R,D(X,B) */
295     			get_user(*((__u16 *) (opcode+2)), location+1);
296     			signal = math_emu_le(opcode, regs);
297     			break;
298     		case 0xb3:
299     			get_user(*((__u16 *) (opcode+2)), location+1);
300     			signal = math_emu_b3(opcode, regs);
301     			break;
302                     case 0xed:
303     			get_user(*((__u32 *) (opcode+2)),
304     				 (__u32 *)(location+1));
305     			signal = math_emu_ed(opcode, regs);
306     			break;
307     	        case 0xb2:
308     			if (opcode[1] == 0x99) {
309     				get_user(*((__u16 *) (opcode+2)), location+1);
310     				signal = math_emu_srnm(opcode, regs);
311     			} else if (opcode[1] == 0x9c) {
312     				get_user(*((__u16 *) (opcode+2)), location+1);
313     				signal = math_emu_stfpc(opcode, regs);
314     			} else if (opcode[1] == 0x9d) {
315     				get_user(*((__u16 *) (opcode+2)), location+1);
316     				signal = math_emu_lfpc(opcode, regs);
317     			} else
318     				signal = SIGILL;
319     			break;
320     		default:
321     			signal = SIGILL;
322     			break;
323                     }
324             }
325     #endif 
326     	if (current->thread.fp_regs.fpc & FPC_DXC_MASK)
327     		signal = SIGFPE;
328     	else
329     		signal = SIGILL;
330             if (signal == SIGFPE) {
331     		current->thread.ieee_instruction_pointer = (addr_t) location;
332     		do_trap(interruption_code, signal,
333     			"floating point exception", regs, NULL);
334     	} else if (signal) 
335                     do_trap(interruption_code, signal,
336     			"data exception", regs, NULL);
337     }
338     
339     
340     
341     /* init is done in lowcore.S and head.S */
342     
343     void __init trap_init(void)
344     {
345             int i;
346     
347             for (i = 0; i < 128; i++)
348               pgm_check_table[i] = &default_trap_handler;
349             pgm_check_table[1] = &illegal_op;
350             pgm_check_table[2] = &privileged_op;
351             pgm_check_table[3] = &execute_exception;
352             pgm_check_table[4] = &do_page_fault;
353             pgm_check_table[5] = &addressing_exception;
354             pgm_check_table[6] = &specification_exception;
355             pgm_check_table[7] = &data_exception;
356             pgm_check_table[9] = &divide_exception;
357             pgm_check_table[0x10] = &do_page_fault;
358             pgm_check_table[0x11] = &do_page_fault;
359             pgm_check_table[0x12] = &translation_exception;
360             pgm_check_table[0x13] = &special_op_exception;
361      	pgm_check_table[0x14] = &do_pseudo_page_fault;
362             pgm_check_table[0x15] = &operand_exception;
363             pgm_check_table[0x1C] = &privileged_op;
364     #ifdef CONFIG_PFAULT
365     	if (MACHINE_IS_VM) {
366     		/* request the 0x2603 external interrupt */
367     		if (register_external_interrupt(0x2603, pfault_interrupt) != 0)
368     			panic("Couldn't request external interrupt 0x2603");
369     		/*
370     		 * First try to get pfault pseudo page faults going.
371     		 * If this isn't available turn on pagex page faults.
372     		 */
373     		if (pfault_init() != 0) {
374     			/* Tough luck, no pfault. */
375     			unregister_external_interrupt(0x2603,
376     						      pfault_interrupt);
377     			cpcmd("SET PAGEX ON", NULL, 0);
378     		}
379     	}
380     #else
381     	if (MACHINE_IS_VM)
382     		cpcmd("SET PAGEX ON", NULL, 0);
383     #endif
384     }
385     
386     
387     void handle_per_exception(struct pt_regs *regs)
388     {
389     	if(regs->psw.mask&PSW_PROBLEM_STATE)
390     	{
391     		per_struct *per_info=&current->thread.per_info;
392     		per_info->lowcore.words.perc_atmid=S390_lowcore.per_perc_atmid;
393     		per_info->lowcore.words.address=S390_lowcore.per_address;
394     		per_info->lowcore.words.access_id=S390_lowcore.per_access_id;
395     	}
396     	if(do_debugger_trap(regs,SIGTRAP))
397     	{
398     		/* I've seen this possibly a task structure being reused ? */
399     		printk("Spurious per exception detected\n");
400     		printk("switching off per tracing for this task.\n");
401     		show_regs(regs);
402     		/* Hopefully switching off per tracing will help us survive */
403     		regs->psw.mask &= ~PSW_PER_MASK;
404     	}
405     }
406     
407