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
tf_session_create(struct tf * tfp,struct tf_session_open_session_parms * parms)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
tf_session_client_create(struct tf * tfp,struct tf_session_client_create_parms * parms)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
tf_session_client_destroy(struct tf * tfp,struct tf_session_client_destroy_parms * parms)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
tf_session_open_session(struct tf * tfp,struct tf_session_open_session_parms * parms)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
tf_session_attach_session(struct tf * tfp __rte_unused,struct tf_session_attach_session_parms * parms __rte_unused)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
tf_session_close_session(struct tf * tfp,struct tf_session_close_session_parms * parms)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
tf_session_is_fid_supported(struct tf_session * tfs,uint16_t fid)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
tf_session_get_session_internal(struct tf * tfp,struct tf_session ** tfs)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
tf_session_get_session(struct tf * tfp,struct tf_session ** tfs)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 *
tf_session_get_session_client(struct tf_session * tfs,union tf_session_client_id session_client_id)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 *
tf_session_find_session_client_by_name(struct tf_session * tfs,const char * ctrl_chan_name)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 *
tf_session_find_session_client_by_fid(struct tf_session * tfs,uint16_t fid)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
tf_session_get_device(struct tf_session * tfs,struct tf_dev_info ** tfd)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
tf_session_get_fw_session_id(struct tf * tfp,uint8_t * fw_session_id)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
tf_session_get_session_id(struct tf * tfp,union tf_session_id * session_id)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