xref: /openbmc/linux/arch/x86/kernel/dumpstack_64.c (revision e361362b)
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",
252a594d4cSThomas Gleixner 		[ ESTACK_DB2	]	= "#DB2",
262a594d4cSThomas Gleixner 		[ ESTACK_DB1	]	= "#DB1",
278f34c5b5SThomas Gleixner 		[ ESTACK_DB	]	= "#DB",
288f34c5b5SThomas Gleixner 		[ ESTACK_MCE	]	= "#MC",
299c003907SJosh Poimboeuf };
309c003907SJosh Poimboeuf 
313d02a9c4SJosh Poimboeuf const char *stack_type_name(enum stack_type type)
320406ca6dSFrederic Weisbecker {
332a594d4cSThomas Gleixner 	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(DB1),
83c450c8f5SThomas Gleixner 	EPAGERANGE(DB),
84c450c8f5SThomas Gleixner 	EPAGERANGE(MCE),
85afcd21daSThomas Gleixner };
86afcd21daSThomas Gleixner 
87fcd709efSJosh Poimboeuf static bool in_exception_stack(unsigned long *stack, struct stack_info *info)
88cb76c939SJosh Poimboeuf {
89c450c8f5SThomas Gleixner 	unsigned long begin, end, stk = (unsigned long)stack;
90c450c8f5SThomas Gleixner 	const struct estack_pages *ep;
91cb76c939SJosh Poimboeuf 	struct pt_regs *regs;
92afcd21daSThomas Gleixner 	unsigned int k;
936fcbede3SAlexander van Heukelum 
942a594d4cSThomas Gleixner 	BUILD_BUG_ON(N_EXCEPTION_STACKS != 6);
959c003907SJosh Poimboeuf 
96c450c8f5SThomas Gleixner 	begin = (unsigned long)__this_cpu_read(cea_exception_stacks);
97e361362bSThomas Gleixner 	/*
98e361362bSThomas Gleixner 	 * Handle the case where stack trace is collected _before_
99e361362bSThomas Gleixner 	 * cea_exception_stacks had been initialized.
100e361362bSThomas Gleixner 	 */
101e361362bSThomas Gleixner 	if (!begin)
102e361362bSThomas Gleixner 		return false;
103e361362bSThomas Gleixner 
104c450c8f5SThomas Gleixner 	end = begin + sizeof(struct cea_exception_stacks);
105c450c8f5SThomas Gleixner 	/* Bail if @stack is outside the exception stack area. */
106c450c8f5SThomas Gleixner 	if (stk < begin || stk >= end)
107c450c8f5SThomas Gleixner 		return false;
108afcd21daSThomas Gleixner 
109c450c8f5SThomas Gleixner 	/* Calc page offset from start of exception stacks */
110c450c8f5SThomas Gleixner 	k = (stk - begin) >> PAGE_SHIFT;
111c450c8f5SThomas Gleixner 	/* Lookup the page descriptor */
112c450c8f5SThomas Gleixner 	ep = &estack_pages[k];
113c450c8f5SThomas Gleixner 	/* Guard page? */
114c450c8f5SThomas Gleixner 	if (!ep->size)
115c450c8f5SThomas Gleixner 		return false;
116c450c8f5SThomas Gleixner 
117c450c8f5SThomas Gleixner 	begin += (unsigned long)ep->offs;
118c450c8f5SThomas Gleixner 	end = begin + (unsigned long)ep->size;
119cb76c939SJosh Poimboeuf 	regs = (struct pt_regs *)end - 1;
1209c003907SJosh Poimboeuf 
121c450c8f5SThomas Gleixner 	info->type	= ep->type;
122afcd21daSThomas Gleixner 	info->begin	= (unsigned long *)begin;
123afcd21daSThomas Gleixner 	info->end	= (unsigned long *)end;
124cb76c939SJosh Poimboeuf 	info->next_sp	= (unsigned long *)regs->sp;
125cb76c939SJosh Poimboeuf 	return true;
1266fcbede3SAlexander van Heukelum }
1279c003907SJosh Poimboeuf 
128cb76c939SJosh Poimboeuf static bool in_irq_stack(unsigned long *stack, struct stack_info *info)
129af2d8289SFrederic Weisbecker {
130758a2e31SThomas Gleixner 	unsigned long *end   = (unsigned long *)this_cpu_read(hardirq_stack_ptr);
131cb76c939SJosh Poimboeuf 	unsigned long *begin = end - (IRQ_STACK_SIZE / sizeof(long));
132cb76c939SJosh Poimboeuf 
1335fe599e0SJosh Poimboeuf 	/*
1345fe599e0SJosh Poimboeuf 	 * This is a software stack, so 'end' can be a valid stack pointer.
1355fe599e0SJosh Poimboeuf 	 * It just means the stack is empty.
1365fe599e0SJosh Poimboeuf 	 */
137fa332154SAndy Lutomirski 	if (stack < begin || stack >= end)
138cb76c939SJosh Poimboeuf 		return false;
139cb76c939SJosh Poimboeuf 
140cb76c939SJosh Poimboeuf 	info->type	= STACK_TYPE_IRQ;
141cb76c939SJosh Poimboeuf 	info->begin	= begin;
142cb76c939SJosh Poimboeuf 	info->end	= end;
143cb76c939SJosh Poimboeuf 
144cb76c939SJosh Poimboeuf 	/*
145cb76c939SJosh Poimboeuf 	 * The next stack pointer is the first thing pushed by the entry code
146cb76c939SJosh Poimboeuf 	 * after switching to the irq stack.
147cb76c939SJosh Poimboeuf 	 */
148cb76c939SJosh Poimboeuf 	info->next_sp = (unsigned long *)*(end - 1);
149cb76c939SJosh Poimboeuf 
150cb76c939SJosh Poimboeuf 	return true;
151af2d8289SFrederic Weisbecker }
152af2d8289SFrederic Weisbecker 
153cb76c939SJosh Poimboeuf int get_stack_info(unsigned long *stack, struct task_struct *task,
154cb76c939SJosh Poimboeuf 		   struct stack_info *info, unsigned long *visit_mask)
1552223f6f6SSteven Rostedt {
156cb76c939SJosh Poimboeuf 	if (!stack)
157cb76c939SJosh Poimboeuf 		goto unknown;
1582223f6f6SSteven Rostedt 
159cb76c939SJosh Poimboeuf 	task = task ? : current;
1602223f6f6SSteven Rostedt 
161cb76c939SJosh Poimboeuf 	if (in_task_stack(stack, task, info))
162fcd709efSJosh Poimboeuf 		goto recursion_check;
1632223f6f6SSteven Rostedt 
164cb76c939SJosh Poimboeuf 	if (task != current)
165cb76c939SJosh Poimboeuf 		goto unknown;
1662223f6f6SSteven Rostedt 
167fcd709efSJosh Poimboeuf 	if (in_exception_stack(stack, info))
168fcd709efSJosh Poimboeuf 		goto recursion_check;
1692223f6f6SSteven Rostedt 
170cb76c939SJosh Poimboeuf 	if (in_irq_stack(stack, info))
171fcd709efSJosh Poimboeuf 		goto recursion_check;
172fcd709efSJosh Poimboeuf 
1734fe2d8b1SDave Hansen 	if (in_entry_stack(stack, info))
17433a2f1a6SAndy Lutomirski 		goto recursion_check;
17533a2f1a6SAndy Lutomirski 
176fcd709efSJosh Poimboeuf 	goto unknown;
177fcd709efSJosh Poimboeuf 
178fcd709efSJosh Poimboeuf recursion_check:
179fcd709efSJosh Poimboeuf 	/*
180fcd709efSJosh Poimboeuf 	 * Make sure we don't iterate through any given stack more than once.
181fcd709efSJosh Poimboeuf 	 * If it comes up a second time then there's something wrong going on:
182fcd709efSJosh Poimboeuf 	 * just break out and report an unknown stack type.
183fcd709efSJosh Poimboeuf 	 */
184fcd709efSJosh Poimboeuf 	if (visit_mask) {
1850d2b8579SJosh Poimboeuf 		if (*visit_mask & (1UL << info->type)) {
1860d2b8579SJosh Poimboeuf 			printk_deferred_once(KERN_WARNING "WARNING: stack recursion on stack type %d\n", info->type);
187fcd709efSJosh Poimboeuf 			goto unknown;
1880d2b8579SJosh Poimboeuf 		}
189fcd709efSJosh Poimboeuf 		*visit_mask |= 1UL << info->type;
190fcd709efSJosh Poimboeuf 	}
1912223f6f6SSteven Rostedt 
192cb76c939SJosh Poimboeuf 	return 0;
193cb76c939SJosh Poimboeuf 
194cb76c939SJosh Poimboeuf unknown:
195cb76c939SJosh Poimboeuf 	info->type = STACK_TYPE_UNKNOWN;
196cb76c939SJosh Poimboeuf 	return -EINVAL;
1972223f6f6SSteven Rostedt }
198