]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - lib/test_bpf.c
PCI: Wait 1 second between disabling VFs and clearing NumVFs
[karo-tx-linux.git] / lib / test_bpf.c
index 3afddf2026c983d279fe822a0c42b7f1a65f193a..d1377390b3adda70bafe7464186e034d1fd7bd01 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/netdevice.h>
 #include <linux/if_vlan.h>
 #include <linux/random.h>
+#include <linux/highmem.h>
 
 /* General test specific settings */
 #define MAX_SUBTESTS   3
@@ -56,6 +57,7 @@
 /* Flags that can be passed to test cases */
 #define FLAG_NO_DATA           BIT(0)
 #define FLAG_EXPECTED_FAIL     BIT(1)
+#define FLAG_SKB_FRAG          BIT(2)
 
 enum {
        CLASSIC  = BIT(6),      /* Old BPF instructions only. */
@@ -81,6 +83,7 @@ struct bpf_test {
                __u32 result;
        } test[MAX_SUBTESTS];
        int (*fill_helper)(struct bpf_test *self);
+       __u8 frag_data[MAX_DATA];
 };
 
 /* Large test cases need separate allocation and fill handler. */
@@ -4490,6 +4493,602 @@ static struct bpf_test tests[] = {
                { { 1, 0xbef } },
                .fill_helper = bpf_fill_ld_abs_vlan_push_pop,
        },
