blob: b855ced6453bf8a0f3bee738d5b6df6765fff24c [file] [log] [blame]
Jeremy Kerr3d36ee22019-05-30 11:15:37 +08001/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
Jeremy Kerr4cdc2002019-02-07 16:49:12 +08002
3#include <assert.h>
4#include <stdarg.h>
5#include <stddef.h>
6#include <stdint.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10
11#undef pr_fmt
12#define pr_fmt(fmt) "core: " fmt
13
14#include "libmctp.h"
15#include "libmctp-alloc.h"
16#include "libmctp-log.h"
17
18/* Internal data structures */
19
20struct mctp_bus {
21 mctp_eid_t eid;
22 struct mctp_binding *binding;
Jeremy Kerr1cd31182019-02-27 18:01:00 +080023 bool tx_enabled;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080024
Jeremy Kerrcc2458d2019-03-01 08:23:33 +080025 struct mctp_pktbuf *tx_queue_head;
26 struct mctp_pktbuf *tx_queue_tail;
27
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080028 /* todo: routing */
29};
30
Jeremy Kerr24db71f2019-02-07 21:37:35 +080031struct mctp_msg_ctx {
32 uint8_t src;
33 uint8_t tag;
34 uint8_t last_seq;
35 void *buf;
36 size_t buf_size;
37 size_t buf_alloc_size;
38};
39
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080040struct mctp {
41 /* todo: multiple busses */
42 struct mctp_bus busses[1];
43
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080044 /* Message RX callback */
45 mctp_rx_fn message_rx;
46 void *message_rx_data;
Jeremy Kerr24db71f2019-02-07 21:37:35 +080047
48 /* Message reassembly.
49 * @todo: flexible context count
50 */
51 struct mctp_msg_ctx msg_ctxs[16];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080052};
53
54#ifndef BUILD_ASSERT
55#define BUILD_ASSERT(x) \
56 do { (void)sizeof(char[0-(!(x))]); } while (0)
57#endif
58
Jeremy Kerr24db71f2019-02-07 21:37:35 +080059#ifndef ARRAY_SIZE
60#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
61#endif
62
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080063struct mctp_pktbuf *mctp_pktbuf_alloc(uint8_t len)
64{
65 struct mctp_pktbuf *buf;
66
67 BUILD_ASSERT(MCTP_PKTBUF_SIZE <= 0xff);
68
69 /* todo: pools */
70 buf = __mctp_alloc(sizeof(*buf));
71
72 buf->start = MCTP_PKTBUF_BINDING_PAD;
73 buf->end = buf->start + len;
74 buf->mctp_hdr_off = buf->start;
Jeremy Kerrdd109f12019-04-04 11:46:49 +080075 buf->next = NULL;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080076
77 return buf;
78}
79
80void mctp_pktbuf_free(struct mctp_pktbuf *pkt)
81{
82 __mctp_free(pkt);
83}
84
85struct mctp_hdr *mctp_pktbuf_hdr(struct mctp_pktbuf *pkt)
86{
87 return (void *)pkt->data + pkt->mctp_hdr_off;
88}
89
90void *mctp_pktbuf_data(struct mctp_pktbuf *pkt)
91{
92 return (void *)pkt->data + pkt->mctp_hdr_off + sizeof(struct mctp_hdr);
93}
94
95uint8_t mctp_pktbuf_size(struct mctp_pktbuf *pkt)
96{
97 return pkt->end - pkt->start;
98}
99
100void *mctp_pktbuf_alloc_start(struct mctp_pktbuf *pkt, uint8_t size)
101{
102 assert(size <= pkt->start);
103 pkt->start -= size;
104 return pkt->data + pkt->start;
105}
106
107void *mctp_pktbuf_alloc_end(struct mctp_pktbuf *pkt, uint8_t size)
108{
109 void *buf;
110
111 assert(size < (MCTP_PKTBUF_SIZE - pkt->end));
112 buf = pkt->data + pkt->end;
113 pkt->end += size;
114 return buf;
115}
116
117int mctp_pktbuf_push(struct mctp_pktbuf *pkt, void *data, uint8_t len)
118{
119 void *p;
120
121 assert(pkt->end + len <= MCTP_PKTBUF_SIZE);
122
123 if (pkt->end + len > MCTP_PKTBUF_SIZE)
124 return -1;
125
126 p = pkt->data + pkt->end;
127
128 pkt->end += len;
129 memcpy(p, data, len);
130
131 return 0;
132}
133
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800134/* Message reassembly */
135static struct mctp_msg_ctx *mctp_msg_ctx_lookup(struct mctp *mctp,
136 uint8_t src, uint8_t tag)
137{
138 unsigned int i;
139
140 /* @todo: better lookup, if we add support for more outstanding
141 * message contexts */
142 for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) {
143 struct mctp_msg_ctx *ctx = &mctp->msg_ctxs[i];
144 if (ctx->src == src && ctx->tag == tag)
145 return ctx;
146 }
147
148 return NULL;
149}
150
151static struct mctp_msg_ctx *mctp_msg_ctx_create(struct mctp *mctp,
152 uint8_t src, uint8_t tag)
153{
Jeremy Kerr11a234e2019-02-27 17:59:53 +0800154 struct mctp_msg_ctx *ctx = NULL;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800155 unsigned int i;
156
157 for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) {
158 struct mctp_msg_ctx *tmp = &mctp->msg_ctxs[i];
159 if (!tmp->src) {
160 ctx = tmp;
161 break;
162 }
163 }
164
165 if (!ctx)
166 return NULL;
167
168 ctx->src = src;
169 ctx->tag = tag;
170
171 return ctx;
172}
173
174static void mctp_msg_ctx_drop(struct mctp_msg_ctx *ctx)
175{
176 ctx->src = 0;
177}
178
179static void mctp_msg_ctx_reset(struct mctp_msg_ctx *ctx)
180{
181 ctx->buf_size = 0;
182}
183
184static int mctp_msg_ctx_add_pkt(struct mctp_msg_ctx *ctx,
185 struct mctp_pktbuf *pkt)
186{
187 size_t len;
188
189 len = mctp_pktbuf_size(pkt) - sizeof(struct mctp_hdr);
190
191 if (ctx->buf_size + len > ctx->buf_alloc_size) {
192 size_t new_alloc_size;
193
194 /* @todo: finer-grained allocation, size limits */
195 if (!ctx->buf_alloc_size) {
196 new_alloc_size = 4096;
197 } else {
198 new_alloc_size = ctx->buf_alloc_size * 2;
199 }
200 ctx->buf = __mctp_realloc(ctx->buf, new_alloc_size);
201 ctx->buf_alloc_size = new_alloc_size;
202 }
203
204 memcpy(ctx->buf + ctx->buf_size, mctp_pktbuf_data(pkt), len);
205 ctx->buf_size += len;
206
207 return 0;
208}
209
210/* Core API functions */
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800211struct mctp *mctp_init(void)
212{
213 struct mctp *mctp;
214
215 mctp = __mctp_alloc(sizeof(*mctp));
216 memset(mctp, 0, sizeof(*mctp));
217
218 return mctp;
219}
220
221int mctp_set_rx_all(struct mctp *mctp, mctp_rx_fn fn, void *data)
222{
223 mctp->message_rx = fn;
224 mctp->message_rx_data = data;
225 return 0;
226}
227
228static struct mctp_bus *find_bus_for_eid(struct mctp *mctp,
229 mctp_eid_t dest __attribute__((unused)))
230{
231 return &mctp->busses[0];
232}
233
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800234int mctp_register_bus(struct mctp *mctp,
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800235 struct mctp_binding *binding,
236 mctp_eid_t eid)
237{
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800238 /* todo: multiple busses */
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800239 assert(!mctp->busses[0].binding);
240 mctp->busses[0].binding = binding;
241 mctp->busses[0].eid = eid;
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800242 binding->bus = &mctp->busses[0];
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800243 binding->mctp = mctp;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800244 return 0;
245}
246
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800247void mctp_bus_rx(struct mctp_binding *binding, struct mctp_pktbuf *pkt)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800248{
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800249 struct mctp_bus *bus = binding->bus;
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800250 struct mctp *mctp = binding->mctp;
Ed Tanousc2def9f2019-02-21 08:33:08 -0800251 uint8_t flags, exp_seq, seq, tag;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800252 struct mctp_msg_ctx *ctx;
253 struct mctp_hdr *hdr;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800254 size_t len;
255 void *p;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800256 int rc;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800257
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800258 assert(bus);
259
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800260 hdr = mctp_pktbuf_hdr(pkt);
261
262 if (hdr->dest != bus->eid)
263 /* @todo: non-local packet routing */
264 return;
265
266 flags = hdr->flags_seq_tag & (MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM);
267 tag = (hdr->flags_seq_tag >> MCTP_HDR_TAG_SHIFT) & MCTP_HDR_TAG_MASK;
268 seq = (hdr->flags_seq_tag >> MCTP_HDR_SEQ_SHIFT) & MCTP_HDR_SEQ_MASK;
269
270 switch (flags) {
271 case MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM:
272 /* single-packet message - send straight up to rx function,
273 * no need to create a message context */
274 len = pkt->end - pkt->mctp_hdr_off - sizeof(struct mctp_hdr);
275 p = pkt->data + pkt->mctp_hdr_off + sizeof(struct mctp_hdr),
Jeremy Kerra8ec7062019-03-06 09:03:54 +0800276 mctp->message_rx(hdr->src, mctp->message_rx_data, p, len);
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800277 break;
278
279 case MCTP_HDR_FLAG_SOM:
280 /* start of a new message - start the new context for
281 * future message reception. If an existing context is
282 * already present, drop it. */
283 ctx = mctp_msg_ctx_lookup(mctp, hdr->src, tag);
284 if (ctx) {
285 mctp_msg_ctx_reset(ctx);
286 } else {
287 ctx = mctp_msg_ctx_create(mctp, hdr->src, tag);
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800288 }
289
290 rc = mctp_msg_ctx_add_pkt(ctx, pkt);
291 if (rc) {
292 mctp_msg_ctx_drop(ctx);
293 } else {
294 ctx->last_seq = seq;
295 }
296
297 break;
298
299 case MCTP_HDR_FLAG_EOM:
300 ctx = mctp_msg_ctx_lookup(mctp, hdr->src, tag);
301 if (!ctx)
302 return;
303
Ed Tanousc2def9f2019-02-21 08:33:08 -0800304 exp_seq = (ctx->last_seq + 1) % 4;
305
306 if (exp_seq != seq) {
307 mctp_prdebug(
308 "Sequence number %d does not match expected %d",
309 seq, exp_seq);
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800310 mctp_msg_ctx_drop(ctx);
311 return;
312 }
313
314 rc = mctp_msg_ctx_add_pkt(ctx, pkt);
315 if (!rc) {
Jeremy Kerr4c951182019-03-20 12:28:23 +0800316 mctp->message_rx(ctx->src, mctp->message_rx_data,
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800317 ctx->buf, ctx->buf_size);
318 }
319
320 mctp_msg_ctx_drop(ctx);
321 break;
Ed Tanousc2def9f2019-02-21 08:33:08 -0800322
323 case 0:
324 /* Neither SOM nor EOM */
325 ctx = mctp_msg_ctx_lookup(mctp, hdr->src, tag);
326 if (!ctx)
327 return;
328
329 exp_seq = (ctx->last_seq + 1) % 4;
330 if (exp_seq != seq) {
331 mctp_prdebug(
332 "Sequence number %d does not match expected %d",
333 seq, exp_seq);
334 mctp_msg_ctx_drop(ctx);
335 return;
336 }
337
338 rc = mctp_msg_ctx_add_pkt(ctx, pkt);
339 if (rc) {
340 mctp_msg_ctx_drop(ctx);
341 return;
342 }
343 ctx->last_seq = seq;
344
345 break;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800346 }
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800347}
348
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800349static int mctp_packet_tx(struct mctp_bus *bus,
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800350 struct mctp_pktbuf *pkt)
351{
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800352 if (!bus->tx_enabled)
353 return -1;
354
355 mctp_prdebug("sending pkt, len %d",
356 mctp_pktbuf_size(pkt));
357
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800358 return bus->binding->tx(bus->binding, pkt);
359}
360
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800361static void mctp_send_tx_queue(struct mctp_bus *bus)
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800362{
363 struct mctp_pktbuf *pkt;
364
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800365 while ((pkt = bus->tx_queue_head)) {
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800366 int rc;
367
368 rc = mctp_packet_tx(bus, pkt);
369 if (rc)
370 break;
371
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800372 bus->tx_queue_head = pkt->next;
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800373 mctp_pktbuf_free(pkt);
374 }
375
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800376 if (!bus->tx_queue_head)
377 bus->tx_queue_tail = NULL;
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800378
379}
380
381void mctp_binding_set_tx_enabled(struct mctp_binding *binding, bool enable)
382{
383 struct mctp_bus *bus = binding->bus;
384 bus->tx_enabled = enable;
385 if (enable)
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800386 mctp_send_tx_queue(bus);
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800387}
388
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800389int mctp_message_tx(struct mctp *mctp, mctp_eid_t eid,
390 void *msg, size_t msg_len)
391{
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800392 struct mctp_pktbuf *pkt;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800393 struct mctp_hdr *hdr;
394 struct mctp_bus *bus;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800395 size_t pkt_len, p;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800396
397 bus = find_bus_for_eid(mctp, eid);
398
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800399 /* queue up packets, each of max MCTP_MTU size */
400 for (p = 0; p < msg_len; ) {
401 pkt_len = msg_len - p;
402 if (pkt_len > MCTP_MTU)
403 pkt_len = MCTP_MTU;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800404
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800405 pkt = mctp_pktbuf_alloc(pkt_len + sizeof(*hdr));
406 hdr = mctp_pktbuf_hdr(pkt);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800407
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800408 /* todo: tags */
409 hdr->ver = bus->binding->version & 0xf;
410 hdr->dest = eid;
411 hdr->src = bus->eid;
412 hdr->flags_seq_tag = MCTP_HDR_FLAG_SOM |
413 MCTP_HDR_FLAG_EOM |
414 (0 << MCTP_HDR_SEQ_SHIFT) |
415 MCTP_HDR_FLAG_TO |
416 (0 << MCTP_HDR_TAG_SHIFT);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800417
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800418 memcpy(mctp_pktbuf_data(pkt), msg + p, pkt_len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800419
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800420 /* add to tx queue */
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800421 if (bus->tx_queue_tail)
422 bus->tx_queue_tail->next = pkt;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800423 else
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800424 bus->tx_queue_head = pkt;
425 bus->tx_queue_tail = pkt;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800426
427 p += pkt_len;
428 }
429
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800430 mctp_send_tx_queue(bus);
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800431
432 return 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800433}