/* * bpf_jit_asm64.S: Packet/header access helper functions * for PPC64 BPF compiler. * * Copyright 2016, Naveen N. Rao * IBM Corporation * * Based on bpf_jit_asm.S by Matt Evans * * 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 #include #include "bpf_jit64.h" /* * All of these routines are called directly from generated code, * with the below register usage: * r27 skb pointer (ctx) * r25 skb header length * r26 skb->data pointer * r4 offset * * Result is passed back in: * r8 data read in host endian format (accumulator) * * r9 is used as a temporary register */ #define r_skb r27 #define r_hlen r25 #define r_data r26 #define r_off r4 #define r_val r8 #define r_tmp r9 _GLOBAL_TOC(sk_load_word) cmpdi r_off, 0 blt bpf_slow_path_word_neg b sk_load_word_positive_offset _GLOBAL_TOC(sk_load_word_positive_offset) /* Are we accessing past headlen? */ subi r_tmp, r_hlen, 4 cmpd r_tmp, r_off blt bpf_slow_path_word /* Nope, just hitting the header. cr0 here is eq or gt! */ LWZX_BE r_val, r_data, r_off blr /* Return success, cr0 != LT */ _GLOBAL_TOC(sk_load_half) cmpdi r_off, 0 blt bpf_slow_path_half_neg b sk_load_half_positive_offset _GLOBAL_TOC(sk_load_half_positive_offset) subi r_tmp, r_hlen, 2 cmpd r_tmp, r_off blt bpf_slow_path_half LHZX_BE r_val, r_data, r_off blr _GLOBAL_TOC(sk_load_byte) cmpdi r_off, 0 blt bpf_slow_path_byte_neg b sk_load_byte_positive_offset _GLOBAL_TOC(sk_load_byte_positive_offset) cmpd r_hlen, r_off ble bpf_slow_path_byte lbzx r_val, r_data, r_off blr /* * Call out to skb_copy_bits: * Allocate a new stack frame here to remain ABI-compliant in * stashing LR. */ #define bpf_slow_path_common(SIZE) \ mflr r0; \ std r0, PPC_LR_STKOFF(r1); \ stdu r1, -(STACK_FRAME_MIN_SIZE + BPF_PPC_STACK_LOCALS)(r1); \ mr r3, r_skb; \ /* r4 = r_off as passed */ \ addi r5, r1, STACK_FRAME_MIN_SIZE; \ li r6, SIZE; \ bl skb_copy_bits; \ nop; \ /* save r5 */ \ addi r5, r1, STACK_FRAME_MIN_SIZE; \ /* r3 = 0 on success */ \ addi r1, r1, STACK_FRAME_MIN_SIZE + BPF_PPC_STACK_LOCALS; \ ld r0, PPC_LR_STKOFF(r1); \ mtlr r0; \ cmpdi r3, 0; \ blt bpf_error; /* cr0 = LT */ bpf_slow_path_word: bpf_slow_path_common(4) /* Data value is on stack, and cr0 != LT */ LWZX_BE r_val, 0, r5 blr bpf_slow_path_half: bpf_slow_path_common(2) LHZX_BE r_val, 0, r5 blr bpf_slow_path_byte: bpf_slow_path_common(1) lbzx r_val, 0, r5 blr /* * Call out to bpf_internal_load_pointer_neg_helper */ #define sk_negative_common(SIZE) \ mflr r0; \ std r0, PPC_LR_STKOFF(r1); \ stdu r1, -STACK_FRAME_MIN_SIZE(r1); \ mr r3, r_skb; \ /* r4 = r_off, as passed */ \ li r5, SIZE; \ bl bpf_internal_load_pointer_neg_helper; \ nop; \ addi r1, r1, STACK_FRAME_MIN_SIZE; \ ld r0, PPC_LR_STKOFF(r1); \ mtlr r0; \ /* R3 != 0 on success */ \ cmpldi r3, 0; \ beq bpf_error_slow; /* cr0 = EQ */ bpf_slow_path_word_neg: lis r_tmp, -32 /* SKF_LL_OFF */ cmpd r_off, r_tmp /* addr < SKF_* */ blt bpf_error /* cr0 = LT */ b sk_load_word_negative_offset _GLOBAL_TOC(sk_load_word_negative_offset) sk_negative_common(4) LWZX_BE r_val, 0, r3 blr bpf_slow_path_half_neg: lis r_tmp, -32 /* SKF_LL_OFF */ cmpd r_off, r_tmp /* addr < SKF_* */ blt bpf_error /* cr0 = LT */ b sk_load_half_negative_offset _GLOBAL_TOC(sk_load_half_negative_offset) sk_negative_common(2) LHZX_BE r_val, 0, r3 blr bpf_slow_path_byte_neg: lis r_tmp, -32 /* SKF_LL_OFF */ cmpd r_off, r_tmp /* addr < SKF_* */ blt bpf_error /* cr0 = LT */ b sk_load_byte_negative_offset _GLOBAL_TOC(sk_load_byte_negative_offset) sk_negative_common(1) lbzx r_val, 0, r3 blr bpf_error_slow: /* fabricate a cr0 = lt */ li r_tmp, -1 cmpdi r_tmp, 0 bpf_error: /* * Entered with cr0 = lt * Generated code will 'blt epilogue', returning 0. */ li r_val, 0 blr