1 // SPDX-License-Identifier: GPL-2.0 2 #include <asm/insn.h> 3 #include <linux/mm.h> 4 5 #include "perf_event.h" 6 7 static int decode_branch_type(struct insn *insn) 8 { 9 int ext; 10 11 if (insn_get_opcode(insn)) 12 return X86_BR_ABORT; 13 14 switch (insn->opcode.bytes[0]) { 15 case 0xf: 16 switch (insn->opcode.bytes[1]) { 17 case 0x05: /* syscall */ 18 case 0x34: /* sysenter */ 19 return X86_BR_SYSCALL; 20 case 0x07: /* sysret */ 21 case 0x35: /* sysexit */ 22 return X86_BR_SYSRET; 23 case 0x80 ... 0x8f: /* conditional */ 24 return X86_BR_JCC; 25 } 26 return X86_BR_NONE; 27 case 0x70 ... 0x7f: /* conditional */ 28 return X86_BR_JCC; 29 case 0xc2: /* near ret */ 30 case 0xc3: /* near ret */ 31 case 0xca: /* far ret */ 32 case 0xcb: /* far ret */ 33 return X86_BR_RET; 34 case 0xcf: /* iret */ 35 return X86_BR_IRET; 36 case 0xcc ... 0xce: /* int */ 37 return X86_BR_INT; 38 case 0xe8: /* call near rel */ 39 if (insn_get_immediate(insn) || insn->immediate1.value == 0) { 40 /* zero length call */ 41 return X86_BR_ZERO_CALL; 42 } 43 fallthrough; 44 case 0x9a: /* call far absolute */ 45 return X86_BR_CALL; 46 case 0xe0 ... 0xe3: /* loop jmp */ 47 return X86_BR_JCC; 48 case 0xe9 ... 0xeb: /* jmp */ 49 return X86_BR_JMP; 50 case 0xff: /* call near absolute, call far absolute ind */ 51 if (insn_get_modrm(insn)) 52 return X86_BR_ABORT; 53 54 ext = (insn->modrm.bytes[0] >> 3) & 0x7; 55 switch (ext) { 56 case 2: /* near ind call */ 57 case 3: /* far ind call */ 58 return X86_BR_IND_CALL; 59 case 4: 60 case 5: 61 return X86_BR_IND_JMP; 62 } 63 return X86_BR_NONE; 64 } 65 66 return X86_BR_NONE; 67 } 68 69 /* 70 * return the type of control flow change at address "from" 71 * instruction is not necessarily a branch (in case of interrupt). 72 * 73 * The branch type returned also includes the priv level of the 74 * target of the control flow change (X86_BR_USER, X86_BR_KERNEL). 75 * 76 * If a branch type is unknown OR the instruction cannot be 77 * decoded (e.g., text page not present), then X86_BR_NONE is 78 * returned. 79 * 80 * While recording branches, some processors can report the "from" 81 * address to be that of an instruction preceding the actual branch 82 * when instruction fusion occurs. If fusion is expected, attempt to 83 * find the type of the first branch instruction within the next 84 * MAX_INSN_SIZE bytes and if found, provide the offset between the 85 * reported "from" address and the actual branch instruction address. 86 */ 87 static int get_branch_type(unsigned long from, unsigned long to, int abort, 88 bool fused, int *offset) 89 { 90 struct insn insn; 91 void *addr; 92 int bytes_read, bytes_left, insn_offset; 93 int ret = X86_BR_NONE; 94 int to_plm, from_plm; 95 u8 buf[MAX_INSN_SIZE]; 96 int is64 = 0; 97 98 /* make sure we initialize offset */ 99 if (offset) 100 *offset = 0; 101 102 to_plm = kernel_ip(to) ? X86_BR_KERNEL : X86_BR_USER; 103 from_plm = kernel_ip(from) ? X86_BR_KERNEL : X86_BR_USER; 104 105 /* 106 * maybe zero if lbr did not fill up after a reset by the time 107 * we get a PMU interrupt 108 */ 109 if (from == 0 || to == 0) 110 return X86_BR_NONE; 111 112 if (abort) 113 return X86_BR_ABORT | to_plm; 114 115 if (from_plm == X86_BR_USER) { 116 /* 117 * can happen if measuring at the user level only 118 * and we interrupt in a kernel thread, e.g., idle. 119 */ 120 if (!current->mm) 121 return X86_BR_NONE; 122 123 /* may fail if text not present */ 124 bytes_left = copy_from_user_nmi(buf, (void __user *)from, 125 MAX_INSN_SIZE); 126 bytes_read = MAX_INSN_SIZE - bytes_left; 127 if (!bytes_read) 128 return X86_BR_NONE; 129 130 addr = buf; 131 } else { 132 /* 133 * The LBR logs any address in the IP, even if the IP just 134 * faulted. This means userspace can control the from address. 135 * Ensure we don't blindly read any address by validating it is 136 * a known text address and not a vsyscall address. 137 */ 138 if (kernel_text_address(from) && !in_gate_area_no_mm(from)) { 139 addr = (void *)from; 140 /* 141 * Assume we can get the maximum possible size 142 * when grabbing kernel data. This is not 143 * _strictly_ true since we could possibly be 144 * executing up next to a memory hole, but 145 * it is very unlikely to be a problem. 146 */ 147 bytes_read = MAX_INSN_SIZE; 148 } else { 149 return X86_BR_NONE; 150 } 151 } 152 153 /* 154 * decoder needs to know the ABI especially 155 * on 64-bit systems running 32-bit apps 156 */ 157 #ifdef CONFIG_X86_64 158 is64 = kernel_ip((unsigned long)addr) || any_64bit_mode(current_pt_regs()); 159 #endif 160 insn_init(&insn, addr, bytes_read, is64); 161 ret = decode_branch_type(&insn); 162 insn_offset = 0; 163 164 /* Check for the possibility of branch fusion */ 165 while (fused && ret == X86_BR_NONE) { 166 /* Check for decoding errors */ 167 if (insn_get_length(&insn) || !insn.length) 168 break; 169 170 insn_offset += insn.length; 171 bytes_read -= insn.length; 172 if (bytes_read < 0) 173 break; 174 175 insn_init(&insn, addr + insn_offset, bytes_read, is64); 176 ret = decode_branch_type(&insn); 177 } 178 179 if (offset) 180 *offset = insn_offset; 181 182 /* 183 * interrupts, traps, faults (and thus ring transition) may 184 * occur on any instructions. Thus, to classify them correctly, 185 * we need to first look at the from and to priv levels. If they 186 * are different and to is in the kernel, then it indicates 187 * a ring transition. If the from instruction is not a ring 188 * transition instr (syscall, systenter, int), then it means 189 * it was a irq, trap or fault. 190 * 191 * we have no way of detecting kernel to kernel faults. 192 */ 193 if (from_plm == X86_BR_USER && to_plm == X86_BR_KERNEL 194 && ret != X86_BR_SYSCALL && ret != X86_BR_INT) 195 ret = X86_BR_IRQ; 196 197 /* 198 * branch priv level determined by target as 199 * is done by HW when LBR_SELECT is implemented 200 */ 201 if (ret != X86_BR_NONE) 202 ret |= to_plm; 203 204 return ret; 205 } 206 207 int branch_type(unsigned long from, unsigned long to, int abort) 208 { 209 return get_branch_type(from, to, abort, false, NULL); 210 } 211 212 int branch_type_fused(unsigned long from, unsigned long to, int abort, 213 int *offset) 214 { 215 return get_branch_type(from, to, abort, true, offset); 216 } 217 218 #define X86_BR_TYPE_MAP_MAX 16 219 220 static int branch_map[X86_BR_TYPE_MAP_MAX] = { 221 PERF_BR_CALL, /* X86_BR_CALL */ 222 PERF_BR_RET, /* X86_BR_RET */ 223 PERF_BR_SYSCALL, /* X86_BR_SYSCALL */ 224 PERF_BR_SYSRET, /* X86_BR_SYSRET */ 225 PERF_BR_UNKNOWN, /* X86_BR_INT */ 226 PERF_BR_ERET, /* X86_BR_IRET */ 227 PERF_BR_COND, /* X86_BR_JCC */ 228 PERF_BR_UNCOND, /* X86_BR_JMP */ 229 PERF_BR_IRQ, /* X86_BR_IRQ */ 230 PERF_BR_IND_CALL, /* X86_BR_IND_CALL */ 231 PERF_BR_UNKNOWN, /* X86_BR_ABORT */ 232 PERF_BR_UNKNOWN, /* X86_BR_IN_TX */ 233 PERF_BR_NO_TX, /* X86_BR_NO_TX */ 234 PERF_BR_CALL, /* X86_BR_ZERO_CALL */ 235 PERF_BR_UNKNOWN, /* X86_BR_CALL_STACK */ 236 PERF_BR_IND, /* X86_BR_IND_JMP */ 237 }; 238 239 int common_branch_type(int type) 240 { 241 int i; 242 243 type >>= 2; /* skip X86_BR_USER and X86_BR_KERNEL */ 244 245 if (type) { 246 i = __ffs(type); 247 if (i < X86_BR_TYPE_MAP_MAX) 248 return branch_map[i]; 249 } 250 251 return PERF_BR_UNKNOWN; 252 } 253
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.