xref: /openbmc/linux/arch/x86/kernel/dumpstack_64.c (revision d7b6d709)
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 
stack_type_name(enum stack_type type)313d02a9c4SJosh Poimboeuf const char *stack_type_name(enum stack_type type)
320406ca6dSFrederic Weisbecker {
3302772fb9SJoerg Roedel 	BUILD_BUG_ON(N_EXCEPTION_STACKS != 6);
34cb76c939SJosh Poimboeuf 
3544b979faSPeter Zijlstra 	if (type == STACK_TYPE_TASK)
3644b979faSPeter Zijlstra 		return "TASK";
3744b979faSPeter Zijlstra 
383d02a9c4SJosh Poimboeuf 	if (type == STACK_TYPE_IRQ)
393d02a9c4SJosh Poimboeuf 		return "IRQ";
403d02a9c4SJosh Poimboeuf 
4144b979faSPeter Zijlstra 	if (type == STACK_TYPE_SOFTIRQ)
4244b979faSPeter Zijlstra 		return "SOFTIRQ";
4344b979faSPeter Zijlstra 
444fe2d8b1SDave Hansen 	if (type == STACK_TYPE_ENTRY) {
454fe2d8b1SDave Hansen 		/*
464fe2d8b1SDave Hansen 		 * On 64-bit, we have a generic entry stack that we
474fe2d8b1SDave Hansen 		 * use for all the kernel entry points, including
484fe2d8b1SDave Hansen 		 * SYSENTER.
494fe2d8b1SDave Hansen 		 */
504fe2d8b1SDave Hansen 		return "ENTRY_TRAMPOLINE";
514fe2d8b1SDave Hansen 	}
5233a2f1a6SAndy Lutomirski 
533d02a9c4SJosh Poimboeuf 	if (type >= STACK_TYPE_EXCEPTION && type <= STACK_TYPE_EXCEPTION_LAST)
543d02a9c4SJosh Poimboeuf 		return exception_stack_names[type - STACK_TYPE_EXCEPTION];
553d02a9c4SJosh Poimboeuf 
563d02a9c4SJosh Poimboeuf 	return NULL;
57cb76c939SJosh Poimboeuf }
58cb76c939SJosh Poimboeuf 
59c450c8f5SThomas Gleixner /**
60c450c8f5SThomas Gleixner  * struct estack_pages - Page descriptor for exception stacks
61c450c8f5SThomas Gleixner  * @offs:	Offset from the start of the exception stack area
62c450c8f5SThomas Gleixner  * @size:	Size of the exception stack
63c450c8f5SThomas Gleixner  * @type:	Type to store in the stack_info struct
64c450c8f5SThomas Gleixner  */
65c450c8f5SThomas Gleixner struct estack_pages {
66c450c8f5SThomas Gleixner 	u32	offs;
67c450c8f5SThomas Gleixner 	u16	size;
68c450c8f5SThomas Gleixner 	u16	type;
69afcd21daSThomas Gleixner };
70afcd21daSThomas Gleixner 
71c450c8f5SThomas Gleixner #define EPAGERANGE(st)							\
72c450c8f5SThomas Gleixner 	[PFN_DOWN(CEA_ESTACK_OFFS(st)) ...				\
73c450c8f5SThomas Gleixner 	 PFN_DOWN(CEA_ESTACK_OFFS(st) + CEA_ESTACK_SIZE(st) - 1)] = {	\
74c450c8f5SThomas Gleixner 		.offs	= CEA_ESTACK_OFFS(st),				\
75c450c8f5SThomas Gleixner 		.size	= CEA_ESTACK_SIZE(st),				\
76c450c8f5SThomas Gleixner 		.type	= STACK_TYPE_EXCEPTION + ESTACK_ ##st, }
77afcd21daSThomas Gleixner 
78c450c8f5SThomas Gleixner /*
79c450c8f5SThomas Gleixner  * Array of exception stack page descriptors. If the stack is larger than
80c450c8f5SThomas Gleixner  * PAGE_SIZE, all pages covering a particular stack will have the same
81c450c8f5SThomas Gleixner  * info. The guard pages including the not mapped DB2 stack are zeroed
82c450c8f5SThomas Gleixner  * out.
83c450c8f5SThomas Gleixner  */
84c450c8f5SThomas Gleixner static const
85c450c8f5SThomas Gleixner struct estack_pages estack_pages[CEA_ESTACK_PAGES] ____cacheline_aligned = {
86c450c8f5SThomas Gleixner 	EPAGERANGE(DF),
87c450c8f5SThomas Gleixner 	EPAGERANGE(NMI),
88c450c8f5SThomas Gleixner 	EPAGERANGE(DB),
89c450c8f5SThomas Gleixner 	EPAGERANGE(MCE),
9002772fb9SJoerg Roedel 	EPAGERANGE(VC),
9102772fb9SJoerg Roedel 	EPAGERANGE(VC2),
92afcd21daSThomas Gleixner };
93afcd21daSThomas Gleixner 
in_exception_stack(unsigned long * stack,struct stack_info * info)946b27edd7SJoerg Roedel static __always_inline bool in_exception_stack(unsigned long *stack, struct stack_info *info)
95cb76c939SJosh Poimboeuf {
96c450c8f5SThomas Gleixner 	unsigned long begin, end, stk = (unsigned long)stack;
97c450c8f5SThomas Gleixner 	const struct estack_pages *ep;
98cb76c939SJosh Poimboeuf 	struct pt_regs *regs;
99afcd21daSThomas Gleixner 	unsigned int k;
1006fcbede3SAlexander van Heukelum 
10102772fb9SJoerg Roedel 	BUILD_BUG_ON(N_EXCEPTION_STACKS != 6);
1029c003907SJosh Poimboeuf 
103c450c8f5SThomas Gleixner 	begin = (unsigned long)__this_cpu_read(cea_exception_stacks);
104e361362bSThomas Gleixner 	/*
105e361362bSThomas Gleixner 	 * Handle the case where stack trace is collected _before_
106e361362bSThomas Gleixner 	 * cea_exception_stacks had been initialized.
107e361362bSThomas Gleixner 	 */
108e361362bSThomas Gleixner 	if (!begin)
109e361362bSThomas Gleixner 		return false;
110e361362bSThomas Gleixner 
111c450c8f5SThomas Gleixner 	end = begin + sizeof(struct cea_exception_stacks);
112c450c8f5SThomas Gleixner 	/* Bail if @stack is outside the exception stack area. */
113c450c8f5SThomas Gleixner 	if (stk < begin || stk >= end)
114c450c8f5SThomas Gleixner 		return false;
115afcd21daSThomas Gleixner 
116c450c8f5SThomas Gleixner 	/* Calc page offset from start of exception stacks */
117c450c8f5SThomas Gleixner 	k = (stk - begin) >> PAGE_SHIFT;
118c450c8f5SThomas Gleixner 	/* Lookup the page descriptor */
119c450c8f5SThomas Gleixner 	ep = &estack_pages[k];
120c450c8f5SThomas Gleixner 	/* Guard page? */
121c450c8f5SThomas Gleixner 	if (!ep->size)
122c450c8f5SThomas Gleixner 		return false;
123c450c8f5SThomas Gleixner 
124c450c8f5SThomas Gleixner 	begin += (unsigned long)ep->offs;
125c450c8f5SThomas Gleixner 	end = begin + (unsigned long)ep->size;
126cb76c939SJosh Poimboeuf 	regs = (struct pt_regs *)end - 1;
1279c003907SJosh Poimboeuf 
128c450c8f5SThomas Gleixner 	info->type	= ep->type;
129afcd21daSThomas Gleixner 	info->begin	= (unsigned long *)begin;
130afcd21daSThomas Gleixner 	info->end	= (unsigned long *)end;
131cb76c939SJosh Poimboeuf 	info->next_sp	= (unsigned long *)regs->sp;
132cb76c939SJosh Poimboeuf 	return true;
1336fcbede3SAlexander van Heukelum }
1349c003907SJosh Poimboeuf 
in_irq_stack(unsigned long * stack,struct stack_info * info)1356b27edd7SJoerg Roedel static __always_inline bool in_irq_stack(unsigned long *stack, struct stack_info *info)
136af2d8289SFrederic Weisbecker {
137*d7b6d709SThomas Gleixner 	unsigned long *end = (unsigned long *)this_cpu_read(pcpu_hot.hardirq_stack_ptr);
138951c2a51SThomas Gleixner 	unsigned long *begin;
139cb76c939SJosh Poimboeuf 
1405fe599e0SJosh Poimboeuf 	/*
141951c2a51SThomas Gleixner 	 * @end points directly to the top most stack entry to avoid a -8
142951c2a51SThomas Gleixner 	 * adjustment in the stack switch hotpath. Adjust it back before
143951c2a51SThomas Gleixner 	 * calculating @begin.
144951c2a51SThomas Gleixner 	 */
145951c2a51SThomas Gleixner 	end++;
146951c2a51SThomas Gleixner 	begin = end - (IRQ_STACK_SIZE / sizeof(long));
147951c2a51SThomas Gleixner 
148951c2a51SThomas Gleixner 	/*
149951c2a51SThomas Gleixner 	 * Due to the switching logic RSP can never be == @end because the
150951c2a51SThomas Gleixner 	 * final operation is 'popq %rsp' which means after that RSP points
151951c2a51SThomas Gleixner 	 * to the original stack and not to @end.
1525fe599e0SJosh Poimboeuf 	 */
153fa332154SAndy Lutomirski 	if (stack < begin || stack >= end)
154cb76c939SJosh Poimboeuf 		return false;
155cb76c939SJosh Poimboeuf 
156cb76c939SJosh Poimboeuf 	info->type	= STACK_TYPE_IRQ;
157cb76c939SJosh Poimboeuf 	info->begin	= begin;
158cb76c939SJosh Poimboeuf 	info->end	= end;
159cb76c939SJosh Poimboeuf 
160cb76c939SJosh Poimboeuf 	/*
161951c2a51SThomas Gleixner 	 * The next stack pointer is stored at the top of the irq stack
162951c2a51SThomas Gleixner 	 * before switching to the irq stack. Actual stack entries are all
163951c2a51SThomas Gleixner 	 * below that.
164cb76c939SJosh Poimboeuf 	 */
165cb76c939SJosh Poimboeuf 	info->next_sp = (unsigned long *)*(end - 1);
166cb76c939SJosh Poimboeuf 
167cb76c939SJosh Poimboeuf 	return true;
168af2d8289SFrederic Weisbecker }
169af2d8289SFrederic Weisbecker 
get_stack_info_noinstr(unsigned long * stack,struct task_struct * task,struct stack_info * info)1706b27edd7SJoerg Roedel bool noinstr get_stack_info_noinstr(unsigned long *stack, struct task_struct *task,
1716b27edd7SJoerg Roedel 				    struct stack_info *info)
1726b27edd7SJoerg Roedel {
1736b27edd7SJoerg Roedel 	if (in_task_stack(stack, task, info))
1746b27edd7SJoerg Roedel 		return true;
1756b27edd7SJoerg Roedel 
1766b27edd7SJoerg Roedel 	if (task != current)
1776b27edd7SJoerg Roedel 		return false;
1786b27edd7SJoerg Roedel 
1796b27edd7SJoerg Roedel 	if (in_exception_stack(stack, info))
1806b27edd7SJoerg Roedel 		return true;
1816b27edd7SJoerg Roedel 
1826b27edd7SJoerg Roedel 	if (in_irq_stack(stack, info))
1836b27edd7SJoerg Roedel 		return true;
1846b27edd7SJoerg Roedel 
1856b27edd7SJoerg Roedel 	if (in_entry_stack(stack, info))
1866b27edd7SJoerg Roedel 		return true;
1876b27edd7SJoerg Roedel 
1886b27edd7SJoerg Roedel 	return false;
1896b27edd7SJoerg Roedel }
1906b27edd7SJoerg Roedel 
get_stack_info(unsigned long * stack,struct task_struct * task,struct stack_info * info,unsigned long * visit_mask)191cb76c939SJosh Poimboeuf int get_stack_info(unsigned long *stack, struct task_struct *task,
192cb76c939SJosh Poimboeuf 		   struct stack_info *info, unsigned long *visit_mask)
1932223f6f6SSteven Rostedt {
1946b27edd7SJoerg Roedel 	task = task ? : current;
1956b27edd7SJoerg Roedel 
196cb76c939SJosh Poimboeuf 	if (!stack)
197cb76c939SJosh Poimboeuf 		goto unknown;
1982223f6f6SSteven Rostedt 
1996b27edd7SJoerg Roedel 	if (!get_stack_info_noinstr(stack, task, info))
200cb76c939SJosh Poimboeuf 		goto unknown;
2012223f6f6SSteven Rostedt 
202fcd709efSJosh Poimboeuf 	/*
203fcd709efSJosh Poimboeuf 	 * Make sure we don't iterate through any given stack more than once.
204fcd709efSJosh Poimboeuf 	 * If it comes up a second time then there's something wrong going on:
205fcd709efSJosh Poimboeuf 	 * just break out and report an unknown stack type.
206fcd709efSJosh Poimboeuf 	 */
207fcd709efSJosh Poimboeuf 	if (visit_mask) {
2080d2b8579SJosh Poimboeuf 		if (*visit_mask & (1UL << info->type)) {
209b08418b5SJosh Poimboeuf 			if (task == current)
2100d2b8579SJosh Poimboeuf 				printk_deferred_once(KERN_WARNING "WARNING: stack recursion on stack type %d\n", info->type);
211fcd709efSJosh Poimboeuf 			goto unknown;
2120d2b8579SJosh Poimboeuf 		}
213fcd709efSJosh Poimboeuf 		*visit_mask |= 1UL << info->type;
214fcd709efSJosh Poimboeuf 	}
2152223f6f6SSteven Rostedt 
216cb76c939SJosh Poimboeuf 	return 0;
217cb76c939SJosh Poimboeuf 
218cb76c939SJosh Poimboeuf unknown:
219cb76c939SJosh Poimboeuf 	info->type = STACK_TYPE_UNKNOWN;
220cb76c939SJosh Poimboeuf 	return -EINVAL;
2212223f6f6SSteven Rostedt }
222