xref: /openbmc/libmctp/core.c (revision f9b99f1fb2bc05862cef37dafd05366a1ec2d13b)
1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2 
3 #include <assert.h>
4 #include <errno.h>
5 #include <stdarg.h>
6 #include <stddef.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #undef pr_fmt
13 #define pr_fmt(fmt) "core: " fmt
14 
15 #include "libmctp.h"
16 #include "libmctp-alloc.h"
17 #include "libmctp-log.h"
18 #include "libmctp-cmds.h"
19 #include "range.h"
20 #include "compiler.h"
21 #include "core-internal.h"
22 
23 #ifndef ARRAY_SIZE
24 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
25 #endif
26 
27 static int mctp_message_tx_on_bus(struct mctp_bus *bus, mctp_eid_t src,
28 				  mctp_eid_t dest, bool tag_owner,
29 				  uint8_t msg_tag, void *msg, size_t msg_len);
30 static void mctp_dealloc_tag(struct mctp_bus *bus, mctp_eid_t local,
31 			     mctp_eid_t remote, uint8_t tag);
32 
33 struct mctp_pktbuf *mctp_pktbuf_alloc(struct mctp_binding *binding, size_t len)
34 {
35 	size_t size =
36 		binding->pkt_size + binding->pkt_header + binding->pkt_trailer;
37 	if (len > size) {
38 		return NULL;
39 	}
40 
41 	void *storage = __mctp_alloc(size + sizeof(struct mctp_pktbuf));
42 	if (!storage) {
43 		return NULL;
44 	}
45 	struct mctp_pktbuf *pkt = mctp_pktbuf_init(binding, storage);
46 	pkt->alloc = true;
47 	pkt->end = pkt->start + len;
48 	return pkt;
49 }
50 
51 void mctp_pktbuf_free(struct mctp_pktbuf *pkt)
52 {
53 	if (pkt->alloc) {
54 		__mctp_free(pkt);
55 	} else {
56 		mctp_prdebug("pktbuf_free called for non-alloced");
57 	}
58 }
59 
60 struct mctp_pktbuf *mctp_pktbuf_init(struct mctp_binding *binding,
61 				     void *storage)
62 {
63 	size_t size =
64 		binding->pkt_size + binding->pkt_header + binding->pkt_trailer;
65 	struct mctp_pktbuf *buf = (struct mctp_pktbuf *)storage;
66 	buf->size = size;
67 	buf->start = binding->pkt_header;
68 	buf->end = buf->start;
69 	buf->mctp_hdr_off = buf->start;
70 	buf->alloc = false;
71 
72 	return buf;
73 }
74 
75 struct mctp_hdr *mctp_pktbuf_hdr(struct mctp_pktbuf *pkt)
76 {
77 	return (struct mctp_hdr *)(pkt->data + pkt->mctp_hdr_off);
78 }
79 
80 void *mctp_pktbuf_data(struct mctp_pktbuf *pkt)
81 {
82 	return pkt->data + pkt->mctp_hdr_off + sizeof(struct mctp_hdr);
83 }
84 
85 size_t mctp_pktbuf_size(const struct mctp_pktbuf *pkt)
86 {
87 	return pkt->end - pkt->start;
88 }
89 
90 void *mctp_pktbuf_alloc_start(struct mctp_pktbuf *pkt, size_t size)
91 {
92 	assert(size <= pkt->start);
93 	pkt->start -= size;
94 	return pkt->data + pkt->start;
95 }
96 
97 void *mctp_pktbuf_alloc_end(struct mctp_pktbuf *pkt, size_t size)
98 {
99 	void *buf;
100 
101 	assert(size <= (pkt->size - pkt->end));
102 	buf = pkt->data + pkt->end;
103 	pkt->end += size;
104 	return buf;
105 }
106 
107 int mctp_pktbuf_push(struct mctp_pktbuf *pkt, const void *data, size_t len)
108 {
109 	void *p;
110 
111 	if (pkt->end + len > pkt->size)
112 		return -1;
113 
114 	p = pkt->data + pkt->end;
115 
116 	pkt->end += len;
117 	memcpy(p, data, len);
118 
119 	return 0;
120 }
121 
122 void *mctp_pktbuf_pop(struct mctp_pktbuf *pkt, size_t len)
123 {
124 	if (len > mctp_pktbuf_size(pkt))
125 		return NULL;
126 
127 	pkt->end -= len;
128 	return pkt->data + pkt->end;
129 }
130 
131 /* Allocate a duplicate of the message and copy it */
132 static void *mctp_msg_dup(const void *msg, size_t msg_len, struct mctp *mctp)
133 {
134 	void *copy = __mctp_msg_alloc(msg_len, mctp);
135 	if (!copy) {
136 		mctp_prdebug("msg dup len %zu failed", msg_len);
137 		return NULL;
138 	}
139 
140 	memcpy(copy, msg, msg_len);
141 	return copy;
142 }
143 
144 /* Message reassembly */
145 static struct mctp_msg_ctx *mctp_msg_ctx_lookup(struct mctp *mctp, uint8_t src,
146 						uint8_t dest, uint8_t tag)
147 {
148 	unsigned int i;
149 
150 	/* @todo: better lookup, if we add support for more outstanding
151 	 * message contexts */
152 	for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) {
153 		struct mctp_msg_ctx *ctx = &mctp->msg_ctxs[i];
154 		if (ctx->buf && ctx->src == src && ctx->dest == dest &&
155 		    ctx->tag == tag)
156 			return ctx;
157 	}
158 
159 	return NULL;
160 }
161 
162 static struct mctp_msg_ctx *mctp_msg_ctx_create(struct mctp *mctp, uint8_t src,
163 						uint8_t dest, uint8_t tag)
164 {
165 	struct mctp_msg_ctx *ctx = NULL;
166 	unsigned int i;
167 
168 	for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) {
169 		struct mctp_msg_ctx *tmp = &mctp->msg_ctxs[i];
170 		if (!tmp->buf) {
171 			ctx = tmp;
172 			break;
173 		}
174 	}
175 
176 	if (!ctx)
177 		return NULL;
178 
179 	ctx->src = src;
180 	ctx->dest = dest;
181 	ctx->tag = tag;
182 
183 	ctx->buf_size = 0;
184 	ctx->buf_alloc_size = mctp->max_message_size;
185 	ctx->buf = __mctp_msg_alloc(ctx->buf_alloc_size, mctp);
186 	if (!ctx->buf) {
187 		return NULL;
188 	}
189 
190 	return ctx;
191 }
192 
193 static void mctp_msg_ctx_drop(struct mctp_bus *bus, struct mctp_msg_ctx *ctx)
194 {
195 	/* Free and mark as unused */
196 	__mctp_msg_free(ctx->buf, bus->mctp);
197 	ctx->buf = NULL;
198 }
199 
200 static void mctp_msg_ctx_reset(struct mctp_msg_ctx *ctx)
201 {
202 	ctx->buf_size = 0;
203 	ctx->fragment_size = 0;
204 }
205 
206 static int mctp_msg_ctx_add_pkt(struct mctp_msg_ctx *ctx,
207 				struct mctp_pktbuf *pkt)
208 {
209 	size_t len;
210 
211 	len = mctp_pktbuf_size(pkt) - sizeof(struct mctp_hdr);
212 
213 	if (len + ctx->buf_size < ctx->buf_size) {
214 		return -1;
215 	}
216 
217 	if (ctx->buf_size + len > ctx->buf_alloc_size) {
218 		return -1;
219 	}
220 
221 	memcpy((uint8_t *)ctx->buf + ctx->buf_size, mctp_pktbuf_data(pkt), len);
222 	ctx->buf_size += len;
223 
224 	return 0;
225 }
226 
227 /* Core API functions */
228 struct mctp *mctp_init(void)
229 {
230 	struct mctp *mctp;
231 
232 	mctp = __mctp_alloc(sizeof(*mctp));
233 
234 	if (!mctp)
235 		return NULL;
236 
237 	mctp_setup(mctp, sizeof(*mctp));
238 	return mctp;
239 }
240 
241 int mctp_setup(struct mctp *mctp, size_t struct_mctp_size)
242 {
243 	if (struct_mctp_size < sizeof(struct mctp)) {
244 		mctp_prdebug("Mismatching struct mctp");
245 		return -EINVAL;
246 	}
247 	memset(mctp, 0, sizeof(*mctp));
248 	mctp->max_message_size = MCTP_MAX_MESSAGE_SIZE;
249 	return 0;
250 }
251 
252 void mctp_set_max_message_size(struct mctp *mctp, size_t message_size)
253 {
254 	mctp->max_message_size = message_size;
255 }
256 
257 void mctp_set_capture_handler(struct mctp *mctp, mctp_capture_fn fn, void *user)
258 {
259 	mctp->capture = fn;
260 	mctp->capture_data = user;
261 }
262 
263 static void mctp_bus_destroy(struct mctp_bus *bus, struct mctp *mctp)
264 {
265 	if (bus->tx_msg) {
266 		__mctp_msg_free(bus->tx_msg, mctp);
267 		bus->tx_msg = NULL;
268 	}
269 }
270 
271 void mctp_cleanup(struct mctp *mctp)
272 {
273 	size_t i;
274 
275 	/* Cleanup message assembly contexts */
276 	static_assert(ARRAY_SIZE(mctp->msg_ctxs) < SIZE_MAX, "size");
277 	for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) {
278 		struct mctp_msg_ctx *tmp = &mctp->msg_ctxs[i];
279 		if (tmp->buf)
280 			__mctp_msg_free(tmp->buf, mctp);
281 	}
282 
283 	while (mctp->n_busses--)
284 		mctp_bus_destroy(&mctp->busses[mctp->n_busses], mctp);
285 }
286 
287 void mctp_destroy(struct mctp *mctp)
288 {
289 	mctp_cleanup(mctp);
290 	__mctp_free(mctp);
291 }
292 
293 int mctp_set_rx_all(struct mctp *mctp, mctp_rx_fn fn, void *data)
294 {
295 	mctp->message_rx = fn;
296 	mctp->message_rx_data = data;
297 	return 0;
298 }
299 
300 static struct mctp_bus *find_bus_for_eid(struct mctp *mctp, mctp_eid_t dest
301 					 __attribute__((unused)))
302 {
303 	if (mctp->n_busses == 0)
304 		return NULL;
305 
306 	/* for now, just use the first bus. For full routing support,
307 	 * we will need a table of neighbours */
308 	return &mctp->busses[0];
309 }
310 
311 int mctp_register_bus(struct mctp *mctp, struct mctp_binding *binding,
312 		      mctp_eid_t eid)
313 {
314 	int rc = 0;
315 
316 	/* todo: multiple busses */
317 	static_assert(MCTP_MAX_BUSSES >= 1, "need a bus");
318 	assert(mctp->n_busses == 0);
319 	mctp->n_busses = 1;
320 
321 	assert(binding->tx_storage);
322 
323 	memset(mctp->busses, 0, sizeof(struct mctp_bus));
324 	mctp->busses[0].mctp = mctp;
325 	mctp->busses[0].binding = binding;
326 	mctp->busses[0].eid = eid;
327 	binding->bus = &mctp->busses[0];
328 	binding->mctp = mctp;
329 	mctp->route_policy = ROUTE_ENDPOINT;
330 
331 	if (binding->start) {
332 		rc = binding->start(binding);
333 		if (rc < 0) {
334 			mctp_prerr("Failed to start binding: %d", rc);
335 			binding->bus = NULL;
336 			mctp->n_busses = 0;
337 		}
338 	}
339 
340 	return rc;
341 }
342 
343 void mctp_unregister_bus(struct mctp *mctp, struct mctp_binding *binding)
344 {
345 	/*
346 	 * We only support one bus right now; once the call completes we will
347 	 * have no more busses
348 	 */
349 	mctp->n_busses = 0;
350 	binding->mctp = NULL;
351 	binding->bus = NULL;
352 }
353 
354 int mctp_bridge_busses(struct mctp *mctp, struct mctp_binding *b1,
355 		       struct mctp_binding *b2)
356 {
357 	int rc = 0;
358 
359 	assert(b1->tx_storage);
360 	assert(b2->tx_storage);
361 
362 	assert(mctp->n_busses == 0);
363 	assert(MCTP_MAX_BUSSES >= 2);
364 	memset(mctp->busses, 0, 2 * sizeof(struct mctp_bus));
365 	mctp->n_busses = 2;
366 	mctp->busses[0].binding = b1;
367 	b1->bus = &mctp->busses[0];
368 	b1->mctp = mctp;
369 	mctp->busses[1].binding = b2;
370 	b2->bus = &mctp->busses[1];
371 	b2->mctp = mctp;
372 
373 	mctp->route_policy = ROUTE_BRIDGE;
374 
375 	if (b1->start) {
376 		rc = b1->start(b1);
377 		if (rc < 0) {
378 			mctp_prerr("Failed to start bridged bus %s: %d",
379 				   b1->name, rc);
380 			goto done;
381 		}
382 	}
383 
384 	if (b2->start) {
385 		rc = b2->start(b2);
386 		if (rc < 0) {
387 			mctp_prerr("Failed to start bridged bus %s: %d",
388 				   b2->name, rc);
389 			goto done;
390 		}
391 	}
392 
393 done:
394 	return rc;
395 }
396 
397 static inline bool mctp_ctrl_cmd_is_transport(struct mctp_ctrl_msg_hdr *hdr)
398 {
399 #pragma GCC diagnostic push
400 #pragma GCC diagnostic ignored "-Wtype-limits"
401 	return ((hdr->command_code >= MCTP_CTRL_CMD_FIRST_TRANSPORT) &&
402 		(hdr->command_code <= MCTP_CTRL_CMD_LAST_TRANSPORT));
403 #pragma GCC diagnostic pop
404 }
405 
406 static bool mctp_ctrl_handle_msg(struct mctp_bus *bus, mctp_eid_t src,
407 				 uint8_t msg_tag, bool tag_owner, void *buffer,
408 				 size_t length)
409 {
410 	struct mctp_ctrl_msg_hdr *msg_hdr = buffer;
411 
412 	/*
413 	 * Control message is received. If a transport control message handler
414 	 * is provided, it will called. If there is no dedicated handler, this
415 	 * function returns false and data can be handled by the generic
416 	 * message handler. The transport control message handler will be
417 	 * provided with messages in the command range 0xF0 - 0xFF.
418 	 */
419 	if (mctp_ctrl_cmd_is_transport(msg_hdr)) {
420 		if (bus->binding->control_rx != NULL) {
421 			/* MCTP bus binding handler */
422 			bus->binding->control_rx(src, msg_tag, tag_owner,
423 						 bus->binding->control_rx_data,
424 						 buffer, length);
425 			return true;
426 		}
427 	}
428 
429 	/*
430 	 * Command was not handled, due to lack of specific callback.
431 	 * It will be passed to regular message_rx handler.
432 	 */
433 	return false;
434 }
435 
436 static inline bool mctp_rx_dest_is_local(struct mctp_bus *bus, mctp_eid_t dest)
437 {
438 	return dest == bus->eid || dest == MCTP_EID_NULL ||
439 	       dest == MCTP_EID_BROADCAST;
440 }
441 
442 static inline bool mctp_ctrl_cmd_is_request(struct mctp_ctrl_msg_hdr *hdr)
443 {
444 	return hdr->ic_msg_type == MCTP_CTRL_HDR_MSG_TYPE &&
445 	       hdr->rq_dgram_inst & MCTP_CTRL_HDR_FLAG_REQUEST;
446 }
447 
448 /*
449  * Receive the complete MCTP message and route it.
450  * Asserts:
451  *     'buf' is not NULL.
452  */
453 static void mctp_rx(struct mctp *mctp, struct mctp_bus *bus, mctp_eid_t src,
454 		    mctp_eid_t dest, bool tag_owner, uint8_t msg_tag, void *buf,
455 		    size_t len)
456 {
457 	assert(buf != NULL);
458 
459 	if (mctp->route_policy == ROUTE_ENDPOINT &&
460 	    mctp_rx_dest_is_local(bus, dest)) {
461 		/* Note responses to allocated tags */
462 		if (!tag_owner) {
463 			mctp_dealloc_tag(bus, dest, src, msg_tag);
464 		}
465 
466 		/* Handle MCTP Control Messages: */
467 		if (len >= sizeof(struct mctp_ctrl_msg_hdr)) {
468 			struct mctp_ctrl_msg_hdr *msg_hdr = buf;
469 
470 			/*
471 			 * Identify if this is a control request message.
472 			 * See DSP0236 v1.3.0 sec. 11.5.
473 			 */
474 			if (mctp_ctrl_cmd_is_request(msg_hdr)) {
475 				bool handled;
476 				handled = mctp_ctrl_handle_msg(
477 					bus, src, msg_tag, tag_owner, buf, len);
478 				if (handled)
479 					return;
480 			}
481 		}
482 
483 		if (mctp->message_rx)
484 			mctp->message_rx(src, tag_owner, msg_tag,
485 					 mctp->message_rx_data, buf, len);
486 	}
487 
488 	if (mctp->route_policy == ROUTE_BRIDGE) {
489 		int i;
490 
491 		for (i = 0; i < mctp->n_busses; i++) {
492 			struct mctp_bus *dest_bus = &mctp->busses[i];
493 			if (dest_bus == bus)
494 				continue;
495 
496 			void *copy = mctp_msg_dup(buf, len, mctp);
497 			if (!copy) {
498 				return;
499 			}
500 
501 			mctp_message_tx_on_bus(dest_bus, src, dest, tag_owner,
502 					       msg_tag, copy, len);
503 		}
504 	}
505 }
506 
507 void mctp_bus_rx(struct mctp_binding *binding, struct mctp_pktbuf *pkt)
508 {
509 	struct mctp_bus *bus = binding->bus;
510 	struct mctp *mctp = binding->mctp;
511 	uint8_t flags, exp_seq, seq, tag;
512 	struct mctp_msg_ctx *ctx;
513 	struct mctp_hdr *hdr;
514 	bool tag_owner;
515 	size_t len;
516 	void *p;
517 	int rc;
518 
519 	assert(bus);
520 
521 	/* Drop packet if it was smaller than mctp hdr size */
522 	if (mctp_pktbuf_size(pkt) < sizeof(struct mctp_hdr))
523 		goto out;
524 
525 	if (mctp->capture)
526 		mctp->capture(pkt, MCTP_MESSAGE_CAPTURE_INCOMING,
527 			      mctp->capture_data);
528 
529 	hdr = mctp_pktbuf_hdr(pkt);
530 
531 	/* small optimisation: don't bother reassembly if we're going to
532 	 * drop the packet in mctp_rx anyway */
533 	if (mctp->route_policy == ROUTE_ENDPOINT &&
534 	    !mctp_rx_dest_is_local(bus, hdr->dest))
535 		goto out;
536 
537 	flags = hdr->flags_seq_tag & (MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM);
538 	tag = (hdr->flags_seq_tag >> MCTP_HDR_TAG_SHIFT) & MCTP_HDR_TAG_MASK;
539 	seq = (hdr->flags_seq_tag >> MCTP_HDR_SEQ_SHIFT) & MCTP_HDR_SEQ_MASK;
540 	tag_owner = (hdr->flags_seq_tag >> MCTP_HDR_TO_SHIFT) &
541 		    MCTP_HDR_TO_MASK;
542 
543 	switch (flags) {
544 	case MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM:
545 		/* single-packet message - send straight up to rx function,
546 		 * no need to create a message context */
547 		len = pkt->end - pkt->mctp_hdr_off - sizeof(struct mctp_hdr);
548 		p = mctp_msg_dup(pkt->data + pkt->mctp_hdr_off +
549 					 sizeof(struct mctp_hdr),
550 				 len, mctp);
551 		if (p) {
552 			mctp_rx(mctp, bus, hdr->src, hdr->dest, tag_owner, tag,
553 				p, len);
554 			__mctp_msg_free(p, mctp);
555 		}
556 		break;
557 
558 	case MCTP_HDR_FLAG_SOM:
559 		/* start of a new message - start the new context for
560 		 * future message reception. If an existing context is
561 		 * already present, drop it. */
562 		ctx = mctp_msg_ctx_lookup(mctp, hdr->src, hdr->dest, tag);
563 		if (ctx) {
564 			mctp_msg_ctx_reset(ctx);
565 		} else {
566 			ctx = mctp_msg_ctx_create(mctp, hdr->src, hdr->dest,
567 						  tag);
568 			/* If context creation fails due to exhaution of contexts we
569 			* can support, drop the packet */
570 			if (!ctx) {
571 				mctp_prdebug("Context buffers exhausted.");
572 				goto out;
573 			}
574 		}
575 
576 		/* Save the fragment size, subsequent middle fragments
577 		 * should of the same size */
578 		ctx->fragment_size = mctp_pktbuf_size(pkt);
579 
580 		rc = mctp_msg_ctx_add_pkt(ctx, pkt);
581 		if (rc) {
582 			mctp_msg_ctx_drop(bus, ctx);
583 		} else {
584 			ctx->last_seq = seq;
585 		}
586 
587 		break;
588 
589 	case MCTP_HDR_FLAG_EOM:
590 		ctx = mctp_msg_ctx_lookup(mctp, hdr->src, hdr->dest, tag);
591 		if (!ctx)
592 			goto out;
593 
594 		exp_seq = (ctx->last_seq + 1) % 4;
595 
596 		if (exp_seq != seq) {
597 			mctp_prdebug(
598 				"Sequence number %d does not match expected %d",
599 				seq, exp_seq);
600 			mctp_msg_ctx_drop(bus, ctx);
601 			goto out;
602 		}
603 
604 		len = mctp_pktbuf_size(pkt);
605 
606 		if (len > ctx->fragment_size) {
607 			mctp_prdebug("Unexpected fragment size. Expected"
608 				     " less than %zu, received = %zu",
609 				     ctx->fragment_size, len);
610 			mctp_msg_ctx_drop(bus, ctx);
611 			goto out;
612 		}
613 
614 		rc = mctp_msg_ctx_add_pkt(ctx, pkt);
615 		if (!rc)
616 			mctp_rx(mctp, bus, ctx->src, ctx->dest, tag_owner, tag,
617 				ctx->buf, ctx->buf_size);
618 
619 		mctp_msg_ctx_drop(bus, ctx);
620 		break;
621 
622 	case 0:
623 		/* Neither SOM nor EOM */
624 		ctx = mctp_msg_ctx_lookup(mctp, hdr->src, hdr->dest, tag);
625 		if (!ctx)
626 			goto out;
627 
628 		exp_seq = (ctx->last_seq + 1) % 4;
629 		if (exp_seq != seq) {
630 			mctp_prdebug(
631 				"Sequence number %d does not match expected %d",
632 				seq, exp_seq);
633 			mctp_msg_ctx_drop(bus, ctx);
634 			goto out;
635 		}
636 
637 		len = mctp_pktbuf_size(pkt);
638 
639 		if (len != ctx->fragment_size) {
640 			mctp_prdebug("Unexpected fragment size. Expected = %zu "
641 				     "received = %zu",
642 				     ctx->fragment_size, len);
643 			mctp_msg_ctx_drop(bus, ctx);
644 			goto out;
645 		}
646 
647 		rc = mctp_msg_ctx_add_pkt(ctx, pkt);
648 		if (rc) {
649 			mctp_msg_ctx_drop(bus, ctx);
650 			goto out;
651 		}
652 		ctx->last_seq = seq;
653 
654 		break;
655 	}
656 out:
657 	return;
658 }
659 
660 static int mctp_packet_tx(struct mctp_bus *bus, struct mctp_pktbuf *pkt)
661 {
662 	struct mctp *mctp = bus->binding->mctp;
663 
664 	if (bus->state != mctp_bus_state_tx_enabled) {
665 		mctp_prdebug("tx with bus disabled");
666 		return -1;
667 	}
668 
669 	if (mctp->capture)
670 		mctp->capture(pkt, MCTP_MESSAGE_CAPTURE_OUTGOING,
671 			      mctp->capture_data);
672 
673 	return bus->binding->tx(bus->binding, pkt);
674 }
675 
676 /* Returns a pointer to the binding's tx_storage */
677 static struct mctp_pktbuf *mctp_next_tx_pkt(struct mctp_bus *bus)
678 {
679 	if (!bus->tx_msg) {
680 		return NULL;
681 	}
682 
683 	size_t p = bus->tx_msgpos;
684 	size_t msg_len = bus->tx_msglen;
685 	size_t payload_len = msg_len - p;
686 	size_t max_payload_len = MCTP_BODY_SIZE(bus->binding->pkt_size);
687 	if (payload_len > max_payload_len)
688 		payload_len = max_payload_len;
689 
690 	struct mctp_pktbuf *pkt =
691 		mctp_pktbuf_init(bus->binding, bus->binding->tx_storage);
692 	struct mctp_hdr *hdr = mctp_pktbuf_hdr(pkt);
693 
694 	hdr->ver = bus->binding->version & 0xf;
695 	hdr->dest = bus->tx_dest;
696 	hdr->src = bus->tx_src;
697 	hdr->flags_seq_tag = (bus->tx_to << MCTP_HDR_TO_SHIFT) |
698 			     (bus->tx_tag << MCTP_HDR_TAG_SHIFT);
699 
700 	if (p == 0)
701 		hdr->flags_seq_tag |= MCTP_HDR_FLAG_SOM;
702 	if (p + payload_len >= msg_len)
703 		hdr->flags_seq_tag |= MCTP_HDR_FLAG_EOM;
704 	hdr->flags_seq_tag |= bus->tx_seq << MCTP_HDR_SEQ_SHIFT;
705 
706 	memcpy(mctp_pktbuf_data(pkt), (uint8_t *)bus->tx_msg + p, payload_len);
707 	pkt->end = pkt->start + sizeof(*hdr) + payload_len;
708 	bus->tx_pktlen = payload_len;
709 
710 	mctp_prdebug(
711 		"tx dst %d tag %d payload len %zu seq %d. msg pos %zu len %zu",
712 		hdr->dest, bus->tx_tag, payload_len, bus->tx_seq, p, msg_len);
713 
714 	return pkt;
715 }
716 
717 /* Called when a packet has successfully been sent */
718 static void mctp_tx_complete(struct mctp_bus *bus)
719 {
720 	if (!bus->tx_msg) {
721 		mctp_prdebug("tx complete no message");
722 		return;
723 	}
724 
725 	bus->tx_seq = (bus->tx_seq + 1) & MCTP_HDR_SEQ_MASK;
726 	bus->tx_msgpos += bus->tx_pktlen;
727 
728 	if (bus->tx_msgpos >= bus->tx_msglen) {
729 		__mctp_msg_free(bus->tx_msg, bus->binding->mctp);
730 		bus->tx_msg = NULL;
731 	}
732 }
733 
734 static void mctp_send_tx_queue(struct mctp_bus *bus)
735 {
736 	struct mctp_pktbuf *pkt;
737 
738 	while (bus->tx_msg && bus->state == mctp_bus_state_tx_enabled) {
739 		int rc;
740 
741 		pkt = mctp_next_tx_pkt(bus);
742 
743 		rc = mctp_packet_tx(bus, pkt);
744 		switch (rc) {
745 		/* If transmission succeded */
746 		case 0:
747 			/* Drop the packet */
748 			mctp_tx_complete(bus);
749 			break;
750 
751 		/* If the binding was busy */
752 		case -EBUSY:
753 			/* Keep the packet for next try */
754 			mctp_prdebug("tx EBUSY");
755 			return;
756 
757 		/* Some other unknown error occurred */
758 		default:
759 			/* Drop the packet */
760 			mctp_prdebug("tx drop %d", rc);
761 			mctp_tx_complete(bus);
762 			return;
763 		};
764 	}
765 }
766 
767 void mctp_binding_set_tx_enabled(struct mctp_binding *binding, bool enable)
768 {
769 	struct mctp_bus *bus = binding->bus;
770 
771 	switch (bus->state) {
772 	case mctp_bus_state_constructed:
773 		if (!enable)
774 			return;
775 
776 		if (binding->pkt_size < MCTP_PACKET_SIZE(MCTP_BTU)) {
777 			mctp_prerr(
778 				"Cannot start %s binding with invalid MTU: %zu",
779 				binding->name,
780 				MCTP_BODY_SIZE(binding->pkt_size));
781 			return;
782 		}
783 
784 		bus->state = mctp_bus_state_tx_enabled;
785 		mctp_prinfo("%s binding started", binding->name);
786 		return;
787 	case mctp_bus_state_tx_enabled:
788 		if (enable)
789 			return;
790 
791 		bus->state = mctp_bus_state_tx_disabled;
792 		mctp_prdebug("%s binding Tx disabled", binding->name);
793 		return;
794 	case mctp_bus_state_tx_disabled:
795 		if (!enable)
796 			return;
797 
798 		bus->state = mctp_bus_state_tx_enabled;
799 		mctp_prdebug("%s binding Tx enabled", binding->name);
800 		mctp_send_tx_queue(bus);
801 		return;
802 	}
803 }
804 
805 static int mctp_message_tx_on_bus(struct mctp_bus *bus, mctp_eid_t src,
806 				  mctp_eid_t dest, bool tag_owner,
807 				  uint8_t msg_tag, void *msg, size_t msg_len)
808 {
809 	size_t max_payload_len;
810 	int rc;
811 
812 	if (bus->state == mctp_bus_state_constructed) {
813 		rc = -ENXIO;
814 		goto err;
815 	}
816 
817 	if ((msg_tag & MCTP_HDR_TAG_MASK) != msg_tag) {
818 		rc = -EINVAL;
819 		goto err;
820 	}
821 
822 	max_payload_len = MCTP_BODY_SIZE(bus->binding->pkt_size);
823 
824 	{
825 		const bool valid_mtu = max_payload_len >= MCTP_BTU;
826 		assert(valid_mtu);
827 		if (!valid_mtu) {
828 			rc = -EINVAL;
829 			goto err;
830 		}
831 	}
832 
833 	mctp_prdebug(
834 		"%s: Generating packets for transmission of %zu byte message from %hhu to %hhu",
835 		__func__, msg_len, src, dest);
836 
837 	if (bus->tx_msg) {
838 		mctp_prdebug("Bus busy");
839 		rc = -EBUSY;
840 		goto err;
841 	}
842 
843 	/* Take the message to send */
844 	bus->tx_msg = msg;
845 	bus->tx_msglen = msg_len;
846 	bus->tx_msgpos = 0;
847 	/* bus->tx_seq is allowed to continue from previous message */
848 	bus->tx_src = src;
849 	bus->tx_dest = dest;
850 	bus->tx_to = tag_owner;
851 	bus->tx_tag = msg_tag;
852 
853 	mctp_send_tx_queue(bus);
854 	return 0;
855 
856 err:
857 	__mctp_msg_free(msg, bus->binding->mctp);
858 	return rc;
859 }
860 
861 int mctp_message_tx_alloced(struct mctp *mctp, mctp_eid_t eid, bool tag_owner,
862 			    uint8_t msg_tag, void *msg, size_t msg_len)
863 {
864 	struct mctp_bus *bus;
865 
866 	/* TODO: Protect against same tag being used across
867 	 * different callers */
868 	if ((msg_tag & MCTP_HDR_TAG_MASK) != msg_tag) {
869 		mctp_prerr("Incorrect message tag %u passed.", msg_tag);
870 		__mctp_msg_free(msg, mctp);
871 		return -EINVAL;
872 	}
873 
874 	bus = find_bus_for_eid(mctp, eid);
875 	if (!bus) {
876 		__mctp_msg_free(msg, mctp);
877 		return 0;
878 	}
879 
880 	return mctp_message_tx_on_bus(bus, bus->eid, eid, tag_owner, msg_tag,
881 				      msg, msg_len);
882 }
883 
884 int mctp_message_tx(struct mctp *mctp, mctp_eid_t eid, bool tag_owner,
885 		    uint8_t msg_tag, const void *msg, size_t msg_len)
886 {
887 	void *copy = mctp_msg_dup(msg, msg_len, mctp);
888 	if (!copy) {
889 		return -ENOMEM;
890 	}
891 
892 	return mctp_message_tx_alloced(mctp, eid, tag_owner, msg_tag, copy,
893 				       msg_len);
894 }
895 
896 static void mctp_dealloc_tag(struct mctp_bus *bus, mctp_eid_t local,
897 			     mctp_eid_t remote, uint8_t tag)
898 {
899 	struct mctp *mctp = bus->binding->mctp;
900 	if (local == 0 || remote == 0) {
901 		return;
902 	}
903 
904 	for (size_t i = 0; i < ARRAY_SIZE(mctp->req_tags); i++) {
905 		struct mctp_req_tag *r = &mctp->req_tags[i];
906 		if (r->local == local && r->remote == remote && r->tag == tag) {
907 			r->local = 0;
908 			r->remote = 0;
909 			r->tag = 0;
910 			return;
911 		}
912 	}
913 }
914 
915 static int mctp_alloc_tag(struct mctp *mctp, mctp_eid_t local,
916 			  mctp_eid_t remote, uint8_t *ret_tag)
917 {
918 	assert(local != 0);
919 	assert(remote != 0);
920 
921 	uint8_t used = 0;
922 	struct mctp_req_tag *spare = NULL;
923 	/* Find which tags and slots are used/spare */
924 	for (size_t i = 0; i < ARRAY_SIZE(mctp->req_tags); i++) {
925 		struct mctp_req_tag *r = &mctp->req_tags[i];
926 		if (r->local == 0) {
927 			spare = r;
928 		} else {
929 			// TODO: check timeouts
930 			if (r->local == local && r->remote == remote) {
931 				used |= 1 << r->tag;
932 			}
933 		}
934 	}
935 
936 	if (spare == NULL) {
937 		// All req_tag slots are in-use
938 		return -EBUSY;
939 	}
940 
941 	for (uint8_t t = 0; t < 8; t++) {
942 		uint8_t tag = (t + mctp->tag_round_robin) % 8;
943 		if ((used & 1 << tag) == 0) {
944 			spare->local = local;
945 			spare->remote = remote;
946 			spare->tag = tag;
947 			*ret_tag = tag;
948 			mctp->tag_round_robin = (tag + 1) % 8;
949 			return 0;
950 		}
951 	}
952 
953 	// All 8 tags are used for this src/dest pair
954 	return -EBUSY;
955 }
956 
957 int mctp_message_tx_request(struct mctp *mctp, mctp_eid_t eid, void *msg,
958 			    size_t msg_len, uint8_t *ret_alloc_msg_tag)
959 {
960 	int rc;
961 	struct mctp_bus *bus;
962 
963 	bus = find_bus_for_eid(mctp, eid);
964 	if (!bus) {
965 		__mctp_msg_free(msg, mctp);
966 		return 0;
967 	}
968 
969 	uint8_t alloc_tag;
970 	rc = mctp_alloc_tag(mctp, bus->eid, eid, &alloc_tag);
971 	if (rc) {
972 		mctp_prdebug("Failed allocating tag");
973 		__mctp_msg_free(msg, mctp);
974 		return rc;
975 	}
976 
977 	if (ret_alloc_msg_tag) {
978 		*ret_alloc_msg_tag = alloc_tag;
979 	}
980 
981 	return mctp_message_tx_alloced(mctp, eid, true, alloc_tag, msg,
982 				       msg_len);
983 }
984 
985 bool mctp_is_tx_ready(struct mctp *mctp, mctp_eid_t eid)
986 {
987 	struct mctp_bus *bus;
988 
989 	bus = find_bus_for_eid(mctp, eid);
990 	if (!bus) {
991 		return true;
992 	}
993 	return bus->tx_msg == NULL;
994 }
995 
996 void *mctp_get_alloc_ctx(struct mctp *mctp)
997 {
998 	return mctp->alloc_ctx;
999 }
1000 
1001 void mctp_set_alloc_ctx(struct mctp *mctp, void *ctx)
1002 {
1003 	mctp->alloc_ctx = ctx;
1004 }
1005