diff options
Diffstat (limited to 'arch/frv/mm/extable.c')
-rw-r--r-- | arch/frv/mm/extable.c | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/arch/frv/mm/extable.c b/arch/frv/mm/extable.c new file mode 100644 index 000000000000..41be1128dc64 --- /dev/null +++ b/arch/frv/mm/extable.c | |||
@@ -0,0 +1,91 @@ | |||
1 | /* | ||
2 | * linux/arch/frv/mm/extable.c | ||
3 | */ | ||
4 | |||
5 | #include <linux/config.h> | ||
6 | #include <linux/module.h> | ||
7 | #include <linux/spinlock.h> | ||
8 | #include <asm/uaccess.h> | ||
9 | |||
10 | extern const struct exception_table_entry __attribute__((aligned(8))) __start___ex_table[]; | ||
11 | extern const struct exception_table_entry __attribute__((aligned(8))) __stop___ex_table[]; | ||
12 | extern const void __memset_end, __memset_user_error_lr, __memset_user_error_handler; | ||
13 | extern const void __memcpy_end, __memcpy_user_error_lr, __memcpy_user_error_handler; | ||
14 | extern spinlock_t modlist_lock; | ||
15 | |||
16 | /*****************************************************************************/ | ||
17 | /* | ||
18 | * | ||
19 | */ | ||
20 | static inline unsigned long search_one_table(const struct exception_table_entry *first, | ||
21 | const struct exception_table_entry *last, | ||
22 | unsigned long value) | ||
23 | { | ||
24 | while (first <= last) { | ||
25 | const struct exception_table_entry __attribute__((aligned(8))) *mid; | ||
26 | long diff; | ||
27 | |||
28 | mid = (last - first) / 2 + first; | ||
29 | diff = mid->insn - value; | ||
30 | if (diff == 0) | ||
31 | return mid->fixup; | ||
32 | else if (diff < 0) | ||
33 | first = mid + 1; | ||
34 | else | ||
35 | last = mid - 1; | ||
36 | } | ||
37 | return 0; | ||
38 | } /* end search_one_table() */ | ||
39 | |||
40 | /*****************************************************************************/ | ||
41 | /* | ||
42 | * see if there's a fixup handler available to deal with a kernel fault | ||
43 | */ | ||
44 | unsigned long search_exception_table(unsigned long pc) | ||
45 | { | ||
46 | unsigned long ret = 0; | ||
47 | |||
48 | /* determine if the fault lay during a memcpy_user or a memset_user */ | ||
49 | if (__frame->lr == (unsigned long) &__memset_user_error_lr && | ||
50 | (unsigned long) &memset <= pc && pc < (unsigned long) &__memset_end | ||
51 | ) { | ||
52 | /* the fault occurred in a protected memset | ||
53 | * - we search for the return address (in LR) instead of the program counter | ||
54 | * - it was probably during a clear_user() | ||
55 | */ | ||
56 | return (unsigned long) &__memset_user_error_handler; | ||
57 | } | ||
58 | else if (__frame->lr == (unsigned long) &__memcpy_user_error_lr && | ||
59 | (unsigned long) &memcpy <= pc && pc < (unsigned long) &__memcpy_end | ||
60 | ) { | ||
61 | /* the fault occurred in a protected memset | ||
62 | * - we search for the return address (in LR) instead of the program counter | ||
63 | * - it was probably during a copy_to/from_user() | ||
64 | */ | ||
65 | return (unsigned long) &__memcpy_user_error_handler; | ||
66 | } | ||
67 | |||
68 | #ifndef CONFIG_MODULES | ||
69 | /* there is only the kernel to search. */ | ||
70 | ret = search_one_table(__start___ex_table, __stop___ex_table - 1, pc); | ||
71 | return ret; | ||
72 | |||
73 | #else | ||
74 | /* the kernel is the last "module" -- no need to treat it special */ | ||
75 | unsigned long flags; | ||
76 | struct module *mp; | ||
77 | |||
78 | spin_lock_irqsave(&modlist_lock, flags); | ||
79 | |||
80 | for (mp = module_list; mp != NULL; mp = mp->next) { | ||
81 | if (mp->ex_table_start == NULL || !(mp->flags & (MOD_RUNNING | MOD_INITIALIZING))) | ||
82 | continue; | ||
83 | ret = search_one_table(mp->ex_table_start, mp->ex_table_end - 1, pc); | ||
84 | if (ret) | ||
85 | break; | ||
86 | } | ||
87 | |||
88 | spin_unlock_irqrestore(&modlist_lock, flags); | ||
89 | return ret; | ||
90 | #endif | ||
91 | } /* end search_exception_table() */ | ||