12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2237fae79SAdrian Hunter /*
3237fae79SAdrian Hunter * intel_pt_insn_decoder.c: Intel Processor Trace support
4237fae79SAdrian Hunter * Copyright (c) 2013-2014, Intel Corporation.
5237fae79SAdrian Hunter */
6237fae79SAdrian Hunter
700a26390SJosh Poimboeuf #include <linux/kernel.h>
8237fae79SAdrian Hunter #include <stdio.h>
9237fae79SAdrian Hunter #include <string.h>
10237fae79SAdrian Hunter #include <endian.h>
11237fae79SAdrian Hunter #include <byteswap.h>
1200a26390SJosh Poimboeuf #include "../../../arch/x86/include/asm/insn.h"
1300a26390SJosh Poimboeuf
1400a26390SJosh Poimboeuf #include "../../../arch/x86/lib/inat.c"
1500a26390SJosh Poimboeuf #include "../../../arch/x86/lib/insn.c"
16237fae79SAdrian Hunter
17237fae79SAdrian Hunter #include "event.h"
18237fae79SAdrian Hunter
19237fae79SAdrian Hunter #include "intel-pt-insn-decoder.h"
2048d02a1dSAndi Kleen #include "dump-insn.h"
219823147dSArnaldo Carvalho de Melo #include "util/sample.h"
22237fae79SAdrian Hunter
23faaa8768SAndi Kleen #if INTEL_PT_INSN_BUF_SZ < MAX_INSN_SIZE || INTEL_PT_INSN_BUF_SZ > MAX_INSN
2432f98aabSAdrian Hunter #error Instruction buffer size too small
2532f98aabSAdrian Hunter #endif
2632f98aabSAdrian Hunter
27940b2f2fSBorislav Petkov /* Based on branch_type() from arch/x86/events/intel/lbr.c */
intel_pt_insn_decoder(struct insn * insn,struct intel_pt_insn * intel_pt_insn)28237fae79SAdrian Hunter static void intel_pt_insn_decoder(struct insn *insn,
29237fae79SAdrian Hunter struct intel_pt_insn *intel_pt_insn)
30237fae79SAdrian Hunter {
31237fae79SAdrian Hunter enum intel_pt_insn_op op = INTEL_PT_OP_OTHER;
32237fae79SAdrian Hunter enum intel_pt_insn_branch branch = INTEL_PT_BR_NO_BRANCH;
33237fae79SAdrian Hunter int ext;
34237fae79SAdrian Hunter
35f1c4d1adSAdrian Hunter intel_pt_insn->rel = 0;
36d7015e50SAdrian Hunter intel_pt_insn->emulated_ptwrite = false;
37f1c4d1adSAdrian Hunter
38237fae79SAdrian Hunter if (insn_is_avx(insn)) {
39237fae79SAdrian Hunter intel_pt_insn->op = INTEL_PT_OP_OTHER;
40237fae79SAdrian Hunter intel_pt_insn->branch = INTEL_PT_BR_NO_BRANCH;
41237fae79SAdrian Hunter intel_pt_insn->length = insn->length;
42237fae79SAdrian Hunter return;
43237fae79SAdrian Hunter }
44237fae79SAdrian Hunter
45237fae79SAdrian Hunter switch (insn->opcode.bytes[0]) {
46237fae79SAdrian Hunter case 0xf:
47237fae79SAdrian Hunter switch (insn->opcode.bytes[1]) {
48b7ecc2d7SAdrian Hunter case 0x01:
49b7ecc2d7SAdrian Hunter switch (insn->modrm.bytes[0]) {
50b7ecc2d7SAdrian Hunter case 0xc2: /* vmlaunch */
51b7ecc2d7SAdrian Hunter case 0xc3: /* vmresume */
52b7ecc2d7SAdrian Hunter op = INTEL_PT_OP_VMENTRY;
53b7ecc2d7SAdrian Hunter branch = INTEL_PT_BR_INDIRECT;
54b7ecc2d7SAdrian Hunter break;
55*052072f6SAdrian Hunter case 0xca:
56*052072f6SAdrian Hunter switch (insn->prefixes.bytes[3]) {
57*052072f6SAdrian Hunter case 0xf2: /* erets */
58*052072f6SAdrian Hunter op = INTEL_PT_OP_ERETS;
59*052072f6SAdrian Hunter branch = INTEL_PT_BR_INDIRECT;
60*052072f6SAdrian Hunter break;
61*052072f6SAdrian Hunter case 0xf3: /* eretu */
62*052072f6SAdrian Hunter op = INTEL_PT_OP_ERETU;
63*052072f6SAdrian Hunter branch = INTEL_PT_BR_INDIRECT;
64*052072f6SAdrian Hunter break;
65*052072f6SAdrian Hunter default:
66*052072f6SAdrian Hunter break;
67*052072f6SAdrian Hunter }
68*052072f6SAdrian Hunter break;
69b7ecc2d7SAdrian Hunter default:
70b7ecc2d7SAdrian Hunter break;
71b7ecc2d7SAdrian Hunter }
72b7ecc2d7SAdrian Hunter break;
73237fae79SAdrian Hunter case 0x05: /* syscall */
74237fae79SAdrian Hunter case 0x34: /* sysenter */
75237fae79SAdrian Hunter op = INTEL_PT_OP_SYSCALL;
76237fae79SAdrian Hunter branch = INTEL_PT_BR_INDIRECT;
77237fae79SAdrian Hunter break;
78237fae79SAdrian Hunter case 0x07: /* sysret */
79237fae79SAdrian Hunter case 0x35: /* sysexit */
80237fae79SAdrian Hunter op = INTEL_PT_OP_SYSRET;
81237fae79SAdrian Hunter branch = INTEL_PT_BR_INDIRECT;
82237fae79SAdrian Hunter break;
83237fae79SAdrian Hunter case 0x80 ... 0x8f: /* jcc */
84237fae79SAdrian Hunter op = INTEL_PT_OP_JCC;
85237fae79SAdrian Hunter branch = INTEL_PT_BR_CONDITIONAL;
86237fae79SAdrian Hunter break;
87237fae79SAdrian Hunter default:
88237fae79SAdrian Hunter break;
89237fae79SAdrian Hunter }
90237fae79SAdrian Hunter break;
91237fae79SAdrian Hunter case 0x70 ... 0x7f: /* jcc */
92237fae79SAdrian Hunter op = INTEL_PT_OP_JCC;
93237fae79SAdrian Hunter branch = INTEL_PT_BR_CONDITIONAL;
94237fae79SAdrian Hunter break;
95237fae79SAdrian Hunter case 0xc2: /* near ret */
96237fae79SAdrian Hunter case 0xc3: /* near ret */
97237fae79SAdrian Hunter case 0xca: /* far ret */
98237fae79SAdrian Hunter case 0xcb: /* far ret */
99237fae79SAdrian Hunter op = INTEL_PT_OP_RET;
100237fae79SAdrian Hunter branch = INTEL_PT_BR_INDIRECT;
101237fae79SAdrian Hunter break;
102237fae79SAdrian Hunter case 0xcf: /* iret */
103237fae79SAdrian Hunter op = INTEL_PT_OP_IRET;
104237fae79SAdrian Hunter branch = INTEL_PT_BR_INDIRECT;
105237fae79SAdrian Hunter break;
106237fae79SAdrian Hunter case 0xcc ... 0xce: /* int */
107237fae79SAdrian Hunter op = INTEL_PT_OP_INT;
108237fae79SAdrian Hunter branch = INTEL_PT_BR_INDIRECT;
109237fae79SAdrian Hunter break;
110237fae79SAdrian Hunter case 0xe8: /* call near rel */
111237fae79SAdrian Hunter op = INTEL_PT_OP_CALL;
112237fae79SAdrian Hunter branch = INTEL_PT_BR_UNCONDITIONAL;
113237fae79SAdrian Hunter break;
114237fae79SAdrian Hunter case 0x9a: /* call far absolute */
115237fae79SAdrian Hunter op = INTEL_PT_OP_CALL;
116237fae79SAdrian Hunter branch = INTEL_PT_BR_INDIRECT;
117237fae79SAdrian Hunter break;
118237fae79SAdrian Hunter case 0xe0 ... 0xe2: /* loop */
119237fae79SAdrian Hunter op = INTEL_PT_OP_LOOP;
120237fae79SAdrian Hunter branch = INTEL_PT_BR_CONDITIONAL;
121237fae79SAdrian Hunter break;
122237fae79SAdrian Hunter case 0xe3: /* jcc */
123237fae79SAdrian Hunter op = INTEL_PT_OP_JCC;
124237fae79SAdrian Hunter branch = INTEL_PT_BR_CONDITIONAL;
125237fae79SAdrian Hunter break;
126237fae79SAdrian Hunter case 0xe9: /* jmp */
127237fae79SAdrian Hunter case 0xeb: /* jmp */
128237fae79SAdrian Hunter op = INTEL_PT_OP_JMP;
129237fae79SAdrian Hunter branch = INTEL_PT_BR_UNCONDITIONAL;
130237fae79SAdrian Hunter break;
131237fae79SAdrian Hunter case 0xea: /* far jmp */
132237fae79SAdrian Hunter op = INTEL_PT_OP_JMP;
133237fae79SAdrian Hunter branch = INTEL_PT_BR_INDIRECT;
134237fae79SAdrian Hunter break;
135237fae79SAdrian Hunter case 0xff: /* call near absolute, call far absolute ind */
136237fae79SAdrian Hunter ext = (insn->modrm.bytes[0] >> 3) & 0x7;
137237fae79SAdrian Hunter switch (ext) {
138237fae79SAdrian Hunter case 2: /* near ind call */
139237fae79SAdrian Hunter case 3: /* far ind call */
140237fae79SAdrian Hunter op = INTEL_PT_OP_CALL;
141237fae79SAdrian Hunter branch = INTEL_PT_BR_INDIRECT;
142237fae79SAdrian Hunter break;
143237fae79SAdrian Hunter case 4:
144237fae79SAdrian Hunter case 5:
145237fae79SAdrian Hunter op = INTEL_PT_OP_JMP;
146237fae79SAdrian Hunter branch = INTEL_PT_BR_INDIRECT;
147237fae79SAdrian Hunter break;
148237fae79SAdrian Hunter default:
149237fae79SAdrian Hunter break;
150237fae79SAdrian Hunter }
151237fae79SAdrian Hunter break;
152237fae79SAdrian Hunter default:
153237fae79SAdrian Hunter break;
154237fae79SAdrian Hunter }
155237fae79SAdrian Hunter
156237fae79SAdrian Hunter intel_pt_insn->op = op;
157237fae79SAdrian Hunter intel_pt_insn->branch = branch;
158237fae79SAdrian Hunter intel_pt_insn->length = insn->length;
159237fae79SAdrian Hunter
160237fae79SAdrian Hunter if (branch == INTEL_PT_BR_CONDITIONAL ||
161237fae79SAdrian Hunter branch == INTEL_PT_BR_UNCONDITIONAL) {
1624e88118cSIlya Leoshkevich #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
163237fae79SAdrian Hunter switch (insn->immediate.nbytes) {
164237fae79SAdrian Hunter case 1:
165237fae79SAdrian Hunter intel_pt_insn->rel = insn->immediate.value;
166237fae79SAdrian Hunter break;
167237fae79SAdrian Hunter case 2:
168237fae79SAdrian Hunter intel_pt_insn->rel =
169237fae79SAdrian Hunter bswap_16((short)insn->immediate.value);
170237fae79SAdrian Hunter break;
171237fae79SAdrian Hunter case 4:
172237fae79SAdrian Hunter intel_pt_insn->rel = bswap_32(insn->immediate.value);
173237fae79SAdrian Hunter break;
17497db6206SAdrian Hunter default:
17597db6206SAdrian Hunter intel_pt_insn->rel = 0;
17697db6206SAdrian Hunter break;
177237fae79SAdrian Hunter }
178237fae79SAdrian Hunter #else
179237fae79SAdrian Hunter intel_pt_insn->rel = insn->immediate.value;
180237fae79SAdrian Hunter #endif
181237fae79SAdrian Hunter }
182237fae79SAdrian Hunter }
183237fae79SAdrian Hunter
intel_pt_get_insn(const unsigned char * buf,size_t len,int x86_64,struct intel_pt_insn * intel_pt_insn)184237fae79SAdrian Hunter int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64,
185237fae79SAdrian Hunter struct intel_pt_insn *intel_pt_insn)
186237fae79SAdrian Hunter {
187237fae79SAdrian Hunter struct insn insn;
18862660b0fSBorislav Petkov int ret;
189237fae79SAdrian Hunter
19062660b0fSBorislav Petkov ret = insn_decode(&insn, buf, len,
19162660b0fSBorislav Petkov x86_64 ? INSN_MODE_64 : INSN_MODE_32);
19262660b0fSBorislav Petkov if (ret < 0 || insn.length > len)
193237fae79SAdrian Hunter return -1;
19462660b0fSBorislav Petkov
195237fae79SAdrian Hunter intel_pt_insn_decoder(&insn, intel_pt_insn);
19632f98aabSAdrian Hunter if (insn.length < INTEL_PT_INSN_BUF_SZ)
197237fae79SAdrian Hunter memcpy(intel_pt_insn->buf, buf, insn.length);
198237fae79SAdrian Hunter else
19932f98aabSAdrian Hunter memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_BUF_SZ);
200237fae79SAdrian Hunter return 0;
201237fae79SAdrian Hunter }
202237fae79SAdrian Hunter
arch_is_branch(const unsigned char * buf,size_t len,int x86_64)20361f61159SAndi Kleen int arch_is_branch(const unsigned char *buf, size_t len, int x86_64)
20461f61159SAndi Kleen {
20561f61159SAndi Kleen struct intel_pt_insn in;
20661f61159SAndi Kleen if (intel_pt_get_insn(buf, len, x86_64, &in) < 0)
20761f61159SAndi Kleen return -1;
20861f61159SAndi Kleen return in.branch != INTEL_PT_BR_NO_BRANCH;
20961f61159SAndi Kleen }
21061f61159SAndi Kleen
dump_insn(struct perf_insn * x,uint64_t ip __maybe_unused,u8 * inbuf,int inlen,int * lenp)21148d02a1dSAndi Kleen const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused,
21248d02a1dSAndi Kleen u8 *inbuf, int inlen, int *lenp)
21348d02a1dSAndi Kleen {
21448d02a1dSAndi Kleen struct insn insn;
21562660b0fSBorislav Petkov int n, i, ret;
21648d02a1dSAndi Kleen int left;
21748d02a1dSAndi Kleen
21862660b0fSBorislav Petkov ret = insn_decode(&insn, inbuf, inlen,
21962660b0fSBorislav Petkov x->is64bit ? INSN_MODE_64 : INSN_MODE_32);
22062660b0fSBorislav Petkov
22162660b0fSBorislav Petkov if (ret < 0 || insn.length > inlen)
22248d02a1dSAndi Kleen return "<bad>";
22348d02a1dSAndi Kleen if (lenp)
22448d02a1dSAndi Kleen *lenp = insn.length;
22548d02a1dSAndi Kleen left = sizeof(x->out);
22648d02a1dSAndi Kleen n = snprintf(x->out, left, "insn: ");
22748d02a1dSAndi Kleen left -= n;
22848d02a1dSAndi Kleen for (i = 0; i < insn.length; i++) {
22948d02a1dSAndi Kleen n += snprintf(x->out + n, left, "%02x ", inbuf[i]);
23048d02a1dSAndi Kleen left -= n;
23148d02a1dSAndi Kleen }
23248d02a1dSAndi Kleen return x->out;
23348d02a1dSAndi Kleen }
23448d02a1dSAndi Kleen
235237fae79SAdrian Hunter const char *branch_name[] = {
236237fae79SAdrian Hunter [INTEL_PT_OP_OTHER] = "Other",
237237fae79SAdrian Hunter [INTEL_PT_OP_CALL] = "Call",
238237fae79SAdrian Hunter [INTEL_PT_OP_RET] = "Ret",
239237fae79SAdrian Hunter [INTEL_PT_OP_JCC] = "Jcc",
240237fae79SAdrian Hunter [INTEL_PT_OP_JMP] = "Jmp",
241237fae79SAdrian Hunter [INTEL_PT_OP_LOOP] = "Loop",
242237fae79SAdrian Hunter [INTEL_PT_OP_IRET] = "IRet",
243237fae79SAdrian Hunter [INTEL_PT_OP_INT] = "Int",
244237fae79SAdrian Hunter [INTEL_PT_OP_SYSCALL] = "Syscall",
245237fae79SAdrian Hunter [INTEL_PT_OP_SYSRET] = "Sysret",
246b7ecc2d7SAdrian Hunter [INTEL_PT_OP_VMENTRY] = "VMentry",
247*052072f6SAdrian Hunter [INTEL_PT_OP_ERETS] = "Erets",
248*052072f6SAdrian Hunter [INTEL_PT_OP_ERETU] = "Eretu",
249237fae79SAdrian Hunter };
250237fae79SAdrian Hunter
intel_pt_insn_name(enum intel_pt_insn_op op)251237fae79SAdrian Hunter const char *intel_pt_insn_name(enum intel_pt_insn_op op)
252237fae79SAdrian Hunter {
253237fae79SAdrian Hunter return branch_name[op];
254237fae79SAdrian Hunter }
255237fae79SAdrian Hunter
intel_pt_insn_desc(const struct intel_pt_insn * intel_pt_insn,char * buf,size_t buf_len)256237fae79SAdrian Hunter int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf,
257237fae79SAdrian Hunter size_t buf_len)
258237fae79SAdrian Hunter {
259237fae79SAdrian Hunter switch (intel_pt_insn->branch) {
260237fae79SAdrian Hunter case INTEL_PT_BR_CONDITIONAL:
261237fae79SAdrian Hunter case INTEL_PT_BR_UNCONDITIONAL:
262237fae79SAdrian Hunter return snprintf(buf, buf_len, "%s %s%d",
263237fae79SAdrian Hunter intel_pt_insn_name(intel_pt_insn->op),
264237fae79SAdrian Hunter intel_pt_insn->rel > 0 ? "+" : "",
265237fae79SAdrian Hunter intel_pt_insn->rel);
266237fae79SAdrian Hunter case INTEL_PT_BR_NO_BRANCH:
267237fae79SAdrian Hunter case INTEL_PT_BR_INDIRECT:
268237fae79SAdrian Hunter return snprintf(buf, buf_len, "%s",
269237fae79SAdrian Hunter intel_pt_insn_name(intel_pt_insn->op));
270237fae79SAdrian Hunter default:
271237fae79SAdrian Hunter break;
272237fae79SAdrian Hunter }
273237fae79SAdrian Hunter return 0;
274237fae79SAdrian Hunter }
275237fae79SAdrian Hunter
intel_pt_insn_type(enum intel_pt_insn_op op)276237fae79SAdrian Hunter int intel_pt_insn_type(enum intel_pt_insn_op op)
277237fae79SAdrian Hunter {
278237fae79SAdrian Hunter switch (op) {
279237fae79SAdrian Hunter case INTEL_PT_OP_OTHER:
280237fae79SAdrian Hunter return 0;
281237fae79SAdrian Hunter case INTEL_PT_OP_CALL:
282237fae79SAdrian Hunter return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL;
283237fae79SAdrian Hunter case INTEL_PT_OP_RET:
284237fae79SAdrian Hunter return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN;
285237fae79SAdrian Hunter case INTEL_PT_OP_JCC:
286237fae79SAdrian Hunter return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
287237fae79SAdrian Hunter case INTEL_PT_OP_JMP:
288237fae79SAdrian Hunter return PERF_IP_FLAG_BRANCH;
289237fae79SAdrian Hunter case INTEL_PT_OP_LOOP:
290237fae79SAdrian Hunter return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
291237fae79SAdrian Hunter case INTEL_PT_OP_IRET:
292*052072f6SAdrian Hunter case INTEL_PT_OP_ERETS:
293*052072f6SAdrian Hunter case INTEL_PT_OP_ERETU:
294237fae79SAdrian Hunter return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
295237fae79SAdrian Hunter PERF_IP_FLAG_INTERRUPT;
296237fae79SAdrian Hunter case INTEL_PT_OP_INT:
297237fae79SAdrian Hunter return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
298237fae79SAdrian Hunter PERF_IP_FLAG_INTERRUPT;
299237fae79SAdrian Hunter case INTEL_PT_OP_SYSCALL:
300237fae79SAdrian Hunter return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
301237fae79SAdrian Hunter PERF_IP_FLAG_SYSCALLRET;
302237fae79SAdrian Hunter case INTEL_PT_OP_SYSRET:
303237fae79SAdrian Hunter return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
304237fae79SAdrian Hunter PERF_IP_FLAG_SYSCALLRET;
305b7ecc2d7SAdrian Hunter case INTEL_PT_OP_VMENTRY:
306b7ecc2d7SAdrian Hunter return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
307b7ecc2d7SAdrian Hunter PERF_IP_FLAG_VMENTRY;
308237fae79SAdrian Hunter default:
309237fae79SAdrian Hunter return 0;
310237fae79SAdrian Hunter }
311237fae79SAdrian Hunter }
312