da442f37ca8b53d7db269640292621876ca4f0c3
[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 extern unsigned int early_recursion_flag;
87
88 /* Restricted version used during very early boot */
89 void __init early_fixup_exception(struct pt_regs *regs, int trapnr)
90 {
91         const struct exception_table_entry *e;
92         unsigned long new_ip;
93         ex_handler_t handler;
94
95         /* Ignore early NMIs. */
96         if (trapnr == X86_TRAP_NMI)
97                 return;
98
99         if (early_recursion_flag > 2)
100                 goto halt_loop;
101
102         if (regs->cs != __KERNEL_CS)
103                 goto fail;
104
105         e = search_exception_tables(regs->ip);
106         if (!e)
107                 goto fail;
108
109         new_ip  = ex_fixup_addr(e);
110         handler = ex_fixup_handler(e);
111
112         /* special handling not supported during early boot */
113         if (handler != ex_handler_default)
114                 goto fail;
115
116         regs->ip = new_ip;
117         return;
118
119 fail:
120         early_printk("PANIC: early exception 0x%02x IP %lx:%lx error %lx cr2 0x%lx\n",
121                      (unsigned)trapnr, (unsigned long)regs->cs, regs->ip,
122                      regs->orig_ax, read_cr2());
123
124         show_regs(regs);
125
126 halt_loop:
127         while (true)
128                 halt();
129 }