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