[PATCH v6 2/4] x86: Cleanup and add a new exception class

From: Tony Luck
Date: Sun Jan 03 2016 - 20:26:42 EST


Make per-class functions for exception table fixup. Add #defines
and general prettiness to make it clear how all the parts tie
together.

Add a new class that fills %rax with the fault number of the exception.

Signed-off-by: Tony Luck <tony.luck@xxxxxxxxx>
---
arch/x86/include/asm/asm.h | 24 ++++++++++-----
arch/x86/include/asm/uaccess.h | 17 ++++++++---
arch/x86/kernel/kprobes/core.c | 2 +-
arch/x86/kernel/traps.c | 6 ++--
arch/x86/mm/extable.c | 67 +++++++++++++++++++++++++++---------------
arch/x86/mm/fault.c | 2 +-
6 files changed, 79 insertions(+), 39 deletions(-)

diff --git a/arch/x86/include/asm/asm.h b/arch/x86/include/asm/asm.h
index b64121ffb2da..1888278d0559 100644
--- a/arch/x86/include/asm/asm.h
+++ b/arch/x86/include/asm/asm.h
@@ -44,6 +44,7 @@

/* Exception table entry */

+#define _EXTABLE_BIAS 0x20000000
/*
* An exception table entry is 64 bits. The first 32 bits are the offset
* from that entry to the potentially faulting instruction. sortextable
@@ -54,26 +55,35 @@
* address. All of these are generated by relocations, so we can only
* rely on addition. We therefore emit:
*
- * (target - here) + (class) + 0x20000000
+ * (target - here) + (class) + _EXTABLE_BIAS
*
* This has the property that the two high bits are the class and the
* rest is easy to decode.
*/

-/* There are two bits of extable entry class, added to a signed offset. */
-#define _EXTABLE_CLASS_DEFAULT 0 /* standard uaccess fixup */
-#define _EXTABLE_CLASS_EX 0x80000000 /* uaccess + set uaccess_err */
+/*
+ * There are two bits of extable entry class giving four classes
+ */
+#define EXTABLE_CLASS_DEFAULT 0 /* standard uaccess fixup */
+#define EXTABLE_CLASS_FAULT 1 /* provide fault number as well as fixup */
+#define EXTABLE_CLASS_EX 2 /* uaccess + set uaccess_err */
+#define EXTABLE_CLASS_UNUSED 3 /* available for something else */

/*
- * The biases are the class constants + 0x20000000, as signed integers.
+ * The biases are the class constants + _EXTABLE_BIAS, as signed integers.
* This can't use ordinary arithmetic -- the assembler isn't that smart.
*/
-#define _EXTABLE_BIAS_DEFAULT 0x20000000
-#define _EXTABLE_BIAS_EX 0x20000000 - 0x80000000
+#define _EXTABLE_BIAS_DEFAULT _EXTABLE_BIAS
+#define _EXTABLE_BIAS_FAULT _EXTABLE_BIAS + 0x40000000
+#define _EXTABLE_BIAS_EX _EXTABLE_BIAS - 0x80000000
+#define _EXTABLE_BIAS_UNUSED _EXTABLE_BIAS - 0x40000000

#define _ASM_EXTABLE(from,to) \
_ASM_EXTABLE_CLASS(from, to, _EXTABLE_BIAS_DEFAULT)

+#define _ASM_EXTABLE_FAULT(from,to) \
+ _ASM_EXTABLE_CLASS(from, to, _EXTABLE_BIAS_FAULT)
+
#define _ASM_EXTABLE_EX(from,to) \
_ASM_EXTABLE_CLASS(from, to, _EXTABLE_BIAS_EX)

diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index a8df874f3e88..b023300cd6f0 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -93,9 +93,12 @@ static inline bool __chk_range_not_ok(unsigned long addr, unsigned long size, un
* The exception table consists of pairs of addresses relative to the
* exception table enty itself: the first is the address of an
* instruction that is allowed to fault, and the second is the address
- * at which the program should continue. No registers are modified,
- * so it is entirely up to the continuation code to figure out what to
- * do.
+ * at which the program should continue. The exception table is linked
+ * soon after the fixup section, so we don't need a full 32-bit offset
+ * for the fixup. We steal the two upper bits so we can tag exception
+ * table entries with different classes. In the default class no registers
+ * are modified, so it is entirely up to the continuation code to figure
+ * out what to do.
*
* All the routines below use bits of fixup code that are out of line
* with the main instruction path. This means when everything is well,
@@ -110,7 +113,13 @@ struct exception_table_entry {
#define ARCH_HAS_SORT_EXTABLE
#define ARCH_HAS_SEARCH_EXTABLE

-extern int fixup_exception(struct pt_regs *regs);
+static inline unsigned int
+ex_class(const struct exception_table_entry *x)
+{
+ return (u32)x->fixup >> 30;
+}
+
+extern int fixup_exception(struct pt_regs *regs, int trapnr);
extern int early_fixup_exception(unsigned long *ip);

/*
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index 1deffe6cc873..0f05deeff5ce 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -988,7 +988,7 @@ int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
* In case the user-specified fault handler returned
* zero, try to fix up.
*/
- if (fixup_exception(regs))
+ if (fixup_exception(regs, trapnr))
return 1;

/*
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 346eec73f7db..df25081e5970 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -199,7 +199,7 @@ do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str,
}

if (!user_mode(regs)) {
- if (!fixup_exception(regs)) {
+ if (!fixup_exception(regs, trapnr)) {
tsk->thread.error_code = error_code;
tsk->thread.trap_nr = trapnr;
die(str, regs, error_code);
@@ -453,7 +453,7 @@ do_general_protection(struct pt_regs *regs, long error_code)

tsk = current;
if (!user_mode(regs)) {
- if (fixup_exception(regs))
+ if (fixup_exception(regs, X86_TRAP_GP))
return;

tsk->thread.error_code = error_code;
@@ -699,7 +699,7 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr)
conditional_sti(regs);

if (!user_mode(regs)) {
- if (!fixup_exception(regs)) {
+ if (!fixup_exception(regs, X86_TRAP_DE)) {
task->thread.error_code = error_code;
task->thread.trap_nr = trapnr;
die(str, regs, error_code);
diff --git a/arch/x86/mm/extable.c b/arch/x86/mm/extable.c
index 95e2ede71206..7a592ec193d5 100644
--- a/arch/x86/mm/extable.c
+++ b/arch/x86/mm/extable.c
@@ -8,24 +8,54 @@ ex_insn_addr(const struct exception_table_entry *x)
{
return (unsigned long)&x->insn + x->insn;
}
-static inline unsigned int
-ex_class(const struct exception_table_entry *x)
-{
- return (unsigned int)x->fixup & 0xC0000000;
-}
-
static inline unsigned long
ex_fixup_addr(const struct exception_table_entry *x)
{
- long offset = (long)((u32)x->fixup & 0x3fffffff) - (long)0x20000000;
+ long offset = (long)((u32)x->fixup & 0x3fffffff) - (long)_EXTABLE_BIAS;
return (unsigned long)&x->fixup + offset;
}

-int fixup_exception(struct pt_regs *regs)
+/* Fixup functions for each exception class */
+static int fix_class_default(const struct exception_table_entry *fixup,
+ struct pt_regs *regs, int trapnr)
+{
+ regs->ip = ex_fixup_addr(fixup);
+ return 1;
+}
+static int fix_class_fault(const struct exception_table_entry *fixup,
+ struct pt_regs *regs, int trapnr)
+{
+ regs->ip = ex_fixup_addr(fixup);
+ regs->ax = trapnr;
+ return 1;
+}
+static int fix_class_ex(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 1;
+}
+static int fix_class_unused(const struct exception_table_entry *fixup,
+ struct pt_regs *regs, int trapnr)
+{
+ /* can't happen unless exception table was corrupted */
+ BUG_ON(1);
+ return 0;
+}
+
+static int (*allclasses[])(const struct exception_table_entry *,
+ struct pt_regs *, int) = {
+ [EXTABLE_CLASS_DEFAULT] = fix_class_default,
+ [EXTABLE_CLASS_FAULT] = fix_class_fault,
+ [EXTABLE_CLASS_EX] = fix_class_ex,
+ [EXTABLE_CLASS_UNUSED] = fix_class_unused
+};
+
+int fixup_exception(struct pt_regs *regs, int trapnr)
{
const struct exception_table_entry *fixup;
- unsigned long new_ip;
- unsigned int class;

#ifdef CONFIG_PNPBIOS
if (unlikely(SEGMENT_IS_PNP_CODE(regs->cs))) {
@@ -42,17 +72,8 @@ int fixup_exception(struct pt_regs *regs)
#endif

fixup = search_exception_tables(regs->ip);
- if (fixup) {
- class = ex_class(fixup);
- new_ip = ex_fixup_addr(fixup);
-
- if (class == _EXTABLE_CLASS_EX) {
- /* Special hack for uaccess_err */
- current_thread_info()->uaccess_err = 1;
- }
- regs->ip = new_ip;
- return 1;
- }
+ if (fixup)
+ return allclasses[ex_class(fixup)](fixup, regs, trapnr);

return 0;
}
@@ -64,8 +85,8 @@ int __init early_fixup_exception(unsigned long *ip)

fixup = search_exception_tables(*ip);
if (fixup) {
- if (ex_class(fixup) == _EXTABLE_CLASS_EX) {
- /* uaccess handling not supported during early boot */
+ if (ex_class(fixup)) {
+ /* special handling not supported during early boot */
return 0;
}

diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index eef44d9a3f77..495946c3f9dd 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -656,7 +656,7 @@ no_context(struct pt_regs *regs, unsigned long error_code,
int sig;

/* Are we prepared to handle this kernel fault? */
- if (fixup_exception(regs)) {
+ if (fixup_exception(regs, X86_TRAP_PF)) {
/*
* Any interrupt that takes a fault gets the fixup. This makes
* the below recursive fault logic only apply to a faults from
--
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/