1 /* 2 * Stack trace management functions 3 * 4 * Copyright IBM Corp. 2006 5 * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> 6 */ 7 8 #include <linux/sched.h> 9 #include <linux/stacktrace.h> 10 #include <linux/kallsyms.h> 11 #include <linux/module.h> 12 13 static unsigned long save_context_stack(struct stack_trace *trace, 14 unsigned long sp, 15 unsigned long low, 16 unsigned long high, 17 int savesched) 18 { 19 struct stack_frame *sf; 20 struct pt_regs *regs; 21 unsigned long addr; 22 23 while(1) { 24 sp &= PSW_ADDR_INSN; 25 if (sp < low || sp > high) 26 return sp; 27 sf = (struct stack_frame *)sp; 28 while(1) { 29 addr = sf->gprs[8] & PSW_ADDR_INSN; 30 if (!trace->skip) 31 trace->entries[trace->nr_entries++] = addr; 32 else 33 trace->skip--; 34 if (trace->nr_entries >= trace->max_entries) 35 return sp; 36 low = sp; 37 sp = sf->back_chain & PSW_ADDR_INSN; 38 if (!sp) 39 break; 40 if (sp <= low || sp > high - sizeof(*sf)) 41 return sp; 42 sf = (struct stack_frame *)sp; 43 } 44 /* Zero backchain detected, check for interrupt frame. */ 45 sp = (unsigned long)(sf + 1); 46 if (sp <= low || sp > high - sizeof(*regs)) 47 return sp; 48 regs = (struct pt_regs *)sp; 49 addr = regs->psw.addr & PSW_ADDR_INSN; 50 if (savesched || !in_sched_functions(addr)) { 51 if (!trace->skip) 52 trace->entries[trace->nr_entries++] = addr; 53 else 54 trace->skip--; 55 } 56 if (trace->nr_entries >= trace->max_entries) 57 return sp; 58 low = sp; 59 sp = regs->gprs[15]; 60 } 61 } 62 63 void save_stack_trace(struct stack_trace *trace) 64 { 65 register unsigned long sp asm ("15"); 66 unsigned long orig_sp, new_sp; 67 68 orig_sp = sp & PSW_ADDR_INSN; 69 new_sp = save_context_stack(trace, orig_sp, 70 S390_lowcore.panic_stack - PAGE_SIZE, 71 S390_lowcore.panic_stack, 1); 72 if (new_sp != orig_sp) 73 return; 74 new_sp = save_context_stack(trace, new_sp, 75 S390_lowcore.async_stack - ASYNC_SIZE, 76 S390_lowcore.async_stack, 1); 77 if (new_sp != orig_sp) 78 return; 79 save_context_stack(trace, new_sp, 80 S390_lowcore.thread_info, 81 S390_lowcore.thread_info + THREAD_SIZE, 1); 82 } 83 EXPORT_SYMBOL_GPL(save_stack_trace); 84 85 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 86 { 87 unsigned long sp, low, high; 88 89 sp = tsk->thread.ksp & PSW_ADDR_INSN; 90 low = (unsigned long) task_stack_page(tsk); 91 high = (unsigned long) task_pt_regs(tsk); 92 save_context_stack(trace, sp, low, high, 0); 93 if (trace->nr_entries < trace->max_entries) 94 trace->entries[trace->nr_entries++] = ULONG_MAX; 95 } 96 EXPORT_SYMBOL_GPL(save_stack_trace_tsk); 97