blob: baf60a84f4223244babbcd0e41347a9148034bc9 [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;
Jeremy Kerr9a3da812019-08-02 15:57:53 +0800170 ctx->buf_size = 0;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800171
172 return ctx;
173}
174
175static void mctp_msg_ctx_drop(struct mctp_msg_ctx *ctx)
176{
177 ctx->src = 0;
178}
179
180static void mctp_msg_ctx_reset(struct mctp_msg_ctx *ctx)
181{
182 ctx->buf_size = 0;
183}
184
185static int mctp_msg_ctx_add_pkt(struct mctp_msg_ctx *ctx,
186 struct mctp_pktbuf *pkt)
187{
188 size_t len;
189
190 len = mctp_pktbuf_size(pkt) - sizeof(struct mctp_hdr);
191
192 if (ctx->buf_size + len > ctx->buf_alloc_size) {
193 size_t new_alloc_size;
194
195 /* @todo: finer-grained allocation, size limits */
196 if (!ctx->buf_alloc_size) {
197 new_alloc_size = 4096;
198 } else {
199 new_alloc_size = ctx->buf_alloc_size * 2;
200 }
201 ctx->buf = __mctp_realloc(ctx->buf, new_alloc_size);
202 ctx->buf_alloc_size = new_alloc_size;
203 }
204
205 memcpy(ctx->buf + ctx->buf_size, mctp_pktbuf_data(pkt), len);
206 ctx->buf_size += len;
207
208 return 0;
209}
210
211/* Core API functions */
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800212struct mctp *mctp_init(void)
213{
214 struct mctp *mctp;
215
216 mctp = __mctp_alloc(sizeof(*mctp));
217 memset(mctp, 0, sizeof(*mctp));
218
219 return mctp;
220}
221
222int mctp_set_rx_all(struct mctp *mctp, mctp_rx_fn fn, void *data)
223{
224 mctp->message_rx = fn;
225 mctp->message_rx_data = data;
226 return 0;
227}
228
229static struct mctp_bus *find_bus_for_eid(struct mctp *mctp,
230 mctp_eid_t dest __attribute__((unused)))
231{
232 return &mctp->busses[0];
233}
234
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800235int mctp_register_bus(struct mctp *mctp,
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800236 struct mctp_binding *binding,
237 mctp_eid_t eid)
238{
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800239 /* todo: multiple busses */
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800240 assert(!mctp->busses[0].binding);
241 mctp->busses[0].binding = binding;
242 mctp->busses[0].eid = eid;
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800243 binding->bus = &mctp->busses[0];
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800244 binding->mctp = mctp;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800245 return 0;
246}
247
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800248void mctp_bus_rx(struct mctp_binding *binding, struct mctp_pktbuf *pkt)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800249{
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800250 struct mctp_bus *bus = binding->bus;
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800251 struct mctp *mctp = binding->mctp;
Ed Tanousc2def9f2019-02-21 08:33:08 -0800252 uint8_t flags, exp_seq, seq, tag;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800253 struct mctp_msg_ctx *ctx;
254 struct mctp_hdr *hdr;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800255 size_t len;
256 void *p;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800257 int rc;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800258
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800259 assert(bus);
260
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800261 hdr = mctp_pktbuf_hdr(pkt);
262
263 if (hdr->dest != bus->eid)
264 /* @todo: non-local packet routing */
265 return;
266
267 flags = hdr->flags_seq_tag & (MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM);
268 tag = (hdr->flags_seq_tag >> MCTP_HDR_TAG_SHIFT) & MCTP_HDR_TAG_MASK;
269 seq = (hdr->flags_seq_tag >> MCTP_HDR_SEQ_SHIFT) & MCTP_HDR_SEQ_MASK;
270
271 switch (flags) {
272 case MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM:
273 /* single-packet message - send straight up to rx function,
274 * no need to create a message context */
275 len = pkt->end - pkt->mctp_hdr_off - sizeof(struct mctp_hdr);
276 p = pkt->data + pkt->mctp_hdr_off + sizeof(struct mctp_hdr),
Jeremy Kerra8ec7062019-03-06 09:03:54 +0800277 mctp->message_rx(hdr->src, mctp->message_rx_data, p, len);
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800278 break;
279
280 case MCTP_HDR_FLAG_SOM:
281 /* start of a new message - start the new context for
282 * future message reception. If an existing context is
283 * already present, drop it. */
284 ctx = mctp_msg_ctx_lookup(mctp, hdr->src, tag);
285 if (ctx) {
286 mctp_msg_ctx_reset(ctx);
287 } else {
288 ctx = mctp_msg_ctx_create(mctp, hdr->src, tag);
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800289 }
290
291 rc = mctp_msg_ctx_add_pkt(ctx, pkt);
292 if (rc) {
293 mctp_msg_ctx_drop(ctx);
294 } else {
295 ctx->last_seq = seq;
296 }
297
298 break;
299
300 case MCTP_HDR_FLAG_EOM:
301 ctx = mctp_msg_ctx_lookup(mctp, hdr->src, tag);
302 if (!ctx)
303 return;
304
Ed Tanousc2def9f2019-02-21 08:33:08 -0800305 exp_seq = (ctx->last_seq + 1) % 4;
306
307 if (exp_seq != seq) {
308 mctp_prdebug(
309 "Sequence number %d does not match expected %d",
310 seq, exp_seq);
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800311 mctp_msg_ctx_drop(ctx);
312 return;
313 }
314
315 rc = mctp_msg_ctx_add_pkt(ctx, pkt);
316 if (!rc) {
Jeremy Kerr4c951182019-03-20 12:28:23 +0800317 mctp->message_rx(ctx->src, mctp->message_rx_data,
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800318 ctx->buf, ctx->buf_size);
319 }
320
321 mctp_msg_ctx_drop(ctx);
322 break;
Ed Tanousc2def9f2019-02-21 08:33:08 -0800323
324 case 0:
325 /* Neither SOM nor EOM */
326 ctx = mctp_msg_ctx_lookup(mctp, hdr->src, tag);
327 if (!ctx)
328 return;
329
330 exp_seq = (ctx->last_seq + 1) % 4;
331 if (exp_seq != seq) {
332 mctp_prdebug(
333 "Sequence number %d does not match expected %d",
334 seq, exp_seq);
335 mctp_msg_ctx_drop(ctx);
336 return;
337 }
338
339 rc = mctp_msg_ctx_add_pkt(ctx, pkt);
340 if (rc) {
341 mctp_msg_ctx_drop(ctx);
342 return;
343 }
344 ctx->last_seq = seq;
345
346 break;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800347 }
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800348}
349
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800350static int mctp_packet_tx(struct mctp_bus *bus,
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800351 struct mctp_pktbuf *pkt)
352{
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800353 if (!bus->tx_enabled)
354 return -1;
355
356 mctp_prdebug("sending pkt, len %d",
357 mctp_pktbuf_size(pkt));
358
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800359 return bus->binding->tx(bus->binding, pkt);
360}
361
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800362static void mctp_send_tx_queue(struct mctp_bus *bus)
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800363{
364 struct mctp_pktbuf *pkt;
365
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800366 while ((pkt = bus->tx_queue_head)) {
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800367 int rc;
368
369 rc = mctp_packet_tx(bus, pkt);
370 if (rc)
371 break;
372
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800373 bus->tx_queue_head = pkt->next;
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800374 mctp_pktbuf_free(pkt);
375 }
376
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800377 if (!bus->tx_queue_head)
378 bus->tx_queue_tail = NULL;
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800379
380}
381
382void mctp_binding_set_tx_enabled(struct mctp_binding *binding, bool enable)
383{
384 struct mctp_bus *bus = binding->bus;
385 bus->tx_enabled = enable;
386 if (enable)
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800387 mctp_send_tx_queue(bus);
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800388}
389
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800390int mctp_message_tx(struct mctp *mctp, mctp_eid_t eid,
391 void *msg, size_t msg_len)
392{
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800393 struct mctp_pktbuf *pkt;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800394 struct mctp_hdr *hdr;
395 struct mctp_bus *bus;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800396 size_t pkt_len, p;
Jeremy Kerrc855d7b2019-08-01 21:18:09 +0800397 int i;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800398
399 bus = find_bus_for_eid(mctp, eid);
400
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800401 /* queue up packets, each of max MCTP_MTU size */
Jeremy Kerrc855d7b2019-08-01 21:18:09 +0800402 for (p = 0, i = 0; p < msg_len; i++) {
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800403 pkt_len = msg_len - p;
404 if (pkt_len > MCTP_MTU)
405 pkt_len = MCTP_MTU;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800406
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800407 pkt = mctp_pktbuf_alloc(pkt_len + sizeof(*hdr));
408 hdr = mctp_pktbuf_hdr(pkt);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800409
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800410 /* todo: tags */
411 hdr->ver = bus->binding->version & 0xf;
412 hdr->dest = eid;
413 hdr->src = bus->eid;
Jeremy Kerrc855d7b2019-08-01 21:18:09 +0800414 hdr->flags_seq_tag = MCTP_HDR_FLAG_TO |
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800415 (0 << MCTP_HDR_TAG_SHIFT);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800416
Jeremy Kerrc855d7b2019-08-01 21:18:09 +0800417 if (i == 0)
418 hdr->flags_seq_tag |= MCTP_HDR_FLAG_SOM;
419 if (p + pkt_len >= msg_len)
420 hdr->flags_seq_tag |= MCTP_HDR_FLAG_EOM;
421 hdr->flags_seq_tag |=
422 (i & MCTP_HDR_SEQ_MASK) << MCTP_HDR_SEQ_SHIFT;
423
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800424 memcpy(mctp_pktbuf_data(pkt), msg + p, pkt_len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800425
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800426 /* add to tx queue */
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800427 if (bus->tx_queue_tail)
428 bus->tx_queue_tail->next = pkt;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800429 else
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800430 bus->tx_queue_head = pkt;
431 bus->tx_queue_tail = pkt;
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800432
433 p += pkt_len;
434 }
435
Jeremy Kerrcc2458d2019-03-01 08:23:33 +0800436 mctp_send_tx_queue(bus);
Jeremy Kerr24db71f2019-02-07 21:37:35 +0800437
438 return 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800439}