1237fae79SAdrian Hunter /*
2237fae79SAdrian Hunter  * intel_pt_insn_decoder.c: Intel Processor Trace support
3237fae79SAdrian Hunter  * Copyright (c) 2013-2014, Intel Corporation.
4237fae79SAdrian Hunter  *
5237fae79SAdrian Hunter  * This program is free software; you can redistribute it and/or modify it
6237fae79SAdrian Hunter  * under the terms and conditions of the GNU General Public License,
7237fae79SAdrian Hunter  * version 2, as published by the Free Software Foundation.
8237fae79SAdrian Hunter  *
9237fae79SAdrian Hunter  * This program is distributed in the hope it will be useful, but WITHOUT
10237fae79SAdrian Hunter  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11237fae79SAdrian Hunter  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12237fae79SAdrian Hunter  * more details.
13237fae79SAdrian Hunter  *
14237fae79SAdrian Hunter  */
15237fae79SAdrian Hunter 
16237fae79SAdrian Hunter #include <stdio.h>
17237fae79SAdrian Hunter #include <string.h>
18237fae79SAdrian Hunter #include <endian.h>
19237fae79SAdrian Hunter #include <byteswap.h>
20237fae79SAdrian Hunter 
21237fae79SAdrian Hunter #include "event.h"
22237fae79SAdrian Hunter 
235839a550SAdrian Hunter #include "insn.h"
24237fae79SAdrian Hunter 
25237fae79SAdrian Hunter #include "inat.c"
26237fae79SAdrian Hunter #include "insn.c"
27237fae79SAdrian Hunter 
28237fae79SAdrian Hunter #include "intel-pt-insn-decoder.h"
29237fae79SAdrian Hunter 
30faaa8768SAndi Kleen #if INTEL_PT_INSN_BUF_SZ < MAX_INSN_SIZE || INTEL_PT_INSN_BUF_SZ > MAX_INSN
3132f98aabSAdrian Hunter #error Instruction buffer size too small
3232f98aabSAdrian Hunter #endif
3332f98aabSAdrian Hunter 
34237fae79SAdrian Hunter /* Based on branch_type() from perf_event_intel_lbr.c */
35237fae79SAdrian Hunter static void intel_pt_insn_decoder(struct insn *insn,
36237fae79SAdrian Hunter 				  struct intel_pt_insn *intel_pt_insn)
37237fae79SAdrian Hunter {
38237fae79SAdrian Hunter 	enum intel_pt_insn_op op = INTEL_PT_OP_OTHER;
39237fae79SAdrian Hunter 	enum intel_pt_insn_branch branch = INTEL_PT_BR_NO_BRANCH;
40237fae79SAdrian Hunter 	int ext;
41237fae79SAdrian Hunter 
42f1c4d1adSAdrian Hunter 	intel_pt_insn->rel = 0;
43f1c4d1adSAdrian Hunter 
44237fae79SAdrian Hunter 	if (insn_is_avx(insn)) {
45237fae79SAdrian Hunter 		intel_pt_insn->op = INTEL_PT_OP_OTHER;
46237fae79SAdrian Hunter 		intel_pt_insn->branch = INTEL_PT_BR_NO_BRANCH;
47237fae79SAdrian Hunter 		intel_pt_insn->length = insn->length;
48237fae79SAdrian Hunter 		return;
49237fae79SAdrian Hunter 	}
50237fae79SAdrian Hunter 
51237fae79SAdrian Hunter 	switch (insn->opcode.bytes[0]) {
52237fae79SAdrian Hunter 	case 0xf:
53237fae79SAdrian Hunter 		switch (insn->opcode.bytes[1]) {
54237fae79SAdrian Hunter 		case 0x05: /* syscall */
55237fae79SAdrian Hunter 		case 0x34: /* sysenter */
56237fae79SAdrian Hunter 			op = INTEL_PT_OP_SYSCALL;
57237fae79SAdrian Hunter 			branch = INTEL_PT_BR_INDIRECT;
58237fae79SAdrian Hunter 			break;
59237fae79SAdrian Hunter 		case 0x07: /* sysret */
60237fae79SAdrian Hunter 		case 0x35: /* sysexit */
61237fae79SAdrian Hunter 			op = INTEL_PT_OP_SYSRET;
62237fae79SAdrian Hunter 			branch = INTEL_PT_BR_INDIRECT;
63237fae79SAdrian Hunter 			break;
64237fae79SAdrian Hunter 		case 0x80 ... 0x8f: /* jcc */
65237fae79SAdrian Hunter 			op = INTEL_PT_OP_JCC;
66237fae79SAdrian Hunter 			branch = INTEL_PT_BR_CONDITIONAL;
67237fae79SAdrian Hunter 			break;
68237fae79SAdrian Hunter 		default:
69237fae79SAdrian Hunter 			break;
70237fae79SAdrian Hunter 		}
71237fae79SAdrian Hunter 		break;
72237fae79SAdrian Hunter 	case 0x70 ... 0x7f: /* jcc */
73237fae79SAdrian Hunter 		op = INTEL_PT_OP_JCC;
74237fae79SAdrian Hunter 		branch = INTEL_PT_BR_CONDITIONAL;
75237fae79SAdrian Hunter 		break;
76237fae79SAdrian Hunter 	case 0xc2: /* near ret */
77237fae79SAdrian Hunter 	case 0xc3: /* near ret */
78237fae79SAdrian Hunter 	case 0xca: /* far ret */
79237fae79SAdrian Hunter 	case 0xcb: /* far ret */
80237fae79SAdrian Hunter 		op = INTEL_PT_OP_RET;
81237fae79SAdrian Hunter 		branch = INTEL_PT_BR_INDIRECT;
82237fae79SAdrian Hunter 		break;
83237fae79SAdrian Hunter 	case 0xcf: /* iret */
84237fae79SAdrian Hunter 		op = INTEL_PT_OP_IRET;
85237fae79SAdrian Hunter 		branch = INTEL_PT_BR_INDIRECT;
86237fae79SAdrian Hunter 		break;
87237fae79SAdrian Hunter 	case 0xcc ... 0xce: /* int */
88237fae79SAdrian Hunter 		op = INTEL_PT_OP_INT;
89237fae79SAdrian Hunter 		branch = INTEL_PT_BR_INDIRECT;
90237fae79SAdrian Hunter 		break;
91237fae79SAdrian Hunter 	case 0xe8: /* call near rel */
92237fae79SAdrian Hunter 		op = INTEL_PT_OP_CALL;
93237fae79SAdrian Hunter 		branch = INTEL_PT_BR_UNCONDITIONAL;
94237fae79SAdrian Hunter 		break;
95237fae79SAdrian Hunter 	case 0x9a: /* call far absolute */
96237fae79SAdrian Hunter 		op = INTEL_PT_OP_CALL;
97237fae79SAdrian Hunter 		branch = INTEL_PT_BR_INDIRECT;
98237fae79SAdrian Hunter 		break;
99237fae79SAdrian Hunter 	case 0xe0 ... 0xe2: /* loop */
100237fae79SAdrian Hunter 		op = INTEL_PT_OP_LOOP;
101237fae79SAdrian Hunter 		branch = INTEL_PT_BR_CONDITIONAL;
102237fae79SAdrian Hunter 		break;
103237fae79SAdrian Hunter 	case 0xe3: /* jcc */
104237fae79SAdrian Hunter 		op = INTEL_PT_OP_JCC;
105237fae79SAdrian Hunter 		branch = INTEL_PT_BR_CONDITIONAL;
106237fae79SAdrian Hunter 		break;
107237fae79SAdrian Hunter 	case 0xe9: /* jmp */
108237fae79SAdrian Hunter 	case 0xeb: /* jmp */
109237fae79SAdrian Hunter 		op = INTEL_PT_OP_JMP;
110237fae79SAdrian Hunter 		branch = INTEL_PT_BR_UNCONDITIONAL;
111237fae79SAdrian Hunter 		break;
112237fae79SAdrian Hunter 	case 0xea: /* far jmp */
113237fae79SAdrian Hunter 		op = INTEL_PT_OP_JMP;
114237fae79SAdrian Hunter 		branch = INTEL_PT_BR_INDIRECT;
115237fae79SAdrian Hunter 		break;
116237fae79SAdrian Hunter 	case 0xff: /* call near absolute, call far absolute ind */
117237fae79SAdrian Hunter 		ext = (insn->modrm.bytes[0] >> 3) & 0x7;
118237fae79SAdrian Hunter 		switch (ext) {
119237fae79SAdrian Hunter 		case 2: /* near ind call */
120237fae79SAdrian Hunter 		case 3: /* far ind call */
121237fae79SAdrian Hunter 			op = INTEL_PT_OP_CALL;
122237fae79SAdrian Hunter 			branch = INTEL_PT_BR_INDIRECT;
123237fae79SAdrian Hunter 			break;
124237fae79SAdrian Hunter 		case 4:
125237fae79SAdrian Hunter 		case 5:
126237fae79SAdrian Hunter 			op = INTEL_PT_OP_JMP;
127237fae79SAdrian Hunter 			branch = INTEL_PT_BR_INDIRECT;
128237fae79SAdrian Hunter 			break;
129237fae79SAdrian Hunter 		default:
130237fae79SAdrian Hunter 			break;
131237fae79SAdrian Hunter 		}
132237fae79SAdrian Hunter 		break;
133237fae79SAdrian Hunter 	default:
134237fae79SAdrian Hunter 		break;
135237fae79SAdrian Hunter 	}
136237fae79SAdrian Hunter 
137237fae79SAdrian Hunter 	intel_pt_insn->op = op;
138237fae79SAdrian Hunter 	intel_pt_insn->branch = branch;
139237fae79SAdrian Hunter 	intel_pt_insn->length = insn->length;
140237fae79SAdrian Hunter 
141237fae79SAdrian Hunter 	if (branch == INTEL_PT_BR_CONDITIONAL ||
142237fae79SAdrian Hunter 	    branch == INTEL_PT_BR_UNCONDITIONAL) {
143237fae79SAdrian Hunter #if __BYTE_ORDER == __BIG_ENDIAN
144237fae79SAdrian Hunter 		switch (insn->immediate.nbytes) {
145237fae79SAdrian Hunter 		case 1:
146237fae79SAdrian Hunter 			intel_pt_insn->rel = insn->immediate.value;
147237fae79SAdrian Hunter 			break;
148237fae79SAdrian Hunter 		case 2:
149237fae79SAdrian Hunter 			intel_pt_insn->rel =
150237fae79SAdrian Hunter 					bswap_16((short)insn->immediate.value);
151237fae79SAdrian Hunter 			break;
152237fae79SAdrian Hunter 		case 4:
153237fae79SAdrian Hunter 			intel_pt_insn->rel = bswap_32(insn->immediate.value);
154237fae79SAdrian Hunter 			break;
15597db6206SAdrian Hunter 		default:
15697db6206SAdrian Hunter 			intel_pt_insn->rel = 0;
15797db6206SAdrian Hunter 			break;
158237fae79SAdrian Hunter 		}
159237fae79SAdrian Hunter #else
160237fae79SAdrian Hunter 		intel_pt_insn->rel = insn->immediate.value;
161237fae79SAdrian Hunter #endif
162237fae79SAdrian Hunter 	}
163237fae79SAdrian Hunter }
164237fae79SAdrian Hunter 
165237fae79SAdrian Hunter int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64,
166237fae79SAdrian Hunter 		      struct intel_pt_insn *intel_pt_insn)
167237fae79SAdrian Hunter {
168237fae79SAdrian Hunter 	struct insn insn;
169237fae79SAdrian Hunter 
170237fae79SAdrian Hunter 	insn_init(&insn, buf, len, x86_64);
171237fae79SAdrian Hunter 	insn_get_length(&insn);
172237fae79SAdrian Hunter 	if (!insn_complete(&insn) || insn.length > len)
173237fae79SAdrian Hunter 		return -1;
174237fae79SAdrian Hunter 	intel_pt_insn_decoder(&insn, intel_pt_insn);
17532f98aabSAdrian Hunter 	if (insn.length < INTEL_PT_INSN_BUF_SZ)
176237fae79SAdrian Hunter 		memcpy(intel_pt_insn->buf, buf, insn.length);
177237fae79SAdrian Hunter 	else
17832f98aabSAdrian Hunter 		memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_BUF_SZ);
179237fae79SAdrian Hunter 	return 0;
180237fae79SAdrian Hunter }
181237fae79SAdrian Hunter 
182237fae79SAdrian Hunter const char *branch_name[] = {
183237fae79SAdrian Hunter 	[INTEL_PT_OP_OTHER]	= "Other",
184237fae79SAdrian Hunter 	[INTEL_PT_OP_CALL]	= "Call",
185237fae79SAdrian Hunter 	[INTEL_PT_OP_RET]	= "Ret",
186237fae79SAdrian Hunter 	[INTEL_PT_OP_JCC]	= "Jcc",
187237fae79SAdrian Hunter 	[INTEL_PT_OP_JMP]	= "Jmp",
188237fae79SAdrian Hunter 	[INTEL_PT_OP_LOOP]	= "Loop",
189237fae79SAdrian Hunter 	[INTEL_PT_OP_IRET]	= "IRet",
190237fae79SAdrian Hunter 	[INTEL_PT_OP_INT]	= "Int",
191237fae79SAdrian Hunter 	[INTEL_PT_OP_SYSCALL]	= "Syscall",
192237fae79SAdrian Hunter 	[INTEL_PT_OP_SYSRET]	= "Sysret",
193237fae79SAdrian Hunter };
194237fae79SAdrian Hunter 
195237fae79SAdrian Hunter const char *intel_pt_insn_name(enum intel_pt_insn_op op)
196237fae79SAdrian Hunter {
197237fae79SAdrian Hunter 	return branch_name[op];
198237fae79SAdrian Hunter }
199237fae79SAdrian Hunter 
200237fae79SAdrian Hunter int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf,
201237fae79SAdrian Hunter 		       size_t buf_len)
202237fae79SAdrian Hunter {
203237fae79SAdrian Hunter 	switch (intel_pt_insn->branch) {
204237fae79SAdrian Hunter 	case INTEL_PT_BR_CONDITIONAL:
205237fae79SAdrian Hunter 	case INTEL_PT_BR_UNCONDITIONAL:
206237fae79SAdrian Hunter 		return snprintf(buf, buf_len, "%s %s%d",
207237fae79SAdrian Hunter 				intel_pt_insn_name(intel_pt_insn->op),
208237fae79SAdrian Hunter 				intel_pt_insn->rel > 0 ? "+" : "",
209237fae79SAdrian Hunter 				intel_pt_insn->rel);
210237fae79SAdrian Hunter 	case INTEL_PT_BR_NO_BRANCH:
211237fae79SAdrian Hunter 	case INTEL_PT_BR_INDIRECT:
212237fae79SAdrian Hunter 		return snprintf(buf, buf_len, "%s",
213237fae79SAdrian Hunter 				intel_pt_insn_name(intel_pt_insn->op));
214237fae79SAdrian Hunter 	default:
215237fae79SAdrian Hunter 		break;
216237fae79SAdrian Hunter 	}
217237fae79SAdrian Hunter 	return 0;
218237fae79SAdrian Hunter }
219237fae79SAdrian Hunter 
220237fae79SAdrian Hunter int intel_pt_insn_type(enum intel_pt_insn_op op)
221237fae79SAdrian Hunter {
222237fae79SAdrian Hunter 	switch (op) {
223237fae79SAdrian Hunter 	case INTEL_PT_OP_OTHER:
224237fae79SAdrian Hunter 		return 0;
225237fae79SAdrian Hunter 	case INTEL_PT_OP_CALL:
226237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL;
227237fae79SAdrian Hunter 	case INTEL_PT_OP_RET:
228237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN;
229237fae79SAdrian Hunter 	case INTEL_PT_OP_JCC:
230237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
231237fae79SAdrian Hunter 	case INTEL_PT_OP_JMP:
232237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH;
233237fae79SAdrian Hunter 	case INTEL_PT_OP_LOOP:
234237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
235237fae79SAdrian Hunter 	case INTEL_PT_OP_IRET:
236237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
237237fae79SAdrian Hunter 		       PERF_IP_FLAG_INTERRUPT;
238237fae79SAdrian Hunter 	case INTEL_PT_OP_INT:
239237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
240237fae79SAdrian Hunter 		       PERF_IP_FLAG_INTERRUPT;
241237fae79SAdrian Hunter 	case INTEL_PT_OP_SYSCALL:
242237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
243237fae79SAdrian Hunter 		       PERF_IP_FLAG_SYSCALLRET;
244237fae79SAdrian Hunter 	case INTEL_PT_OP_SYSRET:
245237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
246237fae79SAdrian Hunter 		       PERF_IP_FLAG_SYSCALLRET;
247237fae79SAdrian Hunter 	default:
248237fae79SAdrian Hunter 		return 0;
249237fae79SAdrian Hunter 	}
250237fae79SAdrian Hunter }
251