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
227