blob: 42d9187c6bdd8f1a4668b93b75beed83059485d2 [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
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
38struct pktbuf {
39 struct mctp_hdr hdr;
40 uint8_t *payload;
41};
42
43struct test_params {
44 bool seen;
45 size_t message_size;
46};
47
48static 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
59static uint8_t get_sequence()
60{
61 static uint8_t pkt_seq = 0;
62
63 return (pkt_seq++ % 4);
64}
65
66static 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 */
81static 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
102static 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
111static 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
131static 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
159static 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
200static 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
240static 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
280static 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
322static 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 }
364static 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
384int 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}