322 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * intel_pt_insn_decoder.c: Intel Processor Trace support4 * 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_INSN24#error Instruction buffer size too small25#endif26 27/* Based on branch_type() from arch/x86/events/intel/lbr.c */28static 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#else188 intel_pt_insn->rel = insn->immediate.value;189#endif190 }191}192 193int 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 else208 memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_BUF_SZ);209 return 0;210}211 212int arch_is_uncond_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_UNCONDITIONAL ||218 in.branch == INTEL_PT_BR_INDIRECT;219}220 221const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused,222 u8 *inbuf, int inlen, int *lenp)223{224 struct insn insn;225 int n, i, ret;226 int left;227 228 ret = insn_decode(&insn, inbuf, inlen,229 x->is64bit ? INSN_MODE_64 : INSN_MODE_32);230 231 if (ret < 0 || insn.length > inlen)232 return "<bad>";233 if (lenp)234 *lenp = insn.length;235 left = sizeof(x->out);236 n = snprintf(x->out, left, "insn: ");237 left -= n;238 for (i = 0; i < insn.length; i++) {239 n += snprintf(x->out + n, left, "%02x ", inbuf[i]);240 left -= n;241 }242 return x->out;243}244 245const char *branch_name[] = {246 [INTEL_PT_OP_OTHER] = "Other",247 [INTEL_PT_OP_CALL] = "Call",248 [INTEL_PT_OP_RET] = "Ret",249 [INTEL_PT_OP_JCC] = "Jcc",250 [INTEL_PT_OP_JMP] = "Jmp",251 [INTEL_PT_OP_LOOP] = "Loop",252 [INTEL_PT_OP_IRET] = "IRet",253 [INTEL_PT_OP_INT] = "Int",254 [INTEL_PT_OP_SYSCALL] = "Syscall",255 [INTEL_PT_OP_SYSRET] = "Sysret",256 [INTEL_PT_OP_VMENTRY] = "VMentry",257 [INTEL_PT_OP_ERETS] = "Erets",258 [INTEL_PT_OP_ERETU] = "Eretu",259};260 261const char *intel_pt_insn_name(enum intel_pt_insn_op op)262{263 return branch_name[op];264}265 266int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf,267 size_t buf_len)268{269 switch (intel_pt_insn->branch) {270 case INTEL_PT_BR_CONDITIONAL:271 case INTEL_PT_BR_UNCONDITIONAL:272 return snprintf(buf, buf_len, "%s %s%d",273 intel_pt_insn_name(intel_pt_insn->op),274 intel_pt_insn->rel > 0 ? "+" : "",275 intel_pt_insn->rel);276 case INTEL_PT_BR_NO_BRANCH:277 case INTEL_PT_BR_INDIRECT:278 return snprintf(buf, buf_len, "%s",279 intel_pt_insn_name(intel_pt_insn->op));280 default:281 break;282 }283 return 0;284}285 286int intel_pt_insn_type(enum intel_pt_insn_op op)287{288 switch (op) {289 case INTEL_PT_OP_OTHER:290 return 0;291 case INTEL_PT_OP_CALL:292 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL;293 case INTEL_PT_OP_RET:294 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN;295 case INTEL_PT_OP_JCC:296 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;297 case INTEL_PT_OP_JMP:298 return PERF_IP_FLAG_BRANCH;299 case INTEL_PT_OP_LOOP:300 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;301 case INTEL_PT_OP_IRET:302 case INTEL_PT_OP_ERETS:303 case INTEL_PT_OP_ERETU:304 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |305 PERF_IP_FLAG_INTERRUPT;306 case INTEL_PT_OP_INT:307 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |308 PERF_IP_FLAG_INTERRUPT;309 case INTEL_PT_OP_SYSCALL:310 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |311 PERF_IP_FLAG_SYSCALLRET;312 case INTEL_PT_OP_SYSRET:313 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |314 PERF_IP_FLAG_SYSCALLRET;315 case INTEL_PT_OP_VMENTRY:316 return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |317 PERF_IP_FLAG_VMENTRY;318 default:319 return 0;320 }321}322