1 /*
2  * S390 Version
3  *   Copyright IBM Corp. 2005
4  *   Author(s): Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
5  */
6 
7 #include <linux/oprofile.h>
8 
9 #include <asm/processor.h> /* for struct stack_frame */
10 
11 static unsigned long
__show_trace(unsigned int * depth,unsigned long sp,unsigned long low,unsigned long high)12 __show_trace(unsigned int *depth, unsigned long sp,
13 	     unsigned long low, unsigned long high)
14 {
15 	struct stack_frame *sf;
16 	struct pt_regs *regs;
17 
18 	while (*depth) {
19 		sp = sp & PSW_ADDR_INSN;
20 		if (sp < low || sp > high - sizeof(*sf))
21 			return sp;
22 		sf = (struct stack_frame *) sp;
23 		(*depth)--;
24 		oprofile_add_trace(sf->gprs[8] & PSW_ADDR_INSN);
25 
26 		/* Follow the backchain.  */
27 		while (*depth) {
28 			low = sp;
29 			sp = sf->back_chain & PSW_ADDR_INSN;
30 			if (!sp)
31 				break;
32 			if (sp <= low || sp > high - sizeof(*sf))
33 				return sp;
34 			sf = (struct stack_frame *) sp;
35 			(*depth)--;
36 			oprofile_add_trace(sf->gprs[8] & PSW_ADDR_INSN);
37 
38 		}
39 
40 		if (*depth == 0)
41 			break;
42 
43 		/* Zero backchain detected, check for interrupt frame.  */
44 		sp = (unsigned long) (sf + 1);
45 		if (sp <= low || sp > high - sizeof(*regs))
46 			return sp;
47 		regs = (struct pt_regs *) sp;
48 		(*depth)--;
49 		oprofile_add_trace(sf->gprs[8] & PSW_ADDR_INSN);
50 		low = sp;
51 		sp = regs->gprs[15];
52 	}
53 	return sp;
54 }
55 
s390_backtrace(struct pt_regs * const regs,unsigned int depth)56 void s390_backtrace(struct pt_regs * const regs, unsigned int depth)
57 {
58 	unsigned long head;
59 	struct stack_frame* head_sf;
60 
61 	if (user_mode(regs))
62 		return;
63 
64 	head = regs->gprs[15];
65 	head_sf = (struct stack_frame*)head;
66 
67 	if (!head_sf->back_chain)
68 		return;
69 
70 	head = head_sf->back_chain;
71 
72 	head = __show_trace(&depth, head, S390_lowcore.async_stack - ASYNC_SIZE,
73 			    S390_lowcore.async_stack);
74 
75 	__show_trace(&depth, head, S390_lowcore.thread_info,
76 		     S390_lowcore.thread_info + THREAD_SIZE);
77 }
78