xref: /openbmc/libmctp/tests/test_core.c (revision 69f545f7)
1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2 
3 #define _GNU_SOURCE
4 
5 #ifdef NDEBUG
6 #undef NDEBUG
7 #endif
8 
9 #if HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12 
13 #include <assert.h>
14 #include <fcntl.h>
15 #include <stdbool.h>
16 #include <stdint.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 
22 #include "libmctp-alloc.h"
23 #include "libmctp-log.h"
24 #include "range.h"
25 #include "test-utils.h"
26 
27 #define TEST_DEST_EID 9
28 #define TEST_SRC_EID  10
29 
30 #ifndef ARRAY_SIZE
31 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
32 #endif
33 
34 #define __unused __attribute__((unused))
35 
36 #define MAX_PAYLOAD_SIZE 50000
37 
38 struct pktbuf {
39 	struct mctp_hdr hdr;
40 	uint8_t *payload;
41 };
42 
43 struct test_params {
44 	bool seen;
45 	size_t message_size;
46 };
47 
48 static void rx_message(uint8_t eid __unused, void *data, void *msg __unused,
49 		       size_t len)
50 {
51 	struct test_params *param = (struct test_params *)data;
52 
53 	mctp_prdebug("MCTP message received: len %zd", len);
54 
55 	param->seen = true;
56 	param->message_size = len;
57 }
58 
59 static uint8_t get_sequence()
60 {
61 	static uint8_t pkt_seq = 0;
62 
63 	return (pkt_seq++ % 4);
64 }
65 
66 static uint8_t get_tag()
67 {
68 	static uint8_t tag = 0;
69 
70 	return (tag++ % 8);
71 }
72 
73 /*
74  * receive_pktbuf bypasses all bindings and directly invokes mctp_bus_rx.
75  * This is necessary in order invoke test cases on the core functionality.
76  * The memory allocated for the mctp packet is capped at MCTP_BTU
77  * size, however, the mimiced rx pkt still retains the len parameter.
78  * This allows to mimic packets larger than a sane memory allocator can
79  * provide.
80  */
81 static void receive_ptkbuf(struct mctp_binding_test *binding,
82 			   const struct pktbuf *pktbuf, size_t len)
83 {
84 	size_t alloc_size = MIN((size_t)MCTP_BTU, len);
85 	struct mctp_pktbuf *rx_pkt;
86 
87 	rx_pkt = __mctp_alloc(sizeof(*rx_pkt) + MCTP_PACKET_SIZE(alloc_size));
88 	assert(rx_pkt);
89 
90 	/* Preserve passed len parameter */
91 	rx_pkt->size = MCTP_PACKET_SIZE(len);
92 	rx_pkt->start = 0;
93 	rx_pkt->end = MCTP_PACKET_SIZE(len);
94 	rx_pkt->mctp_hdr_off = 0;
95 	rx_pkt->next = NULL;
96 	memcpy(rx_pkt->data, &pktbuf->hdr, sizeof(pktbuf->hdr));
97 	memcpy(rx_pkt->data + sizeof(pktbuf->hdr), pktbuf->payload, alloc_size);
98 
99 	mctp_bus_rx((struct mctp_binding *)binding, rx_pkt);
100 }
101 
102 static void receive_one_fragment(struct mctp_binding_test *binding,
103 				uint8_t *payload, size_t fragment_size,
104 				uint8_t flags_seq_tag, struct pktbuf *pktbuf)
105 {
106 	pktbuf->hdr.flags_seq_tag = flags_seq_tag;
107 	pktbuf->payload = payload;
108 	receive_ptkbuf(binding, pktbuf, fragment_size);
109 }
110 
111 static void receive_two_fragment_message(struct mctp_binding_test *binding,
112 					uint8_t *payload,
113 					size_t fragment1_size,
114 					size_t fragment2_size,
115 					struct pktbuf *pktbuf)
116 {
117 	uint8_t tag = MCTP_HDR_FLAG_TO | get_tag();
118 	uint8_t flags_seq_tag;
119 
120 	flags_seq_tag = MCTP_HDR_FLAG_SOM |
121 			(get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
122 	receive_one_fragment(binding, payload, fragment1_size, flags_seq_tag,
123 			pktbuf);
124 
125 	flags_seq_tag = MCTP_HDR_FLAG_EOM |
126 			(get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
127 	receive_one_fragment(binding, payload + fragment1_size, fragment2_size,
128 			flags_seq_tag, pktbuf);
129 }
130 
131 static void mctp_core_test_simple_rx()
132 {
133 	struct mctp *mctp = NULL;
134 	struct mctp_binding_test *binding = NULL;
135 	struct test_params test_param;
136 	uint8_t test_payload[2 * MCTP_BTU];
137 	struct pktbuf pktbuf;
138 
139 	memset(test_payload, 0, sizeof(test_payload));
140 	test_param.seen = false;
141 	test_param.message_size = 0;
142 	mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID);
143 	mctp_set_rx_all(mctp, rx_message, &test_param);
144 	memset(&pktbuf, 0, sizeof(pktbuf));
145 	pktbuf.hdr.dest = TEST_DEST_EID;
146 	pktbuf.hdr.src = TEST_SRC_EID;
147 
148 	/* Receive 2 fragments of equal size */
149 	receive_two_fragment_message(binding, test_payload, MCTP_BTU, MCTP_BTU,
150 			&pktbuf);
151 
152 	assert(test_param.seen);
153 	assert(test_param.message_size == 2 * MCTP_BTU);
154 
155 	mctp_binding_test_destroy(binding);
156 	mctp_destroy(mctp);
157 }
158 
159 static void mctp_core_test_receive_equal_length_fragments()
160 {
161 	struct mctp *mctp = NULL;
162 	struct mctp_binding_test *binding = NULL;
163 	struct test_params test_param;
164 	static uint8_t test_payload[MAX_PAYLOAD_SIZE];
165 	uint8_t tag = MCTP_HDR_FLAG_TO | get_tag();
166 	struct pktbuf pktbuf;
167 	uint8_t flags_seq_tag;
168 
169 	memset(test_payload, 0, sizeof(test_payload));
170 	test_param.seen = false;
171 	test_param.message_size = 0;
172 	mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID);
173 	mctp_set_rx_all(mctp, rx_message, &test_param);
174 	memset(&pktbuf, 0, sizeof(pktbuf));
175 	pktbuf.hdr.dest = TEST_DEST_EID;
176 	pktbuf.hdr.src = TEST_SRC_EID;
177 
178 	/* Receive 3 fragments, each of size MCTP_BTU */
179 	flags_seq_tag = MCTP_HDR_FLAG_SOM |
180 			(get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
181 	receive_one_fragment(binding, test_payload, MCTP_BTU, flags_seq_tag,
182 			&pktbuf);
183 
184 	flags_seq_tag = (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
185 	receive_one_fragment(binding, test_payload + MCTP_BTU, MCTP_BTU,
186 			flags_seq_tag, &pktbuf);
187 
188 	flags_seq_tag = MCTP_HDR_FLAG_EOM |
189 			(get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
190 	receive_one_fragment(binding, test_payload + (2 * MCTP_BTU), MCTP_BTU,
191 			flags_seq_tag, &pktbuf);
192 
193 	assert(test_param.seen);
194 	assert(test_param.message_size == 3 * MCTP_BTU);
195 
196 	mctp_binding_test_destroy(binding);
197 	mctp_destroy(mctp);
198 }
199 
200 static void mctp_core_test_receive_unexpected_smaller_middle_fragment()
201 {
202 	struct mctp *mctp = NULL;
203 	struct mctp_binding_test *binding = NULL;
204 	struct test_params test_param;
205 	static uint8_t test_payload[MAX_PAYLOAD_SIZE];
206 	uint8_t tag = MCTP_HDR_FLAG_TO | get_tag();
207 	struct pktbuf pktbuf;
208 	uint8_t flags_seq_tag;
209 
210 	memset(test_payload, 0, sizeof(test_payload));
211 	test_param.seen = false;
212 	test_param.message_size = 0;
213 	mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID);
214 	mctp_set_rx_all(mctp, rx_message, &test_param);
215 	memset(&pktbuf, 0, sizeof(pktbuf));
216 	pktbuf.hdr.dest = TEST_DEST_EID;
217 	pktbuf.hdr.src = TEST_SRC_EID;
218 
219 	/* Middle fragment with size MCTP_BTU - 1 */
220 	flags_seq_tag = MCTP_HDR_FLAG_SOM |
221 			(get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
222 	receive_one_fragment(binding, test_payload, MCTP_BTU, flags_seq_tag,
223 			&pktbuf);
224 
225 	flags_seq_tag = (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
226 	receive_one_fragment(binding, test_payload + MCTP_BTU, MCTP_BTU - 1,
227 			flags_seq_tag, &pktbuf);
228 
229 	flags_seq_tag = MCTP_HDR_FLAG_EOM |
230 			(get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
231 	receive_one_fragment(binding, test_payload + (2 * MCTP_BTU), MCTP_BTU,
232 			flags_seq_tag, &pktbuf);
233 
234 	assert(!test_param.seen);
235 
236 	mctp_binding_test_destroy(binding);
237 	mctp_destroy(mctp);
238 }
239 
240 static void mctp_core_test_receive_unexpected_bigger_middle_fragment()
241 {
242 	struct mctp *mctp = NULL;
243 	struct mctp_binding_test *binding = NULL;
244 	struct test_params test_param;
245 	static uint8_t test_payload[MAX_PAYLOAD_SIZE];
246 	uint8_t tag = MCTP_HDR_FLAG_TO | get_tag();
247 	struct pktbuf pktbuf;
248 	uint8_t flags_seq_tag;
249 
250 	memset(test_payload, 0, sizeof(test_payload));
251 	test_param.seen = false;
252 	test_param.message_size = 0;
253 	mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID);
254 	mctp_set_rx_all(mctp, rx_message, &test_param);
255 	memset(&pktbuf, 0, sizeof(pktbuf));
256 	pktbuf.hdr.dest = TEST_DEST_EID;
257 	pktbuf.hdr.src = TEST_SRC_EID;
258 
259 	/* Middle fragment with size MCTP_BTU + 1 */
260 	flags_seq_tag = MCTP_HDR_FLAG_SOM |
261 			(get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
262 	receive_one_fragment(binding, test_payload, MCTP_BTU, flags_seq_tag,
263 			&pktbuf);
264 
265 	flags_seq_tag = (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
266 	receive_one_fragment(binding, test_payload + MCTP_BTU, MCTP_BTU + 1,
267 			flags_seq_tag, &pktbuf);
268 
269 	flags_seq_tag = MCTP_HDR_FLAG_EOM |
270 			(get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
271 	receive_one_fragment(binding, test_payload + (2 * MCTP_BTU), MCTP_BTU,
272 			flags_seq_tag, &pktbuf);
273 
274 	assert(!test_param.seen);
275 
276 	mctp_binding_test_destroy(binding);
277 	mctp_destroy(mctp);
278 }
279 
280 static void mctp_core_test_receive_smaller_end_fragment()
281 {
282 	struct mctp *mctp = NULL;
283 	struct mctp_binding_test *binding = NULL;
284 	struct test_params test_param;
285 	static uint8_t test_payload[MAX_PAYLOAD_SIZE];
286 	uint8_t tag = MCTP_HDR_FLAG_TO | get_tag();
287 	uint8_t end_frag_size = MCTP_BTU - 10;
288 	struct pktbuf pktbuf;
289 	uint8_t flags_seq_tag;
290 
291 	memset(test_payload, 0, sizeof(test_payload));
292 	test_param.seen = false;
293 	test_param.message_size = 0;
294 	mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID);
295 	mctp_set_rx_all(mctp, rx_message, &test_param);
296 	memset(&pktbuf, 0, sizeof(pktbuf));
297 	pktbuf.hdr.dest = TEST_DEST_EID;
298 	pktbuf.hdr.src = TEST_SRC_EID;
299 
300 	flags_seq_tag = MCTP_HDR_FLAG_SOM |
301 			(get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
302 	receive_one_fragment(binding, test_payload, MCTP_BTU, flags_seq_tag,
303 			&pktbuf);
304 
305 	flags_seq_tag = (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
306 	receive_one_fragment(binding, test_payload + MCTP_BTU, MCTP_BTU,
307 			flags_seq_tag, &pktbuf);
308 
309 	flags_seq_tag = MCTP_HDR_FLAG_EOM |
310 			(get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
311 	receive_one_fragment(binding, test_payload + (2 * MCTP_BTU),
312 			end_frag_size, flags_seq_tag, &pktbuf);
313 
314 	assert(test_param.seen);
315 	assert(test_param.message_size ==
316 			(size_t)(2 * MCTP_BTU + end_frag_size));
317 
318 	mctp_binding_test_destroy(binding);
319 	mctp_destroy(mctp);
320 }
321 
322 static void mctp_core_test_receive_bigger_end_fragment()
323 {
324 	struct mctp *mctp = NULL;
325 	struct mctp_binding_test *binding = NULL;
326 	struct test_params test_param;
327 	static uint8_t test_payload[MAX_PAYLOAD_SIZE];
328 	uint8_t tag = MCTP_HDR_FLAG_TO | get_tag();
329 	uint8_t end_frag_size = MCTP_BTU + 10;
330 	struct pktbuf pktbuf;
331 	uint8_t flags_seq_tag;
332 
333 	memset(test_payload, 0, sizeof(test_payload));
334 	test_param.seen = false;
335 	test_param.message_size = 0;
336 	mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID);
337 	mctp_set_rx_all(mctp, rx_message, &test_param);
338 	memset(&pktbuf, 0, sizeof(pktbuf));
339 	pktbuf.hdr.dest = TEST_DEST_EID;
340 	pktbuf.hdr.src = TEST_SRC_EID;
341 
342 	flags_seq_tag = MCTP_HDR_FLAG_SOM |
343 			(get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
344 	receive_one_fragment(binding, test_payload, MCTP_BTU, flags_seq_tag,
345 			&pktbuf);
346 
347 	flags_seq_tag = (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
348 	receive_one_fragment(binding, test_payload + MCTP_BTU, MCTP_BTU,
349 			flags_seq_tag, &pktbuf);
350 
351 	flags_seq_tag = MCTP_HDR_FLAG_EOM |
352 			(get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
353 	receive_one_fragment(binding, test_payload + (2 * MCTP_BTU),
354 			end_frag_size, flags_seq_tag, &pktbuf);
355 
356 	assert(!test_param.seen);
357 
358 	mctp_binding_test_destroy(binding);
359 	mctp_destroy(mctp);
360 }
361 
362 /* clang-format off */
363 #define TEST_CASE(test) { #test, test }
364 static const struct {
365 	const char *name;
366 	void (*test)(void);
367 } mctp_core_tests[] = {
368 	TEST_CASE(mctp_core_test_simple_rx),
369 	TEST_CASE(mctp_core_test_receive_equal_length_fragments),
370 	TEST_CASE(mctp_core_test_receive_unexpected_smaller_middle_fragment),
371 	TEST_CASE(mctp_core_test_receive_unexpected_bigger_middle_fragment),
372 	TEST_CASE(mctp_core_test_receive_smaller_end_fragment),
373 	TEST_CASE(mctp_core_test_receive_bigger_end_fragment),
374 };
375 /* clang-format on */
376 
377 #ifndef BUILD_ASSERT
378 #define BUILD_ASSERT(x)                                                        \
379 	do {                                                                   \
380 		(void)sizeof(char[0 - (!(x))]);                                \
381 	} while (0)
382 #endif
383 
384 int main(void)
385 {
386 	uint8_t i;
387 
388 	mctp_set_log_stdio(MCTP_LOG_DEBUG);
389 
390 	BUILD_ASSERT(ARRAY_SIZE(mctp_core_tests) < SIZE_MAX);
391 	for (i = 0; i < ARRAY_SIZE(mctp_core_tests); i++) {
392 		mctp_prlog(MCTP_LOG_DEBUG, "begin: %s",
393 				mctp_core_tests[i].name);
394 		mctp_core_tests[i].test();
395 		mctp_prlog(MCTP_LOG_DEBUG, "end: %s\n",
396 				mctp_core_tests[i].name);
397 	}
398 
399 	return 0;
400 }
401