Merge tag 'media/v4.6-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[linux-2.6-block.git] / arch / x86 / mm / extable.c
CommitLineData
6d48583b
HH
1#include <linux/module.h>
2#include <linux/spinlock.h>
70627654 3#include <linux/sort.h>
6d48583b
HH
4#include <asm/uaccess.h>
5
548acf19
TL
6typedef bool (*ex_handler_t)(const struct exception_table_entry *,
7 struct pt_regs *, int);
8
70627654
PA
9static inline unsigned long
10ex_insn_addr(const struct exception_table_entry *x)
11{
12 return (unsigned long)&x->insn + x->insn;
13}
14static inline unsigned long
15ex_fixup_addr(const struct exception_table_entry *x)
16{
17 return (unsigned long)&x->fixup + x->fixup;
18}
548acf19
TL
19static inline ex_handler_t
20ex_fixup_handler(const struct exception_table_entry *x)
21{
22 return (ex_handler_t)((unsigned long)&x->handler + x->handler);
23}
6d48583b 24
548acf19
TL
25bool ex_handler_default(const struct exception_table_entry *fixup,
26 struct pt_regs *regs, int trapnr)
6d48583b 27{
548acf19
TL
28 regs->ip = ex_fixup_addr(fixup);
29 return true;
30}
31EXPORT_SYMBOL(ex_handler_default);
32
33bool ex_handler_fault(const struct exception_table_entry *fixup,
34 struct pt_regs *regs, int trapnr)
35{
36 regs->ip = ex_fixup_addr(fixup);
37 regs->ax = trapnr;
38 return true;
39}
40EXPORT_SYMBOL_GPL(ex_handler_fault);
41
42bool ex_handler_ext(const struct exception_table_entry *fixup,
43 struct pt_regs *regs, int trapnr)
44{
45 /* Special hack for uaccess_err */
46 current_thread_info()->uaccess_err = 1;
47 regs->ip = ex_fixup_addr(fixup);
48 return true;
49}
50EXPORT_SYMBOL(ex_handler_ext);
51
52bool ex_has_fault_handler(unsigned long ip)
53{
54 const struct exception_table_entry *e;
55 ex_handler_t handler;
56
57 e = search_exception_tables(ip);
58 if (!e)
59 return false;
60 handler = ex_fixup_handler(e);
61
62 return handler == ex_handler_fault;
63}
64
65int fixup_exception(struct pt_regs *regs, int trapnr)
66{
67 const struct exception_table_entry *e;
68 ex_handler_t handler;
6d48583b
HH
69
70#ifdef CONFIG_PNPBIOS
71 if (unlikely(SEGMENT_IS_PNP_CODE(regs->cs))) {
72 extern u32 pnp_bios_fault_eip, pnp_bios_fault_esp;
73 extern u32 pnp_bios_is_utter_crap;
74 pnp_bios_is_utter_crap = 1;
75 printk(KERN_CRIT "PNPBIOS fault.. attempting recovery.\n");
76 __asm__ volatile(
77 "movl %0, %%esp\n\t"
78 "jmp *%1\n\t"
79 : : "g" (pnp_bios_fault_esp), "g" (pnp_bios_fault_eip));
80 panic("do_trap: can't hit this");
81 }
82#endif
83
548acf19
TL
84 e = search_exception_tables(regs->ip);
85 if (!e)
86 return 0;
6d48583b 87
548acf19
TL
88 handler = ex_fixup_handler(e);
89 return handler(e, regs, trapnr);
6d48583b 90}
6a1ea279
PA
91
92/* Restricted version used during very early boot */
93int __init early_fixup_exception(unsigned long *ip)
94{
548acf19 95 const struct exception_table_entry *e;
70627654 96 unsigned long new_ip;
548acf19 97 ex_handler_t handler;
6a1ea279 98
548acf19
TL
99 e = search_exception_tables(*ip);
100 if (!e)
101 return 0;
70627654 102
548acf19
TL
103 new_ip = ex_fixup_addr(e);
104 handler = ex_fixup_handler(e);
6a1ea279 105
548acf19
TL
106 /* special handling not supported during early boot */
107 if (handler != ex_handler_default)
108 return 0;
6a1ea279 109
548acf19
TL
110 *ip = new_ip;
111 return 1;
6a1ea279 112}
70627654
PA
113
114/*
115 * Search one exception table for an entry corresponding to the
116 * given instruction address, and return the address of the entry,
117 * or NULL if none is found.
118 * We use a binary search, and thus we assume that the table is
119 * already sorted.
120 */
121const struct exception_table_entry *
122search_extable(const struct exception_table_entry *first,
123 const struct exception_table_entry *last,
124 unsigned long value)
125{
126 while (first <= last) {
127 const struct exception_table_entry *mid;
128 unsigned long addr;
129
130 mid = ((last - first) >> 1) + first;
131 addr = ex_insn_addr(mid);
132 if (addr < value)
133 first = mid + 1;
134 else if (addr > value)
135 last = mid - 1;
136 else
137 return mid;
138 }
139 return NULL;
140}
141
142/*
143 * The exception table needs to be sorted so that the binary
144 * search that we use to find entries in it works properly.
145 * This is used both for the kernel exception table and for
146 * the exception tables of modules that get loaded.
147 *
148 */
149static int cmp_ex(const void *a, const void *b)
150{
151 const struct exception_table_entry *x = a, *y = b;
152
153 /*
154 * This value will always end up fittin in an int, because on
155 * both i386 and x86-64 the kernel symbol-reachable address
156 * space is < 2 GiB.
157 *
158 * This compare is only valid after normalization.
159 */
160 return x->insn - y->insn;
161}
162
163void sort_extable(struct exception_table_entry *start,
164 struct exception_table_entry *finish)
165{
166 struct exception_table_entry *p;
167 int i;
168
169 /* Convert all entries to being relative to the start of the section */
170 i = 0;
171 for (p = start; p < finish; p++) {
172 p->insn += i;
173 i += 4;
174 p->fixup += i;
175 i += 4;
548acf19
TL
176 p->handler += i;
177 i += 4;
70627654
PA
178 }
179
180 sort(start, finish - start, sizeof(struct exception_table_entry),
181 cmp_ex, NULL);
182
183 /* Denormalize all entries */
184 i = 0;
185 for (p = start; p < finish; p++) {
186 p->insn -= i;
187 i += 4;
188 p->fixup -= i;
189 i += 4;
548acf19
TL
190 p->handler -= i;
191 i += 4;
70627654
PA
192 }
193}
194
195#ifdef CONFIG_MODULES
196/*
197 * If the exception table is sorted, any referring to the module init
198 * will be at the beginning or the end.
199 */
200void trim_init_extable(struct module *m)
201{
202 /*trim the beginning*/
203 while (m->num_exentries &&
204 within_module_init(ex_insn_addr(&m->extable[0]), m)) {
205 m->extable++;
206 m->num_exentries--;
207 }
208 /*trim the end*/
209 while (m->num_exentries &&
210 within_module_init(ex_insn_addr(&m->extable[m->num_exentries-1]), m))
211 m->num_exentries--;
212}
213#endif /* CONFIG_MODULES */