xref: /openbmc/linux/arch/x86/kernel/dumpstack_64.c (revision 02772fb9)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
26fcbede3SAlexander van Heukelum /*
36fcbede3SAlexander van Heukelum  *  Copyright (C) 1991, 1992  Linus Torvalds
46fcbede3SAlexander van Heukelum  *  Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs
56fcbede3SAlexander van Heukelum  */
6b17b0153SIngo Molnar #include <linux/sched/debug.h>
76fcbede3SAlexander van Heukelum #include <linux/kallsyms.h>
86fcbede3SAlexander van Heukelum #include <linux/kprobes.h>
96fcbede3SAlexander van Heukelum #include <linux/uaccess.h>
106fcbede3SAlexander van Heukelum #include <linux/hardirq.h>
116fcbede3SAlexander van Heukelum #include <linux/kdebug.h>
12186f4360SPaul Gortmaker #include <linux/export.h>
136fcbede3SAlexander van Heukelum #include <linux/ptrace.h>
146fcbede3SAlexander van Heukelum #include <linux/kexec.h>
15b8030906SIngo Molnar #include <linux/sysfs.h>
166fcbede3SAlexander van Heukelum #include <linux/bug.h>
176fcbede3SAlexander van Heukelum #include <linux/nmi.h>
186fcbede3SAlexander van Heukelum 
19afcd21daSThomas Gleixner #include <asm/cpu_entry_area.h>
206fcbede3SAlexander van Heukelum #include <asm/stacktrace.h>
216fcbede3SAlexander van Heukelum 
222a594d4cSThomas Gleixner static const char * const exception_stack_names[] = {
238f34c5b5SThomas Gleixner 		[ ESTACK_DF	]	= "#DF",
248f34c5b5SThomas Gleixner 		[ ESTACK_NMI	]	= "NMI",
258f34c5b5SThomas Gleixner 		[ ESTACK_DB	]	= "#DB",
268f34c5b5SThomas Gleixner 		[ ESTACK_MCE	]	= "#MC",
2702772fb9SJoerg Roedel 		[ ESTACK_VC	]	= "#VC",
2802772fb9SJoerg Roedel 		[ ESTACK_VC2	]	= "#VC2",
299c003907SJosh Poimboeuf };
309c003907SJosh Poimboeuf 
313d02a9c4SJosh Poimboeuf const char *stack_type_name(enum stack_type type)
320406ca6dSFrederic Weisbecker {
3302772fb9SJoerg Roedel 	BUILD_BUG_ON(N_EXCEPTION_STACKS != 6);
34cb76c939SJosh Poimboeuf 
353d02a9c4SJosh Poimboeuf 	if (type == STACK_TYPE_IRQ)
363d02a9c4SJosh Poimboeuf 		return "IRQ";
373d02a9c4SJosh Poimboeuf 
384fe2d8b1SDave Hansen 	if (type == STACK_TYPE_ENTRY) {
394fe2d8b1SDave Hansen 		/*
404fe2d8b1SDave Hansen 		 * On 64-bit, we have a generic entry stack that we
414fe2d8b1SDave Hansen 		 * use for all the kernel entry points, including
424fe2d8b1SDave Hansen 		 * SYSENTER.
434fe2d8b1SDave Hansen 		 */
444fe2d8b1SDave Hansen 		return "ENTRY_TRAMPOLINE";
454fe2d8b1SDave Hansen 	}
4633a2f1a6SAndy Lutomirski 
473d02a9c4SJosh Poimboeuf 	if (type >= STACK_TYPE_EXCEPTION && type <= STACK_TYPE_EXCEPTION_LAST)
483d02a9c4SJosh Poimboeuf 		return exception_stack_names[type - STACK_TYPE_EXCEPTION];
493d02a9c4SJosh Poimboeuf 
503d02a9c4SJosh Poimboeuf 	return NULL;
51cb76c939SJosh Poimboeuf }
52cb76c939SJosh Poimboeuf 
53c450c8f5SThomas Gleixner /**
54c450c8f5SThomas Gleixner  * struct estack_pages - Page descriptor for exception stacks
55c450c8f5SThomas Gleixner  * @offs:	Offset from the start of the exception stack area
56c450c8f5SThomas Gleixner  * @size:	Size of the exception stack
57c450c8f5SThomas Gleixner  * @type:	Type to store in the stack_info struct
58c450c8f5SThomas Gleixner  */
59c450c8f5SThomas Gleixner struct estack_pages {
60c450c8f5SThomas Gleixner 	u32	offs;
61c450c8f5SThomas Gleixner 	u16	size;
62c450c8f5SThomas Gleixner 	u16	type;
63afcd21daSThomas Gleixner };
64afcd21daSThomas Gleixner 
65c450c8f5SThomas Gleixner #define EPAGERANGE(st)							\
66c450c8f5SThomas Gleixner 	[PFN_DOWN(CEA_ESTACK_OFFS(st)) ...				\
67c450c8f5SThomas Gleixner 	 PFN_DOWN(CEA_ESTACK_OFFS(st) + CEA_ESTACK_SIZE(st) - 1)] = {	\
68c450c8f5SThomas Gleixner 		.offs	= CEA_ESTACK_OFFS(st),				\
69c450c8f5SThomas Gleixner 		.size	= CEA_ESTACK_SIZE(st),				\
70c450c8f5SThomas Gleixner 		.type	= STACK_TYPE_EXCEPTION + ESTACK_ ##st, }
71afcd21daSThomas Gleixner 
72c450c8f5SThomas Gleixner /*
73c450c8f5SThomas Gleixner  * Array of exception stack page descriptors. If the stack is larger than
74c450c8f5SThomas Gleixner  * PAGE_SIZE, all pages covering a particular stack will have the same
75c450c8f5SThomas Gleixner  * info. The guard pages including the not mapped DB2 stack are zeroed
76c450c8f5SThomas Gleixner  * out.
77c450c8f5SThomas Gleixner  */
78c450c8f5SThomas Gleixner static const
79c450c8f5SThomas Gleixner struct estack_pages estack_pages[CEA_ESTACK_PAGES] ____cacheline_aligned = {
80c450c8f5SThomas Gleixner 	EPAGERANGE(DF),
81c450c8f5SThomas Gleixner 	EPAGERANGE(NMI),
82c450c8f5SThomas Gleixner 	EPAGERANGE(DB),
83c450c8f5SThomas Gleixner 	EPAGERANGE(MCE),
8402772fb9SJoerg Roedel 	EPAGERANGE(VC),
8502772fb9SJoerg Roedel 	EPAGERANGE(VC2),
86afcd21daSThomas Gleixner };
87afcd21daSThomas Gleixner 
88fcd709efSJosh Poimboeuf static bool in_exception_stack(unsigned long *stack, struct stack_info *info)
89cb76c939SJosh Poimboeuf {
90c450c8f5SThomas Gleixner 	unsigned long begin, end, stk = (unsigned long)stack;
91c450c8f5SThomas Gleixner 	const struct estack_pages *ep;
92cb76c939SJosh Poimboeuf 	struct pt_regs *regs;
93afcd21daSThomas Gleixner 	unsigned int k;
946fcbede3SAlexander van Heukelum 
9502772fb9SJoerg Roedel 	BUILD_BUG_ON(N_EXCEPTION_STACKS != 6);
969c003907SJosh Poimboeuf 
97c450c8f5SThomas Gleixner 	begin = (unsigned long)__this_cpu_read(cea_exception_stacks);
98e361362bSThomas Gleixner 	/*
99e361362bSThomas Gleixner 	 * Handle the case where stack trace is collected _before_
100e361362bSThomas Gleixner 	 * cea_exception_stacks had been initialized.
101e361362bSThomas Gleixner 	 */
102e361362bSThomas Gleixner 	if (!begin)
103e361362bSThomas Gleixner 		return false;
104e361362bSThomas Gleixner 
105c450c8f5SThomas Gleixner 	end = begin + sizeof(struct cea_exception_stacks);
106c450c8f5SThomas Gleixner 	/* Bail if @stack is outside the exception stack area. */
107c450c8f5SThomas Gleixner 	if (stk < begin || stk >= end)
108c450c8f5SThomas Gleixner 		return false;
109afcd21daSThomas Gleixner 
110c450c8f5SThomas Gleixner 	/* Calc page offset from start of exception stacks */
111c450c8f5SThomas Gleixner 	k = (stk - begin) >> PAGE_SHIFT;
112c450c8f5SThomas Gleixner 	/* Lookup the page descriptor */
113c450c8f5SThomas Gleixner 	ep = &estack_pages[k];
114c450c8f5SThomas Gleixner 	/* Guard page? */
115c450c8f5SThomas Gleixner 	if (!ep->size)
116c450c8f5SThomas Gleixner 		return false;
117c450c8f5SThomas Gleixner 
118c450c8f5SThomas Gleixner 	begin += (unsigned long)ep->offs;
119c450c8f5SThomas Gleixner 	end = begin + (unsigned long)ep->size;
120cb76c939SJosh Poimboeuf 	regs = (struct pt_regs *)end - 1;
1219c003907SJosh Poimboeuf 
122c450c8f5SThomas Gleixner 	info->type	= ep->type;
123afcd21daSThomas Gleixner 	info->begin	= (unsigned long *)begin;
124afcd21daSThomas Gleixner 	info->end	= (unsigned long *)end;
125cb76c939SJosh Poimboeuf 	info->next_sp	= (unsigned long *)regs->sp;
126cb76c939SJosh Poimboeuf 	return true;
1276fcbede3SAlexander van Heukelum }
1289c003907SJosh Poimboeuf 
129cb76c939SJosh Poimboeuf static bool in_irq_stack(unsigned long *stack, struct stack_info *info)
130af2d8289SFrederic Weisbecker {
131758a2e31SThomas Gleixner 	unsigned long *end   = (unsigned long *)this_cpu_read(hardirq_stack_ptr);
132cb76c939SJosh Poimboeuf 	unsigned long *begin = end - (IRQ_STACK_SIZE / sizeof(long));
133cb76c939SJosh Poimboeuf 
1345fe599e0SJosh Poimboeuf 	/*
1355fe599e0SJosh Poimboeuf 	 * This is a software stack, so 'end' can be a valid stack pointer.
1365fe599e0SJosh Poimboeuf 	 * It just means the stack is empty.
1375fe599e0SJosh Poimboeuf 	 */
138fa332154SAndy Lutomirski 	if (stack < begin || stack >= end)
139cb76c939SJosh Poimboeuf 		return false;
140cb76c939SJosh Poimboeuf 
141cb76c939SJosh Poimboeuf 	info->type	= STACK_TYPE_IRQ;
142cb76c939SJosh Poimboeuf 	info->begin	= begin;
143cb76c939SJosh Poimboeuf 	info->end	= end;
144cb76c939SJosh Poimboeuf 
145cb76c939SJosh Poimboeuf 	/*
146cb76c939SJosh Poimboeuf 	 * The next stack pointer is the first thing pushed by the entry code
147cb76c939SJosh Poimboeuf 	 * after switching to the irq stack.
148cb76c939SJosh Poimboeuf 	 */
149cb76c939SJosh Poimboeuf 	info->next_sp = (unsigned long *)*(end - 1);
150cb76c939SJosh Poimboeuf 
151cb76c939SJosh Poimboeuf 	return true;
152af2d8289SFrederic Weisbecker }
153af2d8289SFrederic Weisbecker 
154cb76c939SJosh Poimboeuf int get_stack_info(unsigned long *stack, struct task_struct *task,
155cb76c939SJosh Poimboeuf 		   struct stack_info *info, unsigned long *visit_mask)
1562223f6f6SSteven Rostedt {
157cb76c939SJosh Poimboeuf 	if (!stack)
158cb76c939SJosh Poimboeuf 		goto unknown;
1592223f6f6SSteven Rostedt 
160cb76c939SJosh Poimboeuf 	task = task ? : current;
1612223f6f6SSteven Rostedt 
162cb76c939SJosh Poimboeuf 	if (in_task_stack(stack, task, info))
163fcd709efSJosh Poimboeuf 		goto recursion_check;
1642223f6f6SSteven Rostedt 
165cb76c939SJosh Poimboeuf 	if (task != current)
166cb76c939SJosh Poimboeuf 		goto unknown;
1672223f6f6SSteven Rostedt 
168fcd709efSJosh Poimboeuf 	if (in_exception_stack(stack, info))
169fcd709efSJosh Poimboeuf 		goto recursion_check;
1702223f6f6SSteven Rostedt 
171cb76c939SJosh Poimboeuf 	if (in_irq_stack(stack, info))
172fcd709efSJosh Poimboeuf 		goto recursion_check;
173fcd709efSJosh Poimboeuf 
1744fe2d8b1SDave Hansen 	if (in_entry_stack(stack, info))
17533a2f1a6SAndy Lutomirski 		goto recursion_check;
17633a2f1a6SAndy Lutomirski 
177fcd709efSJosh Poimboeuf 	goto unknown;
178fcd709efSJosh Poimboeuf 
179fcd709efSJosh Poimboeuf recursion_check:
180fcd709efSJosh Poimboeuf 	/*
181fcd709efSJosh Poimboeuf 	 * Make sure we don't iterate through any given stack more than once.
182fcd709efSJosh Poimboeuf 	 * If it comes up a second time then there's something wrong going on:
183fcd709efSJosh Poimboeuf 	 * just break out and report an unknown stack type.
184fcd709efSJosh Poimboeuf 	 */
185fcd709efSJosh Poimboeuf 	if (visit_mask) {
1860d2b8579SJosh Poimboeuf 		if (*visit_mask & (1UL << info->type)) {
187b08418b5SJosh Poimboeuf 			if (task == current)
1880d2b8579SJosh Poimboeuf 				printk_deferred_once(KERN_WARNING "WARNING: stack recursion on stack type %d\n", info->type);
189fcd709efSJosh Poimboeuf 			goto unknown;
1900d2b8579SJosh Poimboeuf 		}
191fcd709efSJosh Poimboeuf 		*visit_mask |= 1UL << info->type;
192fcd709efSJosh Poimboeuf 	}
1932223f6f6SSteven Rostedt 
194cb76c939SJosh Poimboeuf 	return 0;
195cb76c939SJosh Poimboeuf 
196cb76c939SJosh Poimboeuf unknown:
197cb76c939SJosh Poimboeuf 	info->type = STACK_TYPE_UNKNOWN;
198cb76c939SJosh Poimboeuf 	return -EINVAL;
1992223f6f6SSteven Rostedt }
200