diff options
| -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 |
