1/* bpf_jit.S: Packet/header access helper functions 2 * for PPC64 BPF compiler. 3 * 4 * Copyright 2011 Matt Evans <matt@ozlabs.org>, IBM Corporation 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; version 2 9 * of the License. 10 */ 11 12#include <asm/ppc_asm.h> 13#include <asm/asm-compat.h> 14#include "bpf_jit32.h" 15 16/* 17 * All of these routines are called directly from generated code, 18 * whose register usage is: 19 * 20 * r3 skb 21 * r4,r5 A,X 22 * r6 *** address parameter to helper *** 23 * r7-r10 scratch 24 * r14 skb->data 25 * r15 skb headlen 26 * r16-31 M[] 27 */ 28 29/* 30 * To consider: These helpers are so small it could be better to just 31 * generate them inline. Inline code can do the simple headlen check 32 * then branch directly to slow_path_XXX if required. (In fact, could 33 * load a spare GPR with the address of slow_path_generic and pass size 34 * as an argument, making the call site a mtlr, li and bllr.) 35 */ 36 .globl sk_load_word 37sk_load_word: 38 PPC_LCMPI r_addr, 0 39 blt bpf_slow_path_word_neg 40 .globl sk_load_word_positive_offset 41sk_load_word_positive_offset: 42 /* Are we accessing past headlen? */ 43 subi r_scratch1, r_HL, 4 44 PPC_LCMP r_scratch1, r_addr 45 blt bpf_slow_path_word 46 /* Nope, just hitting the header. cr0 here is eq or gt! */ 47#ifdef __LITTLE_ENDIAN__ 48 lwbrx r_A, r_D, r_addr 49#else 50 lwzx r_A, r_D, r_addr 51#endif 52 blr /* Return success, cr0 != LT */ 53 54 .globl sk_load_half 55sk_load_half: 56 PPC_LCMPI r_addr, 0 57 blt bpf_slow_path_half_neg 58 .globl sk_load_half_positive_offset 59sk_load_half_positive_offset: 60 subi r_scratch1, r_HL, 2 61 PPC_LCMP r_scratch1, r_addr 62 blt bpf_slow_path_half 63#ifdef __LITTLE_ENDIAN__ 64 lhbrx r_A, r_D, r_addr 65#else 66 lhzx r_A, r_D, r_addr 67#endif 68 blr 69 70 .globl sk_load_byte 71sk_load_byte: 72 PPC_LCMPI r_addr, 0 73 blt bpf_slow_path_byte_neg 74 .globl sk_load_byte_positive_offset 75sk_load_byte_positive_offset: 76 PPC_LCMP r_HL, r_addr 77 ble bpf_slow_path_byte 78 lbzx r_A, r_D, r_addr 79 blr 80 81/* 82 * BPF_LDX | BPF_B | BPF_MSH: ldxb 4*([offset]&0xf) 83 * r_addr is the offset value 84 */ 85 .globl sk_load_byte_msh 86sk_load_byte_msh: 87 PPC_LCMPI r_addr, 0 88 blt bpf_slow_path_byte_msh_neg 89 .globl sk_load_byte_msh_positive_offset 90sk_load_byte_msh_positive_offset: 91 PPC_LCMP r_HL, r_addr 92 ble bpf_slow_path_byte_msh 93 lbzx r_X, r_D, r_addr 94 rlwinm r_X, r_X, 2, 32-4-2, 31-2 95 blr 96 97/* Call out to skb_copy_bits: 98 * We'll need to back up our volatile regs first; we have 99 * local variable space at r1+(BPF_PPC_STACK_BASIC). 100 * Allocate a new stack frame here to remain ABI-compliant in 101 * stashing LR. 102 */ 103#define bpf_slow_path_common(SIZE) \ 104 mflr r0; \ 105 PPC_STL r0, PPC_LR_STKOFF(r1); \ 106 /* R3 goes in parameter space of caller's frame */ \ 107 PPC_STL r_skb, (BPF_PPC_STACKFRAME+BPF_PPC_STACK_R3_OFF)(r1); \ 108 PPC_STL r_A, (BPF_PPC_STACK_BASIC+(0*REG_SZ))(r1); \ 109 PPC_STL r_X, (BPF_PPC_STACK_BASIC+(1*REG_SZ))(r1); \ 110 addi r5, r1, BPF_PPC_STACK_BASIC+(2*REG_SZ); \ 111 PPC_STLU r1, -BPF_PPC_SLOWPATH_FRAME(r1); \ 112 /* R3 = r_skb, as passed */ \ 113 mr r4, r_addr; \ 114 li r6, SIZE; \ 115 bl skb_copy_bits; \ 116 nop; \ 117 /* R3 = 0 on success */ \ 118 addi r1, r1, BPF_PPC_SLOWPATH_FRAME; \ 119 PPC_LL r0, PPC_LR_STKOFF(r1); \ 120 PPC_LL r_A, (BPF_PPC_STACK_BASIC+(0*REG_SZ))(r1); \ 121 PPC_LL r_X, (BPF_PPC_STACK_BASIC+(1*REG_SZ))(r1); \ 122 mtlr r0; \ 123 PPC_LCMPI r3, 0; \ 124 blt bpf_error; /* cr0 = LT */ \ 125 PPC_LL r_skb, (BPF_PPC_STACKFRAME+BPF_PPC_STACK_R3_OFF)(r1); \ 126 /* Great success! */ 127 128bpf_slow_path_word: 129 bpf_slow_path_common(4) 130 /* Data value is on stack, and cr0 != LT */ 131 lwz r_A, BPF_PPC_STACK_BASIC+(2*REG_SZ)(r1) 132 blr 133 134bpf_slow_path_half: 135 bpf_slow_path_common(2) 136 lhz r_A, BPF_PPC_STACK_BASIC+(2*8)(r1) 137 blr 138 139bpf_slow_path_byte: 140 bpf_slow_path_common(1) 141 lbz r_A, BPF_PPC_STACK_BASIC+(2*8)(r1) 142 blr 143 144bpf_slow_path_byte_msh: 145 bpf_slow_path_common(1) 146 lbz r_X, BPF_PPC_STACK_BASIC+(2*8)(r1) 147 rlwinm r_X, r_X, 2, 32-4-2, 31-2 148 blr 149 150/* Call out to bpf_internal_load_pointer_neg_helper: 151 * We'll need to back up our volatile regs first; we have 152 * local variable space at r1+(BPF_PPC_STACK_BASIC). 153 * Allocate a new stack frame here to remain ABI-compliant in 154 * stashing LR. 155 */ 156#define sk_negative_common(SIZE) \ 157 mflr r0; \ 158 PPC_STL r0, PPC_LR_STKOFF(r1); \ 159 /* R3 goes in parameter space of caller's frame */ \ 160 PPC_STL r_skb, (BPF_PPC_STACKFRAME+BPF_PPC_STACK_R3_OFF)(r1); \ 161 PPC_STL r_A, (BPF_PPC_STACK_BASIC+(0*REG_SZ))(r1); \ 162 PPC_STL r_X, (BPF_PPC_STACK_BASIC+(1*REG_SZ))(r1); \ 163 PPC_STLU r1, -BPF_PPC_SLOWPATH_FRAME(r1); \ 164 /* R3 = r_skb, as passed */ \ 165 mr r4, r_addr; \ 166 li r5, SIZE; \ 167 bl bpf_internal_load_pointer_neg_helper; \ 168 nop; \ 169 /* R3 != 0 on success */ \ 170 addi r1, r1, BPF_PPC_SLOWPATH_FRAME; \ 171 PPC_LL r0, PPC_LR_STKOFF(r1); \ 172 PPC_LL r_A, (BPF_PPC_STACK_BASIC+(0*REG_SZ))(r1); \ 173 PPC_LL r_X, (BPF_PPC_STACK_BASIC+(1*REG_SZ))(r1); \ 174 mtlr r0; \ 175 PPC_LCMPLI r3, 0; \ 176 beq bpf_error_slow; /* cr0 = EQ */ \ 177 mr r_addr, r3; \ 178 PPC_LL r_skb, (BPF_PPC_STACKFRAME+BPF_PPC_STACK_R3_OFF)(r1); \ 179 /* Great success! */ 180 181bpf_slow_path_word_neg: 182 lis r_scratch1,-32 /* SKF_LL_OFF */ 183 PPC_LCMP r_addr, r_scratch1 /* addr < SKF_* */ 184 blt bpf_error /* cr0 = LT */ 185 .globl sk_load_word_negative_offset 186sk_load_word_negative_offset: 187 sk_negative_common(4) 188 lwz r_A, 0(r_addr) 189 blr 190 191bpf_slow_path_half_neg: 192 lis r_scratch1,-32 /* SKF_LL_OFF */ 193 PPC_LCMP r_addr, r_scratch1 /* addr < SKF_* */ 194 blt bpf_error /* cr0 = LT */ 195 .globl sk_load_half_negative_offset 196sk_load_half_negative_offset: 197 sk_negative_common(2) 198 lhz r_A, 0(r_addr) 199 blr 200 201bpf_slow_path_byte_neg: 202 lis r_scratch1,-32 /* SKF_LL_OFF */ 203 PPC_LCMP r_addr, r_scratch1 /* addr < SKF_* */ 204 blt bpf_error /* cr0 = LT */ 205 .globl sk_load_byte_negative_offset 206sk_load_byte_negative_offset: 207 sk_negative_common(1) 208 lbz r_A, 0(r_addr) 209 blr 210 211bpf_slow_path_byte_msh_neg: 212 lis r_scratch1,-32 /* SKF_LL_OFF */ 213 PPC_LCMP r_addr, r_scratch1 /* addr < SKF_* */ 214 blt bpf_error /* cr0 = LT */ 215 .globl sk_load_byte_msh_negative_offset 216sk_load_byte_msh_negative_offset: 217 sk_negative_common(1) 218 lbz r_X, 0(r_addr) 219 rlwinm r_X, r_X, 2, 32-4-2, 31-2 220 blr 221 222bpf_error_slow: 223 /* fabricate a cr0 = lt */ 224 li r_scratch1, -1 225 PPC_LCMPI r_scratch1, 0 226bpf_error: 227 /* Entered with cr0 = lt */ 228 li r3, 0 229 /* Generated code will 'blt epilogue', returning 0. */ 230 blr 231