18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 28c2ecf20Sopenharmony_ci/* bpf_jit.S: Packet/header access helper functions 38c2ecf20Sopenharmony_ci * for PPC64 BPF compiler. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2011 Matt Evans <matt@ozlabs.org>, IBM Corporation 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <asm/ppc_asm.h> 98c2ecf20Sopenharmony_ci#include <asm/asm-compat.h> 108c2ecf20Sopenharmony_ci#include "bpf_jit32.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* 138c2ecf20Sopenharmony_ci * All of these routines are called directly from generated code, 148c2ecf20Sopenharmony_ci * whose register usage is: 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * r3 skb 178c2ecf20Sopenharmony_ci * r4,r5 A,X 188c2ecf20Sopenharmony_ci * r6 *** address parameter to helper *** 198c2ecf20Sopenharmony_ci * r7-r10 scratch 208c2ecf20Sopenharmony_ci * r14 skb->data 218c2ecf20Sopenharmony_ci * r15 skb headlen 228c2ecf20Sopenharmony_ci * r16-31 M[] 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * To consider: These helpers are so small it could be better to just 278c2ecf20Sopenharmony_ci * generate them inline. Inline code can do the simple headlen check 288c2ecf20Sopenharmony_ci * then branch directly to slow_path_XXX if required. (In fact, could 298c2ecf20Sopenharmony_ci * load a spare GPR with the address of slow_path_generic and pass size 308c2ecf20Sopenharmony_ci * as an argument, making the call site a mtlr, li and bllr.) 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci .globl sk_load_word 338c2ecf20Sopenharmony_cisk_load_word: 348c2ecf20Sopenharmony_ci PPC_LCMPI r_addr, 0 358c2ecf20Sopenharmony_ci blt bpf_slow_path_word_neg 368c2ecf20Sopenharmony_ci .globl sk_load_word_positive_offset 378c2ecf20Sopenharmony_cisk_load_word_positive_offset: 388c2ecf20Sopenharmony_ci /* Are we accessing past headlen? */ 398c2ecf20Sopenharmony_ci subi r_scratch1, r_HL, 4 408c2ecf20Sopenharmony_ci PPC_LCMP r_scratch1, r_addr 418c2ecf20Sopenharmony_ci blt bpf_slow_path_word 428c2ecf20Sopenharmony_ci /* Nope, just hitting the header. cr0 here is eq or gt! */ 438c2ecf20Sopenharmony_ci#ifdef __LITTLE_ENDIAN__ 448c2ecf20Sopenharmony_ci lwbrx r_A, r_D, r_addr 458c2ecf20Sopenharmony_ci#else 468c2ecf20Sopenharmony_ci lwzx r_A, r_D, r_addr 478c2ecf20Sopenharmony_ci#endif 488c2ecf20Sopenharmony_ci blr /* Return success, cr0 != LT */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci .globl sk_load_half 518c2ecf20Sopenharmony_cisk_load_half: 528c2ecf20Sopenharmony_ci PPC_LCMPI r_addr, 0 538c2ecf20Sopenharmony_ci blt bpf_slow_path_half_neg 548c2ecf20Sopenharmony_ci .globl sk_load_half_positive_offset 558c2ecf20Sopenharmony_cisk_load_half_positive_offset: 568c2ecf20Sopenharmony_ci subi r_scratch1, r_HL, 2 578c2ecf20Sopenharmony_ci PPC_LCMP r_scratch1, r_addr 588c2ecf20Sopenharmony_ci blt bpf_slow_path_half 598c2ecf20Sopenharmony_ci#ifdef __LITTLE_ENDIAN__ 608c2ecf20Sopenharmony_ci lhbrx r_A, r_D, r_addr 618c2ecf20Sopenharmony_ci#else 628c2ecf20Sopenharmony_ci lhzx r_A, r_D, r_addr 638c2ecf20Sopenharmony_ci#endif 648c2ecf20Sopenharmony_ci blr 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci .globl sk_load_byte 678c2ecf20Sopenharmony_cisk_load_byte: 688c2ecf20Sopenharmony_ci PPC_LCMPI r_addr, 0 698c2ecf20Sopenharmony_ci blt bpf_slow_path_byte_neg 708c2ecf20Sopenharmony_ci .globl sk_load_byte_positive_offset 718c2ecf20Sopenharmony_cisk_load_byte_positive_offset: 728c2ecf20Sopenharmony_ci PPC_LCMP r_HL, r_addr 738c2ecf20Sopenharmony_ci ble bpf_slow_path_byte 748c2ecf20Sopenharmony_ci lbzx r_A, r_D, r_addr 758c2ecf20Sopenharmony_ci blr 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* 788c2ecf20Sopenharmony_ci * BPF_LDX | BPF_B | BPF_MSH: ldxb 4*([offset]&0xf) 798c2ecf20Sopenharmony_ci * r_addr is the offset value 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci .globl sk_load_byte_msh 828c2ecf20Sopenharmony_cisk_load_byte_msh: 838c2ecf20Sopenharmony_ci PPC_LCMPI r_addr, 0 848c2ecf20Sopenharmony_ci blt bpf_slow_path_byte_msh_neg 858c2ecf20Sopenharmony_ci .globl sk_load_byte_msh_positive_offset 868c2ecf20Sopenharmony_cisk_load_byte_msh_positive_offset: 878c2ecf20Sopenharmony_ci PPC_LCMP r_HL, r_addr 888c2ecf20Sopenharmony_ci ble bpf_slow_path_byte_msh 898c2ecf20Sopenharmony_ci lbzx r_X, r_D, r_addr 908c2ecf20Sopenharmony_ci rlwinm r_X, r_X, 2, 32-4-2, 31-2 918c2ecf20Sopenharmony_ci blr 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* Call out to skb_copy_bits: 948c2ecf20Sopenharmony_ci * We'll need to back up our volatile regs first; we have 958c2ecf20Sopenharmony_ci * local variable space at r1+(BPF_PPC_STACK_BASIC). 968c2ecf20Sopenharmony_ci * Allocate a new stack frame here to remain ABI-compliant in 978c2ecf20Sopenharmony_ci * stashing LR. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ci#define bpf_slow_path_common(SIZE) \ 1008c2ecf20Sopenharmony_ci mflr r0; \ 1018c2ecf20Sopenharmony_ci PPC_STL r0, PPC_LR_STKOFF(r1); \ 1028c2ecf20Sopenharmony_ci /* R3 goes in parameter space of caller's frame */ \ 1038c2ecf20Sopenharmony_ci PPC_STL r_skb, (BPF_PPC_STACKFRAME+BPF_PPC_STACK_R3_OFF)(r1); \ 1048c2ecf20Sopenharmony_ci PPC_STL r_A, (BPF_PPC_STACK_BASIC+(0*REG_SZ))(r1); \ 1058c2ecf20Sopenharmony_ci PPC_STL r_X, (BPF_PPC_STACK_BASIC+(1*REG_SZ))(r1); \ 1068c2ecf20Sopenharmony_ci addi r5, r1, BPF_PPC_STACK_BASIC+(2*REG_SZ); \ 1078c2ecf20Sopenharmony_ci PPC_STLU r1, -BPF_PPC_SLOWPATH_FRAME(r1); \ 1088c2ecf20Sopenharmony_ci /* R3 = r_skb, as passed */ \ 1098c2ecf20Sopenharmony_ci mr r4, r_addr; \ 1108c2ecf20Sopenharmony_ci li r6, SIZE; \ 1118c2ecf20Sopenharmony_ci bl skb_copy_bits; \ 1128c2ecf20Sopenharmony_ci nop; \ 1138c2ecf20Sopenharmony_ci /* R3 = 0 on success */ \ 1148c2ecf20Sopenharmony_ci addi r1, r1, BPF_PPC_SLOWPATH_FRAME; \ 1158c2ecf20Sopenharmony_ci PPC_LL r0, PPC_LR_STKOFF(r1); \ 1168c2ecf20Sopenharmony_ci PPC_LL r_A, (BPF_PPC_STACK_BASIC+(0*REG_SZ))(r1); \ 1178c2ecf20Sopenharmony_ci PPC_LL r_X, (BPF_PPC_STACK_BASIC+(1*REG_SZ))(r1); \ 1188c2ecf20Sopenharmony_ci mtlr r0; \ 1198c2ecf20Sopenharmony_ci PPC_LCMPI r3, 0; \ 1208c2ecf20Sopenharmony_ci blt bpf_error; /* cr0 = LT */ \ 1218c2ecf20Sopenharmony_ci PPC_LL r_skb, (BPF_PPC_STACKFRAME+BPF_PPC_STACK_R3_OFF)(r1); \ 1228c2ecf20Sopenharmony_ci /* Great success! */ 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cibpf_slow_path_word: 1258c2ecf20Sopenharmony_ci bpf_slow_path_common(4) 1268c2ecf20Sopenharmony_ci /* Data value is on stack, and cr0 != LT */ 1278c2ecf20Sopenharmony_ci lwz r_A, BPF_PPC_STACK_BASIC+(2*REG_SZ)(r1) 1288c2ecf20Sopenharmony_ci blr 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cibpf_slow_path_half: 1318c2ecf20Sopenharmony_ci bpf_slow_path_common(2) 1328c2ecf20Sopenharmony_ci lhz r_A, BPF_PPC_STACK_BASIC+(2*8)(r1) 1338c2ecf20Sopenharmony_ci blr 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cibpf_slow_path_byte: 1368c2ecf20Sopenharmony_ci bpf_slow_path_common(1) 1378c2ecf20Sopenharmony_ci lbz r_A, BPF_PPC_STACK_BASIC+(2*8)(r1) 1388c2ecf20Sopenharmony_ci blr 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cibpf_slow_path_byte_msh: 1418c2ecf20Sopenharmony_ci bpf_slow_path_common(1) 1428c2ecf20Sopenharmony_ci lbz r_X, BPF_PPC_STACK_BASIC+(2*8)(r1) 1438c2ecf20Sopenharmony_ci rlwinm r_X, r_X, 2, 32-4-2, 31-2 1448c2ecf20Sopenharmony_ci blr 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/* Call out to bpf_internal_load_pointer_neg_helper: 1478c2ecf20Sopenharmony_ci * We'll need to back up our volatile regs first; we have 1488c2ecf20Sopenharmony_ci * local variable space at r1+(BPF_PPC_STACK_BASIC). 1498c2ecf20Sopenharmony_ci * Allocate a new stack frame here to remain ABI-compliant in 1508c2ecf20Sopenharmony_ci * stashing LR. 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_ci#define sk_negative_common(SIZE) \ 1538c2ecf20Sopenharmony_ci mflr r0; \ 1548c2ecf20Sopenharmony_ci PPC_STL r0, PPC_LR_STKOFF(r1); \ 1558c2ecf20Sopenharmony_ci /* R3 goes in parameter space of caller's frame */ \ 1568c2ecf20Sopenharmony_ci PPC_STL r_skb, (BPF_PPC_STACKFRAME+BPF_PPC_STACK_R3_OFF)(r1); \ 1578c2ecf20Sopenharmony_ci PPC_STL r_A, (BPF_PPC_STACK_BASIC+(0*REG_SZ))(r1); \ 1588c2ecf20Sopenharmony_ci PPC_STL r_X, (BPF_PPC_STACK_BASIC+(1*REG_SZ))(r1); \ 1598c2ecf20Sopenharmony_ci PPC_STLU r1, -BPF_PPC_SLOWPATH_FRAME(r1); \ 1608c2ecf20Sopenharmony_ci /* R3 = r_skb, as passed */ \ 1618c2ecf20Sopenharmony_ci mr r4, r_addr; \ 1628c2ecf20Sopenharmony_ci li r5, SIZE; \ 1638c2ecf20Sopenharmony_ci bl bpf_internal_load_pointer_neg_helper; \ 1648c2ecf20Sopenharmony_ci nop; \ 1658c2ecf20Sopenharmony_ci /* R3 != 0 on success */ \ 1668c2ecf20Sopenharmony_ci addi r1, r1, BPF_PPC_SLOWPATH_FRAME; \ 1678c2ecf20Sopenharmony_ci PPC_LL r0, PPC_LR_STKOFF(r1); \ 1688c2ecf20Sopenharmony_ci PPC_LL r_A, (BPF_PPC_STACK_BASIC+(0*REG_SZ))(r1); \ 1698c2ecf20Sopenharmony_ci PPC_LL r_X, (BPF_PPC_STACK_BASIC+(1*REG_SZ))(r1); \ 1708c2ecf20Sopenharmony_ci mtlr r0; \ 1718c2ecf20Sopenharmony_ci PPC_LCMPLI r3, 0; \ 1728c2ecf20Sopenharmony_ci beq bpf_error_slow; /* cr0 = EQ */ \ 1738c2ecf20Sopenharmony_ci mr r_addr, r3; \ 1748c2ecf20Sopenharmony_ci PPC_LL r_skb, (BPF_PPC_STACKFRAME+BPF_PPC_STACK_R3_OFF)(r1); \ 1758c2ecf20Sopenharmony_ci /* Great success! */ 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cibpf_slow_path_word_neg: 1788c2ecf20Sopenharmony_ci lis r_scratch1,-32 /* SKF_LL_OFF */ 1798c2ecf20Sopenharmony_ci PPC_LCMP r_addr, r_scratch1 /* addr < SKF_* */ 1808c2ecf20Sopenharmony_ci blt bpf_error /* cr0 = LT */ 1818c2ecf20Sopenharmony_ci .globl sk_load_word_negative_offset 1828c2ecf20Sopenharmony_cisk_load_word_negative_offset: 1838c2ecf20Sopenharmony_ci sk_negative_common(4) 1848c2ecf20Sopenharmony_ci lwz r_A, 0(r_addr) 1858c2ecf20Sopenharmony_ci blr 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cibpf_slow_path_half_neg: 1888c2ecf20Sopenharmony_ci lis r_scratch1,-32 /* SKF_LL_OFF */ 1898c2ecf20Sopenharmony_ci PPC_LCMP r_addr, r_scratch1 /* addr < SKF_* */ 1908c2ecf20Sopenharmony_ci blt bpf_error /* cr0 = LT */ 1918c2ecf20Sopenharmony_ci .globl sk_load_half_negative_offset 1928c2ecf20Sopenharmony_cisk_load_half_negative_offset: 1938c2ecf20Sopenharmony_ci sk_negative_common(2) 1948c2ecf20Sopenharmony_ci lhz r_A, 0(r_addr) 1958c2ecf20Sopenharmony_ci blr 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cibpf_slow_path_byte_neg: 1988c2ecf20Sopenharmony_ci lis r_scratch1,-32 /* SKF_LL_OFF */ 1998c2ecf20Sopenharmony_ci PPC_LCMP r_addr, r_scratch1 /* addr < SKF_* */ 2008c2ecf20Sopenharmony_ci blt bpf_error /* cr0 = LT */ 2018c2ecf20Sopenharmony_ci .globl sk_load_byte_negative_offset 2028c2ecf20Sopenharmony_cisk_load_byte_negative_offset: 2038c2ecf20Sopenharmony_ci sk_negative_common(1) 2048c2ecf20Sopenharmony_ci lbz r_A, 0(r_addr) 2058c2ecf20Sopenharmony_ci blr 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cibpf_slow_path_byte_msh_neg: 2088c2ecf20Sopenharmony_ci lis r_scratch1,-32 /* SKF_LL_OFF */ 2098c2ecf20Sopenharmony_ci PPC_LCMP r_addr, r_scratch1 /* addr < SKF_* */ 2108c2ecf20Sopenharmony_ci blt bpf_error /* cr0 = LT */ 2118c2ecf20Sopenharmony_ci .globl sk_load_byte_msh_negative_offset 2128c2ecf20Sopenharmony_cisk_load_byte_msh_negative_offset: 2138c2ecf20Sopenharmony_ci sk_negative_common(1) 2148c2ecf20Sopenharmony_ci lbz r_X, 0(r_addr) 2158c2ecf20Sopenharmony_ci rlwinm r_X, r_X, 2, 32-4-2, 31-2 2168c2ecf20Sopenharmony_ci blr 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cibpf_error_slow: 2198c2ecf20Sopenharmony_ci /* fabricate a cr0 = lt */ 2208c2ecf20Sopenharmony_ci li r_scratch1, -1 2218c2ecf20Sopenharmony_ci PPC_LCMPI r_scratch1, 0 2228c2ecf20Sopenharmony_cibpf_error: 2238c2ecf20Sopenharmony_ci /* Entered with cr0 = lt */ 2248c2ecf20Sopenharmony_ci li r3, 0 2258c2ecf20Sopenharmony_ci /* Generated code will 'blt epilogue', returning 0. */ 2268c2ecf20Sopenharmony_ci blr 227