diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-04 22:19:16 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-04 22:19:16 -0400 |
commit | 831638568702f82dc291ad92bb0e5a4afdcc81be (patch) | |
tree | fd798ad41214b0acdf66b44d5d2df8f0569b5508 | |
parent | 0afe832e55a70f4bc5e725db400779b4f620290c (diff) | |
parent | 4dba072cd097f35fa8f77c49d909ada2b079a4c4 (diff) |
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
-rw-r--r-- | Documentation/admin-guide/kernel-parameters.txt | 5 | ||||
-rw-r--r-- | arch/x86/include/asm/stacktrace.h | 2 | ||||
-rw-r--r-- | arch/x86/kernel/dumpstack.c | 144 | ||||
-rw-r--r-- | arch/x86/kernel/process_32.c | 8 | ||||
-rw-r--r-- | arch/x86/mm/fault.c | 7 |
5 files changed, 80 insertions, 86 deletions
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 746b799e9ed8..9d699c85d8fe 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt | |||
@@ -587,11 +587,6 @@ | |||
587 | Sets the size of memory pool for coherent, atomic dma | 587 | Sets the size of memory pool for coherent, atomic dma |
588 | allocations, by default set to 256K. | 588 | allocations, by default set to 256K. |
589 | 589 | ||
590 | code_bytes [X86] How many bytes of object code to print | ||
591 | in an oops report. | ||
592 | Range: 0 - 8192 | ||
593 | Default: 64 | ||
594 | |||
595 | com20020= [HW,NET] ARCnet - COM20020 chipset | 590 | com20020= [HW,NET] ARCnet - COM20020 chipset |
596 | Format: | 591 | Format: |
597 | <io>[,<irq>[,<nodeID>[,<backplane>[,<ckp>[,<timeout>]]]]] | 592 | <io>[,<irq>[,<nodeID>[,<backplane>[,<ckp>[,<timeout>]]]]] |
diff --git a/arch/x86/include/asm/stacktrace.h b/arch/x86/include/asm/stacktrace.h index 133d9425fced..b6dc698f992a 100644 --- a/arch/x86/include/asm/stacktrace.h +++ b/arch/x86/include/asm/stacktrace.h | |||
@@ -111,4 +111,6 @@ static inline unsigned long caller_frame_pointer(void) | |||
111 | return (unsigned long)frame; | 111 | return (unsigned long)frame; |
112 | } | 112 | } |
113 | 113 | ||
114 | void show_opcodes(u8 *rip, const char *loglvl); | ||
115 | void show_ip(struct pt_regs *regs, const char *loglvl); | ||
114 | #endif /* _ASM_X86_STACKTRACE_H */ | 116 | #endif /* _ASM_X86_STACKTRACE_H */ |
diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index 18fa9d74c182..666a284116ac 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c | |||
@@ -22,11 +22,14 @@ | |||
22 | #include <asm/stacktrace.h> | 22 | #include <asm/stacktrace.h> |
23 | #include <asm/unwind.h> | 23 | #include <asm/unwind.h> |
24 | 24 | ||
25 | #define OPCODE_BUFSIZE 64 | ||
26 | |||
25 | int panic_on_unrecovered_nmi; | 27 | int panic_on_unrecovered_nmi; |
26 | int panic_on_io_nmi; | 28 | int panic_on_io_nmi; |
27 | static unsigned int code_bytes = 64; | ||
28 | static int die_counter; | 29 | static int die_counter; |
29 | 30 | ||
31 | static struct pt_regs exec_summary_regs; | ||
32 | |||
30 | bool in_task_stack(unsigned long *stack, struct task_struct *task, | 33 | bool in_task_stack(unsigned long *stack, struct task_struct *task, |
31 | struct stack_info *info) | 34 | struct stack_info *info) |
32 | { | 35 | { |
@@ -69,9 +72,62 @@ static void printk_stack_address(unsigned long address, int reliable, | |||
69 | printk("%s %s%pB\n", log_lvl, reliable ? "" : "? ", (void *)address); | 72 | printk("%s %s%pB\n", log_lvl, reliable ? "" : "? ", (void *)address); |
70 | } | 73 | } |
71 | 74 | ||
75 | /* | ||
76 | * There are a couple of reasons for the 2/3rd prologue, courtesy of Linus: | ||
77 | * | ||
78 | * In case where we don't have the exact kernel image (which, if we did, we can | ||
79 | * simply disassemble and navigate to the RIP), the purpose of the bigger | ||
80 | * prologue is to have more context and to be able to correlate the code from | ||
81 | * the different toolchains better. | ||
82 | * | ||
83 | * In addition, it helps in recreating the register allocation of the failing | ||
84 | * kernel and thus make sense of the register dump. | ||
85 | * | ||
86 | * What is more, the additional complication of a variable length insn arch like | ||
87 | * x86 warrants having longer byte sequence before rIP so that the disassembler | ||
88 | * can "sync" up properly and find instruction boundaries when decoding the | ||
89 | * opcode bytes. | ||
90 | * | ||
91 | * Thus, the 2/3rds prologue and 64 byte OPCODE_BUFSIZE is just a random | ||
92 | * guesstimate in attempt to achieve all of the above. | ||
93 | */ | ||
94 | void show_opcodes(u8 *rip, const char *loglvl) | ||
95 | { | ||
96 | unsigned int code_prologue = OPCODE_BUFSIZE * 2 / 3; | ||
97 | u8 opcodes[OPCODE_BUFSIZE]; | ||
98 | u8 *ip; | ||
99 | int i; | ||
100 | |||
101 | printk("%sCode: ", loglvl); | ||
102 | |||
103 | ip = (u8 *)rip - code_prologue; | ||
104 | if (probe_kernel_read(opcodes, ip, OPCODE_BUFSIZE)) { | ||
105 | pr_cont("Bad RIP value.\n"); | ||
106 | return; | ||
107 | } | ||
108 | |||
109 | for (i = 0; i < OPCODE_BUFSIZE; i++, ip++) { | ||
110 | if (ip == rip) | ||
111 | pr_cont("<%02x> ", opcodes[i]); | ||
112 | else | ||
113 | pr_cont("%02x ", opcodes[i]); | ||
114 | } | ||
115 | pr_cont("\n"); | ||
116 | } | ||
117 | |||
118 | void show_ip(struct pt_regs *regs, const char *loglvl) | ||
119 | { | ||
120 | #ifdef CONFIG_X86_32 | ||
121 | printk("%sEIP: %pS\n", loglvl, (void *)regs->ip); | ||
122 | #else | ||
123 | printk("%sRIP: %04x:%pS\n", loglvl, (int)regs->cs, (void *)regs->ip); | ||
124 | #endif | ||
125 | show_opcodes((u8 *)regs->ip, loglvl); | ||
126 | } | ||
127 | |||
72 | void show_iret_regs(struct pt_regs *regs) | 128 | void show_iret_regs(struct pt_regs *regs) |
73 | { | 129 | { |
74 | printk(KERN_DEFAULT "RIP: %04x:%pS\n", (int)regs->cs, (void *)regs->ip); | 130 | show_ip(regs, KERN_DEFAULT); |
75 | printk(KERN_DEFAULT "RSP: %04x:%016lx EFLAGS: %08lx", (int)regs->ss, | 131 | printk(KERN_DEFAULT "RSP: %04x:%016lx EFLAGS: %08lx", (int)regs->ss, |
76 | regs->sp, regs->flags); | 132 | regs->sp, regs->flags); |
77 | } | 133 | } |
@@ -267,7 +323,6 @@ unsigned long oops_begin(void) | |||
267 | bust_spinlocks(1); | 323 | bust_spinlocks(1); |
268 | return flags; | 324 | return flags; |
269 | } | 325 | } |
270 | EXPORT_SYMBOL_GPL(oops_begin); | ||
271 | NOKPROBE_SYMBOL(oops_begin); | 326 | NOKPROBE_SYMBOL(oops_begin); |
272 | 327 | ||
273 | void __noreturn rewind_stack_do_exit(int signr); | 328 | 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 | raw_local_irq_restore(flags); | 342 | raw_local_irq_restore(flags); |
288 | oops_exit(); | 343 | oops_exit(); |
289 | 344 | ||
345 | /* Executive summary in case the oops scrolled away */ | ||
346 | __show_regs(&exec_summary_regs, true); | ||
347 | |||
290 | if (!signr) | 348 | if (!signr) |
291 | return; | 349 | return; |
292 | if (in_interrupt()) | 350 | if (in_interrupt()) |
@@ -305,10 +363,10 @@ NOKPROBE_SYMBOL(oops_end); | |||
305 | 363 | ||
306 | int __die(const char *str, struct pt_regs *regs, long err) | 364 | int __die(const char *str, struct pt_regs *regs, long err) |
307 | { | 365 | { |
308 | #ifdef CONFIG_X86_32 | 366 | /* Save the regs of the first oops for the executive summary later. */ |
309 | unsigned short ss; | 367 | if (!die_counter) |
310 | unsigned long sp; | 368 | exec_summary_regs = *regs; |
311 | #endif | 369 | |
312 | printk(KERN_DEFAULT | 370 | printk(KERN_DEFAULT |
313 | "%s: %04lx [#%d]%s%s%s%s%s\n", str, err & 0xffff, ++die_counter, | 371 | "%s: %04lx [#%d]%s%s%s%s%s\n", str, err & 0xffff, ++die_counter, |
314 | IS_ENABLED(CONFIG_PREEMPT) ? " PREEMPT" : "", | 372 | IS_ENABLED(CONFIG_PREEMPT) ? " PREEMPT" : "", |
@@ -318,26 +376,13 @@ int __die(const char *str, struct pt_regs *regs, long err) | |||
318 | IS_ENABLED(CONFIG_PAGE_TABLE_ISOLATION) ? | 376 | IS_ENABLED(CONFIG_PAGE_TABLE_ISOLATION) ? |
319 | (boot_cpu_has(X86_FEATURE_PTI) ? " PTI" : " NOPTI") : ""); | 377 | (boot_cpu_has(X86_FEATURE_PTI) ? " PTI" : " NOPTI") : ""); |
320 | 378 | ||
379 | show_regs(regs); | ||
380 | print_modules(); | ||
381 | |||
321 | if (notify_die(DIE_OOPS, str, regs, err, | 382 | if (notify_die(DIE_OOPS, str, regs, err, |
322 | current->thread.trap_nr, SIGSEGV) == NOTIFY_STOP) | 383 | current->thread.trap_nr, SIGSEGV) == NOTIFY_STOP) |
323 | return 1; | 384 | return 1; |
324 | 385 | ||
325 | print_modules(); | ||
326 | show_regs(regs); | ||
327 | #ifdef CONFIG_X86_32 | ||
328 | if (user_mode(regs)) { | ||
329 | sp = regs->sp; | ||
330 | ss = regs->ss; | ||
331 | } else { | ||
332 | sp = kernel_stack_pointer(regs); | ||
333 | savesegment(ss, ss); | ||
334 | } | ||
335 | printk(KERN_EMERG "EIP: %pS SS:ESP: %04x:%08lx\n", | ||
336 | (void *)regs->ip, ss, sp); | ||
337 | #else | ||
338 | /* Executive summary in case the oops scrolled away */ | ||
339 | printk(KERN_ALERT "RIP: %pS RSP: %016lx\n", (void *)regs->ip, regs->sp); | ||
340 | #endif | ||
341 | return 0; | 386 | return 0; |
342 | } | 387 | } |
343 | NOKPROBE_SYMBOL(__die); | 388 | NOKPROBE_SYMBOL(__die); |
@@ -356,30 +401,9 @@ void die(const char *str, struct pt_regs *regs, long err) | |||
356 | oops_end(flags, regs, sig); | 401 | oops_end(flags, regs, sig); |
357 | } | 402 | } |
358 | 403 | ||
359 | static int __init code_bytes_setup(char *s) | ||
360 | { | ||
361 | ssize_t ret; | ||
362 | unsigned long val; | ||
363 | |||
364 | if (!s) | ||
365 | return -EINVAL; | ||
366 | |||
367 | ret = kstrtoul(s, 0, &val); | ||
368 | if (ret) | ||
369 | return ret; | ||
370 | |||
371 | code_bytes = val; | ||
372 | if (code_bytes > 8192) | ||
373 | code_bytes = 8192; | ||
374 | |||
375 | return 1; | ||
376 | } | ||
377 | __setup("code_bytes=", code_bytes_setup); | ||
378 | |||
379 | void show_regs(struct pt_regs *regs) | 404 | void show_regs(struct pt_regs *regs) |
380 | { | 405 | { |
381 | bool all = true; | 406 | bool all = true; |
382 | int i; | ||
383 | 407 | ||
384 | show_regs_print_info(KERN_DEFAULT); | 408 | show_regs_print_info(KERN_DEFAULT); |
385 | 409 | ||
@@ -389,36 +413,8 @@ void show_regs(struct pt_regs *regs) | |||
389 | __show_regs(regs, all); | 413 | __show_regs(regs, all); |
390 | 414 | ||
391 | /* | 415 | /* |
392 | * When in-kernel, we also print out the stack and code at the | 416 | * When in-kernel, we also print out the stack at the time of the fault.. |
393 | * time of the fault.. | ||
394 | */ | 417 | */ |
395 | if (!user_mode(regs)) { | 418 | if (!user_mode(regs)) |
396 | unsigned int code_prologue = code_bytes * 43 / 64; | ||
397 | unsigned int code_len = code_bytes; | ||
398 | unsigned char c; | ||
399 | u8 *ip; | ||
400 | |||
401 | show_trace_log_lvl(current, regs, NULL, KERN_DEFAULT); | 419 | show_trace_log_lvl(current, regs, NULL, KERN_DEFAULT); |
402 | |||
403 | printk(KERN_DEFAULT "Code: "); | ||
404 | |||
405 | ip = (u8 *)regs->ip - code_prologue; | ||
406 | if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) { | ||
407 | /* try starting at IP */ | ||
408 | ip = (u8 *)regs->ip; | ||
409 | code_len = code_len - code_prologue + 1; | ||
410 | } | ||
411 | for (i = 0; i < code_len; i++, ip++) { | ||
412 | if (ip < (u8 *)PAGE_OFFSET || | ||
413 | probe_kernel_address(ip, c)) { | ||
414 | pr_cont(" Bad RIP value."); | ||
415 | break; | ||
416 | } | ||
417 | if (ip == (u8 *)regs->ip) | ||
418 | pr_cont("<%02x> ", c); | ||
419 | else | ||
420 | pr_cont("%02x ", c); | ||
421 | } | ||
422 | } | ||
423 | pr_cont("\n"); | ||
424 | } | 420 | } |
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index 5224c6099184..0ae659de21eb 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c | |||
@@ -76,16 +76,14 @@ void __show_regs(struct pt_regs *regs, int all) | |||
76 | savesegment(gs, gs); | 76 | savesegment(gs, gs); |
77 | } | 77 | } |
78 | 78 | ||
79 | printk(KERN_DEFAULT "EIP: %pS\n", (void *)regs->ip); | 79 | show_ip(regs, KERN_DEFAULT); |
80 | printk(KERN_DEFAULT "EFLAGS: %08lx CPU: %d\n", regs->flags, | ||
81 | raw_smp_processor_id()); | ||
82 | 80 | ||
83 | printk(KERN_DEFAULT "EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n", | 81 | printk(KERN_DEFAULT "EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n", |
84 | regs->ax, regs->bx, regs->cx, regs->dx); | 82 | regs->ax, regs->bx, regs->cx, regs->dx); |
85 | printk(KERN_DEFAULT "ESI: %08lx EDI: %08lx EBP: %08lx ESP: %08lx\n", | 83 | printk(KERN_DEFAULT "ESI: %08lx EDI: %08lx EBP: %08lx ESP: %08lx\n", |
86 | regs->si, regs->di, regs->bp, sp); | 84 | regs->si, regs->di, regs->bp, sp); |
87 | printk(KERN_DEFAULT " DS: %04x ES: %04x FS: %04x GS: %04x SS: %04x\n", | 85 | printk(KERN_DEFAULT "DS: %04x ES: %04x FS: %04x GS: %04x SS: %04x EFLAGS: %08lx\n", |
88 | (u16)regs->ds, (u16)regs->es, (u16)regs->fs, gs, ss); | 86 | (u16)regs->ds, (u16)regs->es, (u16)regs->fs, gs, ss, regs->flags); |
89 | 87 | ||
90 | if (!all) | 88 | if (!all) |
91 | return; | 89 | return; |
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 0e634956efdf..9a84a0d08727 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c | |||
@@ -829,6 +829,8 @@ static inline void | |||
829 | show_signal_msg(struct pt_regs *regs, unsigned long error_code, | 829 | show_signal_msg(struct pt_regs *regs, unsigned long error_code, |
830 | unsigned long address, struct task_struct *tsk) | 830 | unsigned long address, struct task_struct *tsk) |
831 | { | 831 | { |
832 | const char *loglvl = task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG; | ||
833 | |||
832 | if (!unhandled_signal(tsk, SIGSEGV)) | 834 | if (!unhandled_signal(tsk, SIGSEGV)) |
833 | return; | 835 | return; |
834 | 836 | ||
@@ -836,13 +838,14 @@ show_signal_msg(struct pt_regs *regs, unsigned long error_code, | |||
836 | return; | 838 | return; |
837 | 839 | ||
838 | printk("%s%s[%d]: segfault at %lx ip %px sp %px error %lx", | 840 | printk("%s%s[%d]: segfault at %lx ip %px sp %px error %lx", |
839 | task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG, | 841 | loglvl, tsk->comm, task_pid_nr(tsk), address, |
840 | tsk->comm, task_pid_nr(tsk), address, | ||
841 | (void *)regs->ip, (void *)regs->sp, error_code); | 842 | (void *)regs->ip, (void *)regs->sp, error_code); |
842 | 843 | ||
843 | print_vma_addr(KERN_CONT " in ", regs->ip); | 844 | print_vma_addr(KERN_CONT " in ", regs->ip); |
844 | 845 | ||
845 | printk(KERN_CONT "\n"); | 846 | printk(KERN_CONT "\n"); |
847 | |||
848 | show_opcodes((u8 *)regs->ip, loglvl); | ||
846 | } | 849 | } |
847 | 850 | ||
848 | static void | 851 | static void |