1/*	$NetBSD: t_extmem.c,v 1.2 2017/01/13 21:30:42 christos Exp $ */
2
3/*-
4 * Copyright (c) 2014 Alexander Nasonov.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in
15 *    the documentation and/or other materials provided with the
16 *    distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__RCSID("$NetBSD: t_extmem.c,v 1.2 2017/01/13 21:30:42 christos Exp $");
34
35#include <stdint.h>
36#include <string.h>
37
38#define __BPF_PRIVATE
39#include <net/bpf.h>
40#include <net/bpfjit.h>
41
42#include "../../net/bpf/h_bpf.h"
43
44/* XXX: atf-c.h has collisions with mbuf */
45#undef m_type
46#undef m_data
47#include <atf-c.h>
48
49#include "h_macros.h"
50
51static uint32_t retM(const bpf_ctx_t *bc, bpf_args_t *args, uint32_t A);
52
53static const bpf_copfunc_t copfuncs[] = {
54	&retM
55};
56
57static const bpf_ctx_t ctx = {
58	.copfuncs = copfuncs,
59	.nfuncs = sizeof(copfuncs) / sizeof(copfuncs[0]),
60	.extwords = 4,
61	.preinited = BPF_MEMWORD_INIT(0) | BPF_MEMWORD_INIT(3),
62};
63
64static uint32_t
65retM(const bpf_ctx_t *bc, bpf_args_t *args, uint32_t A)
66{
67
68	return args->mem[(uintptr_t)args->arg];
69}
70
71
72ATF_TC(bpfjit_extmem_load_preinited);
73ATF_TC_HEAD(bpfjit_extmem_load_preinited, tc)
74{
75	atf_tc_set_md_var(tc, "descr", "Test a load of external "
76	    "pre-initialized memory");
77}
78
79ATF_TC_BODY(bpfjit_extmem_load_preinited, tc)
80{
81	static struct bpf_insn insns[] = {
82		BPF_STMT(BPF_LD+BPF_MEM, 3),
83		BPF_STMT(BPF_RET+BPF_A, 0)
84	};
85
86	bpfjit_func_t code;
87	uint8_t pkt[1] = { 0 };
88	uint32_t mem[ctx.extwords];
89
90	/* Pre-inited words. */
91	mem[0] = 0;
92	mem[3] = 3;
93
94	bpf_args_t args = {
95		.pkt = pkt,
96		.buflen = sizeof(pkt),
97		.wirelen = sizeof(pkt),
98		.mem = mem,
99	};
100
101	size_t insn_count = sizeof(insns) / sizeof(insns[0]);
102
103	RZ(rump_init());
104
105	rump_schedule();
106	code = rumpns_bpfjit_generate_code(&ctx, insns, insn_count);
107	rump_unschedule();
108	ATF_REQUIRE(code != NULL);
109
110	ATF_CHECK(code(&ctx, &args) == 3);
111
112	rump_schedule();
113	rumpns_bpfjit_free_code(code);
114	rump_unschedule();
115}
116
117ATF_TC(bpfjit_extmem_invalid_load);
118ATF_TC_HEAD(bpfjit_extmem_invalid_load, tc)
119{
120	atf_tc_set_md_var(tc, "descr", "Test that out-of-range load "
121	    "fails validation");
122}
123
124ATF_TC_BODY(bpfjit_extmem_invalid_load, tc)
125{
126	static struct bpf_insn insns[] = {
127		BPF_STMT(BPF_LD+BPF_MEM, 4),
128		BPF_STMT(BPF_RET+BPF_A, 0)
129	};
130
131	bpfjit_func_t code;
132	size_t insn_count = sizeof(insns) / sizeof(insns[0]);
133
134	RZ(rump_init());
135
136	rump_schedule();
137	code = rumpns_bpfjit_generate_code(&ctx, insns, insn_count);
138	rump_unschedule();
139	ATF_CHECK(code == NULL);
140}
141
142ATF_TC(bpfjit_extmem_store);
143ATF_TC_HEAD(bpfjit_extmem_store, tc)
144{
145	atf_tc_set_md_var(tc, "descr", "Test stores to external memory");
146}
147
148ATF_TC_BODY(bpfjit_extmem_store, tc)
149{
150	static struct bpf_insn insns[] = {
151		BPF_STMT(BPF_LD+BPF_IMM, 1),        /* A <- 1     */
152		BPF_STMT(BPF_LDX+BPF_W+BPF_IMM, 2), /* X <- 2     */
153		BPF_STMT(BPF_ST, 1),                /* M[1] <- A  */
154		BPF_STMT(BPF_ALU+BPF_ADD+BPF_X, 0), /* A <- A + X */
155		BPF_STMT(BPF_STX, 2),               /* M[2] <- X  */
156		BPF_STMT(BPF_ST, 3),                /* M[3] <- A  */
157		BPF_STMT(BPF_RET+BPF_A, 0)          /* ret A      */
158	};
159
160	bpfjit_func_t code;
161	uint8_t pkt[1] = { 0 };
162	uint32_t mem[ctx.extwords];
163
164	/* Pre-inited words. */
165	mem[0] = 0;
166	mem[3] = 7;
167
168	mem[1] = mem[2] = 0xdeadbeef;
169
170	bpf_args_t args = {
171		.pkt = pkt,
172		.buflen = sizeof(pkt),
173		.wirelen = sizeof(pkt),
174		.mem = mem,
175	};
176
177	size_t insn_count = sizeof(insns) / sizeof(insns[0]);
178
179	RZ(rump_init());
180
181	rump_schedule();
182	code = rumpns_bpfjit_generate_code(&ctx, insns, insn_count);
183	rump_unschedule();
184	ATF_REQUIRE(code != NULL);
185
186	ATF_CHECK(code(&ctx, &args) == 3);
187
188	rump_schedule();
189	rumpns_bpfjit_free_code(code);
190	rump_unschedule();
191
192	ATF_CHECK(mem[0] == 0);
193	ATF_CHECK(mem[1] == 1);
194	ATF_CHECK(mem[2] == 2);
195	ATF_CHECK(mem[3] == 3);
196}
197
198ATF_TC(bpfjit_extmem_side_effect);
199ATF_TC_HEAD(bpfjit_extmem_side_effect, tc)
200{
201	atf_tc_set_md_var(tc, "descr", "Test that ABC optimization doesn\'t "
202	    "skip stores to external memory");
203}
204
205ATF_TC_BODY(bpfjit_extmem_side_effect, tc)
206{
207	static struct bpf_insn insns[] = {
208		BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 0),  /* A <- P[0]  */
209		BPF_STMT(BPF_LDX+BPF_W+BPF_IMM, 2), /* X <- 2     */
210		BPF_STMT(BPF_ST, 1),                /* M[1] <- A  */
211		BPF_STMT(BPF_ALU+BPF_ADD+BPF_X, 0), /* A <- A + X */
212		BPF_STMT(BPF_STX, 2),               /* M[2] <- X  */
213		BPF_STMT(BPF_ST, 3),                /* M[3] <- A  */
214		BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 99), /* A <- P[99] */
215		BPF_STMT(BPF_RET+BPF_A, 0)          /* ret A      */
216	};
217
218	bpfjit_func_t code;
219	uint8_t pkt[1] = { 1 };
220	uint32_t mem[ctx.extwords];
221
222	/* Pre-inited words. */
223	mem[0] = 0;
224	mem[3] = 7;
225
226	mem[1] = mem[2] = 0xdeadbeef;
227
228	bpf_args_t args = {
229		.pkt = pkt,
230		.buflen = sizeof(pkt),
231		.wirelen = sizeof(pkt),
232		.mem = mem,
233	};
234
235	size_t insn_count = sizeof(insns) / sizeof(insns[0]);
236
237	RZ(rump_init());
238
239	rump_schedule();
240	code = rumpns_bpfjit_generate_code(&ctx, insns, insn_count);
241	rump_unschedule();
242	ATF_REQUIRE(code != NULL);
243
244	ATF_CHECK(code(&ctx, &args) == 0);
245
246	rump_schedule();
247	rumpns_bpfjit_free_code(code);
248	rump_unschedule();
249
250	ATF_CHECK(mem[0] == 0);
251	ATF_CHECK(mem[1] == 1);
252	ATF_CHECK(mem[2] == 2);
253	ATF_CHECK(mem[3] == 3);
254}
255
256ATF_TC(bpfjit_extmem_invalid_store);
257ATF_TC_HEAD(bpfjit_extmem_invalid_store, tc)
258{
259	atf_tc_set_md_var(tc, "descr", "Test that out-of-range store "
260	    "fails validation");
261}
262
263ATF_TC_BODY(bpfjit_extmem_invalid_store, tc)
264{
265	static struct bpf_insn insns[] = {
266		BPF_STMT(BPF_ST, 4),
267		BPF_STMT(BPF_RET+BPF_A, 0)
268	};
269
270	bpfjit_func_t code;
271	size_t insn_count = sizeof(insns) / sizeof(insns[0]);
272
273	RZ(rump_init());
274
275	rump_schedule();
276	code = rumpns_bpfjit_generate_code(&ctx, insns, insn_count);
277	rump_unschedule();
278	ATF_CHECK(code == NULL);
279}
280
281ATF_TC(bpfjit_cop_ret_mem);
282ATF_TC_HEAD(bpfjit_cop_ret_mem, tc)
283{
284	atf_tc_set_md_var(tc, "descr", "Test coprocessor function "
285	    "that returns a content of external memory word");
286}
287
288ATF_TC_BODY(bpfjit_cop_ret_mem, tc)
289{
290	static struct bpf_insn insns[] = {
291		BPF_STMT(BPF_LD+BPF_IMM, 13),
292		BPF_STMT(BPF_ST, 2),
293		BPF_STMT(BPF_LD+BPF_IMM, 137),
294		BPF_STMT(BPF_ST, 1),
295		BPF_STMT(BPF_MISC+BPF_COP, 0), // retM
296		BPF_STMT(BPF_RET+BPF_A, 0)
297	};
298
299	bpfjit_func_t code;
300	uint8_t pkt[1] = { 0 };
301	uint32_t mem[ctx.extwords];
302	void *arg = (void*)(uintptr_t)2;
303
304	/* Pre-inited words. */
305	mem[0] = 0;
306	mem[3] = 3;
307
308	bpf_args_t args = {
309		.pkt = pkt,
310		.buflen = sizeof(pkt),
311		.wirelen = sizeof(pkt),
312		.arg = arg,
313		.mem = mem,
314	};
315
316	size_t insn_count = sizeof(insns) / sizeof(insns[0]);
317
318	RZ(rump_init());
319
320	rump_schedule();
321	code = rumpns_bpfjit_generate_code(&ctx, insns, insn_count);
322	rump_unschedule();
323	ATF_REQUIRE(code != NULL);
324
325	ATF_CHECK(code(&ctx, &args) == 13);
326
327	rump_schedule();
328	rumpns_bpfjit_free_code(code);
329	rump_unschedule();
330}
331
332ATF_TC(bpfjit_cop_ret_preinited_mem);
333ATF_TC_HEAD(bpfjit_cop_ret_preinited_mem, tc)
334{
335	atf_tc_set_md_var(tc, "descr", "Test coprocessor function that "
336	    "returns a content of external pre-initialized memory word");
337}
338
339ATF_TC_BODY(bpfjit_cop_ret_preinited_mem, tc)
340{
341	static struct bpf_insn insns[] = {
342		BPF_STMT(BPF_LD+BPF_IMM, 13),
343		BPF_STMT(BPF_ST, 2),
344		BPF_STMT(BPF_LD+BPF_IMM, 137),
345		BPF_STMT(BPF_ST, 1),
346		BPF_STMT(BPF_MISC+BPF_COP, 0), // retM
347		BPF_STMT(BPF_RET+BPF_A, 0)
348	};
349
350	bpfjit_func_t code;
351	uint8_t pkt[1] = { 0 };
352	uint32_t mem[ctx.extwords];
353	void *arg = (void*)(uintptr_t)3;
354
355	/* Pre-inited words. */
356	mem[0] = 0;
357	mem[3] = 3;
358
359	bpf_args_t args = {
360		.pkt = pkt,
361		.buflen = sizeof(pkt),
362		.wirelen = sizeof(pkt),
363		.arg = arg,
364		.mem = mem,
365	};
366
367	size_t insn_count = sizeof(insns) / sizeof(insns[0]);
368
369	RZ(rump_init());
370
371	rump_schedule();
372	code = rumpns_bpfjit_generate_code(&ctx, insns, insn_count);
373	rump_unschedule();
374	ATF_REQUIRE(code != NULL);
375
376	ATF_CHECK(code(&ctx, &args) == 3);
377
378	rump_schedule();
379	rumpns_bpfjit_free_code(code);
380	rump_unschedule();
381}
382
383ATF_TC(bpfjit_copx_ret_mem);
384ATF_TC_HEAD(bpfjit_copx_ret_mem, tc)
385{
386	atf_tc_set_md_var(tc, "descr", "Test coprocessor function "
387	    "that returns a content of external memory word");
388}
389
390ATF_TC_BODY(bpfjit_copx_ret_mem, tc)
391{
392	static struct bpf_insn insns[] = {
393		BPF_STMT(BPF_LD+BPF_IMM, 13),
394		BPF_STMT(BPF_ST, 2),
395		BPF_STMT(BPF_LD+BPF_IMM, 137),
396		BPF_STMT(BPF_ST, 1),
397		BPF_STMT(BPF_LDX+BPF_IMM, 0), // retM
398		BPF_STMT(BPF_MISC+BPF_COPX, 0),
399		BPF_STMT(BPF_RET+BPF_A, 0)
400	};
401
402	bpfjit_func_t code;
403	uint8_t pkt[1] = { 0 };
404	uint32_t mem[ctx.extwords];
405	void *arg = (void*)(uintptr_t)2;
406
407	/* Pre-inited words. */
408	mem[0] = 0;
409	mem[3] = 3;
410
411	bpf_args_t args = {
412		.pkt = pkt,
413		.buflen = sizeof(pkt),
414		.wirelen = sizeof(pkt),
415		.arg = arg,
416		.mem = mem,
417	};
418
419	size_t insn_count = sizeof(insns) / sizeof(insns[0]);
420
421	RZ(rump_init());
422
423	rump_schedule();
424	code = rumpns_bpfjit_generate_code(&ctx, insns, insn_count);
425	rump_unschedule();
426	ATF_REQUIRE(code != NULL);
427
428	ATF_CHECK(code(&ctx, &args) == 13);
429
430	rump_schedule();
431	rumpns_bpfjit_free_code(code);
432	rump_unschedule();
433}
434
435ATF_TC(bpfjit_copx_ret_preinited_mem);
436ATF_TC_HEAD(bpfjit_copx_ret_preinited_mem, tc)
437{
438	atf_tc_set_md_var(tc, "descr", "Test coprocessor function that "
439	    "returns a content of external pre-initialized memory word");
440}
441
442ATF_TC_BODY(bpfjit_copx_ret_preinited_mem, tc)
443{
444	static struct bpf_insn insns[] = {
445		BPF_STMT(BPF_LD+BPF_IMM, 13),
446		BPF_STMT(BPF_ST, 2),
447		BPF_STMT(BPF_LD+BPF_IMM, 137),
448		BPF_STMT(BPF_ST, 1),
449		BPF_STMT(BPF_LDX+BPF_IMM, 0), // retM
450		BPF_STMT(BPF_MISC+BPF_COPX, 0),
451		BPF_STMT(BPF_RET+BPF_A, 0)
452	};
453
454	bpfjit_func_t code;
455	uint8_t pkt[1] = { 0 };
456	uint32_t mem[ctx.extwords];
457	void *arg = (void*)(uintptr_t)3;
458
459	/* Pre-inited words. */
460	mem[0] = 0;
461	mem[3] = 3;
462
463	bpf_args_t args = {
464		.pkt = pkt,
465		.buflen = sizeof(pkt),
466		.wirelen = sizeof(pkt),
467		.arg = arg,
468		.mem = mem,
469	};
470
471	size_t insn_count = sizeof(insns) / sizeof(insns[0]);
472
473	RZ(rump_init());
474
475	rump_schedule();
476	code = rumpns_bpfjit_generate_code(&ctx, insns, insn_count);
477	rump_unschedule();
478	ATF_REQUIRE(code != NULL);
479
480	ATF_CHECK(code(&ctx, &args) == 3);
481
482	rump_schedule();
483	rumpns_bpfjit_free_code(code);
484	rump_unschedule();
485}
486
487ATF_TP_ADD_TCS(tp)
488{
489
490	/*
491	 * For every new test please also add a similar test
492	 * to ../../lib/libbpfjit/t_extmem.c
493	 */
494	//ATF_TP_ADD_TC(tp, bpfjit_extmem_load_default);
495	ATF_TP_ADD_TC(tp, bpfjit_extmem_load_preinited);
496	ATF_TP_ADD_TC(tp, bpfjit_extmem_invalid_load);
497	ATF_TP_ADD_TC(tp, bpfjit_extmem_store);
498	ATF_TP_ADD_TC(tp, bpfjit_extmem_side_effect);
499	ATF_TP_ADD_TC(tp, bpfjit_extmem_invalid_store);
500	ATF_TP_ADD_TC(tp, bpfjit_cop_ret_mem);
501	ATF_TP_ADD_TC(tp, bpfjit_cop_ret_preinited_mem);
502	ATF_TP_ADD_TC(tp, bpfjit_copx_ret_mem);
503	ATF_TP_ADD_TC(tp, bpfjit_copx_ret_preinited_mem);
504
505	return atf_no_error();
506}
507