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> |
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 | |
21 | static int read_user_stack_64(const unsigned long __user *ptr, unsigned long *ret) |
22 | { |
23 | return __read_user_stack(ptr, ret, size: sizeof(*ret)); |
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; |
44 | if (current->mm->context.vdso && |
45 | nip == VDSO64_SYMBOL(current->mm->context.vdso, sigtramp_rt64)) |
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(ptr: (unsigned long __user *) &sf->pinfo, ret: &pinfo) || |
61 | read_user_stack_64(ptr: (unsigned long __user *) &sf->puc, ret: &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(ctx: entry, ip: 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(ptr: fp, ret: &next_sp)) |
85 | return; |
86 | if (level > 0 && read_user_stack_64(ptr: &fp[2], ret: &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(nip: next_ip, fp: sp) || |
97 | (level <= 1 && is_sigreturn_64_address(nip: lr, fp: 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(ctx: entry, ip: PERF_CONTEXT_USER); |
110 | perf_callchain_store(ctx: entry, ip: next_ip); |
111 | continue; |
112 | } |
113 | |
114 | if (level == 0) |
115 | next_ip = lr; |
116 | perf_callchain_store(ctx: entry, ip: next_ip); |
117 | ++level; |
118 | sp = next_sp; |
119 | } |
120 | } |
121 | |