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