1/*
2 * IA-32 exception handlers
3 *
4 * Copyright (C) 2000 Asit K. Mallick <asit.k.mallick@intel.com>
5 * Copyright (C) 2001-2002 Hewlett-Packard Co
6 *	David Mosberger-Tang <davidm@hpl.hp.com>
7 *
8 * 06/16/00	A. Mallick	added siginfo for most cases (close to IA32)
9 * 09/29/00	D. Mosberger	added ia32_intercept()
10 */
11
12#include <linux/kernel.h>
13#include <linux/sched.h>
14
15#include <asm/ia32.h>
16#include <asm/ptrace.h>
17
18int
19ia32_intercept (struct pt_regs *regs, unsigned long isr)
20{
21	switch ((isr >> 16) & 0xff) {
22	      case 0:	/* Instruction intercept fault */
23	      case 4:	/* Locked Data reference fault */
24	      case 1:	/* Gate intercept trap */
25		return -1;
26
27	      case 2:	/* System flag trap */
28		if (((isr >> 14) & 0x3) >= 2) {
29			/* MOV SS, POP SS instructions */
30			ia64_psr(regs)->id = 1;
31			return 0;
32		} else
33			return -1;
34	}
35	return -1;
36}
37
38int
39ia32_exception (struct pt_regs *regs, unsigned long isr)
40{
41	struct siginfo siginfo;
42
43	/* initialize these fields to avoid leaking kernel bits to user space: */
44	siginfo.si_errno = 0;
45	siginfo.si_flags = 0;
46	siginfo.si_isr = 0;
47	siginfo.si_imm = 0;
48	switch ((isr >> 16) & 0xff) {
49	      case 1:
50	      case 2:
51		siginfo.si_signo = SIGTRAP;
52		if (isr == 0)
53			siginfo.si_code = TRAP_TRACE;
54		else if (isr & 0x4)
55			siginfo.si_code = TRAP_BRANCH;
56		else
57			siginfo.si_code = TRAP_BRKPT;
58		break;
59
60	      case 3:
61		siginfo.si_signo = SIGTRAP;
62		siginfo.si_code = TRAP_BRKPT;
63		break;
64
65	      case 0:	/* Divide fault */
66		siginfo.si_signo = SIGFPE;
67		siginfo.si_code = FPE_INTDIV;
68		break;
69
70	      case 4:	/* Overflow */
71	      case 5:	/* Bounds fault */
72		siginfo.si_signo = SIGFPE;
73		siginfo.si_code = 0;
74		break;
75
76	      case 6:	/* Invalid Op-code */
77		siginfo.si_signo = SIGILL;
78		siginfo.si_code = ILL_ILLOPN;
79		break;
80
81	      case 7:	/* FP DNA */
82	      case 8:	/* Double Fault */
83	      case 9:	/* Invalid TSS */
84	      case 11:	/* Segment not present */
85	      case 12:	/* Stack fault */
86	      case 13:	/* General Protection Fault */
87		siginfo.si_signo = SIGSEGV;
88		siginfo.si_code = 0;
89		break;
90
91	      case 16:	/* Pending FP error */
92		{
93			unsigned long fsr, fcr;
94
95			asm ("mov %0=ar.fsr;"
96			     "mov %1=ar.fcr;"
97			     : "=r"(fsr), "=r"(fcr));
98
99			siginfo.si_signo = SIGFPE;
100			/*
101			 * (~cwd & swd) will mask out exceptions that are not set to unmasked
102			 * status.  0x3f is the exception bits in these regs, 0x200 is the
103			 * C1 reg you need in case of a stack fault, 0x040 is the stack
104			 * fault bit.  We should only be taking one exception at a time,
105			 * so if this combination doesn't produce any single exception,
106			 * then we have a bad program that isn't syncronizing its FPU usage
107			 * and it will suffer the consequences since we won't be able to
108			 * fully reproduce the context of the exception
109			 */
110			siginfo.si_isr = isr;
111			siginfo.si_flags = __ISR_VALID;
112			switch(((~fcr) & (fsr & 0x3f)) | (fsr & 0x240)) {
113				case 0x000:
114				default:
115					siginfo.si_code = 0;
116					break;
117				case 0x001: /* Invalid Op */
118				case 0x040: /* Stack Fault */
119				case 0x240: /* Stack Fault | Direction */
120					siginfo.si_code = FPE_FLTINV;
121					break;
122				case 0x002: /* Denormalize */
123				case 0x010: /* Underflow */
124					siginfo.si_code = FPE_FLTUND;
125					break;
126				case 0x004: /* Zero Divide */
127					siginfo.si_code = FPE_FLTDIV;
128					break;
129				case 0x008: /* Overflow */
130					siginfo.si_code = FPE_FLTOVF;
131					break;
132				case 0x020: /* Precision */
133					siginfo.si_code = FPE_FLTRES;
134					break;
135			}
136
137			break;
138		}
139
140	      case 17:	/* Alignment check */
141		siginfo.si_signo = SIGSEGV;
142		siginfo.si_code = BUS_ADRALN;
143		break;
144
145	      case 19:	/* SSE Numeric error */
146		siginfo.si_signo = SIGFPE;
147		siginfo.si_code = 0;
148		break;
149
150	      default:
151		return -1;
152	}
153	force_sig_info(siginfo.si_signo, &siginfo, current);
154	return 0;
155}
156