]>
Commit | Line | Data |
---|---|---|
7c0eda1a MS |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * Performance counter callchain support - powerpc architecture code | |
4 | * | |
5 | * Copyright © 2009 Paul Mackerras, IBM Corporation. | |
6 | */ | |
7 | #include <linux/kernel.h> | |
8 | #include <linux/sched.h> | |
9 | #include <linux/perf_event.h> | |
10 | #include <linux/percpu.h> | |
11 | #include <linux/uaccess.h> | |
12 | #include <linux/mm.h> | |
13 | #include <asm/ptrace.h> | |
7c0eda1a MS |
14 | #include <asm/sigcontext.h> |
15 | #include <asm/ucontext.h> | |
16 | #include <asm/vdso.h> | |
17 | #include <asm/pte-walk.h> | |
18 | ||
19 | #include "callchain.h" | |
20 | ||
d3a133aa | 21 | static int read_user_stack_64(const unsigned long __user *ptr, unsigned long *ret) |
7c0eda1a | 22 | { |
d3a133aa | 23 | return __read_user_stack(ptr, ret, sizeof(*ret)); |
7c0eda1a MS |
24 | } |
25 | ||
26 | /* | |
27 | * 64-bit user processes use the same stack frame for RT and non-RT signals. | |
28 | */ | |
29 | struct signal_frame_64 { | |
30 | char dummy[__SIGNAL_FRAMESIZE]; | |
31 | struct ucontext uc; | |
32 | unsigned long unused[2]; | |
33 | unsigned int tramp[6]; | |
34 | struct siginfo *pinfo; | |
35 | void *puc; | |
36 | struct siginfo info; | |
37 | char abigap[288]; | |
38 | }; | |
39 | ||
40 | static int is_sigreturn_64_address(unsigned long nip, unsigned long fp) | |
41 | { | |
42 | if (nip == fp + offsetof(struct signal_frame_64, tramp)) | |
43 | return 1; | |
91bf6955 CL |
44 | if (current->mm->context.vdso && |
45 | nip == VDSO64_SYMBOL(current->mm->context.vdso, sigtramp_rt64)) | |
7c0eda1a MS |
46 | return 1; |
47 | return 0; | |
48 | } | |
49 | ||
50 | /* | |
51 | * Do some sanity checking on the signal frame pointed to by sp. | |
52 | * We check the pinfo and puc pointers in the frame. | |
53 | */ | |
54 | static int sane_signal_64_frame(unsigned long sp) | |
55 | { | |
56 | struct signal_frame_64 __user *sf; | |
57 | unsigned long pinfo, puc; | |
58 | ||
59 | sf = (struct signal_frame_64 __user *) sp; | |
60 | if (read_user_stack_64((unsigned long __user *) &sf->pinfo, &pinfo) || | |
61 | read_user_stack_64((unsigned long __user *) &sf->puc, &puc)) | |
62 | return 0; | |
63 | return pinfo == (unsigned long) &sf->info && | |
64 | puc == (unsigned long) &sf->uc; | |
65 | } | |
66 | ||
67 | void perf_callchain_user_64(struct perf_callchain_entry_ctx *entry, | |
68 | struct pt_regs *regs) | |
69 | { | |
70 | unsigned long sp, next_sp; | |
71 | unsigned long next_ip; | |
72 | unsigned long lr; | |
73 | long level = 0; | |
74 | struct signal_frame_64 __user *sigframe; | |
75 | unsigned long __user *fp, *uregs; | |
76 | ||
77 | next_ip = perf_instruction_pointer(regs); | |
78 | lr = regs->link; | |
79 | sp = regs->gpr[1]; | |
80 | perf_callchain_store(entry, next_ip); | |
81 | ||
82 | while (entry->nr < entry->max_stack) { | |
83 | fp = (unsigned long __user *) sp; | |
84 | if (invalid_user_sp(sp) || read_user_stack_64(fp, &next_sp)) | |
85 | return; | |
86 | if (level > 0 && read_user_stack_64(&fp[2], &next_ip)) | |
87 | return; | |
88 | ||
89 | /* | |
90 | * Note: the next_sp - sp >= signal frame size check | |
91 | * is true when next_sp < sp, which can happen when | |
92 | * transitioning from an alternate signal stack to the | |
93 | * normal stack. | |
94 | */ | |
95 | if (next_sp - sp >= sizeof(struct signal_frame_64) && | |
96 | (is_sigreturn_64_address(next_ip, sp) || | |
97 | (level <= 1 && is_sigreturn_64_address(lr, sp))) && | |
98 | sane_signal_64_frame(sp)) { | |
99 | /* | |
100 | * This looks like an signal frame | |
101 | */ | |
102 | sigframe = (struct signal_frame_64 __user *) sp; | |
103 | uregs = sigframe->uc.uc_mcontext.gp_regs; | |
104 | if (read_user_stack_64(&uregs[PT_NIP], &next_ip) || | |
105 | read_user_stack_64(&uregs[PT_LNK], &lr) || | |
106 | read_user_stack_64(&uregs[PT_R1], &sp)) | |
107 | return; | |
108 | level = 0; | |
109 | perf_callchain_store_context(entry, PERF_CONTEXT_USER); | |
110 | perf_callchain_store(entry, next_ip); | |
111 | continue; | |
112 | } | |
113 | ||
114 | if (level == 0) | |
115 | next_ip = lr; | |
116 | perf_callchain_store(entry, next_ip); | |
117 | ++level; | |
118 | sp = next_sp; | |
119 | } | |
120 | } |