1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * Copyright 2023 Oxide Computer Company
27 */
28
29 #include <sys/conf.h>
30 #include <sys/stat.h>
31 #include <sys/modctl.h>
32 #include <sys/taskq.h>
33 #include <sys/mdi_impldefs.h>
34 #include <sys/sunmdi.h>
35 #include <sys/sunpm.h>
36 #include <sys/ib/mgt/ibdm/ibdm_impl.h>
37 #include <sys/ib/ibnex/ibnex.h>
38 #include <sys/ib/ibnex/ibnex_devctl.h>
39 #include <sys/ib/ibtl/ibti.h>
40 #include <sys/ib/ibtl/impl/ibtl_ibnex.h>
41 #include <sys/file.h>
42 #include <sys/hwconf.h>
43 #include <sys/fs/dv_node.h>
44
45 void ibnex_handle_hca_attach(void *);
46 static int ibnex_hca_bus_config_one(dev_info_t *, void *,
47 ddi_bus_config_op_t, uint_t *, dev_info_t **);
48
49 static ibnex_node_data_t *ibnex_get_cdip_info(dev_info_t *, char *,
50 dev_info_t **, ibnex_node_type_t *);
51 static int ibnex_prom_devname_to_pkey_n_portnum(
52 char *, ib_pkey_t *, uint8_t *);
53 static dev_info_t *ibnex_config_obp_args(dev_info_t *, char *);
54
55 extern int ibnex_busctl(dev_info_t *,
56 dev_info_t *, ddi_ctl_enum_t, void *, void *);
57 extern int ibnex_map_fault(dev_info_t *,
58 dev_info_t *, struct hat *, struct seg *,
59 caddr_t, struct devpage *, pfn_t, uint_t, uint_t);
60 static int ibnex_hca_bus_config(dev_info_t *, uint_t,
61 ddi_bus_config_op_t, void *, dev_info_t **);
62 static int ibnex_hca_bus_unconfig(dev_info_t *,
63 uint_t, ddi_bus_config_op_t, void *);
64 extern dev_info_t *ibnex_config_port_node(dev_info_t *, char *);
65 extern dev_info_t *ibnex_config_obp_args(dev_info_t *, char *);
66 extern int ibnex_ioc_bus_config_one(dev_info_t **, uint_t,
67 ddi_bus_config_op_t, void *, dev_info_t **, int *);
68 extern int ibnex_pseudo_config_one(
69 ibnex_node_data_t *, char *, dev_info_t *);
70 extern void ibnex_config_all_children(dev_info_t *);
71 extern void ibnex_pseudo_initnodes(void);
72
73 extern int ibnex_pseudo_mdi_config_one(int, void *, dev_info_t **,
74 char *, char *);
75 extern int ibnex_get_dip_from_guid(ib_guid_t, int,
76 ib_pkey_t, dev_info_t **);
77 extern dev_info_t *ibnex_commsvc_initnode(dev_info_t *,
78 ibdm_port_attr_t *, int, int, ib_pkey_t, int *,
79 int);
80 extern uint64_t ibnex_str2hex(char *, int, int *);
81 extern int ibnex_str2int(char *, int, int *);
82 extern void ibnex_create_hcasvc_nodes(
83 dev_info_t *, ibdm_port_attr_t *);
84 extern void ibnex_create_port_nodes(
85 dev_info_t *, ibdm_port_attr_t *);
86 extern void ibnex_create_vppa_nodes(
87 dev_info_t *, ibdm_port_attr_t *);
88 extern int ibnex_get_pkey_commsvc_index_portnum(
89 char *, int *, ib_pkey_t *, uint8_t *);
90
91 extern ibnex_t ibnex;
92 extern int ibnex_port_settling_time;
93
94 /*
95 * The bus_ops structure defines the capabilities of HCA nexus driver.
96 */
97 struct bus_ops ibnex_ci_busops = {
98 BUSO_REV,
99 nullbusmap, /* bus_map */
100 NULL, /* bus_get_intrspec */
101 NULL, /* bus_add_intrspec */
102 NULL, /* bus_remove_intrspec */
103 ibnex_map_fault, /* Map Fault */
104 ddi_no_dma_map, /* DMA related entry points */
105 NULL,
106 NULL,
107 NULL,
108 NULL,
109 NULL,
110 NULL,
111 NULL,
112 ibnex_busctl, /* bus_ctl */
113 ddi_bus_prop_op, /* bus_prop_op */
114 NULL, /* bus_get_eventcookie */
115 NULL, /* bus_add_eventcall */
116 NULL, /* bus_remove_eventcall */
117 NULL, /* bus_post_event */
118 NULL,
119 ibnex_hca_bus_config, /* bus config */
120 ibnex_hca_bus_unconfig /* bus unconfig */
121 };
122
123 /*
124 * ibnex_hca_bus_config()
125 *
126 * BUS_CONFIG_ONE:
127 * Enumerate the exact instance of the driver. Use the device node name
128 * to locate the exact instance.
129 * Query IBDM to find whether the hardware exits for the instance of the
130 * driver. If exists, create a device node and return NDI_SUCCESS.
131 *
132 * BUS_CONFIG_ALL:
133 * Enumerate all the instances of all the possible children (seen before
134 * and never seen before).
135 *
136 * BUS_CONFIG_DRIVER:
137 * Enumerate all the instances of a particular driver.
138 */
139 static int
ibnex_hca_bus_config(dev_info_t * parent,uint_t flag,ddi_bus_config_op_t op,void * devname,dev_info_t ** child)140 ibnex_hca_bus_config(dev_info_t *parent, uint_t flag,
141 ddi_bus_config_op_t op, void *devname, dev_info_t **child)
142 {
143 int ret = IBNEX_SUCCESS;
144 boolean_t enteredv;
145 char *srvname, nameaddr[MAXNAMELEN];
146 dev_info_t *cdip;
147 ibnex_node_data_t *node_data;
148 ibnex_port_node_t *port_node;
149
150 /*
151 * In a normal case HCA is setup as a phci.
152 * If an HCA is in maintenance mode, its phci is not set up
153 * but the driver is attached to update the firmware. In this
154 * case, do not configure the MPxIO clients.
155 */
156 if (mdi_component_is_phci(parent, NULL) == MDI_FAILURE) {
157 if (op == BUS_CONFIG_ALL || op == BUS_CONFIG_DRIVER)
158 return (NDI_SUCCESS);
159 else
160 return (NDI_FAILURE);
161 }
162
163 switch (op) {
164 case BUS_CONFIG_ONE:
165 IBTF_DPRINTF_L4("ibnex", "\thca_bus_config: CONFIG_ONE, "
166 "parent %p", parent);
167 ret = ibnex_hca_bus_config_one(
168 parent, devname, op, &flag, child);
169 break;
170
171 case BUS_CONFIG_OBP_ARGS:
172 mdi_devi_enter(parent, &enteredv);
173 cdip = ibnex_config_obp_args(parent, devname);
174 if (cdip) {
175 /*
176 * Boot case.
177 * Special handling because the "devname"
178 * format for the enumerated device is
179 * different.
180 */
181 node_data = ddi_get_parent_data(cdip);
182 port_node = &node_data->node_data.port_node;
183 if (node_data->node_type ==
184 IBNEX_VPPA_COMMSVC_NODE) {
185 srvname =
186 ibnex.ibnex_vppa_comm_svc_names[
187 port_node->port_commsvc_idx];
188 (void) snprintf(nameaddr, MAXNAMELEN,
189 "ibport@%x,%x,%s",
190 port_node->port_num,
191 port_node->port_pkey, srvname);
192 }
193 devname = (void *)nameaddr;
194 } else {
195 IBTF_DPRINTF_L2("ibnex", "\thca_bus_config: "
196 "CONFIG_OBP_ARGS : invalid state!!");
197
198 ret = IBNEX_FAILURE;
199 }
200 mdi_devi_exit(parent, enteredv);
201 break;
202
203 case BUS_CONFIG_ALL:
204 IBTF_DPRINTF_L4("ibnex",
205 "\thca_bus_config: CONFIG_ALL parent %p", parent);
206 ibnex_config_all_children(parent);
207 break;
208
209 case BUS_CONFIG_DRIVER:
210 IBTF_DPRINTF_L4("ibnex", "\thca_bus_config: "
211 "CONFIG_DRIVER parent %p", parent);
212 ibnex_config_all_children(parent);
213 break;
214
215 default:
216 IBTF_DPRINTF_L4("ibnex", "\thca_bus_config: error");
217 ret = IBNEX_FAILURE;
218 break;
219 }
220
221
222 if (ret == IBNEX_SUCCESS) {
223 if (op == BUS_CONFIG_OBP_ARGS)
224 op = BUS_CONFIG_ONE;
225
226 ret = ndi_busop_bus_config(
227 parent, flag, op, devname, child, 0);
228 IBTF_DPRINTF_L4("ibnex", "\thca_bus_config:"
229 "ndi_busop_bus_config : retval %d", ret);
230 return (ret);
231 }
232
233 return (NDI_FAILURE);
234 }
235
236 /*
237 * ibnex_hca_bus_unconfig()
238 *
239 * Unconfigure a particular device node or all instance of a device
240 * driver device or all children of IBnex
241 */
242 static int
ibnex_hca_bus_unconfig(dev_info_t * parent,uint_t flag,ddi_bus_config_op_t op,void * device_name)243 ibnex_hca_bus_unconfig(dev_info_t *parent,
244 uint_t flag, ddi_bus_config_op_t op, void *device_name)
245 {
246
247 if (ndi_busop_bus_unconfig(parent, flag, op, device_name) !=
248 DDI_SUCCESS)
249 return (DDI_FAILURE);
250
251 if ((op == BUS_UNCONFIG_ALL || op == BUS_UNCONFIG_DRIVER) &&
252 (flag & NDI_UNCONFIG)) {
253 ibnex_node_data_t *ndp;
254 dev_info_t *dip = NULL;
255 major_t major = (major_t)(uintptr_t)device_name;
256
257 mutex_enter(&ibnex.ibnex_mutex);
258
259 if (major == -1) {
260 /*
261 * HCA dip. When major number is -1 HCA is
262 * going away cleanup all the port nodes.
263 */
264 for (ndp = ibnex.ibnex_port_node_head;
265 ndp; ndp = ndp->node_next) {
266 ibnex_port_node_t *port_node;
267
268 port_node = &ndp->node_data.port_node;
269 if (port_node->port_pdip == parent) {
270 port_node->port_pdip = NULL;
271 ndp->node_dip = NULL;
272 ndp->node_state =
273 IBNEX_CFGADM_UNCONFIGURED;
274 }
275 }
276 } else {
277 /*
278 * HCA dip. Cleanup only the port nodes that
279 * match the major number.
280 */
281 for (ndp = ibnex.ibnex_port_node_head;
282 ndp; ndp = ndp->node_next) {
283 ibnex_port_node_t *port_node;
284
285 port_node = &ndp->node_data.port_node;
286 dip = ndp->node_dip;
287 if (dip && (ddi_driver_major(dip) ==
288 major) && port_node->port_pdip ==
289 parent) {
290 port_node->port_pdip = NULL;
291 ndp->node_dip = NULL;
292 ndp->node_state =
293 IBNEX_CFGADM_UNCONFIGURED;
294 }
295 }
296 }
297 mutex_exit(&ibnex.ibnex_mutex);
298 }
299 return (DDI_SUCCESS);
300 }
301
302 /*
303 * ibnex_config_obp_args()
304 * Configures a particular port node for a IP over IB communication
305 * service.
306 * The format of the input string "devname" is
307 * port=x,pkey=y,protocol=ip
308 * Thr format of the node name created here is
309 * ibport@<Port#>,<pkey>,<service name>
310 * where pkey = 0 for port communication service nodes
311 * Returns "dev_info_t" of the "child" node just created
312 * NULL when failed to enumerate the child node
313 *
314 */
315 static dev_info_t *
ibnex_config_obp_args(dev_info_t * parent,char * devname)316 ibnex_config_obp_args(dev_info_t *parent, char *devname)
317 {
318 int ii, index;
319 int rval, iter = 0;
320 char *temp;
321 uint8_t port_num;
322 ib_guid_t hca_guid, port_guid;
323 ib_pkey_t pkey;
324 dev_info_t *cdip;
325 boolean_t displayed = B_FALSE;
326 ibdm_port_attr_t *port_attr;
327
328 IBTF_DPRINTF_L4("ibnex", "\tconfig_obp_args: %s", devname);
329
330 /* Is this OBP node for IPoIB ? */
331 temp = devname;
332 do {
333 temp = strstr(temp, ",protocol=ip");
334 if (temp == NULL)
335 break;
336
337 if (strlen(devname) > (int)((temp - devname) + 12)) {
338 if (temp[12] == ',')
339 break;
340 } else {
341 break;
342 }
343 temp++;
344 } while (temp);
345
346 if (temp == NULL)
347 return (NULL);
348 if (ibnex_prom_devname_to_pkey_n_portnum(
349 devname, &pkey, &port_num) != IBNEX_SUCCESS) {
350 return (NULL);
351 }
352 for (index = 0; index < ibnex.ibnex_nvppa_comm_svcs; index++) {
353 if (strcmp(ibnex.ibnex_vppa_comm_svc_names[index],
354 "ipib") == 0) {
355 break;
356 }
357 }
358
359 hca_guid = ibtl_ibnex_hcadip2guid(parent);
360 if ((port_attr = ibdm_ibnex_probe_hcaport(
361 hca_guid, port_num)) == NULL) {
362 IBTF_DPRINTF_L2("ibnex",
363 "\tconfig_port_node: Port does not exist");
364 return (NULL);
365 }
366
367 /* Wait until "port is up" */
368 while (port_attr->pa_state != IBT_PORT_ACTIVE) {
369 ibdm_ibnex_free_port_attr(port_attr);
370 delay(drv_usectohz(10000));
371 if ((port_attr = ibdm_ibnex_probe_hcaport(
372 hca_guid, port_num)) == NULL) {
373 return (NULL);
374 }
375 if (iter++ == 400) {
376 if (displayed == B_FALSE) {
377 cmn_err(CE_NOTE, "\tWaiting for Port %d "
378 "initialization", port_attr->pa_port_num);
379 displayed = B_TRUE;
380 }
381 }
382 }
383 IBTF_DPRINTF_L4("ibnex", "\tPort is initialized");
384
385 mutex_enter(&ibnex.ibnex_mutex);
386 port_guid = port_attr->pa_port_guid;
387 rval = ibnex_get_dip_from_guid(port_guid, index, pkey, &cdip);
388 if (rval == IBNEX_SUCCESS && cdip != NULL) {
389 IBTF_DPRINTF_L4("ibnex", "\tconfig_port_node: Node exists");
390 mutex_exit(&ibnex.ibnex_mutex);
391 ibdm_ibnex_free_port_attr(port_attr);
392 return (cdip);
393 }
394 for (ii = 0; ii < port_attr->pa_npkeys; ii++) {
395 if (pkey == port_attr->pa_pkey_tbl[ii].pt_pkey) {
396 cdip = ibnex_commsvc_initnode(parent, port_attr,
397 index, IBNEX_VPPA_COMMSVC_NODE, pkey, &rval,
398 IBNEX_CFGADM_ENUMERATE);
399 IBTF_DPRINTF_L5("ibnex",
400 "\t ibnex_commsvc_initnode rval %x", rval);
401 break;
402 }
403 }
404 mutex_exit(&ibnex.ibnex_mutex);
405
406 ibdm_ibnex_free_port_attr(port_attr);
407 return (cdip);
408 }
409
410
411 /*
412 * ibnex_prom_devname_to_pkey_n_portnum()
413 * Parses the device node name and extracts "PKEY" and "port#"
414 * Returns IBNEX_SUCCESS/IBNEX_FAILURE
415 */
416 static int
ibnex_prom_devname_to_pkey_n_portnum(char * devname,ib_pkey_t * pkey,uint8_t * port)417 ibnex_prom_devname_to_pkey_n_portnum(
418 char *devname, ib_pkey_t *pkey, uint8_t *port)
419 {
420 int ret = IBNEX_SUCCESS;
421 char *tmp, *tmp1;
422
423 if ((tmp = strstr(devname, "port=")) != NULL) {
424 if ((tmp = strchr(++tmp, '=')) != NULL)
425 if ((tmp1 = strchr(++tmp, ',')) != NULL)
426 *port = ibnex_str2int(tmp, (tmp1 - tmp), &ret);
427 } else
428 ret = IBNEX_FAILURE;
429
430 if ((ret == IBNEX_SUCCESS) &&
431 (tmp = strstr(devname, "pkey=")) != NULL) {
432 if ((tmp = strchr(++tmp, '=')) != NULL)
433 if ((tmp1 = strchr(++tmp, ',')) != NULL)
434 *pkey = ibnex_str2hex(tmp, (tmp1 - tmp), &ret);
435 } else
436 ret = IBNEX_FAILURE;
437
438 return (ret);
439 }
440
441 static ibnex_node_data_t *
ibnex_get_cdip_info(dev_info_t * parent,char * devname,dev_info_t ** cdip,ibnex_node_type_t * type)442 ibnex_get_cdip_info(dev_info_t *parent,
443 char *devname, dev_info_t **cdip, ibnex_node_type_t *type)
444 {
445 char *device_name, *cname = NULL, *caddr = NULL;
446 int len;
447 ibnex_node_data_t *node_data = NULL;
448
449 len = strlen((char *)devname) + 1;
450 device_name = i_ddi_strdup(devname, KM_SLEEP);
451 i_ddi_parse_name(device_name, &cname, &caddr, NULL);
452
453 IBTF_DPRINTF_L4("ibnex",
454 "\tfind_child_dip: cname %s addr %s", cname, caddr);
455
456 if (strncmp(cname, IBNEX_IOC_CNAME, 3) == 0)
457 *type = IBNEX_IOC_NODE;
458 else if (strncmp(cname, IBNEX_IBPORT_CNAME, 3) == 0)
459 *type = IBNEX_HCA_CHILD_NODE;
460 else
461 *type = IBNEX_PSEUDO_NODE;
462
463 *cdip = ndi_devi_findchild(parent, devname);
464
465 IBTF_DPRINTF_L4("ibnex",
466 "\tfind_child_dip: cdip %p type %x", *cdip, *type);
467
468 if (*cdip)
469 node_data = ddi_get_parent_data(*cdip);
470 kmem_free(device_name, len);
471
472 return (node_data);
473 }
474
475 static int
ibnex_hca_bus_config_one(dev_info_t * parent,void * devname,ddi_bus_config_op_t op,uint_t * flag,dev_info_t ** child)476 ibnex_hca_bus_config_one(dev_info_t *parent, void *devname,
477 ddi_bus_config_op_t op, uint_t *flag, dev_info_t **child)
478 {
479 int ret = IBNEX_SUCCESS, len, need_bus_config;
480 char *device_name, *caddr, *cname;
481 dev_info_t *cdip;
482 ibnex_node_data_t *node_data;
483 ibnex_node_type_t node_type;
484 int index;
485 uint8_t port_num;
486 ib_pkey_t pkey;
487 boolean_t enteredv;
488
489 len = strlen((char *)devname) + 1;
490 device_name = i_ddi_strdup(devname, KM_SLEEP);
491 i_ddi_parse_name(device_name, &cname, &caddr, NULL);
492
493 if (caddr == NULL || (strlen(caddr) == 0)) {
494 IBTF_DPRINTF_L2("ibnex",
495 "\thca_bus_config: Invalid device node address");
496 kmem_free(device_name, len);
497 return (IBNEX_FAILURE);
498 }
499
500 ndi_devi_enter(parent);
501 node_data = ibnex_get_cdip_info(
502 parent, devname, &cdip, &node_type);
503 ndi_devi_exit(parent);
504
505 if (cdip) {
506 if ((node_data) && (node_data->node_type ==
507 IBNEX_PORT_COMMSVC_NODE)) {
508 if (node_data->node_dip == NULL) {
509 node_data->node_dip = cdip;
510 node_data->node_data.port_node.port_pdip =
511 parent;
512 }
513 }
514 }
515
516 /*
517 * If child dip is present, just return
518 * from here.
519 */
520 if (cdip != NULL || (node_data != NULL &&
521 node_data->node_dip != NULL)) {
522 goto end;
523 }
524
525 switch (node_type) {
526
527 case IBNEX_IOC_NODE:
528 ret = ibnex_ioc_bus_config_one(&parent, *flag,
529 op, devname, child, &need_bus_config);
530 if (!need_bus_config) {
531 kmem_free(device_name, len);
532 return (ret);
533 }
534 break;
535
536 case IBNEX_PSEUDO_NODE:
537 ret = IBNEX_SUCCESS;
538 mdi_devi_enter(parent, &enteredv);
539 ibnex_pseudo_initnodes();
540 mutex_enter(&ibnex.ibnex_mutex);
541 ret = ibnex_pseudo_config_one(NULL,
542 caddr, parent);
543 mutex_exit(&ibnex.ibnex_mutex);
544 mdi_devi_exit(parent, enteredv);
545 break;
546
547 default:
548 if (ibnex_get_pkey_commsvc_index_portnum(devname,
549 &index, &pkey, &port_num) != IBNEX_SUCCESS) {
550 IBTF_DPRINTF_L2("ibnex",
551 "\tconfig_port_node: Invalid Service Name");
552 kmem_free(device_name, len);
553 return (IBNEX_FAILURE);
554 }
555
556 if ((pkey != 0) && (port_num != 0)) {
557 if (strcmp("ipib",
558 ibnex.ibnex_vppa_comm_svc_names[index]) == 0) {
559 IBTF_DPRINTF_L2("ibnex",
560 "Skipping IBD devices... ");
561 break;
562 }
563 }
564
565 ndi_devi_enter(parent);
566 cdip = ibnex_config_port_node(parent, devname);
567 if (cdip)
568 ret = IBNEX_SUCCESS;
569 else
570 ret = IBNEX_FAILURE;
571 ndi_devi_exit(parent);
572 break;
573 }
574 end:
575 if (node_type == IBNEX_HCA_CHILD_NODE) {
576 /* Allows enumeration under PHCI */
577 *flag |= NDI_MDI_FALLBACK;
578 }
579 kmem_free(device_name, len);
580 return (ret);
581 }
582
583 void
ibnex_handle_hca_attach(void * cb_arg)584 ibnex_handle_hca_attach(void *cb_arg)
585 {
586 ib_guid_t hca_guid = *((ib_guid_t *)cb_arg);
587 dev_info_t *phci;
588 int ii;
589 ibdm_hca_list_t *hca_list;
590
591 IBTF_DPRINTF_L4("ibnex", "handle_hca_attach(%llx)", hca_guid);
592
593 phci = ibtl_ibnex_hcaguid2dip(hca_guid);
594
595 /*
596 * Enumerate children of this HCA, port nodes,
597 * VPPA & HCA_SVC nodes. Use ndi_devi_enter() for
598 * locking. IB Nexus is enumerating the children
599 * of HCA, not MPXIO clients.
600 */
601 ndi_devi_enter(phci);
602 ibdm_ibnex_port_settle_wait(hca_guid, ibnex_port_settling_time);
603 hca_list = ibdm_ibnex_get_hca_info_by_guid(hca_guid);
604 if (hca_list == NULL) {
605 ndi_devi_exit(phci);
606 kmem_free(cb_arg, sizeof (ib_guid_t));
607 return;
608 }
609 ibnex_create_hcasvc_nodes(phci, hca_list->hl_hca_port_attr);
610 for (ii = 0; ii < hca_list->hl_nports; ii++) {
611 ibnex_create_port_nodes(phci, &hca_list->hl_port_attr[ii]);
612 ibnex_create_vppa_nodes(phci, &hca_list->hl_port_attr[ii]);
613 }
614 ibdm_ibnex_free_hca_list(hca_list);
615 ndi_devi_exit(phci);
616 kmem_free(cb_arg, sizeof (ib_guid_t));
617 }
618