diff options
Diffstat (limited to 'arch/x86/kernel/dumpstack.c')
-rw-r--r-- | arch/x86/kernel/dumpstack.c | 98 |
1 files changed, 79 insertions, 19 deletions
diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index f13b4c00a5de..afbecff161d1 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/nmi.h> | 18 | #include <linux/nmi.h> |
19 | #include <linux/sysfs.h> | 19 | #include <linux/sysfs.h> |
20 | 20 | ||
21 | #include <asm/cpu_entry_area.h> | ||
21 | #include <asm/stacktrace.h> | 22 | #include <asm/stacktrace.h> |
22 | #include <asm/unwind.h> | 23 | #include <asm/unwind.h> |
23 | 24 | ||
@@ -43,6 +44,24 @@ bool in_task_stack(unsigned long *stack, struct task_struct *task, | |||
43 | return true; | 44 | return true; |
44 | } | 45 | } |
45 | 46 | ||
47 | bool in_entry_stack(unsigned long *stack, struct stack_info *info) | ||
48 | { | ||
49 | struct entry_stack *ss = cpu_entry_stack(smp_processor_id()); | ||
50 | |||
51 | void *begin = ss; | ||
52 | void *end = ss + 1; | ||
53 | |||
54 | if ((void *)stack < begin || (void *)stack >= end) | ||
55 | return false; | ||
56 | |||
57 | info->type = STACK_TYPE_ENTRY; | ||
58 | info->begin = begin; | ||
59 | info->end = end; | ||
60 | info->next_sp = NULL; | ||
61 | |||
62 | return true; | ||
63 | } | ||
64 | |||
46 | static void printk_stack_address(unsigned long address, int reliable, | 65 | static void printk_stack_address(unsigned long address, int reliable, |
47 | char *log_lvl) | 66 | char *log_lvl) |
48 | { | 67 | { |
@@ -50,6 +69,39 @@ static void printk_stack_address(unsigned long address, int reliable, | |||
50 | printk("%s %s%pB\n", log_lvl, reliable ? "" : "? ", (void *)address); | 69 | printk("%s %s%pB\n", log_lvl, reliable ? "" : "? ", (void *)address); |
51 | } | 70 | } |
52 | 71 | ||
72 | void show_iret_regs(struct pt_regs *regs) | ||
73 | { | ||
74 | printk(KERN_DEFAULT "RIP: %04x:%pS\n", (int)regs->cs, (void *)regs->ip); | ||
75 | printk(KERN_DEFAULT "RSP: %04x:%016lx EFLAGS: %08lx", (int)regs->ss, | ||
76 | regs->sp, regs->flags); | ||
77 | } | ||
78 | |||
79 | static void show_regs_if_on_stack(struct stack_info *info, struct pt_regs *regs, | ||
80 | bool partial) | ||
81 | { | ||
82 | /* | ||
83 | * These on_stack() checks aren't strictly necessary: the unwind code | ||
84 | * has already validated the 'regs' pointer. The checks are done for | ||
85 | * ordering reasons: if the registers are on the next stack, we don't | ||
86 | * want to print them out yet. Otherwise they'll be shown as part of | ||
87 | * the wrong stack. Later, when show_trace_log_lvl() switches to the | ||
88 | * next stack, this function will be called again with the same regs so | ||
89 | * they can be printed in the right context. | ||
90 | */ | ||
91 | if (!partial && on_stack(info, regs, sizeof(*regs))) { | ||
92 | __show_regs(regs, 0); | ||
93 | |||
94 | } else if (partial && on_stack(info, (void *)regs + IRET_FRAME_OFFSET, | ||
95 | IRET_FRAME_SIZE)) { | ||
96 | /* | ||
97 | * When an interrupt or exception occurs in entry code, the | ||
98 | * full pt_regs might not have been saved yet. In that case | ||
99 | * just print the iret frame. | ||
100 | */ | ||
101 | show_iret_regs(regs); | ||
102 | } | ||
103 | } | ||
104 | |||
53 | void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, | 105 | void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, |
54 | unsigned long *stack, char *log_lvl) | 106 | unsigned long *stack, char *log_lvl) |
55 | { | 107 | { |
@@ -57,11 +109,13 @@ void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, | |||
57 | struct stack_info stack_info = {0}; | 109 | struct stack_info stack_info = {0}; |
58 | unsigned long visit_mask = 0; | 110 | unsigned long visit_mask = 0; |
59 | int graph_idx = 0; | 111 | int graph_idx = 0; |
112 | bool partial; | ||
60 | 113 | ||
61 | printk("%sCall Trace:\n", log_lvl); | 114 | printk("%sCall Trace:\n", log_lvl); |
62 | 115 | ||
63 | unwind_start(&state, task, regs, stack); | 116 | unwind_start(&state, task, regs, stack); |
64 | stack = stack ? : get_stack_pointer(task, regs); | 117 | stack = stack ? : get_stack_pointer(task, regs); |
118 | regs = unwind_get_entry_regs(&state, &partial); | ||
65 | 119 | ||
66 | /* | 120 | /* |
67 | * Iterate through the stacks, starting with the current stack pointer. | 121 | * Iterate through the stacks, starting with the current stack pointer. |
@@ -71,31 +125,35 @@ void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, | |||
71 | * - task stack | 125 | * - task stack |
72 | * - interrupt stack | 126 | * - interrupt stack |
73 | * - HW exception stacks (double fault, nmi, debug, mce) | 127 | * - HW exception stacks (double fault, nmi, debug, mce) |
128 | * - entry stack | ||
74 | * | 129 | * |
75 | * x86-32 can have up to three stacks: | 130 | * x86-32 can have up to four stacks: |
76 | * - task stack | 131 | * - task stack |
77 | * - softirq stack | 132 | * - softirq stack |
78 | * - hardirq stack | 133 | * - hardirq stack |
134 | * - entry stack | ||
79 | */ | 135 | */ |
80 | for (regs = NULL; stack; stack = PTR_ALIGN(stack_info.next_sp, sizeof(long))) { | 136 | for ( ; stack; stack = PTR_ALIGN(stack_info.next_sp, sizeof(long))) { |
81 | const char *stack_name; | 137 | const char *stack_name; |
82 | 138 | ||
83 | /* | 139 | if (get_stack_info(stack, task, &stack_info, &visit_mask)) { |
84 | * If we overflowed the task stack into a guard page, jump back | 140 | /* |
85 | * to the bottom of the usable stack. | 141 | * We weren't on a valid stack. It's possible that |
86 | */ | 142 | * we overflowed a valid stack into a guard page. |
87 | if (task_stack_page(task) - (void *)stack < PAGE_SIZE) | 143 | * See if the next page up is valid so that we can |
88 | stack = task_stack_page(task); | 144 | * generate some kind of backtrace if this happens. |
89 | 145 | */ | |
90 | if (get_stack_info(stack, task, &stack_info, &visit_mask)) | 146 | stack = (unsigned long *)PAGE_ALIGN((unsigned long)stack); |
91 | break; | 147 | if (get_stack_info(stack, task, &stack_info, &visit_mask)) |
148 | break; | ||
149 | } | ||
92 | 150 | ||
93 | stack_name = stack_type_name(stack_info.type); | 151 | stack_name = stack_type_name(stack_info.type); |
94 | if (stack_name) | 152 | if (stack_name) |
95 | printk("%s <%s>\n", log_lvl, stack_name); | 153 | printk("%s <%s>\n", log_lvl, stack_name); |
96 | 154 | ||
97 | if (regs && on_stack(&stack_info, regs, sizeof(*regs))) | 155 | if (regs) |
98 | __show_regs(regs, 0); | 156 | show_regs_if_on_stack(&stack_info, regs, partial); |
99 | 157 | ||
100 | /* | 158 | /* |
101 | * Scan the stack, printing any text addresses we find. At the | 159 | * Scan the stack, printing any text addresses we find. At the |
@@ -119,7 +177,7 @@ void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, | |||
119 | 177 | ||
120 | /* | 178 | /* |
121 | * Don't print regs->ip again if it was already printed | 179 | * Don't print regs->ip again if it was already printed |
122 | * by __show_regs() below. | 180 | * by show_regs_if_on_stack(). |
123 | */ | 181 | */ |
124 | if (regs && stack == ®s->ip) | 182 | if (regs && stack == ®s->ip) |
125 | goto next; | 183 | goto next; |
@@ -154,9 +212,9 @@ next: | |||
154 | unwind_next_frame(&state); | 212 | unwind_next_frame(&state); |
155 | 213 | ||
156 | /* if the frame has entry regs, print them */ | 214 | /* if the frame has entry regs, print them */ |
157 | regs = unwind_get_entry_regs(&state); | 215 | regs = unwind_get_entry_regs(&state, &partial); |
158 | if (regs && on_stack(&stack_info, regs, sizeof(*regs))) | 216 | if (regs) |
159 | __show_regs(regs, 0); | 217 | show_regs_if_on_stack(&stack_info, regs, partial); |
160 | } | 218 | } |
161 | 219 | ||
162 | if (stack_name) | 220 | if (stack_name) |
@@ -252,11 +310,13 @@ int __die(const char *str, struct pt_regs *regs, long err) | |||
252 | unsigned long sp; | 310 | unsigned long sp; |
253 | #endif | 311 | #endif |
254 | printk(KERN_DEFAULT | 312 | printk(KERN_DEFAULT |
255 | "%s: %04lx [#%d]%s%s%s%s\n", str, err & 0xffff, ++die_counter, | 313 | "%s: %04lx [#%d]%s%s%s%s%s\n", str, err & 0xffff, ++die_counter, |
256 | IS_ENABLED(CONFIG_PREEMPT) ? " PREEMPT" : "", | 314 | IS_ENABLED(CONFIG_PREEMPT) ? " PREEMPT" : "", |
257 | IS_ENABLED(CONFIG_SMP) ? " SMP" : "", | 315 | IS_ENABLED(CONFIG_SMP) ? " SMP" : "", |
258 | debug_pagealloc_enabled() ? " DEBUG_PAGEALLOC" : "", | 316 | debug_pagealloc_enabled() ? " DEBUG_PAGEALLOC" : "", |
259 | IS_ENABLED(CONFIG_KASAN) ? " KASAN" : ""); | 317 | IS_ENABLED(CONFIG_KASAN) ? " KASAN" : "", |
318 | IS_ENABLED(CONFIG_PAGE_TABLE_ISOLATION) ? | ||
319 | (boot_cpu_has(X86_FEATURE_PTI) ? " PTI" : " NOPTI") : ""); | ||
260 | 320 | ||
261 | if (notify_die(DIE_OOPS, str, regs, err, | 321 | if (notify_die(DIE_OOPS, str, regs, err, |
262 | current->thread.trap_nr, SIGSEGV) == NOTIFY_STOP) | 322 | current->thread.trap_nr, SIGSEGV) == NOTIFY_STOP) |