blob: 2f5030d8383f9d3ff5a944db98c0c25444f5ed10 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/* 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
33sk_load_word:
34 PPC_LCMPI r_addr, 0
35 blt bpf_slow_path_word_neg
36 .globl sk_load_word_positive_offset
37sk_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
51sk_load_half:
52 PPC_LCMPI r_addr, 0
53 blt bpf_slow_path_half_neg
54 .globl sk_load_half_positive_offset
55sk_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
67sk_load_byte:
68 PPC_LCMPI r_addr, 0
69 blt bpf_slow_path_byte_neg
70 .globl sk_load_byte_positive_offset
71sk_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
82sk_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
86sk_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
124bpf_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
130bpf_slow_path_half:
131 bpf_slow_path_common(2)
132 lhz r_A, BPF_PPC_STACK_BASIC+(2*8)(r1)
133 blr
134
135bpf_slow_path_byte:
136 bpf_slow_path_common(1)
137 lbz r_A, BPF_PPC_STACK_BASIC+(2*8)(r1)
138 blr
139
140bpf_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
177bpf_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
182sk_load_word_negative_offset:
183 sk_negative_common(4)
184 lwz r_A, 0(r_addr)
185 blr
186
187bpf_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
192sk_load_half_negative_offset:
193 sk_negative_common(2)
194 lhz r_A, 0(r_addr)
195 blr
196
197bpf_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
202sk_load_byte_negative_offset:
203 sk_negative_common(1)
204 lbz r_A, 0(r_addr)
205 blr
206
207bpf_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
212sk_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
218bpf_error_slow:
219 /* fabricate a cr0 = lt */
220 li r_scratch1, -1
221 PPC_LCMPI r_scratch1, 0
222bpf_error:
223 /* Entered with cr0 = lt */
224 li r3, 0
225 /* Generated code will 'blt epilogue', returning 0. */
226 blr