Merge remote-tracking branches 'asoc/topic/ab8500', 'asoc/topic/arizona', 'asoc/topic...
[sfrench/cifs-2.6.git] / arch / x86 / mm / extable.c
1 #include <linux/extable.h>
2 #include <asm/uaccess.h>
3 #include <asm/traps.h>
4 #include <asm/kdebug.h>
5
6 typedef bool (*ex_handler_t)(const struct exception_table_entry *,
7                             struct pt_regs *, int);
8
9 static inline unsigned long
10 ex_fixup_addr(const struct exception_table_entry *x)
11 {
12         return (unsigned long)&x->fixup + x->fixup;
13 }
14 static inline ex_handler_t
15 ex_fixup_handler(const struct exception_table_entry *x)
16 {
17         return (ex_handler_t)((unsigned long)&x->handler + x->handler);
18 }
19
20 bool ex_handler_default(const struct exception_table_entry *fixup,
21                        struct pt_regs *regs, int trapnr)
22 {
23         regs->ip = ex_fixup_addr(fixup);
24         return true;
25 }
26 EXPORT_SYMBOL(ex_handler_default);
27
28 bool ex_handler_fault(const struct exception_table_entry *fixup,
29                      struct pt_regs *regs, int trapnr)
30 {
31         regs->ip = ex_fixup_addr(fixup);
32         regs->ax = trapnr;
33         return true;
34 }
35 EXPORT_SYMBOL_GPL(ex_handler_fault);
36
37 bool ex_handler_ext(const struct exception_table_entry *fixup,
38                    struct pt_regs *regs, int trapnr)
39 {
40         /* Special hack for uaccess_err */
41         current->thread.uaccess_err = 1;
42         regs->ip = ex_fixup_addr(fixup);
43         return true;
44 }
45 EXPORT_SYMBOL(ex_handler_ext);
46
47 bool ex_handler_rdmsr_unsafe(const struct exception_table_entry *fixup,
48                              struct pt_regs *regs, int trapnr)
49 {
50         if (pr_warn_once("unchecked MSR access error: RDMSR from 0x%x at rIP: 0x%lx (%pF)\n",
51                          (unsigned int)regs->cx, regs->ip, (void *)regs->ip))
52                 show_stack_regs(regs);
53
54         /* Pretend that the read succeeded and returned 0. */
55         regs->ip = ex_fixup_addr(fixup);
56         regs->ax = 0;
57         regs->dx = 0;
58         return true;
59 }
60 EXPORT_SYMBOL(ex_handler_rdmsr_unsafe);
61
62 bool ex_handler_wrmsr_unsafe(const struct exception_table_entry *fixup,
63                              struct pt_regs *regs, int trapnr)
64 {
65         if (pr_warn_once("unchecked MSR access error: WRMSR to 0x%x (tried to write 0x%08x%08x) at rIP: 0x%lx (%pF)\n",
66                          (unsigned int)regs->cx, (unsigned int)regs->dx,
67                          (unsigned int)regs->ax,  regs->ip, (void *)regs->ip))
68                 show_stack_regs(regs);
69
70         /* Pretend that the write succeeded. */
71         regs->ip = ex_fixup_addr(fixup);
72         return true;
73 }
74 EXPORT_SYMBOL(ex_handler_wrmsr_unsafe);
75
76 bool ex_handler_clear_fs(const struct exception_table_entry *fixup,
77                          struct pt_regs *regs, int trapnr)
78 {
79         if (static_cpu_has(X86_BUG_NULL_SEG))
80                 asm volatile ("mov %0, %%fs" : : "rm" (__USER_DS));
81         asm volatile ("mov %0, %%fs" : : "rm" (0));
82         return ex_handler_default(fixup, regs, trapnr);
83 }
84 EXPORT_SYMBOL(ex_handler_clear_fs);
85
86 bool ex_has_fault_handler(unsigned long ip)
87 {
88         const struct exception_table_entry *e;
89         ex_handler_t handler;
90
91         e = search_exception_tables(ip);
92         if (!e)
93                 return false;
94         handler = ex_fixup_handler(e);
95
96         return handler == ex_handler_fault;
97 }
98
99 int fixup_exception(struct pt_regs *regs, int trapnr)
100 {
101         const struct exception_table_entry *e;
102         ex_handler_t handler;
103
104 #ifdef CONFIG_PNPBIOS
105         if (unlikely(SEGMENT_IS_PNP_CODE(regs->cs))) {
106                 extern u32 pnp_bios_fault_eip, pnp_bios_fault_esp;
107                 extern u32 pnp_bios_is_utter_crap;
108                 pnp_bios_is_utter_crap = 1;
109                 printk(KERN_CRIT "PNPBIOS fault.. attempting recovery.\n");
110                 __asm__ volatile(
111                         "movl %0, %%esp\n\t"
112                         "jmp *%1\n\t"
113                         : : "g" (pnp_bios_fault_esp), "g" (pnp_bios_fault_eip));
114                 panic("do_trap: can't hit this");
115         }
116 #endif
117
118         e = search_exception_tables(regs->ip);
119         if (!e)
120                 return 0;
121
122         handler = ex_fixup_handler(e);
123         return handler(e, regs, trapnr);
124 }
125
126 extern unsigned int early_recursion_flag;
127
128 /* Restricted version used during very early boot */
129 void __init early_fixup_exception(struct pt_regs *regs, int trapnr)
130 {
131         /* Ignore early NMIs. */
132         if (trapnr == X86_TRAP_NMI)
133                 return;
134
135         if (early_recursion_flag > 2)
136                 goto halt_loop;
137
138         /*
139          * Old CPUs leave the high bits of CS on the stack
140          * undefined.  I'm not sure which CPUs do this, but at least
141          * the 486 DX works this way.
142          */
143         if ((regs->cs & 0xFFFF) != __KERNEL_CS)
144                 goto fail;
145
146         /*
147          * The full exception fixup machinery is available as soon as
148          * the early IDT is loaded.  This means that it is the
149          * responsibility of extable users to either function correctly
150          * when handlers are invoked early or to simply avoid causing
151          * exceptions before they're ready to handle them.
152          *
153          * This is better than filtering which handlers can be used,
154          * because refusing to call a handler here is guaranteed to
155          * result in a hard-to-debug panic.
156          *
157          * Keep in mind that not all vectors actually get here.  Early
158          * fage faults, for example, are special.
159          */
160         if (fixup_exception(regs, trapnr))
161                 return;
162
163 fail:
164         early_printk("PANIC: early exception 0x%02x IP %lx:%lx error %lx cr2 0x%lx\n",
165                      (unsigned)trapnr, (unsigned long)regs->cs, regs->ip,
166                      regs->orig_ax, read_cr2());
167
168         show_regs(regs);
169
170 halt_loop:
171         while (true)
172                 halt();
173 }