blob: 72b511c6e1bda96b314adc915fb94a00fe80bd15 [file] [log] [blame]
Jeremy Kerr4cdc2002019-02-07 16:49:12 +08001/* SPDX-License-Identifier: Apache-2.0 */
2
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;
75
76 return buf;
77}
78
79void mctp_pktbuf_free(struct mctp_pktbuf *pkt)
80{
81 __mctp_free(pkt);
82}
83
84struct mctp_hdr *mctp_pktbuf_hdr(struct mctp_pktbuf *pkt)
85{
86 return (void *)pkt->data + pkt->mctp_hdr_off;
87}
88
89void *mctp_pktbuf_data(struct mctp_pktbuf *pkt)
90{
91 return (void *)pkt->data + pkt->mctp_hdr_off + sizeof(struct mctp_hdr);
92}
93
94uint8_t mctp_pktbuf_size(struct mctp_pktbuf *pkt)
95{
96 return pkt->end - pkt->start;
97}
98
99void *mctp_pktbuf_alloc_start(struct mctp_pktbuf *pkt, uint8_t size)
100{
101 assert(size <= pkt->start);
102 pkt->start -= size;
103 return pkt->data + pkt->start;
104}
105
106void *mctp_pktbuf_alloc_end(struct mctp_pktbuf *pkt, uint8_t size)
107{
108 void *buf;
109
110 assert(size < (MCTP_PKTBUF_SIZE - pkt->end));
111 buf = pkt->data + pkt->end;
112 pkt->end += size;
113 return buf;
114}
115
116int mctp_pktbuf_push(struct mctp_pktbuf *pkt, void *data, uint8_t len)
117{
118 void *p;
119
120 assert(pkt->end + len <= MCTP_PKTBUF_SIZE);
121
122 if (pkt->end + len > MCTP_PKTBUF_SIZE)
123 return -1;
124
125 p = pkt->data + pkt->end;
126
127 pkt->end += len;
128 memcpy(p, data, len);
129
130 return 0;
131}
132
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800133/* Message reassembly */
134static struct mctp_msg_ctx *mctp_msg_ctx_lookup(struct mctp *mctp,
135 uint8_t src, uint8_t tag)
136{
137 unsigned int i;
138
139 /* @todo: better lookup, if we add support for more outstanding
140 * message contexts */
141 for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) {
142 struct mctp_msg_ctx *ctx = &mctp->msg_ctxs[i];
143 if (ctx->src == src && ctx->tag == tag)
144 return ctx;
145 }
146
147 return NULL;
148}
149
150static struct mctp_msg_ctx *mctp_msg_ctx_create(struct mctp *mctp,
151 uint8_t src, uint8_t tag)
152{
Jeremy Kerr11a234e2019-02-27 17:59:53 +0800153 struct mctp_msg_ctx *ctx = NULL;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800154 unsigned int i;
155
156 for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) {
157 struct mctp_msg_ctx *tmp = &mctp->msg_ctxs[i];
158 if (!tmp->src) {
159 ctx = tmp;
160 break;
161 }
162 }
163
164 if (!ctx)
165 return NULL;
166
167 ctx->src = src;
168 ctx->tag = tag;
169
170 return ctx;
171}
172
173static void mctp_msg_ctx_drop(struct mctp_msg_ctx *ctx)
174{
175 ctx->src = 0;
176}
177
178static void mctp_msg_ctx_reset(struct mctp_msg_ctx *ctx)
179{
180 ctx->buf_size = 0;
181}
182
183static int mctp_msg_ctx_add_pkt(struct mctp_msg_ctx *ctx,
184 struct mctp_pktbuf *pkt)
185{
186 size_t len;
187
188 len = mctp_pktbuf_size(pkt) - sizeof(struct mctp_hdr);
189
190 if (ctx->buf_size + len > ctx->buf_alloc_size) {
191 size_t new_alloc_size;
192
193 /* @todo: finer-grained allocation, size limits */
194 if (!ctx->buf_alloc_size) {
195 new_alloc_size = 4096;
196 } else {
197 new_alloc_size = ctx->buf_alloc_size * 2;
198 }
199 ctx->buf = __mctp_realloc(ctx->buf, new_alloc_size);
200 ctx->buf_alloc_size = new_alloc_size;
201 }
202
203 memcpy(ctx->buf + ctx->buf_size, mctp_pktbuf_data(pkt), len);
204 ctx->buf_size += len;
205
206 return 0;
207}
208
209/* Core API functions */
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800210struct mctp *mctp_init(void)
211{
212 struct mctp *mctp;
213
214 mctp = __mctp_alloc(sizeof(*mctp));
215 memset(mctp, 0, sizeof(*mctp));
216
217 return mctp;
218}
219
220int mctp_set_rx_all(struct mctp *mctp, mctp_rx_fn fn, void *data)
221{
222 mctp->message_rx = fn;
223 mctp->message_rx_data = data;
224 return 0;
225}
226
227static struct mctp_bus *find_bus_for_eid(struct mctp *mctp,
228 mctp_eid_t dest __attribute__((unused)))
229{
230 return &mctp->busses[0];
231}
232
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800233int mctp_register_bus(struct mctp *mctp,
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800234 struct mctp_binding *binding,
235 mctp_eid_t eid)
236{
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800237 /* todo: multiple busses */
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800238 assert(!mctp->busses[0].binding);
239 mctp->busses[0].binding = binding;
240 mctp->busses[0].eid = eid;
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800241 binding->bus = &mctp->busses[0];
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800242 binding->mctp = mctp;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800243 return 0;
244}
245
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800246void mctp_bus_rx(struct mctp_binding *binding, struct mctp_pktbuf *pkt)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800247{
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800248 struct mctp_bus *bus = binding->bus;
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800249 struct mctp *mctp = binding->mctp;
Ed Tanousc2def9f2019-02-21 08:33:08 -0800250 uint8_t flags, exp_seq, seq, tag;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800251 struct mctp_msg_ctx *ctx;
252 struct mctp_hdr *hdr;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800253 size_t len;
254 void *p;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800255 int rc;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800256
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800257 assert(bus);
258
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800259 hdr = mctp_pktbuf_hdr(pkt);
260
261 if (hdr->dest != bus->eid)
262 /* @todo: non-local packet routing */
263 return;
264
265 flags = hdr->flags_seq_tag & (MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM);
266 tag = (hdr->flags_seq_tag >> MCTP_HDR_TAG_SHIFT) & MCTP_HDR_TAG_MASK;
267 seq = (hdr->flags_seq_tag >> MCTP_HDR_SEQ_SHIFT) & MCTP_HDR_SEQ_MASK;
268
269 switch (flags) {
270 case MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM:
271 /* single-packet message - send straight up to rx function,
272 * no need to create a message context */
273 len = pkt->end - pkt->mctp_hdr_off - sizeof(struct mctp_hdr);
274 p = pkt->data + pkt->mctp_hdr_off + sizeof(struct mctp_hdr),
Jeremy Kerra8ec7062019-03-06 09:03:54 +0800275 mctp->message_rx(hdr->src, mctp->message_rx_data, p, len);
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800276 break;
277
278 case MCTP_HDR_FLAG_SOM:
279 /* start of a new message - start the new context for
280 * future message reception. If an existing context is
281 * already present, drop it. */
282 ctx = mctp_msg_ctx_lookup(mctp, hdr->src, tag);
283 if (ctx) {
284 mctp_msg_ctx_reset(ctx);
285 } else {
286 ctx = mctp_msg_ctx_create(mctp, hdr->src, tag);
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800287 }
288
289 rc = mctp_msg_ctx_add_pkt(ctx, pkt);
290 if (rc) {
291 mctp_msg_ctx_drop(ctx);
292 } else {
293 ctx->last_seq = seq;
294 }
295
296 break;
297
298 case MCTP_HDR_FLAG_EOM:
299 ctx = mctp_msg_ctx_lookup(mctp, hdr->src, tag);
300 if (!ctx)
301 return;
302
Ed Tanousc2def9f2019-02-21 08:33:08 -0800303 exp_seq = (ctx->last_seq + 1) % 4;
304
305 if (exp_seq != seq) {
306 mctp_prdebug(
307 "Sequence number %d does not match expected %d",
308 seq, exp_seq);
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800309 mctp_msg_ctx_drop(ctx);
310 return;
311 }
312
313 rc = mctp_msg_ctx_add_pkt(ctx, pkt);
314 if (!rc) {
Jeremy Kerr4c951182019-03-20 12:28:23 +0800315 mctp->message_rx(ctx->src, mctp->message_rx_data,
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800316 ctx->buf, ctx->buf_size);
317 }
318
319 mctp_msg_ctx_drop(ctx);
320 break;
Ed Tanousc2def9f2019-02-21 08:33:08 -0800321
322 case 0:
323 /* Neither SOM nor EOM */
324 ctx = mctp_msg_ctx_lookup(mctp, hdr->src, tag);
325 if (!ctx)
326 return;
327
328 exp_seq = (ctx->last_seq + 1) % 4;
329 if (exp_seq != seq) {
330 mctp_prdebug(
331 "Sequence number %d does not match expected %d",
332 seq, exp_seq);
333 mctp_msg_ctx_drop(ctx);
334 return;
335 }
336
337 rc = mctp_msg_ctx_add_pkt(ctx, pkt);
338 if (rc) {
339 mctp_msg_ctx_drop(ctx);
340 return;
341 }
342 ctx->last_seq = seq;
343
344 break;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800345 }
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800346}
347
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800348static int mctp_packet_tx(struct mctp_bus *bus,
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800349 struct mctp_pktbuf *pkt)
350{
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800351 if (!bus->tx_enabled)
352 return -1;
353
354 mctp_prdebug("sending pkt, len %d",
355 mctp_pktbuf_size(pkt));
356
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800357 return bus->binding->tx(bus->binding, pkt);
358}
359
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800360static void mctp_send_tx_queue(struct mctp_bus *bus)
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800361{
362 struct mctp_pktbuf *pkt;
363
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800364 while ((pkt = bus->tx_queue_head)) {
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800365 int rc;
366
367 rc = mctp_packet_tx(bus, pkt);
368 if (rc)
369 break;
370
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800371 bus->tx_queue_head = pkt->next;
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800372 mctp_pktbuf_free(pkt);
373 }
374
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800375 if (!bus->tx_queue_head)
376 bus->tx_queue_tail = NULL;
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800377
378}
379
380void mctp_binding_set_tx_enabled(struct mctp_binding *binding, bool enable)
381{
382 struct mctp_bus *bus = binding->bus;
383 bus->tx_enabled = enable;
384 if (enable)
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800385 mctp_send_tx_queue(bus);
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800386}
387
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800388int mctp_message_tx(struct mctp *mctp, mctp_eid_t eid,
389 void *msg, size_t msg_len)
390{
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800391 struct mctp_pktbuf *pkt;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800392 struct mctp_hdr *hdr;
393 struct mctp_bus *bus;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800394 size_t pkt_len, p;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800395
396 bus = find_bus_for_eid(mctp, eid);
397
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800398 /* queue up packets, each of max MCTP_MTU size */
399 for (p = 0; p < msg_len; ) {
400 pkt_len = msg_len - p;
401 if (pkt_len > MCTP_MTU)
402 pkt_len = MCTP_MTU;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800403
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800404 pkt = mctp_pktbuf_alloc(pkt_len + sizeof(*hdr));
405 hdr = mctp_pktbuf_hdr(pkt);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800406
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800407 /* todo: tags */
408 hdr->ver = bus->binding->version & 0xf;
409 hdr->dest = eid;
410 hdr->src = bus->eid;
411 hdr->flags_seq_tag = MCTP_HDR_FLAG_SOM |
412 MCTP_HDR_FLAG_EOM |
413 (0 << MCTP_HDR_SEQ_SHIFT) |
414 MCTP_HDR_FLAG_TO |
415 (0 << MCTP_HDR_TAG_SHIFT);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800416
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800417 memcpy(mctp_pktbuf_data(pkt), msg + p, pkt_len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800418
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800419 /* add to tx queue */
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800420 if (bus->tx_queue_tail)
421 bus->tx_queue_tail->next = pkt;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800422 else
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800423 bus->tx_queue_head = pkt;
424 bus->tx_queue_tail = pkt;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800425
426 p += pkt_len;
427 }
428
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800429 mctp_send_tx_queue(bus);
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800430
431 return 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800432}