~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/x86/events/utils.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php