18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * bpf_jit.h: BPF JIT compiler for PPC 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2011 Matt Evans <matt@ozlabs.org>, IBM Corporation 68c2ecf20Sopenharmony_ci * 2016 Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#ifndef _BPF_JIT_H 98c2ecf20Sopenharmony_ci#define _BPF_JIT_H 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#ifndef __ASSEMBLY__ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <asm/types.h> 148c2ecf20Sopenharmony_ci#include <asm/ppc-opcode.h> 158c2ecf20Sopenharmony_ci#include <asm/code-patching.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#ifdef PPC64_ELF_ABI_v1 188c2ecf20Sopenharmony_ci#define FUNCTION_DESCR_SIZE 24 198c2ecf20Sopenharmony_ci#else 208c2ecf20Sopenharmony_ci#define FUNCTION_DESCR_SIZE 0 218c2ecf20Sopenharmony_ci#endif 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define PLANT_INSTR(d, idx, instr) \ 248c2ecf20Sopenharmony_ci do { if (d) { (d)[idx] = instr; } idx++; } while (0) 258c2ecf20Sopenharmony_ci#define EMIT(instr) PLANT_INSTR(image, ctx->idx, instr) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Long jump; (unconditional 'branch') */ 288c2ecf20Sopenharmony_ci#define PPC_JMP(dest) \ 298c2ecf20Sopenharmony_ci do { \ 308c2ecf20Sopenharmony_ci long offset = (long)(dest) - (ctx->idx * 4); \ 318c2ecf20Sopenharmony_ci if (!is_offset_in_branch_range(offset)) { \ 328c2ecf20Sopenharmony_ci pr_err_ratelimited("Branch offset 0x%lx (@%u) out of range\n", offset, ctx->idx); \ 338c2ecf20Sopenharmony_ci return -ERANGE; \ 348c2ecf20Sopenharmony_ci } \ 358c2ecf20Sopenharmony_ci EMIT(PPC_INST_BRANCH | (offset & 0x03fffffc)); \ 368c2ecf20Sopenharmony_ci } while (0) 378c2ecf20Sopenharmony_ci/* "cond" here covers BO:BI fields. */ 388c2ecf20Sopenharmony_ci#define PPC_BCC_SHORT(cond, dest) \ 398c2ecf20Sopenharmony_ci do { \ 408c2ecf20Sopenharmony_ci long offset = (long)(dest) - (ctx->idx * 4); \ 418c2ecf20Sopenharmony_ci if (!is_offset_in_cond_branch_range(offset)) { \ 428c2ecf20Sopenharmony_ci pr_err_ratelimited("Conditional branch offset 0x%lx (@%u) out of range\n", offset, ctx->idx); \ 438c2ecf20Sopenharmony_ci return -ERANGE; \ 448c2ecf20Sopenharmony_ci } \ 458c2ecf20Sopenharmony_ci EMIT(PPC_INST_BRANCH_COND | (((cond) & 0x3ff) << 16) | (offset & 0xfffc)); \ 468c2ecf20Sopenharmony_ci } while (0) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* Sign-extended 32-bit immediate load */ 498c2ecf20Sopenharmony_ci#define PPC_LI32(d, i) do { \ 508c2ecf20Sopenharmony_ci if ((int)(uintptr_t)(i) >= -32768 && \ 518c2ecf20Sopenharmony_ci (int)(uintptr_t)(i) < 32768) \ 528c2ecf20Sopenharmony_ci EMIT(PPC_RAW_LI(d, i)); \ 538c2ecf20Sopenharmony_ci else { \ 548c2ecf20Sopenharmony_ci EMIT(PPC_RAW_LIS(d, IMM_H(i))); \ 558c2ecf20Sopenharmony_ci if (IMM_L(i)) \ 568c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ORI(d, d, IMM_L(i))); \ 578c2ecf20Sopenharmony_ci } } while(0) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define PPC_LI64(d, i) do { \ 608c2ecf20Sopenharmony_ci if ((long)(i) >= -2147483648 && \ 618c2ecf20Sopenharmony_ci (long)(i) < 2147483648) \ 628c2ecf20Sopenharmony_ci PPC_LI32(d, i); \ 638c2ecf20Sopenharmony_ci else { \ 648c2ecf20Sopenharmony_ci if (!((uintptr_t)(i) & 0xffff800000000000ULL)) \ 658c2ecf20Sopenharmony_ci EMIT(PPC_RAW_LI(d, ((uintptr_t)(i) >> 32) & \ 668c2ecf20Sopenharmony_ci 0xffff)); \ 678c2ecf20Sopenharmony_ci else { \ 688c2ecf20Sopenharmony_ci EMIT(PPC_RAW_LIS(d, ((uintptr_t)(i) >> 48))); \ 698c2ecf20Sopenharmony_ci if ((uintptr_t)(i) & 0x0000ffff00000000ULL) \ 708c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ORI(d, d, \ 718c2ecf20Sopenharmony_ci ((uintptr_t)(i) >> 32) & 0xffff)); \ 728c2ecf20Sopenharmony_ci } \ 738c2ecf20Sopenharmony_ci EMIT(PPC_RAW_SLDI(d, d, 32)); \ 748c2ecf20Sopenharmony_ci if ((uintptr_t)(i) & 0x00000000ffff0000ULL) \ 758c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ORIS(d, d, \ 768c2ecf20Sopenharmony_ci ((uintptr_t)(i) >> 16) & 0xffff)); \ 778c2ecf20Sopenharmony_ci if ((uintptr_t)(i) & 0x000000000000ffffULL) \ 788c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ORI(d, d, (uintptr_t)(i) & \ 798c2ecf20Sopenharmony_ci 0xffff)); \ 808c2ecf20Sopenharmony_ci } } while (0) 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64 838c2ecf20Sopenharmony_ci#define PPC_FUNC_ADDR(d,i) do { PPC_LI64(d, i); } while(0) 848c2ecf20Sopenharmony_ci#else 858c2ecf20Sopenharmony_ci#define PPC_FUNC_ADDR(d,i) do { PPC_LI32(d, i); } while(0) 868c2ecf20Sopenharmony_ci#endif 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* 898c2ecf20Sopenharmony_ci * The fly in the ointment of code size changing from pass to pass is 908c2ecf20Sopenharmony_ci * avoided by padding the short branch case with a NOP. If code size differs 918c2ecf20Sopenharmony_ci * with different branch reaches we will have the issue of code moving from 928c2ecf20Sopenharmony_ci * one pass to the next and will need a few passes to converge on a stable 938c2ecf20Sopenharmony_ci * state. 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_ci#define PPC_BCC(cond, dest) do { \ 968c2ecf20Sopenharmony_ci if (is_offset_in_cond_branch_range((long)(dest) - (ctx->idx * 4))) { \ 978c2ecf20Sopenharmony_ci PPC_BCC_SHORT(cond, dest); \ 988c2ecf20Sopenharmony_ci EMIT(PPC_RAW_NOP()); \ 998c2ecf20Sopenharmony_ci } else { \ 1008c2ecf20Sopenharmony_ci /* Flip the 'T or F' bit to invert comparison */ \ 1018c2ecf20Sopenharmony_ci PPC_BCC_SHORT(cond ^ COND_CMP_TRUE, (ctx->idx+2)*4); \ 1028c2ecf20Sopenharmony_ci PPC_JMP(dest); \ 1038c2ecf20Sopenharmony_ci } } while(0) 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* To create a branch condition, select a bit of cr0... */ 1068c2ecf20Sopenharmony_ci#define CR0_LT 0 1078c2ecf20Sopenharmony_ci#define CR0_GT 1 1088c2ecf20Sopenharmony_ci#define CR0_EQ 2 1098c2ecf20Sopenharmony_ci/* ...and modify BO[3] */ 1108c2ecf20Sopenharmony_ci#define COND_CMP_TRUE 0x100 1118c2ecf20Sopenharmony_ci#define COND_CMP_FALSE 0x000 1128c2ecf20Sopenharmony_ci/* Together, they make all required comparisons: */ 1138c2ecf20Sopenharmony_ci#define COND_GT (CR0_GT | COND_CMP_TRUE) 1148c2ecf20Sopenharmony_ci#define COND_GE (CR0_LT | COND_CMP_FALSE) 1158c2ecf20Sopenharmony_ci#define COND_EQ (CR0_EQ | COND_CMP_TRUE) 1168c2ecf20Sopenharmony_ci#define COND_NE (CR0_EQ | COND_CMP_FALSE) 1178c2ecf20Sopenharmony_ci#define COND_LT (CR0_LT | COND_CMP_TRUE) 1188c2ecf20Sopenharmony_ci#define COND_LE (CR0_GT | COND_CMP_FALSE) 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#endif 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci#endif 123