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