1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2019-2021 Broadcom 3 * All rights reserved. 4 */ 5 6 #include <string.h> 7 8 #include <rte_common.h> 9 10 #include "tf_session.h" 11 #include "tf_common.h" 12 #include "tf_msg.h" 13 #include "tfp.h" 14 15 struct tf_session_client_create_parms { 16 /** 17 * [in] Pointer to the control channel name string 18 */ 19 char *ctrl_chan_name; 20 21 /** 22 * [out] Firmware Session Client ID 23 */ 24 union tf_session_client_id *session_client_id; 25 }; 26 27 struct tf_session_client_destroy_parms { 28 /** 29 * FW Session Client Identifier 30 */ 31 union tf_session_client_id session_client_id; 32 }; 33 34 /** 35 * Creates a Session and the associated client. 36 * 37 * [in] tfp 38 * Pointer to TF handle 39 * 40 * [in] parms 41 * Pointer to session client create parameters 42 * 43 * Returns 44 * - (0) if successful. 45 * - (-EINVAL) on failure. 46 * - (-ENOMEM) if max session clients has been reached. 47 */ 48 static int 49 tf_session_create(struct tf *tfp, 50 struct tf_session_open_session_parms *parms) 51 { 52 int rc; 53 struct tf_session *session = NULL; 54 struct tf_session_client *client; 55 struct tfp_calloc_parms cparms; 56 uint8_t fw_session_id; 57 uint8_t fw_session_client_id; 58 union tf_session_id *session_id; 59 60 TF_CHECK_PARMS2(tfp, parms); 61 62 /* Open FW session and get a new session_id */ 63 rc = tf_msg_session_open(tfp, 64 parms->open_cfg->ctrl_chan_name, 65 &fw_session_id, 66 &fw_session_client_id); 67 if (rc) { 68 /* Log error */ 69 if (rc == -EEXIST) 70 TFP_DRV_LOG(ERR, 71 "Session is already open, rc:%s\n", 72 strerror(-rc)); 73 else 74 TFP_DRV_LOG(ERR, 75 "Open message send failed, rc:%s\n", 76 strerror(-rc)); 77 78 parms->open_cfg->session_id.id = TF_FW_SESSION_ID_INVALID; 79 return rc; 80 } 81 82 /* Allocate session */ 83 cparms.nitems = 1; 84 cparms.size = sizeof(struct tf_session_info); 85 cparms.alignment = 0; 86 rc = tfp_calloc(&cparms); 87 if (rc) { 88 /* Log error */ 89 TFP_DRV_LOG(ERR, 90 "Failed to allocate session info, rc:%s\n", 91 strerror(-rc)); 92 goto cleanup; 93 } 94 tfp->session = (struct tf_session_info *)cparms.mem_va; 95 96 /* Allocate core data for the session */ 97 cparms.nitems = 1; 98 cparms.size = sizeof(struct tf_session); 99 cparms.alignment = 0; 100 rc = tfp_calloc(&cparms); 101 if (rc) { 102 /* Log error */ 103 TFP_DRV_LOG(ERR, 104 "Failed to allocate session data, rc:%s\n", 105 strerror(-rc)); 106 goto cleanup; 107 } 108 tfp->session->core_data = cparms.mem_va; 109 session_id = &parms->open_cfg->session_id; 110 111 /* Update Session Info, which is what is visible to the caller */ 112 tfp->session->ver.major = 0; 113 tfp->session->ver.minor = 0; 114 tfp->session->ver.update = 0; 115 116 tfp->session->session_id.internal.domain = session_id->internal.domain; 117 tfp->session->session_id.internal.bus = session_id->internal.bus; 118 tfp->session->session_id.internal.device = session_id->internal.device; 119 tfp->session->session_id.internal.fw_session_id = fw_session_id; 120 121 /* Initialize Session and Device, which is private */ 122 session = (struct tf_session *)tfp->session->core_data; 123 session->ver.major = 0; 124 session->ver.minor = 0; 125 session->ver.update = 0; 126 127 session->session_id.internal.domain = session_id->internal.domain; 128 session->session_id.internal.bus = session_id->internal.bus; 129 session->session_id.internal.device = session_id->internal.device; 130 session->session_id.internal.fw_session_id = fw_session_id; 131 /* Return the allocated session id */ 132 session_id->id = session->session_id.id; 133 134 session->shadow_copy = parms->open_cfg->shadow_copy; 135 136 /* Init session client list */ 137 ll_init(&session->client_ll); 138 139 /* Create the local session client, initialize and attach to 140 * the session 141 */ 142 cparms.nitems = 1; 143 cparms.size = sizeof(struct tf_session_client); 144 cparms.alignment = 0; 145 rc = tfp_calloc(&cparms); 146 if (rc) { 147 /* Log error */ 148 TFP_DRV_LOG(ERR, 149 "Failed to allocate session client, rc:%s\n", 150 strerror(-rc)); 151 goto cleanup; 152 } 153 client = cparms.mem_va; 154 155 /* Register FID with the client */ 156 rc = tfp_get_fid(tfp, &client->fw_fid); 157 if (rc) 158 return rc; 159 160 client->session_client_id.internal.fw_session_id = fw_session_id; 161 client->session_client_id.internal.fw_session_client_id = 162 fw_session_client_id; 163 164 tfp_memcpy(client->ctrl_chan_name, 165 parms->open_cfg->ctrl_chan_name, 166 TF_SESSION_NAME_MAX); 167 168 ll_insert(&session->client_ll, &client->ll_entry); 169 session->ref_count++; 170 171 rc = tf_dev_bind(tfp, 172 parms->open_cfg->device_type, 173 session->shadow_copy, 174 &parms->open_cfg->resources, 175 &session->dev); 176 /* Logging handled by dev_bind */ 177 if (rc) 178 return rc; 179 180 session->dev_init = true; 181 182 return 0; 183 184 cleanup: 185 tfp_free(tfp->session->core_data); 186 tfp_free(tfp->session); 187 tfp->session = NULL; 188 return rc; 189 } 190 191 /** 192 * Creates a Session Client on an existing Session. 193 * 194 * [in] tfp 195 * Pointer to TF handle 196 * 197 * [in] parms 198 * Pointer to session client create parameters 199 * 200 * Returns 201 * - (0) if successful. 202 * - (-EINVAL) on failure. 203 * - (-ENOMEM) if max session clients has been reached. 204 */ 205 static int 206 tf_session_client_create(struct tf *tfp, 207 struct tf_session_client_create_parms *parms) 208 { 209 int rc; 210 struct tf_session *session = NULL; 211 struct tf_session_client *client; 212 struct tfp_calloc_parms cparms; 213 union tf_session_client_id session_client_id; 214 215 TF_CHECK_PARMS2(tfp, parms); 216 217 /* Using internal version as session client may not exist yet */ 218 rc = tf_session_get_session_internal(tfp, &session); 219 if (rc) { 220 TFP_DRV_LOG(ERR, 221 "Failed to lookup session, rc:%s\n", 222 strerror(-rc)); 223 return rc; 224 } 225 226 client = tf_session_find_session_client_by_name(session, 227 parms->ctrl_chan_name); 228 if (client) { 229 TFP_DRV_LOG(ERR, 230 "Client %s, already registered with this session\n", 231 parms->ctrl_chan_name); 232 return -EOPNOTSUPP; 233 } 234 235 rc = tf_msg_session_client_register 236 (tfp, 237 parms->ctrl_chan_name, 238 &session_client_id.internal.fw_session_client_id); 239 if (rc) { 240 TFP_DRV_LOG(ERR, 241 "Failed to create client on session, rc:%s\n", 242 strerror(-rc)); 243 return rc; 244 } 245 246 /* Create the local session client, initialize and attach to 247 * the session 248 */ 249 cparms.nitems = 1; 250 cparms.size = sizeof(struct tf_session_client); 251 cparms.alignment = 0; 252 rc = tfp_calloc(&cparms); 253 if (rc) { 254 TFP_DRV_LOG(ERR, 255 "Failed to allocate session client, rc:%s\n", 256 strerror(-rc)); 257 goto cleanup; 258 } 259 client = cparms.mem_va; 260 261 /* Register FID with the client */ 262 rc = tfp_get_fid(tfp, &client->fw_fid); 263 if (rc) 264 return rc; 265 266 /* Build the Session Client ID by adding the fw_session_id */ 267 rc = tf_session_get_fw_session_id 268 (tfp, 269 &session_client_id.internal.fw_session_id); 270 if (rc) { 271 TFP_DRV_LOG(ERR, 272 "Session Firmware id lookup failed, rc:%s\n", 273 strerror(-rc)); 274 return rc; 275 } 276 277 tfp_memcpy(client->ctrl_chan_name, 278 parms->ctrl_chan_name, 279 TF_SESSION_NAME_MAX); 280 281 client->session_client_id.id = session_client_id.id; 282 283 ll_insert(&session->client_ll, &client->ll_entry); 284 285 session->ref_count++; 286 287 /* Build the return value */ 288 parms->session_client_id->id = session_client_id.id; 289 290 cleanup: 291 /* TBD - Add code to unregister newly create client from fw */ 292 293 return rc; 294 } 295 296 297 /** 298 * Destroys a Session Client on an existing Session. 299 * 300 * [in] tfp 301 * Pointer to TF handle 302 * 303 * [in] parms 304 * Pointer to the session client destroy parameters 305 * 306 * Returns 307 * - (0) if successful. 308 * - (-EINVAL) on failure. 309 * - (-ENOTFOUND) error, client not owned by the session. 310 * - (-ENOTSUPP) error, unable to destroy client as its the last 311 * client. Please use the tf_session_close(). 312 */ 313 static int 314 tf_session_client_destroy(struct tf *tfp, 315 struct tf_session_client_destroy_parms *parms) 316 { 317 int rc; 318 struct tf_session *tfs; 319 struct tf_session_client *client; 320 321 TF_CHECK_PARMS2(tfp, parms); 322 323 rc = tf_session_get_session(tfp, &tfs); 324 if (rc) { 325 TFP_DRV_LOG(ERR, 326 "Failed to lookup session, rc:%s\n", 327 strerror(-rc)); 328 return rc; 329 } 330 331 /* Check session owns this client and that we're not the last client */ 332 client = tf_session_get_session_client(tfs, 333 parms->session_client_id); 334 if (client == NULL) { 335 TFP_DRV_LOG(ERR, 336 "Client %d, not found within this session\n", 337 parms->session_client_id.id); 338 return -EINVAL; 339 } 340 341 /* If last client the request is rejected and cleanup should 342 * be done by session close. 343 */ 344 if (tfs->ref_count == 1) 345 return -EOPNOTSUPP; 346 347 rc = tf_msg_session_client_unregister 348 (tfp, 349 parms->session_client_id.internal.fw_session_client_id); 350 351 /* Log error, but continue. If FW fails we do not really have 352 * a way to fix this but the client would no longer be valid 353 * thus we remove from the session. 354 */ 355 if (rc) { 356 TFP_DRV_LOG(ERR, 357 "Client destroy on FW Failed, rc:%s\n", 358 strerror(-rc)); 359 } 360 361 ll_delete(&tfs->client_ll, &client->ll_entry); 362 363 /* Decrement the session ref_count */ 364 tfs->ref_count--; 365 366 tfp_free(client); 367 368 return rc; 369 } 370 371 int 372 tf_session_open_session(struct tf *tfp, 373 struct tf_session_open_session_parms *parms) 374 { 375 int rc; 376 struct tf_session_client_create_parms scparms; 377 378 TF_CHECK_PARMS2(tfp, parms); 379 380 /* Decide if we're creating a new session or session client */ 381 if (tfp->session == NULL) { 382 rc = tf_session_create(tfp, parms); 383 if (rc) { 384 TFP_DRV_LOG(ERR, 385 "Failed to create session, ctrl_chan_name:%s, rc:%s\n", 386 parms->open_cfg->ctrl_chan_name, 387 strerror(-rc)); 388 return rc; 389 } 390 391 TFP_DRV_LOG(INFO, 392 "Session created, session_client_id:%d, session_id:%d\n", 393 parms->open_cfg->session_client_id.id, 394 parms->open_cfg->session_id.id); 395 } else { 396 scparms.ctrl_chan_name = parms->open_cfg->ctrl_chan_name; 397 scparms.session_client_id = &parms->open_cfg->session_client_id; 398 399 /* Create the new client and get it associated with 400 * the session. 401 */ 402 rc = tf_session_client_create(tfp, &scparms); 403 if (rc) { 404 TFP_DRV_LOG(ERR, 405 "Failed to create client on session %d, rc:%s\n", 406 parms->open_cfg->session_id.id, 407 strerror(-rc)); 408 return rc; 409 } 410 411 TFP_DRV_LOG(INFO, 412 "Session Client:%d created on session:%d\n", 413 parms->open_cfg->session_client_id.id, 414 parms->open_cfg->session_id.id); 415 } 416 417 return 0; 418 } 419 420 int 421 tf_session_attach_session(struct tf *tfp __rte_unused, 422 struct tf_session_attach_session_parms *parms __rte_unused) 423 { 424 int rc = -EOPNOTSUPP; 425 426 TF_CHECK_PARMS2(tfp, parms); 427 428 TFP_DRV_LOG(ERR, 429 "Attach not yet supported, rc:%s\n", 430 strerror(-rc)); 431 return rc; 432 } 433 434 int 435 tf_session_close_session(struct tf *tfp, 436 struct tf_session_close_session_parms *parms) 437 { 438 int rc; 439 struct tf_session *tfs = NULL; 440 struct tf_session_client *client; 441 struct tf_dev_info *tfd = NULL; 442 struct tf_session_client_destroy_parms scdparms; 443 uint16_t fid; 444 445 TF_CHECK_PARMS2(tfp, parms); 446 447 rc = tf_session_get_session(tfp, &tfs); 448 if (rc) { 449 TFP_DRV_LOG(ERR, 450 "Session lookup failed, rc:%s\n", 451 strerror(-rc)); 452 return rc; 453 } 454 455 if (tfs->session_id.id == TF_SESSION_ID_INVALID) { 456 rc = -EINVAL; 457 TFP_DRV_LOG(ERR, 458 "Invalid session id, unable to close, rc:%s\n", 459 strerror(-rc)); 460 return rc; 461 } 462 463 /* Get the client, we need it independently of the closure 464 * type (client or session closure). 465 * 466 * We find the client by way of the fid. Thus one cannot close 467 * a client on behalf of someone else. 468 */ 469 rc = tfp_get_fid(tfp, &fid); 470 if (rc) 471 return rc; 472 473 client = tf_session_find_session_client_by_fid(tfs, 474 fid); 475 if (!client) { 476 rc = -EINVAL; 477 TFP_DRV_LOG(ERR, 478 "Client not part of the session, unable to close, rc:%s\n", 479 strerror(-rc)); 480 return rc; 481 } 482 483 /* In case multiple clients we chose to close those first */ 484 if (tfs->ref_count > 1) { 485 /* Linaro gcc can't static init this structure */ 486 memset(&scdparms, 487 0, 488 sizeof(struct tf_session_client_destroy_parms)); 489 490 scdparms.session_client_id = client->session_client_id; 491 /* Destroy requested client so its no longer 492 * registered with this session. 493 */ 494 rc = tf_session_client_destroy(tfp, &scdparms); 495 if (rc) { 496 TFP_DRV_LOG(ERR, 497 "Failed to unregister Client %d, rc:%s\n", 498 client->session_client_id.id, 499 strerror(-rc)); 500 return rc; 501 } 502 503 TFP_DRV_LOG(INFO, 504 "Closed session client, session_client_id:%d\n", 505 client->session_client_id.id); 506 507 TFP_DRV_LOG(INFO, 508 "session_id:%d, ref_count:%d\n", 509 tfs->session_id.id, 510 tfs->ref_count); 511 512 return 0; 513 } 514 515 /* Record the session we're closing so the caller knows the 516 * details. 517 */ 518 *parms->session_id = tfs->session_id; 519 520 rc = tf_session_get_device(tfs, &tfd); 521 if (rc) { 522 TFP_DRV_LOG(ERR, 523 "Device lookup failed, rc:%s\n", 524 strerror(-rc)); 525 return rc; 526 } 527 528 /* Unbind the device */ 529 rc = tf_dev_unbind(tfp, tfd); 530 if (rc) { 531 /* Log error */ 532 TFP_DRV_LOG(ERR, 533 "Device unbind failed, rc:%s\n", 534 strerror(-rc)); 535 } 536 537 rc = tf_msg_session_close(tfp); 538 if (rc) { 539 /* Log error */ 540 TFP_DRV_LOG(ERR, 541 "FW Session close failed, rc:%s\n", 542 strerror(-rc)); 543 } 544 545 /* Final cleanup as we're last user of the session thus we 546 * also delete the last client. 547 */ 548 ll_delete(&tfs->client_ll, &client->ll_entry); 549 tfp_free(client); 550 551 tfs->ref_count--; 552 553 TFP_DRV_LOG(INFO, 554 "Closed session, session_id:%d, ref_count:%d\n", 555 tfs->session_id.id, 556 tfs->ref_count); 557 558 tfs->dev_init = false; 559 560 tfp_free(tfp->session->core_data); 561 tfp_free(tfp->session); 562 tfp->session = NULL; 563 564 return 0; 565 } 566 567 bool 568 tf_session_is_fid_supported(struct tf_session *tfs, 569 uint16_t fid) 570 { 571 struct ll_entry *c_entry; 572 struct tf_session_client *client; 573 574 for (c_entry = tfs->client_ll.head; 575 c_entry != NULL; 576 c_entry = c_entry->next) { 577 client = (struct tf_session_client *)c_entry; 578 if (client->fw_fid == fid) 579 return true; 580 } 581 582 return false; 583 } 584 585 int 586 tf_session_get_session_internal(struct tf *tfp, 587 struct tf_session **tfs) 588 { 589 int rc = 0; 590 591 /* Skip using the check macro as we want to control the error msg */ 592 if (tfp->session == NULL || tfp->session->core_data == NULL) { 593 rc = -EINVAL; 594 TFP_DRV_LOG(ERR, 595 "Session not created, rc:%s\n", 596 strerror(-rc)); 597 return rc; 598 } 599 600 *tfs = (struct tf_session *)(tfp->session->core_data); 601 602 return rc; 603 } 604 605 int 606 tf_session_get_session(struct tf *tfp, 607 struct tf_session **tfs) 608 { 609 int rc; 610 uint16_t fw_fid; 611 bool supported = false; 612 613 rc = tf_session_get_session_internal(tfp, 614 tfs); 615 /* Logging done by tf_session_get_session_internal */ 616 if (rc) 617 return rc; 618 619 /* As session sharing among functions aka 'individual clients' 620 * is supported we have to assure that the client is indeed 621 * registered before we get deep in the TruFlow api stack. 622 */ 623 rc = tfp_get_fid(tfp, &fw_fid); 624 if (rc) { 625 TFP_DRV_LOG(ERR, 626 "Internal FID lookup\n, rc:%s\n", 627 strerror(-rc)); 628 return rc; 629 } 630 631 supported = tf_session_is_fid_supported(*tfs, fw_fid); 632 if (!supported) { 633 TFP_DRV_LOG 634 (ERR, 635 "Ctrl channel not registered with session\n, rc:%s\n", 636 strerror(-rc)); 637 return -EINVAL; 638 } 639 640 return rc; 641 } 642 643 struct tf_session_client * 644 tf_session_get_session_client(struct tf_session *tfs, 645 union tf_session_client_id session_client_id) 646 { 647 struct ll_entry *c_entry; 648 struct tf_session_client *client; 649 650 /* Skip using the check macro as we just want to return */ 651 if (tfs == NULL) 652 return NULL; 653 654 for (c_entry = tfs->client_ll.head; 655 c_entry != NULL; 656 c_entry = c_entry->next) { 657 client = (struct tf_session_client *)c_entry; 658 if (client->session_client_id.id == session_client_id.id) 659 return client; 660 } 661 662 return NULL; 663 } 664 665 struct tf_session_client * 666 tf_session_find_session_client_by_name(struct tf_session *tfs, 667 const char *ctrl_chan_name) 668 { 669 struct ll_entry *c_entry; 670 struct tf_session_client *client; 671 672 /* Skip using the check macro as we just want to return */ 673 if (tfs == NULL || ctrl_chan_name == NULL) 674 return NULL; 675 676 for (c_entry = tfs->client_ll.head; 677 c_entry != NULL; 678 c_entry = c_entry->next) { 679 client = (struct tf_session_client *)c_entry; 680 if (strncmp(client->ctrl_chan_name, 681 ctrl_chan_name, 682 TF_SESSION_NAME_MAX) == 0) 683 return client; 684 } 685 686 return NULL; 687 } 688 689 struct tf_session_client * 690 tf_session_find_session_client_by_fid(struct tf_session *tfs, 691 uint16_t fid) 692 { 693 struct ll_entry *c_entry; 694 struct tf_session_client *client; 695 696 /* Skip using the check macro as we just want to return */ 697 if (tfs == NULL) 698 return NULL; 699 700 for (c_entry = tfs->client_ll.head; 701 c_entry != NULL; 702 c_entry = c_entry->next) { 703 client = (struct tf_session_client *)c_entry; 704 if (client->fw_fid == fid) 705 return client; 706 } 707 708 return NULL; 709 } 710 711 int 712 tf_session_get_device(struct tf_session *tfs, 713 struct tf_dev_info **tfd) 714 { 715 *tfd = &tfs->dev; 716 717 return 0; 718 } 719 720 int 721 tf_session_get_fw_session_id(struct tf *tfp, 722 uint8_t *fw_session_id) 723 { 724 int rc; 725 struct tf_session *tfs = NULL; 726 727 /* Skip using the check macro as we want to control the error msg */ 728 if (tfp->session == NULL) { 729 rc = -EINVAL; 730 TFP_DRV_LOG(ERR, 731 "Session not created, rc:%s\n", 732 strerror(-rc)); 733 return rc; 734 } 735 736 if (fw_session_id == NULL) { 737 rc = -EINVAL; 738 TFP_DRV_LOG(ERR, 739 "Invalid Argument(s), rc:%s\n", 740 strerror(-rc)); 741 return rc; 742 } 743 744 rc = tf_session_get_session_internal(tfp, &tfs); 745 if (rc) 746 return rc; 747 748 *fw_session_id = tfs->session_id.internal.fw_session_id; 749 750 return 0; 751 } 752 753 int 754 tf_session_get_session_id(struct tf *tfp, 755 union tf_session_id *session_id) 756 { 757 int rc; 758 struct tf_session *tfs = NULL; 759 760 if (tfp->session == NULL) { 761 rc = -EINVAL; 762 TFP_DRV_LOG(ERR, 763 "Session not created, rc:%s\n", 764 strerror(-rc)); 765 return rc; 766 } 767 768 if (session_id == NULL) { 769 rc = -EINVAL; 770 TFP_DRV_LOG(ERR, 771 "Invalid Argument(s), rc:%s\n", 772 strerror(-rc)); 773 return rc; 774 } 775 776 /* Using internal version as session client may not exist yet */ 777 rc = tf_session_get_session_internal(tfp, &tfs); 778 if (rc) 779 return rc; 780 781 *session_id = tfs->session_id; 782 783 return 0; 784 } 785