|
@@ -3,6 +3,9 @@
|
|
|
#include <linux/sort.h>
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
|
+typedef bool (*ex_handler_t)(const struct exception_table_entry *,
|
|
|
+ struct pt_regs *, int);
|
|
|
+
|
|
|
static inline unsigned long
|
|
|
ex_insn_addr(const struct exception_table_entry *x)
|
|
|
{
|
|
@@ -13,11 +16,56 @@ ex_fixup_addr(const struct exception_table_entry *x)
|
|
|
{
|
|
|
return (unsigned long)&x->fixup + x->fixup;
|
|
|
}
|
|
|
+static inline ex_handler_t
|
|
|
+ex_fixup_handler(const struct exception_table_entry *x)
|
|
|
+{
|
|
|
+ return (ex_handler_t)((unsigned long)&x->handler + x->handler);
|
|
|
+}
|
|
|
|
|
|
-int fixup_exception(struct pt_regs *regs)
|
|
|
+bool ex_handler_default(const struct exception_table_entry *fixup,
|
|
|
+ struct pt_regs *regs, int trapnr)
|
|
|
{
|
|
|
- const struct exception_table_entry *fixup;
|
|
|
- unsigned long new_ip;
|
|
|
+ regs->ip = ex_fixup_addr(fixup);
|
|
|
+ return true;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(ex_handler_default);
|
|
|
+
|
|
|
+bool ex_handler_fault(const struct exception_table_entry *fixup,
|
|
|
+ struct pt_regs *regs, int trapnr)
|
|
|
+{
|
|
|
+ regs->ip = ex_fixup_addr(fixup);
|
|
|
+ regs->ax = trapnr;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(ex_handler_fault);
|
|
|
+
|
|
|
+bool ex_handler_ext(const struct exception_table_entry *fixup,
|
|
|
+ struct pt_regs *regs, int trapnr)
|
|
|
+{
|
|
|
+ /* Special hack for uaccess_err */
|
|
|
+ current_thread_info()->uaccess_err = 1;
|
|
|
+ regs->ip = ex_fixup_addr(fixup);
|
|
|
+ return true;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(ex_handler_ext);
|
|
|
+
|
|
|
+bool ex_has_fault_handler(unsigned long ip)
|
|
|
+{
|
|
|
+ const struct exception_table_entry *e;
|
|
|
+ ex_handler_t handler;
|
|
|
+
|
|
|
+ e = search_exception_tables(ip);
|
|
|
+ if (!e)
|
|
|
+ return false;
|
|
|
+ handler = ex_fixup_handler(e);
|
|
|
+
|
|
|
+ return handler == ex_handler_fault;
|
|
|
+}
|
|
|
+
|
|
|
+int fixup_exception(struct pt_regs *regs, int trapnr)
|
|
|
+{
|
|
|
+ const struct exception_table_entry *e;
|
|
|
+ ex_handler_t handler;
|
|
|
|
|
|
#ifdef CONFIG_PNPBIOS
|
|
|
if (unlikely(SEGMENT_IS_PNP_CODE(regs->cs))) {
|
|
@@ -33,42 +81,34 @@ int fixup_exception(struct pt_regs *regs)
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
- fixup = search_exception_tables(regs->ip);
|
|
|
- if (fixup) {
|
|
|
- new_ip = ex_fixup_addr(fixup);
|
|
|
-
|
|
|
- if (fixup->fixup - fixup->insn >= 0x7ffffff0 - 4) {
|
|
|
- /* Special hack for uaccess_err */
|
|
|
- current_thread_info()->uaccess_err = 1;
|
|
|
- new_ip -= 0x7ffffff0;
|
|
|
- }
|
|
|
- regs->ip = new_ip;
|
|
|
- return 1;
|
|
|
- }
|
|
|
+ e = search_exception_tables(regs->ip);
|
|
|
+ if (!e)
|
|
|
+ return 0;
|
|
|
|
|
|
- return 0;
|
|
|
+ handler = ex_fixup_handler(e);
|
|
|
+ return handler(e, regs, trapnr);
|
|
|
}
|
|
|
|
|
|
/* Restricted version used during very early boot */
|
|
|
int __init early_fixup_exception(unsigned long *ip)
|
|
|
{
|
|
|
- const struct exception_table_entry *fixup;
|
|
|
+ const struct exception_table_entry *e;
|
|
|
unsigned long new_ip;
|
|
|
+ ex_handler_t handler;
|
|
|
|
|
|
- fixup = search_exception_tables(*ip);
|
|
|
- if (fixup) {
|
|
|
- new_ip = ex_fixup_addr(fixup);
|
|
|
+ e = search_exception_tables(*ip);
|
|
|
+ if (!e)
|
|
|
+ return 0;
|
|
|
|
|
|
- if (fixup->fixup - fixup->insn >= 0x7ffffff0 - 4) {
|
|
|
- /* uaccess handling not supported during early boot */
|
|
|
- return 0;
|
|
|
- }
|
|
|
+ new_ip = ex_fixup_addr(e);
|
|
|
+ handler = ex_fixup_handler(e);
|
|
|
|
|
|
- *ip = new_ip;
|
|
|
- return 1;
|
|
|
- }
|
|
|
+ /* special handling not supported during early boot */
|
|
|
+ if (handler != ex_handler_default)
|
|
|
+ return 0;
|
|
|
|
|
|
- return 0;
|
|
|
+ *ip = new_ip;
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -133,6 +173,8 @@ void sort_extable(struct exception_table_entry *start,
|
|
|
i += 4;
|
|
|
p->fixup += i;
|
|
|
i += 4;
|
|
|
+ p->handler += i;
|
|
|
+ i += 4;
|
|
|
}
|
|
|
|
|
|
sort(start, finish - start, sizeof(struct exception_table_entry),
|
|
@@ -145,6 +187,8 @@ void sort_extable(struct exception_table_entry *start,
|
|
|
i += 4;
|
|
|
p->fixup -= i;
|
|
|
i += 4;
|
|
|
+ p->handler -= i;
|
|
|
+ i += 4;
|
|
|
}
|
|
|
}
|
|
|
|