1#include <linux/extable.h>
2#include <linux/uaccess.h>
3#include <linux/sched/debug.h>
4#include <xen/xen.h>
5
6#include <asm/fpu/internal.h>
7#include <asm/traps.h>
8#include <asm/kdebug.h>
9
10typedef bool (*ex_handler_t)(const struct exception_table_entry *,
11 struct pt_regs *, int);
12
13static inline unsigned long
14ex_fixup_addr(const struct exception_table_entry *x)
15{
16 return (unsigned long)&x->fixup + x->fixup;
17}
18static inline ex_handler_t
19ex_fixup_handler(const struct exception_table_entry *x)
20{
21 return (ex_handler_t)((unsigned long)&x->handler + x->handler);
22}
23
24__visible bool ex_handler_default(const struct exception_table_entry *fixup,
25 struct pt_regs *regs, int trapnr)
26{
27 regs->ip = ex_fixup_addr(fixup);
28 return true;
29}
30EXPORT_SYMBOL(ex_handler_default);
31
32__visible bool ex_handler_fault(const struct exception_table_entry *fixup,
33 struct pt_regs *regs, int trapnr)
34{
35 regs->ip = ex_fixup_addr(fixup);
36 regs->ax = trapnr;
37 return true;
38}
39EXPORT_SYMBOL_GPL(ex_handler_fault);
40
41
42
43
44
45__visible bool ex_handler_refcount(const struct exception_table_entry *fixup,
46 struct pt_regs *regs, int trapnr)
47{
48
49 *(int *)regs->cx = INT_MIN / 2;
50
51
52
53
54
55
56
57
58
59
60 regs->ip = ex_fixup_addr(fixup);
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 if (regs->flags & (X86_EFLAGS_OF | X86_EFLAGS_ZF)) {
76 bool zero = regs->flags & X86_EFLAGS_ZF;
77
78 refcount_error_report(regs, zero ? "hit zero" : "overflow");
79 } else if ((regs->flags & X86_EFLAGS_SF) == 0) {
80
81 refcount_error_report(regs, "unexpected saturation");
82 }
83
84 return true;
85}
86EXPORT_SYMBOL(ex_handler_refcount);
87
88
89
90
91
92
93
94
95
96
97
98__visible bool ex_handler_fprestore(const struct exception_table_entry *fixup,
99 struct pt_regs *regs, int trapnr)
100{
101 regs->ip = ex_fixup_addr(fixup);
102
103 WARN_ONCE(1, "Bad FPU state detected at %pB, reinitializing FPU registers.",
104 (void *)instruction_pointer(regs));
105
106 __copy_kernel_to_fpregs(&init_fpstate, -1);
107 return true;
108}
109EXPORT_SYMBOL_GPL(ex_handler_fprestore);
110
111__visible bool ex_handler_ext(const struct exception_table_entry *fixup,
112 struct pt_regs *regs, int trapnr)
113{
114
115 current->thread.uaccess_err = 1;
116 regs->ip = ex_fixup_addr(fixup);
117 return true;
118}
119EXPORT_SYMBOL(ex_handler_ext);
120
121__visible bool ex_handler_rdmsr_unsafe(const struct exception_table_entry *fixup,
122 struct pt_regs *regs, int trapnr)
123{
124 if (pr_warn_once("unchecked MSR access error: RDMSR from 0x%x at rIP: 0x%lx (%pF)\n",
125 (unsigned int)regs->cx, regs->ip, (void *)regs->ip))
126 show_stack_regs(regs);
127
128
129 regs->ip = ex_fixup_addr(fixup);
130 regs->ax = 0;
131 regs->dx = 0;
132 return true;
133}
134EXPORT_SYMBOL(ex_handler_rdmsr_unsafe);
135
136__visible bool ex_handler_wrmsr_unsafe(const struct exception_table_entry *fixup,
137 struct pt_regs *regs, int trapnr)
138{
139 if (pr_warn_once("unchecked MSR access error: WRMSR to 0x%x (tried to write 0x%08x%08x) at rIP: 0x%lx (%pF)\n",
140 (unsigned int)regs->cx, (unsigned int)regs->dx,
141 (unsigned int)regs->ax, regs->ip, (void *)regs->ip))
142 show_stack_regs(regs);
143
144
145 regs->ip = ex_fixup_addr(fixup);
146 return true;
147}
148EXPORT_SYMBOL(ex_handler_wrmsr_unsafe);
149
150__visible bool ex_handler_clear_fs(const struct exception_table_entry *fixup,
151 struct pt_regs *regs, int trapnr)
152{
153 if (static_cpu_has(X86_BUG_NULL_SEG))
154 asm volatile ("mov %0, %%fs" : : "rm" (__USER_DS));
155 asm volatile ("mov %0, %%fs" : : "rm" (0));
156 return ex_handler_default(fixup, regs, trapnr);
157}
158EXPORT_SYMBOL(ex_handler_clear_fs);
159
160__visible bool ex_has_fault_handler(unsigned long ip)
161{
162 const struct exception_table_entry *e;
163 ex_handler_t handler;
164
165 e = search_exception_tables(ip);
166 if (!e)
167 return false;
168 handler = ex_fixup_handler(e);
169
170 return handler == ex_handler_fault;
171}
172
173int fixup_exception(struct pt_regs *regs, int trapnr)
174{
175 const struct exception_table_entry *e;
176 ex_handler_t handler;
177
178#ifdef CONFIG_PNPBIOS
179 if (unlikely(SEGMENT_IS_PNP_CODE(regs->cs))) {
180 extern u32 pnp_bios_fault_eip, pnp_bios_fault_esp;
181 extern u32 pnp_bios_is_utter_crap;
182 pnp_bios_is_utter_crap = 1;
183 printk(KERN_CRIT "PNPBIOS fault.. attempting recovery.\n");
184 __asm__ volatile(
185 "movl %0, %%esp\n\t"
186 "jmp *%1\n\t"
187 : : "g" (pnp_bios_fault_esp), "g" (pnp_bios_fault_eip));
188 panic("do_trap: can't hit this");
189 }
190#endif
191
192 e = search_exception_tables(regs->ip);
193 if (!e)
194 return 0;
195
196 handler = ex_fixup_handler(e);
197 return handler(e, regs, trapnr);
198}
199
200extern unsigned int early_recursion_flag;
201
202
203void __init early_fixup_exception(struct pt_regs *regs, int trapnr)
204{
205
206 if (trapnr == X86_TRAP_NMI)
207 return;
208
209 if (early_recursion_flag > 2)
210 goto halt_loop;
211
212
213
214
215
216
217
218 if (!xen_pv_domain() && regs->cs != __KERNEL_CS)
219 goto fail;
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235 if (fixup_exception(regs, trapnr))
236 return;
237
238 if (fixup_bug(regs, trapnr))
239 return;
240
241fail:
242 early_printk("PANIC: early exception 0x%02x IP %lx:%lx error %lx cr2 0x%lx\n",
243 (unsigned)trapnr, (unsigned long)regs->cs, regs->ip,
244 regs->orig_ax, read_cr2());
245
246 show_regs(regs);
247
248halt_loop:
249 while (true)
250 halt();
251}
252