int bpf_jit_emit_insn()

in net/bpf_jit_comp32.c [954:1302]


int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
		      bool extra_pass)
{
	bool is64 = BPF_CLASS(insn->code) == BPF_ALU64 ||
		BPF_CLASS(insn->code) == BPF_JMP;
	int s, e, rvoff, i = insn - ctx->prog->insnsi;
	u8 code = insn->code;
	s16 off = insn->off;
	s32 imm = insn->imm;

	const s8 *dst = bpf2rv32[insn->dst_reg];
	const s8 *src = bpf2rv32[insn->src_reg];
	const s8 *tmp1 = bpf2rv32[TMP_REG_1];
	const s8 *tmp2 = bpf2rv32[TMP_REG_2];

	switch (code) {
	case BPF_ALU64 | BPF_MOV | BPF_X:

	case BPF_ALU64 | BPF_ADD | BPF_X:
	case BPF_ALU64 | BPF_ADD | BPF_K:

	case BPF_ALU64 | BPF_SUB | BPF_X:
	case BPF_ALU64 | BPF_SUB | BPF_K:

	case BPF_ALU64 | BPF_AND | BPF_X:
	case BPF_ALU64 | BPF_OR | BPF_X:
	case BPF_ALU64 | BPF_XOR | BPF_X:

	case BPF_ALU64 | BPF_MUL | BPF_X:
	case BPF_ALU64 | BPF_MUL | BPF_K:

	case BPF_ALU64 | BPF_LSH | BPF_X:
	case BPF_ALU64 | BPF_RSH | BPF_X:
	case BPF_ALU64 | BPF_ARSH | BPF_X:
		if (BPF_SRC(code) == BPF_K) {
			emit_imm32(tmp2, imm, ctx);
			src = tmp2;
		}
		emit_alu_r64(dst, src, ctx, BPF_OP(code));
		break;

	case BPF_ALU64 | BPF_NEG:
		emit_alu_r64(dst, tmp2, ctx, BPF_OP(code));
		break;

	case BPF_ALU64 | BPF_DIV | BPF_X:
	case BPF_ALU64 | BPF_DIV | BPF_K:
	case BPF_ALU64 | BPF_MOD | BPF_X:
	case BPF_ALU64 | BPF_MOD | BPF_K:
		goto notsupported;

	case BPF_ALU64 | BPF_MOV | BPF_K:
	case BPF_ALU64 | BPF_AND | BPF_K:
	case BPF_ALU64 | BPF_OR | BPF_K:
	case BPF_ALU64 | BPF_XOR | BPF_K:
	case BPF_ALU64 | BPF_LSH | BPF_K:
	case BPF_ALU64 | BPF_RSH | BPF_K:
	case BPF_ALU64 | BPF_ARSH | BPF_K:
		emit_alu_i64(dst, imm, ctx, BPF_OP(code));
		break;

	case BPF_ALU | BPF_MOV | BPF_X:
		if (imm == 1) {
			/* Special mov32 for zext. */
			emit_zext64(dst, ctx);
			break;
		}
		fallthrough;

	case BPF_ALU | BPF_ADD | BPF_X:
	case BPF_ALU | BPF_SUB | BPF_X:
	case BPF_ALU | BPF_AND | BPF_X:
	case BPF_ALU | BPF_OR | BPF_X:
	case BPF_ALU | BPF_XOR | BPF_X:

	case BPF_ALU | BPF_MUL | BPF_X:
	case BPF_ALU | BPF_MUL | BPF_K:

	case BPF_ALU | BPF_DIV | BPF_X:
	case BPF_ALU | BPF_DIV | BPF_K:

	case BPF_ALU | BPF_MOD | BPF_X:
	case BPF_ALU | BPF_MOD | BPF_K:

	case BPF_ALU | BPF_LSH | BPF_X:
	case BPF_ALU | BPF_RSH | BPF_X:
	case BPF_ALU | BPF_ARSH | BPF_X:
		if (BPF_SRC(code) == BPF_K) {
			emit_imm32(tmp2, imm, ctx);
			src = tmp2;
		}
		emit_alu_r32(dst, src, ctx, BPF_OP(code));
		break;

	case BPF_ALU | BPF_MOV | BPF_K:
	case BPF_ALU | BPF_ADD | BPF_K:
	case BPF_ALU | BPF_SUB | BPF_K:
	case BPF_ALU | BPF_AND | BPF_K:
	case BPF_ALU | BPF_OR | BPF_K:
	case BPF_ALU | BPF_XOR | BPF_K:
	case BPF_ALU | BPF_LSH | BPF_K:
	case BPF_ALU | BPF_RSH | BPF_K:
	case BPF_ALU | BPF_ARSH | BPF_K:
		/*
		 * mul,div,mod are handled in the BPF_X case since there are
		 * no RISC-V I-type equivalents.
		 */
		emit_alu_i32(dst, imm, ctx, BPF_OP(code));
		break;

	case BPF_ALU | BPF_NEG:
		/*
		 * src is ignored---choose tmp2 as a dummy register since it
		 * is not on the stack.
		 */
		emit_alu_r32(dst, tmp2, ctx, BPF_OP(code));
		break;

	case BPF_ALU | BPF_END | BPF_FROM_LE:
	{
		const s8 *rd = bpf_get_reg64(dst, tmp1, ctx);

		switch (imm) {
		case 16:
			emit(rv_slli(lo(rd), lo(rd), 16), ctx);
			emit(rv_srli(lo(rd), lo(rd), 16), ctx);
			fallthrough;
		case 32:
			if (!ctx->prog->aux->verifier_zext)
				emit(rv_addi(hi(rd), RV_REG_ZERO, 0), ctx);
			break;
		case 64:
			/* Do nothing. */
			break;
		default:
			pr_err("bpf-jit: BPF_END imm %d invalid\n", imm);
			return -1;
		}

		bpf_put_reg64(dst, rd, ctx);
		break;
	}

	case BPF_ALU | BPF_END | BPF_FROM_BE:
	{
		const s8 *rd = bpf_get_reg64(dst, tmp1, ctx);

		switch (imm) {
		case 16:
			emit_rev16(lo(rd), ctx);
			if (!ctx->prog->aux->verifier_zext)
				emit(rv_addi(hi(rd), RV_REG_ZERO, 0), ctx);
			break;
		case 32:
			emit_rev32(lo(rd), ctx);
			if (!ctx->prog->aux->verifier_zext)
				emit(rv_addi(hi(rd), RV_REG_ZERO, 0), ctx);
			break;
		case 64:
			/* Swap upper and lower halves. */
			emit(rv_addi(RV_REG_T0, lo(rd), 0), ctx);
			emit(rv_addi(lo(rd), hi(rd), 0), ctx);
			emit(rv_addi(hi(rd), RV_REG_T0, 0), ctx);

			/* Swap each half. */
			emit_rev32(lo(rd), ctx);
			emit_rev32(hi(rd), ctx);
			break;
		default:
			pr_err("bpf-jit: BPF_END imm %d invalid\n", imm);
			return -1;
		}

		bpf_put_reg64(dst, rd, ctx);
		break;
	}

	case BPF_JMP | BPF_JA:
		rvoff = rv_offset(i, off, ctx);
		emit_jump_and_link(RV_REG_ZERO, rvoff, false, ctx);
		break;

	case BPF_JMP | BPF_CALL:
	{
		bool fixed;
		int ret;
		u64 addr;

		ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, &addr,
					    &fixed);
		if (ret < 0)
			return ret;
		emit_call(fixed, addr, ctx);
		break;
	}

	case BPF_JMP | BPF_TAIL_CALL:
		if (emit_bpf_tail_call(i, ctx))
			return -1;
		break;

	case BPF_JMP | BPF_JEQ | BPF_X:
	case BPF_JMP | BPF_JEQ | BPF_K:
	case BPF_JMP32 | BPF_JEQ | BPF_X:
	case BPF_JMP32 | BPF_JEQ | BPF_K:

	case BPF_JMP | BPF_JNE | BPF_X:
	case BPF_JMP | BPF_JNE | BPF_K:
	case BPF_JMP32 | BPF_JNE | BPF_X:
	case BPF_JMP32 | BPF_JNE | BPF_K:

	case BPF_JMP | BPF_JLE | BPF_X:
	case BPF_JMP | BPF_JLE | BPF_K:
	case BPF_JMP32 | BPF_JLE | BPF_X:
	case BPF_JMP32 | BPF_JLE | BPF_K:

	case BPF_JMP | BPF_JLT | BPF_X:
	case BPF_JMP | BPF_JLT | BPF_K:
	case BPF_JMP32 | BPF_JLT | BPF_X:
	case BPF_JMP32 | BPF_JLT | BPF_K:

	case BPF_JMP | BPF_JGE | BPF_X:
	case BPF_JMP | BPF_JGE | BPF_K:
	case BPF_JMP32 | BPF_JGE | BPF_X:
	case BPF_JMP32 | BPF_JGE | BPF_K:

	case BPF_JMP | BPF_JGT | BPF_X:
	case BPF_JMP | BPF_JGT | BPF_K:
	case BPF_JMP32 | BPF_JGT | BPF_X:
	case BPF_JMP32 | BPF_JGT | BPF_K:

	case BPF_JMP | BPF_JSLE | BPF_X:
	case BPF_JMP | BPF_JSLE | BPF_K:
	case BPF_JMP32 | BPF_JSLE | BPF_X:
	case BPF_JMP32 | BPF_JSLE | BPF_K:

	case BPF_JMP | BPF_JSLT | BPF_X:
	case BPF_JMP | BPF_JSLT | BPF_K:
	case BPF_JMP32 | BPF_JSLT | BPF_X:
	case BPF_JMP32 | BPF_JSLT | BPF_K:

	case BPF_JMP | BPF_JSGE | BPF_X:
	case BPF_JMP | BPF_JSGE | BPF_K:
	case BPF_JMP32 | BPF_JSGE | BPF_X:
	case BPF_JMP32 | BPF_JSGE | BPF_K:

	case BPF_JMP | BPF_JSGT | BPF_X:
	case BPF_JMP | BPF_JSGT | BPF_K:
	case BPF_JMP32 | BPF_JSGT | BPF_X:
	case BPF_JMP32 | BPF_JSGT | BPF_K:

	case BPF_JMP | BPF_JSET | BPF_X:
	case BPF_JMP | BPF_JSET | BPF_K:
	case BPF_JMP32 | BPF_JSET | BPF_X:
	case BPF_JMP32 | BPF_JSET | BPF_K:
		rvoff = rv_offset(i, off, ctx);
		if (BPF_SRC(code) == BPF_K) {
			s = ctx->ninsns;
			emit_imm32(tmp2, imm, ctx);
			src = tmp2;
			e = ctx->ninsns;
			rvoff -= ninsns_rvoff(e - s);
		}

		if (is64)
			emit_branch_r64(dst, src, rvoff, ctx, BPF_OP(code));
		else
			emit_branch_r32(dst, src, rvoff, ctx, BPF_OP(code));
		break;

	case BPF_JMP | BPF_EXIT:
		if (i == ctx->prog->len - 1)
			break;

		rvoff = epilogue_offset(ctx);
		emit_jump_and_link(RV_REG_ZERO, rvoff, false, ctx);
		break;

	case BPF_LD | BPF_IMM | BPF_DW:
	{
		struct bpf_insn insn1 = insn[1];
		s32 imm_lo = imm;
		s32 imm_hi = insn1.imm;
		const s8 *rd = bpf_get_reg64(dst, tmp1, ctx);

		emit_imm64(rd, imm_hi, imm_lo, ctx);
		bpf_put_reg64(dst, rd, ctx);
		return 1;
	}

	case BPF_LDX | BPF_MEM | BPF_B:
	case BPF_LDX | BPF_MEM | BPF_H:
	case BPF_LDX | BPF_MEM | BPF_W:
	case BPF_LDX | BPF_MEM | BPF_DW:
		if (emit_load_r64(dst, src, off, ctx, BPF_SIZE(code)))
			return -1;
		break;

	/* speculation barrier */
	case BPF_ST | BPF_NOSPEC:
		break;

	case BPF_ST | BPF_MEM | BPF_B:
	case BPF_ST | BPF_MEM | BPF_H:
	case BPF_ST | BPF_MEM | BPF_W:
	case BPF_ST | BPF_MEM | BPF_DW:

	case BPF_STX | BPF_MEM | BPF_B:
	case BPF_STX | BPF_MEM | BPF_H:
	case BPF_STX | BPF_MEM | BPF_W:
	case BPF_STX | BPF_MEM | BPF_DW:
		if (BPF_CLASS(code) == BPF_ST) {
			emit_imm32(tmp2, imm, ctx);
			src = tmp2;
		}

		if (emit_store_r64(dst, src, off, ctx, BPF_SIZE(code),
				   BPF_MODE(code)))
			return -1;
		break;

	case BPF_STX | BPF_ATOMIC | BPF_W:
		if (insn->imm != BPF_ADD) {
			pr_info_once(
				"bpf-jit: not supported: atomic operation %02x ***\n",
				insn->imm);
			return -EFAULT;
		}

		if (emit_store_r64(dst, src, off, ctx, BPF_SIZE(code),
				   BPF_MODE(code)))
			return -1;
		break;

	/* No hardware support for 8-byte atomics in RV32. */
	case BPF_STX | BPF_ATOMIC | BPF_DW:
		/* Fallthrough. */

notsupported:
		pr_info_once("bpf-jit: not supported: opcode %02x ***\n", code);
		return -EFAULT;

	default:
		pr_err("bpf-jit: unknown opcode %02x\n", code);
		return -EINVAL;
	}

	return 0;
}