1 /*
2 * linux/arch/x86_64/mm/extable.c
3 */
4
5 #include <linux/config.h>
6 #include <linux/module.h>
7 #include <linux/spinlock.h>
8 #include <linux/init.h>
9 #include <asm/uaccess.h>
10
11 extern const struct exception_table_entry __start___ex_table[];
12 extern const struct exception_table_entry __stop___ex_table[];
13
14 static inline unsigned long
search_one_table(const struct exception_table_entry * first,const struct exception_table_entry * last,unsigned long value)15 search_one_table(const struct exception_table_entry *first,
16 const struct exception_table_entry *last,
17 unsigned long value)
18 {
19 while (first <= last) {
20 const struct exception_table_entry *mid;
21 long diff;
22
23 mid = (last - first) / 2 + first;
24 diff = mid->insn - value;
25 if (diff == 0)
26 return mid->fixup;
27 else if (diff < 0)
28 first = mid+1;
29 else
30 last = mid-1;
31 }
32 return 0;
33 }
34
35 extern spinlock_t modlist_lock;
36
37 unsigned long
search_exception_table(unsigned long addr)38 search_exception_table(unsigned long addr)
39 {
40 unsigned long ret = 0;
41 unsigned long flags;
42
43 /* Workaround for an Opteron issue */
44 if ((addr >> 32) == 0)
45 addr |= 0xffffffffUL << 32;
46
47 #ifndef CONFIG_MODULES
48 /* There is only the kernel to search. */
49 ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr);
50 return ret;
51 #else
52 /* The kernel is the last "module" -- no need to treat it special. */
53 struct module *mp;
54
55 spin_lock_irqsave(&modlist_lock, flags);
56 for (mp = module_list; mp != NULL; mp = mp->next) {
57 if (mp->ex_table_start == NULL || !(mp->flags&(MOD_RUNNING|MOD_INITIALIZING)))
58 continue;
59 ret = search_one_table(mp->ex_table_start,
60 mp->ex_table_end - 1, addr);
61 if (ret)
62 break;
63 }
64 spin_unlock_irqrestore(&modlist_lock, flags);
65 return ret;
66 #endif
67 }
68