162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * BPF Jit compiler for s390. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Minimum build requirements: 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * - HAVE_MARCH_Z196_FEATURES: laal, laalg 862306a36Sopenharmony_ci * - HAVE_MARCH_Z10_FEATURES: msfi, cgrj, clgrj 962306a36Sopenharmony_ci * - HAVE_MARCH_Z9_109_FEATURES: alfi, llilf, clfi, oilf, nilf 1062306a36Sopenharmony_ci * - 64BIT 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Copyright IBM Corp. 2012,2015 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> 1562306a36Sopenharmony_ci * Michael Holzheu <holzheu@linux.vnet.ibm.com> 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define KMSG_COMPONENT "bpf_jit" 1962306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/netdevice.h> 2262306a36Sopenharmony_ci#include <linux/filter.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci#include <linux/bpf.h> 2562306a36Sopenharmony_ci#include <linux/mm.h> 2662306a36Sopenharmony_ci#include <linux/kernel.h> 2762306a36Sopenharmony_ci#include <asm/cacheflush.h> 2862306a36Sopenharmony_ci#include <asm/extable.h> 2962306a36Sopenharmony_ci#include <asm/dis.h> 3062306a36Sopenharmony_ci#include <asm/facility.h> 3162306a36Sopenharmony_ci#include <asm/nospec-branch.h> 3262306a36Sopenharmony_ci#include <asm/set_memory.h> 3362306a36Sopenharmony_ci#include <asm/text-patching.h> 3462306a36Sopenharmony_ci#include "bpf_jit.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct bpf_jit { 3762306a36Sopenharmony_ci u32 seen; /* Flags to remember seen eBPF instructions */ 3862306a36Sopenharmony_ci u32 seen_reg[16]; /* Array to remember which registers are used */ 3962306a36Sopenharmony_ci u32 *addrs; /* Array with relative instruction addresses */ 4062306a36Sopenharmony_ci u8 *prg_buf; /* Start of program */ 4162306a36Sopenharmony_ci int size; /* Size of program and literal pool */ 4262306a36Sopenharmony_ci int size_prg; /* Size of program */ 4362306a36Sopenharmony_ci int prg; /* Current position in program */ 4462306a36Sopenharmony_ci int lit32_start; /* Start of 32-bit literal pool */ 4562306a36Sopenharmony_ci int lit32; /* Current position in 32-bit literal pool */ 4662306a36Sopenharmony_ci int lit64_start; /* Start of 64-bit literal pool */ 4762306a36Sopenharmony_ci int lit64; /* Current position in 64-bit literal pool */ 4862306a36Sopenharmony_ci int base_ip; /* Base address for literal pool */ 4962306a36Sopenharmony_ci int exit_ip; /* Address of exit */ 5062306a36Sopenharmony_ci int r1_thunk_ip; /* Address of expoline thunk for 'br %r1' */ 5162306a36Sopenharmony_ci int r14_thunk_ip; /* Address of expoline thunk for 'br %r14' */ 5262306a36Sopenharmony_ci int tail_call_start; /* Tail call start offset */ 5362306a36Sopenharmony_ci int excnt; /* Number of exception table entries */ 5462306a36Sopenharmony_ci int prologue_plt_ret; /* Return address for prologue hotpatch PLT */ 5562306a36Sopenharmony_ci int prologue_plt; /* Start of prologue hotpatch PLT */ 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define SEEN_MEM BIT(0) /* use mem[] for temporary storage */ 5962306a36Sopenharmony_ci#define SEEN_LITERAL BIT(1) /* code uses literals */ 6062306a36Sopenharmony_ci#define SEEN_FUNC BIT(2) /* calls C functions */ 6162306a36Sopenharmony_ci#define SEEN_STACK (SEEN_FUNC | SEEN_MEM) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * s390 registers 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_ci#define REG_W0 (MAX_BPF_JIT_REG + 0) /* Work register 1 (even) */ 6762306a36Sopenharmony_ci#define REG_W1 (MAX_BPF_JIT_REG + 1) /* Work register 2 (odd) */ 6862306a36Sopenharmony_ci#define REG_L (MAX_BPF_JIT_REG + 2) /* Literal pool register */ 6962306a36Sopenharmony_ci#define REG_15 (MAX_BPF_JIT_REG + 3) /* Register 15 */ 7062306a36Sopenharmony_ci#define REG_0 REG_W0 /* Register 0 */ 7162306a36Sopenharmony_ci#define REG_1 REG_W1 /* Register 1 */ 7262306a36Sopenharmony_ci#define REG_2 BPF_REG_1 /* Register 2 */ 7362306a36Sopenharmony_ci#define REG_3 BPF_REG_2 /* Register 3 */ 7462306a36Sopenharmony_ci#define REG_4 BPF_REG_3 /* Register 4 */ 7562306a36Sopenharmony_ci#define REG_7 BPF_REG_6 /* Register 7 */ 7662306a36Sopenharmony_ci#define REG_8 BPF_REG_7 /* Register 8 */ 7762306a36Sopenharmony_ci#define REG_14 BPF_REG_0 /* Register 14 */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* 8062306a36Sopenharmony_ci * Mapping of BPF registers to s390 registers 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_cistatic const int reg2hex[] = { 8362306a36Sopenharmony_ci /* Return code */ 8462306a36Sopenharmony_ci [BPF_REG_0] = 14, 8562306a36Sopenharmony_ci /* Function parameters */ 8662306a36Sopenharmony_ci [BPF_REG_1] = 2, 8762306a36Sopenharmony_ci [BPF_REG_2] = 3, 8862306a36Sopenharmony_ci [BPF_REG_3] = 4, 8962306a36Sopenharmony_ci [BPF_REG_4] = 5, 9062306a36Sopenharmony_ci [BPF_REG_5] = 6, 9162306a36Sopenharmony_ci /* Call saved registers */ 9262306a36Sopenharmony_ci [BPF_REG_6] = 7, 9362306a36Sopenharmony_ci [BPF_REG_7] = 8, 9462306a36Sopenharmony_ci [BPF_REG_8] = 9, 9562306a36Sopenharmony_ci [BPF_REG_9] = 10, 9662306a36Sopenharmony_ci /* BPF stack pointer */ 9762306a36Sopenharmony_ci [BPF_REG_FP] = 13, 9862306a36Sopenharmony_ci /* Register for blinding */ 9962306a36Sopenharmony_ci [BPF_REG_AX] = 12, 10062306a36Sopenharmony_ci /* Work registers for s390x backend */ 10162306a36Sopenharmony_ci [REG_W0] = 0, 10262306a36Sopenharmony_ci [REG_W1] = 1, 10362306a36Sopenharmony_ci [REG_L] = 11, 10462306a36Sopenharmony_ci [REG_15] = 15, 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic inline u32 reg(u32 dst_reg, u32 src_reg) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci return reg2hex[dst_reg] << 4 | reg2hex[src_reg]; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic inline u32 reg_high(u32 reg) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci return reg2hex[reg] << 4; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic inline void reg_set_seen(struct bpf_jit *jit, u32 b1) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci u32 r1 = reg2hex[b1]; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (r1 >= 6 && r1 <= 15 && !jit->seen_reg[r1]) 12262306a36Sopenharmony_ci jit->seen_reg[r1] = 1; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#define REG_SET_SEEN(b1) \ 12662306a36Sopenharmony_ci({ \ 12762306a36Sopenharmony_ci reg_set_seen(jit, b1); \ 12862306a36Sopenharmony_ci}) 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#define REG_SEEN(b1) jit->seen_reg[reg2hex[(b1)]] 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * EMIT macros for code generation 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci#define _EMIT2(op) \ 13762306a36Sopenharmony_ci({ \ 13862306a36Sopenharmony_ci if (jit->prg_buf) \ 13962306a36Sopenharmony_ci *(u16 *) (jit->prg_buf + jit->prg) = (op); \ 14062306a36Sopenharmony_ci jit->prg += 2; \ 14162306a36Sopenharmony_ci}) 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci#define EMIT2(op, b1, b2) \ 14462306a36Sopenharmony_ci({ \ 14562306a36Sopenharmony_ci _EMIT2((op) | reg(b1, b2)); \ 14662306a36Sopenharmony_ci REG_SET_SEEN(b1); \ 14762306a36Sopenharmony_ci REG_SET_SEEN(b2); \ 14862306a36Sopenharmony_ci}) 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci#define _EMIT4(op) \ 15162306a36Sopenharmony_ci({ \ 15262306a36Sopenharmony_ci if (jit->prg_buf) \ 15362306a36Sopenharmony_ci *(u32 *) (jit->prg_buf + jit->prg) = (op); \ 15462306a36Sopenharmony_ci jit->prg += 4; \ 15562306a36Sopenharmony_ci}) 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci#define EMIT4(op, b1, b2) \ 15862306a36Sopenharmony_ci({ \ 15962306a36Sopenharmony_ci _EMIT4((op) | reg(b1, b2)); \ 16062306a36Sopenharmony_ci REG_SET_SEEN(b1); \ 16162306a36Sopenharmony_ci REG_SET_SEEN(b2); \ 16262306a36Sopenharmony_ci}) 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#define EMIT4_RRF(op, b1, b2, b3) \ 16562306a36Sopenharmony_ci({ \ 16662306a36Sopenharmony_ci _EMIT4((op) | reg_high(b3) << 8 | reg(b1, b2)); \ 16762306a36Sopenharmony_ci REG_SET_SEEN(b1); \ 16862306a36Sopenharmony_ci REG_SET_SEEN(b2); \ 16962306a36Sopenharmony_ci REG_SET_SEEN(b3); \ 17062306a36Sopenharmony_ci}) 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci#define _EMIT4_DISP(op, disp) \ 17362306a36Sopenharmony_ci({ \ 17462306a36Sopenharmony_ci unsigned int __disp = (disp) & 0xfff; \ 17562306a36Sopenharmony_ci _EMIT4((op) | __disp); \ 17662306a36Sopenharmony_ci}) 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci#define EMIT4_DISP(op, b1, b2, disp) \ 17962306a36Sopenharmony_ci({ \ 18062306a36Sopenharmony_ci _EMIT4_DISP((op) | reg_high(b1) << 16 | \ 18162306a36Sopenharmony_ci reg_high(b2) << 8, (disp)); \ 18262306a36Sopenharmony_ci REG_SET_SEEN(b1); \ 18362306a36Sopenharmony_ci REG_SET_SEEN(b2); \ 18462306a36Sopenharmony_ci}) 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci#define EMIT4_IMM(op, b1, imm) \ 18762306a36Sopenharmony_ci({ \ 18862306a36Sopenharmony_ci unsigned int __imm = (imm) & 0xffff; \ 18962306a36Sopenharmony_ci _EMIT4((op) | reg_high(b1) << 16 | __imm); \ 19062306a36Sopenharmony_ci REG_SET_SEEN(b1); \ 19162306a36Sopenharmony_ci}) 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci#define EMIT4_PCREL(op, pcrel) \ 19462306a36Sopenharmony_ci({ \ 19562306a36Sopenharmony_ci long __pcrel = ((pcrel) >> 1) & 0xffff; \ 19662306a36Sopenharmony_ci _EMIT4((op) | __pcrel); \ 19762306a36Sopenharmony_ci}) 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci#define EMIT4_PCREL_RIC(op, mask, target) \ 20062306a36Sopenharmony_ci({ \ 20162306a36Sopenharmony_ci int __rel = ((target) - jit->prg) / 2; \ 20262306a36Sopenharmony_ci _EMIT4((op) | (mask) << 20 | (__rel & 0xffff)); \ 20362306a36Sopenharmony_ci}) 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci#define _EMIT6(op1, op2) \ 20662306a36Sopenharmony_ci({ \ 20762306a36Sopenharmony_ci if (jit->prg_buf) { \ 20862306a36Sopenharmony_ci *(u32 *) (jit->prg_buf + jit->prg) = (op1); \ 20962306a36Sopenharmony_ci *(u16 *) (jit->prg_buf + jit->prg + 4) = (op2); \ 21062306a36Sopenharmony_ci } \ 21162306a36Sopenharmony_ci jit->prg += 6; \ 21262306a36Sopenharmony_ci}) 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci#define _EMIT6_DISP(op1, op2, disp) \ 21562306a36Sopenharmony_ci({ \ 21662306a36Sopenharmony_ci unsigned int __disp = (disp) & 0xfff; \ 21762306a36Sopenharmony_ci _EMIT6((op1) | __disp, op2); \ 21862306a36Sopenharmony_ci}) 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci#define _EMIT6_DISP_LH(op1, op2, disp) \ 22162306a36Sopenharmony_ci({ \ 22262306a36Sopenharmony_ci u32 _disp = (u32) (disp); \ 22362306a36Sopenharmony_ci unsigned int __disp_h = _disp & 0xff000; \ 22462306a36Sopenharmony_ci unsigned int __disp_l = _disp & 0x00fff; \ 22562306a36Sopenharmony_ci _EMIT6((op1) | __disp_l, (op2) | __disp_h >> 4); \ 22662306a36Sopenharmony_ci}) 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci#define EMIT6_DISP_LH(op1, op2, b1, b2, b3, disp) \ 22962306a36Sopenharmony_ci({ \ 23062306a36Sopenharmony_ci _EMIT6_DISP_LH((op1) | reg(b1, b2) << 16 | \ 23162306a36Sopenharmony_ci reg_high(b3) << 8, op2, disp); \ 23262306a36Sopenharmony_ci REG_SET_SEEN(b1); \ 23362306a36Sopenharmony_ci REG_SET_SEEN(b2); \ 23462306a36Sopenharmony_ci REG_SET_SEEN(b3); \ 23562306a36Sopenharmony_ci}) 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci#define EMIT6_PCREL_RIEB(op1, op2, b1, b2, mask, target) \ 23862306a36Sopenharmony_ci({ \ 23962306a36Sopenharmony_ci unsigned int rel = (int)((target) - jit->prg) / 2; \ 24062306a36Sopenharmony_ci _EMIT6((op1) | reg(b1, b2) << 16 | (rel & 0xffff), \ 24162306a36Sopenharmony_ci (op2) | (mask) << 12); \ 24262306a36Sopenharmony_ci REG_SET_SEEN(b1); \ 24362306a36Sopenharmony_ci REG_SET_SEEN(b2); \ 24462306a36Sopenharmony_ci}) 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci#define EMIT6_PCREL_RIEC(op1, op2, b1, imm, mask, target) \ 24762306a36Sopenharmony_ci({ \ 24862306a36Sopenharmony_ci unsigned int rel = (int)((target) - jit->prg) / 2; \ 24962306a36Sopenharmony_ci _EMIT6((op1) | (reg_high(b1) | (mask)) << 16 | \ 25062306a36Sopenharmony_ci (rel & 0xffff), (op2) | ((imm) & 0xff) << 8); \ 25162306a36Sopenharmony_ci REG_SET_SEEN(b1); \ 25262306a36Sopenharmony_ci BUILD_BUG_ON(((unsigned long) (imm)) > 0xff); \ 25362306a36Sopenharmony_ci}) 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci#define EMIT6_PCREL(op1, op2, b1, b2, i, off, mask) \ 25662306a36Sopenharmony_ci({ \ 25762306a36Sopenharmony_ci int rel = (addrs[(i) + (off) + 1] - jit->prg) / 2; \ 25862306a36Sopenharmony_ci _EMIT6((op1) | reg(b1, b2) << 16 | (rel & 0xffff), (op2) | (mask));\ 25962306a36Sopenharmony_ci REG_SET_SEEN(b1); \ 26062306a36Sopenharmony_ci REG_SET_SEEN(b2); \ 26162306a36Sopenharmony_ci}) 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci#define EMIT6_PCREL_RILB(op, b, target) \ 26462306a36Sopenharmony_ci({ \ 26562306a36Sopenharmony_ci unsigned int rel = (int)((target) - jit->prg) / 2; \ 26662306a36Sopenharmony_ci _EMIT6((op) | reg_high(b) << 16 | rel >> 16, rel & 0xffff);\ 26762306a36Sopenharmony_ci REG_SET_SEEN(b); \ 26862306a36Sopenharmony_ci}) 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci#define EMIT6_PCREL_RIL(op, target) \ 27162306a36Sopenharmony_ci({ \ 27262306a36Sopenharmony_ci unsigned int rel = (int)((target) - jit->prg) / 2; \ 27362306a36Sopenharmony_ci _EMIT6((op) | rel >> 16, rel & 0xffff); \ 27462306a36Sopenharmony_ci}) 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci#define EMIT6_PCREL_RILC(op, mask, target) \ 27762306a36Sopenharmony_ci({ \ 27862306a36Sopenharmony_ci EMIT6_PCREL_RIL((op) | (mask) << 20, (target)); \ 27962306a36Sopenharmony_ci}) 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci#define _EMIT6_IMM(op, imm) \ 28262306a36Sopenharmony_ci({ \ 28362306a36Sopenharmony_ci unsigned int __imm = (imm); \ 28462306a36Sopenharmony_ci _EMIT6((op) | (__imm >> 16), __imm & 0xffff); \ 28562306a36Sopenharmony_ci}) 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci#define EMIT6_IMM(op, b1, imm) \ 28862306a36Sopenharmony_ci({ \ 28962306a36Sopenharmony_ci _EMIT6_IMM((op) | reg_high(b1) << 16, imm); \ 29062306a36Sopenharmony_ci REG_SET_SEEN(b1); \ 29162306a36Sopenharmony_ci}) 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci#define _EMIT_CONST_U32(val) \ 29462306a36Sopenharmony_ci({ \ 29562306a36Sopenharmony_ci unsigned int ret; \ 29662306a36Sopenharmony_ci ret = jit->lit32; \ 29762306a36Sopenharmony_ci if (jit->prg_buf) \ 29862306a36Sopenharmony_ci *(u32 *)(jit->prg_buf + jit->lit32) = (u32)(val);\ 29962306a36Sopenharmony_ci jit->lit32 += 4; \ 30062306a36Sopenharmony_ci ret; \ 30162306a36Sopenharmony_ci}) 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci#define EMIT_CONST_U32(val) \ 30462306a36Sopenharmony_ci({ \ 30562306a36Sopenharmony_ci jit->seen |= SEEN_LITERAL; \ 30662306a36Sopenharmony_ci _EMIT_CONST_U32(val) - jit->base_ip; \ 30762306a36Sopenharmony_ci}) 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci#define _EMIT_CONST_U64(val) \ 31062306a36Sopenharmony_ci({ \ 31162306a36Sopenharmony_ci unsigned int ret; \ 31262306a36Sopenharmony_ci ret = jit->lit64; \ 31362306a36Sopenharmony_ci if (jit->prg_buf) \ 31462306a36Sopenharmony_ci *(u64 *)(jit->prg_buf + jit->lit64) = (u64)(val);\ 31562306a36Sopenharmony_ci jit->lit64 += 8; \ 31662306a36Sopenharmony_ci ret; \ 31762306a36Sopenharmony_ci}) 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci#define EMIT_CONST_U64(val) \ 32062306a36Sopenharmony_ci({ \ 32162306a36Sopenharmony_ci jit->seen |= SEEN_LITERAL; \ 32262306a36Sopenharmony_ci _EMIT_CONST_U64(val) - jit->base_ip; \ 32362306a36Sopenharmony_ci}) 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci#define EMIT_ZERO(b1) \ 32662306a36Sopenharmony_ci({ \ 32762306a36Sopenharmony_ci if (!fp->aux->verifier_zext) { \ 32862306a36Sopenharmony_ci /* llgfr %dst,%dst (zero extend to 64 bit) */ \ 32962306a36Sopenharmony_ci EMIT4(0xb9160000, b1, b1); \ 33062306a36Sopenharmony_ci REG_SET_SEEN(b1); \ 33162306a36Sopenharmony_ci } \ 33262306a36Sopenharmony_ci}) 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci/* 33562306a36Sopenharmony_ci * Return whether this is the first pass. The first pass is special, since we 33662306a36Sopenharmony_ci * don't know any sizes yet, and thus must be conservative. 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_cistatic bool is_first_pass(struct bpf_jit *jit) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci return jit->size == 0; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci/* 34462306a36Sopenharmony_ci * Return whether this is the code generation pass. The code generation pass is 34562306a36Sopenharmony_ci * special, since we should change as little as possible. 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_cistatic bool is_codegen_pass(struct bpf_jit *jit) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci return jit->prg_buf; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/* 35362306a36Sopenharmony_ci * Return whether "rel" can be encoded as a short PC-relative offset 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_cistatic bool is_valid_rel(int rel) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci return rel >= -65536 && rel <= 65534; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci/* 36162306a36Sopenharmony_ci * Return whether "off" can be reached using a short PC-relative offset 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_cistatic bool can_use_rel(struct bpf_jit *jit, int off) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci return is_valid_rel(off - jit->prg); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/* 36962306a36Sopenharmony_ci * Return whether given displacement can be encoded using 37062306a36Sopenharmony_ci * Long-Displacement Facility 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_cistatic bool is_valid_ldisp(int disp) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci return disp >= -524288 && disp <= 524287; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci/* 37862306a36Sopenharmony_ci * Return whether the next 32-bit literal pool entry can be referenced using 37962306a36Sopenharmony_ci * Long-Displacement Facility 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_cistatic bool can_use_ldisp_for_lit32(struct bpf_jit *jit) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci return is_valid_ldisp(jit->lit32 - jit->base_ip); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/* 38762306a36Sopenharmony_ci * Return whether the next 64-bit literal pool entry can be referenced using 38862306a36Sopenharmony_ci * Long-Displacement Facility 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_cistatic bool can_use_ldisp_for_lit64(struct bpf_jit *jit) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci return is_valid_ldisp(jit->lit64 - jit->base_ip); 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci/* 39662306a36Sopenharmony_ci * Fill whole space with illegal instructions 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_cistatic void jit_fill_hole(void *area, unsigned int size) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci memset(area, 0, size); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/* 40462306a36Sopenharmony_ci * Save registers from "rs" (register start) to "re" (register end) on stack 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_cistatic void save_regs(struct bpf_jit *jit, u32 rs, u32 re) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci u32 off = STK_OFF_R6 + (rs - 6) * 8; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (rs == re) 41162306a36Sopenharmony_ci /* stg %rs,off(%r15) */ 41262306a36Sopenharmony_ci _EMIT6(0xe300f000 | rs << 20 | off, 0x0024); 41362306a36Sopenharmony_ci else 41462306a36Sopenharmony_ci /* stmg %rs,%re,off(%r15) */ 41562306a36Sopenharmony_ci _EMIT6_DISP(0xeb00f000 | rs << 20 | re << 16, 0x0024, off); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci/* 41962306a36Sopenharmony_ci * Restore registers from "rs" (register start) to "re" (register end) on stack 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_cistatic void restore_regs(struct bpf_jit *jit, u32 rs, u32 re, u32 stack_depth) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci u32 off = STK_OFF_R6 + (rs - 6) * 8; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (jit->seen & SEEN_STACK) 42662306a36Sopenharmony_ci off += STK_OFF + stack_depth; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (rs == re) 42962306a36Sopenharmony_ci /* lg %rs,off(%r15) */ 43062306a36Sopenharmony_ci _EMIT6(0xe300f000 | rs << 20 | off, 0x0004); 43162306a36Sopenharmony_ci else 43262306a36Sopenharmony_ci /* lmg %rs,%re,off(%r15) */ 43362306a36Sopenharmony_ci _EMIT6_DISP(0xeb00f000 | rs << 20 | re << 16, 0x0004, off); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/* 43762306a36Sopenharmony_ci * Return first seen register (from start) 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_cistatic int get_start(struct bpf_jit *jit, int start) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci int i; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci for (i = start; i <= 15; i++) { 44462306a36Sopenharmony_ci if (jit->seen_reg[i]) 44562306a36Sopenharmony_ci return i; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci/* 45162306a36Sopenharmony_ci * Return last seen register (from start) (gap >= 2) 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_cistatic int get_end(struct bpf_jit *jit, int start) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci int i; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci for (i = start; i < 15; i++) { 45862306a36Sopenharmony_ci if (!jit->seen_reg[i] && !jit->seen_reg[i + 1]) 45962306a36Sopenharmony_ci return i - 1; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci return jit->seen_reg[15] ? 15 : 14; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci#define REGS_SAVE 1 46562306a36Sopenharmony_ci#define REGS_RESTORE 0 46662306a36Sopenharmony_ci/* 46762306a36Sopenharmony_ci * Save and restore clobbered registers (6-15) on stack. 46862306a36Sopenharmony_ci * We save/restore registers in chunks with gap >= 2 registers. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_cistatic void save_restore_regs(struct bpf_jit *jit, int op, u32 stack_depth) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci const int last = 15, save_restore_size = 6; 47362306a36Sopenharmony_ci int re = 6, rs; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (is_first_pass(jit)) { 47662306a36Sopenharmony_ci /* 47762306a36Sopenharmony_ci * We don't know yet which registers are used. Reserve space 47862306a36Sopenharmony_ci * conservatively. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ci jit->prg += (last - re + 1) * save_restore_size; 48162306a36Sopenharmony_ci return; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci do { 48562306a36Sopenharmony_ci rs = get_start(jit, re); 48662306a36Sopenharmony_ci if (!rs) 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci re = get_end(jit, rs + 1); 48962306a36Sopenharmony_ci if (op == REGS_SAVE) 49062306a36Sopenharmony_ci save_regs(jit, rs, re); 49162306a36Sopenharmony_ci else 49262306a36Sopenharmony_ci restore_regs(jit, rs, re, stack_depth); 49362306a36Sopenharmony_ci re++; 49462306a36Sopenharmony_ci } while (re <= last); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic void bpf_skip(struct bpf_jit *jit, int size) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci if (size >= 6 && !is_valid_rel(size)) { 50062306a36Sopenharmony_ci /* brcl 0xf,size */ 50162306a36Sopenharmony_ci EMIT6_PCREL_RIL(0xc0f4000000, size); 50262306a36Sopenharmony_ci size -= 6; 50362306a36Sopenharmony_ci } else if (size >= 4 && is_valid_rel(size)) { 50462306a36Sopenharmony_ci /* brc 0xf,size */ 50562306a36Sopenharmony_ci EMIT4_PCREL(0xa7f40000, size); 50662306a36Sopenharmony_ci size -= 4; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci while (size >= 2) { 50962306a36Sopenharmony_ci /* bcr 0,%0 */ 51062306a36Sopenharmony_ci _EMIT2(0x0700); 51162306a36Sopenharmony_ci size -= 2; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci/* 51662306a36Sopenharmony_ci * PLT for hotpatchable calls. The calling convention is the same as for the 51762306a36Sopenharmony_ci * ftrace hotpatch trampolines: %r0 is return address, %r1 is clobbered. 51862306a36Sopenharmony_ci */ 51962306a36Sopenharmony_ciextern const char bpf_plt[]; 52062306a36Sopenharmony_ciextern const char bpf_plt_ret[]; 52162306a36Sopenharmony_ciextern const char bpf_plt_target[]; 52262306a36Sopenharmony_ciextern const char bpf_plt_end[]; 52362306a36Sopenharmony_ci#define BPF_PLT_SIZE 32 52462306a36Sopenharmony_ciasm( 52562306a36Sopenharmony_ci ".pushsection .rodata\n" 52662306a36Sopenharmony_ci " .balign 8\n" 52762306a36Sopenharmony_ci "bpf_plt:\n" 52862306a36Sopenharmony_ci " lgrl %r0,bpf_plt_ret\n" 52962306a36Sopenharmony_ci " lgrl %r1,bpf_plt_target\n" 53062306a36Sopenharmony_ci " br %r1\n" 53162306a36Sopenharmony_ci " .balign 8\n" 53262306a36Sopenharmony_ci "bpf_plt_ret: .quad 0\n" 53362306a36Sopenharmony_ci "bpf_plt_target: .quad 0\n" 53462306a36Sopenharmony_ci "bpf_plt_end:\n" 53562306a36Sopenharmony_ci " .popsection\n" 53662306a36Sopenharmony_ci); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic void bpf_jit_plt(void *plt, void *ret, void *target) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci memcpy(plt, bpf_plt, BPF_PLT_SIZE); 54162306a36Sopenharmony_ci *(void **)((char *)plt + (bpf_plt_ret - bpf_plt)) = ret; 54262306a36Sopenharmony_ci *(void **)((char *)plt + (bpf_plt_target - bpf_plt)) = target ?: ret; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci/* 54662306a36Sopenharmony_ci * Emit function prologue 54762306a36Sopenharmony_ci * 54862306a36Sopenharmony_ci * Save registers and create stack frame if necessary. 54962306a36Sopenharmony_ci * See stack frame layout description in "bpf_jit.h"! 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_cistatic void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp, 55262306a36Sopenharmony_ci u32 stack_depth) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci /* No-op for hotpatching */ 55562306a36Sopenharmony_ci /* brcl 0,prologue_plt */ 55662306a36Sopenharmony_ci EMIT6_PCREL_RILC(0xc0040000, 0, jit->prologue_plt); 55762306a36Sopenharmony_ci jit->prologue_plt_ret = jit->prg; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (fp->aux->func_idx == 0) { 56062306a36Sopenharmony_ci /* Initialize the tail call counter in the main program. */ 56162306a36Sopenharmony_ci /* xc STK_OFF_TCCNT(4,%r15),STK_OFF_TCCNT(%r15) */ 56262306a36Sopenharmony_ci _EMIT6(0xd703f000 | STK_OFF_TCCNT, 0xf000 | STK_OFF_TCCNT); 56362306a36Sopenharmony_ci } else { 56462306a36Sopenharmony_ci /* 56562306a36Sopenharmony_ci * Skip the tail call counter initialization in subprograms. 56662306a36Sopenharmony_ci * Insert nops in order to have tail_call_start at a 56762306a36Sopenharmony_ci * predictable offset. 56862306a36Sopenharmony_ci */ 56962306a36Sopenharmony_ci bpf_skip(jit, 6); 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci /* Tail calls have to skip above initialization */ 57262306a36Sopenharmony_ci jit->tail_call_start = jit->prg; 57362306a36Sopenharmony_ci /* Save registers */ 57462306a36Sopenharmony_ci save_restore_regs(jit, REGS_SAVE, stack_depth); 57562306a36Sopenharmony_ci /* Setup literal pool */ 57662306a36Sopenharmony_ci if (is_first_pass(jit) || (jit->seen & SEEN_LITERAL)) { 57762306a36Sopenharmony_ci if (!is_first_pass(jit) && 57862306a36Sopenharmony_ci is_valid_ldisp(jit->size - (jit->prg + 2))) { 57962306a36Sopenharmony_ci /* basr %l,0 */ 58062306a36Sopenharmony_ci EMIT2(0x0d00, REG_L, REG_0); 58162306a36Sopenharmony_ci jit->base_ip = jit->prg; 58262306a36Sopenharmony_ci } else { 58362306a36Sopenharmony_ci /* larl %l,lit32_start */ 58462306a36Sopenharmony_ci EMIT6_PCREL_RILB(0xc0000000, REG_L, jit->lit32_start); 58562306a36Sopenharmony_ci jit->base_ip = jit->lit32_start; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci /* Setup stack and backchain */ 58962306a36Sopenharmony_ci if (is_first_pass(jit) || (jit->seen & SEEN_STACK)) { 59062306a36Sopenharmony_ci if (is_first_pass(jit) || (jit->seen & SEEN_FUNC)) 59162306a36Sopenharmony_ci /* lgr %w1,%r15 (backchain) */ 59262306a36Sopenharmony_ci EMIT4(0xb9040000, REG_W1, REG_15); 59362306a36Sopenharmony_ci /* la %bfp,STK_160_UNUSED(%r15) (BPF frame pointer) */ 59462306a36Sopenharmony_ci EMIT4_DISP(0x41000000, BPF_REG_FP, REG_15, STK_160_UNUSED); 59562306a36Sopenharmony_ci /* aghi %r15,-STK_OFF */ 59662306a36Sopenharmony_ci EMIT4_IMM(0xa70b0000, REG_15, -(STK_OFF + stack_depth)); 59762306a36Sopenharmony_ci if (is_first_pass(jit) || (jit->seen & SEEN_FUNC)) 59862306a36Sopenharmony_ci /* stg %w1,152(%r15) (backchain) */ 59962306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0, 60062306a36Sopenharmony_ci REG_15, 152); 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci/* 60562306a36Sopenharmony_ci * Emit an expoline for a jump that follows 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_cistatic void emit_expoline(struct bpf_jit *jit) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci /* exrl %r0,.+10 */ 61062306a36Sopenharmony_ci EMIT6_PCREL_RIL(0xc6000000, jit->prg + 10); 61162306a36Sopenharmony_ci /* j . */ 61262306a36Sopenharmony_ci EMIT4_PCREL(0xa7f40000, 0); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci/* 61662306a36Sopenharmony_ci * Emit __s390_indirect_jump_r1 thunk if necessary 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_cistatic void emit_r1_thunk(struct bpf_jit *jit) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci if (nospec_uses_trampoline()) { 62162306a36Sopenharmony_ci jit->r1_thunk_ip = jit->prg; 62262306a36Sopenharmony_ci emit_expoline(jit); 62362306a36Sopenharmony_ci /* br %r1 */ 62462306a36Sopenharmony_ci _EMIT2(0x07f1); 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci/* 62962306a36Sopenharmony_ci * Call r1 either directly or via __s390_indirect_jump_r1 thunk 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_cistatic void call_r1(struct bpf_jit *jit) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci if (nospec_uses_trampoline()) 63462306a36Sopenharmony_ci /* brasl %r14,__s390_indirect_jump_r1 */ 63562306a36Sopenharmony_ci EMIT6_PCREL_RILB(0xc0050000, REG_14, jit->r1_thunk_ip); 63662306a36Sopenharmony_ci else 63762306a36Sopenharmony_ci /* basr %r14,%r1 */ 63862306a36Sopenharmony_ci EMIT2(0x0d00, REG_14, REG_1); 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci/* 64262306a36Sopenharmony_ci * Function epilogue 64362306a36Sopenharmony_ci */ 64462306a36Sopenharmony_cistatic void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci jit->exit_ip = jit->prg; 64762306a36Sopenharmony_ci /* Load exit code: lgr %r2,%b0 */ 64862306a36Sopenharmony_ci EMIT4(0xb9040000, REG_2, BPF_REG_0); 64962306a36Sopenharmony_ci /* Restore registers */ 65062306a36Sopenharmony_ci save_restore_regs(jit, REGS_RESTORE, stack_depth); 65162306a36Sopenharmony_ci if (nospec_uses_trampoline()) { 65262306a36Sopenharmony_ci jit->r14_thunk_ip = jit->prg; 65362306a36Sopenharmony_ci /* Generate __s390_indirect_jump_r14 thunk */ 65462306a36Sopenharmony_ci emit_expoline(jit); 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci /* br %r14 */ 65762306a36Sopenharmony_ci _EMIT2(0x07fe); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (is_first_pass(jit) || (jit->seen & SEEN_FUNC)) 66062306a36Sopenharmony_ci emit_r1_thunk(jit); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci jit->prg = ALIGN(jit->prg, 8); 66362306a36Sopenharmony_ci jit->prologue_plt = jit->prg; 66462306a36Sopenharmony_ci if (jit->prg_buf) 66562306a36Sopenharmony_ci bpf_jit_plt(jit->prg_buf + jit->prg, 66662306a36Sopenharmony_ci jit->prg_buf + jit->prologue_plt_ret, NULL); 66762306a36Sopenharmony_ci jit->prg += BPF_PLT_SIZE; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic int get_probe_mem_regno(const u8 *insn) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci /* 67362306a36Sopenharmony_ci * insn must point to llgc, llgh, llgf or lg, which have destination 67462306a36Sopenharmony_ci * register at the same position. 67562306a36Sopenharmony_ci */ 67662306a36Sopenharmony_ci if (insn[0] != 0xe3) /* common llgc, llgh, llgf and lg prefix */ 67762306a36Sopenharmony_ci return -1; 67862306a36Sopenharmony_ci if (insn[5] != 0x90 && /* llgc */ 67962306a36Sopenharmony_ci insn[5] != 0x91 && /* llgh */ 68062306a36Sopenharmony_ci insn[5] != 0x16 && /* llgf */ 68162306a36Sopenharmony_ci insn[5] != 0x04) /* lg */ 68262306a36Sopenharmony_ci return -1; 68362306a36Sopenharmony_ci return insn[1] >> 4; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cibool ex_handler_bpf(const struct exception_table_entry *x, struct pt_regs *regs) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci regs->psw.addr = extable_fixup(x); 68962306a36Sopenharmony_ci regs->gprs[x->data] = 0; 69062306a36Sopenharmony_ci return true; 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic int bpf_jit_probe_mem(struct bpf_jit *jit, struct bpf_prog *fp, 69462306a36Sopenharmony_ci int probe_prg, int nop_prg) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct exception_table_entry *ex; 69762306a36Sopenharmony_ci int reg, prg; 69862306a36Sopenharmony_ci s64 delta; 69962306a36Sopenharmony_ci u8 *insn; 70062306a36Sopenharmony_ci int i; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (!fp->aux->extable) 70362306a36Sopenharmony_ci /* Do nothing during early JIT passes. */ 70462306a36Sopenharmony_ci return 0; 70562306a36Sopenharmony_ci insn = jit->prg_buf + probe_prg; 70662306a36Sopenharmony_ci reg = get_probe_mem_regno(insn); 70762306a36Sopenharmony_ci if (WARN_ON_ONCE(reg < 0)) 70862306a36Sopenharmony_ci /* JIT bug - unexpected probe instruction. */ 70962306a36Sopenharmony_ci return -1; 71062306a36Sopenharmony_ci if (WARN_ON_ONCE(probe_prg + insn_length(*insn) != nop_prg)) 71162306a36Sopenharmony_ci /* JIT bug - gap between probe and nop instructions. */ 71262306a36Sopenharmony_ci return -1; 71362306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 71462306a36Sopenharmony_ci if (WARN_ON_ONCE(jit->excnt >= fp->aux->num_exentries)) 71562306a36Sopenharmony_ci /* Verifier bug - not enough entries. */ 71662306a36Sopenharmony_ci return -1; 71762306a36Sopenharmony_ci ex = &fp->aux->extable[jit->excnt]; 71862306a36Sopenharmony_ci /* Add extable entries for probe and nop instructions. */ 71962306a36Sopenharmony_ci prg = i == 0 ? probe_prg : nop_prg; 72062306a36Sopenharmony_ci delta = jit->prg_buf + prg - (u8 *)&ex->insn; 72162306a36Sopenharmony_ci if (WARN_ON_ONCE(delta < INT_MIN || delta > INT_MAX)) 72262306a36Sopenharmony_ci /* JIT bug - code and extable must be close. */ 72362306a36Sopenharmony_ci return -1; 72462306a36Sopenharmony_ci ex->insn = delta; 72562306a36Sopenharmony_ci /* 72662306a36Sopenharmony_ci * Always land on the nop. Note that extable infrastructure 72762306a36Sopenharmony_ci * ignores fixup field, it is handled by ex_handler_bpf(). 72862306a36Sopenharmony_ci */ 72962306a36Sopenharmony_ci delta = jit->prg_buf + nop_prg - (u8 *)&ex->fixup; 73062306a36Sopenharmony_ci if (WARN_ON_ONCE(delta < INT_MIN || delta > INT_MAX)) 73162306a36Sopenharmony_ci /* JIT bug - landing pad and extable must be close. */ 73262306a36Sopenharmony_ci return -1; 73362306a36Sopenharmony_ci ex->fixup = delta; 73462306a36Sopenharmony_ci ex->type = EX_TYPE_BPF; 73562306a36Sopenharmony_ci ex->data = reg; 73662306a36Sopenharmony_ci jit->excnt++; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci return 0; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci/* 74262306a36Sopenharmony_ci * Sign-extend the register if necessary 74362306a36Sopenharmony_ci */ 74462306a36Sopenharmony_cistatic int sign_extend(struct bpf_jit *jit, int r, u8 size, u8 flags) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci if (!(flags & BTF_FMODEL_SIGNED_ARG)) 74762306a36Sopenharmony_ci return 0; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci switch (size) { 75062306a36Sopenharmony_ci case 1: 75162306a36Sopenharmony_ci /* lgbr %r,%r */ 75262306a36Sopenharmony_ci EMIT4(0xb9060000, r, r); 75362306a36Sopenharmony_ci return 0; 75462306a36Sopenharmony_ci case 2: 75562306a36Sopenharmony_ci /* lghr %r,%r */ 75662306a36Sopenharmony_ci EMIT4(0xb9070000, r, r); 75762306a36Sopenharmony_ci return 0; 75862306a36Sopenharmony_ci case 4: 75962306a36Sopenharmony_ci /* lgfr %r,%r */ 76062306a36Sopenharmony_ci EMIT4(0xb9140000, r, r); 76162306a36Sopenharmony_ci return 0; 76262306a36Sopenharmony_ci case 8: 76362306a36Sopenharmony_ci return 0; 76462306a36Sopenharmony_ci default: 76562306a36Sopenharmony_ci return -1; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci/* 77062306a36Sopenharmony_ci * Compile one eBPF instruction into s390x code 77162306a36Sopenharmony_ci * 77262306a36Sopenharmony_ci * NOTE: Use noinline because for gcov (-fprofile-arcs) gcc allocates a lot of 77362306a36Sopenharmony_ci * stack space for the large switch statement. 77462306a36Sopenharmony_ci */ 77562306a36Sopenharmony_cistatic noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, 77662306a36Sopenharmony_ci int i, bool extra_pass, u32 stack_depth) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci struct bpf_insn *insn = &fp->insnsi[i]; 77962306a36Sopenharmony_ci u32 dst_reg = insn->dst_reg; 78062306a36Sopenharmony_ci u32 src_reg = insn->src_reg; 78162306a36Sopenharmony_ci int last, insn_count = 1; 78262306a36Sopenharmony_ci u32 *addrs = jit->addrs; 78362306a36Sopenharmony_ci s32 imm = insn->imm; 78462306a36Sopenharmony_ci s16 off = insn->off; 78562306a36Sopenharmony_ci int probe_prg = -1; 78662306a36Sopenharmony_ci unsigned int mask; 78762306a36Sopenharmony_ci int nop_prg; 78862306a36Sopenharmony_ci int err; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (BPF_CLASS(insn->code) == BPF_LDX && 79162306a36Sopenharmony_ci BPF_MODE(insn->code) == BPF_PROBE_MEM) 79262306a36Sopenharmony_ci probe_prg = jit->prg; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci switch (insn->code) { 79562306a36Sopenharmony_ci /* 79662306a36Sopenharmony_ci * BPF_MOV 79762306a36Sopenharmony_ci */ 79862306a36Sopenharmony_ci case BPF_ALU | BPF_MOV | BPF_X: /* dst = (u32) src */ 79962306a36Sopenharmony_ci /* llgfr %dst,%src */ 80062306a36Sopenharmony_ci EMIT4(0xb9160000, dst_reg, src_reg); 80162306a36Sopenharmony_ci if (insn_is_zext(&insn[1])) 80262306a36Sopenharmony_ci insn_count = 2; 80362306a36Sopenharmony_ci break; 80462306a36Sopenharmony_ci case BPF_ALU64 | BPF_MOV | BPF_X: /* dst = src */ 80562306a36Sopenharmony_ci /* lgr %dst,%src */ 80662306a36Sopenharmony_ci EMIT4(0xb9040000, dst_reg, src_reg); 80762306a36Sopenharmony_ci break; 80862306a36Sopenharmony_ci case BPF_ALU | BPF_MOV | BPF_K: /* dst = (u32) imm */ 80962306a36Sopenharmony_ci /* llilf %dst,imm */ 81062306a36Sopenharmony_ci EMIT6_IMM(0xc00f0000, dst_reg, imm); 81162306a36Sopenharmony_ci if (insn_is_zext(&insn[1])) 81262306a36Sopenharmony_ci insn_count = 2; 81362306a36Sopenharmony_ci break; 81462306a36Sopenharmony_ci case BPF_ALU64 | BPF_MOV | BPF_K: /* dst = imm */ 81562306a36Sopenharmony_ci /* lgfi %dst,imm */ 81662306a36Sopenharmony_ci EMIT6_IMM(0xc0010000, dst_reg, imm); 81762306a36Sopenharmony_ci break; 81862306a36Sopenharmony_ci /* 81962306a36Sopenharmony_ci * BPF_LD 64 82062306a36Sopenharmony_ci */ 82162306a36Sopenharmony_ci case BPF_LD | BPF_IMM | BPF_DW: /* dst = (u64) imm */ 82262306a36Sopenharmony_ci { 82362306a36Sopenharmony_ci /* 16 byte instruction that uses two 'struct bpf_insn' */ 82462306a36Sopenharmony_ci u64 imm64; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci imm64 = (u64)(u32) insn[0].imm | ((u64)(u32) insn[1].imm) << 32; 82762306a36Sopenharmony_ci /* lgrl %dst,imm */ 82862306a36Sopenharmony_ci EMIT6_PCREL_RILB(0xc4080000, dst_reg, _EMIT_CONST_U64(imm64)); 82962306a36Sopenharmony_ci insn_count = 2; 83062306a36Sopenharmony_ci break; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci /* 83362306a36Sopenharmony_ci * BPF_ADD 83462306a36Sopenharmony_ci */ 83562306a36Sopenharmony_ci case BPF_ALU | BPF_ADD | BPF_X: /* dst = (u32) dst + (u32) src */ 83662306a36Sopenharmony_ci /* ar %dst,%src */ 83762306a36Sopenharmony_ci EMIT2(0x1a00, dst_reg, src_reg); 83862306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 83962306a36Sopenharmony_ci break; 84062306a36Sopenharmony_ci case BPF_ALU64 | BPF_ADD | BPF_X: /* dst = dst + src */ 84162306a36Sopenharmony_ci /* agr %dst,%src */ 84262306a36Sopenharmony_ci EMIT4(0xb9080000, dst_reg, src_reg); 84362306a36Sopenharmony_ci break; 84462306a36Sopenharmony_ci case BPF_ALU | BPF_ADD | BPF_K: /* dst = (u32) dst + (u32) imm */ 84562306a36Sopenharmony_ci if (imm != 0) { 84662306a36Sopenharmony_ci /* alfi %dst,imm */ 84762306a36Sopenharmony_ci EMIT6_IMM(0xc20b0000, dst_reg, imm); 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 85062306a36Sopenharmony_ci break; 85162306a36Sopenharmony_ci case BPF_ALU64 | BPF_ADD | BPF_K: /* dst = dst + imm */ 85262306a36Sopenharmony_ci if (!imm) 85362306a36Sopenharmony_ci break; 85462306a36Sopenharmony_ci /* agfi %dst,imm */ 85562306a36Sopenharmony_ci EMIT6_IMM(0xc2080000, dst_reg, imm); 85662306a36Sopenharmony_ci break; 85762306a36Sopenharmony_ci /* 85862306a36Sopenharmony_ci * BPF_SUB 85962306a36Sopenharmony_ci */ 86062306a36Sopenharmony_ci case BPF_ALU | BPF_SUB | BPF_X: /* dst = (u32) dst - (u32) src */ 86162306a36Sopenharmony_ci /* sr %dst,%src */ 86262306a36Sopenharmony_ci EMIT2(0x1b00, dst_reg, src_reg); 86362306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 86462306a36Sopenharmony_ci break; 86562306a36Sopenharmony_ci case BPF_ALU64 | BPF_SUB | BPF_X: /* dst = dst - src */ 86662306a36Sopenharmony_ci /* sgr %dst,%src */ 86762306a36Sopenharmony_ci EMIT4(0xb9090000, dst_reg, src_reg); 86862306a36Sopenharmony_ci break; 86962306a36Sopenharmony_ci case BPF_ALU | BPF_SUB | BPF_K: /* dst = (u32) dst - (u32) imm */ 87062306a36Sopenharmony_ci if (imm != 0) { 87162306a36Sopenharmony_ci /* alfi %dst,-imm */ 87262306a36Sopenharmony_ci EMIT6_IMM(0xc20b0000, dst_reg, -imm); 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 87562306a36Sopenharmony_ci break; 87662306a36Sopenharmony_ci case BPF_ALU64 | BPF_SUB | BPF_K: /* dst = dst - imm */ 87762306a36Sopenharmony_ci if (!imm) 87862306a36Sopenharmony_ci break; 87962306a36Sopenharmony_ci if (imm == -0x80000000) { 88062306a36Sopenharmony_ci /* algfi %dst,0x80000000 */ 88162306a36Sopenharmony_ci EMIT6_IMM(0xc20a0000, dst_reg, 0x80000000); 88262306a36Sopenharmony_ci } else { 88362306a36Sopenharmony_ci /* agfi %dst,-imm */ 88462306a36Sopenharmony_ci EMIT6_IMM(0xc2080000, dst_reg, -imm); 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci break; 88762306a36Sopenharmony_ci /* 88862306a36Sopenharmony_ci * BPF_MUL 88962306a36Sopenharmony_ci */ 89062306a36Sopenharmony_ci case BPF_ALU | BPF_MUL | BPF_X: /* dst = (u32) dst * (u32) src */ 89162306a36Sopenharmony_ci /* msr %dst,%src */ 89262306a36Sopenharmony_ci EMIT4(0xb2520000, dst_reg, src_reg); 89362306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 89462306a36Sopenharmony_ci break; 89562306a36Sopenharmony_ci case BPF_ALU64 | BPF_MUL | BPF_X: /* dst = dst * src */ 89662306a36Sopenharmony_ci /* msgr %dst,%src */ 89762306a36Sopenharmony_ci EMIT4(0xb90c0000, dst_reg, src_reg); 89862306a36Sopenharmony_ci break; 89962306a36Sopenharmony_ci case BPF_ALU | BPF_MUL | BPF_K: /* dst = (u32) dst * (u32) imm */ 90062306a36Sopenharmony_ci if (imm != 1) { 90162306a36Sopenharmony_ci /* msfi %r5,imm */ 90262306a36Sopenharmony_ci EMIT6_IMM(0xc2010000, dst_reg, imm); 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 90562306a36Sopenharmony_ci break; 90662306a36Sopenharmony_ci case BPF_ALU64 | BPF_MUL | BPF_K: /* dst = dst * imm */ 90762306a36Sopenharmony_ci if (imm == 1) 90862306a36Sopenharmony_ci break; 90962306a36Sopenharmony_ci /* msgfi %dst,imm */ 91062306a36Sopenharmony_ci EMIT6_IMM(0xc2000000, dst_reg, imm); 91162306a36Sopenharmony_ci break; 91262306a36Sopenharmony_ci /* 91362306a36Sopenharmony_ci * BPF_DIV / BPF_MOD 91462306a36Sopenharmony_ci */ 91562306a36Sopenharmony_ci case BPF_ALU | BPF_DIV | BPF_X: /* dst = (u32) dst / (u32) src */ 91662306a36Sopenharmony_ci case BPF_ALU | BPF_MOD | BPF_X: /* dst = (u32) dst % (u32) src */ 91762306a36Sopenharmony_ci { 91862306a36Sopenharmony_ci int rc_reg = BPF_OP(insn->code) == BPF_DIV ? REG_W1 : REG_W0; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci /* lhi %w0,0 */ 92162306a36Sopenharmony_ci EMIT4_IMM(0xa7080000, REG_W0, 0); 92262306a36Sopenharmony_ci /* lr %w1,%dst */ 92362306a36Sopenharmony_ci EMIT2(0x1800, REG_W1, dst_reg); 92462306a36Sopenharmony_ci /* dlr %w0,%src */ 92562306a36Sopenharmony_ci EMIT4(0xb9970000, REG_W0, src_reg); 92662306a36Sopenharmony_ci /* llgfr %dst,%rc */ 92762306a36Sopenharmony_ci EMIT4(0xb9160000, dst_reg, rc_reg); 92862306a36Sopenharmony_ci if (insn_is_zext(&insn[1])) 92962306a36Sopenharmony_ci insn_count = 2; 93062306a36Sopenharmony_ci break; 93162306a36Sopenharmony_ci } 93262306a36Sopenharmony_ci case BPF_ALU64 | BPF_DIV | BPF_X: /* dst = dst / src */ 93362306a36Sopenharmony_ci case BPF_ALU64 | BPF_MOD | BPF_X: /* dst = dst % src */ 93462306a36Sopenharmony_ci { 93562306a36Sopenharmony_ci int rc_reg = BPF_OP(insn->code) == BPF_DIV ? REG_W1 : REG_W0; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci /* lghi %w0,0 */ 93862306a36Sopenharmony_ci EMIT4_IMM(0xa7090000, REG_W0, 0); 93962306a36Sopenharmony_ci /* lgr %w1,%dst */ 94062306a36Sopenharmony_ci EMIT4(0xb9040000, REG_W1, dst_reg); 94162306a36Sopenharmony_ci /* dlgr %w0,%dst */ 94262306a36Sopenharmony_ci EMIT4(0xb9870000, REG_W0, src_reg); 94362306a36Sopenharmony_ci /* lgr %dst,%rc */ 94462306a36Sopenharmony_ci EMIT4(0xb9040000, dst_reg, rc_reg); 94562306a36Sopenharmony_ci break; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci case BPF_ALU | BPF_DIV | BPF_K: /* dst = (u32) dst / (u32) imm */ 94862306a36Sopenharmony_ci case BPF_ALU | BPF_MOD | BPF_K: /* dst = (u32) dst % (u32) imm */ 94962306a36Sopenharmony_ci { 95062306a36Sopenharmony_ci int rc_reg = BPF_OP(insn->code) == BPF_DIV ? REG_W1 : REG_W0; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (imm == 1) { 95362306a36Sopenharmony_ci if (BPF_OP(insn->code) == BPF_MOD) 95462306a36Sopenharmony_ci /* lhgi %dst,0 */ 95562306a36Sopenharmony_ci EMIT4_IMM(0xa7090000, dst_reg, 0); 95662306a36Sopenharmony_ci else 95762306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 95862306a36Sopenharmony_ci break; 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci /* lhi %w0,0 */ 96162306a36Sopenharmony_ci EMIT4_IMM(0xa7080000, REG_W0, 0); 96262306a36Sopenharmony_ci /* lr %w1,%dst */ 96362306a36Sopenharmony_ci EMIT2(0x1800, REG_W1, dst_reg); 96462306a36Sopenharmony_ci if (!is_first_pass(jit) && can_use_ldisp_for_lit32(jit)) { 96562306a36Sopenharmony_ci /* dl %w0,<d(imm)>(%l) */ 96662306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0097, REG_W0, REG_0, REG_L, 96762306a36Sopenharmony_ci EMIT_CONST_U32(imm)); 96862306a36Sopenharmony_ci } else { 96962306a36Sopenharmony_ci /* lgfrl %dst,imm */ 97062306a36Sopenharmony_ci EMIT6_PCREL_RILB(0xc40c0000, dst_reg, 97162306a36Sopenharmony_ci _EMIT_CONST_U32(imm)); 97262306a36Sopenharmony_ci jit->seen |= SEEN_LITERAL; 97362306a36Sopenharmony_ci /* dlr %w0,%dst */ 97462306a36Sopenharmony_ci EMIT4(0xb9970000, REG_W0, dst_reg); 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci /* llgfr %dst,%rc */ 97762306a36Sopenharmony_ci EMIT4(0xb9160000, dst_reg, rc_reg); 97862306a36Sopenharmony_ci if (insn_is_zext(&insn[1])) 97962306a36Sopenharmony_ci insn_count = 2; 98062306a36Sopenharmony_ci break; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci case BPF_ALU64 | BPF_DIV | BPF_K: /* dst = dst / imm */ 98362306a36Sopenharmony_ci case BPF_ALU64 | BPF_MOD | BPF_K: /* dst = dst % imm */ 98462306a36Sopenharmony_ci { 98562306a36Sopenharmony_ci int rc_reg = BPF_OP(insn->code) == BPF_DIV ? REG_W1 : REG_W0; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (imm == 1) { 98862306a36Sopenharmony_ci if (BPF_OP(insn->code) == BPF_MOD) 98962306a36Sopenharmony_ci /* lhgi %dst,0 */ 99062306a36Sopenharmony_ci EMIT4_IMM(0xa7090000, dst_reg, 0); 99162306a36Sopenharmony_ci break; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci /* lghi %w0,0 */ 99462306a36Sopenharmony_ci EMIT4_IMM(0xa7090000, REG_W0, 0); 99562306a36Sopenharmony_ci /* lgr %w1,%dst */ 99662306a36Sopenharmony_ci EMIT4(0xb9040000, REG_W1, dst_reg); 99762306a36Sopenharmony_ci if (!is_first_pass(jit) && can_use_ldisp_for_lit64(jit)) { 99862306a36Sopenharmony_ci /* dlg %w0,<d(imm)>(%l) */ 99962306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0087, REG_W0, REG_0, REG_L, 100062306a36Sopenharmony_ci EMIT_CONST_U64(imm)); 100162306a36Sopenharmony_ci } else { 100262306a36Sopenharmony_ci /* lgrl %dst,imm */ 100362306a36Sopenharmony_ci EMIT6_PCREL_RILB(0xc4080000, dst_reg, 100462306a36Sopenharmony_ci _EMIT_CONST_U64(imm)); 100562306a36Sopenharmony_ci jit->seen |= SEEN_LITERAL; 100662306a36Sopenharmony_ci /* dlgr %w0,%dst */ 100762306a36Sopenharmony_ci EMIT4(0xb9870000, REG_W0, dst_reg); 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci /* lgr %dst,%rc */ 101062306a36Sopenharmony_ci EMIT4(0xb9040000, dst_reg, rc_reg); 101162306a36Sopenharmony_ci break; 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci /* 101462306a36Sopenharmony_ci * BPF_AND 101562306a36Sopenharmony_ci */ 101662306a36Sopenharmony_ci case BPF_ALU | BPF_AND | BPF_X: /* dst = (u32) dst & (u32) src */ 101762306a36Sopenharmony_ci /* nr %dst,%src */ 101862306a36Sopenharmony_ci EMIT2(0x1400, dst_reg, src_reg); 101962306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 102062306a36Sopenharmony_ci break; 102162306a36Sopenharmony_ci case BPF_ALU64 | BPF_AND | BPF_X: /* dst = dst & src */ 102262306a36Sopenharmony_ci /* ngr %dst,%src */ 102362306a36Sopenharmony_ci EMIT4(0xb9800000, dst_reg, src_reg); 102462306a36Sopenharmony_ci break; 102562306a36Sopenharmony_ci case BPF_ALU | BPF_AND | BPF_K: /* dst = (u32) dst & (u32) imm */ 102662306a36Sopenharmony_ci /* nilf %dst,imm */ 102762306a36Sopenharmony_ci EMIT6_IMM(0xc00b0000, dst_reg, imm); 102862306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 102962306a36Sopenharmony_ci break; 103062306a36Sopenharmony_ci case BPF_ALU64 | BPF_AND | BPF_K: /* dst = dst & imm */ 103162306a36Sopenharmony_ci if (!is_first_pass(jit) && can_use_ldisp_for_lit64(jit)) { 103262306a36Sopenharmony_ci /* ng %dst,<d(imm)>(%l) */ 103362306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0080, 103462306a36Sopenharmony_ci dst_reg, REG_0, REG_L, 103562306a36Sopenharmony_ci EMIT_CONST_U64(imm)); 103662306a36Sopenharmony_ci } else { 103762306a36Sopenharmony_ci /* lgrl %w0,imm */ 103862306a36Sopenharmony_ci EMIT6_PCREL_RILB(0xc4080000, REG_W0, 103962306a36Sopenharmony_ci _EMIT_CONST_U64(imm)); 104062306a36Sopenharmony_ci jit->seen |= SEEN_LITERAL; 104162306a36Sopenharmony_ci /* ngr %dst,%w0 */ 104262306a36Sopenharmony_ci EMIT4(0xb9800000, dst_reg, REG_W0); 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci break; 104562306a36Sopenharmony_ci /* 104662306a36Sopenharmony_ci * BPF_OR 104762306a36Sopenharmony_ci */ 104862306a36Sopenharmony_ci case BPF_ALU | BPF_OR | BPF_X: /* dst = (u32) dst | (u32) src */ 104962306a36Sopenharmony_ci /* or %dst,%src */ 105062306a36Sopenharmony_ci EMIT2(0x1600, dst_reg, src_reg); 105162306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 105262306a36Sopenharmony_ci break; 105362306a36Sopenharmony_ci case BPF_ALU64 | BPF_OR | BPF_X: /* dst = dst | src */ 105462306a36Sopenharmony_ci /* ogr %dst,%src */ 105562306a36Sopenharmony_ci EMIT4(0xb9810000, dst_reg, src_reg); 105662306a36Sopenharmony_ci break; 105762306a36Sopenharmony_ci case BPF_ALU | BPF_OR | BPF_K: /* dst = (u32) dst | (u32) imm */ 105862306a36Sopenharmony_ci /* oilf %dst,imm */ 105962306a36Sopenharmony_ci EMIT6_IMM(0xc00d0000, dst_reg, imm); 106062306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 106162306a36Sopenharmony_ci break; 106262306a36Sopenharmony_ci case BPF_ALU64 | BPF_OR | BPF_K: /* dst = dst | imm */ 106362306a36Sopenharmony_ci if (!is_first_pass(jit) && can_use_ldisp_for_lit64(jit)) { 106462306a36Sopenharmony_ci /* og %dst,<d(imm)>(%l) */ 106562306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0081, 106662306a36Sopenharmony_ci dst_reg, REG_0, REG_L, 106762306a36Sopenharmony_ci EMIT_CONST_U64(imm)); 106862306a36Sopenharmony_ci } else { 106962306a36Sopenharmony_ci /* lgrl %w0,imm */ 107062306a36Sopenharmony_ci EMIT6_PCREL_RILB(0xc4080000, REG_W0, 107162306a36Sopenharmony_ci _EMIT_CONST_U64(imm)); 107262306a36Sopenharmony_ci jit->seen |= SEEN_LITERAL; 107362306a36Sopenharmony_ci /* ogr %dst,%w0 */ 107462306a36Sopenharmony_ci EMIT4(0xb9810000, dst_reg, REG_W0); 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci break; 107762306a36Sopenharmony_ci /* 107862306a36Sopenharmony_ci * BPF_XOR 107962306a36Sopenharmony_ci */ 108062306a36Sopenharmony_ci case BPF_ALU | BPF_XOR | BPF_X: /* dst = (u32) dst ^ (u32) src */ 108162306a36Sopenharmony_ci /* xr %dst,%src */ 108262306a36Sopenharmony_ci EMIT2(0x1700, dst_reg, src_reg); 108362306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 108462306a36Sopenharmony_ci break; 108562306a36Sopenharmony_ci case BPF_ALU64 | BPF_XOR | BPF_X: /* dst = dst ^ src */ 108662306a36Sopenharmony_ci /* xgr %dst,%src */ 108762306a36Sopenharmony_ci EMIT4(0xb9820000, dst_reg, src_reg); 108862306a36Sopenharmony_ci break; 108962306a36Sopenharmony_ci case BPF_ALU | BPF_XOR | BPF_K: /* dst = (u32) dst ^ (u32) imm */ 109062306a36Sopenharmony_ci if (imm != 0) { 109162306a36Sopenharmony_ci /* xilf %dst,imm */ 109262306a36Sopenharmony_ci EMIT6_IMM(0xc0070000, dst_reg, imm); 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 109562306a36Sopenharmony_ci break; 109662306a36Sopenharmony_ci case BPF_ALU64 | BPF_XOR | BPF_K: /* dst = dst ^ imm */ 109762306a36Sopenharmony_ci if (!is_first_pass(jit) && can_use_ldisp_for_lit64(jit)) { 109862306a36Sopenharmony_ci /* xg %dst,<d(imm)>(%l) */ 109962306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0082, 110062306a36Sopenharmony_ci dst_reg, REG_0, REG_L, 110162306a36Sopenharmony_ci EMIT_CONST_U64(imm)); 110262306a36Sopenharmony_ci } else { 110362306a36Sopenharmony_ci /* lgrl %w0,imm */ 110462306a36Sopenharmony_ci EMIT6_PCREL_RILB(0xc4080000, REG_W0, 110562306a36Sopenharmony_ci _EMIT_CONST_U64(imm)); 110662306a36Sopenharmony_ci jit->seen |= SEEN_LITERAL; 110762306a36Sopenharmony_ci /* xgr %dst,%w0 */ 110862306a36Sopenharmony_ci EMIT4(0xb9820000, dst_reg, REG_W0); 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci break; 111162306a36Sopenharmony_ci /* 111262306a36Sopenharmony_ci * BPF_LSH 111362306a36Sopenharmony_ci */ 111462306a36Sopenharmony_ci case BPF_ALU | BPF_LSH | BPF_X: /* dst = (u32) dst << (u32) src */ 111562306a36Sopenharmony_ci /* sll %dst,0(%src) */ 111662306a36Sopenharmony_ci EMIT4_DISP(0x89000000, dst_reg, src_reg, 0); 111762306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 111862306a36Sopenharmony_ci break; 111962306a36Sopenharmony_ci case BPF_ALU64 | BPF_LSH | BPF_X: /* dst = dst << src */ 112062306a36Sopenharmony_ci /* sllg %dst,%dst,0(%src) */ 112162306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, 0x000d, dst_reg, dst_reg, src_reg, 0); 112262306a36Sopenharmony_ci break; 112362306a36Sopenharmony_ci case BPF_ALU | BPF_LSH | BPF_K: /* dst = (u32) dst << (u32) imm */ 112462306a36Sopenharmony_ci if (imm != 0) { 112562306a36Sopenharmony_ci /* sll %dst,imm(%r0) */ 112662306a36Sopenharmony_ci EMIT4_DISP(0x89000000, dst_reg, REG_0, imm); 112762306a36Sopenharmony_ci } 112862306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 112962306a36Sopenharmony_ci break; 113062306a36Sopenharmony_ci case BPF_ALU64 | BPF_LSH | BPF_K: /* dst = dst << imm */ 113162306a36Sopenharmony_ci if (imm == 0) 113262306a36Sopenharmony_ci break; 113362306a36Sopenharmony_ci /* sllg %dst,%dst,imm(%r0) */ 113462306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, 0x000d, dst_reg, dst_reg, REG_0, imm); 113562306a36Sopenharmony_ci break; 113662306a36Sopenharmony_ci /* 113762306a36Sopenharmony_ci * BPF_RSH 113862306a36Sopenharmony_ci */ 113962306a36Sopenharmony_ci case BPF_ALU | BPF_RSH | BPF_X: /* dst = (u32) dst >> (u32) src */ 114062306a36Sopenharmony_ci /* srl %dst,0(%src) */ 114162306a36Sopenharmony_ci EMIT4_DISP(0x88000000, dst_reg, src_reg, 0); 114262306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 114362306a36Sopenharmony_ci break; 114462306a36Sopenharmony_ci case BPF_ALU64 | BPF_RSH | BPF_X: /* dst = dst >> src */ 114562306a36Sopenharmony_ci /* srlg %dst,%dst,0(%src) */ 114662306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, 0x000c, dst_reg, dst_reg, src_reg, 0); 114762306a36Sopenharmony_ci break; 114862306a36Sopenharmony_ci case BPF_ALU | BPF_RSH | BPF_K: /* dst = (u32) dst >> (u32) imm */ 114962306a36Sopenharmony_ci if (imm != 0) { 115062306a36Sopenharmony_ci /* srl %dst,imm(%r0) */ 115162306a36Sopenharmony_ci EMIT4_DISP(0x88000000, dst_reg, REG_0, imm); 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 115462306a36Sopenharmony_ci break; 115562306a36Sopenharmony_ci case BPF_ALU64 | BPF_RSH | BPF_K: /* dst = dst >> imm */ 115662306a36Sopenharmony_ci if (imm == 0) 115762306a36Sopenharmony_ci break; 115862306a36Sopenharmony_ci /* srlg %dst,%dst,imm(%r0) */ 115962306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, 0x000c, dst_reg, dst_reg, REG_0, imm); 116062306a36Sopenharmony_ci break; 116162306a36Sopenharmony_ci /* 116262306a36Sopenharmony_ci * BPF_ARSH 116362306a36Sopenharmony_ci */ 116462306a36Sopenharmony_ci case BPF_ALU | BPF_ARSH | BPF_X: /* ((s32) dst) >>= src */ 116562306a36Sopenharmony_ci /* sra %dst,%dst,0(%src) */ 116662306a36Sopenharmony_ci EMIT4_DISP(0x8a000000, dst_reg, src_reg, 0); 116762306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 116862306a36Sopenharmony_ci break; 116962306a36Sopenharmony_ci case BPF_ALU64 | BPF_ARSH | BPF_X: /* ((s64) dst) >>= src */ 117062306a36Sopenharmony_ci /* srag %dst,%dst,0(%src) */ 117162306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, 0x000a, dst_reg, dst_reg, src_reg, 0); 117262306a36Sopenharmony_ci break; 117362306a36Sopenharmony_ci case BPF_ALU | BPF_ARSH | BPF_K: /* ((s32) dst >> imm */ 117462306a36Sopenharmony_ci if (imm != 0) { 117562306a36Sopenharmony_ci /* sra %dst,imm(%r0) */ 117662306a36Sopenharmony_ci EMIT4_DISP(0x8a000000, dst_reg, REG_0, imm); 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 117962306a36Sopenharmony_ci break; 118062306a36Sopenharmony_ci case BPF_ALU64 | BPF_ARSH | BPF_K: /* ((s64) dst) >>= imm */ 118162306a36Sopenharmony_ci if (imm == 0) 118262306a36Sopenharmony_ci break; 118362306a36Sopenharmony_ci /* srag %dst,%dst,imm(%r0) */ 118462306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, 0x000a, dst_reg, dst_reg, REG_0, imm); 118562306a36Sopenharmony_ci break; 118662306a36Sopenharmony_ci /* 118762306a36Sopenharmony_ci * BPF_NEG 118862306a36Sopenharmony_ci */ 118962306a36Sopenharmony_ci case BPF_ALU | BPF_NEG: /* dst = (u32) -dst */ 119062306a36Sopenharmony_ci /* lcr %dst,%dst */ 119162306a36Sopenharmony_ci EMIT2(0x1300, dst_reg, dst_reg); 119262306a36Sopenharmony_ci EMIT_ZERO(dst_reg); 119362306a36Sopenharmony_ci break; 119462306a36Sopenharmony_ci case BPF_ALU64 | BPF_NEG: /* dst = -dst */ 119562306a36Sopenharmony_ci /* lcgr %dst,%dst */ 119662306a36Sopenharmony_ci EMIT4(0xb9030000, dst_reg, dst_reg); 119762306a36Sopenharmony_ci break; 119862306a36Sopenharmony_ci /* 119962306a36Sopenharmony_ci * BPF_FROM_BE/LE 120062306a36Sopenharmony_ci */ 120162306a36Sopenharmony_ci case BPF_ALU | BPF_END | BPF_FROM_BE: 120262306a36Sopenharmony_ci /* s390 is big endian, therefore only clear high order bytes */ 120362306a36Sopenharmony_ci switch (imm) { 120462306a36Sopenharmony_ci case 16: /* dst = (u16) cpu_to_be16(dst) */ 120562306a36Sopenharmony_ci /* llghr %dst,%dst */ 120662306a36Sopenharmony_ci EMIT4(0xb9850000, dst_reg, dst_reg); 120762306a36Sopenharmony_ci if (insn_is_zext(&insn[1])) 120862306a36Sopenharmony_ci insn_count = 2; 120962306a36Sopenharmony_ci break; 121062306a36Sopenharmony_ci case 32: /* dst = (u32) cpu_to_be32(dst) */ 121162306a36Sopenharmony_ci if (!fp->aux->verifier_zext) 121262306a36Sopenharmony_ci /* llgfr %dst,%dst */ 121362306a36Sopenharmony_ci EMIT4(0xb9160000, dst_reg, dst_reg); 121462306a36Sopenharmony_ci break; 121562306a36Sopenharmony_ci case 64: /* dst = (u64) cpu_to_be64(dst) */ 121662306a36Sopenharmony_ci break; 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci break; 121962306a36Sopenharmony_ci case BPF_ALU | BPF_END | BPF_FROM_LE: 122062306a36Sopenharmony_ci switch (imm) { 122162306a36Sopenharmony_ci case 16: /* dst = (u16) cpu_to_le16(dst) */ 122262306a36Sopenharmony_ci /* lrvr %dst,%dst */ 122362306a36Sopenharmony_ci EMIT4(0xb91f0000, dst_reg, dst_reg); 122462306a36Sopenharmony_ci /* srl %dst,16(%r0) */ 122562306a36Sopenharmony_ci EMIT4_DISP(0x88000000, dst_reg, REG_0, 16); 122662306a36Sopenharmony_ci /* llghr %dst,%dst */ 122762306a36Sopenharmony_ci EMIT4(0xb9850000, dst_reg, dst_reg); 122862306a36Sopenharmony_ci if (insn_is_zext(&insn[1])) 122962306a36Sopenharmony_ci insn_count = 2; 123062306a36Sopenharmony_ci break; 123162306a36Sopenharmony_ci case 32: /* dst = (u32) cpu_to_le32(dst) */ 123262306a36Sopenharmony_ci /* lrvr %dst,%dst */ 123362306a36Sopenharmony_ci EMIT4(0xb91f0000, dst_reg, dst_reg); 123462306a36Sopenharmony_ci if (!fp->aux->verifier_zext) 123562306a36Sopenharmony_ci /* llgfr %dst,%dst */ 123662306a36Sopenharmony_ci EMIT4(0xb9160000, dst_reg, dst_reg); 123762306a36Sopenharmony_ci break; 123862306a36Sopenharmony_ci case 64: /* dst = (u64) cpu_to_le64(dst) */ 123962306a36Sopenharmony_ci /* lrvgr %dst,%dst */ 124062306a36Sopenharmony_ci EMIT4(0xb90f0000, dst_reg, dst_reg); 124162306a36Sopenharmony_ci break; 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci break; 124462306a36Sopenharmony_ci /* 124562306a36Sopenharmony_ci * BPF_NOSPEC (speculation barrier) 124662306a36Sopenharmony_ci */ 124762306a36Sopenharmony_ci case BPF_ST | BPF_NOSPEC: 124862306a36Sopenharmony_ci break; 124962306a36Sopenharmony_ci /* 125062306a36Sopenharmony_ci * BPF_ST(X) 125162306a36Sopenharmony_ci */ 125262306a36Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_B: /* *(u8 *)(dst + off) = src_reg */ 125362306a36Sopenharmony_ci /* stcy %src,off(%dst) */ 125462306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0072, src_reg, dst_reg, REG_0, off); 125562306a36Sopenharmony_ci jit->seen |= SEEN_MEM; 125662306a36Sopenharmony_ci break; 125762306a36Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_H: /* (u16 *)(dst + off) = src */ 125862306a36Sopenharmony_ci /* sthy %src,off(%dst) */ 125962306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0070, src_reg, dst_reg, REG_0, off); 126062306a36Sopenharmony_ci jit->seen |= SEEN_MEM; 126162306a36Sopenharmony_ci break; 126262306a36Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_W: /* *(u32 *)(dst + off) = src */ 126362306a36Sopenharmony_ci /* sty %src,off(%dst) */ 126462306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0050, src_reg, dst_reg, REG_0, off); 126562306a36Sopenharmony_ci jit->seen |= SEEN_MEM; 126662306a36Sopenharmony_ci break; 126762306a36Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_DW: /* (u64 *)(dst + off) = src */ 126862306a36Sopenharmony_ci /* stg %src,off(%dst) */ 126962306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0024, src_reg, dst_reg, REG_0, off); 127062306a36Sopenharmony_ci jit->seen |= SEEN_MEM; 127162306a36Sopenharmony_ci break; 127262306a36Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_B: /* *(u8 *)(dst + off) = imm */ 127362306a36Sopenharmony_ci /* lhi %w0,imm */ 127462306a36Sopenharmony_ci EMIT4_IMM(0xa7080000, REG_W0, (u8) imm); 127562306a36Sopenharmony_ci /* stcy %w0,off(dst) */ 127662306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0072, REG_W0, dst_reg, REG_0, off); 127762306a36Sopenharmony_ci jit->seen |= SEEN_MEM; 127862306a36Sopenharmony_ci break; 127962306a36Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_H: /* (u16 *)(dst + off) = imm */ 128062306a36Sopenharmony_ci /* lhi %w0,imm */ 128162306a36Sopenharmony_ci EMIT4_IMM(0xa7080000, REG_W0, (u16) imm); 128262306a36Sopenharmony_ci /* sthy %w0,off(dst) */ 128362306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0070, REG_W0, dst_reg, REG_0, off); 128462306a36Sopenharmony_ci jit->seen |= SEEN_MEM; 128562306a36Sopenharmony_ci break; 128662306a36Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_W: /* *(u32 *)(dst + off) = imm */ 128762306a36Sopenharmony_ci /* llilf %w0,imm */ 128862306a36Sopenharmony_ci EMIT6_IMM(0xc00f0000, REG_W0, (u32) imm); 128962306a36Sopenharmony_ci /* sty %w0,off(%dst) */ 129062306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0050, REG_W0, dst_reg, REG_0, off); 129162306a36Sopenharmony_ci jit->seen |= SEEN_MEM; 129262306a36Sopenharmony_ci break; 129362306a36Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_DW: /* *(u64 *)(dst + off) = imm */ 129462306a36Sopenharmony_ci /* lgfi %w0,imm */ 129562306a36Sopenharmony_ci EMIT6_IMM(0xc0010000, REG_W0, imm); 129662306a36Sopenharmony_ci /* stg %w0,off(%dst) */ 129762306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W0, dst_reg, REG_0, off); 129862306a36Sopenharmony_ci jit->seen |= SEEN_MEM; 129962306a36Sopenharmony_ci break; 130062306a36Sopenharmony_ci /* 130162306a36Sopenharmony_ci * BPF_ATOMIC 130262306a36Sopenharmony_ci */ 130362306a36Sopenharmony_ci case BPF_STX | BPF_ATOMIC | BPF_DW: 130462306a36Sopenharmony_ci case BPF_STX | BPF_ATOMIC | BPF_W: 130562306a36Sopenharmony_ci { 130662306a36Sopenharmony_ci bool is32 = BPF_SIZE(insn->code) == BPF_W; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci switch (insn->imm) { 130962306a36Sopenharmony_ci/* {op32|op64} {%w0|%src},%src,off(%dst) */ 131062306a36Sopenharmony_ci#define EMIT_ATOMIC(op32, op64) do { \ 131162306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, is32 ? (op32) : (op64), \ 131262306a36Sopenharmony_ci (insn->imm & BPF_FETCH) ? src_reg : REG_W0, \ 131362306a36Sopenharmony_ci src_reg, dst_reg, off); \ 131462306a36Sopenharmony_ci if (is32 && (insn->imm & BPF_FETCH)) \ 131562306a36Sopenharmony_ci EMIT_ZERO(src_reg); \ 131662306a36Sopenharmony_ci} while (0) 131762306a36Sopenharmony_ci case BPF_ADD: 131862306a36Sopenharmony_ci case BPF_ADD | BPF_FETCH: 131962306a36Sopenharmony_ci /* {laal|laalg} */ 132062306a36Sopenharmony_ci EMIT_ATOMIC(0x00fa, 0x00ea); 132162306a36Sopenharmony_ci break; 132262306a36Sopenharmony_ci case BPF_AND: 132362306a36Sopenharmony_ci case BPF_AND | BPF_FETCH: 132462306a36Sopenharmony_ci /* {lan|lang} */ 132562306a36Sopenharmony_ci EMIT_ATOMIC(0x00f4, 0x00e4); 132662306a36Sopenharmony_ci break; 132762306a36Sopenharmony_ci case BPF_OR: 132862306a36Sopenharmony_ci case BPF_OR | BPF_FETCH: 132962306a36Sopenharmony_ci /* {lao|laog} */ 133062306a36Sopenharmony_ci EMIT_ATOMIC(0x00f6, 0x00e6); 133162306a36Sopenharmony_ci break; 133262306a36Sopenharmony_ci case BPF_XOR: 133362306a36Sopenharmony_ci case BPF_XOR | BPF_FETCH: 133462306a36Sopenharmony_ci /* {lax|laxg} */ 133562306a36Sopenharmony_ci EMIT_ATOMIC(0x00f7, 0x00e7); 133662306a36Sopenharmony_ci break; 133762306a36Sopenharmony_ci#undef EMIT_ATOMIC 133862306a36Sopenharmony_ci case BPF_XCHG: 133962306a36Sopenharmony_ci /* {ly|lg} %w0,off(%dst) */ 134062306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 134162306a36Sopenharmony_ci is32 ? 0x0058 : 0x0004, REG_W0, REG_0, 134262306a36Sopenharmony_ci dst_reg, off); 134362306a36Sopenharmony_ci /* 0: {csy|csg} %w0,%src,off(%dst) */ 134462306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, is32 ? 0x0014 : 0x0030, 134562306a36Sopenharmony_ci REG_W0, src_reg, dst_reg, off); 134662306a36Sopenharmony_ci /* brc 4,0b */ 134762306a36Sopenharmony_ci EMIT4_PCREL_RIC(0xa7040000, 4, jit->prg - 6); 134862306a36Sopenharmony_ci /* {llgfr|lgr} %src,%w0 */ 134962306a36Sopenharmony_ci EMIT4(is32 ? 0xb9160000 : 0xb9040000, src_reg, REG_W0); 135062306a36Sopenharmony_ci if (is32 && insn_is_zext(&insn[1])) 135162306a36Sopenharmony_ci insn_count = 2; 135262306a36Sopenharmony_ci break; 135362306a36Sopenharmony_ci case BPF_CMPXCHG: 135462306a36Sopenharmony_ci /* 0: {csy|csg} %b0,%src,off(%dst) */ 135562306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, is32 ? 0x0014 : 0x0030, 135662306a36Sopenharmony_ci BPF_REG_0, src_reg, dst_reg, off); 135762306a36Sopenharmony_ci break; 135862306a36Sopenharmony_ci default: 135962306a36Sopenharmony_ci pr_err("Unknown atomic operation %02x\n", insn->imm); 136062306a36Sopenharmony_ci return -1; 136162306a36Sopenharmony_ci } 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci jit->seen |= SEEN_MEM; 136462306a36Sopenharmony_ci break; 136562306a36Sopenharmony_ci } 136662306a36Sopenharmony_ci /* 136762306a36Sopenharmony_ci * BPF_LDX 136862306a36Sopenharmony_ci */ 136962306a36Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_B: /* dst = *(u8 *)(ul) (src + off) */ 137062306a36Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_B: 137162306a36Sopenharmony_ci /* llgc %dst,0(off,%src) */ 137262306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0090, dst_reg, src_reg, REG_0, off); 137362306a36Sopenharmony_ci jit->seen |= SEEN_MEM; 137462306a36Sopenharmony_ci if (insn_is_zext(&insn[1])) 137562306a36Sopenharmony_ci insn_count = 2; 137662306a36Sopenharmony_ci break; 137762306a36Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_H: /* dst = *(u16 *)(ul) (src + off) */ 137862306a36Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_H: 137962306a36Sopenharmony_ci /* llgh %dst,0(off,%src) */ 138062306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0091, dst_reg, src_reg, REG_0, off); 138162306a36Sopenharmony_ci jit->seen |= SEEN_MEM; 138262306a36Sopenharmony_ci if (insn_is_zext(&insn[1])) 138362306a36Sopenharmony_ci insn_count = 2; 138462306a36Sopenharmony_ci break; 138562306a36Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_W: /* dst = *(u32 *)(ul) (src + off) */ 138662306a36Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_W: 138762306a36Sopenharmony_ci /* llgf %dst,off(%src) */ 138862306a36Sopenharmony_ci jit->seen |= SEEN_MEM; 138962306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0016, dst_reg, src_reg, REG_0, off); 139062306a36Sopenharmony_ci if (insn_is_zext(&insn[1])) 139162306a36Sopenharmony_ci insn_count = 2; 139262306a36Sopenharmony_ci break; 139362306a36Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_DW: /* dst = *(u64 *)(ul) (src + off) */ 139462306a36Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_DW: 139562306a36Sopenharmony_ci /* lg %dst,0(off,%src) */ 139662306a36Sopenharmony_ci jit->seen |= SEEN_MEM; 139762306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0004, dst_reg, src_reg, REG_0, off); 139862306a36Sopenharmony_ci break; 139962306a36Sopenharmony_ci /* 140062306a36Sopenharmony_ci * BPF_JMP / CALL 140162306a36Sopenharmony_ci */ 140262306a36Sopenharmony_ci case BPF_JMP | BPF_CALL: 140362306a36Sopenharmony_ci { 140462306a36Sopenharmony_ci const struct btf_func_model *m; 140562306a36Sopenharmony_ci bool func_addr_fixed; 140662306a36Sopenharmony_ci int j, ret; 140762306a36Sopenharmony_ci u64 func; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci ret = bpf_jit_get_func_addr(fp, insn, extra_pass, 141062306a36Sopenharmony_ci &func, &func_addr_fixed); 141162306a36Sopenharmony_ci if (ret < 0) 141262306a36Sopenharmony_ci return -1; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci REG_SET_SEEN(BPF_REG_5); 141562306a36Sopenharmony_ci jit->seen |= SEEN_FUNC; 141662306a36Sopenharmony_ci /* 141762306a36Sopenharmony_ci * Copy the tail call counter to where the callee expects it. 141862306a36Sopenharmony_ci * 141962306a36Sopenharmony_ci * Note 1: The callee can increment the tail call counter, but 142062306a36Sopenharmony_ci * we do not load it back, since the x86 JIT does not do this 142162306a36Sopenharmony_ci * either. 142262306a36Sopenharmony_ci * 142362306a36Sopenharmony_ci * Note 2: We assume that the verifier does not let us call the 142462306a36Sopenharmony_ci * main program, which clears the tail call counter on entry. 142562306a36Sopenharmony_ci */ 142662306a36Sopenharmony_ci /* mvc STK_OFF_TCCNT(4,%r15),N(%r15) */ 142762306a36Sopenharmony_ci _EMIT6(0xd203f000 | STK_OFF_TCCNT, 142862306a36Sopenharmony_ci 0xf000 | (STK_OFF_TCCNT + STK_OFF + stack_depth)); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci /* Sign-extend the kfunc arguments. */ 143162306a36Sopenharmony_ci if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) { 143262306a36Sopenharmony_ci m = bpf_jit_find_kfunc_model(fp, insn); 143362306a36Sopenharmony_ci if (!m) 143462306a36Sopenharmony_ci return -1; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci for (j = 0; j < m->nr_args; j++) { 143762306a36Sopenharmony_ci if (sign_extend(jit, BPF_REG_1 + j, 143862306a36Sopenharmony_ci m->arg_size[j], 143962306a36Sopenharmony_ci m->arg_flags[j])) 144062306a36Sopenharmony_ci return -1; 144162306a36Sopenharmony_ci } 144262306a36Sopenharmony_ci } 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci /* lgrl %w1,func */ 144562306a36Sopenharmony_ci EMIT6_PCREL_RILB(0xc4080000, REG_W1, _EMIT_CONST_U64(func)); 144662306a36Sopenharmony_ci /* %r1() */ 144762306a36Sopenharmony_ci call_r1(jit); 144862306a36Sopenharmony_ci /* lgr %b0,%r2: load return value into %b0 */ 144962306a36Sopenharmony_ci EMIT4(0xb9040000, BPF_REG_0, REG_2); 145062306a36Sopenharmony_ci break; 145162306a36Sopenharmony_ci } 145262306a36Sopenharmony_ci case BPF_JMP | BPF_TAIL_CALL: { 145362306a36Sopenharmony_ci int patch_1_clrj, patch_2_clij, patch_3_brc; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci /* 145662306a36Sopenharmony_ci * Implicit input: 145762306a36Sopenharmony_ci * B1: pointer to ctx 145862306a36Sopenharmony_ci * B2: pointer to bpf_array 145962306a36Sopenharmony_ci * B3: index in bpf_array 146062306a36Sopenharmony_ci * 146162306a36Sopenharmony_ci * if (index >= array->map.max_entries) 146262306a36Sopenharmony_ci * goto out; 146362306a36Sopenharmony_ci */ 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci /* llgf %w1,map.max_entries(%b2) */ 146662306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0016, REG_W1, REG_0, BPF_REG_2, 146762306a36Sopenharmony_ci offsetof(struct bpf_array, map.max_entries)); 146862306a36Sopenharmony_ci /* if ((u32)%b3 >= (u32)%w1) goto out; */ 146962306a36Sopenharmony_ci /* clrj %b3,%w1,0xa,out */ 147062306a36Sopenharmony_ci patch_1_clrj = jit->prg; 147162306a36Sopenharmony_ci EMIT6_PCREL_RIEB(0xec000000, 0x0077, BPF_REG_3, REG_W1, 0xa, 147262306a36Sopenharmony_ci jit->prg); 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci /* 147562306a36Sopenharmony_ci * if (tail_call_cnt++ >= MAX_TAIL_CALL_CNT) 147662306a36Sopenharmony_ci * goto out; 147762306a36Sopenharmony_ci */ 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci if (jit->seen & SEEN_STACK) 148062306a36Sopenharmony_ci off = STK_OFF_TCCNT + STK_OFF + stack_depth; 148162306a36Sopenharmony_ci else 148262306a36Sopenharmony_ci off = STK_OFF_TCCNT; 148362306a36Sopenharmony_ci /* lhi %w0,1 */ 148462306a36Sopenharmony_ci EMIT4_IMM(0xa7080000, REG_W0, 1); 148562306a36Sopenharmony_ci /* laal %w1,%w0,off(%r15) */ 148662306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, 0x00fa, REG_W1, REG_W0, REG_15, off); 148762306a36Sopenharmony_ci /* clij %w1,MAX_TAIL_CALL_CNT-1,0x2,out */ 148862306a36Sopenharmony_ci patch_2_clij = jit->prg; 148962306a36Sopenharmony_ci EMIT6_PCREL_RIEC(0xec000000, 0x007f, REG_W1, MAX_TAIL_CALL_CNT - 1, 149062306a36Sopenharmony_ci 2, jit->prg); 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci /* 149362306a36Sopenharmony_ci * prog = array->ptrs[index]; 149462306a36Sopenharmony_ci * if (prog == NULL) 149562306a36Sopenharmony_ci * goto out; 149662306a36Sopenharmony_ci */ 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci /* llgfr %r1,%b3: %r1 = (u32) index */ 149962306a36Sopenharmony_ci EMIT4(0xb9160000, REG_1, BPF_REG_3); 150062306a36Sopenharmony_ci /* sllg %r1,%r1,3: %r1 *= 8 */ 150162306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, 0x000d, REG_1, REG_1, REG_0, 3); 150262306a36Sopenharmony_ci /* ltg %r1,prog(%b2,%r1) */ 150362306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0002, REG_1, BPF_REG_2, 150462306a36Sopenharmony_ci REG_1, offsetof(struct bpf_array, ptrs)); 150562306a36Sopenharmony_ci /* brc 0x8,out */ 150662306a36Sopenharmony_ci patch_3_brc = jit->prg; 150762306a36Sopenharmony_ci EMIT4_PCREL_RIC(0xa7040000, 8, jit->prg); 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci /* 151062306a36Sopenharmony_ci * Restore registers before calling function 151162306a36Sopenharmony_ci */ 151262306a36Sopenharmony_ci save_restore_regs(jit, REGS_RESTORE, stack_depth); 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci /* 151562306a36Sopenharmony_ci * goto *(prog->bpf_func + tail_call_start); 151662306a36Sopenharmony_ci */ 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci /* lg %r1,bpf_func(%r1) */ 151962306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0004, REG_1, REG_1, REG_0, 152062306a36Sopenharmony_ci offsetof(struct bpf_prog, bpf_func)); 152162306a36Sopenharmony_ci if (nospec_uses_trampoline()) { 152262306a36Sopenharmony_ci jit->seen |= SEEN_FUNC; 152362306a36Sopenharmony_ci /* aghi %r1,tail_call_start */ 152462306a36Sopenharmony_ci EMIT4_IMM(0xa70b0000, REG_1, jit->tail_call_start); 152562306a36Sopenharmony_ci /* brcl 0xf,__s390_indirect_jump_r1 */ 152662306a36Sopenharmony_ci EMIT6_PCREL_RILC(0xc0040000, 0xf, jit->r1_thunk_ip); 152762306a36Sopenharmony_ci } else { 152862306a36Sopenharmony_ci /* bc 0xf,tail_call_start(%r1) */ 152962306a36Sopenharmony_ci _EMIT4(0x47f01000 + jit->tail_call_start); 153062306a36Sopenharmony_ci } 153162306a36Sopenharmony_ci /* out: */ 153262306a36Sopenharmony_ci if (jit->prg_buf) { 153362306a36Sopenharmony_ci *(u16 *)(jit->prg_buf + patch_1_clrj + 2) = 153462306a36Sopenharmony_ci (jit->prg - patch_1_clrj) >> 1; 153562306a36Sopenharmony_ci *(u16 *)(jit->prg_buf + patch_2_clij + 2) = 153662306a36Sopenharmony_ci (jit->prg - patch_2_clij) >> 1; 153762306a36Sopenharmony_ci *(u16 *)(jit->prg_buf + patch_3_brc + 2) = 153862306a36Sopenharmony_ci (jit->prg - patch_3_brc) >> 1; 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci break; 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci case BPF_JMP | BPF_EXIT: /* return b0 */ 154362306a36Sopenharmony_ci last = (i == fp->len - 1) ? 1 : 0; 154462306a36Sopenharmony_ci if (last) 154562306a36Sopenharmony_ci break; 154662306a36Sopenharmony_ci if (!is_first_pass(jit) && can_use_rel(jit, jit->exit_ip)) 154762306a36Sopenharmony_ci /* brc 0xf, <exit> */ 154862306a36Sopenharmony_ci EMIT4_PCREL_RIC(0xa7040000, 0xf, jit->exit_ip); 154962306a36Sopenharmony_ci else 155062306a36Sopenharmony_ci /* brcl 0xf, <exit> */ 155162306a36Sopenharmony_ci EMIT6_PCREL_RILC(0xc0040000, 0xf, jit->exit_ip); 155262306a36Sopenharmony_ci break; 155362306a36Sopenharmony_ci /* 155462306a36Sopenharmony_ci * Branch relative (number of skipped instructions) to offset on 155562306a36Sopenharmony_ci * condition. 155662306a36Sopenharmony_ci * 155762306a36Sopenharmony_ci * Condition code to mask mapping: 155862306a36Sopenharmony_ci * 155962306a36Sopenharmony_ci * CC | Description | Mask 156062306a36Sopenharmony_ci * ------------------------------ 156162306a36Sopenharmony_ci * 0 | Operands equal | 8 156262306a36Sopenharmony_ci * 1 | First operand low | 4 156362306a36Sopenharmony_ci * 2 | First operand high | 2 156462306a36Sopenharmony_ci * 3 | Unused | 1 156562306a36Sopenharmony_ci * 156662306a36Sopenharmony_ci * For s390x relative branches: ip = ip + off_bytes 156762306a36Sopenharmony_ci * For BPF relative branches: insn = insn + off_insns + 1 156862306a36Sopenharmony_ci * 156962306a36Sopenharmony_ci * For example for s390x with offset 0 we jump to the branch 157062306a36Sopenharmony_ci * instruction itself (loop) and for BPF with offset 0 we 157162306a36Sopenharmony_ci * branch to the instruction behind the branch. 157262306a36Sopenharmony_ci */ 157362306a36Sopenharmony_ci case BPF_JMP | BPF_JA: /* if (true) */ 157462306a36Sopenharmony_ci mask = 0xf000; /* j */ 157562306a36Sopenharmony_ci goto branch_oc; 157662306a36Sopenharmony_ci case BPF_JMP | BPF_JSGT | BPF_K: /* ((s64) dst > (s64) imm) */ 157762306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSGT | BPF_K: /* ((s32) dst > (s32) imm) */ 157862306a36Sopenharmony_ci mask = 0x2000; /* jh */ 157962306a36Sopenharmony_ci goto branch_ks; 158062306a36Sopenharmony_ci case BPF_JMP | BPF_JSLT | BPF_K: /* ((s64) dst < (s64) imm) */ 158162306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSLT | BPF_K: /* ((s32) dst < (s32) imm) */ 158262306a36Sopenharmony_ci mask = 0x4000; /* jl */ 158362306a36Sopenharmony_ci goto branch_ks; 158462306a36Sopenharmony_ci case BPF_JMP | BPF_JSGE | BPF_K: /* ((s64) dst >= (s64) imm) */ 158562306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSGE | BPF_K: /* ((s32) dst >= (s32) imm) */ 158662306a36Sopenharmony_ci mask = 0xa000; /* jhe */ 158762306a36Sopenharmony_ci goto branch_ks; 158862306a36Sopenharmony_ci case BPF_JMP | BPF_JSLE | BPF_K: /* ((s64) dst <= (s64) imm) */ 158962306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSLE | BPF_K: /* ((s32) dst <= (s32) imm) */ 159062306a36Sopenharmony_ci mask = 0xc000; /* jle */ 159162306a36Sopenharmony_ci goto branch_ks; 159262306a36Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_K: /* (dst_reg > imm) */ 159362306a36Sopenharmony_ci case BPF_JMP32 | BPF_JGT | BPF_K: /* ((u32) dst_reg > (u32) imm) */ 159462306a36Sopenharmony_ci mask = 0x2000; /* jh */ 159562306a36Sopenharmony_ci goto branch_ku; 159662306a36Sopenharmony_ci case BPF_JMP | BPF_JLT | BPF_K: /* (dst_reg < imm) */ 159762306a36Sopenharmony_ci case BPF_JMP32 | BPF_JLT | BPF_K: /* ((u32) dst_reg < (u32) imm) */ 159862306a36Sopenharmony_ci mask = 0x4000; /* jl */ 159962306a36Sopenharmony_ci goto branch_ku; 160062306a36Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_K: /* (dst_reg >= imm) */ 160162306a36Sopenharmony_ci case BPF_JMP32 | BPF_JGE | BPF_K: /* ((u32) dst_reg >= (u32) imm) */ 160262306a36Sopenharmony_ci mask = 0xa000; /* jhe */ 160362306a36Sopenharmony_ci goto branch_ku; 160462306a36Sopenharmony_ci case BPF_JMP | BPF_JLE | BPF_K: /* (dst_reg <= imm) */ 160562306a36Sopenharmony_ci case BPF_JMP32 | BPF_JLE | BPF_K: /* ((u32) dst_reg <= (u32) imm) */ 160662306a36Sopenharmony_ci mask = 0xc000; /* jle */ 160762306a36Sopenharmony_ci goto branch_ku; 160862306a36Sopenharmony_ci case BPF_JMP | BPF_JNE | BPF_K: /* (dst_reg != imm) */ 160962306a36Sopenharmony_ci case BPF_JMP32 | BPF_JNE | BPF_K: /* ((u32) dst_reg != (u32) imm) */ 161062306a36Sopenharmony_ci mask = 0x7000; /* jne */ 161162306a36Sopenharmony_ci goto branch_ku; 161262306a36Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_K: /* (dst_reg == imm) */ 161362306a36Sopenharmony_ci case BPF_JMP32 | BPF_JEQ | BPF_K: /* ((u32) dst_reg == (u32) imm) */ 161462306a36Sopenharmony_ci mask = 0x8000; /* je */ 161562306a36Sopenharmony_ci goto branch_ku; 161662306a36Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_K: /* (dst_reg & imm) */ 161762306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSET | BPF_K: /* ((u32) dst_reg & (u32) imm) */ 161862306a36Sopenharmony_ci mask = 0x7000; /* jnz */ 161962306a36Sopenharmony_ci if (BPF_CLASS(insn->code) == BPF_JMP32) { 162062306a36Sopenharmony_ci /* llilf %w1,imm (load zero extend imm) */ 162162306a36Sopenharmony_ci EMIT6_IMM(0xc00f0000, REG_W1, imm); 162262306a36Sopenharmony_ci /* nr %w1,%dst */ 162362306a36Sopenharmony_ci EMIT2(0x1400, REG_W1, dst_reg); 162462306a36Sopenharmony_ci } else { 162562306a36Sopenharmony_ci /* lgfi %w1,imm (load sign extend imm) */ 162662306a36Sopenharmony_ci EMIT6_IMM(0xc0010000, REG_W1, imm); 162762306a36Sopenharmony_ci /* ngr %w1,%dst */ 162862306a36Sopenharmony_ci EMIT4(0xb9800000, REG_W1, dst_reg); 162962306a36Sopenharmony_ci } 163062306a36Sopenharmony_ci goto branch_oc; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci case BPF_JMP | BPF_JSGT | BPF_X: /* ((s64) dst > (s64) src) */ 163362306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSGT | BPF_X: /* ((s32) dst > (s32) src) */ 163462306a36Sopenharmony_ci mask = 0x2000; /* jh */ 163562306a36Sopenharmony_ci goto branch_xs; 163662306a36Sopenharmony_ci case BPF_JMP | BPF_JSLT | BPF_X: /* ((s64) dst < (s64) src) */ 163762306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSLT | BPF_X: /* ((s32) dst < (s32) src) */ 163862306a36Sopenharmony_ci mask = 0x4000; /* jl */ 163962306a36Sopenharmony_ci goto branch_xs; 164062306a36Sopenharmony_ci case BPF_JMP | BPF_JSGE | BPF_X: /* ((s64) dst >= (s64) src) */ 164162306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSGE | BPF_X: /* ((s32) dst >= (s32) src) */ 164262306a36Sopenharmony_ci mask = 0xa000; /* jhe */ 164362306a36Sopenharmony_ci goto branch_xs; 164462306a36Sopenharmony_ci case BPF_JMP | BPF_JSLE | BPF_X: /* ((s64) dst <= (s64) src) */ 164562306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSLE | BPF_X: /* ((s32) dst <= (s32) src) */ 164662306a36Sopenharmony_ci mask = 0xc000; /* jle */ 164762306a36Sopenharmony_ci goto branch_xs; 164862306a36Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_X: /* (dst > src) */ 164962306a36Sopenharmony_ci case BPF_JMP32 | BPF_JGT | BPF_X: /* ((u32) dst > (u32) src) */ 165062306a36Sopenharmony_ci mask = 0x2000; /* jh */ 165162306a36Sopenharmony_ci goto branch_xu; 165262306a36Sopenharmony_ci case BPF_JMP | BPF_JLT | BPF_X: /* (dst < src) */ 165362306a36Sopenharmony_ci case BPF_JMP32 | BPF_JLT | BPF_X: /* ((u32) dst < (u32) src) */ 165462306a36Sopenharmony_ci mask = 0x4000; /* jl */ 165562306a36Sopenharmony_ci goto branch_xu; 165662306a36Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_X: /* (dst >= src) */ 165762306a36Sopenharmony_ci case BPF_JMP32 | BPF_JGE | BPF_X: /* ((u32) dst >= (u32) src) */ 165862306a36Sopenharmony_ci mask = 0xa000; /* jhe */ 165962306a36Sopenharmony_ci goto branch_xu; 166062306a36Sopenharmony_ci case BPF_JMP | BPF_JLE | BPF_X: /* (dst <= src) */ 166162306a36Sopenharmony_ci case BPF_JMP32 | BPF_JLE | BPF_X: /* ((u32) dst <= (u32) src) */ 166262306a36Sopenharmony_ci mask = 0xc000; /* jle */ 166362306a36Sopenharmony_ci goto branch_xu; 166462306a36Sopenharmony_ci case BPF_JMP | BPF_JNE | BPF_X: /* (dst != src) */ 166562306a36Sopenharmony_ci case BPF_JMP32 | BPF_JNE | BPF_X: /* ((u32) dst != (u32) src) */ 166662306a36Sopenharmony_ci mask = 0x7000; /* jne */ 166762306a36Sopenharmony_ci goto branch_xu; 166862306a36Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_X: /* (dst == src) */ 166962306a36Sopenharmony_ci case BPF_JMP32 | BPF_JEQ | BPF_X: /* ((u32) dst == (u32) src) */ 167062306a36Sopenharmony_ci mask = 0x8000; /* je */ 167162306a36Sopenharmony_ci goto branch_xu; 167262306a36Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_X: /* (dst & src) */ 167362306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSET | BPF_X: /* ((u32) dst & (u32) src) */ 167462306a36Sopenharmony_ci { 167562306a36Sopenharmony_ci bool is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32; 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci mask = 0x7000; /* jnz */ 167862306a36Sopenharmony_ci /* nrk or ngrk %w1,%dst,%src */ 167962306a36Sopenharmony_ci EMIT4_RRF((is_jmp32 ? 0xb9f40000 : 0xb9e40000), 168062306a36Sopenharmony_ci REG_W1, dst_reg, src_reg); 168162306a36Sopenharmony_ci goto branch_oc; 168262306a36Sopenharmony_cibranch_ks: 168362306a36Sopenharmony_ci is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32; 168462306a36Sopenharmony_ci /* cfi or cgfi %dst,imm */ 168562306a36Sopenharmony_ci EMIT6_IMM(is_jmp32 ? 0xc20d0000 : 0xc20c0000, 168662306a36Sopenharmony_ci dst_reg, imm); 168762306a36Sopenharmony_ci if (!is_first_pass(jit) && 168862306a36Sopenharmony_ci can_use_rel(jit, addrs[i + off + 1])) { 168962306a36Sopenharmony_ci /* brc mask,off */ 169062306a36Sopenharmony_ci EMIT4_PCREL_RIC(0xa7040000, 169162306a36Sopenharmony_ci mask >> 12, addrs[i + off + 1]); 169262306a36Sopenharmony_ci } else { 169362306a36Sopenharmony_ci /* brcl mask,off */ 169462306a36Sopenharmony_ci EMIT6_PCREL_RILC(0xc0040000, 169562306a36Sopenharmony_ci mask >> 12, addrs[i + off + 1]); 169662306a36Sopenharmony_ci } 169762306a36Sopenharmony_ci break; 169862306a36Sopenharmony_cibranch_ku: 169962306a36Sopenharmony_ci /* lgfi %w1,imm (load sign extend imm) */ 170062306a36Sopenharmony_ci src_reg = REG_1; 170162306a36Sopenharmony_ci EMIT6_IMM(0xc0010000, src_reg, imm); 170262306a36Sopenharmony_ci goto branch_xu; 170362306a36Sopenharmony_cibranch_xs: 170462306a36Sopenharmony_ci is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32; 170562306a36Sopenharmony_ci if (!is_first_pass(jit) && 170662306a36Sopenharmony_ci can_use_rel(jit, addrs[i + off + 1])) { 170762306a36Sopenharmony_ci /* crj or cgrj %dst,%src,mask,off */ 170862306a36Sopenharmony_ci EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0076 : 0x0064), 170962306a36Sopenharmony_ci dst_reg, src_reg, i, off, mask); 171062306a36Sopenharmony_ci } else { 171162306a36Sopenharmony_ci /* cr or cgr %dst,%src */ 171262306a36Sopenharmony_ci if (is_jmp32) 171362306a36Sopenharmony_ci EMIT2(0x1900, dst_reg, src_reg); 171462306a36Sopenharmony_ci else 171562306a36Sopenharmony_ci EMIT4(0xb9200000, dst_reg, src_reg); 171662306a36Sopenharmony_ci /* brcl mask,off */ 171762306a36Sopenharmony_ci EMIT6_PCREL_RILC(0xc0040000, 171862306a36Sopenharmony_ci mask >> 12, addrs[i + off + 1]); 171962306a36Sopenharmony_ci } 172062306a36Sopenharmony_ci break; 172162306a36Sopenharmony_cibranch_xu: 172262306a36Sopenharmony_ci is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32; 172362306a36Sopenharmony_ci if (!is_first_pass(jit) && 172462306a36Sopenharmony_ci can_use_rel(jit, addrs[i + off + 1])) { 172562306a36Sopenharmony_ci /* clrj or clgrj %dst,%src,mask,off */ 172662306a36Sopenharmony_ci EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0077 : 0x0065), 172762306a36Sopenharmony_ci dst_reg, src_reg, i, off, mask); 172862306a36Sopenharmony_ci } else { 172962306a36Sopenharmony_ci /* clr or clgr %dst,%src */ 173062306a36Sopenharmony_ci if (is_jmp32) 173162306a36Sopenharmony_ci EMIT2(0x1500, dst_reg, src_reg); 173262306a36Sopenharmony_ci else 173362306a36Sopenharmony_ci EMIT4(0xb9210000, dst_reg, src_reg); 173462306a36Sopenharmony_ci /* brcl mask,off */ 173562306a36Sopenharmony_ci EMIT6_PCREL_RILC(0xc0040000, 173662306a36Sopenharmony_ci mask >> 12, addrs[i + off + 1]); 173762306a36Sopenharmony_ci } 173862306a36Sopenharmony_ci break; 173962306a36Sopenharmony_cibranch_oc: 174062306a36Sopenharmony_ci if (!is_first_pass(jit) && 174162306a36Sopenharmony_ci can_use_rel(jit, addrs[i + off + 1])) { 174262306a36Sopenharmony_ci /* brc mask,off */ 174362306a36Sopenharmony_ci EMIT4_PCREL_RIC(0xa7040000, 174462306a36Sopenharmony_ci mask >> 12, addrs[i + off + 1]); 174562306a36Sopenharmony_ci } else { 174662306a36Sopenharmony_ci /* brcl mask,off */ 174762306a36Sopenharmony_ci EMIT6_PCREL_RILC(0xc0040000, 174862306a36Sopenharmony_ci mask >> 12, addrs[i + off + 1]); 174962306a36Sopenharmony_ci } 175062306a36Sopenharmony_ci break; 175162306a36Sopenharmony_ci } 175262306a36Sopenharmony_ci default: /* too complex, give up */ 175362306a36Sopenharmony_ci pr_err("Unknown opcode %02x\n", insn->code); 175462306a36Sopenharmony_ci return -1; 175562306a36Sopenharmony_ci } 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci if (probe_prg != -1) { 175862306a36Sopenharmony_ci /* 175962306a36Sopenharmony_ci * Handlers of certain exceptions leave psw.addr pointing to 176062306a36Sopenharmony_ci * the instruction directly after the failing one. Therefore, 176162306a36Sopenharmony_ci * create two exception table entries and also add a nop in 176262306a36Sopenharmony_ci * case two probing instructions come directly after each 176362306a36Sopenharmony_ci * other. 176462306a36Sopenharmony_ci */ 176562306a36Sopenharmony_ci nop_prg = jit->prg; 176662306a36Sopenharmony_ci /* bcr 0,%0 */ 176762306a36Sopenharmony_ci _EMIT2(0x0700); 176862306a36Sopenharmony_ci err = bpf_jit_probe_mem(jit, fp, probe_prg, nop_prg); 176962306a36Sopenharmony_ci if (err < 0) 177062306a36Sopenharmony_ci return err; 177162306a36Sopenharmony_ci } 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci return insn_count; 177462306a36Sopenharmony_ci} 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci/* 177762306a36Sopenharmony_ci * Return whether new i-th instruction address does not violate any invariant 177862306a36Sopenharmony_ci */ 177962306a36Sopenharmony_cistatic bool bpf_is_new_addr_sane(struct bpf_jit *jit, int i) 178062306a36Sopenharmony_ci{ 178162306a36Sopenharmony_ci /* On the first pass anything goes */ 178262306a36Sopenharmony_ci if (is_first_pass(jit)) 178362306a36Sopenharmony_ci return true; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci /* The codegen pass must not change anything */ 178662306a36Sopenharmony_ci if (is_codegen_pass(jit)) 178762306a36Sopenharmony_ci return jit->addrs[i] == jit->prg; 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci /* Passes in between must not increase code size */ 179062306a36Sopenharmony_ci return jit->addrs[i] >= jit->prg; 179162306a36Sopenharmony_ci} 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci/* 179462306a36Sopenharmony_ci * Update the address of i-th instruction 179562306a36Sopenharmony_ci */ 179662306a36Sopenharmony_cistatic int bpf_set_addr(struct bpf_jit *jit, int i) 179762306a36Sopenharmony_ci{ 179862306a36Sopenharmony_ci int delta; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci if (is_codegen_pass(jit)) { 180162306a36Sopenharmony_ci delta = jit->prg - jit->addrs[i]; 180262306a36Sopenharmony_ci if (delta < 0) 180362306a36Sopenharmony_ci bpf_skip(jit, -delta); 180462306a36Sopenharmony_ci } 180562306a36Sopenharmony_ci if (WARN_ON_ONCE(!bpf_is_new_addr_sane(jit, i))) 180662306a36Sopenharmony_ci return -1; 180762306a36Sopenharmony_ci jit->addrs[i] = jit->prg; 180862306a36Sopenharmony_ci return 0; 180962306a36Sopenharmony_ci} 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci/* 181262306a36Sopenharmony_ci * Compile eBPF program into s390x code 181362306a36Sopenharmony_ci */ 181462306a36Sopenharmony_cistatic int bpf_jit_prog(struct bpf_jit *jit, struct bpf_prog *fp, 181562306a36Sopenharmony_ci bool extra_pass, u32 stack_depth) 181662306a36Sopenharmony_ci{ 181762306a36Sopenharmony_ci int i, insn_count, lit32_size, lit64_size; 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci jit->lit32 = jit->lit32_start; 182062306a36Sopenharmony_ci jit->lit64 = jit->lit64_start; 182162306a36Sopenharmony_ci jit->prg = 0; 182262306a36Sopenharmony_ci jit->excnt = 0; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci bpf_jit_prologue(jit, fp, stack_depth); 182562306a36Sopenharmony_ci if (bpf_set_addr(jit, 0) < 0) 182662306a36Sopenharmony_ci return -1; 182762306a36Sopenharmony_ci for (i = 0; i < fp->len; i += insn_count) { 182862306a36Sopenharmony_ci insn_count = bpf_jit_insn(jit, fp, i, extra_pass, stack_depth); 182962306a36Sopenharmony_ci if (insn_count < 0) 183062306a36Sopenharmony_ci return -1; 183162306a36Sopenharmony_ci /* Next instruction address */ 183262306a36Sopenharmony_ci if (bpf_set_addr(jit, i + insn_count) < 0) 183362306a36Sopenharmony_ci return -1; 183462306a36Sopenharmony_ci } 183562306a36Sopenharmony_ci bpf_jit_epilogue(jit, stack_depth); 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci lit32_size = jit->lit32 - jit->lit32_start; 183862306a36Sopenharmony_ci lit64_size = jit->lit64 - jit->lit64_start; 183962306a36Sopenharmony_ci jit->lit32_start = jit->prg; 184062306a36Sopenharmony_ci if (lit32_size) 184162306a36Sopenharmony_ci jit->lit32_start = ALIGN(jit->lit32_start, 4); 184262306a36Sopenharmony_ci jit->lit64_start = jit->lit32_start + lit32_size; 184362306a36Sopenharmony_ci if (lit64_size) 184462306a36Sopenharmony_ci jit->lit64_start = ALIGN(jit->lit64_start, 8); 184562306a36Sopenharmony_ci jit->size = jit->lit64_start + lit64_size; 184662306a36Sopenharmony_ci jit->size_prg = jit->prg; 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci if (WARN_ON_ONCE(fp->aux->extable && 184962306a36Sopenharmony_ci jit->excnt != fp->aux->num_exentries)) 185062306a36Sopenharmony_ci /* Verifier bug - too many entries. */ 185162306a36Sopenharmony_ci return -1; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci return 0; 185462306a36Sopenharmony_ci} 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_cibool bpf_jit_needs_zext(void) 185762306a36Sopenharmony_ci{ 185862306a36Sopenharmony_ci return true; 185962306a36Sopenharmony_ci} 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_cistruct s390_jit_data { 186262306a36Sopenharmony_ci struct bpf_binary_header *header; 186362306a36Sopenharmony_ci struct bpf_jit ctx; 186462306a36Sopenharmony_ci int pass; 186562306a36Sopenharmony_ci}; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_cistatic struct bpf_binary_header *bpf_jit_alloc(struct bpf_jit *jit, 186862306a36Sopenharmony_ci struct bpf_prog *fp) 186962306a36Sopenharmony_ci{ 187062306a36Sopenharmony_ci struct bpf_binary_header *header; 187162306a36Sopenharmony_ci u32 extable_size; 187262306a36Sopenharmony_ci u32 code_size; 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci /* We need two entries per insn. */ 187562306a36Sopenharmony_ci fp->aux->num_exentries *= 2; 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci code_size = roundup(jit->size, 187862306a36Sopenharmony_ci __alignof__(struct exception_table_entry)); 187962306a36Sopenharmony_ci extable_size = fp->aux->num_exentries * 188062306a36Sopenharmony_ci sizeof(struct exception_table_entry); 188162306a36Sopenharmony_ci header = bpf_jit_binary_alloc(code_size + extable_size, &jit->prg_buf, 188262306a36Sopenharmony_ci 8, jit_fill_hole); 188362306a36Sopenharmony_ci if (!header) 188462306a36Sopenharmony_ci return NULL; 188562306a36Sopenharmony_ci fp->aux->extable = (struct exception_table_entry *) 188662306a36Sopenharmony_ci (jit->prg_buf + code_size); 188762306a36Sopenharmony_ci return header; 188862306a36Sopenharmony_ci} 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci/* 189162306a36Sopenharmony_ci * Compile eBPF program "fp" 189262306a36Sopenharmony_ci */ 189362306a36Sopenharmony_cistruct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) 189462306a36Sopenharmony_ci{ 189562306a36Sopenharmony_ci u32 stack_depth = round_up(fp->aux->stack_depth, 8); 189662306a36Sopenharmony_ci struct bpf_prog *tmp, *orig_fp = fp; 189762306a36Sopenharmony_ci struct bpf_binary_header *header; 189862306a36Sopenharmony_ci struct s390_jit_data *jit_data; 189962306a36Sopenharmony_ci bool tmp_blinded = false; 190062306a36Sopenharmony_ci bool extra_pass = false; 190162306a36Sopenharmony_ci struct bpf_jit jit; 190262306a36Sopenharmony_ci int pass; 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci if (WARN_ON_ONCE(bpf_plt_end - bpf_plt != BPF_PLT_SIZE)) 190562306a36Sopenharmony_ci return orig_fp; 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci if (!fp->jit_requested) 190862306a36Sopenharmony_ci return orig_fp; 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci tmp = bpf_jit_blind_constants(fp); 191162306a36Sopenharmony_ci /* 191262306a36Sopenharmony_ci * If blinding was requested and we failed during blinding, 191362306a36Sopenharmony_ci * we must fall back to the interpreter. 191462306a36Sopenharmony_ci */ 191562306a36Sopenharmony_ci if (IS_ERR(tmp)) 191662306a36Sopenharmony_ci return orig_fp; 191762306a36Sopenharmony_ci if (tmp != fp) { 191862306a36Sopenharmony_ci tmp_blinded = true; 191962306a36Sopenharmony_ci fp = tmp; 192062306a36Sopenharmony_ci } 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci jit_data = fp->aux->jit_data; 192362306a36Sopenharmony_ci if (!jit_data) { 192462306a36Sopenharmony_ci jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 192562306a36Sopenharmony_ci if (!jit_data) { 192662306a36Sopenharmony_ci fp = orig_fp; 192762306a36Sopenharmony_ci goto out; 192862306a36Sopenharmony_ci } 192962306a36Sopenharmony_ci fp->aux->jit_data = jit_data; 193062306a36Sopenharmony_ci } 193162306a36Sopenharmony_ci if (jit_data->ctx.addrs) { 193262306a36Sopenharmony_ci jit = jit_data->ctx; 193362306a36Sopenharmony_ci header = jit_data->header; 193462306a36Sopenharmony_ci extra_pass = true; 193562306a36Sopenharmony_ci pass = jit_data->pass + 1; 193662306a36Sopenharmony_ci goto skip_init_ctx; 193762306a36Sopenharmony_ci } 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci memset(&jit, 0, sizeof(jit)); 194062306a36Sopenharmony_ci jit.addrs = kvcalloc(fp->len + 1, sizeof(*jit.addrs), GFP_KERNEL); 194162306a36Sopenharmony_ci if (jit.addrs == NULL) { 194262306a36Sopenharmony_ci fp = orig_fp; 194362306a36Sopenharmony_ci goto free_addrs; 194462306a36Sopenharmony_ci } 194562306a36Sopenharmony_ci /* 194662306a36Sopenharmony_ci * Three initial passes: 194762306a36Sopenharmony_ci * - 1/2: Determine clobbered registers 194862306a36Sopenharmony_ci * - 3: Calculate program size and addrs array 194962306a36Sopenharmony_ci */ 195062306a36Sopenharmony_ci for (pass = 1; pass <= 3; pass++) { 195162306a36Sopenharmony_ci if (bpf_jit_prog(&jit, fp, extra_pass, stack_depth)) { 195262306a36Sopenharmony_ci fp = orig_fp; 195362306a36Sopenharmony_ci goto free_addrs; 195462306a36Sopenharmony_ci } 195562306a36Sopenharmony_ci } 195662306a36Sopenharmony_ci /* 195762306a36Sopenharmony_ci * Final pass: Allocate and generate program 195862306a36Sopenharmony_ci */ 195962306a36Sopenharmony_ci header = bpf_jit_alloc(&jit, fp); 196062306a36Sopenharmony_ci if (!header) { 196162306a36Sopenharmony_ci fp = orig_fp; 196262306a36Sopenharmony_ci goto free_addrs; 196362306a36Sopenharmony_ci } 196462306a36Sopenharmony_ciskip_init_ctx: 196562306a36Sopenharmony_ci if (bpf_jit_prog(&jit, fp, extra_pass, stack_depth)) { 196662306a36Sopenharmony_ci bpf_jit_binary_free(header); 196762306a36Sopenharmony_ci fp = orig_fp; 196862306a36Sopenharmony_ci goto free_addrs; 196962306a36Sopenharmony_ci } 197062306a36Sopenharmony_ci if (bpf_jit_enable > 1) { 197162306a36Sopenharmony_ci bpf_jit_dump(fp->len, jit.size, pass, jit.prg_buf); 197262306a36Sopenharmony_ci print_fn_code(jit.prg_buf, jit.size_prg); 197362306a36Sopenharmony_ci } 197462306a36Sopenharmony_ci if (!fp->is_func || extra_pass) { 197562306a36Sopenharmony_ci bpf_jit_binary_lock_ro(header); 197662306a36Sopenharmony_ci } else { 197762306a36Sopenharmony_ci jit_data->header = header; 197862306a36Sopenharmony_ci jit_data->ctx = jit; 197962306a36Sopenharmony_ci jit_data->pass = pass; 198062306a36Sopenharmony_ci } 198162306a36Sopenharmony_ci fp->bpf_func = (void *) jit.prg_buf; 198262306a36Sopenharmony_ci fp->jited = 1; 198362306a36Sopenharmony_ci fp->jited_len = jit.size; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci if (!fp->is_func || extra_pass) { 198662306a36Sopenharmony_ci bpf_prog_fill_jited_linfo(fp, jit.addrs + 1); 198762306a36Sopenharmony_cifree_addrs: 198862306a36Sopenharmony_ci kvfree(jit.addrs); 198962306a36Sopenharmony_ci kfree(jit_data); 199062306a36Sopenharmony_ci fp->aux->jit_data = NULL; 199162306a36Sopenharmony_ci } 199262306a36Sopenharmony_ciout: 199362306a36Sopenharmony_ci if (tmp_blinded) 199462306a36Sopenharmony_ci bpf_jit_prog_release_other(fp, fp == orig_fp ? 199562306a36Sopenharmony_ci tmp : orig_fp); 199662306a36Sopenharmony_ci return fp; 199762306a36Sopenharmony_ci} 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_cibool bpf_jit_supports_kfunc_call(void) 200062306a36Sopenharmony_ci{ 200162306a36Sopenharmony_ci return true; 200262306a36Sopenharmony_ci} 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_cibool bpf_jit_supports_far_kfunc_call(void) 200562306a36Sopenharmony_ci{ 200662306a36Sopenharmony_ci return true; 200762306a36Sopenharmony_ci} 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ciint bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t, 201062306a36Sopenharmony_ci void *old_addr, void *new_addr) 201162306a36Sopenharmony_ci{ 201262306a36Sopenharmony_ci struct { 201362306a36Sopenharmony_ci u16 opc; 201462306a36Sopenharmony_ci s32 disp; 201562306a36Sopenharmony_ci } __packed insn; 201662306a36Sopenharmony_ci char expected_plt[BPF_PLT_SIZE]; 201762306a36Sopenharmony_ci char current_plt[BPF_PLT_SIZE]; 201862306a36Sopenharmony_ci char new_plt[BPF_PLT_SIZE]; 201962306a36Sopenharmony_ci char *plt; 202062306a36Sopenharmony_ci char *ret; 202162306a36Sopenharmony_ci int err; 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci /* Verify the branch to be patched. */ 202462306a36Sopenharmony_ci err = copy_from_kernel_nofault(&insn, ip, sizeof(insn)); 202562306a36Sopenharmony_ci if (err < 0) 202662306a36Sopenharmony_ci return err; 202762306a36Sopenharmony_ci if (insn.opc != (0xc004 | (old_addr ? 0xf0 : 0))) 202862306a36Sopenharmony_ci return -EINVAL; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci if (t == BPF_MOD_JUMP && 203162306a36Sopenharmony_ci insn.disp == ((char *)new_addr - (char *)ip) >> 1) { 203262306a36Sopenharmony_ci /* 203362306a36Sopenharmony_ci * The branch already points to the destination, 203462306a36Sopenharmony_ci * there is no PLT. 203562306a36Sopenharmony_ci */ 203662306a36Sopenharmony_ci } else { 203762306a36Sopenharmony_ci /* Verify the PLT. */ 203862306a36Sopenharmony_ci plt = (char *)ip + (insn.disp << 1); 203962306a36Sopenharmony_ci err = copy_from_kernel_nofault(current_plt, plt, BPF_PLT_SIZE); 204062306a36Sopenharmony_ci if (err < 0) 204162306a36Sopenharmony_ci return err; 204262306a36Sopenharmony_ci ret = (char *)ip + 6; 204362306a36Sopenharmony_ci bpf_jit_plt(expected_plt, ret, old_addr); 204462306a36Sopenharmony_ci if (memcmp(current_plt, expected_plt, BPF_PLT_SIZE)) 204562306a36Sopenharmony_ci return -EINVAL; 204662306a36Sopenharmony_ci /* Adjust the call address. */ 204762306a36Sopenharmony_ci bpf_jit_plt(new_plt, ret, new_addr); 204862306a36Sopenharmony_ci s390_kernel_write(plt + (bpf_plt_target - bpf_plt), 204962306a36Sopenharmony_ci new_plt + (bpf_plt_target - bpf_plt), 205062306a36Sopenharmony_ci sizeof(void *)); 205162306a36Sopenharmony_ci } 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci /* Adjust the mask of the branch. */ 205462306a36Sopenharmony_ci insn.opc = 0xc004 | (new_addr ? 0xf0 : 0); 205562306a36Sopenharmony_ci s390_kernel_write((char *)ip + 1, (char *)&insn.opc + 1, 1); 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci /* Make the new code visible to the other CPUs. */ 205862306a36Sopenharmony_ci text_poke_sync_lock(); 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci return 0; 206162306a36Sopenharmony_ci} 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_cistruct bpf_tramp_jit { 206462306a36Sopenharmony_ci struct bpf_jit common; 206562306a36Sopenharmony_ci int orig_stack_args_off;/* Offset of arguments placed on stack by the 206662306a36Sopenharmony_ci * func_addr's original caller 206762306a36Sopenharmony_ci */ 206862306a36Sopenharmony_ci int stack_size; /* Trampoline stack size */ 206962306a36Sopenharmony_ci int backchain_off; /* Offset of backchain */ 207062306a36Sopenharmony_ci int stack_args_off; /* Offset of stack arguments for calling 207162306a36Sopenharmony_ci * func_addr, has to be at the top 207262306a36Sopenharmony_ci */ 207362306a36Sopenharmony_ci int reg_args_off; /* Offset of register arguments for calling 207462306a36Sopenharmony_ci * func_addr 207562306a36Sopenharmony_ci */ 207662306a36Sopenharmony_ci int ip_off; /* For bpf_get_func_ip(), has to be at 207762306a36Sopenharmony_ci * (ctx - 16) 207862306a36Sopenharmony_ci */ 207962306a36Sopenharmony_ci int arg_cnt_off; /* For bpf_get_func_arg_cnt(), has to be at 208062306a36Sopenharmony_ci * (ctx - 8) 208162306a36Sopenharmony_ci */ 208262306a36Sopenharmony_ci int bpf_args_off; /* Offset of BPF_PROG context, which consists 208362306a36Sopenharmony_ci * of BPF arguments followed by return value 208462306a36Sopenharmony_ci */ 208562306a36Sopenharmony_ci int retval_off; /* Offset of return value (see above) */ 208662306a36Sopenharmony_ci int r7_r8_off; /* Offset of saved %r7 and %r8, which are used 208762306a36Sopenharmony_ci * for __bpf_prog_enter() return value and 208862306a36Sopenharmony_ci * func_addr respectively 208962306a36Sopenharmony_ci */ 209062306a36Sopenharmony_ci int run_ctx_off; /* Offset of struct bpf_tramp_run_ctx */ 209162306a36Sopenharmony_ci int tccnt_off; /* Offset of saved tailcall counter */ 209262306a36Sopenharmony_ci int r14_off; /* Offset of saved %r14, has to be at the 209362306a36Sopenharmony_ci * bottom */ 209462306a36Sopenharmony_ci int do_fexit; /* do_fexit: label */ 209562306a36Sopenharmony_ci}; 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_cistatic void load_imm64(struct bpf_jit *jit, int dst_reg, u64 val) 209862306a36Sopenharmony_ci{ 209962306a36Sopenharmony_ci /* llihf %dst_reg,val_hi */ 210062306a36Sopenharmony_ci EMIT6_IMM(0xc00e0000, dst_reg, (val >> 32)); 210162306a36Sopenharmony_ci /* oilf %rdst_reg,val_lo */ 210262306a36Sopenharmony_ci EMIT6_IMM(0xc00d0000, dst_reg, val); 210362306a36Sopenharmony_ci} 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_cistatic int invoke_bpf_prog(struct bpf_tramp_jit *tjit, 210662306a36Sopenharmony_ci const struct btf_func_model *m, 210762306a36Sopenharmony_ci struct bpf_tramp_link *tlink, bool save_ret) 210862306a36Sopenharmony_ci{ 210962306a36Sopenharmony_ci struct bpf_jit *jit = &tjit->common; 211062306a36Sopenharmony_ci int cookie_off = tjit->run_ctx_off + 211162306a36Sopenharmony_ci offsetof(struct bpf_tramp_run_ctx, bpf_cookie); 211262306a36Sopenharmony_ci struct bpf_prog *p = tlink->link.prog; 211362306a36Sopenharmony_ci int patch; 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci /* 211662306a36Sopenharmony_ci * run_ctx.cookie = tlink->cookie; 211762306a36Sopenharmony_ci */ 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci /* %r0 = tlink->cookie */ 212062306a36Sopenharmony_ci load_imm64(jit, REG_W0, tlink->cookie); 212162306a36Sopenharmony_ci /* stg %r0,cookie_off(%r15) */ 212262306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W0, REG_0, REG_15, cookie_off); 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci /* 212562306a36Sopenharmony_ci * if ((start = __bpf_prog_enter(p, &run_ctx)) == 0) 212662306a36Sopenharmony_ci * goto skip; 212762306a36Sopenharmony_ci */ 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci /* %r1 = __bpf_prog_enter */ 213062306a36Sopenharmony_ci load_imm64(jit, REG_1, (u64)bpf_trampoline_enter(p)); 213162306a36Sopenharmony_ci /* %r2 = p */ 213262306a36Sopenharmony_ci load_imm64(jit, REG_2, (u64)p); 213362306a36Sopenharmony_ci /* la %r3,run_ctx_off(%r15) */ 213462306a36Sopenharmony_ci EMIT4_DISP(0x41000000, REG_3, REG_15, tjit->run_ctx_off); 213562306a36Sopenharmony_ci /* %r1() */ 213662306a36Sopenharmony_ci call_r1(jit); 213762306a36Sopenharmony_ci /* ltgr %r7,%r2 */ 213862306a36Sopenharmony_ci EMIT4(0xb9020000, REG_7, REG_2); 213962306a36Sopenharmony_ci /* brcl 8,skip */ 214062306a36Sopenharmony_ci patch = jit->prg; 214162306a36Sopenharmony_ci EMIT6_PCREL_RILC(0xc0040000, 8, 0); 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci /* 214462306a36Sopenharmony_ci * retval = bpf_func(args, p->insnsi); 214562306a36Sopenharmony_ci */ 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci /* %r1 = p->bpf_func */ 214862306a36Sopenharmony_ci load_imm64(jit, REG_1, (u64)p->bpf_func); 214962306a36Sopenharmony_ci /* la %r2,bpf_args_off(%r15) */ 215062306a36Sopenharmony_ci EMIT4_DISP(0x41000000, REG_2, REG_15, tjit->bpf_args_off); 215162306a36Sopenharmony_ci /* %r3 = p->insnsi */ 215262306a36Sopenharmony_ci if (!p->jited) 215362306a36Sopenharmony_ci load_imm64(jit, REG_3, (u64)p->insnsi); 215462306a36Sopenharmony_ci /* %r1() */ 215562306a36Sopenharmony_ci call_r1(jit); 215662306a36Sopenharmony_ci /* stg %r2,retval_off(%r15) */ 215762306a36Sopenharmony_ci if (save_ret) { 215862306a36Sopenharmony_ci if (sign_extend(jit, REG_2, m->ret_size, m->ret_flags)) 215962306a36Sopenharmony_ci return -1; 216062306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0024, REG_2, REG_0, REG_15, 216162306a36Sopenharmony_ci tjit->retval_off); 216262306a36Sopenharmony_ci } 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci /* skip: */ 216562306a36Sopenharmony_ci if (jit->prg_buf) 216662306a36Sopenharmony_ci *(u32 *)&jit->prg_buf[patch + 2] = (jit->prg - patch) >> 1; 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci /* 216962306a36Sopenharmony_ci * __bpf_prog_exit(p, start, &run_ctx); 217062306a36Sopenharmony_ci */ 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci /* %r1 = __bpf_prog_exit */ 217362306a36Sopenharmony_ci load_imm64(jit, REG_1, (u64)bpf_trampoline_exit(p)); 217462306a36Sopenharmony_ci /* %r2 = p */ 217562306a36Sopenharmony_ci load_imm64(jit, REG_2, (u64)p); 217662306a36Sopenharmony_ci /* lgr %r3,%r7 */ 217762306a36Sopenharmony_ci EMIT4(0xb9040000, REG_3, REG_7); 217862306a36Sopenharmony_ci /* la %r4,run_ctx_off(%r15) */ 217962306a36Sopenharmony_ci EMIT4_DISP(0x41000000, REG_4, REG_15, tjit->run_ctx_off); 218062306a36Sopenharmony_ci /* %r1() */ 218162306a36Sopenharmony_ci call_r1(jit); 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci return 0; 218462306a36Sopenharmony_ci} 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_cistatic int alloc_stack(struct bpf_tramp_jit *tjit, size_t size) 218762306a36Sopenharmony_ci{ 218862306a36Sopenharmony_ci int stack_offset = tjit->stack_size; 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci tjit->stack_size += size; 219162306a36Sopenharmony_ci return stack_offset; 219262306a36Sopenharmony_ci} 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci/* ABI uses %r2 - %r6 for parameter passing. */ 219562306a36Sopenharmony_ci#define MAX_NR_REG_ARGS 5 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci/* The "L" field of the "mvc" instruction is 8 bits. */ 219862306a36Sopenharmony_ci#define MAX_MVC_SIZE 256 219962306a36Sopenharmony_ci#define MAX_NR_STACK_ARGS (MAX_MVC_SIZE / sizeof(u64)) 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci/* -mfentry generates a 6-byte nop on s390x. */ 220262306a36Sopenharmony_ci#define S390X_PATCH_SIZE 6 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_cistatic int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, 220562306a36Sopenharmony_ci struct bpf_tramp_jit *tjit, 220662306a36Sopenharmony_ci const struct btf_func_model *m, 220762306a36Sopenharmony_ci u32 flags, 220862306a36Sopenharmony_ci struct bpf_tramp_links *tlinks, 220962306a36Sopenharmony_ci void *func_addr) 221062306a36Sopenharmony_ci{ 221162306a36Sopenharmony_ci struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN]; 221262306a36Sopenharmony_ci struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY]; 221362306a36Sopenharmony_ci struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT]; 221462306a36Sopenharmony_ci int nr_bpf_args, nr_reg_args, nr_stack_args; 221562306a36Sopenharmony_ci struct bpf_jit *jit = &tjit->common; 221662306a36Sopenharmony_ci int arg, bpf_arg_off; 221762306a36Sopenharmony_ci int i, j; 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci /* Support as many stack arguments as "mvc" instruction can handle. */ 222062306a36Sopenharmony_ci nr_reg_args = min_t(int, m->nr_args, MAX_NR_REG_ARGS); 222162306a36Sopenharmony_ci nr_stack_args = m->nr_args - nr_reg_args; 222262306a36Sopenharmony_ci if (nr_stack_args > MAX_NR_STACK_ARGS) 222362306a36Sopenharmony_ci return -ENOTSUPP; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci /* Return to %r14, since func_addr and %r0 are not available. */ 222662306a36Sopenharmony_ci if (!func_addr && !(flags & BPF_TRAMP_F_ORIG_STACK)) 222762306a36Sopenharmony_ci flags |= BPF_TRAMP_F_SKIP_FRAME; 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci /* 223062306a36Sopenharmony_ci * Compute how many arguments we need to pass to BPF programs. 223162306a36Sopenharmony_ci * BPF ABI mirrors that of x86_64: arguments that are 16 bytes or 223262306a36Sopenharmony_ci * smaller are packed into 1 or 2 registers; larger arguments are 223362306a36Sopenharmony_ci * passed via pointers. 223462306a36Sopenharmony_ci * In s390x ABI, arguments that are 8 bytes or smaller are packed into 223562306a36Sopenharmony_ci * a register; larger arguments are passed via pointers. 223662306a36Sopenharmony_ci * We need to deal with this difference. 223762306a36Sopenharmony_ci */ 223862306a36Sopenharmony_ci nr_bpf_args = 0; 223962306a36Sopenharmony_ci for (i = 0; i < m->nr_args; i++) { 224062306a36Sopenharmony_ci if (m->arg_size[i] <= 8) 224162306a36Sopenharmony_ci nr_bpf_args += 1; 224262306a36Sopenharmony_ci else if (m->arg_size[i] <= 16) 224362306a36Sopenharmony_ci nr_bpf_args += 2; 224462306a36Sopenharmony_ci else 224562306a36Sopenharmony_ci return -ENOTSUPP; 224662306a36Sopenharmony_ci } 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci /* 224962306a36Sopenharmony_ci * Calculate the stack layout. 225062306a36Sopenharmony_ci */ 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci /* 225362306a36Sopenharmony_ci * Allocate STACK_FRAME_OVERHEAD bytes for the callees. As the s390x 225462306a36Sopenharmony_ci * ABI requires, put our backchain at the end of the allocated memory. 225562306a36Sopenharmony_ci */ 225662306a36Sopenharmony_ci tjit->stack_size = STACK_FRAME_OVERHEAD; 225762306a36Sopenharmony_ci tjit->backchain_off = tjit->stack_size - sizeof(u64); 225862306a36Sopenharmony_ci tjit->stack_args_off = alloc_stack(tjit, nr_stack_args * sizeof(u64)); 225962306a36Sopenharmony_ci tjit->reg_args_off = alloc_stack(tjit, nr_reg_args * sizeof(u64)); 226062306a36Sopenharmony_ci tjit->ip_off = alloc_stack(tjit, sizeof(u64)); 226162306a36Sopenharmony_ci tjit->arg_cnt_off = alloc_stack(tjit, sizeof(u64)); 226262306a36Sopenharmony_ci tjit->bpf_args_off = alloc_stack(tjit, nr_bpf_args * sizeof(u64)); 226362306a36Sopenharmony_ci tjit->retval_off = alloc_stack(tjit, sizeof(u64)); 226462306a36Sopenharmony_ci tjit->r7_r8_off = alloc_stack(tjit, 2 * sizeof(u64)); 226562306a36Sopenharmony_ci tjit->run_ctx_off = alloc_stack(tjit, 226662306a36Sopenharmony_ci sizeof(struct bpf_tramp_run_ctx)); 226762306a36Sopenharmony_ci tjit->tccnt_off = alloc_stack(tjit, sizeof(u64)); 226862306a36Sopenharmony_ci tjit->r14_off = alloc_stack(tjit, sizeof(u64) * 2); 226962306a36Sopenharmony_ci /* 227062306a36Sopenharmony_ci * In accordance with the s390x ABI, the caller has allocated 227162306a36Sopenharmony_ci * STACK_FRAME_OVERHEAD bytes for us. 8 of them contain the caller's 227262306a36Sopenharmony_ci * backchain, and the rest we can use. 227362306a36Sopenharmony_ci */ 227462306a36Sopenharmony_ci tjit->stack_size -= STACK_FRAME_OVERHEAD - sizeof(u64); 227562306a36Sopenharmony_ci tjit->orig_stack_args_off = tjit->stack_size + STACK_FRAME_OVERHEAD; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci /* lgr %r1,%r15 */ 227862306a36Sopenharmony_ci EMIT4(0xb9040000, REG_1, REG_15); 227962306a36Sopenharmony_ci /* aghi %r15,-stack_size */ 228062306a36Sopenharmony_ci EMIT4_IMM(0xa70b0000, REG_15, -tjit->stack_size); 228162306a36Sopenharmony_ci /* stg %r1,backchain_off(%r15) */ 228262306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0024, REG_1, REG_0, REG_15, 228362306a36Sopenharmony_ci tjit->backchain_off); 228462306a36Sopenharmony_ci /* mvc tccnt_off(4,%r15),stack_size+STK_OFF_TCCNT(%r15) */ 228562306a36Sopenharmony_ci _EMIT6(0xd203f000 | tjit->tccnt_off, 228662306a36Sopenharmony_ci 0xf000 | (tjit->stack_size + STK_OFF_TCCNT)); 228762306a36Sopenharmony_ci /* stmg %r2,%rN,fwd_reg_args_off(%r15) */ 228862306a36Sopenharmony_ci if (nr_reg_args) 228962306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, 0x0024, REG_2, 229062306a36Sopenharmony_ci REG_2 + (nr_reg_args - 1), REG_15, 229162306a36Sopenharmony_ci tjit->reg_args_off); 229262306a36Sopenharmony_ci for (i = 0, j = 0; i < m->nr_args; i++) { 229362306a36Sopenharmony_ci if (i < MAX_NR_REG_ARGS) 229462306a36Sopenharmony_ci arg = REG_2 + i; 229562306a36Sopenharmony_ci else 229662306a36Sopenharmony_ci arg = tjit->orig_stack_args_off + 229762306a36Sopenharmony_ci (i - MAX_NR_REG_ARGS) * sizeof(u64); 229862306a36Sopenharmony_ci bpf_arg_off = tjit->bpf_args_off + j * sizeof(u64); 229962306a36Sopenharmony_ci if (m->arg_size[i] <= 8) { 230062306a36Sopenharmony_ci if (i < MAX_NR_REG_ARGS) 230162306a36Sopenharmony_ci /* stg %arg,bpf_arg_off(%r15) */ 230262306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0024, arg, 230362306a36Sopenharmony_ci REG_0, REG_15, bpf_arg_off); 230462306a36Sopenharmony_ci else 230562306a36Sopenharmony_ci /* mvc bpf_arg_off(8,%r15),arg(%r15) */ 230662306a36Sopenharmony_ci _EMIT6(0xd207f000 | bpf_arg_off, 230762306a36Sopenharmony_ci 0xf000 | arg); 230862306a36Sopenharmony_ci j += 1; 230962306a36Sopenharmony_ci } else { 231062306a36Sopenharmony_ci if (i < MAX_NR_REG_ARGS) { 231162306a36Sopenharmony_ci /* mvc bpf_arg_off(16,%r15),0(%arg) */ 231262306a36Sopenharmony_ci _EMIT6(0xd20ff000 | bpf_arg_off, 231362306a36Sopenharmony_ci reg2hex[arg] << 12); 231462306a36Sopenharmony_ci } else { 231562306a36Sopenharmony_ci /* lg %r1,arg(%r15) */ 231662306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0004, REG_1, REG_0, 231762306a36Sopenharmony_ci REG_15, arg); 231862306a36Sopenharmony_ci /* mvc bpf_arg_off(16,%r15),0(%r1) */ 231962306a36Sopenharmony_ci _EMIT6(0xd20ff000 | bpf_arg_off, 0x1000); 232062306a36Sopenharmony_ci } 232162306a36Sopenharmony_ci j += 2; 232262306a36Sopenharmony_ci } 232362306a36Sopenharmony_ci } 232462306a36Sopenharmony_ci /* stmg %r7,%r8,r7_r8_off(%r15) */ 232562306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, 0x0024, REG_7, REG_8, REG_15, 232662306a36Sopenharmony_ci tjit->r7_r8_off); 232762306a36Sopenharmony_ci /* stg %r14,r14_off(%r15) */ 232862306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0024, REG_14, REG_0, REG_15, tjit->r14_off); 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci if (flags & BPF_TRAMP_F_ORIG_STACK) { 233162306a36Sopenharmony_ci /* 233262306a36Sopenharmony_ci * The ftrace trampoline puts the return address (which is the 233362306a36Sopenharmony_ci * address of the original function + S390X_PATCH_SIZE) into 233462306a36Sopenharmony_ci * %r0; see ftrace_shared_hotpatch_trampoline_br and 233562306a36Sopenharmony_ci * ftrace_init_nop() for details. 233662306a36Sopenharmony_ci */ 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci /* lgr %r8,%r0 */ 233962306a36Sopenharmony_ci EMIT4(0xb9040000, REG_8, REG_0); 234062306a36Sopenharmony_ci } else { 234162306a36Sopenharmony_ci /* %r8 = func_addr + S390X_PATCH_SIZE */ 234262306a36Sopenharmony_ci load_imm64(jit, REG_8, (u64)func_addr + S390X_PATCH_SIZE); 234362306a36Sopenharmony_ci } 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci /* 234662306a36Sopenharmony_ci * ip = func_addr; 234762306a36Sopenharmony_ci * arg_cnt = m->nr_args; 234862306a36Sopenharmony_ci */ 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ci if (flags & BPF_TRAMP_F_IP_ARG) { 235162306a36Sopenharmony_ci /* %r0 = func_addr */ 235262306a36Sopenharmony_ci load_imm64(jit, REG_0, (u64)func_addr); 235362306a36Sopenharmony_ci /* stg %r0,ip_off(%r15) */ 235462306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0024, REG_0, REG_0, REG_15, 235562306a36Sopenharmony_ci tjit->ip_off); 235662306a36Sopenharmony_ci } 235762306a36Sopenharmony_ci /* lghi %r0,nr_bpf_args */ 235862306a36Sopenharmony_ci EMIT4_IMM(0xa7090000, REG_0, nr_bpf_args); 235962306a36Sopenharmony_ci /* stg %r0,arg_cnt_off(%r15) */ 236062306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0024, REG_0, REG_0, REG_15, 236162306a36Sopenharmony_ci tjit->arg_cnt_off); 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci if (flags & BPF_TRAMP_F_CALL_ORIG) { 236462306a36Sopenharmony_ci /* 236562306a36Sopenharmony_ci * __bpf_tramp_enter(im); 236662306a36Sopenharmony_ci */ 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_ci /* %r1 = __bpf_tramp_enter */ 236962306a36Sopenharmony_ci load_imm64(jit, REG_1, (u64)__bpf_tramp_enter); 237062306a36Sopenharmony_ci /* %r2 = im */ 237162306a36Sopenharmony_ci load_imm64(jit, REG_2, (u64)im); 237262306a36Sopenharmony_ci /* %r1() */ 237362306a36Sopenharmony_ci call_r1(jit); 237462306a36Sopenharmony_ci } 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci for (i = 0; i < fentry->nr_links; i++) 237762306a36Sopenharmony_ci if (invoke_bpf_prog(tjit, m, fentry->links[i], 237862306a36Sopenharmony_ci flags & BPF_TRAMP_F_RET_FENTRY_RET)) 237962306a36Sopenharmony_ci return -EINVAL; 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci if (fmod_ret->nr_links) { 238262306a36Sopenharmony_ci /* 238362306a36Sopenharmony_ci * retval = 0; 238462306a36Sopenharmony_ci */ 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci /* xc retval_off(8,%r15),retval_off(%r15) */ 238762306a36Sopenharmony_ci _EMIT6(0xd707f000 | tjit->retval_off, 238862306a36Sopenharmony_ci 0xf000 | tjit->retval_off); 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci for (i = 0; i < fmod_ret->nr_links; i++) { 239162306a36Sopenharmony_ci if (invoke_bpf_prog(tjit, m, fmod_ret->links[i], true)) 239262306a36Sopenharmony_ci return -EINVAL; 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci /* 239562306a36Sopenharmony_ci * if (retval) 239662306a36Sopenharmony_ci * goto do_fexit; 239762306a36Sopenharmony_ci */ 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci /* ltg %r0,retval_off(%r15) */ 240062306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0002, REG_0, REG_0, REG_15, 240162306a36Sopenharmony_ci tjit->retval_off); 240262306a36Sopenharmony_ci /* brcl 7,do_fexit */ 240362306a36Sopenharmony_ci EMIT6_PCREL_RILC(0xc0040000, 7, tjit->do_fexit); 240462306a36Sopenharmony_ci } 240562306a36Sopenharmony_ci } 240662306a36Sopenharmony_ci 240762306a36Sopenharmony_ci if (flags & BPF_TRAMP_F_CALL_ORIG) { 240862306a36Sopenharmony_ci /* 240962306a36Sopenharmony_ci * retval = func_addr(args); 241062306a36Sopenharmony_ci */ 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci /* lmg %r2,%rN,reg_args_off(%r15) */ 241362306a36Sopenharmony_ci if (nr_reg_args) 241462306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, 0x0004, REG_2, 241562306a36Sopenharmony_ci REG_2 + (nr_reg_args - 1), REG_15, 241662306a36Sopenharmony_ci tjit->reg_args_off); 241762306a36Sopenharmony_ci /* mvc stack_args_off(N,%r15),orig_stack_args_off(%r15) */ 241862306a36Sopenharmony_ci if (nr_stack_args) 241962306a36Sopenharmony_ci _EMIT6(0xd200f000 | 242062306a36Sopenharmony_ci (nr_stack_args * sizeof(u64) - 1) << 16 | 242162306a36Sopenharmony_ci tjit->stack_args_off, 242262306a36Sopenharmony_ci 0xf000 | tjit->orig_stack_args_off); 242362306a36Sopenharmony_ci /* mvc STK_OFF_TCCNT(4,%r15),tccnt_off(%r15) */ 242462306a36Sopenharmony_ci _EMIT6(0xd203f000 | STK_OFF_TCCNT, 0xf000 | tjit->tccnt_off); 242562306a36Sopenharmony_ci /* lgr %r1,%r8 */ 242662306a36Sopenharmony_ci EMIT4(0xb9040000, REG_1, REG_8); 242762306a36Sopenharmony_ci /* %r1() */ 242862306a36Sopenharmony_ci call_r1(jit); 242962306a36Sopenharmony_ci /* stg %r2,retval_off(%r15) */ 243062306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0024, REG_2, REG_0, REG_15, 243162306a36Sopenharmony_ci tjit->retval_off); 243262306a36Sopenharmony_ci 243362306a36Sopenharmony_ci im->ip_after_call = jit->prg_buf + jit->prg; 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci /* 243662306a36Sopenharmony_ci * The following nop will be patched by bpf_tramp_image_put(). 243762306a36Sopenharmony_ci */ 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci /* brcl 0,im->ip_epilogue */ 244062306a36Sopenharmony_ci EMIT6_PCREL_RILC(0xc0040000, 0, (u64)im->ip_epilogue); 244162306a36Sopenharmony_ci } 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci /* do_fexit: */ 244462306a36Sopenharmony_ci tjit->do_fexit = jit->prg; 244562306a36Sopenharmony_ci for (i = 0; i < fexit->nr_links; i++) 244662306a36Sopenharmony_ci if (invoke_bpf_prog(tjit, m, fexit->links[i], false)) 244762306a36Sopenharmony_ci return -EINVAL; 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci if (flags & BPF_TRAMP_F_CALL_ORIG) { 245062306a36Sopenharmony_ci im->ip_epilogue = jit->prg_buf + jit->prg; 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci /* 245362306a36Sopenharmony_ci * __bpf_tramp_exit(im); 245462306a36Sopenharmony_ci */ 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci /* %r1 = __bpf_tramp_exit */ 245762306a36Sopenharmony_ci load_imm64(jit, REG_1, (u64)__bpf_tramp_exit); 245862306a36Sopenharmony_ci /* %r2 = im */ 245962306a36Sopenharmony_ci load_imm64(jit, REG_2, (u64)im); 246062306a36Sopenharmony_ci /* %r1() */ 246162306a36Sopenharmony_ci call_r1(jit); 246262306a36Sopenharmony_ci } 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci /* lmg %r2,%rN,reg_args_off(%r15) */ 246562306a36Sopenharmony_ci if ((flags & BPF_TRAMP_F_RESTORE_REGS) && nr_reg_args) 246662306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, 0x0004, REG_2, 246762306a36Sopenharmony_ci REG_2 + (nr_reg_args - 1), REG_15, 246862306a36Sopenharmony_ci tjit->reg_args_off); 246962306a36Sopenharmony_ci /* lgr %r1,%r8 */ 247062306a36Sopenharmony_ci if (!(flags & BPF_TRAMP_F_SKIP_FRAME)) 247162306a36Sopenharmony_ci EMIT4(0xb9040000, REG_1, REG_8); 247262306a36Sopenharmony_ci /* lmg %r7,%r8,r7_r8_off(%r15) */ 247362306a36Sopenharmony_ci EMIT6_DISP_LH(0xeb000000, 0x0004, REG_7, REG_8, REG_15, 247462306a36Sopenharmony_ci tjit->r7_r8_off); 247562306a36Sopenharmony_ci /* lg %r14,r14_off(%r15) */ 247662306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0004, REG_14, REG_0, REG_15, tjit->r14_off); 247762306a36Sopenharmony_ci /* lg %r2,retval_off(%r15) */ 247862306a36Sopenharmony_ci if (flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET)) 247962306a36Sopenharmony_ci EMIT6_DISP_LH(0xe3000000, 0x0004, REG_2, REG_0, REG_15, 248062306a36Sopenharmony_ci tjit->retval_off); 248162306a36Sopenharmony_ci /* mvc stack_size+STK_OFF_TCCNT(4,%r15),tccnt_off(%r15) */ 248262306a36Sopenharmony_ci _EMIT6(0xd203f000 | (tjit->stack_size + STK_OFF_TCCNT), 248362306a36Sopenharmony_ci 0xf000 | tjit->tccnt_off); 248462306a36Sopenharmony_ci /* aghi %r15,stack_size */ 248562306a36Sopenharmony_ci EMIT4_IMM(0xa70b0000, REG_15, tjit->stack_size); 248662306a36Sopenharmony_ci /* Emit an expoline for the following indirect jump. */ 248762306a36Sopenharmony_ci if (nospec_uses_trampoline()) 248862306a36Sopenharmony_ci emit_expoline(jit); 248962306a36Sopenharmony_ci if (flags & BPF_TRAMP_F_SKIP_FRAME) 249062306a36Sopenharmony_ci /* br %r14 */ 249162306a36Sopenharmony_ci _EMIT2(0x07fe); 249262306a36Sopenharmony_ci else 249362306a36Sopenharmony_ci /* br %r1 */ 249462306a36Sopenharmony_ci _EMIT2(0x07f1); 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci emit_r1_thunk(jit); 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci return 0; 249962306a36Sopenharmony_ci} 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ciint arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, 250262306a36Sopenharmony_ci void *image_end, const struct btf_func_model *m, 250362306a36Sopenharmony_ci u32 flags, struct bpf_tramp_links *tlinks, 250462306a36Sopenharmony_ci void *func_addr) 250562306a36Sopenharmony_ci{ 250662306a36Sopenharmony_ci struct bpf_tramp_jit tjit; 250762306a36Sopenharmony_ci int ret; 250862306a36Sopenharmony_ci int i; 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 251162306a36Sopenharmony_ci if (i == 0) { 251262306a36Sopenharmony_ci /* Compute offsets, check whether the code fits. */ 251362306a36Sopenharmony_ci memset(&tjit, 0, sizeof(tjit)); 251462306a36Sopenharmony_ci } else { 251562306a36Sopenharmony_ci /* Generate the code. */ 251662306a36Sopenharmony_ci tjit.common.prg = 0; 251762306a36Sopenharmony_ci tjit.common.prg_buf = image; 251862306a36Sopenharmony_ci } 251962306a36Sopenharmony_ci ret = __arch_prepare_bpf_trampoline(im, &tjit, m, flags, 252062306a36Sopenharmony_ci tlinks, func_addr); 252162306a36Sopenharmony_ci if (ret < 0) 252262306a36Sopenharmony_ci return ret; 252362306a36Sopenharmony_ci if (tjit.common.prg > (char *)image_end - (char *)image) 252462306a36Sopenharmony_ci /* 252562306a36Sopenharmony_ci * Use the same error code as for exceeding 252662306a36Sopenharmony_ci * BPF_MAX_TRAMP_LINKS. 252762306a36Sopenharmony_ci */ 252862306a36Sopenharmony_ci return -E2BIG; 252962306a36Sopenharmony_ci } 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_ci return tjit.common.prg; 253262306a36Sopenharmony_ci} 253362306a36Sopenharmony_ci 253462306a36Sopenharmony_cibool bpf_jit_supports_subprog_tailcalls(void) 253562306a36Sopenharmony_ci{ 253662306a36Sopenharmony_ci return true; 253762306a36Sopenharmony_ci} 2538