Commit 9a93848f authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Ingo Molnar

x86/debug: Implement __WARN() using UD0

By using "UD0" for WARN()s we remove the function call and its possible
__FILE__ and __LINE__ immediate arguments from the instruction stream.

Total image size will not change much, what we win in the instruction
stream we'll lose because of the __bug_table entries. Still, saves on
I$ footprint and the total image size does go down a bit.

      text    data       filename
  10702123    4530992    defconfig-build/vmlinux.orig
  10682460    4530992    defconfig-build/vmlinux.patched

(UML didn't seem to use GENERIC_BUG at all, so remove it)
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Cc: Arjan van de Ven <arjan@linux.intel.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Richard Weinberger <richard.weinberger@gmail.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-kernel@vger.kernel.org
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 1fa9d67a
...@@ -50,11 +50,6 @@ config GENERIC_CALIBRATE_DELAY ...@@ -50,11 +50,6 @@ config GENERIC_CALIBRATE_DELAY
bool bool
default y default y
config GENERIC_BUG
bool
default y
depends on BUG
config HZ config HZ
int int
default 100 default 100
......
#ifndef _ASM_X86_BUG_H #ifndef _ASM_X86_BUG_H
#define _ASM_X86_BUG_H #define _ASM_X86_BUG_H
#define HAVE_ARCH_BUG #include <linux/stringify.h>
#ifdef CONFIG_DEBUG_BUGVERBOSE /*
* Since some emulators terminate on UD2, we cannot use it for WARN.
* Since various instruction decoders disagree on the length of UD1,
* we cannot use it either. So use UD0 for WARN.
*
* (binutils knows about "ud1" but {en,de}codes it as 2 bytes, whereas
* our kernel decoder thinks it takes a ModRM byte, which seems consistent
* with various things like the Intel SDM instruction encoding rules)
*/
#define ASM_UD0 ".byte 0x0f, 0xff"
#define ASM_UD1 ".byte 0x0f, 0xb9" /* + ModRM */
#define ASM_UD2 ".byte 0x0f, 0x0b"
#define INSN_UD0 0xff0f
#define INSN_UD2 0x0b0f
#define LEN_UD0 2
#ifdef CONFIG_GENERIC_BUG
#define HAVE_ARCH_BUG
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
# define __BUG_C0 "2:\t.long 1b, %c0\n" # define __BUG_REL(val) ".long " __stringify(val)
#else #else
# define __BUG_C0 "2:\t.long 1b - 2b, %c0 - 2b\n" # define __BUG_REL(val) ".long " __stringify(val) " - 2b"
#endif #endif
#define BUG() \ #ifdef CONFIG_DEBUG_BUGVERBOSE
do { \
asm volatile("1:\tud2\n" \ #define _BUG_FLAGS(ins, flags) \
".pushsection __bug_table,\"a\"\n" \ do { \
__BUG_C0 \ asm volatile("1:\t" ins "\n" \
"\t.word %c1, 0\n" \ ".pushsection __bug_table,\"a\"\n" \
"\t.org 2b+%c2\n" \ "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n" \
".popsection" \ "\t" __BUG_REL(%c0) "\t# bug_entry::file\n" \
: : "i" (__FILE__), "i" (__LINE__), \ "\t.word %c1" "\t# bug_entry::line\n" \
"i" (sizeof(struct bug_entry))); \ "\t.word %c2" "\t# bug_entry::flags\n" \
unreachable(); \ "\t.org 2b+%c3\n" \
".popsection" \
: : "i" (__FILE__), "i" (__LINE__), \
"i" (flags), \
"i" (sizeof(struct bug_entry))); \
} while (0) } while (0)
#else #else /* !CONFIG_DEBUG_BUGVERBOSE */
#define _BUG_FLAGS(ins, flags) \
do { \
asm volatile("1:\t" ins "\n" \
".pushsection __bug_table,\"a\"\n" \
"2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n" \
"\t.word %c0" "\t# bug_entry::flags\n" \
"\t.org 2b+%c1\n" \
".popsection" \
: : "i" (flags), \
"i" (sizeof(struct bug_entry))); \
} while (0)
#endif /* CONFIG_DEBUG_BUGVERBOSE */
#define BUG() \ #define BUG() \
do { \ do { \
asm volatile("ud2"); \ _BUG_FLAGS(ASM_UD2, 0); \
unreachable(); \ unreachable(); \
} while (0) } while (0)
#endif
#define __WARN_TAINT(taint) _BUG_FLAGS(ASM_UD0, BUGFLAG_TAINT(taint))
#endif /* CONFIG_GENERIC_BUG */
#include <asm-generic/bug.h> #include <asm-generic/bug.h>
......
...@@ -289,9 +289,6 @@ void die(const char *str, struct pt_regs *regs, long err) ...@@ -289,9 +289,6 @@ void die(const char *str, struct pt_regs *regs, long err)
unsigned long flags = oops_begin(); unsigned long flags = oops_begin();
int sig = SIGSEGV; int sig = SIGSEGV;
if (!user_mode(regs))
report_bug(regs->ip, regs);
if (__die(str, regs, err)) if (__die(str, regs, err))
sig = 0; sig = 0;
oops_end(flags, regs, sig); oops_end(flags, regs, sig);
......
...@@ -162,15 +162,3 @@ void show_regs(struct pt_regs *regs) ...@@ -162,15 +162,3 @@ void show_regs(struct pt_regs *regs)
} }
pr_cont("\n"); pr_cont("\n");
} }
int is_valid_bugaddr(unsigned long ip)
{
unsigned short ud2;
if (ip < PAGE_OFFSET)
return 0;
if (probe_kernel_address((unsigned short *)ip, ud2))
return 0;
return ud2 == 0x0b0f;
}
...@@ -178,13 +178,3 @@ void show_regs(struct pt_regs *regs) ...@@ -178,13 +178,3 @@ void show_regs(struct pt_regs *regs)
} }
pr_cont("\n"); pr_cont("\n");
} }
int is_valid_bugaddr(unsigned long ip)
{
unsigned short ud2;
if (__copy_from_user(&ud2, (const void __user *) ip, sizeof(ud2)))
return 0;
return ud2 == 0x0b0f;
}
...@@ -169,6 +169,37 @@ void ist_end_non_atomic(void) ...@@ -169,6 +169,37 @@ void ist_end_non_atomic(void)
preempt_disable(); preempt_disable();
} }
int is_valid_bugaddr(unsigned long addr)
{
unsigned short ud;
if (addr < TASK_SIZE_MAX)
return 0;
if (probe_kernel_address((unsigned short *)addr, ud))
return 0;
return ud == INSN_UD0 || ud == INSN_UD2;
}
static int fixup_bug(struct pt_regs *regs, int trapnr)
{
if (trapnr != X86_TRAP_UD)
return 0;
switch (report_bug(regs->ip, regs)) {
case BUG_TRAP_TYPE_NONE:
case BUG_TRAP_TYPE_BUG:
break;
case BUG_TRAP_TYPE_WARN:
regs->ip += LEN_UD0;
return 1;
}
return 0;
}
static nokprobe_inline int static nokprobe_inline int
do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str, do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str,
struct pt_regs *regs, long error_code) struct pt_regs *regs, long error_code)
...@@ -187,12 +218,15 @@ do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str, ...@@ -187,12 +218,15 @@ do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str,
} }
if (!user_mode(regs)) { if (!user_mode(regs)) {
if (!fixup_exception(regs, trapnr)) { if (fixup_exception(regs, trapnr))
tsk->thread.error_code = error_code; return 0;
tsk->thread.trap_nr = trapnr;
die(str, regs, error_code); if (fixup_bug(regs, trapnr))
} return 0;
return 0;
tsk->thread.error_code = error_code;
tsk->thread.trap_nr = trapnr;
die(str, regs, error_code);
} }
return -1; return -1;
......
...@@ -8,7 +8,7 @@ else ...@@ -8,7 +8,7 @@ else
BITS := 64 BITS := 64
endif endif
obj-y = bug.o bugs_$(BITS).o delay.o fault.o ldt.o \ obj-y = bugs_$(BITS).o delay.o fault.o ldt.o \
ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \ ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \
stub_$(BITS).o stub_segv.o \ stub_$(BITS).o stub_segv.o \
sys_call_table_$(BITS).o sysrq_$(BITS).o tls_$(BITS).o \ sys_call_table_$(BITS).o sysrq_$(BITS).o tls_$(BITS).o \
......
/*
* Copyright (C) 2006 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL V2
*/
#include <linux/uaccess.h>
/*
* Mostly copied from i386/x86_86 - eliminated the eip < PAGE_OFFSET because
* that's not relevant in skas mode.
*/
int is_valid_bugaddr(unsigned long eip)
{
unsigned short ud2;
if (probe_kernel_address((unsigned short __user *)eip, ud2))
return 0;
return ud2 == 0x0b0f;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment