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