staticint decode_branch_type(struct insn *insn)
{ int ext;
if (insn_get_opcode(insn)) return X86_BR_ABORT;
switch (insn->opcode.bytes[0]) { case 0xf: switch (insn->opcode.bytes[1]) { case 0x05: /* syscall */ case 0x34: /* sysenter */ return X86_BR_SYSCALL; case 0x07: /* sysret */ case 0x35: /* sysexit */ return X86_BR_SYSRET; case 0x80 ... 0x8f: /* conditional */ return X86_BR_JCC;
} return X86_BR_NONE; case 0x70 ... 0x7f: /* conditional */ return X86_BR_JCC; case 0xc2: /* near ret */ case 0xc3: /* near ret */ case 0xca: /* far ret */ case 0xcb: /* far ret */ return X86_BR_RET; case 0xcf: /* iret */ return X86_BR_IRET; case 0xcc ... 0xce: /* int */ return X86_BR_INT; case 0xe8: /* call near rel */ if (insn_get_immediate(insn) || insn->immediate1.value == 0) { /* zero length call */ return X86_BR_ZERO_CALL;
}
fallthrough; case 0x9a: /* call far absolute */ return X86_BR_CALL; case 0xe0 ... 0xe3: /* loop jmp */ return X86_BR_JCC; case 0xe9 ... 0xeb: /* jmp */ return X86_BR_JMP; case 0xff: /* call near absolute, call far absolute ind */ if (insn_get_modrm(insn)) return X86_BR_ABORT;
ext = (insn->modrm.bytes[0] >> 3) & 0x7; switch (ext) { case 2: /* near ind call */ case 3: /* far ind call */ return X86_BR_IND_CALL; case 4: case 5: return X86_BR_IND_JMP;
} return X86_BR_NONE;
}
return X86_BR_NONE;
}
/* * return the type of control flow change at address "from" * instruction is not necessarily a branch (in case of interrupt). * * The branch type returned also includes the priv level of the * target of the control flow change (X86_BR_USER, X86_BR_KERNEL). * * If a branch type is unknown OR the instruction cannot be * decoded (e.g., text page not present), then X86_BR_NONE is * returned. * * While recording branches, some processors can report the "from" * address to be that of an instruction preceding the actual branch * when instruction fusion occurs. If fusion is expected, attempt to * find the type of the first branch instruction within the next * MAX_INSN_SIZE bytes and if found, provide the offset between the * reported "from" address and the actual branch instruction address.
*/ staticint get_branch_type(unsignedlong from, unsignedlong to, int abort, bool fused, int *offset)
{ struct insn insn; void *addr; int bytes_read, bytes_left, insn_offset; int ret = X86_BR_NONE; int to_plm, from_plm;
u8 buf[MAX_INSN_SIZE]; int is64 = 0;
/* make sure we initialize offset */ if (offset)
*offset = 0;
/* * maybe zero if lbr did not fill up after a reset by the time * we get a PMU interrupt
*/ if (from == 0 || to == 0) return X86_BR_NONE;
if (abort) return X86_BR_ABORT | to_plm;
if (from_plm == X86_BR_USER) { /* * can happen if measuring at the user level only * and we interrupt in a kernel thread, e.g., idle.
*/ if (!current->mm) return X86_BR_NONE;
/* may fail if text not present */
bytes_left = copy_from_user_nmi(buf, (void __user *)from,
MAX_INSN_SIZE);
bytes_read = MAX_INSN_SIZE - bytes_left; if (!bytes_read) return X86_BR_NONE;
addr = buf;
} else { /* * The LBR logs any address in the IP, even if the IP just * faulted. This means userspace can control the from address. * Ensure we don't blindly read any address by validating it is * a known text address and not a vsyscall address.
*/ if (kernel_text_address(from) && !in_gate_area_no_mm(from)) {
addr = (void *)from; /* * Assume we can get the maximum possible size * when grabbing kernel data. This is not * _strictly_ true since we could possibly be * executing up next to a memory hole, but * it is very unlikely to be a problem.
*/
bytes_read = MAX_INSN_SIZE;
} else { return X86_BR_NONE;
}
}
/* * decoder needs to know the ABI especially * on 64-bit systems running 32-bit apps
*/ #ifdef CONFIG_X86_64
is64 = kernel_ip((unsignedlong)addr) || any_64bit_mode(current_pt_regs()); #endif
insn_init(&insn, addr, bytes_read, is64);
ret = decode_branch_type(&insn);
insn_offset = 0;
/* Check for the possibility of branch fusion */ while (fused && ret == X86_BR_NONE) { /* Check for decoding errors */ if (insn_get_length(&insn) || !insn.length) break;
insn_init(&insn, addr + insn_offset, bytes_read, is64);
ret = decode_branch_type(&insn);
}
if (offset)
*offset = insn_offset;
/* * interrupts, traps, faults (and thus ring transition) may * occur on any instructions. Thus, to classify them correctly, * we need to first look at the from and to priv levels. If they * are different and to is in the kernel, then it indicates * a ring transition. If the from instruction is not a ring * transition instr (syscall, systenter, int), then it means * it was a irq, trap or fault. * * we have no way of detecting kernel to kernel faults.
*/ if (from_plm == X86_BR_USER && to_plm == X86_BR_KERNEL
&& ret != X86_BR_SYSCALL && ret != X86_BR_INT)
ret = X86_BR_IRQ;
/* * branch priv level determined by target as * is done by HW when LBR_SELECT is implemented
*/ if (ret != X86_BR_NONE)
ret |= to_plm;
return ret;
}
int branch_type(unsignedlong from, unsignedlong to, int abort)
{ return get_branch_type(from, to, abort, false, NULL);
}
int branch_type_fused(unsignedlong from, unsignedlong to, int abort, int *offset)
{ return get_branch_type(from, to, abort, true, offset);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.