Commit 83163856 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'x86-debug-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 debug updates from Ingo Molnar:
 "This contains the x86 oops code printing reorganization and cleanups
  from Borislav Betkov, with a particular focus in enhancing opcode
  dumping all around"

* 'x86-debug-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/dumpstack: Explain the reasoning for the prologue and buffer size
  x86/dumpstack: Save first regs set for the executive summary
  x86/dumpstack: Add a show_ip() function
  x86/fault: Dump user opcode bytes on fatal faults
  x86/dumpstack: Add loglevel argument to show_opcodes()
  x86/dumpstack: Improve opcodes dumping in the code section
  x86/dumpstack: Carve out code-dumping into a function
  x86/dumpstack: Unexport oops_begin()
  x86/dumpstack: Remove code_bytes
parents 0afe832e 4dba072c
...@@ -587,11 +587,6 @@ ...@@ -587,11 +587,6 @@
Sets the size of memory pool for coherent, atomic dma Sets the size of memory pool for coherent, atomic dma
allocations, by default set to 256K. allocations, by default set to 256K.
code_bytes [X86] How many bytes of object code to print
in an oops report.
Range: 0 - 8192
Default: 64
com20020= [HW,NET] ARCnet - COM20020 chipset com20020= [HW,NET] ARCnet - COM20020 chipset
Format: Format:
<io>[,<irq>[,<nodeID>[,<backplane>[,<ckp>[,<timeout>]]]]] <io>[,<irq>[,<nodeID>[,<backplane>[,<ckp>[,<timeout>]]]]]
......
...@@ -111,4 +111,6 @@ static inline unsigned long caller_frame_pointer(void) ...@@ -111,4 +111,6 @@ static inline unsigned long caller_frame_pointer(void)
return (unsigned long)frame; return (unsigned long)frame;
} }
void show_opcodes(u8 *rip, const char *loglvl);
void show_ip(struct pt_regs *regs, const char *loglvl);
#endif /* _ASM_X86_STACKTRACE_H */ #endif /* _ASM_X86_STACKTRACE_H */
...@@ -22,11 +22,14 @@ ...@@ -22,11 +22,14 @@
#include <asm/stacktrace.h> #include <asm/stacktrace.h>
#include <asm/unwind.h> #include <asm/unwind.h>
#define OPCODE_BUFSIZE 64
int panic_on_unrecovered_nmi; int panic_on_unrecovered_nmi;
int panic_on_io_nmi; int panic_on_io_nmi;
static unsigned int code_bytes = 64;
static int die_counter; static int die_counter;
static struct pt_regs exec_summary_regs;
bool in_task_stack(unsigned long *stack, struct task_struct *task, bool in_task_stack(unsigned long *stack, struct task_struct *task,
struct stack_info *info) struct stack_info *info)
{ {
...@@ -69,9 +72,62 @@ static void printk_stack_address(unsigned long address, int reliable, ...@@ -69,9 +72,62 @@ static void printk_stack_address(unsigned long address, int reliable,
printk("%s %s%pB\n", log_lvl, reliable ? "" : "? ", (void *)address); printk("%s %s%pB\n", log_lvl, reliable ? "" : "? ", (void *)address);
} }
/*
* There are a couple of reasons for the 2/3rd prologue, courtesy of Linus:
*
* In case where we don't have the exact kernel image (which, if we did, we can
* simply disassemble and navigate to the RIP), the purpose of the bigger
* prologue is to have more context and to be able to correlate the code from
* the different toolchains better.
*
* In addition, it helps in recreating the register allocation of the failing
* kernel and thus make sense of the register dump.
*
* What is more, the additional complication of a variable length insn arch like
* x86 warrants having longer byte sequence before rIP so that the disassembler
* can "sync" up properly and find instruction boundaries when decoding the
* opcode bytes.
*
* Thus, the 2/3rds prologue and 64 byte OPCODE_BUFSIZE is just a random
* guesstimate in attempt to achieve all of the above.
*/
void show_opcodes(u8 *rip, const char *loglvl)
{
unsigned int code_prologue = OPCODE_BUFSIZE * 2 / 3;
u8 opcodes[OPCODE_BUFSIZE];
u8 *ip;
int i;
printk("%sCode: ", loglvl);
ip = (u8 *)rip - code_prologue;
if (probe_kernel_read(opcodes, ip, OPCODE_BUFSIZE)) {
pr_cont("Bad RIP value.\n");
return;
}
for (i = 0; i < OPCODE_BUFSIZE; i++, ip++) {
if (ip == rip)
pr_cont("<%02x> ", opcodes[i]);
else
pr_cont("%02x ", opcodes[i]);
}
pr_cont("\n");
}
void show_ip(struct pt_regs *regs, const char *loglvl)
{
#ifdef CONFIG_X86_32
printk("%sEIP: %pS\n", loglvl, (void *)regs->ip);
#else
printk("%sRIP: %04x:%pS\n", loglvl, (int)regs->cs, (void *)regs->ip);
#endif
show_opcodes((u8 *)regs->ip, loglvl);
}
void show_iret_regs(struct pt_regs *regs) void show_iret_regs(struct pt_regs *regs)
{ {
printk(KERN_DEFAULT "RIP: %04x:%pS\n", (int)regs->cs, (void *)regs->ip); show_ip(regs, KERN_DEFAULT);
printk(KERN_DEFAULT "RSP: %04x:%016lx EFLAGS: %08lx", (int)regs->ss, printk(KERN_DEFAULT "RSP: %04x:%016lx EFLAGS: %08lx", (int)regs->ss,
regs->sp, regs->flags); regs->sp, regs->flags);
} }
...@@ -267,7 +323,6 @@ unsigned long oops_begin(void) ...@@ -267,7 +323,6 @@ unsigned long oops_begin(void)
bust_spinlocks(1); bust_spinlocks(1);
return flags; return flags;
} }
EXPORT_SYMBOL_GPL(oops_begin);
NOKPROBE_SYMBOL(oops_begin); NOKPROBE_SYMBOL(oops_begin);
void __noreturn rewind_stack_do_exit(int signr); void __noreturn rewind_stack_do_exit(int signr);
...@@ -287,6 +342,9 @@ void oops_end(unsigned long flags, struct pt_regs *regs, int signr) ...@@ -287,6 +342,9 @@ void oops_end(unsigned long flags, struct pt_regs *regs, int signr)
raw_local_irq_restore(flags); raw_local_irq_restore(flags);
oops_exit(); oops_exit();
/* Executive summary in case the oops scrolled away */
__show_regs(&exec_summary_regs, true);
if (!signr) if (!signr)
return; return;
if (in_interrupt()) if (in_interrupt())
...@@ -305,10 +363,10 @@ NOKPROBE_SYMBOL(oops_end); ...@@ -305,10 +363,10 @@ NOKPROBE_SYMBOL(oops_end);
int __die(const char *str, struct pt_regs *regs, long err) int __die(const char *str, struct pt_regs *regs, long err)
{ {
#ifdef CONFIG_X86_32 /* Save the regs of the first oops for the executive summary later. */
unsigned short ss; if (!die_counter)
unsigned long sp; exec_summary_regs = *regs;
#endif
printk(KERN_DEFAULT printk(KERN_DEFAULT
"%s: %04lx [#%d]%s%s%s%s%s\n", str, err & 0xffff, ++die_counter, "%s: %04lx [#%d]%s%s%s%s%s\n", str, err & 0xffff, ++die_counter,
IS_ENABLED(CONFIG_PREEMPT) ? " PREEMPT" : "", IS_ENABLED(CONFIG_PREEMPT) ? " PREEMPT" : "",
...@@ -318,26 +376,13 @@ int __die(const char *str, struct pt_regs *regs, long err) ...@@ -318,26 +376,13 @@ int __die(const char *str, struct pt_regs *regs, long err)
IS_ENABLED(CONFIG_PAGE_TABLE_ISOLATION) ? IS_ENABLED(CONFIG_PAGE_TABLE_ISOLATION) ?
(boot_cpu_has(X86_FEATURE_PTI) ? " PTI" : " NOPTI") : ""); (boot_cpu_has(X86_FEATURE_PTI) ? " PTI" : " NOPTI") : "");
show_regs(regs);
print_modules();
if (notify_die(DIE_OOPS, str, regs, err, if (notify_die(DIE_OOPS, str, regs, err,
current->thread.trap_nr, SIGSEGV) == NOTIFY_STOP) current->thread.trap_nr, SIGSEGV) == NOTIFY_STOP)
return 1; return 1;
print_modules();
show_regs(regs);
#ifdef CONFIG_X86_32
if (user_mode(regs)) {
sp = regs->sp;
ss = regs->ss;
} else {
sp = kernel_stack_pointer(regs);
savesegment(ss, ss);
}
printk(KERN_EMERG "EIP: %pS SS:ESP: %04x:%08lx\n",
(void *)regs->ip, ss, sp);
#else
/* Executive summary in case the oops scrolled away */
printk(KERN_ALERT "RIP: %pS RSP: %016lx\n", (void *)regs->ip, regs->sp);
#endif
return 0; return 0;
} }
NOKPROBE_SYMBOL(__die); NOKPROBE_SYMBOL(__die);
...@@ -356,30 +401,9 @@ void die(const char *str, struct pt_regs *regs, long err) ...@@ -356,30 +401,9 @@ void die(const char *str, struct pt_regs *regs, long err)
oops_end(flags, regs, sig); oops_end(flags, regs, sig);
} }
static int __init code_bytes_setup(char *s)
{
ssize_t ret;
unsigned long val;
if (!s)
return -EINVAL;
ret = kstrtoul(s, 0, &val);
if (ret)
return ret;
code_bytes = val;
if (code_bytes > 8192)
code_bytes = 8192;
return 1;
}
__setup("code_bytes=", code_bytes_setup);
void show_regs(struct pt_regs *regs) void show_regs(struct pt_regs *regs)
{ {
bool all = true; bool all = true;
int i;
show_regs_print_info(KERN_DEFAULT); show_regs_print_info(KERN_DEFAULT);
...@@ -389,36 +413,8 @@ void show_regs(struct pt_regs *regs) ...@@ -389,36 +413,8 @@ void show_regs(struct pt_regs *regs)
__show_regs(regs, all); __show_regs(regs, all);
/* /*
* When in-kernel, we also print out the stack and code at the * When in-kernel, we also print out the stack at the time of the fault..
* time of the fault..
*/ */
if (!user_mode(regs)) { if (!user_mode(regs))
unsigned int code_prologue = code_bytes * 43 / 64;
unsigned int code_len = code_bytes;
unsigned char c;
u8 *ip;
show_trace_log_lvl(current, regs, NULL, KERN_DEFAULT); show_trace_log_lvl(current, regs, NULL, KERN_DEFAULT);
printk(KERN_DEFAULT "Code: ");
ip = (u8 *)regs->ip - code_prologue;
if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) {
/* try starting at IP */
ip = (u8 *)regs->ip;
code_len = code_len - code_prologue + 1;
}
for (i = 0; i < code_len; i++, ip++) {
if (ip < (u8 *)PAGE_OFFSET ||
probe_kernel_address(ip, c)) {
pr_cont(" Bad RIP value.");
break;
}
if (ip == (u8 *)regs->ip)
pr_cont("<%02x> ", c);
else
pr_cont("%02x ", c);
}
}
pr_cont("\n");
} }
...@@ -76,16 +76,14 @@ void __show_regs(struct pt_regs *regs, int all) ...@@ -76,16 +76,14 @@ void __show_regs(struct pt_regs *regs, int all)
savesegment(gs, gs); savesegment(gs, gs);
} }
printk(KERN_DEFAULT "EIP: %pS\n", (void *)regs->ip); show_ip(regs, KERN_DEFAULT);
printk(KERN_DEFAULT "EFLAGS: %08lx CPU: %d\n", regs->flags,
raw_smp_processor_id());
printk(KERN_DEFAULT "EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n", printk(KERN_DEFAULT "EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
regs->ax, regs->bx, regs->cx, regs->dx); regs->ax, regs->bx, regs->cx, regs->dx);
printk(KERN_DEFAULT "ESI: %08lx EDI: %08lx EBP: %08lx ESP: %08lx\n", printk(KERN_DEFAULT "ESI: %08lx EDI: %08lx EBP: %08lx ESP: %08lx\n",
regs->si, regs->di, regs->bp, sp); regs->si, regs->di, regs->bp, sp);
printk(KERN_DEFAULT " DS: %04x ES: %04x FS: %04x GS: %04x SS: %04x\n", printk(KERN_DEFAULT "DS: %04x ES: %04x FS: %04x GS: %04x SS: %04x EFLAGS: %08lx\n",
(u16)regs->ds, (u16)regs->es, (u16)regs->fs, gs, ss); (u16)regs->ds, (u16)regs->es, (u16)regs->fs, gs, ss, regs->flags);
if (!all) if (!all)
return; return;
......
...@@ -829,6 +829,8 @@ static inline void ...@@ -829,6 +829,8 @@ static inline void
show_signal_msg(struct pt_regs *regs, unsigned long error_code, show_signal_msg(struct pt_regs *regs, unsigned long error_code,
unsigned long address, struct task_struct *tsk) unsigned long address, struct task_struct *tsk)
{ {
const char *loglvl = task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG;
if (!unhandled_signal(tsk, SIGSEGV)) if (!unhandled_signal(tsk, SIGSEGV))
return; return;
...@@ -836,13 +838,14 @@ show_signal_msg(struct pt_regs *regs, unsigned long error_code, ...@@ -836,13 +838,14 @@ show_signal_msg(struct pt_regs *regs, unsigned long error_code,
return; return;
printk("%s%s[%d]: segfault at %lx ip %px sp %px error %lx", printk("%s%s[%d]: segfault at %lx ip %px sp %px error %lx",
task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG, loglvl, tsk->comm, task_pid_nr(tsk), address,
tsk->comm, task_pid_nr(tsk), address,
(void *)regs->ip, (void *)regs->sp, error_code); (void *)regs->ip, (void *)regs->sp, error_code);
print_vma_addr(KERN_CONT " in ", regs->ip); print_vma_addr(KERN_CONT " in ", regs->ip);
printk(KERN_CONT "\n"); printk(KERN_CONT "\n");
show_opcodes((u8 *)regs->ip, loglvl);
} }
static void static void
......
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