1*c776a021Sriastradh /* $NetBSD: bpfjit.c,v 1.48 2020/02/01 02:54:02 riastradh Exp $ */
224e8f876Srmind
3e8c0d6c6Salnsn /*-
484db77b7Salnsn * Copyright (c) 2011-2015 Alexander Nasonov.
5e8c0d6c6Salnsn * All rights reserved.
6e8c0d6c6Salnsn *
7e8c0d6c6Salnsn * Redistribution and use in source and binary forms, with or without
8e8c0d6c6Salnsn * modification, are permitted provided that the following conditions
9e8c0d6c6Salnsn * are met:
10e8c0d6c6Salnsn *
11e8c0d6c6Salnsn * 1. Redistributions of source code must retain the above copyright
12e8c0d6c6Salnsn * notice, this list of conditions and the following disclaimer.
13e8c0d6c6Salnsn * 2. Redistributions in binary form must reproduce the above copyright
14e8c0d6c6Salnsn * notice, this list of conditions and the following disclaimer in
15e8c0d6c6Salnsn * the documentation and/or other materials provided with the
16e8c0d6c6Salnsn * distribution.
17e8c0d6c6Salnsn *
18e8c0d6c6Salnsn * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19e8c0d6c6Salnsn * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20e8c0d6c6Salnsn * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21e8c0d6c6Salnsn * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22e8c0d6c6Salnsn * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23e8c0d6c6Salnsn * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24e8c0d6c6Salnsn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25e8c0d6c6Salnsn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26e8c0d6c6Salnsn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27e8c0d6c6Salnsn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28e8c0d6c6Salnsn * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29e8c0d6c6Salnsn * SUCH DAMAGE.
30e8c0d6c6Salnsn */
31e8c0d6c6Salnsn
3285151d38Salnsn #include <sys/cdefs.h>
3385151d38Salnsn #ifdef _KERNEL
34*c776a021Sriastradh __KERNEL_RCSID(0, "$NetBSD: bpfjit.c,v 1.48 2020/02/01 02:54:02 riastradh Exp $");
3585151d38Salnsn #else
36*c776a021Sriastradh __RCSID("$NetBSD: bpfjit.c,v 1.48 2020/02/01 02:54:02 riastradh Exp $");
3785151d38Salnsn #endif
3885151d38Salnsn
3924e8f876Srmind #include <sys/types.h>
4024e8f876Srmind #include <sys/queue.h>
41e8c0d6c6Salnsn
42e8c0d6c6Salnsn #ifndef _KERNEL
43e8c0d6c6Salnsn #include <assert.h>
4498f8ebfbSalnsn #define BJ_ASSERT(c) assert(c)
4598f8ebfbSalnsn #else
4698f8ebfbSalnsn #define BJ_ASSERT(c) KASSERT(c)
4798f8ebfbSalnsn #endif
4898f8ebfbSalnsn
4998f8ebfbSalnsn #ifndef _KERNEL
5098f8ebfbSalnsn #include <stdlib.h>
5198f8ebfbSalnsn #define BJ_ALLOC(sz) malloc(sz)
5298f8ebfbSalnsn #define BJ_FREE(p, sz) free(p)
53e8c0d6c6Salnsn #else
5424e8f876Srmind #include <sys/kmem.h>
5598f8ebfbSalnsn #define BJ_ALLOC(sz) kmem_alloc(sz, KM_SLEEP)
5698f8ebfbSalnsn #define BJ_FREE(p, sz) kmem_free(p, sz)
57e8c0d6c6Salnsn #endif
58e8c0d6c6Salnsn
59e8c0d6c6Salnsn #ifndef _KERNEL
60e8c0d6c6Salnsn #include <limits.h>
61e8c0d6c6Salnsn #include <stdbool.h>
62e8c0d6c6Salnsn #include <stddef.h>
63e8c0d6c6Salnsn #include <stdint.h>
6442037f4eSalnsn #include <string.h>
65e8c0d6c6Salnsn #else
66e8c0d6c6Salnsn #include <sys/atomic.h>
67e8c0d6c6Salnsn #include <sys/module.h>
68e8c0d6c6Salnsn #endif
69e8c0d6c6Salnsn
70df5750d8Srmind #define __BPF_PRIVATE
71df5750d8Srmind #include <net/bpf.h>
7224e8f876Srmind #include <net/bpfjit.h>
73e8c0d6c6Salnsn #include <sljitLir.h>
74e8c0d6c6Salnsn
7598f8ebfbSalnsn #if !defined(_KERNEL) && defined(SLJIT_VERBOSE) && SLJIT_VERBOSE
7698f8ebfbSalnsn #include <stdio.h> /* for stderr */
7798f8ebfbSalnsn #endif
78e8c0d6c6Salnsn
79e8c0d6c6Salnsn /*
8040bda2eeSalnsn * Number of saved registers to pass to sljit_emit_enter() function.
8140bda2eeSalnsn */
8240bda2eeSalnsn #define NSAVEDS 3
8340bda2eeSalnsn
8440bda2eeSalnsn /*
8519fed70dSalnsn * Arguments of generated bpfjit_func_t.
8619fed70dSalnsn * The first argument is reassigned upon entry
8719fed70dSalnsn * to a more frequently used buf argument.
8819fed70dSalnsn */
8944dbc048Salnsn #define BJ_CTX_ARG SLJIT_S0
9044dbc048Salnsn #define BJ_ARGS SLJIT_S1
9119fed70dSalnsn
9219fed70dSalnsn /*
9398f8ebfbSalnsn * Permanent register assignments.
94e8c0d6c6Salnsn */
9544dbc048Salnsn #define BJ_BUF SLJIT_S0
9644dbc048Salnsn //#define BJ_ARGS SLJIT_S1
9744dbc048Salnsn #define BJ_BUFLEN SLJIT_S2
9844dbc048Salnsn #define BJ_AREG SLJIT_R0
9944dbc048Salnsn #define BJ_TMP1REG SLJIT_R1
10044dbc048Salnsn #define BJ_TMP2REG SLJIT_R2
10144dbc048Salnsn #define BJ_XREG SLJIT_R3
10244dbc048Salnsn #define BJ_TMP3REG SLJIT_R4
10398f8ebfbSalnsn
10419fed70dSalnsn #ifdef _KERNEL
10519fed70dSalnsn #define MAX_MEMWORDS BPF_MAX_MEMWORDS
10619fed70dSalnsn #else
10719fed70dSalnsn #define MAX_MEMWORDS BPF_MEMWORDS
10819fed70dSalnsn #endif
10919fed70dSalnsn
11019fed70dSalnsn #define BJ_INIT_NOBITS ((bpf_memword_init_t)0)
11119fed70dSalnsn #define BJ_INIT_MBIT(k) BPF_MEMWORD_INIT(k)
11219fed70dSalnsn #define BJ_INIT_ABIT BJ_INIT_MBIT(MAX_MEMWORDS)
11319fed70dSalnsn #define BJ_INIT_XBIT BJ_INIT_MBIT(MAX_MEMWORDS + 1)
11498f8ebfbSalnsn
1151bf51582Salnsn /*
116c676220dSalnsn * Get a number of memwords and external memwords from a bpf_ctx object.
117c676220dSalnsn */
118c676220dSalnsn #define GET_EXTWORDS(bc) ((bc) ? (bc)->extwords : 0)
119c676220dSalnsn #define GET_MEMWORDS(bc) (GET_EXTWORDS(bc) ? GET_EXTWORDS(bc) : BPF_MEMWORDS)
120c676220dSalnsn
121c676220dSalnsn /*
12224d883a5Salnsn * Optimization hints.
12324d883a5Salnsn */
12424d883a5Salnsn typedef unsigned int bpfjit_hint_t;
125981f777cSalnsn #define BJ_HINT_ABS 0x01 /* packet read at absolute offset */
126981f777cSalnsn #define BJ_HINT_IND 0x02 /* packet read at variable offset */
1276435b941Salnsn #define BJ_HINT_MSH 0x04 /* BPF_MSH instruction */
1286435b941Salnsn #define BJ_HINT_COP 0x08 /* BPF_COP or BPF_COPX instruction */
1296435b941Salnsn #define BJ_HINT_COPX 0x10 /* BPF_COPX instruction */
1306435b941Salnsn #define BJ_HINT_XREG 0x20 /* BJ_XREG is needed */
1316435b941Salnsn #define BJ_HINT_LDX 0x40 /* BPF_LDX instruction */
1326435b941Salnsn #define BJ_HINT_PKT (BJ_HINT_ABS|BJ_HINT_IND|BJ_HINT_MSH)
13324d883a5Salnsn
13424d883a5Salnsn /*
1351bf51582Salnsn * Datatype for Array Bounds Check Elimination (ABC) pass.
1361bf51582Salnsn */
1371bf51582Salnsn typedef uint64_t bpfjit_abc_length_t;
1381bf51582Salnsn #define MAX_ABC_LENGTH (UINT32_MAX + UINT64_C(4)) /* max. width is 4 */
13974e580d2Salnsn
14098f8ebfbSalnsn struct bpfjit_stack
14198f8ebfbSalnsn {
14219fed70dSalnsn bpf_ctx_t *ctx;
14319fed70dSalnsn uint32_t *extmem; /* pointer to external memory store */
14480a29916Salnsn uint32_t reg; /* saved A or X register */
14598f8ebfbSalnsn #ifdef _KERNEL
146b7e8e67dSalnsn int err; /* 3rd argument for m_xword/m_xhalf/m_xbyte function call */
14798f8ebfbSalnsn #endif
14819fed70dSalnsn uint32_t mem[BPF_MEMWORDS]; /* internal memory store */
14998f8ebfbSalnsn };
150e8c0d6c6Salnsn
151e8c0d6c6Salnsn /*
15298f8ebfbSalnsn * Data for BPF_JMP instruction.
15398f8ebfbSalnsn * Forward declaration for struct bpfjit_jump.
15498f8ebfbSalnsn */
15598f8ebfbSalnsn struct bpfjit_jump_data;
15698f8ebfbSalnsn
15798f8ebfbSalnsn /*
15898f8ebfbSalnsn * Node of bjumps list.
159e8c0d6c6Salnsn */
16024e8f876Srmind struct bpfjit_jump {
16198f8ebfbSalnsn struct sljit_jump *sjump;
16298f8ebfbSalnsn SLIST_ENTRY(bpfjit_jump) entries;
16398f8ebfbSalnsn struct bpfjit_jump_data *jdata;
164e8c0d6c6Salnsn };
165e8c0d6c6Salnsn
166e8c0d6c6Salnsn /*
167e8c0d6c6Salnsn * Data for BPF_JMP instruction.
168e8c0d6c6Salnsn */
16924e8f876Srmind struct bpfjit_jump_data {
170e8c0d6c6Salnsn /*
17198f8ebfbSalnsn * These entries make up bjumps list:
17298f8ebfbSalnsn * jtf[0] - when coming from jt path,
17398f8ebfbSalnsn * jtf[1] - when coming from jf path.
174e8c0d6c6Salnsn */
17598f8ebfbSalnsn struct bpfjit_jump jtf[2];
17698f8ebfbSalnsn /*
17798f8ebfbSalnsn * Length calculated by Array Bounds Check Elimination (ABC) pass.
17898f8ebfbSalnsn */
17974e580d2Salnsn bpfjit_abc_length_t abc_length;
18098f8ebfbSalnsn /*
18198f8ebfbSalnsn * Length checked by the last out-of-bounds check.
18298f8ebfbSalnsn */
18374e580d2Salnsn bpfjit_abc_length_t checked_length;
184e8c0d6c6Salnsn };
185e8c0d6c6Salnsn
186e8c0d6c6Salnsn /*
187e8c0d6c6Salnsn * Data for "read from packet" instructions.
188e8c0d6c6Salnsn * See also read_pkt_insn() function below.
189e8c0d6c6Salnsn */
19024e8f876Srmind struct bpfjit_read_pkt_data {
191e8c0d6c6Salnsn /*
19298f8ebfbSalnsn * Length calculated by Array Bounds Check Elimination (ABC) pass.
193e8c0d6c6Salnsn */
19474e580d2Salnsn bpfjit_abc_length_t abc_length;
19598f8ebfbSalnsn /*
19698f8ebfbSalnsn * If positive, emit "if (buflen < check_length) return 0"
19798f8ebfbSalnsn * out-of-bounds check.
1981bf51582Salnsn * Values greater than UINT32_MAX generate unconditional "return 0".
19998f8ebfbSalnsn */
20074e580d2Salnsn bpfjit_abc_length_t check_length;
201e8c0d6c6Salnsn };
202e8c0d6c6Salnsn
203e8c0d6c6Salnsn /*
204e8c0d6c6Salnsn * Additional (optimization-related) data for bpf_insn.
205e8c0d6c6Salnsn */
20624e8f876Srmind struct bpfjit_insn_data {
207e8c0d6c6Salnsn /* List of jumps to this insn. */
20898f8ebfbSalnsn SLIST_HEAD(, bpfjit_jump) bjumps;
209e8c0d6c6Salnsn
210e8c0d6c6Salnsn union {
21198f8ebfbSalnsn struct bpfjit_jump_data jdata;
21298f8ebfbSalnsn struct bpfjit_read_pkt_data rdata;
21398f8ebfbSalnsn } u;
214e8c0d6c6Salnsn
21519fed70dSalnsn bpf_memword_init_t invalid;
21698f8ebfbSalnsn bool unreachable;
217e8c0d6c6Salnsn };
218e8c0d6c6Salnsn
219e8c0d6c6Salnsn #ifdef _KERNEL
220e8c0d6c6Salnsn
221e8c0d6c6Salnsn uint32_t m_xword(const struct mbuf *, uint32_t, int *);
222e8c0d6c6Salnsn uint32_t m_xhalf(const struct mbuf *, uint32_t, int *);
223e8c0d6c6Salnsn uint32_t m_xbyte(const struct mbuf *, uint32_t, int *);
224e8c0d6c6Salnsn
225e8c0d6c6Salnsn MODULE(MODULE_CLASS_MISC, bpfjit, "sljit")
226e8c0d6c6Salnsn
227e8c0d6c6Salnsn static int
bpfjit_modcmd(modcmd_t cmd,void * arg)228e8c0d6c6Salnsn bpfjit_modcmd(modcmd_t cmd, void *arg)
229e8c0d6c6Salnsn {
230e8c0d6c6Salnsn
231e8c0d6c6Salnsn switch (cmd) {
232e8c0d6c6Salnsn case MODULE_CMD_INIT:
233e8c0d6c6Salnsn bpfjit_module_ops.bj_free_code = &bpfjit_free_code;
234*c776a021Sriastradh atomic_store_release(&bpfjit_module_ops.bj_generate_code,
235*c776a021Sriastradh &bpfjit_generate_code);
236e8c0d6c6Salnsn return 0;
237e8c0d6c6Salnsn
238e8c0d6c6Salnsn case MODULE_CMD_FINI:
239e8c0d6c6Salnsn return EOPNOTSUPP;
240e8c0d6c6Salnsn
241e8c0d6c6Salnsn default:
242e8c0d6c6Salnsn return ENOTTY;
243e8c0d6c6Salnsn }
244e8c0d6c6Salnsn }
245e8c0d6c6Salnsn #endif
246e8c0d6c6Salnsn
24724d883a5Salnsn /*
248b7e8e67dSalnsn * Return a number of scratch registers to pass
24924d883a5Salnsn * to sljit_emit_enter() function.
25024d883a5Salnsn */
25144dbc048Salnsn static sljit_s32
nscratches(bpfjit_hint_t hints)25224d883a5Salnsn nscratches(bpfjit_hint_t hints)
25324d883a5Salnsn {
25444dbc048Salnsn sljit_s32 rv = 2;
25524d883a5Salnsn
256b8f42e53Salnsn #ifdef _KERNEL
257d35700ddSalnsn if (hints & BJ_HINT_PKT)
258d35700ddSalnsn rv = 3; /* xcall with three arguments */
259b8f42e53Salnsn #endif
260b8f42e53Salnsn
261981f777cSalnsn if (hints & BJ_HINT_IND)
26224d883a5Salnsn rv = 3; /* uses BJ_TMP2REG */
26324d883a5Salnsn
26424d883a5Salnsn if (hints & BJ_HINT_COP)
26524d883a5Salnsn rv = 3; /* calls copfunc with three arguments */
26624d883a5Salnsn
26780a29916Salnsn if (hints & BJ_HINT_XREG)
26880a29916Salnsn rv = 4; /* uses BJ_XREG */
26980a29916Salnsn
27080a29916Salnsn #ifdef _KERNEL
27180a29916Salnsn if (hints & BJ_HINT_LDX)
27280a29916Salnsn rv = 5; /* uses BJ_TMP3REG */
27380a29916Salnsn #endif
27480a29916Salnsn
2756435b941Salnsn if (hints & BJ_HINT_COPX)
27680a29916Salnsn rv = 5; /* uses BJ_TMP3REG */
2776435b941Salnsn
2786435b941Salnsn return rv;
2796435b941Salnsn }
2806435b941Salnsn
281e8c0d6c6Salnsn static uint32_t
read_width(const struct bpf_insn * pc)28298f8ebfbSalnsn read_width(const struct bpf_insn *pc)
283e8c0d6c6Salnsn {
284e8c0d6c6Salnsn
285e8c0d6c6Salnsn switch (BPF_SIZE(pc->code)) {
286f34037edSalnsn case BPF_W: return 4;
287f34037edSalnsn case BPF_H: return 2;
288f34037edSalnsn case BPF_B: return 1;
289f34037edSalnsn default: return 0;
290e8c0d6c6Salnsn }
291e8c0d6c6Salnsn }
292e8c0d6c6Salnsn
29319fed70dSalnsn /*
29419fed70dSalnsn * Copy buf and buflen members of bpf_args from BJ_ARGS
29519fed70dSalnsn * pointer to BJ_BUF and BJ_BUFLEN registers.
29619fed70dSalnsn */
29719fed70dSalnsn static int
load_buf_buflen(struct sljit_compiler * compiler)29819fed70dSalnsn load_buf_buflen(struct sljit_compiler *compiler)
29919fed70dSalnsn {
30019fed70dSalnsn int status;
30119fed70dSalnsn
30219fed70dSalnsn status = sljit_emit_op1(compiler,
30319fed70dSalnsn SLJIT_MOV_P,
30419fed70dSalnsn BJ_BUF, 0,
30519fed70dSalnsn SLJIT_MEM1(BJ_ARGS),
30619fed70dSalnsn offsetof(struct bpf_args, pkt));
30719fed70dSalnsn if (status != SLJIT_SUCCESS)
30819fed70dSalnsn return status;
30919fed70dSalnsn
31019fed70dSalnsn status = sljit_emit_op1(compiler,
311b7e8e67dSalnsn SLJIT_MOV, /* size_t source */
31219fed70dSalnsn BJ_BUFLEN, 0,
31319fed70dSalnsn SLJIT_MEM1(BJ_ARGS),
31419fed70dSalnsn offsetof(struct bpf_args, buflen));
31519fed70dSalnsn
31619fed70dSalnsn return status;
31719fed70dSalnsn }
31819fed70dSalnsn
31998f8ebfbSalnsn static bool
grow_jumps(struct sljit_jump *** jumps,size_t * size)32098f8ebfbSalnsn grow_jumps(struct sljit_jump ***jumps, size_t *size)
321e8c0d6c6Salnsn {
32298f8ebfbSalnsn struct sljit_jump **newptr;
32398f8ebfbSalnsn const size_t elemsz = sizeof(struct sljit_jump *);
32498f8ebfbSalnsn size_t old_size = *size;
32598f8ebfbSalnsn size_t new_size = 2 * old_size;
326e8c0d6c6Salnsn
32798f8ebfbSalnsn if (new_size < old_size || new_size > SIZE_MAX / elemsz)
32898f8ebfbSalnsn return false;
32998f8ebfbSalnsn
33098f8ebfbSalnsn newptr = BJ_ALLOC(new_size * elemsz);
33198f8ebfbSalnsn if (newptr == NULL)
33298f8ebfbSalnsn return false;
33398f8ebfbSalnsn
33498f8ebfbSalnsn memcpy(newptr, *jumps, old_size * elemsz);
33598f8ebfbSalnsn BJ_FREE(*jumps, old_size * elemsz);
33698f8ebfbSalnsn
33798f8ebfbSalnsn *jumps = newptr;
33898f8ebfbSalnsn *size = new_size;
33998f8ebfbSalnsn return true;
34098f8ebfbSalnsn }
34198f8ebfbSalnsn
34298f8ebfbSalnsn static bool
append_jump(struct sljit_jump * jump,struct sljit_jump *** jumps,size_t * size,size_t * max_size)34398f8ebfbSalnsn append_jump(struct sljit_jump *jump, struct sljit_jump ***jumps,
34498f8ebfbSalnsn size_t *size, size_t *max_size)
34598f8ebfbSalnsn {
34698f8ebfbSalnsn if (*size == *max_size && !grow_jumps(jumps, max_size))
34798f8ebfbSalnsn return false;
34898f8ebfbSalnsn
34998f8ebfbSalnsn (*jumps)[(*size)++] = jump;
35098f8ebfbSalnsn return true;
351e8c0d6c6Salnsn }
352e8c0d6c6Salnsn
353e8c0d6c6Salnsn /*
354d35700ddSalnsn * Emit code for BPF_LD+BPF_B+BPF_ABS A <- P[k:1].
355e8c0d6c6Salnsn */
356e8c0d6c6Salnsn static int
emit_read8(struct sljit_compiler * compiler,sljit_s32 src,uint32_t k)35744dbc048Salnsn emit_read8(struct sljit_compiler *compiler, sljit_s32 src, uint32_t k)
358e8c0d6c6Salnsn {
359e8c0d6c6Salnsn
360e8c0d6c6Salnsn return sljit_emit_op1(compiler,
36144dbc048Salnsn SLJIT_MOV_U8,
36298f8ebfbSalnsn BJ_AREG, 0,
363981f777cSalnsn SLJIT_MEM1(src), k);
364e8c0d6c6Salnsn }
365e8c0d6c6Salnsn
366e8c0d6c6Salnsn /*
367d35700ddSalnsn * Emit code for BPF_LD+BPF_H+BPF_ABS A <- P[k:2].
368e8c0d6c6Salnsn */
369e8c0d6c6Salnsn static int
emit_read16(struct sljit_compiler * compiler,sljit_s32 src,uint32_t k)37044dbc048Salnsn emit_read16(struct sljit_compiler *compiler, sljit_s32 src, uint32_t k)
371e8c0d6c6Salnsn {
372e8c0d6c6Salnsn int status;
373e8c0d6c6Salnsn
374981f777cSalnsn BJ_ASSERT(k <= UINT32_MAX - 1);
375e8c0d6c6Salnsn
376981f777cSalnsn /* A = buf[k]; */
377e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
37844dbc048Salnsn SLJIT_MOV_U8,
37998f8ebfbSalnsn BJ_AREG, 0,
380981f777cSalnsn SLJIT_MEM1(src), k);
381e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
382e8c0d6c6Salnsn return status;
383e8c0d6c6Salnsn
384981f777cSalnsn /* tmp1 = buf[k+1]; */
385981f777cSalnsn status = sljit_emit_op1(compiler,
38644dbc048Salnsn SLJIT_MOV_U8,
387981f777cSalnsn BJ_TMP1REG, 0,
388981f777cSalnsn SLJIT_MEM1(src), k+1);
389981f777cSalnsn if (status != SLJIT_SUCCESS)
390981f777cSalnsn return status;
391981f777cSalnsn
392981f777cSalnsn /* A = A << 8; */
393e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
394e8c0d6c6Salnsn SLJIT_SHL,
395981f777cSalnsn BJ_AREG, 0,
396981f777cSalnsn BJ_AREG, 0,
397e8c0d6c6Salnsn SLJIT_IMM, 8);
398e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
399e8c0d6c6Salnsn return status;
400e8c0d6c6Salnsn
401e8c0d6c6Salnsn /* A = A + tmp1; */
402e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
403e8c0d6c6Salnsn SLJIT_ADD,
40498f8ebfbSalnsn BJ_AREG, 0,
40598f8ebfbSalnsn BJ_AREG, 0,
40698f8ebfbSalnsn BJ_TMP1REG, 0);
407e8c0d6c6Salnsn return status;
408e8c0d6c6Salnsn }
409e8c0d6c6Salnsn
410e8c0d6c6Salnsn /*
411d35700ddSalnsn * Emit code for BPF_LD+BPF_W+BPF_ABS A <- P[k:4].
412e8c0d6c6Salnsn */
413e8c0d6c6Salnsn static int
emit_read32(struct sljit_compiler * compiler,sljit_s32 src,uint32_t k)41444dbc048Salnsn emit_read32(struct sljit_compiler *compiler, sljit_s32 src, uint32_t k)
415e8c0d6c6Salnsn {
416e8c0d6c6Salnsn int status;
417e8c0d6c6Salnsn
418981f777cSalnsn BJ_ASSERT(k <= UINT32_MAX - 3);
419e8c0d6c6Salnsn
420981f777cSalnsn /* A = buf[k]; */
421e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
42244dbc048Salnsn SLJIT_MOV_U8,
42398f8ebfbSalnsn BJ_AREG, 0,
424981f777cSalnsn SLJIT_MEM1(src), k);
425e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
426e8c0d6c6Salnsn return status;
427e8c0d6c6Salnsn
428981f777cSalnsn /* tmp1 = buf[k+1]; */
429981f777cSalnsn status = sljit_emit_op1(compiler,
43044dbc048Salnsn SLJIT_MOV_U8,
431981f777cSalnsn BJ_TMP1REG, 0,
432981f777cSalnsn SLJIT_MEM1(src), k+1);
433981f777cSalnsn if (status != SLJIT_SUCCESS)
434981f777cSalnsn return status;
435981f777cSalnsn
436981f777cSalnsn /* A = A << 8; */
437e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
438e8c0d6c6Salnsn SLJIT_SHL,
439981f777cSalnsn BJ_AREG, 0,
440981f777cSalnsn BJ_AREG, 0,
441981f777cSalnsn SLJIT_IMM, 8);
442e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
443e8c0d6c6Salnsn return status;
444e8c0d6c6Salnsn
445e8c0d6c6Salnsn /* A = A + tmp1; */
446e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
447e8c0d6c6Salnsn SLJIT_ADD,
44898f8ebfbSalnsn BJ_AREG, 0,
44998f8ebfbSalnsn BJ_AREG, 0,
45098f8ebfbSalnsn BJ_TMP1REG, 0);
451e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
452e8c0d6c6Salnsn return status;
453e8c0d6c6Salnsn
454e8c0d6c6Salnsn /* tmp1 = buf[k+2]; */
455e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
45644dbc048Salnsn SLJIT_MOV_U8,
45798f8ebfbSalnsn BJ_TMP1REG, 0,
458981f777cSalnsn SLJIT_MEM1(src), k+2);
459e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
460e8c0d6c6Salnsn return status;
461e8c0d6c6Salnsn
462981f777cSalnsn /* A = A << 8; */
463e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
464e8c0d6c6Salnsn SLJIT_SHL,
465981f777cSalnsn BJ_AREG, 0,
466981f777cSalnsn BJ_AREG, 0,
467981f777cSalnsn SLJIT_IMM, 8);
468e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
469e8c0d6c6Salnsn return status;
470e8c0d6c6Salnsn
471981f777cSalnsn /* A = A + tmp1; */
472e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
473e8c0d6c6Salnsn SLJIT_ADD,
47498f8ebfbSalnsn BJ_AREG, 0,
47598f8ebfbSalnsn BJ_AREG, 0,
476981f777cSalnsn BJ_TMP1REG, 0);
477e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
478e8c0d6c6Salnsn return status;
479e8c0d6c6Salnsn
480981f777cSalnsn /* tmp1 = buf[k+3]; */
481981f777cSalnsn status = sljit_emit_op1(compiler,
48244dbc048Salnsn SLJIT_MOV_U8,
483981f777cSalnsn BJ_TMP1REG, 0,
484981f777cSalnsn SLJIT_MEM1(src), k+3);
485981f777cSalnsn if (status != SLJIT_SUCCESS)
486981f777cSalnsn return status;
487981f777cSalnsn
488981f777cSalnsn /* A = A << 8; */
489e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
490e8c0d6c6Salnsn SLJIT_SHL,
491981f777cSalnsn BJ_AREG, 0,
492981f777cSalnsn BJ_AREG, 0,
493e8c0d6c6Salnsn SLJIT_IMM, 8);
494e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
495e8c0d6c6Salnsn return status;
496e8c0d6c6Salnsn
497e8c0d6c6Salnsn /* A = A + tmp1; */
498e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
499e8c0d6c6Salnsn SLJIT_ADD,
50098f8ebfbSalnsn BJ_AREG, 0,
50198f8ebfbSalnsn BJ_AREG, 0,
50298f8ebfbSalnsn BJ_TMP1REG, 0);
503e8c0d6c6Salnsn return status;
504e8c0d6c6Salnsn }
505e8c0d6c6Salnsn
506e8c0d6c6Salnsn #ifdef _KERNEL
507e8c0d6c6Salnsn /*
508d35700ddSalnsn * Emit code for m_xword/m_xhalf/m_xbyte call.
509e8c0d6c6Salnsn *
510d35700ddSalnsn * @pc BPF_LD+BPF_W+BPF_ABS A <- P[k:4]
511e8c0d6c6Salnsn * BPF_LD+BPF_H+BPF_ABS A <- P[k:2]
512e8c0d6c6Salnsn * BPF_LD+BPF_B+BPF_ABS A <- P[k:1]
513e8c0d6c6Salnsn * BPF_LD+BPF_W+BPF_IND A <- P[X+k:4]
514e8c0d6c6Salnsn * BPF_LD+BPF_H+BPF_IND A <- P[X+k:2]
515e8c0d6c6Salnsn * BPF_LD+BPF_B+BPF_IND A <- P[X+k:1]
516e8c0d6c6Salnsn * BPF_LDX+BPF_B+BPF_MSH X <- 4*(P[k:1]&0xf)
517e8c0d6c6Salnsn */
518e8c0d6c6Salnsn static int
emit_xcall(struct sljit_compiler * compiler,bpfjit_hint_t hints,const struct bpf_insn * pc,int dst,struct sljit_jump *** ret0,size_t * ret0_size,size_t * ret0_maxsize,uint32_t (* fn)(const struct mbuf *,uint32_t,int *))51980a29916Salnsn emit_xcall(struct sljit_compiler *compiler, bpfjit_hint_t hints,
52080a29916Salnsn const struct bpf_insn *pc, int dst, struct sljit_jump ***ret0,
52180a29916Salnsn size_t *ret0_size, size_t *ret0_maxsize,
522e8c0d6c6Salnsn uint32_t (*fn)(const struct mbuf *, uint32_t, int *))
523e8c0d6c6Salnsn {
52480a29916Salnsn #if BJ_XREG == SLJIT_RETURN_REG || \
52544dbc048Salnsn BJ_XREG == SLJIT_R0 || \
52644dbc048Salnsn BJ_XREG == SLJIT_R1 || \
52744dbc048Salnsn BJ_XREG == SLJIT_R2
52880a29916Salnsn #error "Not supported assignment of registers."
52980a29916Salnsn #endif
530b81423b7Salnsn struct sljit_jump *jump;
53144dbc048Salnsn sljit_s32 save_reg;
532e8c0d6c6Salnsn int status;
533e8c0d6c6Salnsn
53480a29916Salnsn save_reg = (BPF_CLASS(pc->code) == BPF_LDX) ? BJ_AREG : BJ_XREG;
535b81423b7Salnsn
53680a29916Salnsn if (save_reg == BJ_AREG || (hints & BJ_HINT_XREG)) {
53780a29916Salnsn /* save A or X */
538e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
53944dbc048Salnsn SLJIT_MOV_U32,
54044dbc048Salnsn SLJIT_MEM1(SLJIT_SP),
54180a29916Salnsn offsetof(struct bpfjit_stack, reg),
54280a29916Salnsn save_reg, 0);
543e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
544e8c0d6c6Salnsn return status;
545e8c0d6c6Salnsn }
546e8c0d6c6Salnsn
547e8c0d6c6Salnsn /*
548b81423b7Salnsn * Prepare registers for fn(mbuf, k, &err) call.
549e8c0d6c6Salnsn */
550e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
551e8c0d6c6Salnsn SLJIT_MOV,
55244dbc048Salnsn SLJIT_R0, 0,
55398f8ebfbSalnsn BJ_BUF, 0);
554e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
555e8c0d6c6Salnsn return status;
556e8c0d6c6Salnsn
557e8c0d6c6Salnsn if (BPF_CLASS(pc->code) == BPF_LD && BPF_MODE(pc->code) == BPF_IND) {
5589f32be6cSalnsn if (pc->k == 0) {
5599f32be6cSalnsn /* k = X; */
5609f32be6cSalnsn status = sljit_emit_op1(compiler,
5619f32be6cSalnsn SLJIT_MOV,
56244dbc048Salnsn SLJIT_R1, 0,
5639f32be6cSalnsn BJ_XREG, 0);
5649f32be6cSalnsn if (status != SLJIT_SUCCESS)
5659f32be6cSalnsn return status;
5669f32be6cSalnsn } else {
5674c9cf960Salnsn /* if (X > UINT32_MAX - pc->k) return 0; */
5684c9cf960Salnsn jump = sljit_emit_cmp(compiler,
56944dbc048Salnsn SLJIT_GREATER,
5704c9cf960Salnsn BJ_XREG, 0,
5714c9cf960Salnsn SLJIT_IMM, UINT32_MAX - pc->k);
5724c9cf960Salnsn if (jump == NULL)
5734c9cf960Salnsn return SLJIT_ERR_ALLOC_FAILED;
5744c9cf960Salnsn if (!append_jump(jump, ret0, ret0_size, ret0_maxsize))
5754c9cf960Salnsn return SLJIT_ERR_ALLOC_FAILED;
5764c9cf960Salnsn
577b81423b7Salnsn /* k = X + pc->k; */
578e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
5794c9cf960Salnsn SLJIT_ADD,
58044dbc048Salnsn SLJIT_R1, 0,
58198f8ebfbSalnsn BJ_XREG, 0,
582e8c0d6c6Salnsn SLJIT_IMM, (uint32_t)pc->k);
583d35700ddSalnsn if (status != SLJIT_SUCCESS)
584d35700ddSalnsn return status;
5859f32be6cSalnsn }
586e8c0d6c6Salnsn } else {
587b81423b7Salnsn /* k = pc->k */
588e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
589e8c0d6c6Salnsn SLJIT_MOV,
59044dbc048Salnsn SLJIT_R1, 0,
591e8c0d6c6Salnsn SLJIT_IMM, (uint32_t)pc->k);
592e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
593e8c0d6c6Salnsn return status;
594d35700ddSalnsn }
595e8c0d6c6Salnsn
596b7e8e67dSalnsn /*
597b7e8e67dSalnsn * The third argument of fn is an address on stack.
598b7e8e67dSalnsn */
599e8c0d6c6Salnsn status = sljit_get_local_base(compiler,
60044dbc048Salnsn SLJIT_R2, 0,
601b7e8e67dSalnsn offsetof(struct bpfjit_stack, err));
602e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
603e8c0d6c6Salnsn return status;
604e8c0d6c6Salnsn
605e8c0d6c6Salnsn /* fn(buf, k, &err); */
606e8c0d6c6Salnsn status = sljit_emit_ijump(compiler,
607e8c0d6c6Salnsn SLJIT_CALL3,
608e8c0d6c6Salnsn SLJIT_IMM, SLJIT_FUNC_OFFSET(fn));
609d35700ddSalnsn if (status != SLJIT_SUCCESS)
610d35700ddSalnsn return status;
611e8c0d6c6Salnsn
61298f8ebfbSalnsn if (dst != SLJIT_RETURN_REG) {
613e8c0d6c6Salnsn /* move return value to dst */
614e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
615e8c0d6c6Salnsn SLJIT_MOV,
616b81423b7Salnsn dst, 0,
617e8c0d6c6Salnsn SLJIT_RETURN_REG, 0);
618e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
619e8c0d6c6Salnsn return status;
62098f8ebfbSalnsn }
621e8c0d6c6Salnsn
6225e176a43Salnsn /* if (*err != 0) return 0; */
623b81423b7Salnsn jump = sljit_emit_cmp(compiler,
62444dbc048Salnsn SLJIT_NOT_EQUAL|SLJIT_I32_OP,
62544dbc048Salnsn SLJIT_MEM1(SLJIT_SP),
6265e176a43Salnsn offsetof(struct bpfjit_stack, err),
627e8c0d6c6Salnsn SLJIT_IMM, 0);
628b81423b7Salnsn if (jump == NULL)
629b81423b7Salnsn return SLJIT_ERR_ALLOC_FAILED;
630b81423b7Salnsn
631b81423b7Salnsn if (!append_jump(jump, ret0, ret0_size, ret0_maxsize))
632e8c0d6c6Salnsn return SLJIT_ERR_ALLOC_FAILED;
633e8c0d6c6Salnsn
63480a29916Salnsn if (save_reg == BJ_AREG || (hints & BJ_HINT_XREG)) {
63580a29916Salnsn /* restore A or X */
6364c9cf960Salnsn status = sljit_emit_op1(compiler,
63744dbc048Salnsn SLJIT_MOV_U32,
63880a29916Salnsn save_reg, 0,
63944dbc048Salnsn SLJIT_MEM1(SLJIT_SP),
64080a29916Salnsn offsetof(struct bpfjit_stack, reg));
6414c9cf960Salnsn if (status != SLJIT_SUCCESS)
6424c9cf960Salnsn return status;
6434c9cf960Salnsn }
6444c9cf960Salnsn
645d35700ddSalnsn return SLJIT_SUCCESS;
646e8c0d6c6Salnsn }
647e8c0d6c6Salnsn #endif
648e8c0d6c6Salnsn
649e8c0d6c6Salnsn /*
65019fed70dSalnsn * Emit code for BPF_COP and BPF_COPX instructions.
65119fed70dSalnsn */
65219fed70dSalnsn static int
emit_cop(struct sljit_compiler * compiler,bpfjit_hint_t hints,const bpf_ctx_t * bc,const struct bpf_insn * pc,struct sljit_jump *** ret0,size_t * ret0_size,size_t * ret0_maxsize)65380a29916Salnsn emit_cop(struct sljit_compiler *compiler, bpfjit_hint_t hints,
6547d10db23Salnsn const bpf_ctx_t *bc, const struct bpf_insn *pc,
6557d10db23Salnsn struct sljit_jump ***ret0, size_t *ret0_size, size_t *ret0_maxsize)
65619fed70dSalnsn {
65780a29916Salnsn #if BJ_XREG == SLJIT_RETURN_REG || \
65844dbc048Salnsn BJ_XREG == SLJIT_R0 || \
65944dbc048Salnsn BJ_XREG == SLJIT_R1 || \
66044dbc048Salnsn BJ_XREG == SLJIT_R2 || \
66144dbc048Salnsn BJ_TMP3REG == SLJIT_R0 || \
66244dbc048Salnsn BJ_TMP3REG == SLJIT_R1 || \
66344dbc048Salnsn BJ_TMP3REG == SLJIT_R2
66419fed70dSalnsn #error "Not supported assignment of registers."
66519fed70dSalnsn #endif
66619fed70dSalnsn
66719fed70dSalnsn struct sljit_jump *jump;
66844dbc048Salnsn sljit_s32 call_reg;
6697d10db23Salnsn sljit_sw call_off;
67019fed70dSalnsn int status;
67119fed70dSalnsn
67219fed70dSalnsn BJ_ASSERT(bc != NULL && bc->copfuncs != NULL);
67319fed70dSalnsn
67480a29916Salnsn if (hints & BJ_HINT_LDX) {
67580a29916Salnsn /* save X */
67680a29916Salnsn status = sljit_emit_op1(compiler,
67744dbc048Salnsn SLJIT_MOV_U32,
67844dbc048Salnsn SLJIT_MEM1(SLJIT_SP),
67980a29916Salnsn offsetof(struct bpfjit_stack, reg),
68080a29916Salnsn BJ_XREG, 0);
68180a29916Salnsn if (status != SLJIT_SUCCESS)
68280a29916Salnsn return status;
68380a29916Salnsn }
68480a29916Salnsn
6857d10db23Salnsn if (BPF_MISCOP(pc->code) == BPF_COP) {
6867d10db23Salnsn call_reg = SLJIT_IMM;
6877d10db23Salnsn call_off = SLJIT_FUNC_OFFSET(bc->copfuncs[pc->k]);
6887d10db23Salnsn } else {
68919fed70dSalnsn /* if (X >= bc->nfuncs) return 0; */
69019fed70dSalnsn jump = sljit_emit_cmp(compiler,
69144dbc048Salnsn SLJIT_GREATER_EQUAL,
69219fed70dSalnsn BJ_XREG, 0,
69319fed70dSalnsn SLJIT_IMM, bc->nfuncs);
69419fed70dSalnsn if (jump == NULL)
69519fed70dSalnsn return SLJIT_ERR_ALLOC_FAILED;
6967d10db23Salnsn if (!append_jump(jump, ret0, ret0_size, ret0_maxsize))
6977d10db23Salnsn return SLJIT_ERR_ALLOC_FAILED;
69819fed70dSalnsn
6997d10db23Salnsn /* tmp1 = ctx; */
7007d10db23Salnsn status = sljit_emit_op1(compiler,
7017d10db23Salnsn SLJIT_MOV_P,
7027d10db23Salnsn BJ_TMP1REG, 0,
70344dbc048Salnsn SLJIT_MEM1(SLJIT_SP),
7047d10db23Salnsn offsetof(struct bpfjit_stack, ctx));
7057d10db23Salnsn if (status != SLJIT_SUCCESS)
7067d10db23Salnsn return status;
7077d10db23Salnsn
7087d10db23Salnsn /* tmp1 = ctx->copfuncs; */
7097d10db23Salnsn status = sljit_emit_op1(compiler,
7107d10db23Salnsn SLJIT_MOV_P,
7117d10db23Salnsn BJ_TMP1REG, 0,
7127d10db23Salnsn SLJIT_MEM1(BJ_TMP1REG),
7137d10db23Salnsn offsetof(struct bpf_ctx, copfuncs));
7147d10db23Salnsn if (status != SLJIT_SUCCESS)
7157d10db23Salnsn return status;
7167d10db23Salnsn
7177d10db23Salnsn /* tmp2 = X; */
7187d10db23Salnsn status = sljit_emit_op1(compiler,
7197d10db23Salnsn SLJIT_MOV,
7207d10db23Salnsn BJ_TMP2REG, 0,
7217d10db23Salnsn BJ_XREG, 0);
7227d10db23Salnsn if (status != SLJIT_SUCCESS)
7237d10db23Salnsn return status;
7247d10db23Salnsn
7257d10db23Salnsn /* tmp3 = ctx->copfuncs[tmp2]; */
7267d10db23Salnsn call_reg = BJ_TMP3REG;
7277d10db23Salnsn call_off = 0;
7287d10db23Salnsn status = sljit_emit_op1(compiler,
7297d10db23Salnsn SLJIT_MOV_P,
7307d10db23Salnsn call_reg, call_off,
7317d10db23Salnsn SLJIT_MEM2(BJ_TMP1REG, BJ_TMP2REG),
7327d10db23Salnsn SLJIT_WORD_SHIFT);
7337d10db23Salnsn if (status != SLJIT_SUCCESS)
7347d10db23Salnsn return status;
7357d10db23Salnsn }
73619fed70dSalnsn
73719fed70dSalnsn /*
73819fed70dSalnsn * Copy bpf_copfunc_t arguments to registers.
73919fed70dSalnsn */
74044dbc048Salnsn #if BJ_AREG != SLJIT_R2
74119fed70dSalnsn status = sljit_emit_op1(compiler,
74244dbc048Salnsn SLJIT_MOV_U32,
74344dbc048Salnsn SLJIT_R2, 0,
74419fed70dSalnsn BJ_AREG, 0);
74519fed70dSalnsn if (status != SLJIT_SUCCESS)
74619fed70dSalnsn return status;
74719fed70dSalnsn #endif
74819fed70dSalnsn
74919fed70dSalnsn status = sljit_emit_op1(compiler,
75019fed70dSalnsn SLJIT_MOV_P,
75144dbc048Salnsn SLJIT_R0, 0,
75244dbc048Salnsn SLJIT_MEM1(SLJIT_SP),
75319fed70dSalnsn offsetof(struct bpfjit_stack, ctx));
75419fed70dSalnsn if (status != SLJIT_SUCCESS)
75519fed70dSalnsn return status;
75619fed70dSalnsn
75719fed70dSalnsn status = sljit_emit_op1(compiler,
75819fed70dSalnsn SLJIT_MOV_P,
75944dbc048Salnsn SLJIT_R1, 0,
76019fed70dSalnsn BJ_ARGS, 0);
76119fed70dSalnsn if (status != SLJIT_SUCCESS)
76219fed70dSalnsn return status;
76319fed70dSalnsn
76419fed70dSalnsn status = sljit_emit_ijump(compiler,
7657d10db23Salnsn SLJIT_CALL3, call_reg, call_off);
76619fed70dSalnsn if (status != SLJIT_SUCCESS)
76719fed70dSalnsn return status;
76819fed70dSalnsn
76919fed70dSalnsn #if BJ_AREG != SLJIT_RETURN_REG
77019fed70dSalnsn status = sljit_emit_op1(compiler,
77119fed70dSalnsn SLJIT_MOV,
77219fed70dSalnsn BJ_AREG, 0,
77319fed70dSalnsn SLJIT_RETURN_REG, 0);
77419fed70dSalnsn if (status != SLJIT_SUCCESS)
77519fed70dSalnsn return status;
77619fed70dSalnsn #endif
77719fed70dSalnsn
77880a29916Salnsn if (hints & BJ_HINT_LDX) {
77980a29916Salnsn /* restore X */
78080a29916Salnsn status = sljit_emit_op1(compiler,
78144dbc048Salnsn SLJIT_MOV_U32,
78280a29916Salnsn BJ_XREG, 0,
78344dbc048Salnsn SLJIT_MEM1(SLJIT_SP),
78480a29916Salnsn offsetof(struct bpfjit_stack, reg));
78580a29916Salnsn if (status != SLJIT_SUCCESS)
78680a29916Salnsn return status;
78780a29916Salnsn }
78880a29916Salnsn
789d35700ddSalnsn return SLJIT_SUCCESS;
79019fed70dSalnsn }
79119fed70dSalnsn
79219fed70dSalnsn /*
793e8c0d6c6Salnsn * Generate code for
794e8c0d6c6Salnsn * BPF_LD+BPF_W+BPF_ABS A <- P[k:4]
795e8c0d6c6Salnsn * BPF_LD+BPF_H+BPF_ABS A <- P[k:2]
796e8c0d6c6Salnsn * BPF_LD+BPF_B+BPF_ABS A <- P[k:1]
797e8c0d6c6Salnsn * BPF_LD+BPF_W+BPF_IND A <- P[X+k:4]
798e8c0d6c6Salnsn * BPF_LD+BPF_H+BPF_IND A <- P[X+k:2]
799e8c0d6c6Salnsn * BPF_LD+BPF_B+BPF_IND A <- P[X+k:1]
800e8c0d6c6Salnsn */
801e8c0d6c6Salnsn static int
emit_pkt_read(struct sljit_compiler * compiler,bpfjit_hint_t hints,const struct bpf_insn * pc,struct sljit_jump * to_mchain_jump,struct sljit_jump *** ret0,size_t * ret0_size,size_t * ret0_maxsize)80280a29916Salnsn emit_pkt_read(struct sljit_compiler *compiler, bpfjit_hint_t hints,
80398f8ebfbSalnsn const struct bpf_insn *pc, struct sljit_jump *to_mchain_jump,
80498f8ebfbSalnsn struct sljit_jump ***ret0, size_t *ret0_size, size_t *ret0_maxsize)
805e8c0d6c6Salnsn {
806ea84cea2Salnsn int status = SLJIT_ERR_ALLOC_FAILED;
807e8c0d6c6Salnsn uint32_t width;
80844dbc048Salnsn sljit_s32 ld_reg;
809e8c0d6c6Salnsn struct sljit_jump *jump;
810e8c0d6c6Salnsn #ifdef _KERNEL
811e8c0d6c6Salnsn struct sljit_label *label;
812e8c0d6c6Salnsn struct sljit_jump *over_mchain_jump;
813e8c0d6c6Salnsn const bool check_zero_buflen = (to_mchain_jump != NULL);
814e8c0d6c6Salnsn #endif
815e8c0d6c6Salnsn const uint32_t k = pc->k;
816e8c0d6c6Salnsn
817e8c0d6c6Salnsn #ifdef _KERNEL
818e8c0d6c6Salnsn if (to_mchain_jump == NULL) {
819e8c0d6c6Salnsn to_mchain_jump = sljit_emit_cmp(compiler,
82044dbc048Salnsn SLJIT_EQUAL,
82198f8ebfbSalnsn BJ_BUFLEN, 0,
822e8c0d6c6Salnsn SLJIT_IMM, 0);
823e8c0d6c6Salnsn if (to_mchain_jump == NULL)
824e8c0d6c6Salnsn return SLJIT_ERR_ALLOC_FAILED;
825e8c0d6c6Salnsn }
826e8c0d6c6Salnsn #endif
827e8c0d6c6Salnsn
828981f777cSalnsn ld_reg = BJ_BUF;
829e8c0d6c6Salnsn width = read_width(pc);
830f34037edSalnsn if (width == 0)
831f34037edSalnsn return SLJIT_ERR_ALLOC_FAILED;
832e8c0d6c6Salnsn
833e8c0d6c6Salnsn if (BPF_MODE(pc->code) == BPF_IND) {
834e8c0d6c6Salnsn /* tmp1 = buflen - (pc->k + width); */
835e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
836e8c0d6c6Salnsn SLJIT_SUB,
83798f8ebfbSalnsn BJ_TMP1REG, 0,
83898f8ebfbSalnsn BJ_BUFLEN, 0,
839e8c0d6c6Salnsn SLJIT_IMM, k + width);
840e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
841e8c0d6c6Salnsn return status;
842e8c0d6c6Salnsn
843981f777cSalnsn /* ld_reg = buf + X; */
844981f777cSalnsn ld_reg = BJ_TMP2REG;
845e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
846e8c0d6c6Salnsn SLJIT_ADD,
847981f777cSalnsn ld_reg, 0,
84898f8ebfbSalnsn BJ_BUF, 0,
84998f8ebfbSalnsn BJ_XREG, 0);
850e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
851e8c0d6c6Salnsn return status;
852e8c0d6c6Salnsn
853e8c0d6c6Salnsn /* if (tmp1 < X) return 0; */
854e8c0d6c6Salnsn jump = sljit_emit_cmp(compiler,
85544dbc048Salnsn SLJIT_LESS,
85698f8ebfbSalnsn BJ_TMP1REG, 0,
85798f8ebfbSalnsn BJ_XREG, 0);
858e8c0d6c6Salnsn if (jump == NULL)
859e8c0d6c6Salnsn return SLJIT_ERR_ALLOC_FAILED;
86098f8ebfbSalnsn if (!append_jump(jump, ret0, ret0_size, ret0_maxsize))
86198f8ebfbSalnsn return SLJIT_ERR_ALLOC_FAILED;
862e8c0d6c6Salnsn }
863e8c0d6c6Salnsn
8649c78c570Salnsn /*
8659c78c570Salnsn * Don't emit wrapped-around reads. They're dead code but
8669c78c570Salnsn * dead code elimination logic isn't smart enough to figure
8679c78c570Salnsn * it out.
8689c78c570Salnsn */
8699c78c570Salnsn if (k <= UINT32_MAX - width + 1) {
870e8c0d6c6Salnsn switch (width) {
871e8c0d6c6Salnsn case 4:
872981f777cSalnsn status = emit_read32(compiler, ld_reg, k);
873e8c0d6c6Salnsn break;
874e8c0d6c6Salnsn case 2:
875981f777cSalnsn status = emit_read16(compiler, ld_reg, k);
876e8c0d6c6Salnsn break;
877e8c0d6c6Salnsn case 1:
878981f777cSalnsn status = emit_read8(compiler, ld_reg, k);
879e8c0d6c6Salnsn break;
880e8c0d6c6Salnsn }
881e8c0d6c6Salnsn
882e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
883e8c0d6c6Salnsn return status;
8849c78c570Salnsn }
885e8c0d6c6Salnsn
886e8c0d6c6Salnsn #ifdef _KERNEL
887e8c0d6c6Salnsn over_mchain_jump = sljit_emit_jump(compiler, SLJIT_JUMP);
888e8c0d6c6Salnsn if (over_mchain_jump == NULL)
889e8c0d6c6Salnsn return SLJIT_ERR_ALLOC_FAILED;
890e8c0d6c6Salnsn
891e8c0d6c6Salnsn /* entry point to mchain handler */
892e8c0d6c6Salnsn label = sljit_emit_label(compiler);
893e8c0d6c6Salnsn if (label == NULL)
894e8c0d6c6Salnsn return SLJIT_ERR_ALLOC_FAILED;
895e8c0d6c6Salnsn sljit_set_label(to_mchain_jump, label);
896e8c0d6c6Salnsn
897e8c0d6c6Salnsn if (check_zero_buflen) {
898e8c0d6c6Salnsn /* if (buflen != 0) return 0; */
899e8c0d6c6Salnsn jump = sljit_emit_cmp(compiler,
90044dbc048Salnsn SLJIT_NOT_EQUAL,
90198f8ebfbSalnsn BJ_BUFLEN, 0,
902e8c0d6c6Salnsn SLJIT_IMM, 0);
903e8c0d6c6Salnsn if (jump == NULL)
904e8c0d6c6Salnsn return SLJIT_ERR_ALLOC_FAILED;
90598f8ebfbSalnsn if (!append_jump(jump, ret0, ret0_size, ret0_maxsize))
90698f8ebfbSalnsn return SLJIT_ERR_ALLOC_FAILED;
907e8c0d6c6Salnsn }
908e8c0d6c6Salnsn
909e8c0d6c6Salnsn switch (width) {
910e8c0d6c6Salnsn case 4:
91180a29916Salnsn status = emit_xcall(compiler, hints, pc, BJ_AREG,
912b81423b7Salnsn ret0, ret0_size, ret0_maxsize, &m_xword);
913e8c0d6c6Salnsn break;
914e8c0d6c6Salnsn case 2:
91580a29916Salnsn status = emit_xcall(compiler, hints, pc, BJ_AREG,
916b81423b7Salnsn ret0, ret0_size, ret0_maxsize, &m_xhalf);
917e8c0d6c6Salnsn break;
918e8c0d6c6Salnsn case 1:
91980a29916Salnsn status = emit_xcall(compiler, hints, pc, BJ_AREG,
920b81423b7Salnsn ret0, ret0_size, ret0_maxsize, &m_xbyte);
921e8c0d6c6Salnsn break;
922e8c0d6c6Salnsn }
923e8c0d6c6Salnsn
924e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
925e8c0d6c6Salnsn return status;
926e8c0d6c6Salnsn
927e8c0d6c6Salnsn label = sljit_emit_label(compiler);
928e8c0d6c6Salnsn if (label == NULL)
929e8c0d6c6Salnsn return SLJIT_ERR_ALLOC_FAILED;
930e8c0d6c6Salnsn sljit_set_label(over_mchain_jump, label);
931e8c0d6c6Salnsn #endif
932e8c0d6c6Salnsn
933d35700ddSalnsn return SLJIT_SUCCESS;
934e8c0d6c6Salnsn }
935e8c0d6c6Salnsn
93619fed70dSalnsn static int
emit_memload(struct sljit_compiler * compiler,sljit_s32 dst,uint32_t k,size_t extwords)93719fed70dSalnsn emit_memload(struct sljit_compiler *compiler,
93844dbc048Salnsn sljit_s32 dst, uint32_t k, size_t extwords)
93919fed70dSalnsn {
94019fed70dSalnsn int status;
94144dbc048Salnsn sljit_s32 src;
94219fed70dSalnsn sljit_sw srcw;
94319fed70dSalnsn
94419fed70dSalnsn srcw = k * sizeof(uint32_t);
94519fed70dSalnsn
94619fed70dSalnsn if (extwords == 0) {
94744dbc048Salnsn src = SLJIT_MEM1(SLJIT_SP);
94819fed70dSalnsn srcw += offsetof(struct bpfjit_stack, mem);
94919fed70dSalnsn } else {
95019fed70dSalnsn /* copy extmem pointer to the tmp1 register */
95119fed70dSalnsn status = sljit_emit_op1(compiler,
952e8d0d0ecSalnsn SLJIT_MOV_P,
95319fed70dSalnsn BJ_TMP1REG, 0,
95444dbc048Salnsn SLJIT_MEM1(SLJIT_SP),
95519fed70dSalnsn offsetof(struct bpfjit_stack, extmem));
95619fed70dSalnsn if (status != SLJIT_SUCCESS)
95719fed70dSalnsn return status;
95819fed70dSalnsn src = SLJIT_MEM1(BJ_TMP1REG);
95919fed70dSalnsn }
96019fed70dSalnsn
96144dbc048Salnsn return sljit_emit_op1(compiler, SLJIT_MOV_U32, dst, 0, src, srcw);
96219fed70dSalnsn }
96319fed70dSalnsn
96419fed70dSalnsn static int
emit_memstore(struct sljit_compiler * compiler,sljit_s32 src,uint32_t k,size_t extwords)96519fed70dSalnsn emit_memstore(struct sljit_compiler *compiler,
96644dbc048Salnsn sljit_s32 src, uint32_t k, size_t extwords)
96719fed70dSalnsn {
96819fed70dSalnsn int status;
96944dbc048Salnsn sljit_s32 dst;
97019fed70dSalnsn sljit_sw dstw;
97119fed70dSalnsn
97219fed70dSalnsn dstw = k * sizeof(uint32_t);
97319fed70dSalnsn
97419fed70dSalnsn if (extwords == 0) {
97544dbc048Salnsn dst = SLJIT_MEM1(SLJIT_SP);
97619fed70dSalnsn dstw += offsetof(struct bpfjit_stack, mem);
97719fed70dSalnsn } else {
97819fed70dSalnsn /* copy extmem pointer to the tmp1 register */
97919fed70dSalnsn status = sljit_emit_op1(compiler,
980e8d0d0ecSalnsn SLJIT_MOV_P,
98119fed70dSalnsn BJ_TMP1REG, 0,
98244dbc048Salnsn SLJIT_MEM1(SLJIT_SP),
98319fed70dSalnsn offsetof(struct bpfjit_stack, extmem));
98419fed70dSalnsn if (status != SLJIT_SUCCESS)
98519fed70dSalnsn return status;
98619fed70dSalnsn dst = SLJIT_MEM1(BJ_TMP1REG);
98719fed70dSalnsn }
98819fed70dSalnsn
98944dbc048Salnsn return sljit_emit_op1(compiler, SLJIT_MOV_U32, dst, dstw, src, 0);
99019fed70dSalnsn }
99119fed70dSalnsn
992e8c0d6c6Salnsn /*
993d35700ddSalnsn * Emit code for BPF_LDX+BPF_B+BPF_MSH X <- 4*(P[k:1]&0xf).
994e8c0d6c6Salnsn */
995e8c0d6c6Salnsn static int
emit_msh(struct sljit_compiler * compiler,bpfjit_hint_t hints,const struct bpf_insn * pc,struct sljit_jump * to_mchain_jump,struct sljit_jump *** ret0,size_t * ret0_size,size_t * ret0_maxsize)99680a29916Salnsn emit_msh(struct sljit_compiler *compiler, bpfjit_hint_t hints,
99798f8ebfbSalnsn const struct bpf_insn *pc, struct sljit_jump *to_mchain_jump,
99898f8ebfbSalnsn struct sljit_jump ***ret0, size_t *ret0_size, size_t *ret0_maxsize)
999e8c0d6c6Salnsn {
1000e8c0d6c6Salnsn int status;
1001e8c0d6c6Salnsn #ifdef _KERNEL
1002e8c0d6c6Salnsn struct sljit_label *label;
1003e8c0d6c6Salnsn struct sljit_jump *jump, *over_mchain_jump;
1004e8c0d6c6Salnsn const bool check_zero_buflen = (to_mchain_jump != NULL);
1005e8c0d6c6Salnsn #endif
1006e8c0d6c6Salnsn const uint32_t k = pc->k;
1007e8c0d6c6Salnsn
1008e8c0d6c6Salnsn #ifdef _KERNEL
1009e8c0d6c6Salnsn if (to_mchain_jump == NULL) {
1010e8c0d6c6Salnsn to_mchain_jump = sljit_emit_cmp(compiler,
101144dbc048Salnsn SLJIT_EQUAL,
101298f8ebfbSalnsn BJ_BUFLEN, 0,
1013e8c0d6c6Salnsn SLJIT_IMM, 0);
1014e8c0d6c6Salnsn if (to_mchain_jump == NULL)
1015e8c0d6c6Salnsn return SLJIT_ERR_ALLOC_FAILED;
1016e8c0d6c6Salnsn }
1017e8c0d6c6Salnsn #endif
1018e8c0d6c6Salnsn
1019e8c0d6c6Salnsn /* tmp1 = buf[k] */
1020e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
102144dbc048Salnsn SLJIT_MOV_U8,
102298f8ebfbSalnsn BJ_TMP1REG, 0,
102398f8ebfbSalnsn SLJIT_MEM1(BJ_BUF), k);
1024e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1025e8c0d6c6Salnsn return status;
1026e8c0d6c6Salnsn
1027e8c0d6c6Salnsn #ifdef _KERNEL
1028e8c0d6c6Salnsn over_mchain_jump = sljit_emit_jump(compiler, SLJIT_JUMP);
1029e8c0d6c6Salnsn if (over_mchain_jump == NULL)
1030e8c0d6c6Salnsn return SLJIT_ERR_ALLOC_FAILED;
1031e8c0d6c6Salnsn
1032e8c0d6c6Salnsn /* entry point to mchain handler */
1033e8c0d6c6Salnsn label = sljit_emit_label(compiler);
1034e8c0d6c6Salnsn if (label == NULL)
1035e8c0d6c6Salnsn return SLJIT_ERR_ALLOC_FAILED;
1036e8c0d6c6Salnsn sljit_set_label(to_mchain_jump, label);
1037e8c0d6c6Salnsn
1038e8c0d6c6Salnsn if (check_zero_buflen) {
1039e8c0d6c6Salnsn /* if (buflen != 0) return 0; */
1040e8c0d6c6Salnsn jump = sljit_emit_cmp(compiler,
104144dbc048Salnsn SLJIT_NOT_EQUAL,
104298f8ebfbSalnsn BJ_BUFLEN, 0,
1043e8c0d6c6Salnsn SLJIT_IMM, 0);
1044e8c0d6c6Salnsn if (jump == NULL)
1045e8c0d6c6Salnsn return SLJIT_ERR_ALLOC_FAILED;
104698f8ebfbSalnsn if (!append_jump(jump, ret0, ret0_size, ret0_maxsize))
104798f8ebfbSalnsn return SLJIT_ERR_ALLOC_FAILED;
1048e8c0d6c6Salnsn }
1049e8c0d6c6Salnsn
105080a29916Salnsn status = emit_xcall(compiler, hints, pc, BJ_TMP1REG,
1051b81423b7Salnsn ret0, ret0_size, ret0_maxsize, &m_xbyte);
1052e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1053e8c0d6c6Salnsn return status;
105498f8ebfbSalnsn
10555e176a43Salnsn label = sljit_emit_label(compiler);
10565e176a43Salnsn if (label == NULL)
10575e176a43Salnsn return SLJIT_ERR_ALLOC_FAILED;
10585e176a43Salnsn sljit_set_label(over_mchain_jump, label);
10595e176a43Salnsn #endif
10605e176a43Salnsn
1061e8c0d6c6Salnsn /* tmp1 &= 0xf */
1062e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
1063e8c0d6c6Salnsn SLJIT_AND,
106498f8ebfbSalnsn BJ_TMP1REG, 0,
106598f8ebfbSalnsn BJ_TMP1REG, 0,
1066e8c0d6c6Salnsn SLJIT_IMM, 0xf);
1067e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1068e8c0d6c6Salnsn return status;
1069e8c0d6c6Salnsn
10705e176a43Salnsn /* X = tmp1 << 2 */
1071e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
1072e8c0d6c6Salnsn SLJIT_SHL,
107398f8ebfbSalnsn BJ_XREG, 0,
107498f8ebfbSalnsn BJ_TMP1REG, 0,
1075e8c0d6c6Salnsn SLJIT_IMM, 2);
1076e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1077e8c0d6c6Salnsn return status;
1078e8c0d6c6Salnsn
1079d35700ddSalnsn return SLJIT_SUCCESS;
1080e8c0d6c6Salnsn }
1081e8c0d6c6Salnsn
1082e7d58502Salnsn /*
1083e7d58502Salnsn * Emit code for A = A / k or A = A % k when k is a power of 2.
1084e7d58502Salnsn * @pc BPF_DIV or BPF_MOD instruction.
1085e7d58502Salnsn */
1086e8c0d6c6Salnsn static int
emit_pow2_moddiv(struct sljit_compiler * compiler,const struct bpf_insn * pc)1087e7d58502Salnsn emit_pow2_moddiv(struct sljit_compiler *compiler, const struct bpf_insn *pc)
1088e8c0d6c6Salnsn {
1089e7d58502Salnsn uint32_t k = pc->k;
1090e8c0d6c6Salnsn int status = SLJIT_SUCCESS;
1091e8c0d6c6Salnsn
1092e7d58502Salnsn BJ_ASSERT(k != 0 && (k & (k - 1)) == 0);
1093e7d58502Salnsn
1094e7d58502Salnsn if (BPF_OP(pc->code) == BPF_MOD) {
1095e7d58502Salnsn status = sljit_emit_op2(compiler,
1096e7d58502Salnsn SLJIT_AND,
1097e7d58502Salnsn BJ_AREG, 0,
1098e7d58502Salnsn BJ_AREG, 0,
1099e7d58502Salnsn SLJIT_IMM, k - 1);
1100e7d58502Salnsn } else {
1101e7d58502Salnsn int shift = 0;
1102e7d58502Salnsn
1103e7d58502Salnsn /*
1104e7d58502Salnsn * Do shift = __builtin_ctz(k).
1105e7d58502Salnsn * The loop is slower, but that's ok.
1106e7d58502Salnsn */
1107e8c0d6c6Salnsn while (k > 1) {
1108e8c0d6c6Salnsn k >>= 1;
1109e8c0d6c6Salnsn shift++;
1110e8c0d6c6Salnsn }
1111e8c0d6c6Salnsn
1112e8c0d6c6Salnsn if (shift != 0) {
1113e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
111444dbc048Salnsn SLJIT_LSHR|SLJIT_I32_OP,
111598f8ebfbSalnsn BJ_AREG, 0,
111698f8ebfbSalnsn BJ_AREG, 0,
1117e8c0d6c6Salnsn SLJIT_IMM, shift);
1118e8c0d6c6Salnsn }
1119e7d58502Salnsn }
1120e8c0d6c6Salnsn
1121e8c0d6c6Salnsn return status;
1122e8c0d6c6Salnsn }
1123e8c0d6c6Salnsn
1124e8c0d6c6Salnsn #if !defined(BPFJIT_USE_UDIV)
1125e8c0d6c6Salnsn static sljit_uw
divide(sljit_uw x,sljit_uw y)1126e8c0d6c6Salnsn divide(sljit_uw x, sljit_uw y)
1127e8c0d6c6Salnsn {
1128e8c0d6c6Salnsn
1129e8c0d6c6Salnsn return (uint32_t)x / (uint32_t)y;
1130e8c0d6c6Salnsn }
1131bca1938aSchristos
1132bca1938aSchristos static sljit_uw
modulus(sljit_uw x,sljit_uw y)1133bca1938aSchristos modulus(sljit_uw x, sljit_uw y)
1134bca1938aSchristos {
1135bca1938aSchristos
1136bca1938aSchristos return (uint32_t)x % (uint32_t)y;
1137bca1938aSchristos }
1138e8c0d6c6Salnsn #endif
1139e8c0d6c6Salnsn
1140e8c0d6c6Salnsn /*
1141e7d58502Salnsn * Emit code for A = A / div or A = A % div.
1142e7d58502Salnsn * @pc BPF_DIV or BPF_MOD instruction.
1143e8c0d6c6Salnsn */
1144e8c0d6c6Salnsn static int
emit_moddiv(struct sljit_compiler * compiler,const struct bpf_insn * pc)1145e7d58502Salnsn emit_moddiv(struct sljit_compiler *compiler, const struct bpf_insn *pc)
1146e8c0d6c6Salnsn {
1147e8c0d6c6Salnsn int status;
11483eceacffSchristos const bool xdiv = BPF_OP(pc->code) == BPF_DIV;
1149e7d58502Salnsn const bool xreg = BPF_SRC(pc->code) == BPF_X;
1150e8c0d6c6Salnsn
115180a29916Salnsn #if BJ_XREG == SLJIT_RETURN_REG || \
115244dbc048Salnsn BJ_XREG == SLJIT_R0 || \
115344dbc048Salnsn BJ_XREG == SLJIT_R1 || \
115444dbc048Salnsn BJ_AREG == SLJIT_R1
115580a29916Salnsn #error "Not supported assignment of registers."
115680a29916Salnsn #endif
115780a29916Salnsn
115844dbc048Salnsn #if BJ_AREG != SLJIT_R0
1159e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
1160e8c0d6c6Salnsn SLJIT_MOV,
116144dbc048Salnsn SLJIT_R0, 0,
116298f8ebfbSalnsn BJ_AREG, 0);
1163e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1164e8c0d6c6Salnsn return status;
1165e8c0d6c6Salnsn #endif
1166e8c0d6c6Salnsn
1167e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
1168e8c0d6c6Salnsn SLJIT_MOV,
116944dbc048Salnsn SLJIT_R1, 0,
1170e7d58502Salnsn xreg ? BJ_XREG : SLJIT_IMM,
1171e7d58502Salnsn xreg ? 0 : (uint32_t)pc->k);
1172e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1173e8c0d6c6Salnsn return status;
1174e8c0d6c6Salnsn
1175e8c0d6c6Salnsn #if defined(BPFJIT_USE_UDIV)
117644dbc048Salnsn status = sljit_emit_op0(compiler, SLJIT_UDIV|SLJIT_I32_OP);
1177e8c0d6c6Salnsn
11781bc241b4Salnsn if (BPF_OP(pc->code) == BPF_DIV) {
117944dbc048Salnsn #if BJ_AREG != SLJIT_R0
1180e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
1181e8c0d6c6Salnsn SLJIT_MOV,
118298f8ebfbSalnsn BJ_AREG, 0,
118344dbc048Salnsn SLJIT_R0, 0);
11841bc241b4Salnsn #endif
11851bc241b4Salnsn } else {
118644dbc048Salnsn #if BJ_AREG != SLJIT_R1
118744dbc048Salnsn /* Remainder is in SLJIT_R1. */
11881bc241b4Salnsn status = sljit_emit_op1(compiler,
11891bc241b4Salnsn SLJIT_MOV,
11901bc241b4Salnsn BJ_AREG, 0,
119144dbc048Salnsn SLJIT_R1, 0);
11921bc241b4Salnsn #endif
11931bc241b4Salnsn }
11941bc241b4Salnsn
1195e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1196e8c0d6c6Salnsn return status;
1197e8c0d6c6Salnsn #else
1198e8c0d6c6Salnsn status = sljit_emit_ijump(compiler,
1199e8c0d6c6Salnsn SLJIT_CALL2,
12003eceacffSchristos SLJIT_IMM, xdiv ? SLJIT_FUNC_OFFSET(divide) :
1201bca1938aSchristos SLJIT_FUNC_OFFSET(modulus));
1202e8c0d6c6Salnsn
120398f8ebfbSalnsn #if BJ_AREG != SLJIT_RETURN_REG
1204e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
1205e8c0d6c6Salnsn SLJIT_MOV,
120698f8ebfbSalnsn BJ_AREG, 0,
1207e8c0d6c6Salnsn SLJIT_RETURN_REG, 0);
1208e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1209e8c0d6c6Salnsn return status;
1210e8c0d6c6Salnsn #endif
1211e8c0d6c6Salnsn #endif
1212e8c0d6c6Salnsn
1213e8c0d6c6Salnsn return status;
1214e8c0d6c6Salnsn }
1215e8c0d6c6Salnsn
1216e8c0d6c6Salnsn /*
1217e8c0d6c6Salnsn * Return true if pc is a "read from packet" instruction.
1218e8c0d6c6Salnsn * If length is not NULL and return value is true, *length will
1219e8c0d6c6Salnsn * be set to a safe length required to read a packet.
1220e8c0d6c6Salnsn */
1221e8c0d6c6Salnsn static bool
read_pkt_insn(const struct bpf_insn * pc,bpfjit_abc_length_t * length)122274e580d2Salnsn read_pkt_insn(const struct bpf_insn *pc, bpfjit_abc_length_t *length)
1223e8c0d6c6Salnsn {
1224e8c0d6c6Salnsn bool rv;
1225a25ec945Sjustin bpfjit_abc_length_t width = 0; /* XXXuninit */
1226e8c0d6c6Salnsn
1227e8c0d6c6Salnsn switch (BPF_CLASS(pc->code)) {
1228e8c0d6c6Salnsn default:
1229e8c0d6c6Salnsn rv = false;
1230e8c0d6c6Salnsn break;
1231e8c0d6c6Salnsn
1232e8c0d6c6Salnsn case BPF_LD:
1233e8c0d6c6Salnsn rv = BPF_MODE(pc->code) == BPF_ABS ||
1234e8c0d6c6Salnsn BPF_MODE(pc->code) == BPF_IND;
1235f34037edSalnsn if (rv) {
1236e8c0d6c6Salnsn width = read_width(pc);
1237f34037edSalnsn rv = (width != 0);
1238f34037edSalnsn }
1239e8c0d6c6Salnsn break;
1240e8c0d6c6Salnsn
1241e8c0d6c6Salnsn case BPF_LDX:
1242f34037edSalnsn rv = BPF_MODE(pc->code) == BPF_MSH &&
1243f34037edSalnsn BPF_SIZE(pc->code) == BPF_B;
1244e8c0d6c6Salnsn width = 1;
1245e8c0d6c6Salnsn break;
1246e8c0d6c6Salnsn }
1247e8c0d6c6Salnsn
1248e8c0d6c6Salnsn if (rv && length != NULL) {
12491bf51582Salnsn /*
12501bf51582Salnsn * Values greater than UINT32_MAX will generate
12511bf51582Salnsn * unconditional "return 0".
12521bf51582Salnsn */
12531bf51582Salnsn *length = (uint32_t)pc->k + width;
1254e8c0d6c6Salnsn }
1255e8c0d6c6Salnsn
1256e8c0d6c6Salnsn return rv;
1257e8c0d6c6Salnsn }
1258e8c0d6c6Salnsn
1259e8c0d6c6Salnsn static void
optimize_init(struct bpfjit_insn_data * insn_dat,size_t insn_count)126098f8ebfbSalnsn optimize_init(struct bpfjit_insn_data *insn_dat, size_t insn_count)
1261e8c0d6c6Salnsn {
126298f8ebfbSalnsn size_t i;
1263e8c0d6c6Salnsn
126498f8ebfbSalnsn for (i = 0; i < insn_count; i++) {
126598f8ebfbSalnsn SLIST_INIT(&insn_dat[i].bjumps);
126698f8ebfbSalnsn insn_dat[i].invalid = BJ_INIT_NOBITS;
1267e8c0d6c6Salnsn }
1268e8c0d6c6Salnsn }
1269e8c0d6c6Salnsn
1270e8c0d6c6Salnsn /*
1271e8c0d6c6Salnsn * The function divides instructions into blocks. Destination of a jump
1272e8c0d6c6Salnsn * instruction starts a new block. BPF_RET and BPF_JMP instructions
1273e8c0d6c6Salnsn * terminate a block. Blocks are linear, that is, there are no jumps out
1274e8c0d6c6Salnsn * from the middle of a block and there are no jumps in to the middle of
1275e8c0d6c6Salnsn * a block.
127698f8ebfbSalnsn *
127798f8ebfbSalnsn * The function also sets bits in *initmask for memwords that
127898f8ebfbSalnsn * need to be initialized to zero. Note that this set should be empty
127998f8ebfbSalnsn * for any valid kernel filter program.
1280e8c0d6c6Salnsn */
128198f8ebfbSalnsn static bool
optimize_pass1(const bpf_ctx_t * bc,const struct bpf_insn * insns,struct bpfjit_insn_data * insn_dat,size_t insn_count,bpf_memword_init_t * initmask,bpfjit_hint_t * hints)1282c676220dSalnsn optimize_pass1(const bpf_ctx_t *bc, const struct bpf_insn *insns,
1283c676220dSalnsn struct bpfjit_insn_data *insn_dat, size_t insn_count,
128424d883a5Salnsn bpf_memword_init_t *initmask, bpfjit_hint_t *hints)
1285e8c0d6c6Salnsn {
128698f8ebfbSalnsn struct bpfjit_jump *jtf;
1287e8c0d6c6Salnsn size_t i;
1288e8c0d6c6Salnsn uint32_t jt, jf;
1289a9f87100Salnsn bpfjit_abc_length_t length;
129019fed70dSalnsn bpf_memword_init_t invalid; /* borrowed from bpf_filter() */
129198f8ebfbSalnsn bool unreachable;
1292e8c0d6c6Salnsn
1293c676220dSalnsn const size_t memwords = GET_MEMWORDS(bc);
129419fed70dSalnsn
129524d883a5Salnsn *hints = 0;
129698f8ebfbSalnsn *initmask = BJ_INIT_NOBITS;
1297e8c0d6c6Salnsn
1298e8c0d6c6Salnsn unreachable = false;
129998f8ebfbSalnsn invalid = ~BJ_INIT_NOBITS;
1300e8c0d6c6Salnsn
1301e8c0d6c6Salnsn for (i = 0; i < insn_count; i++) {
130298f8ebfbSalnsn if (!SLIST_EMPTY(&insn_dat[i].bjumps))
1303e8c0d6c6Salnsn unreachable = false;
130498f8ebfbSalnsn insn_dat[i].unreachable = unreachable;
1305e8c0d6c6Salnsn
1306e8c0d6c6Salnsn if (unreachable)
1307e8c0d6c6Salnsn continue;
1308e8c0d6c6Salnsn
130998f8ebfbSalnsn invalid |= insn_dat[i].invalid;
1310e8c0d6c6Salnsn
1311a9f87100Salnsn if (read_pkt_insn(&insns[i], &length) && length > UINT32_MAX)
1312a9f87100Salnsn unreachable = true;
1313a9f87100Salnsn
1314e8c0d6c6Salnsn switch (BPF_CLASS(insns[i].code)) {
1315e8c0d6c6Salnsn case BPF_RET:
131698f8ebfbSalnsn if (BPF_RVAL(insns[i].code) == BPF_A)
131798f8ebfbSalnsn *initmask |= invalid & BJ_INIT_ABIT;
131898f8ebfbSalnsn
1319e8c0d6c6Salnsn unreachable = true;
1320e8c0d6c6Salnsn continue;
1321e8c0d6c6Salnsn
132298f8ebfbSalnsn case BPF_LD:
1323981f777cSalnsn if (BPF_MODE(insns[i].code) == BPF_ABS)
1324981f777cSalnsn *hints |= BJ_HINT_ABS;
1325d35700ddSalnsn
132624d883a5Salnsn if (BPF_MODE(insns[i].code) == BPF_IND) {
1327981f777cSalnsn *hints |= BJ_HINT_IND | BJ_HINT_XREG;
132898f8ebfbSalnsn *initmask |= invalid & BJ_INIT_XBIT;
132924d883a5Salnsn }
133098f8ebfbSalnsn
133198f8ebfbSalnsn if (BPF_MODE(insns[i].code) == BPF_MEM &&
133219fed70dSalnsn (uint32_t)insns[i].k < memwords) {
133398f8ebfbSalnsn *initmask |= invalid & BJ_INIT_MBIT(insns[i].k);
133498f8ebfbSalnsn }
133598f8ebfbSalnsn
133698f8ebfbSalnsn invalid &= ~BJ_INIT_ABIT;
133798f8ebfbSalnsn continue;
133898f8ebfbSalnsn
133998f8ebfbSalnsn case BPF_LDX:
134024d883a5Salnsn *hints |= BJ_HINT_XREG | BJ_HINT_LDX;
134198f8ebfbSalnsn
134298f8ebfbSalnsn if (BPF_MODE(insns[i].code) == BPF_MEM &&
134319fed70dSalnsn (uint32_t)insns[i].k < memwords) {
134498f8ebfbSalnsn *initmask |= invalid & BJ_INIT_MBIT(insns[i].k);
134598f8ebfbSalnsn }
134698f8ebfbSalnsn
13476435b941Salnsn if (BPF_MODE(insns[i].code) == BPF_MSH &&
13486435b941Salnsn BPF_SIZE(insns[i].code) == BPF_B) {
13496435b941Salnsn *hints |= BJ_HINT_MSH;
13506435b941Salnsn }
13516435b941Salnsn
135298f8ebfbSalnsn invalid &= ~BJ_INIT_XBIT;
135398f8ebfbSalnsn continue;
135498f8ebfbSalnsn
135598f8ebfbSalnsn case BPF_ST:
135698f8ebfbSalnsn *initmask |= invalid & BJ_INIT_ABIT;
135798f8ebfbSalnsn
135819fed70dSalnsn if ((uint32_t)insns[i].k < memwords)
135998f8ebfbSalnsn invalid &= ~BJ_INIT_MBIT(insns[i].k);
136098f8ebfbSalnsn
136198f8ebfbSalnsn continue;
136298f8ebfbSalnsn
136398f8ebfbSalnsn case BPF_STX:
136424d883a5Salnsn *hints |= BJ_HINT_XREG;
136598f8ebfbSalnsn *initmask |= invalid & BJ_INIT_XBIT;
136698f8ebfbSalnsn
136719fed70dSalnsn if ((uint32_t)insns[i].k < memwords)
136898f8ebfbSalnsn invalid &= ~BJ_INIT_MBIT(insns[i].k);
136998f8ebfbSalnsn
137098f8ebfbSalnsn continue;
137198f8ebfbSalnsn
137298f8ebfbSalnsn case BPF_ALU:
137398f8ebfbSalnsn *initmask |= invalid & BJ_INIT_ABIT;
137498f8ebfbSalnsn
137598f8ebfbSalnsn if (insns[i].code != (BPF_ALU|BPF_NEG) &&
137698f8ebfbSalnsn BPF_SRC(insns[i].code) == BPF_X) {
137724d883a5Salnsn *hints |= BJ_HINT_XREG;
137898f8ebfbSalnsn *initmask |= invalid & BJ_INIT_XBIT;
137998f8ebfbSalnsn }
138098f8ebfbSalnsn
138198f8ebfbSalnsn invalid &= ~BJ_INIT_ABIT;
138298f8ebfbSalnsn continue;
138398f8ebfbSalnsn
138498f8ebfbSalnsn case BPF_MISC:
138598f8ebfbSalnsn switch (BPF_MISCOP(insns[i].code)) {
138698f8ebfbSalnsn case BPF_TAX: // X <- A
138724d883a5Salnsn *hints |= BJ_HINT_XREG;
138898f8ebfbSalnsn *initmask |= invalid & BJ_INIT_ABIT;
138998f8ebfbSalnsn invalid &= ~BJ_INIT_XBIT;
139098f8ebfbSalnsn continue;
139198f8ebfbSalnsn
139298f8ebfbSalnsn case BPF_TXA: // A <- X
139324d883a5Salnsn *hints |= BJ_HINT_XREG;
139498f8ebfbSalnsn *initmask |= invalid & BJ_INIT_XBIT;
139598f8ebfbSalnsn invalid &= ~BJ_INIT_ABIT;
139698f8ebfbSalnsn continue;
139719fed70dSalnsn
139819fed70dSalnsn case BPF_COPX:
13997d10db23Salnsn *hints |= BJ_HINT_XREG | BJ_HINT_COPX;
140019fed70dSalnsn /* FALLTHROUGH */
140119fed70dSalnsn
140219fed70dSalnsn case BPF_COP:
140324d883a5Salnsn *hints |= BJ_HINT_COP;
140419fed70dSalnsn *initmask |= invalid & BJ_INIT_ABIT;
140519fed70dSalnsn invalid &= ~BJ_INIT_ABIT;
140619fed70dSalnsn continue;
140798f8ebfbSalnsn }
140898f8ebfbSalnsn
140998f8ebfbSalnsn continue;
141098f8ebfbSalnsn
1411e8c0d6c6Salnsn case BPF_JMP:
141298f8ebfbSalnsn /* Initialize abc_length for ABC pass. */
141374e580d2Salnsn insn_dat[i].u.jdata.abc_length = MAX_ABC_LENGTH;
141498f8ebfbSalnsn
14157674f279Salnsn *initmask |= invalid & BJ_INIT_ABIT;
14167674f279Salnsn
14177674f279Salnsn if (BPF_SRC(insns[i].code) == BPF_X) {
1418f34037edSalnsn *hints |= BJ_HINT_XREG;
14197674f279Salnsn *initmask |= invalid & BJ_INIT_XBIT;
14207674f279Salnsn }
1421f34037edSalnsn
142298f8ebfbSalnsn if (BPF_OP(insns[i].code) == BPF_JA) {
1423e8c0d6c6Salnsn jt = jf = insns[i].k;
1424e8c0d6c6Salnsn } else {
1425e8c0d6c6Salnsn jt = insns[i].jt;
1426e8c0d6c6Salnsn jf = insns[i].jf;
1427e8c0d6c6Salnsn }
1428e8c0d6c6Salnsn
1429e8c0d6c6Salnsn if (jt >= insn_count - (i + 1) ||
1430e8c0d6c6Salnsn jf >= insn_count - (i + 1)) {
143198f8ebfbSalnsn return false;
1432e8c0d6c6Salnsn }
1433e8c0d6c6Salnsn
1434e8c0d6c6Salnsn if (jt > 0 && jf > 0)
1435e8c0d6c6Salnsn unreachable = true;
1436e8c0d6c6Salnsn
143798f8ebfbSalnsn jt += i + 1;
143898f8ebfbSalnsn jf += i + 1;
1439e8c0d6c6Salnsn
144098f8ebfbSalnsn jtf = insn_dat[i].u.jdata.jtf;
144198f8ebfbSalnsn
144298f8ebfbSalnsn jtf[0].jdata = &insn_dat[i].u.jdata;
144398f8ebfbSalnsn SLIST_INSERT_HEAD(&insn_dat[jt].bjumps,
144498f8ebfbSalnsn &jtf[0], entries);
1445e8c0d6c6Salnsn
1446e8c0d6c6Salnsn if (jf != jt) {
144798f8ebfbSalnsn jtf[1].jdata = &insn_dat[i].u.jdata;
144898f8ebfbSalnsn SLIST_INSERT_HEAD(&insn_dat[jf].bjumps,
144998f8ebfbSalnsn &jtf[1], entries);
1450e8c0d6c6Salnsn }
1451e8c0d6c6Salnsn
145298f8ebfbSalnsn insn_dat[jf].invalid |= invalid;
145398f8ebfbSalnsn insn_dat[jt].invalid |= invalid;
145498f8ebfbSalnsn invalid = 0;
145598f8ebfbSalnsn
1456e8c0d6c6Salnsn continue;
1457e8c0d6c6Salnsn }
1458e8c0d6c6Salnsn }
1459e8c0d6c6Salnsn
146098f8ebfbSalnsn return true;
1461e8c0d6c6Salnsn }
1462e8c0d6c6Salnsn
1463e8c0d6c6Salnsn /*
146498f8ebfbSalnsn * Array Bounds Check Elimination (ABC) pass.
1465e8c0d6c6Salnsn */
146698f8ebfbSalnsn static void
optimize_pass2(const bpf_ctx_t * bc,const struct bpf_insn * insns,struct bpfjit_insn_data * insn_dat,size_t insn_count)1467c676220dSalnsn optimize_pass2(const bpf_ctx_t *bc, const struct bpf_insn *insns,
1468c676220dSalnsn struct bpfjit_insn_data *insn_dat, size_t insn_count)
1469e8c0d6c6Salnsn {
147098f8ebfbSalnsn struct bpfjit_jump *jmp;
147198f8ebfbSalnsn const struct bpf_insn *pc;
147298f8ebfbSalnsn struct bpfjit_insn_data *pd;
1473e8c0d6c6Salnsn size_t i;
147474e580d2Salnsn bpfjit_abc_length_t length, abc_length = 0;
147598f8ebfbSalnsn
1476c676220dSalnsn const size_t extwords = GET_EXTWORDS(bc);
1477c676220dSalnsn
147898f8ebfbSalnsn for (i = insn_count; i != 0; i--) {
147998f8ebfbSalnsn pc = &insns[i-1];
148098f8ebfbSalnsn pd = &insn_dat[i-1];
148198f8ebfbSalnsn
148298f8ebfbSalnsn if (pd->unreachable)
148398f8ebfbSalnsn continue;
148498f8ebfbSalnsn
148598f8ebfbSalnsn switch (BPF_CLASS(pc->code)) {
148698f8ebfbSalnsn case BPF_RET:
148708d17fbeSalnsn /*
148808d17fbeSalnsn * It's quite common for bpf programs to
148908d17fbeSalnsn * check packet bytes in increasing order
149008d17fbeSalnsn * and return zero if bytes don't match
149108d17fbeSalnsn * specified critetion. Such programs disable
149208d17fbeSalnsn * ABC optimization completely because for
149308d17fbeSalnsn * every jump there is a branch with no read
149408d17fbeSalnsn * instruction.
149519fed70dSalnsn * With no side effects, BPF_STMT(BPF_RET+BPF_K, 0)
149619fed70dSalnsn * is indistinguishable from out-of-bound load.
149708d17fbeSalnsn * Therefore, abc_length can be set to
149808d17fbeSalnsn * MAX_ABC_LENGTH and enable ABC for many
149908d17fbeSalnsn * bpf programs.
150019fed70dSalnsn * If this optimization encounters any
150108d17fbeSalnsn * instruction with a side effect, it will
150208d17fbeSalnsn * reset abc_length.
150308d17fbeSalnsn */
150408d17fbeSalnsn if (BPF_RVAL(pc->code) == BPF_K && pc->k == 0)
150508d17fbeSalnsn abc_length = MAX_ABC_LENGTH;
150608d17fbeSalnsn else
150798f8ebfbSalnsn abc_length = 0;
150898f8ebfbSalnsn break;
150998f8ebfbSalnsn
151019fed70dSalnsn case BPF_MISC:
151119fed70dSalnsn if (BPF_MISCOP(pc->code) == BPF_COP ||
151219fed70dSalnsn BPF_MISCOP(pc->code) == BPF_COPX) {
151319fed70dSalnsn /* COP instructions can have side effects. */
151419fed70dSalnsn abc_length = 0;
151519fed70dSalnsn }
151619fed70dSalnsn break;
151719fed70dSalnsn
151819fed70dSalnsn case BPF_ST:
151919fed70dSalnsn case BPF_STX:
152019fed70dSalnsn if (extwords != 0) {
152119fed70dSalnsn /* Write to memory is visible after a call. */
152219fed70dSalnsn abc_length = 0;
152319fed70dSalnsn }
152419fed70dSalnsn break;
152519fed70dSalnsn
152698f8ebfbSalnsn case BPF_JMP:
152798f8ebfbSalnsn abc_length = pd->u.jdata.abc_length;
152898f8ebfbSalnsn break;
152998f8ebfbSalnsn
153098f8ebfbSalnsn default:
153198f8ebfbSalnsn if (read_pkt_insn(pc, &length)) {
153298f8ebfbSalnsn if (abc_length < length)
153398f8ebfbSalnsn abc_length = length;
153498f8ebfbSalnsn pd->u.rdata.abc_length = abc_length;
153598f8ebfbSalnsn }
153698f8ebfbSalnsn break;
153798f8ebfbSalnsn }
153898f8ebfbSalnsn
153998f8ebfbSalnsn SLIST_FOREACH(jmp, &pd->bjumps, entries) {
154098f8ebfbSalnsn if (jmp->jdata->abc_length > abc_length)
154198f8ebfbSalnsn jmp->jdata->abc_length = abc_length;
154298f8ebfbSalnsn }
154398f8ebfbSalnsn }
154498f8ebfbSalnsn }
154598f8ebfbSalnsn
154698f8ebfbSalnsn static void
optimize_pass3(const struct bpf_insn * insns,struct bpfjit_insn_data * insn_dat,size_t insn_count)154798f8ebfbSalnsn optimize_pass3(const struct bpf_insn *insns,
154898f8ebfbSalnsn struct bpfjit_insn_data *insn_dat, size_t insn_count)
154998f8ebfbSalnsn {
155098f8ebfbSalnsn struct bpfjit_jump *jmp;
155198f8ebfbSalnsn size_t i;
155274e580d2Salnsn bpfjit_abc_length_t checked_length = 0;
1553e8c0d6c6Salnsn
1554e8c0d6c6Salnsn for (i = 0; i < insn_count; i++) {
155598f8ebfbSalnsn if (insn_dat[i].unreachable)
155698f8ebfbSalnsn continue;
1557e8c0d6c6Salnsn
155898f8ebfbSalnsn SLIST_FOREACH(jmp, &insn_dat[i].bjumps, entries) {
155998f8ebfbSalnsn if (jmp->jdata->checked_length < checked_length)
156098f8ebfbSalnsn checked_length = jmp->jdata->checked_length;
1561e8c0d6c6Salnsn }
1562e8c0d6c6Salnsn
156398f8ebfbSalnsn if (BPF_CLASS(insns[i].code) == BPF_JMP) {
156498f8ebfbSalnsn insn_dat[i].u.jdata.checked_length = checked_length;
156574e580d2Salnsn } else if (read_pkt_insn(&insns[i], NULL)) {
156698f8ebfbSalnsn struct bpfjit_read_pkt_data *rdata =
156798f8ebfbSalnsn &insn_dat[i].u.rdata;
156898f8ebfbSalnsn rdata->check_length = 0;
156998f8ebfbSalnsn if (checked_length < rdata->abc_length) {
157098f8ebfbSalnsn checked_length = rdata->abc_length;
157198f8ebfbSalnsn rdata->check_length = checked_length;
1572e8c0d6c6Salnsn }
157398f8ebfbSalnsn }
1574e8c0d6c6Salnsn }
1575e8c0d6c6Salnsn }
1576e8c0d6c6Salnsn
157798f8ebfbSalnsn static bool
optimize(const bpf_ctx_t * bc,const struct bpf_insn * insns,struct bpfjit_insn_data * insn_dat,size_t insn_count,bpf_memword_init_t * initmask,bpfjit_hint_t * hints)1578c676220dSalnsn optimize(const bpf_ctx_t *bc, const struct bpf_insn *insns,
157998f8ebfbSalnsn struct bpfjit_insn_data *insn_dat, size_t insn_count,
158024d883a5Salnsn bpf_memword_init_t *initmask, bpfjit_hint_t *hints)
158198f8ebfbSalnsn {
158298f8ebfbSalnsn
158398f8ebfbSalnsn optimize_init(insn_dat, insn_count);
158498f8ebfbSalnsn
158524d883a5Salnsn if (!optimize_pass1(bc, insns, insn_dat, insn_count, initmask, hints))
158698f8ebfbSalnsn return false;
158798f8ebfbSalnsn
1588c676220dSalnsn optimize_pass2(bc, insns, insn_dat, insn_count);
158998f8ebfbSalnsn optimize_pass3(insns, insn_dat, insn_count);
159098f8ebfbSalnsn
159198f8ebfbSalnsn return true;
1592e8c0d6c6Salnsn }
1593e8c0d6c6Salnsn
1594e8c0d6c6Salnsn /*
1595e8c0d6c6Salnsn * Convert BPF_ALU operations except BPF_NEG and BPF_DIV to sljit operation.
1596e8c0d6c6Salnsn */
1597db4395c5Salnsn static bool
alu_to_op(const struct bpf_insn * pc,int * res)1598db4395c5Salnsn alu_to_op(const struct bpf_insn *pc, int *res)
1599e8c0d6c6Salnsn {
1600321f9dd4Salnsn const uint32_t k = pc->k;
1601e8c0d6c6Salnsn
1602e8c0d6c6Salnsn /*
1603e8c0d6c6Salnsn * Note: all supported 64bit arches have 32bit multiply
160444dbc048Salnsn * instruction so SLJIT_I32_OP doesn't have any overhead.
1605e8c0d6c6Salnsn */
1606e8c0d6c6Salnsn switch (BPF_OP(pc->code)) {
1607db4395c5Salnsn case BPF_ADD:
1608db4395c5Salnsn *res = SLJIT_ADD;
1609db4395c5Salnsn return true;
1610db4395c5Salnsn case BPF_SUB:
1611db4395c5Salnsn *res = SLJIT_SUB;
1612db4395c5Salnsn return true;
1613db4395c5Salnsn case BPF_MUL:
1614db4395c5Salnsn *res = SLJIT_MUL|SLJIT_I32_OP;
1615db4395c5Salnsn return true;
1616db4395c5Salnsn case BPF_OR:
1617db4395c5Salnsn *res = SLJIT_OR;
1618db4395c5Salnsn return true;
1619db4395c5Salnsn case BPF_XOR:
1620db4395c5Salnsn *res = SLJIT_XOR;
1621db4395c5Salnsn return true;
1622db4395c5Salnsn case BPF_AND:
1623db4395c5Salnsn *res = SLJIT_AND;
1624db4395c5Salnsn return true;
1625db4395c5Salnsn case BPF_LSH:
1626db4395c5Salnsn *res = SLJIT_SHL;
1627db4395c5Salnsn return k < 32;
1628db4395c5Salnsn case BPF_RSH:
1629db4395c5Salnsn *res = SLJIT_LSHR|SLJIT_I32_OP;
1630db4395c5Salnsn return k < 32;
1631e8c0d6c6Salnsn default:
1632db4395c5Salnsn return false;
1633e8c0d6c6Salnsn }
1634e8c0d6c6Salnsn }
1635e8c0d6c6Salnsn
1636e8c0d6c6Salnsn /*
1637e8c0d6c6Salnsn * Convert BPF_JMP operations except BPF_JA to sljit condition.
1638e8c0d6c6Salnsn */
1639db4395c5Salnsn static bool
jmp_to_cond(const struct bpf_insn * pc,bool negate,int * res)1640db4395c5Salnsn jmp_to_cond(const struct bpf_insn *pc, bool negate, int *res)
1641e8c0d6c6Salnsn {
1642db4395c5Salnsn
1643e8c0d6c6Salnsn /*
1644e8c0d6c6Salnsn * Note: all supported 64bit arches have 32bit comparison
164544dbc048Salnsn * instructions so SLJIT_I32_OP doesn't have any overhead.
1646e8c0d6c6Salnsn */
1647db4395c5Salnsn *res = SLJIT_I32_OP;
1648e8c0d6c6Salnsn
1649e8c0d6c6Salnsn switch (BPF_OP(pc->code)) {
1650e8c0d6c6Salnsn case BPF_JGT:
1651db4395c5Salnsn *res |= negate ? SLJIT_LESS_EQUAL : SLJIT_GREATER;
1652db4395c5Salnsn return true;
1653e8c0d6c6Salnsn case BPF_JGE:
1654db4395c5Salnsn *res |= negate ? SLJIT_LESS : SLJIT_GREATER_EQUAL;
1655db4395c5Salnsn return true;
1656e8c0d6c6Salnsn case BPF_JEQ:
1657db4395c5Salnsn *res |= negate ? SLJIT_NOT_EQUAL : SLJIT_EQUAL;
1658db4395c5Salnsn return true;
1659e8c0d6c6Salnsn case BPF_JSET:
1660db4395c5Salnsn *res |= negate ? SLJIT_EQUAL : SLJIT_NOT_EQUAL;
1661db4395c5Salnsn return true;
1662e8c0d6c6Salnsn default:
1663db4395c5Salnsn return false;
1664e8c0d6c6Salnsn }
1665e8c0d6c6Salnsn }
1666e8c0d6c6Salnsn
1667e8c0d6c6Salnsn /*
1668e8c0d6c6Salnsn * Convert BPF_K and BPF_X to sljit register.
1669e8c0d6c6Salnsn */
1670e8c0d6c6Salnsn static int
kx_to_reg(const struct bpf_insn * pc)167198f8ebfbSalnsn kx_to_reg(const struct bpf_insn *pc)
1672e8c0d6c6Salnsn {
1673e8c0d6c6Salnsn
1674e8c0d6c6Salnsn switch (BPF_SRC(pc->code)) {
1675e8c0d6c6Salnsn case BPF_K: return SLJIT_IMM;
167698f8ebfbSalnsn case BPF_X: return BJ_XREG;
1677e8c0d6c6Salnsn default:
167898f8ebfbSalnsn BJ_ASSERT(false);
1679e8c0d6c6Salnsn return 0;
1680e8c0d6c6Salnsn }
1681e8c0d6c6Salnsn }
1682e8c0d6c6Salnsn
168333abe179Salnsn static sljit_sw
kx_to_reg_arg(const struct bpf_insn * pc)168498f8ebfbSalnsn kx_to_reg_arg(const struct bpf_insn *pc)
1685e8c0d6c6Salnsn {
1686e8c0d6c6Salnsn
1687e8c0d6c6Salnsn switch (BPF_SRC(pc->code)) {
1688e8c0d6c6Salnsn case BPF_K: return (uint32_t)pc->k; /* SLJIT_IMM, pc->k, */
168998f8ebfbSalnsn case BPF_X: return 0; /* BJ_XREG, 0, */
1690e8c0d6c6Salnsn default:
169198f8ebfbSalnsn BJ_ASSERT(false);
1692e8c0d6c6Salnsn return 0;
1693e8c0d6c6Salnsn }
1694e8c0d6c6Salnsn }
1695e8c0d6c6Salnsn
1696c676220dSalnsn static bool
generate_insn_code(struct sljit_compiler * compiler,bpfjit_hint_t hints,const bpf_ctx_t * bc,const struct bpf_insn * insns,struct bpfjit_insn_data * insn_dat,size_t insn_count)169780a29916Salnsn generate_insn_code(struct sljit_compiler *compiler, bpfjit_hint_t hints,
169880a29916Salnsn const bpf_ctx_t *bc, const struct bpf_insn *insns,
169980a29916Salnsn struct bpfjit_insn_data *insn_dat, size_t insn_count)
1700e8c0d6c6Salnsn {
1701c676220dSalnsn /* a list of jumps to out-of-bound return from a generated function */
1702c676220dSalnsn struct sljit_jump **ret0;
1703c676220dSalnsn size_t ret0_size, ret0_maxsize;
1704c676220dSalnsn
1705c676220dSalnsn struct sljit_jump *jump;
1706c676220dSalnsn struct sljit_label *label;
1707c676220dSalnsn const struct bpf_insn *pc;
1708c676220dSalnsn struct bpfjit_jump *bjump, *jtf;
1709c676220dSalnsn struct sljit_jump *to_mchain_jump;
171098f8ebfbSalnsn
1711e8c0d6c6Salnsn size_t i;
1712bca1938aSchristos unsigned int rval, mode, src, op;
1713db4395c5Salnsn int branching, negate;
1714db4395c5Salnsn int status, cond, op2;
1715e8c0d6c6Salnsn uint32_t jt, jf;
1716e8c0d6c6Salnsn
1717c676220dSalnsn bool unconditional_ret;
1718c676220dSalnsn bool rv;
171919fed70dSalnsn
1720c676220dSalnsn const size_t extwords = GET_EXTWORDS(bc);
1721c676220dSalnsn const size_t memwords = GET_MEMWORDS(bc);
1722c676220dSalnsn
172319fed70dSalnsn ret0 = NULL;
1724c676220dSalnsn rv = false;
1725e8c0d6c6Salnsn
172698f8ebfbSalnsn ret0_size = 0;
172798f8ebfbSalnsn ret0_maxsize = 64;
172898f8ebfbSalnsn ret0 = BJ_ALLOC(ret0_maxsize * sizeof(ret0[0]));
172998f8ebfbSalnsn if (ret0 == NULL)
1730e8c0d6c6Salnsn goto fail;
1731e8c0d6c6Salnsn
1732d35700ddSalnsn /* reset sjump members of jdata */
1733d35700ddSalnsn for (i = 0; i < insn_count; i++) {
1734d35700ddSalnsn if (insn_dat[i].unreachable ||
1735d35700ddSalnsn BPF_CLASS(insns[i].code) != BPF_JMP) {
1736d35700ddSalnsn continue;
1737d35700ddSalnsn }
1738d35700ddSalnsn
1739d35700ddSalnsn jtf = insn_dat[i].u.jdata.jtf;
1740d35700ddSalnsn jtf[0].sjump = jtf[1].sjump = NULL;
1741d35700ddSalnsn }
1742d35700ddSalnsn
1743d35700ddSalnsn /* main loop */
1744e8c0d6c6Salnsn for (i = 0; i < insn_count; i++) {
174598f8ebfbSalnsn if (insn_dat[i].unreachable)
1746e8c0d6c6Salnsn continue;
1747e8c0d6c6Salnsn
1748e8c0d6c6Salnsn /*
1749e8c0d6c6Salnsn * Resolve jumps to the current insn.
1750e8c0d6c6Salnsn */
1751e8c0d6c6Salnsn label = NULL;
175298f8ebfbSalnsn SLIST_FOREACH(bjump, &insn_dat[i].bjumps, entries) {
175398f8ebfbSalnsn if (bjump->sjump != NULL) {
1754e8c0d6c6Salnsn if (label == NULL)
1755e8c0d6c6Salnsn label = sljit_emit_label(compiler);
1756e8c0d6c6Salnsn if (label == NULL)
1757e8c0d6c6Salnsn goto fail;
175898f8ebfbSalnsn sljit_set_label(bjump->sjump, label);
1759e8c0d6c6Salnsn }
1760e8c0d6c6Salnsn }
1761e8c0d6c6Salnsn
17621bf51582Salnsn to_mchain_jump = NULL;
17631bf51582Salnsn unconditional_ret = false;
17641bf51582Salnsn
17651bf51582Salnsn if (read_pkt_insn(&insns[i], NULL)) {
17661bf51582Salnsn if (insn_dat[i].u.rdata.check_length > UINT32_MAX) {
17671bf51582Salnsn /* Jump to "return 0" unconditionally. */
17681bf51582Salnsn unconditional_ret = true;
17691bf51582Salnsn jump = sljit_emit_jump(compiler, SLJIT_JUMP);
17701bf51582Salnsn if (jump == NULL)
17711bf51582Salnsn goto fail;
17721bf51582Salnsn if (!append_jump(jump, &ret0,
17731bf51582Salnsn &ret0_size, &ret0_maxsize))
17741bf51582Salnsn goto fail;
17751bf51582Salnsn } else if (insn_dat[i].u.rdata.check_length > 0) {
177698f8ebfbSalnsn /* if (buflen < check_length) return 0; */
1777e8c0d6c6Salnsn jump = sljit_emit_cmp(compiler,
177844dbc048Salnsn SLJIT_LESS,
177998f8ebfbSalnsn BJ_BUFLEN, 0,
1780e8c0d6c6Salnsn SLJIT_IMM,
178198f8ebfbSalnsn insn_dat[i].u.rdata.check_length);
1782e8c0d6c6Salnsn if (jump == NULL)
1783e8c0d6c6Salnsn goto fail;
1784e8c0d6c6Salnsn #ifdef _KERNEL
1785e8c0d6c6Salnsn to_mchain_jump = jump;
1786e8c0d6c6Salnsn #else
178798f8ebfbSalnsn if (!append_jump(jump, &ret0,
178898f8ebfbSalnsn &ret0_size, &ret0_maxsize))
178998f8ebfbSalnsn goto fail;
1790e8c0d6c6Salnsn #endif
1791e8c0d6c6Salnsn }
17921bf51582Salnsn }
1793e8c0d6c6Salnsn
1794e8c0d6c6Salnsn pc = &insns[i];
1795e8c0d6c6Salnsn switch (BPF_CLASS(pc->code)) {
1796e8c0d6c6Salnsn
1797e8c0d6c6Salnsn default:
1798e8c0d6c6Salnsn goto fail;
1799e8c0d6c6Salnsn
1800e8c0d6c6Salnsn case BPF_LD:
1801e8c0d6c6Salnsn /* BPF_LD+BPF_IMM A <- k */
1802e8c0d6c6Salnsn if (pc->code == (BPF_LD|BPF_IMM)) {
1803e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
1804e8c0d6c6Salnsn SLJIT_MOV,
180598f8ebfbSalnsn BJ_AREG, 0,
1806e8c0d6c6Salnsn SLJIT_IMM, (uint32_t)pc->k);
1807e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1808e8c0d6c6Salnsn goto fail;
1809e8c0d6c6Salnsn
1810e8c0d6c6Salnsn continue;
1811e8c0d6c6Salnsn }
1812e8c0d6c6Salnsn
1813e8c0d6c6Salnsn /* BPF_LD+BPF_MEM A <- M[k] */
1814e8c0d6c6Salnsn if (pc->code == (BPF_LD|BPF_MEM)) {
181519fed70dSalnsn if ((uint32_t)pc->k >= memwords)
1816e8c0d6c6Salnsn goto fail;
181719fed70dSalnsn status = emit_memload(compiler,
181819fed70dSalnsn BJ_AREG, pc->k, extwords);
1819e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1820e8c0d6c6Salnsn goto fail;
1821e8c0d6c6Salnsn
1822e8c0d6c6Salnsn continue;
1823e8c0d6c6Salnsn }
1824e8c0d6c6Salnsn
1825e8c0d6c6Salnsn /* BPF_LD+BPF_W+BPF_LEN A <- len */
1826e8c0d6c6Salnsn if (pc->code == (BPF_LD|BPF_W|BPF_LEN)) {
1827e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
1828b7e8e67dSalnsn SLJIT_MOV, /* size_t source */
182998f8ebfbSalnsn BJ_AREG, 0,
183019fed70dSalnsn SLJIT_MEM1(BJ_ARGS),
183119fed70dSalnsn offsetof(struct bpf_args, wirelen));
1832e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1833e8c0d6c6Salnsn goto fail;
1834e8c0d6c6Salnsn
1835e8c0d6c6Salnsn continue;
1836e8c0d6c6Salnsn }
1837e8c0d6c6Salnsn
1838e8c0d6c6Salnsn mode = BPF_MODE(pc->code);
1839e8c0d6c6Salnsn if (mode != BPF_ABS && mode != BPF_IND)
1840e8c0d6c6Salnsn goto fail;
1841e8c0d6c6Salnsn
18421bf51582Salnsn if (unconditional_ret)
18431bf51582Salnsn continue;
18441bf51582Salnsn
184580a29916Salnsn status = emit_pkt_read(compiler, hints, pc,
184698f8ebfbSalnsn to_mchain_jump, &ret0, &ret0_size, &ret0_maxsize);
1847e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1848e8c0d6c6Salnsn goto fail;
1849e8c0d6c6Salnsn
1850e8c0d6c6Salnsn continue;
1851e8c0d6c6Salnsn
1852e8c0d6c6Salnsn case BPF_LDX:
1853e8c0d6c6Salnsn mode = BPF_MODE(pc->code);
1854e8c0d6c6Salnsn
1855e8c0d6c6Salnsn /* BPF_LDX+BPF_W+BPF_IMM X <- k */
1856e8c0d6c6Salnsn if (mode == BPF_IMM) {
1857e8c0d6c6Salnsn if (BPF_SIZE(pc->code) != BPF_W)
1858e8c0d6c6Salnsn goto fail;
1859e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
1860e8c0d6c6Salnsn SLJIT_MOV,
186198f8ebfbSalnsn BJ_XREG, 0,
1862e8c0d6c6Salnsn SLJIT_IMM, (uint32_t)pc->k);
1863e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1864e8c0d6c6Salnsn goto fail;
1865e8c0d6c6Salnsn
1866e8c0d6c6Salnsn continue;
1867e8c0d6c6Salnsn }
1868e8c0d6c6Salnsn
1869e8c0d6c6Salnsn /* BPF_LDX+BPF_W+BPF_LEN X <- len */
1870e8c0d6c6Salnsn if (mode == BPF_LEN) {
1871e8c0d6c6Salnsn if (BPF_SIZE(pc->code) != BPF_W)
1872e8c0d6c6Salnsn goto fail;
1873e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
1874b7e8e67dSalnsn SLJIT_MOV, /* size_t source */
187598f8ebfbSalnsn BJ_XREG, 0,
187619fed70dSalnsn SLJIT_MEM1(BJ_ARGS),
187719fed70dSalnsn offsetof(struct bpf_args, wirelen));
1878e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1879e8c0d6c6Salnsn goto fail;
1880e8c0d6c6Salnsn
1881e8c0d6c6Salnsn continue;
1882e8c0d6c6Salnsn }
1883e8c0d6c6Salnsn
1884e8c0d6c6Salnsn /* BPF_LDX+BPF_W+BPF_MEM X <- M[k] */
1885e8c0d6c6Salnsn if (mode == BPF_MEM) {
1886e8c0d6c6Salnsn if (BPF_SIZE(pc->code) != BPF_W)
1887e8c0d6c6Salnsn goto fail;
188819fed70dSalnsn if ((uint32_t)pc->k >= memwords)
1889e8c0d6c6Salnsn goto fail;
189019fed70dSalnsn status = emit_memload(compiler,
189119fed70dSalnsn BJ_XREG, pc->k, extwords);
1892e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1893e8c0d6c6Salnsn goto fail;
1894e8c0d6c6Salnsn
1895e8c0d6c6Salnsn continue;
1896e8c0d6c6Salnsn }
1897e8c0d6c6Salnsn
1898e8c0d6c6Salnsn /* BPF_LDX+BPF_B+BPF_MSH X <- 4*(P[k:1]&0xf) */
1899e8c0d6c6Salnsn if (mode != BPF_MSH || BPF_SIZE(pc->code) != BPF_B)
1900e8c0d6c6Salnsn goto fail;
1901e8c0d6c6Salnsn
19021bf51582Salnsn if (unconditional_ret)
19031bf51582Salnsn continue;
19041bf51582Salnsn
190580a29916Salnsn status = emit_msh(compiler, hints, pc,
190698f8ebfbSalnsn to_mchain_jump, &ret0, &ret0_size, &ret0_maxsize);
1907e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1908e8c0d6c6Salnsn goto fail;
1909e8c0d6c6Salnsn
1910e8c0d6c6Salnsn continue;
1911e8c0d6c6Salnsn
1912e8c0d6c6Salnsn case BPF_ST:
191374e580d2Salnsn if (pc->code != BPF_ST ||
191419fed70dSalnsn (uint32_t)pc->k >= memwords) {
1915e8c0d6c6Salnsn goto fail;
191674e580d2Salnsn }
1917e8c0d6c6Salnsn
191819fed70dSalnsn status = emit_memstore(compiler,
191919fed70dSalnsn BJ_AREG, pc->k, extwords);
1920e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1921e8c0d6c6Salnsn goto fail;
1922e8c0d6c6Salnsn
1923e8c0d6c6Salnsn continue;
1924e8c0d6c6Salnsn
1925e8c0d6c6Salnsn case BPF_STX:
192674e580d2Salnsn if (pc->code != BPF_STX ||
192719fed70dSalnsn (uint32_t)pc->k >= memwords) {
1928e8c0d6c6Salnsn goto fail;
192974e580d2Salnsn }
1930e8c0d6c6Salnsn
193119fed70dSalnsn status = emit_memstore(compiler,
193219fed70dSalnsn BJ_XREG, pc->k, extwords);
1933e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1934e8c0d6c6Salnsn goto fail;
1935e8c0d6c6Salnsn
1936e8c0d6c6Salnsn continue;
1937e8c0d6c6Salnsn
1938e8c0d6c6Salnsn case BPF_ALU:
1939e8c0d6c6Salnsn if (pc->code == (BPF_ALU|BPF_NEG)) {
1940e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
1941e8c0d6c6Salnsn SLJIT_NEG,
194298f8ebfbSalnsn BJ_AREG, 0,
194398f8ebfbSalnsn BJ_AREG, 0);
1944e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1945e8c0d6c6Salnsn goto fail;
1946e8c0d6c6Salnsn
1947e8c0d6c6Salnsn continue;
1948e8c0d6c6Salnsn }
1949e8c0d6c6Salnsn
1950bca1938aSchristos op = BPF_OP(pc->code);
1951bca1938aSchristos if (op != BPF_DIV && op != BPF_MOD) {
1952db4395c5Salnsn if (!alu_to_op(pc, &op2))
1953f34037edSalnsn goto fail;
1954db4395c5Salnsn
1955e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
1956f34037edSalnsn op2, BJ_AREG, 0, BJ_AREG, 0,
1957e8c0d6c6Salnsn kx_to_reg(pc), kx_to_reg_arg(pc));
1958e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1959e8c0d6c6Salnsn goto fail;
1960e8c0d6c6Salnsn
1961e8c0d6c6Salnsn continue;
1962e8c0d6c6Salnsn }
1963e8c0d6c6Salnsn
1964bca1938aSchristos /* BPF_DIV/BPF_MOD */
1965e8c0d6c6Salnsn
1966e8c0d6c6Salnsn src = BPF_SRC(pc->code);
1967e8c0d6c6Salnsn if (src != BPF_X && src != BPF_K)
1968e8c0d6c6Salnsn goto fail;
1969e8c0d6c6Salnsn
1970e8c0d6c6Salnsn /* division by zero? */
1971e8c0d6c6Salnsn if (src == BPF_X) {
1972e8c0d6c6Salnsn jump = sljit_emit_cmp(compiler,
197344dbc048Salnsn SLJIT_EQUAL|SLJIT_I32_OP,
197498f8ebfbSalnsn BJ_XREG, 0,
1975e8c0d6c6Salnsn SLJIT_IMM, 0);
1976e8c0d6c6Salnsn if (jump == NULL)
1977e8c0d6c6Salnsn goto fail;
197898f8ebfbSalnsn if (!append_jump(jump, &ret0,
197998f8ebfbSalnsn &ret0_size, &ret0_maxsize))
198098f8ebfbSalnsn goto fail;
1981e8c0d6c6Salnsn } else if (pc->k == 0) {
1982e8c0d6c6Salnsn jump = sljit_emit_jump(compiler, SLJIT_JUMP);
1983e8c0d6c6Salnsn if (jump == NULL)
1984e8c0d6c6Salnsn goto fail;
198598f8ebfbSalnsn if (!append_jump(jump, &ret0,
198698f8ebfbSalnsn &ret0_size, &ret0_maxsize))
198798f8ebfbSalnsn goto fail;
1988e8c0d6c6Salnsn }
1989e8c0d6c6Salnsn
1990e8c0d6c6Salnsn if (src == BPF_X) {
1991e7d58502Salnsn status = emit_moddiv(compiler, pc);
1992e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
1993e8c0d6c6Salnsn goto fail;
1994e8c0d6c6Salnsn } else if (pc->k != 0) {
1995e7d58502Salnsn if (pc->k & (pc->k - 1)) {
1996e7d58502Salnsn status = emit_moddiv(compiler, pc);
1997e8c0d6c6Salnsn } else {
1998e7d58502Salnsn status = emit_pow2_moddiv(compiler, pc);
1999e8c0d6c6Salnsn }
2000e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
2001e8c0d6c6Salnsn goto fail;
2002e8c0d6c6Salnsn }
2003e8c0d6c6Salnsn
2004e8c0d6c6Salnsn continue;
2005e8c0d6c6Salnsn
2006e8c0d6c6Salnsn case BPF_JMP:
2007bca1938aSchristos op = BPF_OP(pc->code);
2008bca1938aSchristos if (op == BPF_JA) {
2009e8c0d6c6Salnsn jt = jf = pc->k;
2010e8c0d6c6Salnsn } else {
2011e8c0d6c6Salnsn jt = pc->jt;
2012e8c0d6c6Salnsn jf = pc->jf;
2013e8c0d6c6Salnsn }
2014e8c0d6c6Salnsn
2015e8c0d6c6Salnsn negate = (jt == 0) ? 1 : 0;
2016e8c0d6c6Salnsn branching = (jt == jf) ? 0 : 1;
201798f8ebfbSalnsn jtf = insn_dat[i].u.jdata.jtf;
2018e8c0d6c6Salnsn
2019e8c0d6c6Salnsn if (branching) {
2020bca1938aSchristos if (op != BPF_JSET) {
2021db4395c5Salnsn if (!jmp_to_cond(pc, negate, &cond))
2022db4395c5Salnsn goto fail;
2023e8c0d6c6Salnsn jump = sljit_emit_cmp(compiler,
2024db4395c5Salnsn cond, BJ_AREG, 0,
2025e8c0d6c6Salnsn kx_to_reg(pc), kx_to_reg_arg(pc));
2026e8c0d6c6Salnsn } else {
2027e8c0d6c6Salnsn status = sljit_emit_op2(compiler,
2028e8c0d6c6Salnsn SLJIT_AND,
202998f8ebfbSalnsn BJ_TMP1REG, 0,
203098f8ebfbSalnsn BJ_AREG, 0,
2031e8c0d6c6Salnsn kx_to_reg(pc), kx_to_reg_arg(pc));
2032e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
2033e8c0d6c6Salnsn goto fail;
2034e8c0d6c6Salnsn
2035db4395c5Salnsn if (!jmp_to_cond(pc, negate, &cond))
2036db4395c5Salnsn goto fail;
2037e8c0d6c6Salnsn jump = sljit_emit_cmp(compiler,
2038db4395c5Salnsn cond, BJ_TMP1REG, 0, SLJIT_IMM, 0);
2039e8c0d6c6Salnsn }
2040e8c0d6c6Salnsn
2041e8c0d6c6Salnsn if (jump == NULL)
2042e8c0d6c6Salnsn goto fail;
2043e8c0d6c6Salnsn
204498f8ebfbSalnsn BJ_ASSERT(jtf[negate].sjump == NULL);
204598f8ebfbSalnsn jtf[negate].sjump = jump;
2046e8c0d6c6Salnsn }
2047e8c0d6c6Salnsn
2048e8c0d6c6Salnsn if (!branching || (jt != 0 && jf != 0)) {
2049e8c0d6c6Salnsn jump = sljit_emit_jump(compiler, SLJIT_JUMP);
2050e8c0d6c6Salnsn if (jump == NULL)
2051e8c0d6c6Salnsn goto fail;
2052e8c0d6c6Salnsn
205398f8ebfbSalnsn BJ_ASSERT(jtf[branching].sjump == NULL);
205498f8ebfbSalnsn jtf[branching].sjump = jump;
2055e8c0d6c6Salnsn }
2056e8c0d6c6Salnsn
2057e8c0d6c6Salnsn continue;
2058e8c0d6c6Salnsn
2059e8c0d6c6Salnsn case BPF_RET:
2060e8c0d6c6Salnsn rval = BPF_RVAL(pc->code);
2061e8c0d6c6Salnsn if (rval == BPF_X)
2062e8c0d6c6Salnsn goto fail;
2063e8c0d6c6Salnsn
2064e8c0d6c6Salnsn /* BPF_RET+BPF_K accept k bytes */
2065e8c0d6c6Salnsn if (rval == BPF_K) {
206698f8ebfbSalnsn status = sljit_emit_return(compiler,
206744dbc048Salnsn SLJIT_MOV_U32,
2068e8c0d6c6Salnsn SLJIT_IMM, (uint32_t)pc->k);
2069e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
2070e8c0d6c6Salnsn goto fail;
2071e8c0d6c6Salnsn }
2072e8c0d6c6Salnsn
2073e8c0d6c6Salnsn /* BPF_RET+BPF_A accept A bytes */
2074e8c0d6c6Salnsn if (rval == BPF_A) {
207598f8ebfbSalnsn status = sljit_emit_return(compiler,
207644dbc048Salnsn SLJIT_MOV_U32,
207798f8ebfbSalnsn BJ_AREG, 0);
2078e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
2079e8c0d6c6Salnsn goto fail;
2080e8c0d6c6Salnsn }
2081e8c0d6c6Salnsn
2082e8c0d6c6Salnsn continue;
2083e8c0d6c6Salnsn
2084e8c0d6c6Salnsn case BPF_MISC:
208598f8ebfbSalnsn switch (BPF_MISCOP(pc->code)) {
208698f8ebfbSalnsn case BPF_TAX:
2087e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
208844dbc048Salnsn SLJIT_MOV_U32,
208998f8ebfbSalnsn BJ_XREG, 0,
209098f8ebfbSalnsn BJ_AREG, 0);
2091e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
2092e8c0d6c6Salnsn goto fail;
2093e8c0d6c6Salnsn
2094e8c0d6c6Salnsn continue;
2095e8c0d6c6Salnsn
209698f8ebfbSalnsn case BPF_TXA:
2097e8c0d6c6Salnsn status = sljit_emit_op1(compiler,
2098e8c0d6c6Salnsn SLJIT_MOV,
209998f8ebfbSalnsn BJ_AREG, 0,
210098f8ebfbSalnsn BJ_XREG, 0);
2101e8c0d6c6Salnsn if (status != SLJIT_SUCCESS)
2102e8c0d6c6Salnsn goto fail;
2103e8c0d6c6Salnsn
2104e8c0d6c6Salnsn continue;
210519fed70dSalnsn
210619fed70dSalnsn case BPF_COP:
210719fed70dSalnsn case BPF_COPX:
210819fed70dSalnsn if (bc == NULL || bc->copfuncs == NULL)
210919fed70dSalnsn goto fail;
211019fed70dSalnsn if (BPF_MISCOP(pc->code) == BPF_COP &&
211119fed70dSalnsn (uint32_t)pc->k >= bc->nfuncs) {
211219fed70dSalnsn goto fail;
211319fed70dSalnsn }
211419fed70dSalnsn
211580a29916Salnsn status = emit_cop(compiler, hints, bc, pc,
21167d10db23Salnsn &ret0, &ret0_size, &ret0_maxsize);
211719fed70dSalnsn if (status != SLJIT_SUCCESS)
211819fed70dSalnsn goto fail;
211919fed70dSalnsn
212019fed70dSalnsn continue;
2121e8c0d6c6Salnsn }
2122e8c0d6c6Salnsn
2123e8c0d6c6Salnsn goto fail;
2124e8c0d6c6Salnsn } /* switch */
2125e8c0d6c6Salnsn } /* main loop */
2126e8c0d6c6Salnsn
212798f8ebfbSalnsn BJ_ASSERT(ret0_size <= ret0_maxsize);
2128e8c0d6c6Salnsn
2129e8c0d6c6Salnsn if (ret0_size > 0) {
2130e8c0d6c6Salnsn label = sljit_emit_label(compiler);
2131e8c0d6c6Salnsn if (label == NULL)
2132e8c0d6c6Salnsn goto fail;
2133e8c0d6c6Salnsn for (i = 0; i < ret0_size; i++)
2134e8c0d6c6Salnsn sljit_set_label(ret0[i], label);
213598f8ebfbSalnsn }
2136e8c0d6c6Salnsn
2137b81423b7Salnsn status = sljit_emit_return(compiler,
213844dbc048Salnsn SLJIT_MOV_U32,
2139b81423b7Salnsn SLJIT_IMM, 0);
2140b81423b7Salnsn if (status != SLJIT_SUCCESS)
2141b81423b7Salnsn goto fail;
2142b81423b7Salnsn
2143c676220dSalnsn rv = true;
2144c676220dSalnsn
2145c676220dSalnsn fail:
2146c676220dSalnsn if (ret0 != NULL)
2147c676220dSalnsn BJ_FREE(ret0, ret0_maxsize * sizeof(ret0[0]));
2148c676220dSalnsn
2149c676220dSalnsn return rv;
2150c676220dSalnsn }
2151c676220dSalnsn
2152c676220dSalnsn bpfjit_func_t
bpfjit_generate_code(const bpf_ctx_t * bc,const struct bpf_insn * insns,size_t insn_count)2153c676220dSalnsn bpfjit_generate_code(const bpf_ctx_t *bc,
2154c676220dSalnsn const struct bpf_insn *insns, size_t insn_count)
2155c676220dSalnsn {
2156c676220dSalnsn void *rv;
2157c676220dSalnsn struct sljit_compiler *compiler;
2158c676220dSalnsn
2159c676220dSalnsn size_t i;
2160c676220dSalnsn int status;
2161c676220dSalnsn
2162c676220dSalnsn /* optimization related */
2163c676220dSalnsn bpf_memword_init_t initmask;
216424d883a5Salnsn bpfjit_hint_t hints;
2165c676220dSalnsn
2166c676220dSalnsn /* memory store location for initial zero initialization */
216744dbc048Salnsn sljit_s32 mem_reg;
2168c676220dSalnsn sljit_sw mem_off;
2169c676220dSalnsn
2170c676220dSalnsn struct bpfjit_insn_data *insn_dat;
2171c676220dSalnsn
2172c676220dSalnsn const size_t extwords = GET_EXTWORDS(bc);
2173c676220dSalnsn const size_t memwords = GET_MEMWORDS(bc);
2174c676220dSalnsn const bpf_memword_init_t preinited = extwords ? bc->preinited : 0;
2175c676220dSalnsn
2176c676220dSalnsn rv = NULL;
2177c676220dSalnsn compiler = NULL;
2178c676220dSalnsn insn_dat = NULL;
2179c676220dSalnsn
2180c676220dSalnsn if (memwords > MAX_MEMWORDS)
2181c676220dSalnsn goto fail;
2182c676220dSalnsn
2183c676220dSalnsn if (insn_count == 0 || insn_count > SIZE_MAX / sizeof(insn_dat[0]))
2184c676220dSalnsn goto fail;
2185c676220dSalnsn
2186c676220dSalnsn insn_dat = BJ_ALLOC(insn_count * sizeof(insn_dat[0]));
2187c676220dSalnsn if (insn_dat == NULL)
2188c676220dSalnsn goto fail;
2189c676220dSalnsn
219024d883a5Salnsn if (!optimize(bc, insns, insn_dat, insn_count, &initmask, &hints))
2191c676220dSalnsn goto fail;
2192c676220dSalnsn
219344dbc048Salnsn compiler = sljit_create_compiler(NULL);
2194c676220dSalnsn if (compiler == NULL)
2195c676220dSalnsn goto fail;
2196c676220dSalnsn
2197c676220dSalnsn #if !defined(_KERNEL) && defined(SLJIT_VERBOSE) && SLJIT_VERBOSE
2198c676220dSalnsn sljit_compiler_verbose(compiler, stderr);
2199c676220dSalnsn #endif
2200c676220dSalnsn
220144dbc048Salnsn status = sljit_emit_enter(compiler, 0, 2, nscratches(hints),
220244dbc048Salnsn NSAVEDS, 0, 0, sizeof(struct bpfjit_stack));
2203c676220dSalnsn if (status != SLJIT_SUCCESS)
2204c676220dSalnsn goto fail;
2205c676220dSalnsn
220624d883a5Salnsn if (hints & BJ_HINT_COP) {
2207c676220dSalnsn /* save ctx argument */
2208c676220dSalnsn status = sljit_emit_op1(compiler,
2209c676220dSalnsn SLJIT_MOV_P,
221044dbc048Salnsn SLJIT_MEM1(SLJIT_SP),
2211c676220dSalnsn offsetof(struct bpfjit_stack, ctx),
2212c676220dSalnsn BJ_CTX_ARG, 0);
2213c676220dSalnsn if (status != SLJIT_SUCCESS)
2214c676220dSalnsn goto fail;
2215c676220dSalnsn }
2216c676220dSalnsn
2217c676220dSalnsn if (extwords == 0) {
221844dbc048Salnsn mem_reg = SLJIT_MEM1(SLJIT_SP);
2219c676220dSalnsn mem_off = offsetof(struct bpfjit_stack, mem);
2220c676220dSalnsn } else {
2221c676220dSalnsn /* copy "mem" argument from bpf_args to bpfjit_stack */
2222c676220dSalnsn status = sljit_emit_op1(compiler,
2223c676220dSalnsn SLJIT_MOV_P,
2224c676220dSalnsn BJ_TMP1REG, 0,
2225c676220dSalnsn SLJIT_MEM1(BJ_ARGS), offsetof(struct bpf_args, mem));
2226c676220dSalnsn if (status != SLJIT_SUCCESS)
2227c676220dSalnsn goto fail;
2228c676220dSalnsn
2229c676220dSalnsn status = sljit_emit_op1(compiler,
2230c676220dSalnsn SLJIT_MOV_P,
223144dbc048Salnsn SLJIT_MEM1(SLJIT_SP),
2232c676220dSalnsn offsetof(struct bpfjit_stack, extmem),
2233c676220dSalnsn BJ_TMP1REG, 0);
2234c676220dSalnsn if (status != SLJIT_SUCCESS)
2235c676220dSalnsn goto fail;
2236c676220dSalnsn
2237c676220dSalnsn mem_reg = SLJIT_MEM1(BJ_TMP1REG);
2238c676220dSalnsn mem_off = 0;
2239c676220dSalnsn }
2240c676220dSalnsn
2241c676220dSalnsn /*
2242c676220dSalnsn * Exclude pre-initialised external memory words but keep
2243c676220dSalnsn * initialization statuses of A and X registers in case
2244c676220dSalnsn * bc->preinited wrongly sets those two bits.
2245c676220dSalnsn */
2246c676220dSalnsn initmask &= ~preinited | BJ_INIT_ABIT | BJ_INIT_XBIT;
2247c676220dSalnsn
2248c676220dSalnsn #if defined(_KERNEL)
2249c676220dSalnsn /* bpf_filter() checks initialization of memwords. */
2250c676220dSalnsn BJ_ASSERT((initmask & (BJ_INIT_MBIT(memwords) - 1)) == 0);
2251c676220dSalnsn #endif
2252c676220dSalnsn for (i = 0; i < memwords; i++) {
2253c676220dSalnsn if (initmask & BJ_INIT_MBIT(i)) {
2254c676220dSalnsn /* M[i] = 0; */
2255c676220dSalnsn status = sljit_emit_op1(compiler,
225644dbc048Salnsn SLJIT_MOV_U32,
2257c676220dSalnsn mem_reg, mem_off + i * sizeof(uint32_t),
2258c676220dSalnsn SLJIT_IMM, 0);
2259c676220dSalnsn if (status != SLJIT_SUCCESS)
2260c676220dSalnsn goto fail;
2261c676220dSalnsn }
2262c676220dSalnsn }
2263c676220dSalnsn
2264c676220dSalnsn if (initmask & BJ_INIT_ABIT) {
2265c676220dSalnsn /* A = 0; */
2266c676220dSalnsn status = sljit_emit_op1(compiler,
2267c676220dSalnsn SLJIT_MOV,
2268c676220dSalnsn BJ_AREG, 0,
2269c676220dSalnsn SLJIT_IMM, 0);
2270c676220dSalnsn if (status != SLJIT_SUCCESS)
2271c676220dSalnsn goto fail;
2272c676220dSalnsn }
2273c676220dSalnsn
2274c676220dSalnsn if (initmask & BJ_INIT_XBIT) {
2275c676220dSalnsn /* X = 0; */
2276c676220dSalnsn status = sljit_emit_op1(compiler,
2277c676220dSalnsn SLJIT_MOV,
2278c676220dSalnsn BJ_XREG, 0,
2279c676220dSalnsn SLJIT_IMM, 0);
2280c676220dSalnsn if (status != SLJIT_SUCCESS)
2281c676220dSalnsn goto fail;
2282c676220dSalnsn }
2283c676220dSalnsn
2284c676220dSalnsn status = load_buf_buflen(compiler);
2285c676220dSalnsn if (status != SLJIT_SUCCESS)
2286c676220dSalnsn goto fail;
2287c676220dSalnsn
228880a29916Salnsn if (!generate_insn_code(compiler, hints,
228980a29916Salnsn bc, insns, insn_dat, insn_count)) {
2290c676220dSalnsn goto fail;
229180a29916Salnsn }
2292c676220dSalnsn
2293e8c0d6c6Salnsn rv = sljit_generate_code(compiler);
2294e8c0d6c6Salnsn
2295e8c0d6c6Salnsn fail:
2296e8c0d6c6Salnsn if (compiler != NULL)
2297e8c0d6c6Salnsn sljit_free_compiler(compiler);
2298e8c0d6c6Salnsn
2299e8c0d6c6Salnsn if (insn_dat != NULL)
230098f8ebfbSalnsn BJ_FREE(insn_dat, insn_count * sizeof(insn_dat[0]));
2301e8c0d6c6Salnsn
2302d0748eb9Srmind return (bpfjit_func_t)rv;
2303e8c0d6c6Salnsn }
2304e8c0d6c6Salnsn
2305e8c0d6c6Salnsn void
bpfjit_free_code(bpfjit_func_t code)2306d0748eb9Srmind bpfjit_free_code(bpfjit_func_t code)
2307e8c0d6c6Salnsn {
230898f8ebfbSalnsn
2309e8c0d6c6Salnsn sljit_free_code((void *)code);
2310e8c0d6c6Salnsn }
2311