+       /*
+        * LD_IND / LD_ABS on fragmented SKBs
+        */
+       {
+               "LD_IND byte frag",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0x40),
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_B, 0x0),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_SKB_FRAG,
+               { },
+               { {0x40, 0x42} },
+               .frag_data = {
+                       0x42, 0x00, 0x00, 0x00,
+                       0x43, 0x44, 0x00, 0x00,
+                       0x21, 0x07, 0x19, 0x83,
+               },
+       },
+       {
+               "LD_IND halfword frag",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0x40),
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_H, 0x4),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_SKB_FRAG,
+               { },
+               { {0x40, 0x4344} },
+               .frag_data = {
+                       0x42, 0x00, 0x00, 0x00,
+                       0x43, 0x44, 0x00, 0x00,
+                       0x21, 0x07, 0x19, 0x83,
+               },
+       },
+       {
+               "LD_IND word frag",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0x40),
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_W, 0x8),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_SKB_FRAG,
+               { },
+               { {0x40, 0x21071983} },
+               .frag_data = {
+                       0x42, 0x00, 0x00, 0x00,
+                       0x43, 0x44, 0x00, 0x00,
+                       0x21, 0x07, 0x19, 0x83,
+               },
+       },
+       {
+               "LD_IND halfword mixed head/frag",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0x40),
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_H, -0x1),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_SKB_FRAG,
+               { [0x3e] = 0x25, [0x3f] = 0x05, },
+               { {0x40, 0x0519} },
+               .frag_data = { 0x19, 0x82 },
+       },
+       {
+               "LD_IND word mixed head/frag",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0x40),
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_W, -0x2),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_SKB_FRAG,
+               { [0x3e] = 0x25, [0x3f] = 0x05, },
+               { {0x40, 0x25051982} },
+               .frag_data = { 0x19, 0x82 },
+       },
+       {
+               "LD_ABS byte frag",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_ABS | BPF_B, 0x40),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_SKB_FRAG,
+               { },
+               { {0x40, 0x42} },
+               .frag_data = {
+                       0x42, 0x00, 0x00, 0x00,
+                       0x43, 0x44, 0x00, 0x00,
+                       0x21, 0x07, 0x19, 0x83,
+               },
+       },
+       {
+               "LD_ABS halfword frag",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_ABS | BPF_H, 0x44),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_SKB_FRAG,
+               { },
+               { {0x40, 0x4344} },
+               .frag_data = {
+                       0x42, 0x00, 0x00, 0x00,
+                       0x43, 0x44, 0x00, 0x00,
+                       0x21, 0x07, 0x19, 0x83,
+               },
+       },
+       {
+               "LD_ABS word frag",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0x48),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_SKB_FRAG,
+               { },
+               { {0x40, 0x21071983} },
+               .frag_data = {
+                       0x42, 0x00, 0x00, 0x00,
+                       0x43, 0x44, 0x00, 0x00,
+                       0x21, 0x07, 0x19, 0x83,
+               },
+       },
+       {
+               "LD_ABS halfword mixed head/frag",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_ABS | BPF_H, 0x3f),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_SKB_FRAG,
+               { [0x3e] = 0x25, [0x3f] = 0x05, },
+               { {0x40, 0x0519} },
+               .frag_data = { 0x19, 0x82 },
+       },
+       {
+               "LD_ABS word mixed head/frag",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0x3e),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_SKB_FRAG,
+               { [0x3e] = 0x25, [0x3f] = 0x05, },
+               { {0x40, 0x25051982} },
+               .frag_data = { 0x19, 0x82 },
+       },
+       /*
+        * LD_IND / LD_ABS on non fragmented SKBs
+        */
+       {
+               /*
+                * this tests that the JIT/interpreter correctly resets X
+                * before using it in an LD_IND instruction.
+                */
+               "LD_IND byte default X",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_B, 0x1),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               { [0x1] = 0x42 },
+               { {0x40, 0x42 } },
+       },
+       {
+               "LD_IND byte positive offset",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0x3e),
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_B, 0x1),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               { [0x3c] = 0x25, [0x3d] = 0x05,  [0x3e] = 0x19, [0x3f] = 0x82 },
+               { {0x40, 0x82 } },
+       },
+       {
+               "LD_IND byte negative offset",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0x3e),
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_B, -0x1),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               { [0x3c] = 0x25, [0x3d] = 0x05,  [0x3e] = 0x19, [0x3f] = 0x82 },
+               { {0x40, 0x05 } },
+       },
+       {
+               "LD_IND halfword positive offset",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_H, 0x2),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               {
+                       [0x1c] = 0xaa, [0x1d] = 0x55,
+                       [0x1e] = 0xbb, [0x1f] = 0x66,
+                       [0x20] = 0xcc, [0x21] = 0x77,
+                       [0x22] = 0xdd, [0x23] = 0x88,
+               },
+               { {0x40, 0xdd88 } },
+       },
+       {
+               "LD_IND halfword negative offset",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_H, -0x2),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               {
+                       [0x1c] = 0xaa, [0x1d] = 0x55,
+                       [0x1e] = 0xbb, [0x1f] = 0x66,
+                       [0x20] = 0xcc, [0x21] = 0x77,
+                       [0x22] = 0xdd, [0x23] = 0x88,
+               },
+               { {0x40, 0xbb66 } },
+       },
+       {
+               "LD_IND halfword unaligned",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_H, -0x1),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               {
+                       [0x1c] = 0xaa, [0x1d] = 0x55,
+                       [0x1e] = 0xbb, [0x1f] = 0x66,
+                       [0x20] = 0xcc, [0x21] = 0x77,
+                       [0x22] = 0xdd, [0x23] = 0x88,
+               },
+               { {0x40, 0x66cc } },
+       },
+       {
+               "LD_IND word positive offset",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_W, 0x4),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               {
+                       [0x1c] = 0xaa, [0x1d] = 0x55,
+                       [0x1e] = 0xbb, [0x1f] = 0x66,
+                       [0x20] = 0xcc, [0x21] = 0x77,
+                       [0x22] = 0xdd, [0x23] = 0x88,
+                       [0x24] = 0xee, [0x25] = 0x99,
+                       [0x26] = 0xff, [0x27] = 0xaa,
+               },
+               { {0x40, 0xee99ffaa } },
+       },
+       {
+               "LD_IND word negative offset",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_W, -0x4),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               {
+                       [0x1c] = 0xaa, [0x1d] = 0x55,
+                       [0x1e] = 0xbb, [0x1f] = 0x66,
+                       [0x20] = 0xcc, [0x21] = 0x77,
+                       [0x22] = 0xdd, [0x23] = 0x88,
+                       [0x24] = 0xee, [0x25] = 0x99,
+                       [0x26] = 0xff, [0x27] = 0xaa,
+               },
+               { {0x40, 0xaa55bb66 } },
+       },
+       {
+               "LD_IND word unaligned (addr & 3 == 2)",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_W, -0x2),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               {
+                       [0x1c] = 0xaa, [0x1d] = 0x55,
+                       [0x1e] = 0xbb, [0x1f] = 0x66,
+                       [0x20] = 0xcc, [0x21] = 0x77,
+                       [0x22] = 0xdd, [0x23] = 0x88,
+                       [0x24] = 0xee, [0x25] = 0x99,
+                       [0x26] = 0xff, [0x27] = 0xaa,
+               },
+               { {0x40, 0xbb66cc77 } },
+       },
+       {
+               "LD_IND word unaligned (addr & 3 == 1)",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_W, -0x3),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               {
+                       [0x1c] = 0xaa, [0x1d] = 0x55,
+                       [0x1e] = 0xbb, [0x1f] = 0x66,
+                       [0x20] = 0xcc, [0x21] = 0x77,
+                       [0x22] = 0xdd, [0x23] = 0x88,
+                       [0x24] = 0xee, [0x25] = 0x99,
+                       [0x26] = 0xff, [0x27] = 0xaa,
+               },
+               { {0x40, 0x55bb66cc } },
+       },
+       {
+               "LD_IND word unaligned (addr & 3 == 3)",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
+                       BPF_STMT(BPF_LD | BPF_IND | BPF_W, -0x1),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               {
+                       [0x1c] = 0xaa, [0x1d] = 0x55,
+                       [0x1e] = 0xbb, [0x1f] = 0x66,
+                       [0x20] = 0xcc, [0x21] = 0x77,
+                       [0x22] = 0xdd, [0x23] = 0x88,
+                       [0x24] = 0xee, [0x25] = 0x99,
+                       [0x26] = 0xff, [0x27] = 0xaa,
+               },
+               { {0x40, 0x66cc77dd } },
+       },
+       {
+               "LD_ABS byte",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_ABS | BPF_B, 0x20),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               {
+                       [0x1c] = 0xaa, [0x1d] = 0x55,
+                       [0x1e] = 0xbb, [0x1f] = 0x66,
+                       [0x20] = 0xcc, [0x21] = 0x77,
+                       [0x22] = 0xdd, [0x23] = 0x88,
+                       [0x24] = 0xee, [0x25] = 0x99,
+                       [0x26] = 0xff, [0x27] = 0xaa,
+               },
+               { {0x40, 0xcc } },
+       },
+       {
+               "LD_ABS halfword",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_ABS | BPF_H, 0x22),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               {
+                       [0x1c] = 0xaa, [0x1d] = 0x55,
+                       [0x1e] = 0xbb, [0x1f] = 0x66,
+                       [0x20] = 0xcc, [0x21] = 0x77,
+                       [0x22] = 0xdd, [0x23] = 0x88,
+                       [0x24] = 0xee, [0x25] = 0x99,
+                       [0x26] = 0xff, [0x27] = 0xaa,
+               },
+               { {0x40, 0xdd88 } },
+       },
+       {
+               "LD_ABS halfword unaligned",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_ABS | BPF_H, 0x25),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               {
+                       [0x1c] = 0xaa, [0x1d] = 0x55,
+                       [0x1e] = 0xbb, [0x1f] = 0x66,
+                       [0x20] = 0xcc, [0x21] = 0x77,
+                       [0x22] = 0xdd, [0x23] = 0x88,
+                       [0x24] = 0xee, [0x25] = 0x99,
+                       [0x26] = 0xff, [0x27] = 0xaa,
+               },
+               { {0x40, 0x99ff } },
+       },
+       {
+               "LD_ABS word",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0x1c),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               {
+                       [0x1c] = 0xaa, [0x1d] = 0x55,
+                       [0x1e] = 0xbb, [0x1f] = 0x66,
+                       [0x20] = 0xcc, [0x21] = 0x77,
+                       [0x22] = 0xdd, [0x23] = 0x88,
+                       [0x24] = 0xee, [0x25] = 0x99,
+                       [0x26] = 0xff, [0x27] = 0xaa,
+               },
+               { {0x40, 0xaa55bb66 } },
+       },
+       {
+               "LD_ABS word unaligned (addr & 3 == 2)",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0x22),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               {
+                       [0x1c] = 0xaa, [0x1d] = 0x55,
+                       [0x1e] = 0xbb, [0x1f] = 0x66,
+                       [0x20] = 0xcc, [0x21] = 0x77,
+                       [0x22] = 0xdd, [0x23] = 0x88,
+                       [0x24] = 0xee, [0x25] = 0x99,
+                       [0x26] = 0xff, [0x27] = 0xaa,
+               },
+               { {0x40, 0xdd88ee99 } },
+       },
+       {
+               "LD_ABS word unaligned (addr & 3 == 1)",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0x21),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               {
+                       [0x1c] = 0xaa, [0x1d] = 0x55,
+                       [0x1e] = 0xbb, [0x1f] = 0x66,
+                       [0x20] = 0xcc, [0x21] = 0x77,
+                       [0x22] = 0xdd, [0x23] = 0x88,
+                       [0x24] = 0xee, [0x25] = 0x99,
+                       [0x26] = 0xff, [0x27] = 0xaa,
+               },
+               { {0x40, 0x77dd88ee } },
+       },
+       {
+               "LD_ABS word unaligned (addr & 3 == 3)",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0x23),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC,
+               {
+                       [0x1c] = 0xaa, [0x1d] = 0x55,
+                       [0x1e] = 0xbb, [0x1f] = 0x66,
+                       [0x20] = 0xcc, [0x21] = 0x77,
+                       [0x22] = 0xdd, [0x23] = 0x88,
+                       [0x24] = 0xee, [0x25] = 0x99,
+                       [0x26] = 0xff, [0x27] = 0xaa,
+               },
+               { {0x40, 0x88ee99ff } },
+       },
+       /*
+        * verify that the interpreter or JIT correctly sets A and X
+        * to 0.
+        */
+       {
+               "ADD default X",
+               .u.insns = {
+                       /*
+                        * A = 0x42
+                        * A = A + X
+                        * ret A
+                        */
+                       BPF_STMT(BPF_LD | BPF_IMM, 0x42),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_NO_DATA,
+               {},
+               { {0x1, 0x42 } },
+       },
+       {
+               "ADD default A",
+               .u.insns = {
+                       /*
+                        * A = A + 0x42
+                        * ret A
+                        */
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 0x42),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_NO_DATA,
+               {},
+               { {0x1, 0x42 } },
+       },
+       {
+               "SUB default X",
+               .u.insns = {
+                       /*
+                        * A = 0x66
+                        * A = A - X
+                        * ret A
+                        */
+                       BPF_STMT(BPF_LD | BPF_IMM, 0x66),
+                       BPF_STMT(BPF_ALU | BPF_SUB | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_NO_DATA,
+               {},
+               { {0x1, 0x66 } },
+       },
+       {
+               "SUB default A",
+               .u.insns = {
+                       /*
+                        * A = A - -0x66
+                        * ret A
+                        */
+                       BPF_STMT(BPF_ALU | BPF_SUB | BPF_K, -0x66),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_NO_DATA,
+               {},
+               { {0x1, 0x66 } },
+       },
+       {
+               "MUL default X",
+               .u.insns = {
+                       /*
+                        * A = 0x42
+                        * A = A * X
+                        * ret A
+                        */
+                       BPF_STMT(BPF_LD | BPF_IMM, 0x42),
+                       BPF_STMT(BPF_ALU | BPF_MUL | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_NO_DATA,
+               {},
+               { {0x1, 0x0 } },
+       },
+       {
+               "MUL default A",
+               .u.insns = {
+                       /*
+                        * A = A * 0x66
+                        * ret A
+                        */
+                       BPF_STMT(BPF_ALU | BPF_MUL | BPF_K, 0x66),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_NO_DATA,
+               {},
+               { {0x1, 0x0 } },
+       },
+       {
+               "DIV default X",
+               .u.insns = {
+                       /*
+                        * A = 0x42
+                        * A = A / X ; this halt the filter execution if X is 0
+                        * ret 0x42
+                        */
+                       BPF_STMT(BPF_LD | BPF_IMM, 0x42),
+                       BPF_STMT(BPF_ALU | BPF_DIV | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 0x42),
+               },
+               CLASSIC | FLAG_NO_DATA,
+               {},
+               { {0x1, 0x0 } },
+       },
+       {
+               "DIV default A",
+               .u.insns = {
+                       /*
+                        * A = A / 1
+                        * ret A
+                        */
+                       BPF_STMT(BPF_ALU | BPF_DIV | BPF_K, 0x1),
+                       BPF_STMT(BPF_RET | BPF_A, 0x0),
+               },
+               CLASSIC | FLAG_NO_DATA,
+               {},
+               { {0x1, 0x0 } },
+       },
+       {
+               "JMP EQ default A",
+               .u.insns = {
+                       /*
+                        * cmp A, 0x0, 0, 1
+                        * ret 0x42
+                        * ret 0x66
+                        */
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0, 0, 1),
+                       BPF_STMT(BPF_RET | BPF_K, 0x42),
+                       BPF_STMT(BPF_RET | BPF_K, 0x66),
+               },
+               CLASSIC | FLAG_NO_DATA,
+               {},
+               { {0x1, 0x42 } },
+       },
+       {
+               "JMP EQ default X",
+               .u.insns = {
+                       /*
+                        * A = 0x0
+                        * cmp A, X, 0, 1
+                        * ret 0x42
+                        * ret 0x66
+                        */
+                       BPF_STMT(BPF_LD | BPF_IMM, 0x0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0x0, 0, 1),
+                       BPF_STMT(BPF_RET | BPF_K, 0x42),
+                       BPF_STMT(BPF_RET | BPF_K, 0x66),
+               },
+               CLASSIC | FLAG_NO_DATA,
+               {},
+               { {0x1, 0x42 } },
+       },
 };
 
 static struct net_device dev;
@@ -4525,6 +5124,9 @@ static struct sk_buff *populate_skb(char *buf, int size)
 
 static void *generate_test_data(struct bpf_test *test, int sub)
 {
+       struct sk_buff *skb;
+       struct page *page;
+
        if (test->aux & FLAG_NO_DATA)
                return NULL;
 
@@ -4532,7 +5134,38 @@ static void *generate_test_data(struct bpf_test *test, int sub)
         * subtests generate skbs of different sizes based on
         * the same data.
         */
-       return populate_skb(test->data, test->test[sub].data_size);
+       skb = populate_skb(test->data, test->test[sub].data_size);
+       if (!skb)
+               return NULL;
+
+       if (test->aux & FLAG_SKB_FRAG) {
+               /*
+                * when the test requires a fragmented skb, add a
+                * single fragment to the skb, filled with
+                * test->frag_data.
+                */
+               void *ptr;
+
+               page = alloc_page(GFP_KERNEL);
+
+               if (!page)
+                       goto err_kfree_skb;
+
+               ptr = kmap(page);
+               if (!ptr)
+                       goto err_free_page;
+               memcpy(ptr, test->frag_data, MAX_DATA);
+               kunmap(page);
+               skb_add_rx_frag(skb, 0, page, 0, MAX_DATA, MAX_DATA);
+       }
+
+       return skb;
+
+err_free_page:
+       __free_page(page);
+err_kfree_skb:
+       kfree_skb(skb);
+       return NULL;
 }
 
 static void release_test_data(const struct bpf_test *test, void *data)
@@ -4672,6 +5305,11 @@ static int run_one(const struct bpf_prog *fp, struct bpf_test *test)
                        break;
 
                data = generate_test_data(test, i);
+               if (!data && !(test->aux & FLAG_NO_DATA)) {
+                       pr_cont("data generation failed ");
+                       err_cnt++;
+                       break;
+               }
                ret = __run_one(fp, data, runs, &duration);
                release_test_data(test, data);
 
@@ -4687,10 +5325,73 @@ static int run_one(const struct bpf_prog *fp, struct bpf_test *test)
        return err_cnt;
 }
 
+static char test_name[64];
+module_param_string(test_name, test_name, sizeof(test_name), 0);
+
+static int test_id = -1;
+module_param(test_id, int, 0);
+
+static int test_range[2] = { 0, ARRAY_SIZE(tests) - 1 };
+module_param_array(test_range, int, NULL, 0);
+
+static __init int find_test_index(const char *test_name)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(tests); i++) {
+               if (!strcmp(tests[i].descr, test_name))
+                       return i;
+       }
+       return -1;
+}
+
 static __init int prepare_bpf_tests(void)
 {
        int i;
 
+       if (test_id >= 0) {
+               /*
+                * if a test_id was specified, use test_range to
+                * cover only that test.
+                */
+               if (test_id >= ARRAY_SIZE(tests)) {
+                       pr_err("test_bpf: invalid test_id specified.\n");
+                       return -EINVAL;
+               }
+
+               test_range[0] = test_id;
+               test_range[1] = test_id;
+       } else if (*test_name) {
+               /*
+                * if a test_name was specified, find it and setup
+                * test_range to cover only that test.
+                */
+               int idx = find_test_index(test_name);
+
+               if (idx < 0) {
+                       pr_err("test_bpf: no test named '%s' found.\n",
+                              test_name);
+                       return -EINVAL;
+               }
+               test_range[0] = idx;
+               test_range[1] = idx;
+       } else {
+               /*
+                * check that the supplied test_range is valid.
+                */
+               if (test_range[0] >= ARRAY_SIZE(tests) ||
+                   test_range[1] >= ARRAY_SIZE(tests) ||
+                   test_range[0] < 0 || test_range[1] < 0) {
+                       pr_err("test_bpf: test_range is out of bound.\n");
+                       return -EINVAL;
+               }
+
+               if (test_range[1] < test_range[0]) {
+                       pr_err("test_bpf: test_range is ending before it starts.\n");
+                       return -EINVAL;
+               }
+       }
+
        for (i = 0; i < ARRAY_SIZE(tests); i++) {
                if (tests[i].fill_helper &&
                    tests[i].fill_helper(&tests[i]) < 0)
@@ -4710,6 +5411,11 @@ static __init void destroy_bpf_tests(void)
        }
 }
 
+static bool exclude_test(int test_id)
+{
+       return test_id < test_range[0] || test_id > test_range[1];
+}
+
 static __init int test_bpf(void)
 {
        int i, err_cnt = 0, pass_cnt = 0;
@@ -4719,6 +5425,9 @@ static __init int test_bpf(void)
                struct bpf_prog *fp;
                int err;
 
+               if (exclude_test(i))
+                       continue;
+
                pr_info("#%d %s ", i, tests[i].descr);
 
                fp = generate_filter(i, &err);