05be18241e
Now the helper function from filter.c for negative offsets is exported, it can be used it in the jit to handle negative offsets. First modify the asm load helper functions to handle: - know positive offsets - know negative offsets - any offset then the compiler can be modified to explicitly use these helper when appropriate. This fixes the case of a negative X register and allows to lift the restriction that bpf programs with negative offsets can't be jited. Tested-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Jan Seiffert <kaffeemonster@googlemail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
221 lines
5.9 KiB
ArmAsm
221 lines
5.9 KiB
ArmAsm
/* bpf_jit.S: Packet/header access helper functions
|
|
* for PPC64 BPF compiler.
|
|
*
|
|
* Copyright 2011 Matt Evans <matt@ozlabs.org>, IBM Corporation
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; version 2
|
|
* of the License.
|
|
*/
|
|
|
|
#include <asm/ppc_asm.h>
|
|
#include "bpf_jit.h"
|
|
|
|
/*
|
|
* All of these routines are called directly from generated code,
|
|
* whose register usage is:
|
|
*
|
|
* r3 skb
|
|
* r4,r5 A,X
|
|
* r6 *** address parameter to helper ***
|
|
* r7-r10 scratch
|
|
* r14 skb->data
|
|
* r15 skb headlen
|
|
* r16-31 M[]
|
|
*/
|
|
|
|
/*
|
|
* To consider: These helpers are so small it could be better to just
|
|
* generate them inline. Inline code can do the simple headlen check
|
|
* then branch directly to slow_path_XXX if required. (In fact, could
|
|
* load a spare GPR with the address of slow_path_generic and pass size
|
|
* as an argument, making the call site a mtlr, li and bllr.)
|
|
*/
|
|
.globl sk_load_word
|
|
sk_load_word:
|
|
cmpdi r_addr, 0
|
|
blt bpf_slow_path_word_neg
|
|
.globl sk_load_word_positive_offset
|
|
sk_load_word_positive_offset:
|
|
/* Are we accessing past headlen? */
|
|
subi r_scratch1, r_HL, 4
|
|
cmpd r_scratch1, r_addr
|
|
blt bpf_slow_path_word
|
|
/* Nope, just hitting the header. cr0 here is eq or gt! */
|
|
lwzx r_A, r_D, r_addr
|
|
/* When big endian we don't need to byteswap. */
|
|
blr /* Return success, cr0 != LT */
|
|
|
|
.globl sk_load_half
|
|
sk_load_half:
|
|
cmpdi r_addr, 0
|
|
blt bpf_slow_path_half_neg
|
|
.globl sk_load_half_positive_offset
|
|
sk_load_half_positive_offset:
|
|
subi r_scratch1, r_HL, 2
|
|
cmpd r_scratch1, r_addr
|
|
blt bpf_slow_path_half
|
|
lhzx r_A, r_D, r_addr
|
|
blr
|
|
|
|
.globl sk_load_byte
|
|
sk_load_byte:
|
|
cmpdi r_addr, 0
|
|
blt bpf_slow_path_byte_neg
|
|
.globl sk_load_byte_positive_offset
|
|
sk_load_byte_positive_offset:
|
|
cmpd r_HL, r_addr
|
|
ble bpf_slow_path_byte
|
|
lbzx r_A, r_D, r_addr
|
|
blr
|
|
|
|
/*
|
|
* BPF_S_LDX_B_MSH: ldxb 4*([offset]&0xf)
|
|
* r_addr is the offset value
|
|
*/
|
|
.globl sk_load_byte_msh
|
|
sk_load_byte_msh:
|
|
cmpdi r_addr, 0
|
|
blt bpf_slow_path_byte_msh_neg
|
|
.globl sk_load_byte_msh_positive_offset
|
|
sk_load_byte_msh_positive_offset:
|
|
cmpd r_HL, r_addr
|
|
ble bpf_slow_path_byte_msh
|
|
lbzx r_X, r_D, r_addr
|
|
rlwinm r_X, r_X, 2, 32-4-2, 31-2
|
|
blr
|
|
|
|
/* Call out to skb_copy_bits:
|
|
* We'll need to back up our volatile regs first; we have
|
|
* local variable space at r1+(BPF_PPC_STACK_BASIC).
|
|
* Allocate a new stack frame here to remain ABI-compliant in
|
|
* stashing LR.
|
|
*/
|
|
#define bpf_slow_path_common(SIZE) \
|
|
mflr r0; \
|
|
std r0, 16(r1); \
|
|
/* R3 goes in parameter space of caller's frame */ \
|
|
std r_skb, (BPF_PPC_STACKFRAME+48)(r1); \
|
|
std r_A, (BPF_PPC_STACK_BASIC+(0*8))(r1); \
|
|
std r_X, (BPF_PPC_STACK_BASIC+(1*8))(r1); \
|
|
addi r5, r1, BPF_PPC_STACK_BASIC+(2*8); \
|
|
stdu r1, -BPF_PPC_SLOWPATH_FRAME(r1); \
|
|
/* R3 = r_skb, as passed */ \
|
|
mr r4, r_addr; \
|
|
li r6, SIZE; \
|
|
bl skb_copy_bits; \
|
|
/* R3 = 0 on success */ \
|
|
addi r1, r1, BPF_PPC_SLOWPATH_FRAME; \
|
|
ld r0, 16(r1); \
|
|
ld r_A, (BPF_PPC_STACK_BASIC+(0*8))(r1); \
|
|
ld r_X, (BPF_PPC_STACK_BASIC+(1*8))(r1); \
|
|
mtlr r0; \
|
|
cmpdi r3, 0; \
|
|
blt bpf_error; /* cr0 = LT */ \
|
|
ld r_skb, (BPF_PPC_STACKFRAME+48)(r1); \
|
|
/* Great success! */
|
|
|
|
bpf_slow_path_word:
|
|
bpf_slow_path_common(4)
|
|
/* Data value is on stack, and cr0 != LT */
|
|
lwz r_A, BPF_PPC_STACK_BASIC+(2*8)(r1)
|
|
blr
|
|
|
|
bpf_slow_path_half:
|
|
bpf_slow_path_common(2)
|
|
lhz r_A, BPF_PPC_STACK_BASIC+(2*8)(r1)
|
|
blr
|
|
|
|
bpf_slow_path_byte:
|
|
bpf_slow_path_common(1)
|
|
lbz r_A, BPF_PPC_STACK_BASIC+(2*8)(r1)
|
|
blr
|
|
|
|
bpf_slow_path_byte_msh:
|
|
bpf_slow_path_common(1)
|
|
lbz r_X, BPF_PPC_STACK_BASIC+(2*8)(r1)
|
|
rlwinm r_X, r_X, 2, 32-4-2, 31-2
|
|
blr
|
|
|
|
/* Call out to bpf_internal_load_pointer_neg_helper:
|
|
* We'll need to back up our volatile regs first; we have
|
|
* local variable space at r1+(BPF_PPC_STACK_BASIC).
|
|
* Allocate a new stack frame here to remain ABI-compliant in
|
|
* stashing LR.
|
|
*/
|
|
#define sk_negative_common(SIZE) \
|
|
mflr r0; \
|
|
std r0, 16(r1); \
|
|
/* R3 goes in parameter space of caller's frame */ \
|
|
std r_skb, (BPF_PPC_STACKFRAME+48)(r1); \
|
|
std r_A, (BPF_PPC_STACK_BASIC+(0*8))(r1); \
|
|
std r_X, (BPF_PPC_STACK_BASIC+(1*8))(r1); \
|
|
stdu r1, -BPF_PPC_SLOWPATH_FRAME(r1); \
|
|
/* R3 = r_skb, as passed */ \
|
|
mr r4, r_addr; \
|
|
li r5, SIZE; \
|
|
bl bpf_internal_load_pointer_neg_helper; \
|
|
/* R3 != 0 on success */ \
|
|
addi r1, r1, BPF_PPC_SLOWPATH_FRAME; \
|
|
ld r0, 16(r1); \
|
|
ld r_A, (BPF_PPC_STACK_BASIC+(0*8))(r1); \
|
|
ld r_X, (BPF_PPC_STACK_BASIC+(1*8))(r1); \
|
|
mtlr r0; \
|
|
cmpldi r3, 0; \
|
|
beq bpf_error_slow; /* cr0 = EQ */ \
|
|
mr r_addr, r3; \
|
|
ld r_skb, (BPF_PPC_STACKFRAME+48)(r1); \
|
|
/* Great success! */
|
|
|
|
bpf_slow_path_word_neg:
|
|
lis r_scratch1,-32 /* SKF_LL_OFF */
|
|
cmpd r_addr, r_scratch1 /* addr < SKF_* */
|
|
blt bpf_error /* cr0 = LT */
|
|
.globl sk_load_word_negative_offset
|
|
sk_load_word_negative_offset:
|
|
sk_negative_common(4)
|
|
lwz r_A, 0(r_addr)
|
|
blr
|
|
|
|
bpf_slow_path_half_neg:
|
|
lis r_scratch1,-32 /* SKF_LL_OFF */
|
|
cmpd r_addr, r_scratch1 /* addr < SKF_* */
|
|
blt bpf_error /* cr0 = LT */
|
|
.globl sk_load_half_negative_offset
|
|
sk_load_half_negative_offset:
|
|
sk_negative_common(2)
|
|
lhz r_A, 0(r_addr)
|
|
blr
|
|
|
|
bpf_slow_path_byte_neg:
|
|
lis r_scratch1,-32 /* SKF_LL_OFF */
|
|
cmpd r_addr, r_scratch1 /* addr < SKF_* */
|
|
blt bpf_error /* cr0 = LT */
|
|
.globl sk_load_byte_negative_offset
|
|
sk_load_byte_negative_offset:
|
|
sk_negative_common(1)
|
|
lbz r_A, 0(r_addr)
|
|
blr
|
|
|
|
bpf_slow_path_byte_msh_neg:
|
|
lis r_scratch1,-32 /* SKF_LL_OFF */
|
|
cmpd r_addr, r_scratch1 /* addr < SKF_* */
|
|
blt bpf_error /* cr0 = LT */
|
|
.globl sk_load_byte_msh_negative_offset
|
|
sk_load_byte_msh_negative_offset:
|
|
sk_negative_common(1)
|
|
lbz r_X, 0(r_addr)
|
|
rlwinm r_X, r_X, 2, 32-4-2, 31-2
|
|
blr
|
|
|
|
bpf_error_slow:
|
|
/* fabricate a cr0 = lt */
|
|
li r_scratch1, -1
|
|
cmpdi r_scratch1, 0
|
|
bpf_error:
|
|
/* Entered with cr0 = lt */
|
|
li r3, 0
|
|
/* Generated code will 'blt epilogue', returning 0. */
|
|
blr
|