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 
30237fae79SAdrian Hunter /* Based on branch_type() from perf_event_intel_lbr.c */
31237fae79SAdrian Hunter static void intel_pt_insn_decoder(struct insn *insn,
32237fae79SAdrian Hunter 				  struct intel_pt_insn *intel_pt_insn)
33237fae79SAdrian Hunter {
34237fae79SAdrian Hunter 	enum intel_pt_insn_op op = INTEL_PT_OP_OTHER;
35237fae79SAdrian Hunter 	enum intel_pt_insn_branch branch = INTEL_PT_BR_NO_BRANCH;
36237fae79SAdrian Hunter 	int ext;
37237fae79SAdrian 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]) {
48237fae79SAdrian Hunter 		case 0x05: /* syscall */
49237fae79SAdrian Hunter 		case 0x34: /* sysenter */
50237fae79SAdrian Hunter 			op = INTEL_PT_OP_SYSCALL;
51237fae79SAdrian Hunter 			branch = INTEL_PT_BR_INDIRECT;
52237fae79SAdrian Hunter 			break;
53237fae79SAdrian Hunter 		case 0x07: /* sysret */
54237fae79SAdrian Hunter 		case 0x35: /* sysexit */
55237fae79SAdrian Hunter 			op = INTEL_PT_OP_SYSRET;
56237fae79SAdrian Hunter 			branch = INTEL_PT_BR_INDIRECT;
57237fae79SAdrian Hunter 			break;
58237fae79SAdrian Hunter 		case 0x80 ... 0x8f: /* jcc */
59237fae79SAdrian Hunter 			op = INTEL_PT_OP_JCC;
60237fae79SAdrian Hunter 			branch = INTEL_PT_BR_CONDITIONAL;
61237fae79SAdrian Hunter 			break;
62237fae79SAdrian Hunter 		default:
63237fae79SAdrian Hunter 			break;
64237fae79SAdrian Hunter 		}
65237fae79SAdrian Hunter 		break;
66237fae79SAdrian Hunter 	case 0x70 ... 0x7f: /* jcc */
67237fae79SAdrian Hunter 		op = INTEL_PT_OP_JCC;
68237fae79SAdrian Hunter 		branch = INTEL_PT_BR_CONDITIONAL;
69237fae79SAdrian Hunter 		break;
70237fae79SAdrian Hunter 	case 0xc2: /* near ret */
71237fae79SAdrian Hunter 	case 0xc3: /* near ret */
72237fae79SAdrian Hunter 	case 0xca: /* far ret */
73237fae79SAdrian Hunter 	case 0xcb: /* far ret */
74237fae79SAdrian Hunter 		op = INTEL_PT_OP_RET;
75237fae79SAdrian Hunter 		branch = INTEL_PT_BR_INDIRECT;
76237fae79SAdrian Hunter 		break;
77237fae79SAdrian Hunter 	case 0xcf: /* iret */
78237fae79SAdrian Hunter 		op = INTEL_PT_OP_IRET;
79237fae79SAdrian Hunter 		branch = INTEL_PT_BR_INDIRECT;
80237fae79SAdrian Hunter 		break;
81237fae79SAdrian Hunter 	case 0xcc ... 0xce: /* int */
82237fae79SAdrian Hunter 		op = INTEL_PT_OP_INT;
83237fae79SAdrian Hunter 		branch = INTEL_PT_BR_INDIRECT;
84237fae79SAdrian Hunter 		break;
85237fae79SAdrian Hunter 	case 0xe8: /* call near rel */
86237fae79SAdrian Hunter 		op = INTEL_PT_OP_CALL;
87237fae79SAdrian Hunter 		branch = INTEL_PT_BR_UNCONDITIONAL;
88237fae79SAdrian Hunter 		break;
89237fae79SAdrian Hunter 	case 0x9a: /* call far absolute */
90237fae79SAdrian Hunter 		op = INTEL_PT_OP_CALL;
91237fae79SAdrian Hunter 		branch = INTEL_PT_BR_INDIRECT;
92237fae79SAdrian Hunter 		break;
93237fae79SAdrian Hunter 	case 0xe0 ... 0xe2: /* loop */
94237fae79SAdrian Hunter 		op = INTEL_PT_OP_LOOP;
95237fae79SAdrian Hunter 		branch = INTEL_PT_BR_CONDITIONAL;
96237fae79SAdrian Hunter 		break;
97237fae79SAdrian Hunter 	case 0xe3: /* jcc */
98237fae79SAdrian Hunter 		op = INTEL_PT_OP_JCC;
99237fae79SAdrian Hunter 		branch = INTEL_PT_BR_CONDITIONAL;
100237fae79SAdrian Hunter 		break;
101237fae79SAdrian Hunter 	case 0xe9: /* jmp */
102237fae79SAdrian Hunter 	case 0xeb: /* jmp */
103237fae79SAdrian Hunter 		op = INTEL_PT_OP_JMP;
104237fae79SAdrian Hunter 		branch = INTEL_PT_BR_UNCONDITIONAL;
105237fae79SAdrian Hunter 		break;
106237fae79SAdrian Hunter 	case 0xea: /* far jmp */
107237fae79SAdrian Hunter 		op = INTEL_PT_OP_JMP;
108237fae79SAdrian Hunter 		branch = INTEL_PT_BR_INDIRECT;
109237fae79SAdrian Hunter 		break;
110237fae79SAdrian Hunter 	case 0xff: /* call near absolute, call far absolute ind */
111237fae79SAdrian Hunter 		ext = (insn->modrm.bytes[0] >> 3) & 0x7;
112237fae79SAdrian Hunter 		switch (ext) {
113237fae79SAdrian Hunter 		case 2: /* near ind call */
114237fae79SAdrian Hunter 		case 3: /* far ind call */
115237fae79SAdrian Hunter 			op = INTEL_PT_OP_CALL;
116237fae79SAdrian Hunter 			branch = INTEL_PT_BR_INDIRECT;
117237fae79SAdrian Hunter 			break;
118237fae79SAdrian Hunter 		case 4:
119237fae79SAdrian Hunter 		case 5:
120237fae79SAdrian Hunter 			op = INTEL_PT_OP_JMP;
121237fae79SAdrian Hunter 			branch = INTEL_PT_BR_INDIRECT;
122237fae79SAdrian Hunter 			break;
123237fae79SAdrian Hunter 		default:
124237fae79SAdrian Hunter 			break;
125237fae79SAdrian Hunter 		}
126237fae79SAdrian Hunter 		break;
127237fae79SAdrian Hunter 	default:
128237fae79SAdrian Hunter 		break;
129237fae79SAdrian Hunter 	}
130237fae79SAdrian Hunter 
131237fae79SAdrian Hunter 	intel_pt_insn->op = op;
132237fae79SAdrian Hunter 	intel_pt_insn->branch = branch;
133237fae79SAdrian Hunter 	intel_pt_insn->length = insn->length;
134237fae79SAdrian Hunter 
135237fae79SAdrian Hunter 	if (branch == INTEL_PT_BR_CONDITIONAL ||
136237fae79SAdrian Hunter 	    branch == INTEL_PT_BR_UNCONDITIONAL) {
137237fae79SAdrian Hunter #if __BYTE_ORDER == __BIG_ENDIAN
138237fae79SAdrian Hunter 		switch (insn->immediate.nbytes) {
139237fae79SAdrian Hunter 		case 1:
140237fae79SAdrian Hunter 			intel_pt_insn->rel = insn->immediate.value;
141237fae79SAdrian Hunter 			break;
142237fae79SAdrian Hunter 		case 2:
143237fae79SAdrian Hunter 			intel_pt_insn->rel =
144237fae79SAdrian Hunter 					bswap_16((short)insn->immediate.value);
145237fae79SAdrian Hunter 			break;
146237fae79SAdrian Hunter 		case 4:
147237fae79SAdrian Hunter 			intel_pt_insn->rel = bswap_32(insn->immediate.value);
148237fae79SAdrian Hunter 			break;
14997db6206SAdrian Hunter 		default:
15097db6206SAdrian Hunter 			intel_pt_insn->rel = 0;
15197db6206SAdrian Hunter 			break;
152237fae79SAdrian Hunter 		}
153237fae79SAdrian Hunter #else
154237fae79SAdrian Hunter 		intel_pt_insn->rel = insn->immediate.value;
155237fae79SAdrian Hunter #endif
156237fae79SAdrian Hunter 	}
157237fae79SAdrian Hunter }
158237fae79SAdrian Hunter 
159237fae79SAdrian Hunter int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64,
160237fae79SAdrian Hunter 		      struct intel_pt_insn *intel_pt_insn)
161237fae79SAdrian Hunter {
162237fae79SAdrian Hunter 	struct insn insn;
163237fae79SAdrian Hunter 
164237fae79SAdrian Hunter 	insn_init(&insn, buf, len, x86_64);
165237fae79SAdrian Hunter 	insn_get_length(&insn);
166237fae79SAdrian Hunter 	if (!insn_complete(&insn) || insn.length > len)
167237fae79SAdrian Hunter 		return -1;
168237fae79SAdrian Hunter 	intel_pt_insn_decoder(&insn, intel_pt_insn);
169237fae79SAdrian Hunter 	if (insn.length < INTEL_PT_INSN_DBG_BUF_SZ)
170237fae79SAdrian Hunter 		memcpy(intel_pt_insn->buf, buf, insn.length);
171237fae79SAdrian Hunter 	else
172237fae79SAdrian Hunter 		memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_DBG_BUF_SZ);
173237fae79SAdrian Hunter 	return 0;
174237fae79SAdrian Hunter }
175237fae79SAdrian Hunter 
176237fae79SAdrian Hunter const char *branch_name[] = {
177237fae79SAdrian Hunter 	[INTEL_PT_OP_OTHER]	= "Other",
178237fae79SAdrian Hunter 	[INTEL_PT_OP_CALL]	= "Call",
179237fae79SAdrian Hunter 	[INTEL_PT_OP_RET]	= "Ret",
180237fae79SAdrian Hunter 	[INTEL_PT_OP_JCC]	= "Jcc",
181237fae79SAdrian Hunter 	[INTEL_PT_OP_JMP]	= "Jmp",
182237fae79SAdrian Hunter 	[INTEL_PT_OP_LOOP]	= "Loop",
183237fae79SAdrian Hunter 	[INTEL_PT_OP_IRET]	= "IRet",
184237fae79SAdrian Hunter 	[INTEL_PT_OP_INT]	= "Int",
185237fae79SAdrian Hunter 	[INTEL_PT_OP_SYSCALL]	= "Syscall",
186237fae79SAdrian Hunter 	[INTEL_PT_OP_SYSRET]	= "Sysret",
187237fae79SAdrian Hunter };
188237fae79SAdrian Hunter 
189237fae79SAdrian Hunter const char *intel_pt_insn_name(enum intel_pt_insn_op op)
190237fae79SAdrian Hunter {
191237fae79SAdrian Hunter 	return branch_name[op];
192237fae79SAdrian Hunter }
193237fae79SAdrian Hunter 
194237fae79SAdrian Hunter int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf,
195237fae79SAdrian Hunter 		       size_t buf_len)
196237fae79SAdrian Hunter {
197237fae79SAdrian Hunter 	switch (intel_pt_insn->branch) {
198237fae79SAdrian Hunter 	case INTEL_PT_BR_CONDITIONAL:
199237fae79SAdrian Hunter 	case INTEL_PT_BR_UNCONDITIONAL:
200237fae79SAdrian Hunter 		return snprintf(buf, buf_len, "%s %s%d",
201237fae79SAdrian Hunter 				intel_pt_insn_name(intel_pt_insn->op),
202237fae79SAdrian Hunter 				intel_pt_insn->rel > 0 ? "+" : "",
203237fae79SAdrian Hunter 				intel_pt_insn->rel);
204237fae79SAdrian Hunter 	case INTEL_PT_BR_NO_BRANCH:
205237fae79SAdrian Hunter 	case INTEL_PT_BR_INDIRECT:
206237fae79SAdrian Hunter 		return snprintf(buf, buf_len, "%s",
207237fae79SAdrian Hunter 				intel_pt_insn_name(intel_pt_insn->op));
208237fae79SAdrian Hunter 	default:
209237fae79SAdrian Hunter 		break;
210237fae79SAdrian Hunter 	}
211237fae79SAdrian Hunter 	return 0;
212237fae79SAdrian Hunter }
213237fae79SAdrian Hunter 
214237fae79SAdrian Hunter size_t intel_pt_insn_max_size(void)
215237fae79SAdrian Hunter {
216237fae79SAdrian Hunter 	return MAX_INSN_SIZE;
217237fae79SAdrian Hunter }
218237fae79SAdrian Hunter 
219237fae79SAdrian Hunter int intel_pt_insn_type(enum intel_pt_insn_op op)
220237fae79SAdrian Hunter {
221237fae79SAdrian Hunter 	switch (op) {
222237fae79SAdrian Hunter 	case INTEL_PT_OP_OTHER:
223237fae79SAdrian Hunter 		return 0;
224237fae79SAdrian Hunter 	case INTEL_PT_OP_CALL:
225237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL;
226237fae79SAdrian Hunter 	case INTEL_PT_OP_RET:
227237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN;
228237fae79SAdrian Hunter 	case INTEL_PT_OP_JCC:
229237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
230237fae79SAdrian Hunter 	case INTEL_PT_OP_JMP:
231237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH;
232237fae79SAdrian Hunter 	case INTEL_PT_OP_LOOP:
233237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
234237fae79SAdrian Hunter 	case INTEL_PT_OP_IRET:
235237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
236237fae79SAdrian Hunter 		       PERF_IP_FLAG_INTERRUPT;
237237fae79SAdrian Hunter 	case INTEL_PT_OP_INT:
238237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
239237fae79SAdrian Hunter 		       PERF_IP_FLAG_INTERRUPT;
240237fae79SAdrian Hunter 	case INTEL_PT_OP_SYSCALL:
241237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
242237fae79SAdrian Hunter 		       PERF_IP_FLAG_SYSCALLRET;
243237fae79SAdrian Hunter 	case INTEL_PT_OP_SYSRET:
244237fae79SAdrian Hunter 		return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
245237fae79SAdrian Hunter 		       PERF_IP_FLAG_SYSCALLRET;
246237fae79SAdrian Hunter 	default:
247237fae79SAdrian Hunter 		return 0;
248237fae79SAdrian Hunter 	}
249237fae79SAdrian Hunter }
250