.. | .. |
---|
1 | | -/* |
---|
2 | | - * Copyright (C) 2016-2018 Netronome Systems, Inc. |
---|
3 | | - * |
---|
4 | | - * This software is dual licensed under the GNU General License Version 2, |
---|
5 | | - * June 1991 as shown in the file COPYING in the top-level directory of this |
---|
6 | | - * source tree or the BSD 2-Clause License provided below. You have the |
---|
7 | | - * option to license this software under the complete terms of either license. |
---|
8 | | - * |
---|
9 | | - * The BSD 2-Clause License: |
---|
10 | | - * |
---|
11 | | - * Redistribution and use in source and binary forms, with or |
---|
12 | | - * without modification, are permitted provided that the following |
---|
13 | | - * conditions are met: |
---|
14 | | - * |
---|
15 | | - * 1. Redistributions of source code must retain the above |
---|
16 | | - * copyright notice, this list of conditions and the following |
---|
17 | | - * disclaimer. |
---|
18 | | - * |
---|
19 | | - * 2. Redistributions in binary form must reproduce the above |
---|
20 | | - * copyright notice, this list of conditions and the following |
---|
21 | | - * disclaimer in the documentation and/or other materials |
---|
22 | | - * provided with the distribution. |
---|
23 | | - * |
---|
24 | | - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
---|
25 | | - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
---|
26 | | - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
---|
27 | | - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
---|
28 | | - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
---|
29 | | - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
---|
30 | | - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
---|
31 | | - * SOFTWARE. |
---|
32 | | - */ |
---|
| 1 | +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
---|
| 2 | +/* Copyright (C) 2016-2018 Netronome Systems, Inc. */ |
---|
33 | 3 | |
---|
34 | 4 | #include <linux/bpf.h> |
---|
35 | 5 | #include <linux/bpf_verifier.h> |
---|
36 | 6 | #include <linux/kernel.h> |
---|
| 7 | +#include <linux/netdevice.h> |
---|
37 | 8 | #include <linux/pkt_cls.h> |
---|
38 | 9 | |
---|
39 | 10 | #include "../nfp_app.h" |
---|
40 | 11 | #include "../nfp_main.h" |
---|
| 12 | +#include "../nfp_net.h" |
---|
41 | 13 | #include "fw.h" |
---|
42 | 14 | #include "main.h" |
---|
43 | 15 | |
---|
.. | .. |
---|
46 | 18 | |
---|
47 | 19 | struct nfp_insn_meta * |
---|
48 | 20 | nfp_bpf_goto_meta(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, |
---|
49 | | - unsigned int insn_idx, unsigned int n_insns) |
---|
| 21 | + unsigned int insn_idx) |
---|
50 | 22 | { |
---|
51 | 23 | unsigned int forward, backward, i; |
---|
52 | 24 | |
---|
53 | 25 | backward = meta->n - insn_idx; |
---|
54 | 26 | forward = insn_idx - meta->n; |
---|
55 | 27 | |
---|
56 | | - if (min(forward, backward) > n_insns - insn_idx - 1) { |
---|
57 | | - backward = n_insns - insn_idx - 1; |
---|
| 28 | + if (min(forward, backward) > nfp_prog->n_insns - insn_idx - 1) { |
---|
| 29 | + backward = nfp_prog->n_insns - insn_idx - 1; |
---|
58 | 30 | meta = nfp_prog_last_meta(nfp_prog); |
---|
59 | 31 | } |
---|
60 | 32 | if (min(forward, backward) > insn_idx && backward > insn_idx) { |
---|
.. | .. |
---|
195 | 167 | } |
---|
196 | 168 | |
---|
197 | 169 | static int |
---|
198 | | -nfp_bpf_check_call(struct nfp_prog *nfp_prog, struct bpf_verifier_env *env, |
---|
199 | | - struct nfp_insn_meta *meta) |
---|
| 170 | +nfp_bpf_check_helper_call(struct nfp_prog *nfp_prog, |
---|
| 171 | + struct bpf_verifier_env *env, |
---|
| 172 | + struct nfp_insn_meta *meta) |
---|
200 | 173 | { |
---|
201 | 174 | const struct bpf_reg_state *reg1 = cur_regs(env) + BPF_REG_1; |
---|
202 | 175 | const struct bpf_reg_state *reg2 = cur_regs(env) + BPF_REG_2; |
---|
.. | .. |
---|
373 | 346 | struct bpf_verifier_env *env) |
---|
374 | 347 | { |
---|
375 | 348 | s32 old_off, new_off; |
---|
| 349 | + |
---|
| 350 | + if (reg->frameno != env->cur_state->curframe) |
---|
| 351 | + meta->flags |= FLAG_INSN_PTR_CALLER_STACK_FRAME; |
---|
376 | 352 | |
---|
377 | 353 | if (!tnum_is_const(reg->var_off)) { |
---|
378 | 354 | pr_vlog(env, "variable ptr stack access\n"); |
---|
.. | .. |
---|
647 | 623 | return 0; |
---|
648 | 624 | } |
---|
649 | 625 | |
---|
650 | | -static int |
---|
651 | | -nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx) |
---|
| 626 | +int nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, |
---|
| 627 | + int prev_insn_idx) |
---|
652 | 628 | { |
---|
653 | 629 | struct nfp_prog *nfp_prog = env->prog->aux->offload->dev_priv; |
---|
654 | 630 | struct nfp_insn_meta *meta = nfp_prog->verifier_meta; |
---|
655 | 631 | |
---|
656 | | - meta = nfp_bpf_goto_meta(nfp_prog, meta, insn_idx, env->prog->len); |
---|
| 632 | + meta = nfp_bpf_goto_meta(nfp_prog, meta, insn_idx); |
---|
657 | 633 | nfp_prog->verifier_meta = meta; |
---|
658 | 634 | |
---|
659 | 635 | if (!nfp_bpf_supported_opcode(meta->insn.code)) { |
---|
.. | .. |
---|
668 | 644 | return -EINVAL; |
---|
669 | 645 | } |
---|
670 | 646 | |
---|
671 | | - if (meta->insn.code == (BPF_JMP | BPF_CALL)) |
---|
672 | | - return nfp_bpf_check_call(nfp_prog, env, meta); |
---|
| 647 | + if (is_mbpf_helper_call(meta)) |
---|
| 648 | + return nfp_bpf_check_helper_call(nfp_prog, env, meta); |
---|
673 | 649 | if (meta->insn.code == (BPF_JMP | BPF_EXIT)) |
---|
674 | 650 | return nfp_bpf_check_exit(nfp_prog, env); |
---|
675 | 651 | |
---|
.. | .. |
---|
688 | 664 | return 0; |
---|
689 | 665 | } |
---|
690 | 666 | |
---|
691 | | -const struct bpf_prog_offload_ops nfp_bpf_analyzer_ops = { |
---|
692 | | - .insn_hook = nfp_verify_insn, |
---|
693 | | -}; |
---|
| 667 | +static int |
---|
| 668 | +nfp_assign_subprog_idx_and_regs(struct bpf_verifier_env *env, |
---|
| 669 | + struct nfp_prog *nfp_prog) |
---|
| 670 | +{ |
---|
| 671 | + struct nfp_insn_meta *meta; |
---|
| 672 | + int index = 0; |
---|
| 673 | + |
---|
| 674 | + list_for_each_entry(meta, &nfp_prog->insns, l) { |
---|
| 675 | + if (nfp_is_subprog_start(meta)) |
---|
| 676 | + index++; |
---|
| 677 | + meta->subprog_idx = index; |
---|
| 678 | + |
---|
| 679 | + if (meta->insn.dst_reg >= BPF_REG_6 && |
---|
| 680 | + meta->insn.dst_reg <= BPF_REG_9) |
---|
| 681 | + nfp_prog->subprog[index].needs_reg_push = 1; |
---|
| 682 | + } |
---|
| 683 | + |
---|
| 684 | + if (index + 1 != nfp_prog->subprog_cnt) { |
---|
| 685 | + pr_vlog(env, "BUG: number of processed BPF functions is not consistent (processed %d, expected %d)\n", |
---|
| 686 | + index + 1, nfp_prog->subprog_cnt); |
---|
| 687 | + return -EFAULT; |
---|
| 688 | + } |
---|
| 689 | + |
---|
| 690 | + return 0; |
---|
| 691 | +} |
---|
| 692 | + |
---|
| 693 | +static unsigned int nfp_bpf_get_stack_usage(struct nfp_prog *nfp_prog) |
---|
| 694 | +{ |
---|
| 695 | + struct nfp_insn_meta *meta = nfp_prog_first_meta(nfp_prog); |
---|
| 696 | + unsigned int max_depth = 0, depth = 0, frame = 0; |
---|
| 697 | + struct nfp_insn_meta *ret_insn[MAX_CALL_FRAMES]; |
---|
| 698 | + unsigned short frame_depths[MAX_CALL_FRAMES]; |
---|
| 699 | + unsigned short ret_prog[MAX_CALL_FRAMES]; |
---|
| 700 | + unsigned short idx = meta->subprog_idx; |
---|
| 701 | + |
---|
| 702 | + /* Inspired from check_max_stack_depth() from kernel verifier. |
---|
| 703 | + * Starting from main subprogram, walk all instructions and recursively |
---|
| 704 | + * walk all callees that given subprogram can call. Since recursion is |
---|
| 705 | + * prevented by the kernel verifier, this algorithm only needs a local |
---|
| 706 | + * stack of MAX_CALL_FRAMES to remember callsites. |
---|
| 707 | + */ |
---|
| 708 | +process_subprog: |
---|
| 709 | + frame_depths[frame] = nfp_prog->subprog[idx].stack_depth; |
---|
| 710 | + frame_depths[frame] = round_up(frame_depths[frame], STACK_FRAME_ALIGN); |
---|
| 711 | + depth += frame_depths[frame]; |
---|
| 712 | + max_depth = max(max_depth, depth); |
---|
| 713 | + |
---|
| 714 | +continue_subprog: |
---|
| 715 | + for (; meta != nfp_prog_last_meta(nfp_prog) && meta->subprog_idx == idx; |
---|
| 716 | + meta = nfp_meta_next(meta)) { |
---|
| 717 | + if (!is_mbpf_pseudo_call(meta)) |
---|
| 718 | + continue; |
---|
| 719 | + |
---|
| 720 | + /* We found a call to a subprogram. Remember instruction to |
---|
| 721 | + * return to and subprog id. |
---|
| 722 | + */ |
---|
| 723 | + ret_insn[frame] = nfp_meta_next(meta); |
---|
| 724 | + ret_prog[frame] = idx; |
---|
| 725 | + |
---|
| 726 | + /* Find the callee and start processing it. */ |
---|
| 727 | + meta = nfp_bpf_goto_meta(nfp_prog, meta, |
---|
| 728 | + meta->n + 1 + meta->insn.imm); |
---|
| 729 | + idx = meta->subprog_idx; |
---|
| 730 | + frame++; |
---|
| 731 | + goto process_subprog; |
---|
| 732 | + } |
---|
| 733 | + /* End of for() loop means the last instruction of the subprog was |
---|
| 734 | + * reached. If we popped all stack frames, return; otherwise, go on |
---|
| 735 | + * processing remaining instructions from the caller. |
---|
| 736 | + */ |
---|
| 737 | + if (frame == 0) |
---|
| 738 | + return max_depth; |
---|
| 739 | + |
---|
| 740 | + depth -= frame_depths[frame]; |
---|
| 741 | + frame--; |
---|
| 742 | + meta = ret_insn[frame]; |
---|
| 743 | + idx = ret_prog[frame]; |
---|
| 744 | + goto continue_subprog; |
---|
| 745 | +} |
---|
| 746 | + |
---|
| 747 | +static void nfp_bpf_insn_flag_zext(struct nfp_prog *nfp_prog, |
---|
| 748 | + struct bpf_insn_aux_data *aux) |
---|
| 749 | +{ |
---|
| 750 | + struct nfp_insn_meta *meta; |
---|
| 751 | + |
---|
| 752 | + list_for_each_entry(meta, &nfp_prog->insns, l) { |
---|
| 753 | + if (aux[meta->n].zext_dst) |
---|
| 754 | + meta->flags |= FLAG_INSN_DO_ZEXT; |
---|
| 755 | + } |
---|
| 756 | +} |
---|
| 757 | + |
---|
| 758 | +int nfp_bpf_finalize(struct bpf_verifier_env *env) |
---|
| 759 | +{ |
---|
| 760 | + struct bpf_subprog_info *info; |
---|
| 761 | + struct nfp_prog *nfp_prog; |
---|
| 762 | + unsigned int max_stack; |
---|
| 763 | + struct nfp_net *nn; |
---|
| 764 | + int i; |
---|
| 765 | + |
---|
| 766 | + nfp_prog = env->prog->aux->offload->dev_priv; |
---|
| 767 | + nfp_prog->subprog_cnt = env->subprog_cnt; |
---|
| 768 | + nfp_prog->subprog = kcalloc(nfp_prog->subprog_cnt, |
---|
| 769 | + sizeof(nfp_prog->subprog[0]), GFP_KERNEL); |
---|
| 770 | + if (!nfp_prog->subprog) |
---|
| 771 | + return -ENOMEM; |
---|
| 772 | + |
---|
| 773 | + nfp_assign_subprog_idx_and_regs(env, nfp_prog); |
---|
| 774 | + |
---|
| 775 | + info = env->subprog_info; |
---|
| 776 | + for (i = 0; i < nfp_prog->subprog_cnt; i++) { |
---|
| 777 | + nfp_prog->subprog[i].stack_depth = info[i].stack_depth; |
---|
| 778 | + |
---|
| 779 | + if (i == 0) |
---|
| 780 | + continue; |
---|
| 781 | + |
---|
| 782 | + /* Account for size of return address. */ |
---|
| 783 | + nfp_prog->subprog[i].stack_depth += REG_WIDTH; |
---|
| 784 | + /* Account for size of saved registers, if necessary. */ |
---|
| 785 | + if (nfp_prog->subprog[i].needs_reg_push) |
---|
| 786 | + nfp_prog->subprog[i].stack_depth += BPF_REG_SIZE * 4; |
---|
| 787 | + } |
---|
| 788 | + |
---|
| 789 | + nn = netdev_priv(env->prog->aux->offload->netdev); |
---|
| 790 | + max_stack = nn_readb(nn, NFP_NET_CFG_BPF_STACK_SZ) * 64; |
---|
| 791 | + nfp_prog->stack_size = nfp_bpf_get_stack_usage(nfp_prog); |
---|
| 792 | + if (nfp_prog->stack_size > max_stack) { |
---|
| 793 | + pr_vlog(env, "stack too large: program %dB > FW stack %dB\n", |
---|
| 794 | + nfp_prog->stack_size, max_stack); |
---|
| 795 | + return -EOPNOTSUPP; |
---|
| 796 | + } |
---|
| 797 | + |
---|
| 798 | + nfp_bpf_insn_flag_zext(nfp_prog, env->insn_aux_data); |
---|
| 799 | + return 0; |
---|
| 800 | +} |
---|
| 801 | + |
---|
| 802 | +int nfp_bpf_opt_replace_insn(struct bpf_verifier_env *env, u32 off, |
---|
| 803 | + struct bpf_insn *insn) |
---|
| 804 | +{ |
---|
| 805 | + struct nfp_prog *nfp_prog = env->prog->aux->offload->dev_priv; |
---|
| 806 | + struct bpf_insn_aux_data *aux_data = env->insn_aux_data; |
---|
| 807 | + struct nfp_insn_meta *meta = nfp_prog->verifier_meta; |
---|
| 808 | + |
---|
| 809 | + meta = nfp_bpf_goto_meta(nfp_prog, meta, aux_data[off].orig_idx); |
---|
| 810 | + nfp_prog->verifier_meta = meta; |
---|
| 811 | + |
---|
| 812 | + /* conditional jump to jump conversion */ |
---|
| 813 | + if (is_mbpf_cond_jump(meta) && |
---|
| 814 | + insn->code == (BPF_JMP | BPF_JA | BPF_K)) { |
---|
| 815 | + unsigned int tgt_off; |
---|
| 816 | + |
---|
| 817 | + tgt_off = off + insn->off + 1; |
---|
| 818 | + |
---|
| 819 | + if (!insn->off) { |
---|
| 820 | + meta->jmp_dst = list_next_entry(meta, l); |
---|
| 821 | + meta->jump_neg_op = false; |
---|
| 822 | + } else if (meta->jmp_dst->n != aux_data[tgt_off].orig_idx) { |
---|
| 823 | + pr_vlog(env, "branch hard wire at %d changes target %d -> %d\n", |
---|
| 824 | + off, meta->jmp_dst->n, |
---|
| 825 | + aux_data[tgt_off].orig_idx); |
---|
| 826 | + return -EINVAL; |
---|
| 827 | + } |
---|
| 828 | + return 0; |
---|
| 829 | + } |
---|
| 830 | + |
---|
| 831 | + pr_vlog(env, "unsupported instruction replacement %hhx -> %hhx\n", |
---|
| 832 | + meta->insn.code, insn->code); |
---|
| 833 | + return -EINVAL; |
---|
| 834 | +} |
---|
| 835 | + |
---|
| 836 | +int nfp_bpf_opt_remove_insns(struct bpf_verifier_env *env, u32 off, u32 cnt) |
---|
| 837 | +{ |
---|
| 838 | + struct nfp_prog *nfp_prog = env->prog->aux->offload->dev_priv; |
---|
| 839 | + struct bpf_insn_aux_data *aux_data = env->insn_aux_data; |
---|
| 840 | + struct nfp_insn_meta *meta = nfp_prog->verifier_meta; |
---|
| 841 | + unsigned int i; |
---|
| 842 | + |
---|
| 843 | + meta = nfp_bpf_goto_meta(nfp_prog, meta, aux_data[off].orig_idx); |
---|
| 844 | + |
---|
| 845 | + for (i = 0; i < cnt; i++) { |
---|
| 846 | + if (WARN_ON_ONCE(&meta->l == &nfp_prog->insns)) |
---|
| 847 | + return -EINVAL; |
---|
| 848 | + |
---|
| 849 | + /* doesn't count if it already has the flag */ |
---|
| 850 | + if (meta->flags & FLAG_INSN_SKIP_VERIFIER_OPT) |
---|
| 851 | + i--; |
---|
| 852 | + |
---|
| 853 | + meta->flags |= FLAG_INSN_SKIP_VERIFIER_OPT; |
---|
| 854 | + meta = list_next_entry(meta, l); |
---|
| 855 | + } |
---|
| 856 | + |
---|
| 857 | + return 0; |
---|
| 858 | +} |
---|