bool __kprobes simulate_branch()

in kernel/probes/simulate-insn.c [146:197]


bool __kprobes simulate_branch(u32 opcode, unsigned long addr, struct pt_regs *regs)
{
	/*
	 * branch instructions:
	 *      31    30       25 24 20 19 15 14    12 11       8    7      6      0
	 * | imm[12] | imm[10:5] | rs2 | rs1 | funct3 | imm[4:1] | imm[11] | opcode |
	 *     1           6        5     5      3         4         1         7
	 *     imm[12|10:5]        rs2   rs1    000       imm[4:1|11]       1100011  BEQ
	 *     imm[12|10:5]        rs2   rs1    001       imm[4:1|11]       1100011  BNE
	 *     imm[12|10:5]        rs2   rs1    100       imm[4:1|11]       1100011  BLT
	 *     imm[12|10:5]        rs2   rs1    101       imm[4:1|11]       1100011  BGE
	 *     imm[12|10:5]        rs2   rs1    110       imm[4:1|11]       1100011  BLTU
	 *     imm[12|10:5]        rs2   rs1    111       imm[4:1|11]       1100011  BGEU
	 */

	s32 offset;
	s32 offset_tmp;
	unsigned long rs1_val;
	unsigned long rs2_val;

	if (!rv_insn_reg_get_val(regs, branch_rs1_idx(opcode), &rs1_val) ||
	    !rv_insn_reg_get_val(regs, branch_rs2_idx(opcode), &rs2_val))
		return false;

	offset_tmp = branch_offset(opcode);
	switch (branch_funct3(opcode)) {
	case BRANCH_BEQ:
		offset = (rs1_val == rs2_val) ? offset_tmp : 4;
		break;
	case BRANCH_BNE:
		offset = (rs1_val != rs2_val) ? offset_tmp : 4;
		break;
	case BRANCH_BLT:
		offset = ((long)rs1_val < (long)rs2_val) ? offset_tmp : 4;
		break;
	case BRANCH_BGE:
		offset = ((long)rs1_val >= (long)rs2_val) ? offset_tmp : 4;
		break;
	case BRANCH_BLTU:
		offset = (rs1_val < rs2_val) ? offset_tmp : 4;
		break;
	case BRANCH_BGEU:
		offset = (rs1_val >= rs2_val) ? offset_tmp : 4;
		break;
	default:
		return false;
	}

	instruction_pointer_set(regs, addr + offset);

	return true;
}