diff options
Diffstat (limited to 'arch/x86/kernel/dumpstack.c')
| -rw-r--r-- | arch/x86/kernel/dumpstack.c | 75 | 
1 files changed, 60 insertions, 15 deletions
| diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index f13b4c00a5de..36b17e0febe8 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c @@ -18,6 +18,7 @@  #include <linux/nmi.h>  #include <linux/sysfs.h> +#include <asm/cpu_entry_area.h>  #include <asm/stacktrace.h>  #include <asm/unwind.h> @@ -43,6 +44,24 @@ bool in_task_stack(unsigned long *stack, struct task_struct *task,  	return true;  } +bool in_entry_stack(unsigned long *stack, struct stack_info *info) +{ +	struct entry_stack *ss = cpu_entry_stack(smp_processor_id()); + +	void *begin = ss; +	void *end = ss + 1; + +	if ((void *)stack < begin || (void *)stack >= end) +		return false; + +	info->type	= STACK_TYPE_ENTRY; +	info->begin	= begin; +	info->end	= end; +	info->next_sp	= NULL; + +	return true; +} +  static void printk_stack_address(unsigned long address, int reliable,  				 char *log_lvl)  { @@ -50,6 +69,28 @@ static void printk_stack_address(unsigned long address, int reliable,  	printk("%s %s%pB\n", log_lvl, reliable ? "" : "? ", (void *)address);  } +void show_iret_regs(struct pt_regs *regs) +{ +	printk(KERN_DEFAULT "RIP: %04x:%pS\n", (int)regs->cs, (void *)regs->ip); +	printk(KERN_DEFAULT "RSP: %04x:%016lx EFLAGS: %08lx", (int)regs->ss, +		regs->sp, regs->flags); +} + +static void show_regs_safe(struct stack_info *info, struct pt_regs *regs) +{ +	if (on_stack(info, regs, sizeof(*regs))) +		__show_regs(regs, 0); +	else if (on_stack(info, (void *)regs + IRET_FRAME_OFFSET, +			  IRET_FRAME_SIZE)) { +		/* +		 * When an interrupt or exception occurs in entry code, the +		 * full pt_regs might not have been saved yet.  In that case +		 * just print the iret frame. +		 */ +		show_iret_regs(regs); +	} +} +  void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,  			unsigned long *stack, char *log_lvl)  { @@ -71,31 +112,35 @@ void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,  	 * - task stack  	 * - interrupt stack  	 * - HW exception stacks (double fault, nmi, debug, mce) +	 * - entry stack  	 * -	 * x86-32 can have up to three stacks: +	 * x86-32 can have up to four stacks:  	 * - task stack  	 * - softirq stack  	 * - hardirq stack +	 * - entry stack  	 */  	for (regs = NULL; stack; stack = PTR_ALIGN(stack_info.next_sp, sizeof(long))) {  		const char *stack_name; -		/* -		 * If we overflowed the task stack into a guard page, jump back -		 * to the bottom of the usable stack. -		 */ -		if (task_stack_page(task) - (void *)stack < PAGE_SIZE) -			stack = task_stack_page(task); - -		if (get_stack_info(stack, task, &stack_info, &visit_mask)) -			break; +		if (get_stack_info(stack, task, &stack_info, &visit_mask)) { +			/* +			 * We weren't on a valid stack.  It's possible that +			 * we overflowed a valid stack into a guard page. +			 * See if the next page up is valid so that we can +			 * generate some kind of backtrace if this happens. +			 */ +			stack = (unsigned long *)PAGE_ALIGN((unsigned long)stack); +			if (get_stack_info(stack, task, &stack_info, &visit_mask)) +				break; +		}  		stack_name = stack_type_name(stack_info.type);  		if (stack_name)  			printk("%s <%s>\n", log_lvl, stack_name); -		if (regs && on_stack(&stack_info, regs, sizeof(*regs))) -			__show_regs(regs, 0); +		if (regs) +			show_regs_safe(&stack_info, regs);  		/*  		 * Scan the stack, printing any text addresses we find.  At the @@ -119,7 +164,7 @@ void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,  			/*  			 * Don't print regs->ip again if it was already printed -			 * by __show_regs() below. +			 * by show_regs_safe() below.  			 */  			if (regs && stack == ®s->ip)  				goto next; @@ -155,8 +200,8 @@ next:  			/* if the frame has entry regs, print them */  			regs = unwind_get_entry_regs(&state); -			if (regs && on_stack(&stack_info, regs, sizeof(*regs))) -				__show_regs(regs, 0); +			if (regs) +				show_regs_safe(&stack_info, regs);  		}  		if (stack_name) |