x86/head: Move the early NMI fixup into C
[cascardo/linux.git] / arch / x86 / mm / extable.c
1 #include <linux/module.h>
2 #include <asm/uaccess.h>
3 #include <asm/traps.h>
4
5 typedef bool (*ex_handler_t)(const struct exception_table_entry *,
6                             struct pt_regs *, int);
7
8 static inline unsigned long
9 ex_fixup_addr(const struct exception_table_entry *x)
10 {
11         return (unsigned long)&x->fixup + x->fixup;
12 }
13 static inline ex_handler_t
14 ex_fixup_handler(const struct exception_table_entry *x)
15 {
16         return (ex_handler_t)((unsigned long)&x->handler + x->handler);
17 }
18
19 bool ex_handler_default(const struct exception_table_entry *fixup,
20                        struct pt_regs *regs, int trapnr)
21 {
22         regs->ip = ex_fixup_addr(fixup);
23         return true;
24 }
25 EXPORT_SYMBOL(ex_handler_default);
26
27 bool ex_handler_fault(const struct exception_table_entry *fixup,
28                      struct pt_regs *regs, int trapnr)
29 {
30         regs->ip = ex_fixup_addr(fixup);
31         regs->ax = trapnr;
32         return true;
33 }
34 EXPORT_SYMBOL_GPL(ex_handler_fault);
35
36 bool ex_handler_ext(const struct exception_table_entry *fixup,
37                    struct pt_regs *regs, int trapnr)
38 {
39         /* Special hack for uaccess_err */
40         current_thread_info()->uaccess_err = 1;
41         regs->ip = ex_fixup_addr(fixup);
42         return true;
43 }
44 EXPORT_SYMBOL(ex_handler_ext);
45
46 bool ex_has_fault_handler(unsigned long ip)
47 {
48         const struct exception_table_entry *e;
49         ex_handler_t handler;
50
51         e = search_exception_tables(ip);
52         if (!e)
53                 return false;
54         handler = ex_fixup_handler(e);
55
56         return handler == ex_handler_fault;
57 }
58
59 int fixup_exception(struct pt_regs *regs, int trapnr)
60 {
61         const struct exception_table_entry *e;
62         ex_handler_t handler;
63
64 #ifdef CONFIG_PNPBIOS
65         if (unlikely(SEGMENT_IS_PNP_CODE(regs->cs))) {
66                 extern u32 pnp_bios_fault_eip, pnp_bios_fault_esp;
67                 extern u32 pnp_bios_is_utter_crap;
68                 pnp_bios_is_utter_crap = 1;
69                 printk(KERN_CRIT "PNPBIOS fault.. attempting recovery.\n");
70                 __asm__ volatile(
71                         "movl %0, %%esp\n\t"
72                         "jmp *%1\n\t"
73                         : : "g" (pnp_bios_fault_esp), "g" (pnp_bios_fault_eip));
74                 panic("do_trap: can't hit this");
75         }
76 #endif
77
78         e = search_exception_tables(regs->ip);
79         if (!e)
80                 return 0;
81
82         handler = ex_fixup_handler(e);
83         return handler(e, regs, trapnr);
84 }
85
86 /* Restricted version used during very early boot */
87 int __init early_fixup_exception(struct pt_regs *regs, int trapnr)
88 {
89         const struct exception_table_entry *e;
90         unsigned long new_ip;
91         ex_handler_t handler;
92
93         /* Ignore early NMIs. */
94         if (trapnr == X86_TRAP_NMI)
95                 return 1;
96
97         e = search_exception_tables(regs->ip);
98         if (!e)
99                 return 0;
100
101         new_ip  = ex_fixup_addr(e);
102         handler = ex_fixup_handler(e);
103
104         /* special handling not supported during early boot */
105         if (handler != ex_handler_default)
106                 return 0;
107
108         regs->ip = new_ip;
109         return 1;
110 }