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

TOMOYO Linux Cross Reference
Linux/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.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-only
  2 /*
  3  * intel_pt_insn_decoder.c: Intel Processor Trace support
  4  * Copyright (c) 2013-2014, Intel Corporation.
  5  */
  6 
  7 #include <linux/kernel.h>
  8 #include <stdio.h>
  9 #include <string.h>
 10 #include <endian.h>
 11 #include <byteswap.h>
 12 #include "../../../arch/x86/include/asm/insn.h"
 13 
 14 #include "../../../arch/x86/lib/inat.c"
 15 #include "../../../arch/x86/lib/insn.c"
 16 
 17 #include "event.h"
 18 
 19 #include "intel-pt-insn-decoder.h"
 20 #include "dump-insn.h"
 21 #include "util/sample.h"
 22 
 23 #if INTEL_PT_INSN_BUF_SZ < MAX_INSN_SIZE || INTEL_PT_INSN_BUF_SZ > MAX_INSN
 24 #error Instruction buffer size too small
 25 #endif
 26 
 27 /* Based on branch_type() from arch/x86/events/intel/lbr.c */
 28 static void intel_pt_insn_decoder(struct insn *insn,
 29                                   struct intel_pt_insn *intel_pt_insn)
 30 {
 31         enum intel_pt_insn_op op = INTEL_PT_OP_OTHER;
 32         enum intel_pt_insn_branch branch = INTEL_PT_BR_NO_BRANCH;
 33         int ext;
 34 
 35         intel_pt_insn->rel = 0;
 36         intel_pt_insn->emulated_ptwrite = false;
 37 
 38         if (insn_is_avx(insn)) {
 39                 intel_pt_insn->op = INTEL_PT_OP_OTHER;
 40                 intel_pt_insn->branch = INTEL_PT_BR_NO_BRANCH;
 41                 intel_pt_insn->length = insn->length;
 42                 return;
 43         }
 44 
 45         switch (insn->opcode.bytes[0]) {
 46         case 0xf:
 47                 switch (insn->opcode.bytes[1]) {
 48                 case 0x01:
 49                         switch (insn->modrm.bytes[0]) {
 50                         case 0xc2: /* vmlaunch */
 51                         case 0xc3: /* vmresume */
 52                                 op = INTEL_PT_OP_VMENTRY;
 53                                 branch = INTEL_PT_BR_INDIRECT;
 54                                 break;
 55                         case 0xca:
 56                                 switch (insn->prefixes.bytes[3]) {
 57                                 case 0xf2: /* erets */
 58                                         op = INTEL_PT_OP_ERETS;
 59                                         branch = INTEL_PT_BR_INDIRECT;
 60                                         break;
 61                                 case 0xf3: /* eretu */
 62                                         op = INTEL_PT_OP_ERETU;
 63                                         branch = INTEL_PT_BR_INDIRECT;
 64                                         break;
 65                                 default:
 66                                         break;
 67                                 }
 68                                 break;
 69                         default:
 70                                 break;
 71                         }
 72                         break;
 73                 case 0x05: /* syscall */
 74                 case 0x34: /* sysenter */
 75                         op = INTEL_PT_OP_SYSCALL;
 76                         branch = INTEL_PT_BR_INDIRECT;
 77                         break;
 78                 case 0x07: /* sysret */
 79                 case 0x35: /* sysexit */
 80                         op = INTEL_PT_OP_SYSRET;
 81                         branch = INTEL_PT_BR_INDIRECT;
 82                         break;
 83                 case 0x80 ... 0x8f: /* jcc */
 84                         op = INTEL_PT_OP_JCC;
 85                         branch = INTEL_PT_BR_CONDITIONAL;
 86                         break;
 87                 default:
 88                         break;
 89                 }
 90                 break;
 91         case 0x70 ... 0x7f: /* jcc */
 92                 op = INTEL_PT_OP_JCC;
 93                 branch = INTEL_PT_BR_CONDITIONAL;
 94                 break;
 95         case 0xa1:
 96                 if (insn_is_rex2(insn)) { /* jmpabs */
 97                         intel_pt_insn->op = INTEL_PT_OP_JMP;
 98                         /* jmpabs causes a TIP packet like an indirect branch */
 99                         intel_pt_insn->branch = INTEL_PT_BR_INDIRECT;
100                         intel_pt_insn->length = insn->length;
101                         return;
102                 }
103                 break;
104         case 0xc2: /* near ret */
105         case 0xc3: /* near ret */
106         case 0xca: /* far ret */
107         case 0xcb: /* far ret */
108                 op = INTEL_PT_OP_RET;
109                 branch = INTEL_PT_BR_INDIRECT;
110                 break;
111         case 0xcf: /* iret */
112                 op = INTEL_PT_OP_IRET;
113                 branch = INTEL_PT_BR_INDIRECT;
114                 break;
115         case 0xcc ... 0xce: /* int */
116                 op = INTEL_PT_OP_INT;
117                 branch = INTEL_PT_BR_INDIRECT;
118                 break;
119         case 0xe8: /* call near rel */
120                 op = INTEL_PT_OP_CALL;
121                 branch = INTEL_PT_BR_UNCONDITIONAL;
122                 break;
123         case 0x9a: /* call far absolute */
124                 op = INTEL_PT_OP_CALL;
125                 branch = INTEL_PT_BR_INDIRECT;
126                 break;
127         case 0xe0 ... 0xe2: /* loop */
128                 op = INTEL_PT_OP_LOOP;
129                 branch = INTEL_PT_BR_CONDITIONAL;
130                 break;
131         case 0xe3: /* jcc */
132                 op = INTEL_PT_OP_JCC;
133                 branch = INTEL_PT_BR_CONDITIONAL;
134                 break;
135         case 0xe9: /* jmp */
136         case 0xeb: /* jmp */
137                 op = INTEL_PT_OP_JMP;
138                 branch = INTEL_PT_BR_UNCONDITIONAL;
139                 break;
140         case 0xea: /* far jmp */
141                 op = INTEL_PT_OP_JMP;
142                 branch = INTEL_PT_BR_INDIRECT;
143                 break;
144         case 0xff: /* call near absolute, call far absolute ind */
145                 ext = (insn->modrm.bytes[0] >> 3) & 0x7;
146                 switch (ext) {
147                 case 2: /* near ind call */
148                 case 3: /* far ind call */
149                         op = INTEL_PT_OP_CALL;
150                         branch = INTEL_PT_BR_INDIRECT;
151                         break;
152                 case 4:
153                 case 5:
154                         op = INTEL_PT_OP_JMP;
155                         branch = INTEL_PT_BR_INDIRECT;
156                         break;
157                 default:
158                         break;
159                 }
160                 break;
161         default:
162                 break;
163         }
164 
165         intel_pt_insn->op = op;
166         intel_pt_insn->branch = branch;
167         intel_pt_insn->length = insn->length;
168 
169         if (branch == INTEL_PT_BR_CONDITIONAL ||
170             branch == INTEL_PT_BR_UNCONDITIONAL) {
171 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
172                 switch (insn->immediate.nbytes) {
173                 case 1:
174                         intel_pt_insn->rel = insn->immediate.value;
175                         break;
176                 case 2:
177                         intel_pt_insn->rel =
178                                         bswap_16((short)insn->immediate.value);
179                         break;
180                 case 4:
181                         intel_pt_insn->rel = bswap_32(insn->immediate.value);
182                         break;
183                 default:
184                         intel_pt_insn->rel = 0;
185                         break;
186                 }
187 #else
188                 intel_pt_insn->rel = insn->immediate.value;
189 #endif
190         }
191 }
192 
193 int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64,
194                       struct intel_pt_insn *intel_pt_insn)
195 {
196         struct insn insn;
197         int ret;
198 
199         ret = insn_decode(&insn, buf, len,
200                           x86_64 ? INSN_MODE_64 : INSN_MODE_32);
201         if (ret < 0 || insn.length > len)
202                 return -1;
203 
204         intel_pt_insn_decoder(&insn, intel_pt_insn);
205         if (insn.length < INTEL_PT_INSN_BUF_SZ)
206                 memcpy(intel_pt_insn->buf, buf, insn.length);
207         else
208                 memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_BUF_SZ);
209         return 0;
210 }
211 
212 int arch_is_branch(const unsigned char *buf, size_t len, int x86_64)
213 {
214         struct intel_pt_insn in;
215         if (intel_pt_get_insn(buf, len, x86_64, &in) < 0)
216                 return -1;
217         return in.branch != INTEL_PT_BR_NO_BRANCH;
218 }
219 
220 const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused,
221                       u8 *inbuf, int inlen, int *lenp)
222 {
223         struct insn insn;
224         int n, i, ret;
225         int left;
226 
227         ret = insn_decode(&insn, inbuf, inlen,
228                           x->is64bit ? INSN_MODE_64 : INSN_MODE_32);
229 
230         if (ret < 0 || insn.length > inlen)
231                 return "<bad>";
232         if (lenp)
233                 *lenp = insn.length;
234         left = sizeof(x->out);
235         n = snprintf(x->out, left, "insn: ");
236         left -= n;
237         for (i = 0; i < insn.length; i++) {
238                 n += snprintf(x->out + n, left, "%02x ", inbuf[i]);
239                 left -= n;
240         }
241         return x->out;
242 }
243 
244 const char *branch_name[] = {
245         [INTEL_PT_OP_OTHER]     = "Other",
246         [INTEL_PT_OP_CALL]      = "Call",
247         [INTEL_PT_OP_RET]       = "Ret",
248         [INTEL_PT_OP_JCC]       = "Jcc",
249         [INTEL_PT_OP_JMP]       = "Jmp",
250         [INTEL_PT_OP_LOOP]      = "Loop",
251         [INTEL_PT_OP_IRET]      = "IRet",
252         [INTEL_PT_OP_INT]       = "Int",
253         [INTEL_PT_OP_SYSCALL]   = "Syscall",
254         [INTEL_PT_OP_SYSRET]    = "Sysret",
255         [INTEL_PT_OP_VMENTRY]   = "VMentry",
256         [INTEL_PT_OP_ERETS]     = "Erets",
257         [INTEL_PT_OP_ERETU]     = "Eretu",
258 };
259 
260 const char *intel_pt_insn_name(enum intel_pt_insn_op op)
261 {
262         return branch_name[op];
263 }
264 
265 int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf,
266                        size_t buf_len)
267 {
268         switch (intel_pt_insn->branch) {
269         case INTEL_PT_BR_CONDITIONAL:
270         case INTEL_PT_BR_UNCONDITIONAL:
271                 return snprintf(buf, buf_len, "%s %s%d",
272                                 intel_pt_insn_name(intel_pt_insn->op),
273                                 intel_pt_insn->rel > 0 ? "+" : "",
274                                 intel_pt_insn->rel);
275         case INTEL_PT_BR_NO_BRANCH:
276         case INTEL_PT_BR_INDIRECT:
277                 return snprintf(buf, buf_len, "%s",
278                                 intel_pt_insn_name(intel_pt_insn->op));
279         default:
280                 break;
281         }
282         return 0;
283 }
284 
285 int intel_pt_insn_type(enum intel_pt_insn_op op)
286 {
287         switch (op) {
288         case INTEL_PT_OP_OTHER:
289                 return 0;
290         case INTEL_PT_OP_CALL:
291                 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL;
292         case INTEL_PT_OP_RET:
293                 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN;
294         case INTEL_PT_OP_JCC:
295                 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
296         case INTEL_PT_OP_JMP:
297                 return PERF_IP_FLAG_BRANCH;
298         case INTEL_PT_OP_LOOP:
299                 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
300         case INTEL_PT_OP_IRET:
301         case INTEL_PT_OP_ERETS:
302         case INTEL_PT_OP_ERETU:
303                 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
304                        PERF_IP_FLAG_INTERRUPT;
305         case INTEL_PT_OP_INT:
306                 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
307                        PERF_IP_FLAG_INTERRUPT;
308         case INTEL_PT_OP_SYSCALL:
309                 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
310                        PERF_IP_FLAG_SYSCALLRET;
311         case INTEL_PT_OP_SYSRET:
312                 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
313                        PERF_IP_FLAG_SYSCALLRET;
314         case INTEL_PT_OP_VMENTRY:
315                 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
316                        PERF_IP_FLAG_VMENTRY;
317         default:
318                 return 0;
319         }
320 }
321 

~ [ 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