162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * bpf_jit.h: BPF JIT compiler for PPC
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2011 Matt Evans <matt@ozlabs.org>, IBM Corporation
662306a36Sopenharmony_ci * 	     2016 Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#ifndef _BPF_JIT_H
962306a36Sopenharmony_ci#define _BPF_JIT_H
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#ifndef __ASSEMBLY__
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <asm/types.h>
1462306a36Sopenharmony_ci#include <asm/ppc-opcode.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#ifdef CONFIG_PPC64_ELF_ABI_V1
1762306a36Sopenharmony_ci#define FUNCTION_DESCR_SIZE	24
1862306a36Sopenharmony_ci#else
1962306a36Sopenharmony_ci#define FUNCTION_DESCR_SIZE	0
2062306a36Sopenharmony_ci#endif
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define CTX_NIA(ctx) ((unsigned long)ctx->idx * 4)
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define PLANT_INSTR(d, idx, instr)					      \
2562306a36Sopenharmony_ci	do { if (d) { (d)[idx] = instr; } idx++; } while (0)
2662306a36Sopenharmony_ci#define EMIT(instr)		PLANT_INSTR(image, ctx->idx, instr)
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* Long jump; (unconditional 'branch') */
2962306a36Sopenharmony_ci#define PPC_JMP(dest)							      \
3062306a36Sopenharmony_ci	do {								      \
3162306a36Sopenharmony_ci		long offset = (long)(dest) - CTX_NIA(ctx);		      \
3262306a36Sopenharmony_ci		if ((dest) != 0 && !is_offset_in_branch_range(offset)) {		      \
3362306a36Sopenharmony_ci			pr_err_ratelimited("Branch offset 0x%lx (@%u) out of range\n", offset, ctx->idx);			\
3462306a36Sopenharmony_ci			return -ERANGE;					      \
3562306a36Sopenharmony_ci		}							      \
3662306a36Sopenharmony_ci		EMIT(PPC_RAW_BRANCH(offset));				      \
3762306a36Sopenharmony_ci	} while (0)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* bl (unconditional 'branch' with link) */
4062306a36Sopenharmony_ci#define PPC_BL(dest)	EMIT(PPC_RAW_BL((dest) - (unsigned long)(image + ctx->idx)))
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* "cond" here covers BO:BI fields. */
4362306a36Sopenharmony_ci#define PPC_BCC_SHORT(cond, dest)					      \
4462306a36Sopenharmony_ci	do {								      \
4562306a36Sopenharmony_ci		long offset = (long)(dest) - CTX_NIA(ctx);		      \
4662306a36Sopenharmony_ci		if ((dest) != 0 && !is_offset_in_cond_branch_range(offset)) {		      \
4762306a36Sopenharmony_ci			pr_err_ratelimited("Conditional branch offset 0x%lx (@%u) out of range\n", offset, ctx->idx);		\
4862306a36Sopenharmony_ci			return -ERANGE;					      \
4962306a36Sopenharmony_ci		}							      \
5062306a36Sopenharmony_ci		EMIT(PPC_INST_BRANCH_COND | (((cond) & 0x3ff) << 16) | (offset & 0xfffc));					\
5162306a36Sopenharmony_ci	} while (0)
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* Sign-extended 32-bit immediate load */
5462306a36Sopenharmony_ci#define PPC_LI32(d, i)		do {					      \
5562306a36Sopenharmony_ci		if ((int)(uintptr_t)(i) >= -32768 &&			      \
5662306a36Sopenharmony_ci				(int)(uintptr_t)(i) < 32768)		      \
5762306a36Sopenharmony_ci			EMIT(PPC_RAW_LI(d, i));				      \
5862306a36Sopenharmony_ci		else {							      \
5962306a36Sopenharmony_ci			EMIT(PPC_RAW_LIS(d, IMM_H(i)));			      \
6062306a36Sopenharmony_ci			if (IMM_L(i))					      \
6162306a36Sopenharmony_ci				EMIT(PPC_RAW_ORI(d, d, IMM_L(i)));	      \
6262306a36Sopenharmony_ci		} } while(0)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#ifdef CONFIG_PPC64
6562306a36Sopenharmony_ci#define PPC_LI64(d, i)		do {					      \
6662306a36Sopenharmony_ci		if ((long)(i) >= -2147483648 &&				      \
6762306a36Sopenharmony_ci				(long)(i) < 2147483648)			      \
6862306a36Sopenharmony_ci			PPC_LI32(d, i);					      \
6962306a36Sopenharmony_ci		else {							      \
7062306a36Sopenharmony_ci			if (!((uintptr_t)(i) & 0xffff800000000000ULL))	      \
7162306a36Sopenharmony_ci				EMIT(PPC_RAW_LI(d, ((uintptr_t)(i) >> 32) &   \
7262306a36Sopenharmony_ci						0xffff));		      \
7362306a36Sopenharmony_ci			else {						      \
7462306a36Sopenharmony_ci				EMIT(PPC_RAW_LIS(d, ((uintptr_t)(i) >> 48))); \
7562306a36Sopenharmony_ci				if ((uintptr_t)(i) & 0x0000ffff00000000ULL)   \
7662306a36Sopenharmony_ci					EMIT(PPC_RAW_ORI(d, d,		      \
7762306a36Sopenharmony_ci					  ((uintptr_t)(i) >> 32) & 0xffff));  \
7862306a36Sopenharmony_ci			}						      \
7962306a36Sopenharmony_ci			EMIT(PPC_RAW_SLDI(d, d, 32));			      \
8062306a36Sopenharmony_ci			if ((uintptr_t)(i) & 0x00000000ffff0000ULL)	      \
8162306a36Sopenharmony_ci				EMIT(PPC_RAW_ORIS(d, d,			      \
8262306a36Sopenharmony_ci					 ((uintptr_t)(i) >> 16) & 0xffff));   \
8362306a36Sopenharmony_ci			if ((uintptr_t)(i) & 0x000000000000ffffULL)	      \
8462306a36Sopenharmony_ci				EMIT(PPC_RAW_ORI(d, d, (uintptr_t)(i) &       \
8562306a36Sopenharmony_ci							0xffff));             \
8662306a36Sopenharmony_ci		} } while (0)
8762306a36Sopenharmony_ci#endif
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/*
9062306a36Sopenharmony_ci * The fly in the ointment of code size changing from pass to pass is
9162306a36Sopenharmony_ci * avoided by padding the short branch case with a NOP.	 If code size differs
9262306a36Sopenharmony_ci * with different branch reaches we will have the issue of code moving from
9362306a36Sopenharmony_ci * one pass to the next and will need a few passes to converge on a stable
9462306a36Sopenharmony_ci * state.
9562306a36Sopenharmony_ci */
9662306a36Sopenharmony_ci#define PPC_BCC(cond, dest)	do {					      \
9762306a36Sopenharmony_ci		if (is_offset_in_cond_branch_range((long)(dest) - CTX_NIA(ctx))) {	\
9862306a36Sopenharmony_ci			PPC_BCC_SHORT(cond, dest);			      \
9962306a36Sopenharmony_ci			EMIT(PPC_RAW_NOP());				      \
10062306a36Sopenharmony_ci		} else {						      \
10162306a36Sopenharmony_ci			/* Flip the 'T or F' bit to invert comparison */      \
10262306a36Sopenharmony_ci			PPC_BCC_SHORT(cond ^ COND_CMP_TRUE, CTX_NIA(ctx) + 2*4);  \
10362306a36Sopenharmony_ci			PPC_JMP(dest);					      \
10462306a36Sopenharmony_ci		} } while(0)
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/* To create a branch condition, select a bit of cr0... */
10762306a36Sopenharmony_ci#define CR0_LT		0
10862306a36Sopenharmony_ci#define CR0_GT		1
10962306a36Sopenharmony_ci#define CR0_EQ		2
11062306a36Sopenharmony_ci/* ...and modify BO[3] */
11162306a36Sopenharmony_ci#define COND_CMP_TRUE	0x100
11262306a36Sopenharmony_ci#define COND_CMP_FALSE	0x000
11362306a36Sopenharmony_ci/* Together, they make all required comparisons: */
11462306a36Sopenharmony_ci#define COND_GT		(CR0_GT | COND_CMP_TRUE)
11562306a36Sopenharmony_ci#define COND_GE		(CR0_LT | COND_CMP_FALSE)
11662306a36Sopenharmony_ci#define COND_EQ		(CR0_EQ | COND_CMP_TRUE)
11762306a36Sopenharmony_ci#define COND_NE		(CR0_EQ | COND_CMP_FALSE)
11862306a36Sopenharmony_ci#define COND_LT		(CR0_LT | COND_CMP_TRUE)
11962306a36Sopenharmony_ci#define COND_LE		(CR0_GT | COND_CMP_FALSE)
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci#define SEEN_FUNC	0x20000000 /* might call external helpers */
12262306a36Sopenharmony_ci#define SEEN_TAILCALL	0x40000000 /* uses tail calls */
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistruct codegen_context {
12562306a36Sopenharmony_ci	/*
12662306a36Sopenharmony_ci	 * This is used to track register usage as well
12762306a36Sopenharmony_ci	 * as calls to external helpers.
12862306a36Sopenharmony_ci	 * - register usage is tracked with corresponding
12962306a36Sopenharmony_ci	 *   bits (r3-r31)
13062306a36Sopenharmony_ci	 * - rest of the bits can be used to track other
13162306a36Sopenharmony_ci	 *   things -- for now, we use bits 0 to 2
13262306a36Sopenharmony_ci	 *   encoded in SEEN_* macros above
13362306a36Sopenharmony_ci	 */
13462306a36Sopenharmony_ci	unsigned int seen;
13562306a36Sopenharmony_ci	unsigned int idx;
13662306a36Sopenharmony_ci	unsigned int stack_size;
13762306a36Sopenharmony_ci	int b2p[MAX_BPF_JIT_REG + 2];
13862306a36Sopenharmony_ci	unsigned int exentry_idx;
13962306a36Sopenharmony_ci	unsigned int alt_exit_addr;
14062306a36Sopenharmony_ci};
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci#define bpf_to_ppc(r)	(ctx->b2p[r])
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci#ifdef CONFIG_PPC32
14562306a36Sopenharmony_ci#define BPF_FIXUP_LEN	3 /* Three instructions => 12 bytes */
14662306a36Sopenharmony_ci#else
14762306a36Sopenharmony_ci#define BPF_FIXUP_LEN	2 /* Two instructions => 8 bytes */
14862306a36Sopenharmony_ci#endif
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic inline void bpf_flush_icache(void *start, void *end)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	smp_wmb();	/* smp write barrier */
15362306a36Sopenharmony_ci	flush_icache_range((unsigned long)start, (unsigned long)end);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic inline bool bpf_is_seen_register(struct codegen_context *ctx, int i)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	return ctx->seen & (1 << (31 - i));
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic inline void bpf_set_seen_register(struct codegen_context *ctx, int i)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	ctx->seen |= 1 << (31 - i);
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic inline void bpf_clear_seen_register(struct codegen_context *ctx, int i)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	ctx->seen &= ~(1 << (31 - i));
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_civoid bpf_jit_init_reg_mapping(struct codegen_context *ctx);
17262306a36Sopenharmony_ciint bpf_jit_emit_func_call_rel(u32 *image, struct codegen_context *ctx, u64 func);
17362306a36Sopenharmony_ciint bpf_jit_build_body(struct bpf_prog *fp, u32 *image, struct codegen_context *ctx,
17462306a36Sopenharmony_ci		       u32 *addrs, int pass, bool extra_pass);
17562306a36Sopenharmony_civoid bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx);
17662306a36Sopenharmony_civoid bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx);
17762306a36Sopenharmony_civoid bpf_jit_realloc_regs(struct codegen_context *ctx);
17862306a36Sopenharmony_ciint bpf_jit_emit_exit_insn(u32 *image, struct codegen_context *ctx, int tmp_reg, long exit_addr);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ciint bpf_add_extable_entry(struct bpf_prog *fp, u32 *image, int pass, struct codegen_context *ctx,
18162306a36Sopenharmony_ci			  int insn_idx, int jmp_off, int dst_reg);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci#endif
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci#endif
186