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