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