1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 1994, 1995, 1996, 1999 by Ralf Baechle
7  * Copyright (C) 1999 by Silicon Graphics
8  */
9 #include <linux/config.h>
10 #include <linux/module.h>
11 #include <linux/spinlock.h>
12 #include <asm/uaccess.h>
13 
14 extern const struct exception_table_entry __start___ex_table[];
15 extern const struct exception_table_entry __stop___ex_table[];
16 
17 static inline unsigned long
search_one_table(const struct exception_table_entry * first,const struct exception_table_entry * last,unsigned long value)18 search_one_table(const struct exception_table_entry *first,
19                  const struct exception_table_entry *last,
20                  unsigned long value)
21 {
22 	while (first <= last) {
23 		const struct exception_table_entry *mid;
24 		long diff;
25 
26 		mid = (last - first) / 2 + first;
27 		diff = mid->insn - value;
28 		if (diff == 0)
29 			return mid->nextinsn;
30 		else if (diff < 0)
31 			first = mid+1;
32 		else
33 			last = mid-1;
34 	}
35 	return 0;
36 }
37 
38 extern spinlock_t modlist_lock;
39 
search_exception_table(unsigned long addr)40 unsigned long search_exception_table(unsigned long addr)
41 {
42 	unsigned long ret = 0;
43 
44 #ifndef CONFIG_MODULES
45 	/* There is only the kernel to search.  */
46 	ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr);
47 	return ret;
48 #else
49 	unsigned long flags;
50 	/* The kernel is the last "module" -- no need to treat it special.  */
51 	struct module *mp;
52 
53 	spin_lock_irqsave(&modlist_lock, flags);
54 	for (mp = module_list; mp != NULL; mp = mp->next) {
55 		if (mp->ex_table_start == NULL)
56 			continue;
57 		ret = search_one_table(mp->ex_table_start,
58 				       mp->ex_table_end - 1, addr);
59 		if (ret)
60 			break;
61 	}
62 	spin_unlock_irqrestore(&modlist_lock, flags);
63 	return ret;
64 #endif
65 }
66