summaryrefslogtreecommitdiffstats
path: root/kernel/arch/alpha/mm/extable.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/arch/alpha/mm/extable.c')
-rw-r--r--kernel/arch/alpha/mm/extable.c92
1 files changed, 92 insertions, 0 deletions
diff --git a/kernel/arch/alpha/mm/extable.c b/kernel/arch/alpha/mm/extable.c
new file mode 100644
index 000000000..813c9b63c
--- /dev/null
+++ b/kernel/arch/alpha/mm/extable.c
@@ -0,0 +1,92 @@
+/*
+ * linux/arch/alpha/mm/extable.c
+ */
+
+#include <linux/module.h>
+#include <linux/sort.h>
+#include <asm/uaccess.h>
+
+static inline unsigned long ex_to_addr(const struct exception_table_entry *x)
+{
+ return (unsigned long)&x->insn + x->insn;
+}
+
+static void swap_ex(void *a, void *b, int size)
+{
+ struct exception_table_entry *ex_a = a, *ex_b = b;
+ unsigned long addr_a = ex_to_addr(ex_a), addr_b = ex_to_addr(ex_b);
+ unsigned int t = ex_a->fixup.unit;
+
+ ex_a->fixup.unit = ex_b->fixup.unit;
+ ex_b->fixup.unit = t;
+ ex_a->insn = (int)(addr_b - (unsigned long)&ex_a->insn);
+ ex_b->insn = (int)(addr_a - (unsigned long)&ex_b->insn);
+}
+
+/*
+ * The exception table needs to be sorted so that the binary
+ * search that we use to find entries in it works properly.
+ * This is used both for the kernel exception table and for
+ * the exception tables of modules that get loaded.
+ */
+static int cmp_ex(const void *a, const void *b)
+{
+ const struct exception_table_entry *x = a, *y = b;
+
+ /* avoid overflow */
+ if (ex_to_addr(x) > ex_to_addr(y))
+ return 1;
+ if (ex_to_addr(x) < ex_to_addr(y))
+ return -1;
+ return 0;
+}
+
+void sort_extable(struct exception_table_entry *start,
+ struct exception_table_entry *finish)
+{
+ sort(start, finish - start, sizeof(struct exception_table_entry),
+ cmp_ex, swap_ex);
+}
+
+#ifdef CONFIG_MODULES
+/*
+ * Any entry referring to the module init will be at the beginning or
+ * the end.
+ */
+void trim_init_extable(struct module *m)
+{
+ /*trim the beginning*/
+ while (m->num_exentries &&
+ within_module_init(ex_to_addr(&m->extable[0]), m)) {
+ m->extable++;
+ m->num_exentries--;
+ }
+ /*trim the end*/
+ while (m->num_exentries &&
+ within_module_init(ex_to_addr(&m->extable[m->num_exentries-1]),
+ m))
+ m->num_exentries--;
+}
+#endif /* CONFIG_MODULES */
+
+const struct exception_table_entry *
+search_extable(const struct exception_table_entry *first,
+ const struct exception_table_entry *last,
+ unsigned long value)
+{
+ while (first <= last) {
+ const struct exception_table_entry *mid;
+ unsigned long mid_value;
+
+ mid = (last - first) / 2 + first;
+ mid_value = ex_to_addr(mid);
+ if (mid_value == value)
+ return mid;
+ else if (mid_value < value)
+ first = mid+1;
+ else
+ last = mid-1;
+ }
+
+ return NULL;
+}