1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2019-2020 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 /* In case multiple clients we chose to close those first */ 476 if (tfs->ref_count > 1) { 477 /* Linaro gcc can't static init this structure */ 478 memset(&scdparms, 479 0, 480 sizeof(struct tf_session_client_destroy_parms)); 481 482 scdparms.session_client_id = client->session_client_id; 483 /* Destroy requested client so its no longer 484 * registered with this session. 485 */ 486 rc = tf_session_client_destroy(tfp, &scdparms); 487 if (rc) { 488 TFP_DRV_LOG(ERR, 489 "Failed to unregister Client %d, rc:%s\n", 490 client->session_client_id.id, 491 strerror(-rc)); 492 return rc; 493 } 494 495 TFP_DRV_LOG(INFO, 496 "Closed session client, session_client_id:%d\n", 497 client->session_client_id.id); 498 499 TFP_DRV_LOG(INFO, 500 "session_id:%d, ref_count:%d\n", 501 tfs->session_id.id, 502 tfs->ref_count); 503 504 return 0; 505 } 506 507 /* Record the session we're closing so the caller knows the 508 * details. 509 */ 510 *parms->session_id = tfs->session_id; 511 512 rc = tf_session_get_device(tfs, &tfd); 513 if (rc) { 514 TFP_DRV_LOG(ERR, 515 "Device lookup failed, rc:%s\n", 516 strerror(-rc)); 517 return rc; 518 } 519 520 /* Unbind the device */ 521 rc = tf_dev_unbind(tfp, tfd); 522 if (rc) { 523 /* Log error */ 524 TFP_DRV_LOG(ERR, 525 "Device unbind failed, rc:%s\n", 526 strerror(-rc)); 527 } 528 529 rc = tf_msg_session_close(tfp); 530 if (rc) { 531 /* Log error */ 532 TFP_DRV_LOG(ERR, 533 "FW Session close failed, rc:%s\n", 534 strerror(-rc)); 535 } 536 537 /* Final cleanup as we're last user of the session thus we 538 * also delete the last client. 539 */ 540 ll_delete(&tfs->client_ll, &client->ll_entry); 541 tfp_free(client); 542 543 tfs->ref_count--; 544 545 TFP_DRV_LOG(INFO, 546 "Closed session, session_id:%d, ref_count:%d\n", 547 tfs->session_id.id, 548 tfs->ref_count); 549 550 tfs->dev_init = false; 551 552 tfp_free(tfp->session->core_data); 553 tfp_free(tfp->session); 554 tfp->session = NULL; 555 556 return 0; 557 } 558 559 bool 560 tf_session_is_fid_supported(struct tf_session *tfs, 561 uint16_t fid) 562 { 563 struct ll_entry *c_entry; 564 struct tf_session_client *client; 565 566 for (c_entry = tfs->client_ll.head; 567 c_entry != NULL; 568 c_entry = c_entry->next) { 569 client = (struct tf_session_client *)c_entry; 570 if (client->fw_fid == fid) 571 return true; 572 } 573 574 return false; 575 } 576 577 int 578 tf_session_get_session_internal(struct tf *tfp, 579 struct tf_session **tfs) 580 { 581 int rc = 0; 582 583 /* Skip using the check macro as we want to control the error msg */ 584 if (tfp->session == NULL || tfp->session->core_data == NULL) { 585 rc = -EINVAL; 586 TFP_DRV_LOG(ERR, 587 "Session not created, rc:%s\n", 588 strerror(-rc)); 589 return rc; 590 } 591 592 *tfs = (struct tf_session *)(tfp->session->core_data); 593 594 return rc; 595 } 596 597 int 598 tf_session_get_session(struct tf *tfp, 599 struct tf_session **tfs) 600 { 601 int rc; 602 uint16_t fw_fid; 603 bool supported = false; 604 605 rc = tf_session_get_session_internal(tfp, 606 tfs); 607 /* Logging done by tf_session_get_session_internal */ 608 if (rc) 609 return rc; 610 611 /* As session sharing among functions aka 'individual clients' 612 * is supported we have to assure that the client is indeed 613 * registered before we get deep in the TruFlow api stack. 614 */ 615 rc = tfp_get_fid(tfp, &fw_fid); 616 if (rc) { 617 TFP_DRV_LOG(ERR, 618 "Internal FID lookup\n, rc:%s\n", 619 strerror(-rc)); 620 return rc; 621 } 622 623 supported = tf_session_is_fid_supported(*tfs, fw_fid); 624 if (!supported) { 625 TFP_DRV_LOG 626 (ERR, 627 "Ctrl channel not registered with session\n, rc:%s\n", 628 strerror(-rc)); 629 return -EINVAL; 630 } 631 632 return rc; 633 } 634 635 struct tf_session_client * 636 tf_session_get_session_client(struct tf_session *tfs, 637 union tf_session_client_id session_client_id) 638 { 639 struct ll_entry *c_entry; 640 struct tf_session_client *client; 641 642 /* Skip using the check macro as we just want to return */ 643 if (tfs == NULL) 644 return NULL; 645 646 for (c_entry = tfs->client_ll.head; 647 c_entry != NULL; 648 c_entry = c_entry->next) { 649 client = (struct tf_session_client *)c_entry; 650 if (client->session_client_id.id == session_client_id.id) 651 return client; 652 } 653 654 return NULL; 655 } 656 657 struct tf_session_client * 658 tf_session_find_session_client_by_name(struct tf_session *tfs, 659 const char *ctrl_chan_name) 660 { 661 struct ll_entry *c_entry; 662 struct tf_session_client *client; 663 664 /* Skip using the check macro as we just want to return */ 665 if (tfs == NULL || ctrl_chan_name == NULL) 666 return NULL; 667 668 for (c_entry = tfs->client_ll.head; 669 c_entry != NULL; 670 c_entry = c_entry->next) { 671 client = (struct tf_session_client *)c_entry; 672 if (strncmp(client->ctrl_chan_name, 673 ctrl_chan_name, 674 TF_SESSION_NAME_MAX) == 0) 675 return client; 676 } 677 678 return NULL; 679 } 680 681 struct tf_session_client * 682 tf_session_find_session_client_by_fid(struct tf_session *tfs, 683 uint16_t fid) 684 { 685 struct ll_entry *c_entry; 686 struct tf_session_client *client; 687 688 /* Skip using the check macro as we just want to return */ 689 if (tfs == NULL) 690 return NULL; 691 692 for (c_entry = tfs->client_ll.head; 693 c_entry != NULL; 694 c_entry = c_entry->next) { 695 client = (struct tf_session_client *)c_entry; 696 if (client->fw_fid == fid) 697 return client; 698 } 699 700 return NULL; 701 } 702 703 int 704 tf_session_get_device(struct tf_session *tfs, 705 struct tf_dev_info **tfd) 706 { 707 *tfd = &tfs->dev; 708 709 return 0; 710 } 711 712 int 713 tf_session_get_fw_session_id(struct tf *tfp, 714 uint8_t *fw_session_id) 715 { 716 int rc; 717 struct tf_session *tfs = NULL; 718 719 /* Skip using the check macro as we want to control the error msg */ 720 if (tfp->session == NULL) { 721 rc = -EINVAL; 722 TFP_DRV_LOG(ERR, 723 "Session not created, rc:%s\n", 724 strerror(-rc)); 725 return rc; 726 } 727 728 if (fw_session_id == NULL) { 729 rc = -EINVAL; 730 TFP_DRV_LOG(ERR, 731 "Invalid Argument(s), rc:%s\n", 732 strerror(-rc)); 733 return rc; 734 } 735 736 rc = tf_session_get_session_internal(tfp, &tfs); 737 if (rc) 738 return rc; 739 740 *fw_session_id = tfs->session_id.internal.fw_session_id; 741 742 return 0; 743 } 744 745 int 746 tf_session_get_session_id(struct tf *tfp, 747 union tf_session_id *session_id) 748 { 749 int rc; 750 struct tf_session *tfs = NULL; 751 752 if (tfp->session == NULL) { 753 rc = -EINVAL; 754 TFP_DRV_LOG(ERR, 755 "Session not created, rc:%s\n", 756 strerror(-rc)); 757 return rc; 758 } 759 760 if (session_id == NULL) { 761 rc = -EINVAL; 762 TFP_DRV_LOG(ERR, 763 "Invalid Argument(s), rc:%s\n", 764 strerror(-rc)); 765 return rc; 766 } 767 768 /* Using internal version as session client may not exist yet */ 769 rc = tf_session_get_session_internal(tfp, &tfs); 770 if (rc) 771 return rc; 772 773 *session_id = tfs->session_id; 774 775 return 0; 776 } 777