]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
6fcbede3 AH |
2 | /* |
3 | * Copyright (C) 1991, 1992 Linus Torvalds | |
4 | * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs | |
5 | */ | |
b17b0153 | 6 | #include <linux/sched/debug.h> |
6fcbede3 AH |
7 | #include <linux/kallsyms.h> |
8 | #include <linux/kprobes.h> | |
9 | #include <linux/uaccess.h> | |
6fcbede3 AH |
10 | #include <linux/hardirq.h> |
11 | #include <linux/kdebug.h> | |
186f4360 | 12 | #include <linux/export.h> |
6fcbede3 AH |
13 | #include <linux/ptrace.h> |
14 | #include <linux/kexec.h> | |
b8030906 | 15 | #include <linux/sysfs.h> |
6fcbede3 AH |
16 | #include <linux/bug.h> |
17 | #include <linux/nmi.h> | |
18 | ||
afcd21da | 19 | #include <asm/cpu_entry_area.h> |
6fcbede3 AH |
20 | #include <asm/stacktrace.h> |
21 | ||
2a594d4c | 22 | static const char * const exception_stack_names[] = { |
8f34c5b5 TG |
23 | [ ESTACK_DF ] = "#DF", |
24 | [ ESTACK_NMI ] = "NMI", | |
2a594d4c TG |
25 | [ ESTACK_DB2 ] = "#DB2", |
26 | [ ESTACK_DB1 ] = "#DB1", | |
8f34c5b5 TG |
27 | [ ESTACK_DB ] = "#DB", |
28 | [ ESTACK_MCE ] = "#MC", | |
9c003907 | 29 | }; |
6fcbede3 | 30 | |
3d02a9c4 | 31 | const char *stack_type_name(enum stack_type type) |
0406ca6d | 32 | { |
2a594d4c | 33 | BUILD_BUG_ON(N_EXCEPTION_STACKS != 6); |
cb76c939 | 34 | |
3d02a9c4 JP |
35 | if (type == STACK_TYPE_IRQ) |
36 | return "IRQ"; | |
37 | ||
4fe2d8b1 DH |
38 | if (type == STACK_TYPE_ENTRY) { |
39 | /* | |
40 | * On 64-bit, we have a generic entry stack that we | |
41 | * use for all the kernel entry points, including | |
42 | * SYSENTER. | |
43 | */ | |
44 | return "ENTRY_TRAMPOLINE"; | |
45 | } | |
33a2f1a6 | 46 | |
3d02a9c4 JP |
47 | if (type >= STACK_TYPE_EXCEPTION && type <= STACK_TYPE_EXCEPTION_LAST) |
48 | return exception_stack_names[type - STACK_TYPE_EXCEPTION]; | |
49 | ||
50 | return NULL; | |
cb76c939 JP |
51 | } |
52 | ||
c450c8f5 TG |
53 | /** |
54 | * struct estack_pages - Page descriptor for exception stacks | |
55 | * @offs: Offset from the start of the exception stack area | |
56 | * @size: Size of the exception stack | |
57 | * @type: Type to store in the stack_info struct | |
58 | */ | |
59 | struct estack_pages { | |
60 | u32 offs; | |
61 | u16 size; | |
62 | u16 type; | |
afcd21da TG |
63 | }; |
64 | ||
c450c8f5 TG |
65 | #define EPAGERANGE(st) \ |
66 | [PFN_DOWN(CEA_ESTACK_OFFS(st)) ... \ | |
67 | PFN_DOWN(CEA_ESTACK_OFFS(st) + CEA_ESTACK_SIZE(st) - 1)] = { \ | |
68 | .offs = CEA_ESTACK_OFFS(st), \ | |
69 | .size = CEA_ESTACK_SIZE(st), \ | |
70 | .type = STACK_TYPE_EXCEPTION + ESTACK_ ##st, } | |
afcd21da | 71 | |
c450c8f5 TG |
72 | /* |
73 | * Array of exception stack page descriptors. If the stack is larger than | |
74 | * PAGE_SIZE, all pages covering a particular stack will have the same | |
75 | * info. The guard pages including the not mapped DB2 stack are zeroed | |
76 | * out. | |
77 | */ | |
78 | static const | |
79 | struct estack_pages estack_pages[CEA_ESTACK_PAGES] ____cacheline_aligned = { | |
80 | EPAGERANGE(DF), | |
81 | EPAGERANGE(NMI), | |
82 | EPAGERANGE(DB1), | |
83 | EPAGERANGE(DB), | |
84 | EPAGERANGE(MCE), | |
afcd21da TG |
85 | }; |
86 | ||
fcd709ef | 87 | static bool in_exception_stack(unsigned long *stack, struct stack_info *info) |
cb76c939 | 88 | { |
c450c8f5 TG |
89 | unsigned long begin, end, stk = (unsigned long)stack; |
90 | const struct estack_pages *ep; | |
cb76c939 | 91 | struct pt_regs *regs; |
afcd21da | 92 | unsigned int k; |
6fcbede3 | 93 | |
2a594d4c | 94 | BUILD_BUG_ON(N_EXCEPTION_STACKS != 6); |
9c003907 | 95 | |
c450c8f5 | 96 | begin = (unsigned long)__this_cpu_read(cea_exception_stacks); |
e361362b TG |
97 | /* |
98 | * Handle the case where stack trace is collected _before_ | |
99 | * cea_exception_stacks had been initialized. | |
100 | */ | |
101 | if (!begin) | |
102 | return false; | |
103 | ||
c450c8f5 TG |
104 | end = begin + sizeof(struct cea_exception_stacks); |
105 | /* Bail if @stack is outside the exception stack area. */ | |
106 | if (stk < begin || stk >= end) | |
107 | return false; | |
9c003907 | 108 | |
c450c8f5 TG |
109 | /* Calc page offset from start of exception stacks */ |
110 | k = (stk - begin) >> PAGE_SHIFT; | |
111 | /* Lookup the page descriptor */ | |
112 | ep = &estack_pages[k]; | |
113 | /* Guard page? */ | |
114 | if (!ep->size) | |
115 | return false; | |
cb76c939 | 116 | |
c450c8f5 TG |
117 | begin += (unsigned long)ep->offs; |
118 | end = begin + (unsigned long)ep->size; | |
119 | regs = (struct pt_regs *)end - 1; | |
9c003907 | 120 | |
c450c8f5 TG |
121 | info->type = ep->type; |
122 | info->begin = (unsigned long *)begin; | |
123 | info->end = (unsigned long *)end; | |
124 | info->next_sp = (unsigned long *)regs->sp; | |
125 | return true; | |
6fcbede3 AH |
126 | } |
127 | ||
cb76c939 | 128 | static bool in_irq_stack(unsigned long *stack, struct stack_info *info) |
af2d8289 | 129 | { |
758a2e31 | 130 | unsigned long *end = (unsigned long *)this_cpu_read(hardirq_stack_ptr); |
cb76c939 | 131 | unsigned long *begin = end - (IRQ_STACK_SIZE / sizeof(long)); |
af2d8289 | 132 | |
5fe599e0 JP |
133 | /* |
134 | * This is a software stack, so 'end' can be a valid stack pointer. | |
135 | * It just means the stack is empty. | |
136 | */ | |
fa332154 | 137 | if (stack < begin || stack >= end) |
cb76c939 | 138 | return false; |
2223f6f6 | 139 | |
cb76c939 JP |
140 | info->type = STACK_TYPE_IRQ; |
141 | info->begin = begin; | |
142 | info->end = end; | |
143 | ||
144 | /* | |
145 | * The next stack pointer is the first thing pushed by the entry code | |
146 | * after switching to the irq stack. | |
147 | */ | |
148 | info->next_sp = (unsigned long *)*(end - 1); | |
149 | ||
150 | return true; | |
151 | } | |
152 | ||
153 | int get_stack_info(unsigned long *stack, struct task_struct *task, | |
154 | struct stack_info *info, unsigned long *visit_mask) | |
2223f6f6 | 155 | { |
cb76c939 JP |
156 | if (!stack) |
157 | goto unknown; | |
158 | ||
159 | task = task ? : current; | |
2223f6f6 | 160 | |
cb76c939 | 161 | if (in_task_stack(stack, task, info)) |
fcd709ef | 162 | goto recursion_check; |
2223f6f6 | 163 | |
cb76c939 JP |
164 | if (task != current) |
165 | goto unknown; | |
2223f6f6 | 166 | |
fcd709ef JP |
167 | if (in_exception_stack(stack, info)) |
168 | goto recursion_check; | |
2223f6f6 | 169 | |
cb76c939 | 170 | if (in_irq_stack(stack, info)) |
fcd709ef JP |
171 | goto recursion_check; |
172 | ||
4fe2d8b1 | 173 | if (in_entry_stack(stack, info)) |
33a2f1a6 AL |
174 | goto recursion_check; |
175 | ||
fcd709ef JP |
176 | goto unknown; |
177 | ||
178 | recursion_check: | |
179 | /* | |
180 | * Make sure we don't iterate through any given stack more than once. | |
181 | * If it comes up a second time then there's something wrong going on: | |
182 | * just break out and report an unknown stack type. | |
183 | */ | |
184 | if (visit_mask) { | |
0d2b8579 | 185 | if (*visit_mask & (1UL << info->type)) { |
b08418b5 JP |
186 | if (task == current) |
187 | printk_deferred_once(KERN_WARNING "WARNING: stack recursion on stack type %d\n", info->type); | |
fcd709ef | 188 | goto unknown; |
0d2b8579 | 189 | } |
fcd709ef JP |
190 | *visit_mask |= 1UL << info->type; |
191 | } | |
2223f6f6 | 192 | |
cb76c939 | 193 | return 0; |
2223f6f6 | 194 | |
cb76c939 JP |
195 | unknown: |
196 | info->type = STACK_TYPE_UNKNOWN; | |
197 | return -EINVAL; | |
2223f6f6 | 198 | } |