| /* | |
| * bpf_jit_asm64.S: Packet/header access helper functions | |
| * for PPC64 BPF compiler. | |
| * | |
| * Copyright 2016, Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> | |
| * 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 <asm/ppc_asm.h> | |
| #include <asm/ptrace.h> | |
| #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 |