1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2019 Facebook */
3 
4 #include <test_progs.h>
5 #include <sys/mman.h>
6 #include <sys/utsname.h>
7 #include <linux/version.h>
8 #include "test_core_extern.skel.h"
9 
10 static uint32_t get_kernel_version(void)
11 {
12 	uint32_t major, minor, patch;
13 	struct utsname info;
14 
15 	uname(&info);
16 	if (sscanf(info.release, "%u.%u.%u", &major, &minor, &patch) != 3)
17 		return 0;
18 	return KERNEL_VERSION(major, minor, patch);
19 }
20 
21 #define CFG "CONFIG_BPF_SYSCALL=n\n"
22 
23 static struct test_case {
24 	const char *name;
25 	const char *cfg;
26 	bool fails;
27 	struct test_core_extern__data data;
28 } test_cases[] = {
29 	{ .name = "default search path", .data = { .bpf_syscall = true } },
30 	{
31 		.name = "custom values",
32 		.cfg = "CONFIG_BPF_SYSCALL=n\n"
33 		       "CONFIG_TRISTATE=m\n"
34 		       "CONFIG_BOOL=y\n"
35 		       "CONFIG_CHAR=100\n"
36 		       "CONFIG_USHORT=30000\n"
37 		       "CONFIG_INT=123456\n"
38 		       "CONFIG_ULONG=0xDEADBEEFC0DE\n"
39 		       "CONFIG_STR=\"abracad\"\n"
40 		       "CONFIG_MISSING=0",
41 		.data = {
42 			.unkn_virt_val = 0,
43 			.bpf_syscall = false,
44 			.tristate_val = TRI_MODULE,
45 			.bool_val = true,
46 			.char_val = 100,
47 			.ushort_val = 30000,
48 			.int_val = 123456,
49 			.ulong_val = 0xDEADBEEFC0DE,
50 			.str_val = "abracad",
51 		},
52 	},
53 	/* TRISTATE */
54 	{ .name = "tristate (y)", .cfg = CFG"CONFIG_TRISTATE=y\n",
55 	  .data = { .tristate_val = TRI_YES } },
56 	{ .name = "tristate (n)", .cfg = CFG"CONFIG_TRISTATE=n\n",
57 	  .data = { .tristate_val = TRI_NO } },
58 	{ .name = "tristate (m)", .cfg = CFG"CONFIG_TRISTATE=m\n",
59 	  .data = { .tristate_val = TRI_MODULE } },
60 	{ .name = "tristate (int)", .fails = 1, .cfg = CFG"CONFIG_TRISTATE=1" },
61 	{ .name = "tristate (bad)", .fails = 1, .cfg = CFG"CONFIG_TRISTATE=M" },
62 	/* BOOL */
63 	{ .name = "bool (y)", .cfg = CFG"CONFIG_BOOL=y\n",
64 	  .data = { .bool_val = true } },
65 	{ .name = "bool (n)", .cfg = CFG"CONFIG_BOOL=n\n",
66 	  .data = { .bool_val = false } },
67 	{ .name = "bool (tristate)", .fails = 1, .cfg = CFG"CONFIG_BOOL=m" },
68 	{ .name = "bool (int)", .fails = 1, .cfg = CFG"CONFIG_BOOL=1" },
69 	/* CHAR */
70 	{ .name = "char (tristate)", .cfg = CFG"CONFIG_CHAR=m\n",
71 	  .data = { .char_val = 'm' } },
72 	{ .name = "char (bad)", .fails = 1, .cfg = CFG"CONFIG_CHAR=q\n" },
73 	{ .name = "char (empty)", .fails = 1, .cfg = CFG"CONFIG_CHAR=\n" },
74 	{ .name = "char (str)", .fails = 1, .cfg = CFG"CONFIG_CHAR=\"y\"\n" },
75 	/* STRING */
76 	{ .name = "str (empty)", .cfg = CFG"CONFIG_STR=\"\"\n",
77 	  .data = { .str_val = "\0\0\0\0\0\0\0" } },
78 	{ .name = "str (padded)", .cfg = CFG"CONFIG_STR=\"abra\"\n",
79 	  .data = { .str_val = "abra\0\0\0" } },
80 	{ .name = "str (too long)", .cfg = CFG"CONFIG_STR=\"abracada\"\n",
81 	  .data = { .str_val = "abracad" } },
82 	{ .name = "str (no value)", .fails = 1, .cfg = CFG"CONFIG_STR=\n" },
83 	{ .name = "str (bad value)", .fails = 1, .cfg = CFG"CONFIG_STR=bla\n" },
84 	/* INTEGERS */
85 	{
86 		.name = "integer forms",
87 		.cfg = CFG
88 		       "CONFIG_CHAR=0xA\n"
89 		       "CONFIG_USHORT=0462\n"
90 		       "CONFIG_INT=-100\n"
91 		       "CONFIG_ULONG=+1000000000000",
92 		.data = {
93 			.char_val = 0xA,
94 			.ushort_val = 0462,
95 			.int_val = -100,
96 			.ulong_val = 1000000000000,
97 		},
98 	},
99 	{ .name = "int (bad)", .fails = 1, .cfg = CFG"CONFIG_INT=abc" },
100 	{ .name = "int (str)", .fails = 1, .cfg = CFG"CONFIG_INT=\"abc\"" },
101 	{ .name = "int (empty)", .fails = 1, .cfg = CFG"CONFIG_INT=" },
102 	{ .name = "int (mixed)", .fails = 1, .cfg = CFG"CONFIG_INT=123abc" },
103 	{ .name = "int (max)", .cfg = CFG"CONFIG_INT=2147483647",
104 	  .data = { .int_val = 2147483647 } },
105 	{ .name = "int (min)", .cfg = CFG"CONFIG_INT=-2147483648",
106 	  .data = { .int_val = -2147483648 } },
107 	{ .name = "int (max+1)", .fails = 1, .cfg = CFG"CONFIG_INT=2147483648" },
108 	{ .name = "int (min-1)", .fails = 1, .cfg = CFG"CONFIG_INT=-2147483649" },
109 	{ .name = "ushort (max)", .cfg = CFG"CONFIG_USHORT=65535",
110 	  .data = { .ushort_val = 65535 } },
111 	{ .name = "ushort (min)", .cfg = CFG"CONFIG_USHORT=0",
112 	  .data = { .ushort_val = 0 } },
113 	{ .name = "ushort (max+1)", .fails = 1, .cfg = CFG"CONFIG_USHORT=65536" },
114 	{ .name = "ushort (min-1)", .fails = 1, .cfg = CFG"CONFIG_USHORT=-1" },
115 	{ .name = "u64 (max)", .cfg = CFG"CONFIG_ULONG=0xffffffffffffffff",
116 	  .data = { .ulong_val = 0xffffffffffffffff } },
117 	{ .name = "u64 (min)", .cfg = CFG"CONFIG_ULONG=0",
118 	  .data = { .ulong_val = 0 } },
119 	{ .name = "u64 (max+1)", .fails = 1, .cfg = CFG"CONFIG_ULONG=0x10000000000000000" },
120 };
121 
122 void test_core_extern(void)
123 {
124 	const uint32_t kern_ver = get_kernel_version();
125 	int err, i, j;
126 	struct test_core_extern *skel = NULL;
127 	uint64_t *got, *exp;
128 	int n = sizeof(*skel->data) / sizeof(uint64_t);
129 
130 	for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
131 		struct test_case *t = &test_cases[i];
132 		DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
133 			.kconfig = t->cfg,
134 		);
135 
136 		if (!test__start_subtest(t->name))
137 			continue;
138 
139 		skel = test_core_extern__open_opts(&opts);
140 		if (!ASSERT_OK_PTR(skel, "skel_open"))
141 			goto cleanup;
142 		err = test_core_extern__load(skel);
143 		if (t->fails) {
144 			ASSERT_ERR(err, "skel_load_should_fail");
145 			goto cleanup;
146 		} else if (!ASSERT_OK(err, "skel_load")) {
147 			goto cleanup;
148 		}
149 		err = test_core_extern__attach(skel);
150 		if (!ASSERT_OK(err, "attach_raw_tp"))
151 			goto cleanup;
152 
153 		usleep(1);
154 
155 		t->data.kern_ver = kern_ver;
156 		t->data.missing_val = 0xDEADC0DE;
157 		got = (uint64_t *)skel->data;
158 		exp = (uint64_t *)&t->data;
159 		for (j = 0; j < n; j++) {
160 			ASSERT_EQ(got[j], exp[j], "result");
161 		}
162 cleanup:
163 		test_core_extern__destroy(skel);
164 		skel = NULL;
165 	}
166 }
167