• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * bpf_jib_asm.S: Packet/header access helper functions for MIPS/MIPS64 BPF
3 * compiler.
4 *
5 * Copyright (C) 2015 Imagination Technologies Ltd.
6 * Author: Markos Chandras <markos.chandras@imgtec.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; version 2 of the License.
11 */
12
13#include <asm/asm.h>
14#include <asm/regdef.h>
15#include "bpf_jit.h"
16
17/* ABI
18 *
19 * r_skb_hl	skb header length
20 * r_skb_data	skb data
21 * r_off(a1)	offset register
22 * r_A		BPF register A
23 * r_X		PF register X
24 * r_skb(a0)	*skb
25 * r_M		*scratch memory
26 * r_skb_le	skb length
27 * r_s0		Scratch register 0
28 * r_s1		Scratch register 1
29 *
30 * On entry:
31 * a0: *skb
32 * a1: offset (imm or imm + X)
33 *
34 * All non-BPF-ABI registers are free for use. On return, we only
35 * care about r_ret. The BPF-ABI registers are assumed to remain
36 * unmodified during the entire filter operation.
37 */
38
39#define skb	a0
40#define offset	a1
41#define SKF_LL_OFF  (-0x200000) /* Can't include linux/filter.h in assembly */
42
43	/* We know better :) so prevent assembler reordering etc */
44	.set 	noreorder
45
46#define is_offset_negative(TYPE)				\
47	/* If offset is negative we have more work to do */	\
48	slti	t0, offset, 0;					\
49	bgtz	t0, bpf_slow_path_##TYPE##_neg;			\
50	/* Be careful what follows in DS. */
51
52#define is_offset_in_header(SIZE, TYPE)				\
53	/* Reading from header? */				\
54	addiu	$r_s0, $r_skb_hl, -SIZE;			\
55	slt	t0, $r_s0, offset;				\
56	bgtz	t0, bpf_slow_path_##TYPE;			\
57
58LEAF(sk_load_word)
59	is_offset_negative(word)
60FEXPORT(sk_load_word_positive)
61	is_offset_in_header(4, word)
62	/* Offset within header boundaries */
63	PTR_ADDU t1, $r_skb_data, offset
64	.set	reorder
65	lw	$r_A, 0(t1)
66	.set	noreorder
67#ifdef CONFIG_CPU_LITTLE_ENDIAN
68# if defined(__mips_isa_rev) && (__mips_isa_rev >= 2)
69	wsbh	t0, $r_A
70	rotr	$r_A, t0, 16
71# else
72	sll	t0, $r_A, 24
73	srl	t1, $r_A, 24
74	srl	t2, $r_A, 8
75	or	t0, t0, t1
76	andi	t2, t2, 0xff00
77	andi	t1, $r_A, 0xff00
78	or	t0, t0, t2
79	sll	t1, t1, 8
80	or	$r_A, t0, t1
81# endif
82#endif
83	jr	$r_ra
84	 move	$r_ret, zero
85	END(sk_load_word)
86
87LEAF(sk_load_half)
88	is_offset_negative(half)
89FEXPORT(sk_load_half_positive)
90	is_offset_in_header(2, half)
91	/* Offset within header boundaries */
92	PTR_ADDU t1, $r_skb_data, offset
93	lhu	$r_A, 0(t1)
94#ifdef CONFIG_CPU_LITTLE_ENDIAN
95# if defined(__mips_isa_rev) && (__mips_isa_rev >= 2)
96	wsbh	$r_A, $r_A
97# else
98	sll	t0, $r_A, 8
99	srl	t1, $r_A, 8
100	andi	t0, t0, 0xff00
101	or	$r_A, t0, t1
102# endif
103#endif
104	jr	$r_ra
105	 move	$r_ret, zero
106	END(sk_load_half)
107
108LEAF(sk_load_byte)
109	is_offset_negative(byte)
110FEXPORT(sk_load_byte_positive)
111	is_offset_in_header(1, byte)
112	/* Offset within header boundaries */
113	PTR_ADDU t1, $r_skb_data, offset
114	lbu	$r_A, 0(t1)
115	jr	$r_ra
116	 move	$r_ret, zero
117	END(sk_load_byte)
118
119/*
120 * call skb_copy_bits:
121 * (prototype in linux/skbuff.h)
122 *
123 * int skb_copy_bits(sk_buff *skb, int offset, void *to, int len)
124 *
125 * o32 mandates we leave 4 spaces for argument registers in case
126 * the callee needs to use them. Even though we don't care about
127 * the argument registers ourselves, we need to allocate that space
128 * to remain ABI compliant since the callee may want to use that space.
129 * We also allocate 2 more spaces for $r_ra and our return register (*to).
130 *
131 * n64 is a bit different. The *caller* will allocate the space to preserve
132 * the arguments. So in 64-bit kernels, we allocate the 4-arg space for no
133 * good reason but it does not matter that much really.
134 *
135 * (void *to) is returned in r_s0
136 *
137 */
138#ifdef CONFIG_CPU_LITTLE_ENDIAN
139#define DS_OFFSET(SIZE) (4 * SZREG)
140#else
141#define DS_OFFSET(SIZE) ((4 * SZREG) + (4 - SIZE))
142#endif
143#define bpf_slow_path_common(SIZE)				\
144	/* Quick check. Are we within reasonable boundaries? */ \
145	LONG_ADDIU	$r_s1, $r_skb_len, -SIZE;		\
146	sltu		$r_s0, offset, $r_s1;			\
147	beqz		$r_s0, fault;				\
148	/* Load 4th argument in DS */				\
149	 LONG_ADDIU	a3, zero, SIZE;				\
150	PTR_ADDIU	$r_sp, $r_sp, -(6 * SZREG);		\
151	PTR_LA		t0, skb_copy_bits;			\
152	PTR_S		$r_ra, (5 * SZREG)($r_sp);		\
153	/* Assign low slot to a2 */				\
154	PTR_ADDIU	a2, $r_sp, DS_OFFSET(SIZE);		\
155	jalr		t0;					\
156	/* Reset our destination slot (DS but it's ok) */	\
157	 INT_S		zero, (4 * SZREG)($r_sp);		\
158	/*							\
159	 * skb_copy_bits returns 0 on success and -EFAULT	\
160	 * on error. Our data live in a2. Do not bother with	\
161	 * our data if an error has been returned.		\
162	 */							\
163	/* Restore our frame */					\
164	PTR_L		$r_ra, (5 * SZREG)($r_sp);		\
165	INT_L		$r_s0, (4 * SZREG)($r_sp);		\
166	bltz		v0, fault;				\
167	 PTR_ADDIU	$r_sp, $r_sp, 6 * SZREG;		\
168	move		$r_ret, zero;				\
169
170NESTED(bpf_slow_path_word, (6 * SZREG), $r_sp)
171	bpf_slow_path_common(4)
172#ifdef CONFIG_CPU_LITTLE_ENDIAN
173# if defined(__mips_isa_rev) && (__mips_isa_rev >= 2)
174	wsbh	t0, $r_s0
175	jr	$r_ra
176	 rotr	$r_A, t0, 16
177# else
178	sll	t0, $r_s0, 24
179	srl	t1, $r_s0, 24
180	srl	t2, $r_s0, 8
181	or	t0, t0, t1
182	andi	t2, t2, 0xff00
183	andi	t1, $r_s0, 0xff00
184	or	t0, t0, t2
185	sll	t1, t1, 8
186	jr	$r_ra
187	 or	$r_A, t0, t1
188# endif
189#else
190	jr	$r_ra
191	 move	$r_A, $r_s0
192#endif
193
194	END(bpf_slow_path_word)
195
196NESTED(bpf_slow_path_half, (6 * SZREG), $r_sp)
197	bpf_slow_path_common(2)
198#ifdef CONFIG_CPU_LITTLE_ENDIAN
199# if defined(__mips_isa_rev) && (__mips_isa_rev >= 2)
200	jr	$r_ra
201	 wsbh	$r_A, $r_s0
202# else
203	sll	t0, $r_s0, 8
204	andi	t1, $r_s0, 0xff00
205	andi	t0, t0, 0xff00
206	srl	t1, t1, 8
207	jr	$r_ra
208	 or	$r_A, t0, t1
209# endif
210#else
211	jr	$r_ra
212	 move	$r_A, $r_s0
213#endif
214
215	END(bpf_slow_path_half)
216
217NESTED(bpf_slow_path_byte, (6 * SZREG), $r_sp)
218	bpf_slow_path_common(1)
219	jr	$r_ra
220	 move	$r_A, $r_s0
221
222	END(bpf_slow_path_byte)
223
224/*
225 * Negative entry points
226 */
227	.macro bpf_is_end_of_data
228	li	t0, SKF_LL_OFF
229	/* Reading link layer data? */
230	slt	t1, offset, t0
231	bgtz	t1, fault
232	/* Be careful what follows in DS. */
233	.endm
234/*
235 * call skb_copy_bits:
236 * (prototype in linux/filter.h)
237 *
238 * void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb,
239 *                                            int k, unsigned int size)
240 *
241 * see above (bpf_slow_path_common) for ABI restrictions
242 */
243#define bpf_negative_common(SIZE)					\
244	PTR_ADDIU	$r_sp, $r_sp, -(6 * SZREG);			\
245	PTR_LA		t0, bpf_internal_load_pointer_neg_helper;	\
246	PTR_S		$r_ra, (5 * SZREG)($r_sp);			\
247	jalr		t0;						\
248	 li		a2, SIZE;					\
249	PTR_L		$r_ra, (5 * SZREG)($r_sp);			\
250	/* Check return pointer */					\
251	beqz		v0, fault;					\
252	 PTR_ADDIU	$r_sp, $r_sp, 6 * SZREG;			\
253	/* Preserve our pointer */					\
254	move		$r_s0, v0;					\
255	/* Set return value */						\
256	move		$r_ret, zero;					\
257
258bpf_slow_path_word_neg:
259	bpf_is_end_of_data
260NESTED(sk_load_word_negative, (6 * SZREG), $r_sp)
261	bpf_negative_common(4)
262	jr	$r_ra
263	 lw	$r_A, 0($r_s0)
264	END(sk_load_word_negative)
265
266bpf_slow_path_half_neg:
267	bpf_is_end_of_data
268NESTED(sk_load_half_negative, (6 * SZREG), $r_sp)
269	bpf_negative_common(2)
270	jr	$r_ra
271	 lhu	$r_A, 0($r_s0)
272	END(sk_load_half_negative)
273
274bpf_slow_path_byte_neg:
275	bpf_is_end_of_data
276NESTED(sk_load_byte_negative, (6 * SZREG), $r_sp)
277	bpf_negative_common(1)
278	jr	$r_ra
279	 lbu	$r_A, 0($r_s0)
280	END(sk_load_byte_negative)
281
282fault:
283	jr	$r_ra
284	 addiu $r_ret, zero, 1
285