import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / arch / sparc / mm / extable.c
1 /*
2  * linux/arch/sparc/mm/extable.c
3  */
4
5 #include <linux/config.h>
6 #include <linux/module.h>
7 #include <asm/uaccess.h>
8
9 extern const struct exception_table_entry __start___ex_table[];
10 extern const struct exception_table_entry __stop___ex_table[];
11
12 static unsigned long
13 search_one_table(const struct exception_table_entry *start,
14                  const struct exception_table_entry *end,
15                  unsigned long value, unsigned long *g2)
16 {
17         const struct exception_table_entry *walk;
18
19         /* Single insn entries are encoded as:
20          *      word 1: insn address
21          *      word 2: fixup code address
22          *
23          * Range entries are encoded as:
24          *      word 1: first insn address
25          *      word 2: 0
26          *      word 3: last insn address + 4 bytes
27          *      word 4: fixup code address
28          *
29          * See asm/uaccess.h for more details.
30          */
31
32         /* 1. Try to find an exact match. */
33         for (walk = start; walk <= end; walk++) {
34                 if (walk->fixup == 0) {
35                         /* A range entry, skip both parts. */
36                         walk++;
37                         continue;
38                 }
39
40                 if (walk->insn == value)
41                         return walk->fixup;
42         }
43
44         /* 2. Try to find a range match. */
45         for (walk = start; walk <= (end - 1); walk++) {
46                 if (walk->fixup)
47                         continue;
48
49                 if (walk[0].insn <= value &&
50                     walk[1].insn > value) {
51                         *g2 = (value - walk[0].insn) / 4;
52                         return walk[1].fixup;
53                 }
54                 walk++;
55         }
56
57         return 0;
58 }
59
60 extern spinlock_t modlist_lock;
61
62 unsigned long
63 search_exception_table(unsigned long addr, unsigned long *g2)
64 {
65         unsigned long ret = 0, flags;
66
67 #ifndef CONFIG_MODULES
68         /* There is only the kernel to search.  */
69         ret = search_one_table(__start___ex_table,
70                                __stop___ex_table-1, addr, g2);
71         return ret;
72 #else
73         /* The kernel is the last "module" -- no need to treat it special.  */
74         struct module *mp;
75
76         spin_lock_irqsave(&modlist_lock, flags);
77         for (mp = module_list; mp != NULL; mp = mp->next) {
78                 if (mp->ex_table_start == NULL || !(mp->flags & (MOD_RUNNING | MOD_INITIALIZING)))
79                         continue;
80                 ret = search_one_table(mp->ex_table_start,
81                                        mp->ex_table_end-1, addr, g2);
82                 if (ret)
83                         break;
84         }
85         spin_unlock_irqrestore(&modlist_lock, flags);
86         return ret;
87 #endif
88 }