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
11static unsigned long
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
56void 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