Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * intel_pt_insn_decoder.c: Intel Processor Trace support
0004  * Copyright (c) 2013-2014, Intel Corporation.
0005  */
0006 
0007 #include <linux/kernel.h>
0008 #include <stdio.h>
0009 #include <string.h>
0010 #include <endian.h>
0011 #include <byteswap.h>
0012 #include "../../../arch/x86/include/asm/insn.h"
0013 
0014 #include "../../../arch/x86/lib/inat.c"
0015 #include "../../../arch/x86/lib/insn.c"
0016 
0017 #include "event.h"
0018 
0019 #include "intel-pt-insn-decoder.h"
0020 #include "dump-insn.h"
0021 
0022 #if INTEL_PT_INSN_BUF_SZ < MAX_INSN_SIZE || INTEL_PT_INSN_BUF_SZ > MAX_INSN
0023 #error Instruction buffer size too small
0024 #endif
0025 
0026 /* Based on branch_type() from arch/x86/events/intel/lbr.c */
0027 static void intel_pt_insn_decoder(struct insn *insn,
0028                   struct intel_pt_insn *intel_pt_insn)
0029 {
0030     enum intel_pt_insn_op op = INTEL_PT_OP_OTHER;
0031     enum intel_pt_insn_branch branch = INTEL_PT_BR_NO_BRANCH;
0032     int ext;
0033 
0034     intel_pt_insn->rel = 0;
0035     intel_pt_insn->emulated_ptwrite = false;
0036 
0037     if (insn_is_avx(insn)) {
0038         intel_pt_insn->op = INTEL_PT_OP_OTHER;
0039         intel_pt_insn->branch = INTEL_PT_BR_NO_BRANCH;
0040         intel_pt_insn->length = insn->length;
0041         return;
0042     }
0043 
0044     switch (insn->opcode.bytes[0]) {
0045     case 0xf:
0046         switch (insn->opcode.bytes[1]) {
0047         case 0x01:
0048             switch (insn->modrm.bytes[0]) {
0049             case 0xc2: /* vmlaunch */
0050             case 0xc3: /* vmresume */
0051                 op = INTEL_PT_OP_VMENTRY;
0052                 branch = INTEL_PT_BR_INDIRECT;
0053                 break;
0054             default:
0055                 break;
0056             }
0057             break;
0058         case 0x05: /* syscall */
0059         case 0x34: /* sysenter */
0060             op = INTEL_PT_OP_SYSCALL;
0061             branch = INTEL_PT_BR_INDIRECT;
0062             break;
0063         case 0x07: /* sysret */
0064         case 0x35: /* sysexit */
0065             op = INTEL_PT_OP_SYSRET;
0066             branch = INTEL_PT_BR_INDIRECT;
0067             break;
0068         case 0x80 ... 0x8f: /* jcc */
0069             op = INTEL_PT_OP_JCC;
0070             branch = INTEL_PT_BR_CONDITIONAL;
0071             break;
0072         default:
0073             break;
0074         }
0075         break;
0076     case 0x70 ... 0x7f: /* jcc */
0077         op = INTEL_PT_OP_JCC;
0078         branch = INTEL_PT_BR_CONDITIONAL;
0079         break;
0080     case 0xc2: /* near ret */
0081     case 0xc3: /* near ret */
0082     case 0xca: /* far ret */
0083     case 0xcb: /* far ret */
0084         op = INTEL_PT_OP_RET;
0085         branch = INTEL_PT_BR_INDIRECT;
0086         break;
0087     case 0xcf: /* iret */
0088         op = INTEL_PT_OP_IRET;
0089         branch = INTEL_PT_BR_INDIRECT;
0090         break;
0091     case 0xcc ... 0xce: /* int */
0092         op = INTEL_PT_OP_INT;
0093         branch = INTEL_PT_BR_INDIRECT;
0094         break;
0095     case 0xe8: /* call near rel */
0096         op = INTEL_PT_OP_CALL;
0097         branch = INTEL_PT_BR_UNCONDITIONAL;
0098         break;
0099     case 0x9a: /* call far absolute */
0100         op = INTEL_PT_OP_CALL;
0101         branch = INTEL_PT_BR_INDIRECT;
0102         break;
0103     case 0xe0 ... 0xe2: /* loop */
0104         op = INTEL_PT_OP_LOOP;
0105         branch = INTEL_PT_BR_CONDITIONAL;
0106         break;
0107     case 0xe3: /* jcc */
0108         op = INTEL_PT_OP_JCC;
0109         branch = INTEL_PT_BR_CONDITIONAL;
0110         break;
0111     case 0xe9: /* jmp */
0112     case 0xeb: /* jmp */
0113         op = INTEL_PT_OP_JMP;
0114         branch = INTEL_PT_BR_UNCONDITIONAL;
0115         break;
0116     case 0xea: /* far jmp */
0117         op = INTEL_PT_OP_JMP;
0118         branch = INTEL_PT_BR_INDIRECT;
0119         break;
0120     case 0xff: /* call near absolute, call far absolute ind */
0121         ext = (insn->modrm.bytes[0] >> 3) & 0x7;
0122         switch (ext) {
0123         case 2: /* near ind call */
0124         case 3: /* far ind call */
0125             op = INTEL_PT_OP_CALL;
0126             branch = INTEL_PT_BR_INDIRECT;
0127             break;
0128         case 4:
0129         case 5:
0130             op = INTEL_PT_OP_JMP;
0131             branch = INTEL_PT_BR_INDIRECT;
0132             break;
0133         default:
0134             break;
0135         }
0136         break;
0137     default:
0138         break;
0139     }
0140 
0141     intel_pt_insn->op = op;
0142     intel_pt_insn->branch = branch;
0143     intel_pt_insn->length = insn->length;
0144 
0145     if (branch == INTEL_PT_BR_CONDITIONAL ||
0146         branch == INTEL_PT_BR_UNCONDITIONAL) {
0147 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
0148         switch (insn->immediate.nbytes) {
0149         case 1:
0150             intel_pt_insn->rel = insn->immediate.value;
0151             break;
0152         case 2:
0153             intel_pt_insn->rel =
0154                     bswap_16((short)insn->immediate.value);
0155             break;
0156         case 4:
0157             intel_pt_insn->rel = bswap_32(insn->immediate.value);
0158             break;
0159         default:
0160             intel_pt_insn->rel = 0;
0161             break;
0162         }
0163 #else
0164         intel_pt_insn->rel = insn->immediate.value;
0165 #endif
0166     }
0167 }
0168 
0169 int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64,
0170               struct intel_pt_insn *intel_pt_insn)
0171 {
0172     struct insn insn;
0173     int ret;
0174 
0175     ret = insn_decode(&insn, buf, len,
0176               x86_64 ? INSN_MODE_64 : INSN_MODE_32);
0177     if (ret < 0 || insn.length > len)
0178         return -1;
0179 
0180     intel_pt_insn_decoder(&insn, intel_pt_insn);
0181     if (insn.length < INTEL_PT_INSN_BUF_SZ)
0182         memcpy(intel_pt_insn->buf, buf, insn.length);
0183     else
0184         memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_BUF_SZ);
0185     return 0;
0186 }
0187 
0188 int arch_is_branch(const unsigned char *buf, size_t len, int x86_64)
0189 {
0190     struct intel_pt_insn in;
0191     if (intel_pt_get_insn(buf, len, x86_64, &in) < 0)
0192         return -1;
0193     return in.branch != INTEL_PT_BR_NO_BRANCH;
0194 }
0195 
0196 const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused,
0197               u8 *inbuf, int inlen, int *lenp)
0198 {
0199     struct insn insn;
0200     int n, i, ret;
0201     int left;
0202 
0203     ret = insn_decode(&insn, inbuf, inlen,
0204               x->is64bit ? INSN_MODE_64 : INSN_MODE_32);
0205 
0206     if (ret < 0 || insn.length > inlen)
0207         return "<bad>";
0208     if (lenp)
0209         *lenp = insn.length;
0210     left = sizeof(x->out);
0211     n = snprintf(x->out, left, "insn: ");
0212     left -= n;
0213     for (i = 0; i < insn.length; i++) {
0214         n += snprintf(x->out + n, left, "%02x ", inbuf[i]);
0215         left -= n;
0216     }
0217     return x->out;
0218 }
0219 
0220 const char *branch_name[] = {
0221     [INTEL_PT_OP_OTHER] = "Other",
0222     [INTEL_PT_OP_CALL]  = "Call",
0223     [INTEL_PT_OP_RET]   = "Ret",
0224     [INTEL_PT_OP_JCC]   = "Jcc",
0225     [INTEL_PT_OP_JMP]   = "Jmp",
0226     [INTEL_PT_OP_LOOP]  = "Loop",
0227     [INTEL_PT_OP_IRET]  = "IRet",
0228     [INTEL_PT_OP_INT]   = "Int",
0229     [INTEL_PT_OP_SYSCALL]   = "Syscall",
0230     [INTEL_PT_OP_SYSRET]    = "Sysret",
0231     [INTEL_PT_OP_VMENTRY]   = "VMentry",
0232 };
0233 
0234 const char *intel_pt_insn_name(enum intel_pt_insn_op op)
0235 {
0236     return branch_name[op];
0237 }
0238 
0239 int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf,
0240                size_t buf_len)
0241 {
0242     switch (intel_pt_insn->branch) {
0243     case INTEL_PT_BR_CONDITIONAL:
0244     case INTEL_PT_BR_UNCONDITIONAL:
0245         return snprintf(buf, buf_len, "%s %s%d",
0246                 intel_pt_insn_name(intel_pt_insn->op),
0247                 intel_pt_insn->rel > 0 ? "+" : "",
0248                 intel_pt_insn->rel);
0249     case INTEL_PT_BR_NO_BRANCH:
0250     case INTEL_PT_BR_INDIRECT:
0251         return snprintf(buf, buf_len, "%s",
0252                 intel_pt_insn_name(intel_pt_insn->op));
0253     default:
0254         break;
0255     }
0256     return 0;
0257 }
0258 
0259 int intel_pt_insn_type(enum intel_pt_insn_op op)
0260 {
0261     switch (op) {
0262     case INTEL_PT_OP_OTHER:
0263         return 0;
0264     case INTEL_PT_OP_CALL:
0265         return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL;
0266     case INTEL_PT_OP_RET:
0267         return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN;
0268     case INTEL_PT_OP_JCC:
0269         return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
0270     case INTEL_PT_OP_JMP:
0271         return PERF_IP_FLAG_BRANCH;
0272     case INTEL_PT_OP_LOOP:
0273         return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
0274     case INTEL_PT_OP_IRET:
0275         return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
0276                PERF_IP_FLAG_INTERRUPT;
0277     case INTEL_PT_OP_INT:
0278         return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
0279                PERF_IP_FLAG_INTERRUPT;
0280     case INTEL_PT_OP_SYSCALL:
0281         return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
0282                PERF_IP_FLAG_SYSCALLRET;
0283     case INTEL_PT_OP_SYSRET:
0284         return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
0285                PERF_IP_FLAG_SYSCALLRET;
0286     case INTEL_PT_OP_VMENTRY:
0287         return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
0288                PERF_IP_FLAG_VMENTRY;
0289     default:
0290         return 0;
0291     }
0292 }