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 20 /* Internal data structures */ 21 22 struct mctp_bus { 23 mctp_eid_t eid; 24 struct mctp_binding *binding; 25 bool tx_enabled; 26 27 struct mctp_pktbuf *tx_queue_head; 28 struct mctp_pktbuf *tx_queue_tail; 29 30 /* todo: routing */ 31 }; 32 33 struct mctp_msg_ctx { 34 uint8_t src; 35 uint8_t dest; 36 uint8_t tag; 37 uint8_t last_seq; 38 void *buf; 39 size_t buf_size; 40 size_t buf_alloc_size; 41 }; 42 43 struct mctp { 44 int n_busses; 45 struct mctp_bus *busses; 46 47 /* Message RX callback */ 48 mctp_rx_fn message_rx; 49 void *message_rx_data; 50 51 /* Message reassembly. 52 * @todo: flexible context count 53 */ 54 struct mctp_msg_ctx msg_ctxs[16]; 55 56 enum { 57 ROUTE_ENDPOINT, 58 ROUTE_BRIDGE, 59 } route_policy; 60 }; 61 62 #ifndef BUILD_ASSERT 63 #define BUILD_ASSERT(x) \ 64 do { (void)sizeof(char[0-(!(x))]); } while (0) 65 #endif 66 67 #ifndef ARRAY_SIZE 68 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) 69 #endif 70 71 static int mctp_message_tx_on_bus(struct mctp_bus *bus, mctp_eid_t src, 72 mctp_eid_t dest, void *msg, size_t msg_len); 73 74 struct mctp_pktbuf *mctp_pktbuf_alloc(struct mctp_binding *binding, size_t len) 75 { 76 struct mctp_pktbuf *buf; 77 size_t size; 78 79 size = binding->pkt_size + binding->pkt_pad; 80 81 /* todo: pools */ 82 buf = __mctp_alloc(sizeof(*buf) + size); 83 84 buf->size = size; 85 buf->start = binding->pkt_pad; 86 buf->end = buf->start + len; 87 buf->mctp_hdr_off = buf->start; 88 buf->next = NULL; 89 90 return buf; 91 } 92 93 void mctp_pktbuf_free(struct mctp_pktbuf *pkt) 94 { 95 __mctp_free(pkt); 96 } 97 98 struct mctp_hdr *mctp_pktbuf_hdr(struct mctp_pktbuf *pkt) 99 { 100 return (void *)pkt->data + pkt->mctp_hdr_off; 101 } 102 103 void *mctp_pktbuf_data(struct mctp_pktbuf *pkt) 104 { 105 return (void *)pkt->data + pkt->mctp_hdr_off + sizeof(struct mctp_hdr); 106 } 107 108 uint8_t mctp_pktbuf_size(struct mctp_pktbuf *pkt) 109 { 110 return pkt->end - pkt->start; 111 } 112 113 void *mctp_pktbuf_alloc_start(struct mctp_pktbuf *pkt, size_t size) 114 { 115 assert(size <= pkt->start); 116 pkt->start -= size; 117 return pkt->data + pkt->start; 118 } 119 120 void *mctp_pktbuf_alloc_end(struct mctp_pktbuf *pkt, size_t size) 121 { 122 void *buf; 123 124 assert(size < (pkt->size - pkt->end)); 125 buf = pkt->data + pkt->end; 126 pkt->end += size; 127 return buf; 128 } 129 130 int mctp_pktbuf_push(struct mctp_pktbuf *pkt, void *data, size_t len) 131 { 132 void *p; 133 134 if (pkt->end + len > pkt->size) 135 return -1; 136 137 p = pkt->data + pkt->end; 138 139 pkt->end += len; 140 memcpy(p, data, len); 141 142 return 0; 143 } 144 145 /* Message reassembly */ 146 static struct mctp_msg_ctx *mctp_msg_ctx_lookup(struct mctp *mctp, 147 uint8_t src, uint8_t dest, uint8_t tag) 148 { 149 unsigned int i; 150 151 /* @todo: better lookup, if we add support for more outstanding 152 * message contexts */ 153 for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) { 154 struct mctp_msg_ctx *ctx = &mctp->msg_ctxs[i]; 155 if (ctx->src == src && ctx->dest == dest && 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, 163 uint8_t src, 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->src) { 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 ctx->buf_size = 0; 183 184 return ctx; 185 } 186 187 static void mctp_msg_ctx_drop(struct mctp_msg_ctx *ctx) 188 { 189 ctx->src = 0; 190 } 191 192 static void mctp_msg_ctx_reset(struct mctp_msg_ctx *ctx) 193 { 194 ctx->buf_size = 0; 195 } 196 197 static int mctp_msg_ctx_add_pkt(struct mctp_msg_ctx *ctx, 198 struct mctp_pktbuf *pkt) 199 { 200 size_t len; 201 202 len = mctp_pktbuf_size(pkt) - sizeof(struct mctp_hdr); 203 204 if (ctx->buf_size + len > ctx->buf_alloc_size) { 205 size_t new_alloc_size; 206 void *lbuf; 207 208 /* @todo: finer-grained allocation, size limits */ 209 if (!ctx->buf_alloc_size) { 210 new_alloc_size = 4096; 211 } else { 212 new_alloc_size = ctx->buf_alloc_size * 2; 213 } 214 215 lbuf = __mctp_realloc(ctx->buf, new_alloc_size); 216 if (lbuf) { 217 ctx->buf = lbuf; 218 ctx->buf_alloc_size = new_alloc_size; 219 } else { 220 __mctp_free(ctx->buf); 221 return -1; 222 } 223 } 224 225 memcpy(ctx->buf + ctx->buf_size, mctp_pktbuf_data(pkt), len); 226 ctx->buf_size += len; 227 228 return 0; 229 } 230 231 /* Core API functions */ 232 struct mctp *mctp_init(void) 233 { 234 struct mctp *mctp; 235 236 mctp = __mctp_alloc(sizeof(*mctp)); 237 memset(mctp, 0, sizeof(*mctp)); 238 239 return mctp; 240 } 241 242 void mctp_destroy(struct mctp *mctp) 243 { 244 size_t i; 245 246 /* Cleanup message assembly contexts */ 247 BUILD_ASSERT(ARRAY_SIZE(mctp->msg_ctxs) < SIZE_MAX); 248 for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) { 249 struct mctp_msg_ctx *tmp = &mctp->msg_ctxs[i]; 250 if (tmp->buf) 251 __mctp_free(tmp->buf); 252 } 253 254 __mctp_free(mctp->busses); 255 __mctp_free(mctp); 256 } 257 258 int mctp_set_rx_all(struct mctp *mctp, mctp_rx_fn fn, void *data) 259 { 260 mctp->message_rx = fn; 261 mctp->message_rx_data = data; 262 return 0; 263 } 264 265 static struct mctp_bus *find_bus_for_eid(struct mctp *mctp, 266 mctp_eid_t dest __attribute__((unused))) 267 { 268 /* for now, just use the first bus. For full routing support, 269 * we will need a table of neighbours */ 270 return &mctp->busses[0]; 271 } 272 273 int mctp_register_bus(struct mctp *mctp, 274 struct mctp_binding *binding, 275 mctp_eid_t eid) 276 { 277 int rc = 0; 278 279 /* todo: multiple busses */ 280 assert(mctp->n_busses == 0); 281 mctp->n_busses = 1; 282 283 mctp->busses = __mctp_alloc(sizeof(struct mctp_bus)); 284 if (!mctp->busses) 285 return -ENOMEM; 286 287 memset(mctp->busses, 0, sizeof(struct mctp_bus)); 288 mctp->busses[0].binding = binding; 289 mctp->busses[0].eid = eid; 290 binding->bus = &mctp->busses[0]; 291 binding->mctp = mctp; 292 mctp->route_policy = ROUTE_ENDPOINT; 293 294 if (binding->start) { 295 rc = binding->start(binding); 296 if (rc < 0) { 297 mctp_prerr("Failed to start binding: %d", rc); 298 __mctp_free(mctp->busses); 299 mctp->busses = NULL; 300 } 301 } 302 303 return rc; 304 } 305 306 int mctp_bridge_busses(struct mctp *mctp, 307 struct mctp_binding *b1, struct mctp_binding *b2) 308 { 309 assert(mctp->n_busses == 0); 310 mctp->busses = __mctp_alloc(2 * sizeof(struct mctp_bus)); 311 memset(mctp->busses, 0, 2 * sizeof(struct mctp_bus)); 312 mctp->n_busses = 2; 313 mctp->busses[0].binding = b1; 314 b1->bus = &mctp->busses[0]; 315 b1->mctp = mctp; 316 mctp->busses[1].binding = b2; 317 b2->bus = &mctp->busses[1]; 318 b2->mctp = mctp; 319 320 mctp->route_policy = ROUTE_BRIDGE; 321 322 if (b1->start) 323 b1->start(b1); 324 325 if (b2->start) 326 b2->start(b2); 327 328 return 0; 329 } 330 331 static inline bool mctp_ctrl_cmd_is_transport(struct mctp_ctrl_msg_hdr *hdr) 332 { 333 return ((hdr->command_code >= MCTP_CTRL_CMD_FIRST_TRANSPORT) && 334 (hdr->command_code <= MCTP_CTRL_CMD_LAST_TRANSPORT)); 335 } 336 337 static bool mctp_ctrl_handle_msg(struct mctp_bus *bus, mctp_eid_t src, 338 void *buffer, size_t length) 339 { 340 struct mctp_ctrl_msg_hdr *msg_hdr = buffer; 341 342 /* 343 * Control message is received. If a transport control message handler 344 * is provided, it will called. If there is no dedicated handler, this 345 * function returns false and data can be handled by the generic 346 * message handler. The transport control message handler will be 347 * provided with messages in the command range 0xF0 - 0xFF. 348 */ 349 if (mctp_ctrl_cmd_is_transport(msg_hdr)) { 350 if (bus->binding->control_rx != NULL) { 351 /* MCTP bus binding handler */ 352 bus->binding->control_rx(src, 353 bus->binding->control_rx_data, 354 buffer, length); 355 return true; 356 } 357 } 358 359 /* 360 * Command was not handled, due to lack of specific callback. 361 * It will be passed to regular message_rx handler. 362 */ 363 return false; 364 } 365 366 static inline bool mctp_rx_dest_is_local(struct mctp_bus *bus, mctp_eid_t dest) 367 { 368 return dest == bus->eid || dest == MCTP_EID_NULL || 369 dest == MCTP_EID_BROADCAST; 370 } 371 372 static inline bool mctp_ctrl_cmd_is_request(struct mctp_ctrl_msg_hdr *hdr) 373 { 374 return hdr->ic_msg_type == MCTP_CTRL_HDR_MSG_TYPE && 375 hdr->rq_dgram_inst & MCTP_CTRL_HDR_FLAG_REQUEST; 376 } 377 378 /* 379 * Receive the complete MCTP message and route it. 380 * Asserts: 381 * 'buf' is not NULL. 382 */ 383 static void mctp_rx(struct mctp *mctp, struct mctp_bus *bus, mctp_eid_t src, 384 mctp_eid_t dest, void *buf, size_t len) 385 { 386 assert(buf != NULL); 387 388 if (mctp->route_policy == ROUTE_ENDPOINT && 389 mctp_rx_dest_is_local(bus, dest)) { 390 /* Handle MCTP Control Messages: */ 391 if (len >= sizeof(struct mctp_ctrl_msg_hdr)) { 392 struct mctp_ctrl_msg_hdr *msg_hdr = buf; 393 394 /* 395 * Identify if this is a control request message. 396 * See DSP0236 v1.3.0 sec. 11.5. 397 */ 398 if (mctp_ctrl_cmd_is_request(msg_hdr)) { 399 bool handled; 400 handled = mctp_ctrl_handle_msg(bus, src, buf, 401 len); 402 if (handled) 403 return; 404 } 405 } 406 if (mctp->message_rx) 407 mctp->message_rx(src, mctp->message_rx_data, buf, len); 408 } 409 410 if (mctp->route_policy == ROUTE_BRIDGE) { 411 int i; 412 413 for (i = 0; i < mctp->n_busses; i++) { 414 struct mctp_bus *dest_bus = &mctp->busses[i]; 415 if (dest_bus == bus) 416 continue; 417 418 mctp_message_tx_on_bus(dest_bus, src, dest, buf, len); 419 } 420 421 } 422 } 423 424 void mctp_bus_rx(struct mctp_binding *binding, struct mctp_pktbuf *pkt) 425 { 426 struct mctp_bus *bus = binding->bus; 427 struct mctp *mctp = binding->mctp; 428 uint8_t flags, exp_seq, seq, tag; 429 struct mctp_msg_ctx *ctx; 430 struct mctp_hdr *hdr; 431 size_t len; 432 void *p; 433 int rc; 434 435 assert(bus); 436 437 hdr = mctp_pktbuf_hdr(pkt); 438 439 /* small optimisation: don't bother reassembly if we're going to 440 * drop the packet in mctp_rx anyway */ 441 if (mctp->route_policy == ROUTE_ENDPOINT && hdr->dest != bus->eid) 442 goto out; 443 444 flags = hdr->flags_seq_tag & (MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM); 445 tag = (hdr->flags_seq_tag >> MCTP_HDR_TAG_SHIFT) & MCTP_HDR_TAG_MASK; 446 seq = (hdr->flags_seq_tag >> MCTP_HDR_SEQ_SHIFT) & MCTP_HDR_SEQ_MASK; 447 448 switch (flags) { 449 case MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM: 450 /* single-packet message - send straight up to rx function, 451 * no need to create a message context */ 452 len = pkt->end - pkt->mctp_hdr_off - sizeof(struct mctp_hdr); 453 p = pkt->data + pkt->mctp_hdr_off + sizeof(struct mctp_hdr), 454 mctp_rx(mctp, bus, hdr->src, hdr->dest, p, len); 455 break; 456 457 case MCTP_HDR_FLAG_SOM: 458 /* start of a new message - start the new context for 459 * future message reception. If an existing context is 460 * already present, drop it. */ 461 ctx = mctp_msg_ctx_lookup(mctp, hdr->src, hdr->dest, tag); 462 if (ctx) { 463 mctp_msg_ctx_reset(ctx); 464 } else { 465 ctx = mctp_msg_ctx_create(mctp, 466 hdr->src, hdr->dest, tag); 467 } 468 469 rc = mctp_msg_ctx_add_pkt(ctx, pkt); 470 if (rc) { 471 mctp_msg_ctx_drop(ctx); 472 } else { 473 ctx->last_seq = seq; 474 } 475 476 break; 477 478 case MCTP_HDR_FLAG_EOM: 479 ctx = mctp_msg_ctx_lookup(mctp, hdr->src, hdr->dest, tag); 480 if (!ctx) 481 goto out; 482 483 exp_seq = (ctx->last_seq + 1) % 4; 484 485 if (exp_seq != seq) { 486 mctp_prdebug( 487 "Sequence number %d does not match expected %d", 488 seq, exp_seq); 489 mctp_msg_ctx_drop(ctx); 490 goto out; 491 } 492 493 rc = mctp_msg_ctx_add_pkt(ctx, pkt); 494 if (!rc) 495 mctp_rx(mctp, bus, ctx->src, ctx->dest, 496 ctx->buf, ctx->buf_size); 497 498 mctp_msg_ctx_drop(ctx); 499 break; 500 501 case 0: 502 /* Neither SOM nor EOM */ 503 ctx = mctp_msg_ctx_lookup(mctp, hdr->src,hdr->dest, tag); 504 if (!ctx) 505 goto out; 506 507 exp_seq = (ctx->last_seq + 1) % 4; 508 if (exp_seq != seq) { 509 mctp_prdebug( 510 "Sequence number %d does not match expected %d", 511 seq, exp_seq); 512 mctp_msg_ctx_drop(ctx); 513 goto out; 514 } 515 516 rc = mctp_msg_ctx_add_pkt(ctx, pkt); 517 if (rc) { 518 mctp_msg_ctx_drop(ctx); 519 goto out; 520 } 521 ctx->last_seq = seq; 522 523 break; 524 } 525 out: 526 mctp_pktbuf_free(pkt); 527 } 528 529 static int mctp_packet_tx(struct mctp_bus *bus, 530 struct mctp_pktbuf *pkt) 531 { 532 if (!bus->tx_enabled) 533 return -1; 534 535 return bus->binding->tx(bus->binding, pkt); 536 } 537 538 static void mctp_send_tx_queue(struct mctp_bus *bus) 539 { 540 struct mctp_pktbuf *pkt; 541 542 while ((pkt = bus->tx_queue_head)) { 543 int rc; 544 545 rc = mctp_packet_tx(bus, pkt); 546 if (rc) 547 break; 548 549 bus->tx_queue_head = pkt->next; 550 mctp_pktbuf_free(pkt); 551 } 552 553 if (!bus->tx_queue_head) 554 bus->tx_queue_tail = NULL; 555 556 } 557 558 void mctp_binding_set_tx_enabled(struct mctp_binding *binding, bool enable) 559 { 560 struct mctp_bus *bus = binding->bus; 561 bus->tx_enabled = enable; 562 if (enable) 563 mctp_send_tx_queue(bus); 564 } 565 566 static int mctp_message_tx_on_bus(struct mctp_bus *bus, mctp_eid_t src, 567 mctp_eid_t dest, void *msg, size_t msg_len) 568 { 569 size_t max_payload_len, payload_len, p; 570 struct mctp_pktbuf *pkt; 571 struct mctp_hdr *hdr; 572 int i; 573 574 max_payload_len = bus->binding->pkt_size - sizeof(*hdr); 575 576 mctp_prdebug("%s: Generating packets for transmission of %zu byte message from %hhu to %hhu", 577 __func__, msg_len, src, dest); 578 579 /* queue up packets, each of max MCTP_MTU size */ 580 for (p = 0, i = 0; p < msg_len; i++) { 581 payload_len = msg_len - p; 582 if (payload_len > max_payload_len) 583 payload_len = max_payload_len; 584 585 pkt = mctp_pktbuf_alloc(bus->binding, 586 payload_len + sizeof(*hdr)); 587 hdr = mctp_pktbuf_hdr(pkt); 588 589 /* todo: tags */ 590 hdr->ver = bus->binding->version & 0xf; 591 hdr->dest = dest; 592 hdr->src = src; 593 hdr->flags_seq_tag = MCTP_HDR_FLAG_TO | 594 (0 << MCTP_HDR_TAG_SHIFT); 595 596 if (i == 0) 597 hdr->flags_seq_tag |= MCTP_HDR_FLAG_SOM; 598 if (p + payload_len >= msg_len) 599 hdr->flags_seq_tag |= MCTP_HDR_FLAG_EOM; 600 hdr->flags_seq_tag |= 601 (i & MCTP_HDR_SEQ_MASK) << MCTP_HDR_SEQ_SHIFT; 602 603 memcpy(mctp_pktbuf_data(pkt), msg + p, payload_len); 604 605 /* add to tx queue */ 606 if (bus->tx_queue_tail) 607 bus->tx_queue_tail->next = pkt; 608 else 609 bus->tx_queue_head = pkt; 610 bus->tx_queue_tail = pkt; 611 612 p += payload_len; 613 } 614 615 mctp_prdebug("%s: Enqueued %d packets", __func__, i); 616 617 mctp_send_tx_queue(bus); 618 619 return 0; 620 } 621 622 int mctp_message_tx(struct mctp *mctp, mctp_eid_t eid, 623 void *msg, size_t msg_len) 624 { 625 struct mctp_bus *bus; 626 627 bus = find_bus_for_eid(mctp, eid); 628 return mctp_message_tx_on_bus(bus, bus->eid, eid, msg, msg_len); 629 } 630