blob: dacf197b978b69b1bfe1a40946bc0a7199ba91d0 [file] [log] [blame]
Sumanth Bhat69f545f2021-05-18 09:16:43 +00001/* 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
Andrew Jeffery5ab78252022-02-17 21:04:59 +103022#include "compiler.h"
Sumanth Bhat69f545f2021-05-18 09:16:43 +000023#include "libmctp-alloc.h"
24#include "libmctp-log.h"
25#include "range.h"
26#include "test-utils.h"
27
28#define TEST_DEST_EID 9
29#define TEST_SRC_EID 10
30
31#ifndef ARRAY_SIZE
32#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
33#endif
34
Sumanth Bhat69f545f2021-05-18 09:16:43 +000035#define MAX_PAYLOAD_SIZE 50000
36
37struct pktbuf {
38 struct mctp_hdr hdr;
39 uint8_t *payload;
40};
41
42struct test_params {
43 bool seen;
44 size_t message_size;
45};
46
47static void rx_message(uint8_t eid __unused, void *data, void *msg __unused,
48 size_t len)
49{
50 struct test_params *param = (struct test_params *)data;
51
52 mctp_prdebug("MCTP message received: len %zd", len);
53
54 param->seen = true;
55 param->message_size = len;
56}
57
58static uint8_t get_sequence()
59{
60 static uint8_t pkt_seq = 0;
61
62 return (pkt_seq++ % 4);
63}
64
65static uint8_t get_tag()
66{
67 static uint8_t tag = 0;
68
69 return (tag++ % 8);
70}
71
72/*
73 * receive_pktbuf bypasses all bindings and directly invokes mctp_bus_rx.
74 * This is necessary in order invoke test cases on the core functionality.
75 * The memory allocated for the mctp packet is capped at MCTP_BTU
76 * size, however, the mimiced rx pkt still retains the len parameter.
77 * This allows to mimic packets larger than a sane memory allocator can
78 * provide.
79 */
80static void receive_ptkbuf(struct mctp_binding_test *binding,
81 const struct pktbuf *pktbuf, size_t len)
82{
83 size_t alloc_size = MIN((size_t)MCTP_BTU, len);
84 struct mctp_pktbuf *rx_pkt;
85
86 rx_pkt = __mctp_alloc(sizeof(*rx_pkt) + MCTP_PACKET_SIZE(alloc_size));
87 assert(rx_pkt);
88
89 /* Preserve passed len parameter */
90 rx_pkt->size = MCTP_PACKET_SIZE(len);
91 rx_pkt->start = 0;
92 rx_pkt->end = MCTP_PACKET_SIZE(len);
93 rx_pkt->mctp_hdr_off = 0;
94 rx_pkt->next = NULL;
95 memcpy(rx_pkt->data, &pktbuf->hdr, sizeof(pktbuf->hdr));
96 memcpy(rx_pkt->data + sizeof(pktbuf->hdr), pktbuf->payload, alloc_size);
97
98 mctp_bus_rx((struct mctp_binding *)binding, rx_pkt);
99}
100
101static void receive_one_fragment(struct mctp_binding_test *binding,
102 uint8_t *payload, size_t fragment_size,
103 uint8_t flags_seq_tag, struct pktbuf *pktbuf)
104{
105 pktbuf->hdr.flags_seq_tag = flags_seq_tag;
106 pktbuf->payload = payload;
107 receive_ptkbuf(binding, pktbuf, fragment_size);
108}
109
110static void receive_two_fragment_message(struct mctp_binding_test *binding,
111 uint8_t *payload,
112 size_t fragment1_size,
113 size_t fragment2_size,
114 struct pktbuf *pktbuf)
115{
116 uint8_t tag = MCTP_HDR_FLAG_TO | get_tag();
117 uint8_t flags_seq_tag;
118
119 flags_seq_tag = MCTP_HDR_FLAG_SOM |
120 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
121 receive_one_fragment(binding, payload, fragment1_size, flags_seq_tag,
122 pktbuf);
123
124 flags_seq_tag = MCTP_HDR_FLAG_EOM |
125 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
126 receive_one_fragment(binding, payload + fragment1_size, fragment2_size,
127 flags_seq_tag, pktbuf);
128}
129
130static void mctp_core_test_simple_rx()
131{
132 struct mctp *mctp = NULL;
133 struct mctp_binding_test *binding = NULL;
134 struct test_params test_param;
135 uint8_t test_payload[2 * MCTP_BTU];
136 struct pktbuf pktbuf;
137
138 memset(test_payload, 0, sizeof(test_payload));
139 test_param.seen = false;
140 test_param.message_size = 0;
141 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID);
142 mctp_set_rx_all(mctp, rx_message, &test_param);
143 memset(&pktbuf, 0, sizeof(pktbuf));
144 pktbuf.hdr.dest = TEST_DEST_EID;
145 pktbuf.hdr.src = TEST_SRC_EID;
146
147 /* Receive 2 fragments of equal size */
148 receive_two_fragment_message(binding, test_payload, MCTP_BTU, MCTP_BTU,
149 &pktbuf);
150
151 assert(test_param.seen);
152 assert(test_param.message_size == 2 * MCTP_BTU);
153
154 mctp_binding_test_destroy(binding);
155 mctp_destroy(mctp);
156}
157
158static void mctp_core_test_receive_equal_length_fragments()
159{
160 struct mctp *mctp = NULL;
161 struct mctp_binding_test *binding = NULL;
162 struct test_params test_param;
163 static uint8_t test_payload[MAX_PAYLOAD_SIZE];
164 uint8_t tag = MCTP_HDR_FLAG_TO | get_tag();
165 struct pktbuf pktbuf;
166 uint8_t flags_seq_tag;
167
168 memset(test_payload, 0, sizeof(test_payload));
169 test_param.seen = false;
170 test_param.message_size = 0;
171 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID);
172 mctp_set_rx_all(mctp, rx_message, &test_param);
173 memset(&pktbuf, 0, sizeof(pktbuf));
174 pktbuf.hdr.dest = TEST_DEST_EID;
175 pktbuf.hdr.src = TEST_SRC_EID;
176
177 /* Receive 3 fragments, each of size MCTP_BTU */
178 flags_seq_tag = MCTP_HDR_FLAG_SOM |
179 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
180 receive_one_fragment(binding, test_payload, MCTP_BTU, flags_seq_tag,
181 &pktbuf);
182
183 flags_seq_tag = (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
184 receive_one_fragment(binding, test_payload + MCTP_BTU, MCTP_BTU,
185 flags_seq_tag, &pktbuf);
186
187 flags_seq_tag = MCTP_HDR_FLAG_EOM |
188 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
189 receive_one_fragment(binding, test_payload + (2 * MCTP_BTU), MCTP_BTU,
190 flags_seq_tag, &pktbuf);
191
192 assert(test_param.seen);
193 assert(test_param.message_size == 3 * MCTP_BTU);
194
195 mctp_binding_test_destroy(binding);
196 mctp_destroy(mctp);
197}
198
199static void mctp_core_test_receive_unexpected_smaller_middle_fragment()
200{
201 struct mctp *mctp = NULL;
202 struct mctp_binding_test *binding = NULL;
203 struct test_params test_param;
204 static uint8_t test_payload[MAX_PAYLOAD_SIZE];
205 uint8_t tag = MCTP_HDR_FLAG_TO | get_tag();
206 struct pktbuf pktbuf;
207 uint8_t flags_seq_tag;
208
209 memset(test_payload, 0, sizeof(test_payload));
210 test_param.seen = false;
211 test_param.message_size = 0;
212 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID);
213 mctp_set_rx_all(mctp, rx_message, &test_param);
214 memset(&pktbuf, 0, sizeof(pktbuf));
215 pktbuf.hdr.dest = TEST_DEST_EID;
216 pktbuf.hdr.src = TEST_SRC_EID;
217
218 /* Middle fragment with size MCTP_BTU - 1 */
219 flags_seq_tag = MCTP_HDR_FLAG_SOM |
220 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
221 receive_one_fragment(binding, test_payload, MCTP_BTU, flags_seq_tag,
222 &pktbuf);
223
224 flags_seq_tag = (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
225 receive_one_fragment(binding, test_payload + MCTP_BTU, MCTP_BTU - 1,
226 flags_seq_tag, &pktbuf);
227
228 flags_seq_tag = MCTP_HDR_FLAG_EOM |
229 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
230 receive_one_fragment(binding, test_payload + (2 * MCTP_BTU), MCTP_BTU,
231 flags_seq_tag, &pktbuf);
232
233 assert(!test_param.seen);
234
235 mctp_binding_test_destroy(binding);
236 mctp_destroy(mctp);
237}
238
239static void mctp_core_test_receive_unexpected_bigger_middle_fragment()
240{
241 struct mctp *mctp = NULL;
242 struct mctp_binding_test *binding = NULL;
243 struct test_params test_param;
244 static uint8_t test_payload[MAX_PAYLOAD_SIZE];
245 uint8_t tag = MCTP_HDR_FLAG_TO | get_tag();
246 struct pktbuf pktbuf;
247 uint8_t flags_seq_tag;
248
249 memset(test_payload, 0, sizeof(test_payload));
250 test_param.seen = false;
251 test_param.message_size = 0;
252 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID);
253 mctp_set_rx_all(mctp, rx_message, &test_param);
254 memset(&pktbuf, 0, sizeof(pktbuf));
255 pktbuf.hdr.dest = TEST_DEST_EID;
256 pktbuf.hdr.src = TEST_SRC_EID;
257
258 /* Middle fragment with size MCTP_BTU + 1 */
259 flags_seq_tag = MCTP_HDR_FLAG_SOM |
260 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
261 receive_one_fragment(binding, test_payload, MCTP_BTU, flags_seq_tag,
262 &pktbuf);
263
264 flags_seq_tag = (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
265 receive_one_fragment(binding, test_payload + MCTP_BTU, MCTP_BTU + 1,
266 flags_seq_tag, &pktbuf);
267
268 flags_seq_tag = MCTP_HDR_FLAG_EOM |
269 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
270 receive_one_fragment(binding, test_payload + (2 * MCTP_BTU), MCTP_BTU,
271 flags_seq_tag, &pktbuf);
272
273 assert(!test_param.seen);
274
275 mctp_binding_test_destroy(binding);
276 mctp_destroy(mctp);
277}
278
279static void mctp_core_test_receive_smaller_end_fragment()
280{
281 struct mctp *mctp = NULL;
282 struct mctp_binding_test *binding = NULL;
283 struct test_params test_param;
284 static uint8_t test_payload[MAX_PAYLOAD_SIZE];
285 uint8_t tag = MCTP_HDR_FLAG_TO | get_tag();
286 uint8_t end_frag_size = MCTP_BTU - 10;
287 struct pktbuf pktbuf;
288 uint8_t flags_seq_tag;
289
290 memset(test_payload, 0, sizeof(test_payload));
291 test_param.seen = false;
292 test_param.message_size = 0;
293 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID);
294 mctp_set_rx_all(mctp, rx_message, &test_param);
295 memset(&pktbuf, 0, sizeof(pktbuf));
296 pktbuf.hdr.dest = TEST_DEST_EID;
297 pktbuf.hdr.src = TEST_SRC_EID;
298
299 flags_seq_tag = MCTP_HDR_FLAG_SOM |
300 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
301 receive_one_fragment(binding, test_payload, MCTP_BTU, flags_seq_tag,
302 &pktbuf);
303
304 flags_seq_tag = (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
305 receive_one_fragment(binding, test_payload + MCTP_BTU, MCTP_BTU,
306 flags_seq_tag, &pktbuf);
307
308 flags_seq_tag = MCTP_HDR_FLAG_EOM |
309 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
310 receive_one_fragment(binding, test_payload + (2 * MCTP_BTU),
311 end_frag_size, flags_seq_tag, &pktbuf);
312
313 assert(test_param.seen);
314 assert(test_param.message_size ==
315 (size_t)(2 * MCTP_BTU + end_frag_size));
316
317 mctp_binding_test_destroy(binding);
318 mctp_destroy(mctp);
319}
320
321static void mctp_core_test_receive_bigger_end_fragment()
322{
323 struct mctp *mctp = NULL;
324 struct mctp_binding_test *binding = NULL;
325 struct test_params test_param;
326 static uint8_t test_payload[MAX_PAYLOAD_SIZE];
327 uint8_t tag = MCTP_HDR_FLAG_TO | get_tag();
328 uint8_t end_frag_size = MCTP_BTU + 10;
329 struct pktbuf pktbuf;
330 uint8_t flags_seq_tag;
331
332 memset(test_payload, 0, sizeof(test_payload));
333 test_param.seen = false;
334 test_param.message_size = 0;
335 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID);
336 mctp_set_rx_all(mctp, rx_message, &test_param);
337 memset(&pktbuf, 0, sizeof(pktbuf));
338 pktbuf.hdr.dest = TEST_DEST_EID;
339 pktbuf.hdr.src = TEST_SRC_EID;
340
341 flags_seq_tag = MCTP_HDR_FLAG_SOM |
342 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
343 receive_one_fragment(binding, test_payload, MCTP_BTU, flags_seq_tag,
344 &pktbuf);
345
346 flags_seq_tag = (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
347 receive_one_fragment(binding, test_payload + MCTP_BTU, MCTP_BTU,
348 flags_seq_tag, &pktbuf);
349
350 flags_seq_tag = MCTP_HDR_FLAG_EOM |
351 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
352 receive_one_fragment(binding, test_payload + (2 * MCTP_BTU),
353 end_frag_size, flags_seq_tag, &pktbuf);
354
355 assert(!test_param.seen);
356
357 mctp_binding_test_destroy(binding);
358 mctp_destroy(mctp);
359}
360
Sumanth Bhatbc79c242021-06-16 12:36:56 +0530361static void mctp_core_test_drop_large_fragments()
362{
363 struct mctp *mctp = NULL;
364 struct mctp_binding_test *binding = NULL;
365 struct test_params test_param;
366 static uint8_t test_payload[MAX_PAYLOAD_SIZE];
367 struct pktbuf pktbuf;
368
369 memset(test_payload, 0, sizeof(test_payload));
370 test_param.seen = false;
371 test_param.message_size = 0;
372 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID);
373 mctp_set_rx_all(mctp, rx_message, &test_param);
374 memset(&pktbuf, 0, sizeof(pktbuf));
375 pktbuf.hdr.dest = TEST_DEST_EID;
376 pktbuf.hdr.src = TEST_SRC_EID;
377
378 /* Receive a large payload - first fragment with MCTP_BTU bytes,
379 * 2nd fragment of SIZE_MAX */
380
381 receive_two_fragment_message(binding, test_payload, MCTP_BTU,
382 SIZE_MAX - sizeof(struct mctp_hdr), &pktbuf);
383
384 assert(!test_param.seen);
385
386 mctp_binding_test_destroy(binding);
387 mctp_destroy(mctp);
388}
389
Sumanth Bhat34d4c962021-06-16 12:50:48 +0530390static void mctp_core_test_exhaust_context_buffers()
391{
392 struct mctp *mctp = NULL;
393 struct mctp_binding_test *binding = NULL;
394 struct test_params test_param;
395 static uint8_t test_payload[MAX_PAYLOAD_SIZE];
396 uint8_t tag = MCTP_HDR_FLAG_TO | get_tag();
397 uint8_t i = 0;
398 const uint8_t max_context_buffers = 16;
399 struct pktbuf pktbuf;
400 uint8_t flags_seq_tag;
401
402 memset(test_payload, 0, sizeof(test_payload));
403 test_param.seen = false;
404 test_param.message_size = 0;
405 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID);
406 mctp_set_rx_all(mctp, rx_message, &test_param);
407 memset(&pktbuf, 0, sizeof(pktbuf));
408 pktbuf.hdr.dest = TEST_DEST_EID;
409 pktbuf.hdr.src = TEST_SRC_EID;
410
411 /* Exhaust all 16 context buffers*/
412 for (i = 0; i < max_context_buffers; i++) {
413 flags_seq_tag = MCTP_HDR_FLAG_SOM |
414 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
415 receive_one_fragment(binding, test_payload, MCTP_BTU,
416 flags_seq_tag, &pktbuf);
417
418 /* Change source EID so that different contexts are created */
419 pktbuf.hdr.src++;
420 }
421
422 /* Send a full message from a different EID */
423 pktbuf.hdr.src++;
424 receive_two_fragment_message(binding, test_payload, MCTP_BTU, MCTP_BTU,
425 &pktbuf);
426
427 /* Message assembly should fail */
428 assert(!test_param.seen);
429
430 /* Complete message assembly for one of the messages */
431 pktbuf.hdr.src -= max_context_buffers;
432 flags_seq_tag = MCTP_HDR_FLAG_EOM |
433 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag;
434 receive_one_fragment(binding, test_payload, MCTP_BTU,
435 flags_seq_tag, &pktbuf);
436
437 assert(test_param.seen);
438 assert(test_param.message_size == (2 * MCTP_BTU));
439
440 mctp_binding_test_destroy(binding);
441 mctp_destroy(mctp);
442}
443
Sumanth Bhat69f545f2021-05-18 09:16:43 +0000444/* clang-format off */
445#define TEST_CASE(test) { #test, test }
446static const struct {
447 const char *name;
448 void (*test)(void);
449} mctp_core_tests[] = {
450 TEST_CASE(mctp_core_test_simple_rx),
451 TEST_CASE(mctp_core_test_receive_equal_length_fragments),
452 TEST_CASE(mctp_core_test_receive_unexpected_smaller_middle_fragment),
453 TEST_CASE(mctp_core_test_receive_unexpected_bigger_middle_fragment),
454 TEST_CASE(mctp_core_test_receive_smaller_end_fragment),
455 TEST_CASE(mctp_core_test_receive_bigger_end_fragment),
Sumanth Bhatbc79c242021-06-16 12:36:56 +0530456 TEST_CASE(mctp_core_test_drop_large_fragments),
Sumanth Bhat34d4c962021-06-16 12:50:48 +0530457 TEST_CASE(mctp_core_test_exhaust_context_buffers),
Sumanth Bhat69f545f2021-05-18 09:16:43 +0000458};
459/* clang-format on */
460
461#ifndef BUILD_ASSERT
462#define BUILD_ASSERT(x) \
463 do { \
464 (void)sizeof(char[0 - (!(x))]); \
465 } while (0)
466#endif
467
468int main(void)
469{
470 uint8_t i;
471
472 mctp_set_log_stdio(MCTP_LOG_DEBUG);
473
474 BUILD_ASSERT(ARRAY_SIZE(mctp_core_tests) < SIZE_MAX);
475 for (i = 0; i < ARRAY_SIZE(mctp_core_tests); i++) {
476 mctp_prlog(MCTP_LOG_DEBUG, "begin: %s",
477 mctp_core_tests[i].name);
478 mctp_core_tests[i].test();
479 mctp_prlog(MCTP_LOG_DEBUG, "end: %s\n",
480 mctp_core_tests[i].name);
481 }
482
483 return 0;
484}