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