Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/x86/events/utils.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0
2
#include <asm/insn.h>
3
#include <linux/mm.h>
4
5
#include <asm/msr.h>
6
#include "perf_event.h"
7
8
static int decode_branch_type(struct insn *insn)
9
{
10
int ext;
11
12
if (insn_get_opcode(insn))
13
return X86_BR_ABORT;
14
15
switch (insn->opcode.bytes[0]) {
16
case 0xf:
17
switch (insn->opcode.bytes[1]) {
18
case 0x05: /* syscall */
19
case 0x34: /* sysenter */
20
return X86_BR_SYSCALL;
21
case 0x07: /* sysret */
22
case 0x35: /* sysexit */
23
return X86_BR_SYSRET;
24
case 0x80 ... 0x8f: /* conditional */
25
return X86_BR_JCC;
26
}
27
return X86_BR_NONE;
28
case 0x70 ... 0x7f: /* conditional */
29
return X86_BR_JCC;
30
case 0xc2: /* near ret */
31
case 0xc3: /* near ret */
32
case 0xca: /* far ret */
33
case 0xcb: /* far ret */
34
return X86_BR_RET;
35
case 0xcf: /* iret */
36
return X86_BR_IRET;
37
case 0xcc ... 0xce: /* int */
38
return X86_BR_INT;
39
case 0xe8: /* call near rel */
40
if (insn_get_immediate(insn) || insn->immediate1.value == 0) {
41
/* zero length call */
42
return X86_BR_ZERO_CALL;
43
}
44
fallthrough;
45
case 0x9a: /* call far absolute */
46
return X86_BR_CALL;
47
case 0xe0 ... 0xe3: /* loop jmp */
48
return X86_BR_JCC;
49
case 0xe9 ... 0xeb: /* jmp */
50
return X86_BR_JMP;
51
case 0xff: /* call near absolute, call far absolute ind */
52
if (insn_get_modrm(insn))
53
return X86_BR_ABORT;
54
55
ext = (insn->modrm.bytes[0] >> 3) & 0x7;
56
switch (ext) {
57
case 2: /* near ind call */
58
case 3: /* far ind call */
59
return X86_BR_IND_CALL;
60
case 4:
61
case 5:
62
return X86_BR_IND_JMP;
63
}
64
return X86_BR_NONE;
65
}
66
67
return X86_BR_NONE;
68
}
69
70
/*
71
* return the type of control flow change at address "from"
72
* instruction is not necessarily a branch (in case of interrupt).
73
*
74
* The branch type returned also includes the priv level of the
75
* target of the control flow change (X86_BR_USER, X86_BR_KERNEL).
76
*
77
* If a branch type is unknown OR the instruction cannot be
78
* decoded (e.g., text page not present), then X86_BR_NONE is
79
* returned.
80
*
81
* While recording branches, some processors can report the "from"
82
* address to be that of an instruction preceding the actual branch
83
* when instruction fusion occurs. If fusion is expected, attempt to
84
* find the type of the first branch instruction within the next
85
* MAX_INSN_SIZE bytes and if found, provide the offset between the
86
* reported "from" address and the actual branch instruction address.
87
*/
88
static int get_branch_type(unsigned long from, unsigned long to, int abort,
89
bool fused, int *offset)
90
{
91
struct insn insn;
92
void *addr;
93
int bytes_read, bytes_left, insn_offset;
94
int ret = X86_BR_NONE;
95
int to_plm, from_plm;
96
u8 buf[MAX_INSN_SIZE];
97
int is64 = 0;
98
99
/* make sure we initialize offset */
100
if (offset)
101
*offset = 0;
102
103
to_plm = kernel_ip(to) ? X86_BR_KERNEL : X86_BR_USER;
104
from_plm = kernel_ip(from) ? X86_BR_KERNEL : X86_BR_USER;
105
106
/*
107
* maybe zero if lbr did not fill up after a reset by the time
108
* we get a PMU interrupt
109
*/
110
if (from == 0 || to == 0)
111
return X86_BR_NONE;
112
113
if (abort)
114
return X86_BR_ABORT | to_plm;
115
116
if (from_plm == X86_BR_USER) {
117
/*
118
* can happen if measuring at the user level only
119
* and we interrupt in a kernel thread, e.g., idle.
120
*/
121
if (!current->mm)
122
return X86_BR_NONE;
123
124
/* may fail if text not present */
125
bytes_left = copy_from_user_nmi(buf, (void __user *)from,
126
MAX_INSN_SIZE);
127
bytes_read = MAX_INSN_SIZE - bytes_left;
128
if (!bytes_read)
129
return X86_BR_NONE;
130
131
addr = buf;
132
} else {
133
/*
134
* The LBR logs any address in the IP, even if the IP just
135
* faulted. This means userspace can control the from address.
136
* Ensure we don't blindly read any address by validating it is
137
* a known text address and not a vsyscall address.
138
*/
139
if (kernel_text_address(from) && !in_gate_area_no_mm(from)) {
140
addr = (void *)from;
141
/*
142
* Assume we can get the maximum possible size
143
* when grabbing kernel data. This is not
144
* _strictly_ true since we could possibly be
145
* executing up next to a memory hole, but
146
* it is very unlikely to be a problem.
147
*/
148
bytes_read = MAX_INSN_SIZE;
149
} else {
150
return X86_BR_NONE;
151
}
152
}
153
154
/*
155
* decoder needs to know the ABI especially
156
* on 64-bit systems running 32-bit apps
157
*/
158
#ifdef CONFIG_X86_64
159
is64 = kernel_ip((unsigned long)addr) || any_64bit_mode(current_pt_regs());
160
#endif
161
insn_init(&insn, addr, bytes_read, is64);
162
ret = decode_branch_type(&insn);
163
insn_offset = 0;
164
165
/* Check for the possibility of branch fusion */
166
while (fused && ret == X86_BR_NONE) {
167
/* Check for decoding errors */
168
if (insn_get_length(&insn) || !insn.length)
169
break;
170
171
insn_offset += insn.length;
172
bytes_read -= insn.length;
173
if (bytes_read < 0)
174
break;
175
176
insn_init(&insn, addr + insn_offset, bytes_read, is64);
177
ret = decode_branch_type(&insn);
178
}
179
180
if (offset)
181
*offset = insn_offset;
182
183
/*
184
* interrupts, traps, faults (and thus ring transition) may
185
* occur on any instructions. Thus, to classify them correctly,
186
* we need to first look at the from and to priv levels. If they
187
* are different and to is in the kernel, then it indicates
188
* a ring transition. If the from instruction is not a ring
189
* transition instr (syscall, systenter, int), then it means
190
* it was a irq, trap or fault.
191
*
192
* we have no way of detecting kernel to kernel faults.
193
*/
194
if (from_plm == X86_BR_USER && to_plm == X86_BR_KERNEL
195
&& ret != X86_BR_SYSCALL && ret != X86_BR_INT)
196
ret = X86_BR_IRQ;
197
198
/*
199
* branch priv level determined by target as
200
* is done by HW when LBR_SELECT is implemented
201
*/
202
if (ret != X86_BR_NONE)
203
ret |= to_plm;
204
205
return ret;
206
}
207
208
int branch_type(unsigned long from, unsigned long to, int abort)
209
{
210
return get_branch_type(from, to, abort, false, NULL);
211
}
212
213
int branch_type_fused(unsigned long from, unsigned long to, int abort,
214
int *offset)
215
{
216
return get_branch_type(from, to, abort, true, offset);
217
}
218
219
#define X86_BR_TYPE_MAP_MAX 16
220
221
static int branch_map[X86_BR_TYPE_MAP_MAX] = {
222
PERF_BR_CALL, /* X86_BR_CALL */
223
PERF_BR_RET, /* X86_BR_RET */
224
PERF_BR_SYSCALL, /* X86_BR_SYSCALL */
225
PERF_BR_SYSRET, /* X86_BR_SYSRET */
226
PERF_BR_UNKNOWN, /* X86_BR_INT */
227
PERF_BR_ERET, /* X86_BR_IRET */
228
PERF_BR_COND, /* X86_BR_JCC */
229
PERF_BR_UNCOND, /* X86_BR_JMP */
230
PERF_BR_IRQ, /* X86_BR_IRQ */
231
PERF_BR_IND_CALL, /* X86_BR_IND_CALL */
232
PERF_BR_UNKNOWN, /* X86_BR_ABORT */
233
PERF_BR_UNKNOWN, /* X86_BR_IN_TX */
234
PERF_BR_NO_TX, /* X86_BR_NO_TX */
235
PERF_BR_CALL, /* X86_BR_ZERO_CALL */
236
PERF_BR_UNKNOWN, /* X86_BR_CALL_STACK */
237
PERF_BR_IND, /* X86_BR_IND_JMP */
238
};
239
240
int common_branch_type(int type)
241
{
242
int i;
243
244
type >>= 2; /* skip X86_BR_USER and X86_BR_KERNEL */
245
246
if (type) {
247
i = __ffs(type);
248
if (i < X86_BR_TYPE_MAP_MAX)
249
return branch_map[i];
250
}
251
252
return PERF_BR_UNKNOWN;
253
}
254
255