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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 
28 #include	<sun_sas.h>
29 #include	<sys/modctl.h>
30 #include	<sys/types.h>
31 #include	<netinet/in.h>
32 #include	<inttypes.h>
33 #include	<ctype.h>
34 
35 /* free hba port info for the given hba */
36 static void
37 free_hba_port(struct sun_sas_hba *hba_ptr)
38 {
39 	struct sun_sas_port	*hba_port = NULL;
40 	struct sun_sas_port	*last_hba_port = NULL;
41 	struct sun_sas_port	*tgt_port = NULL;
42 	struct sun_sas_port	*last_tgt_port = NULL;
43 	struct ScsiEntryList	*scsi_info = NULL;
44 	struct ScsiEntryList	*last_scsi_info = NULL;
45 	struct phy_info		*phy_ptr = NULL;
46 	struct phy_info		*last_phy = NULL;
47 
48 	/* Free the nested structures (port and attached port) */
49 	hba_port = hba_ptr->first_port;
50 	while (hba_port != NULL) {
51 		/* Free discovered port structure list. */
52 		tgt_port = hba_port->first_attached_port;
53 		while (tgt_port != NULL) {
54 			/* Free target mapping data list first. */
55 			scsi_info = tgt_port->scsiInfo;
56 			while (scsi_info != NULL) {
57 				last_scsi_info = scsi_info;
58 				scsi_info = scsi_info->next;
59 				free(last_scsi_info);
60 			}
61 			last_tgt_port = tgt_port;
62 			tgt_port = tgt_port->next;
63 			free(last_tgt_port->port_attributes.\
64 			    PortSpecificAttribute.SASPort);
65 			free(last_tgt_port);
66 		}
67 		hba_port->first_attached_port = NULL;
68 
69 		phy_ptr = hba_port->first_phy;
70 		while (phy_ptr != NULL) {
71 			last_phy = phy_ptr;
72 			phy_ptr = phy_ptr->next;
73 			free(last_phy);
74 		}
75 		hba_port->first_phy = NULL;
76 
77 		last_hba_port = hba_port;
78 		hba_port = hba_port->next;
79 		free(last_hba_port->port_attributes.\
80 		    PortSpecificAttribute.SASPort);
81 		free(last_hba_port);
82 	}
83 
84 	hba_ptr->first_port = NULL;
85 }
86 
87 /*
88  * Internal routine for adding an HBA port
89  */
90 static HBA_STATUS
91 add_hba_port_info(di_node_t portNode, struct sun_sas_hba *hba_ptr, int protocol)
92 {
93 	const char		    ROUTINE[] = "add_hba_port_info";
94 	struct sun_sas_port	    *port_ptr;
95 	char			    *portDevpath;
96 	int			    *propIntData;
97 	char			    *propStringData;
98 	uint64_t		    tmpAddr;
99 	char			    *charptr, cntlLink[MAXPATHLEN] = {'\0'};
100 	int			    rval;
101 	uint_t			    state = HBA_PORTSTATE_UNKNOWN;
102 
103 	if (hba_ptr == NULL) {
104 		log(LOG_DEBUG, ROUTINE,
105 		    "Sun_sas handle ptr set to NULL.");
106 		return (HBA_STATUS_ERROR_ARG);
107 	}
108 
109 	if ((port_ptr = (struct sun_sas_port *)calloc(1,
110 	    sizeof (struct sun_sas_port))) == NULL) {
111 		OUT_OF_MEMORY(ROUTINE);
112 		return (HBA_STATUS_ERROR);
113 	}
114 
115 	if ((port_ptr->port_attributes.PortSpecificAttribute.SASPort =
116 	    (struct SMHBA_SAS_Port *)calloc(1, sizeof (struct SMHBA_SAS_Port)))
117 	    == NULL) {
118 		OUT_OF_MEMORY(ROUTINE);
119 		return (HBA_STATUS_ERROR);
120 	}
121 
122 	if ((portDevpath = di_devfs_path(portNode)) == NULL) {
123 		log(LOG_DEBUG, ROUTINE,
124 		    "Unable to get device path from HBA Port Node.");
125 		S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
126 		S_FREE(port_ptr);
127 		return (HBA_STATUS_ERROR);
128 	}
129 
130 	state = di_state(portNode);
131 	if (((state & DI_DRIVER_DETACHED) == DI_DRIVER_DETACHED) ||
132 	    ((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE)) {
133 		log(LOG_DEBUG, ROUTINE,
134 		    "HBA port node %s is either OFFLINE or DETACHED",
135 		    portDevpath);
136 		port_ptr->port_attributes.PortState = HBA_PORTSTATE_OFFLINE;
137 	} else {
138 		port_ptr->port_attributes.PortState = HBA_PORTSTATE_ONLINE;
139 	}
140 
141 	port_ptr->port_attributes.PortType = HBA_PORTTYPE_SASDEVICE;
142 
143 	(void) strlcpy(port_ptr->device_path, portDevpath, MAXPATHLEN + 1);
144 
145 	if (lookupControllerLink(portDevpath, (char *)cntlLink) ==
146 	    HBA_STATUS_OK) {
147 		(void) strlcpy(port_ptr->port_attributes.OSDeviceName, cntlLink,
148 		    sizeof (port_ptr->port_attributes.OSDeviceName));
149 		if ((charptr = strrchr(cntlLink, '/')) != NULL) {
150 			charptr++;
151 		}
152 		if (charptr[0] ==  'c') {
153 			port_ptr->cntlNumber = atoi(++charptr);
154 		} else {
155 			port_ptr->cntlNumber = -1;
156 		}
157 	} else {
158 		(void) snprintf(port_ptr->port_attributes.OSDeviceName,
159 		    sizeof (port_ptr->port_attributes.OSDeviceName),
160 		    "%s%s%s", DEVICES_DIR, portDevpath, SCSI_SUFFIX);
161 	}
162 
163 	di_devfs_path_free(portDevpath);
164 
165 	port_ptr->port_attributes.PortSpecificAttribute.
166 	    SASPort->PortProtocol = protocol;
167 
168 	rval = di_prop_lookup_strings(DDI_DEV_T_ANY, portNode,
169 	    "initiator-port", &propStringData);
170 	if (rval < 0) {
171 		log(LOG_DEBUG, ROUTINE,
172 		    "Unable to get initiator-port from HBA port node %s.",
173 		    port_ptr->port_attributes.OSDeviceName);
174 		S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
175 		S_FREE(port_ptr);
176 		return (HBA_STATUS_ERROR);
177 	} else {
178 		for (charptr = propStringData; *charptr != '\0'; charptr++) {
179 			if (isxdigit(*charptr)) {
180 				break;
181 			}
182 		}
183 		if (*charptr != '\0') {
184 			tmpAddr = htonll(strtoll(charptr, NULL, 16));
185 			(void) memcpy(port_ptr->port_attributes.
186 			    PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
187 			    &tmpAddr, 8);
188 		} else {
189 			log(LOG_DEBUG, ROUTINE,
190 			    "No proper intiator-port prop value on HBA port %s",
191 			    port_ptr->port_attributes.OSDeviceName);
192 		}
193 	}
194 
195 	rval = di_prop_lookup_strings(DDI_DEV_T_ANY, portNode,
196 	    "attached-port", &propStringData);
197 	if (rval < 0) {
198 		log(LOG_DEBUG, ROUTINE,
199 		    "Unable to get attached-port from HBA port node %s.",
200 		    port_ptr->port_attributes.OSDeviceName);
201 		S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
202 		S_FREE(port_ptr);
203 		return (HBA_STATUS_ERROR);
204 	} else {
205 		for (charptr = propStringData; *charptr != '\0'; charptr++) {
206 			if (isxdigit(*charptr)) {
207 				break;
208 			}
209 		}
210 		if (*charptr != '\0') {
211 			tmpAddr = htonll(strtoll(charptr, NULL, 16));
212 			(void) memcpy(port_ptr->port_attributes.
213 			    PortSpecificAttribute.SASPort->
214 			    AttachedSASAddress.wwn, &tmpAddr, 8);
215 		} else {
216 			/* continue even if the attached port is NULL. */
217 			log(LOG_DEBUG, ROUTINE,
218 			    "No proper attached-port prop value: "
219 			    "HBA port Local SAS Address(%016llx)",
220 			    wwnConversion(port_ptr->port_attributes.
221 			    PortSpecificAttribute.
222 			    SASPort->LocalSASAddress.wwn));
223 		}
224 	}
225 
226 	rval = di_prop_lookup_ints(DDI_DEV_T_ANY, portNode,
227 	    "num-phys", &propIntData);
228 	if (rval < 0) {
229 		log(LOG_DEBUG, ROUTINE,
230 		    "Unable to get NumberofPhys from HBA port %s.",
231 		    port_ptr->port_attributes.OSDeviceName);
232 		S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
233 		S_FREE(port_ptr);
234 		return (HBA_STATUS_ERROR);
235 	} else {
236 		port_ptr->port_attributes.PortSpecificAttribute.\
237 		    SASPort->NumberofPhys = *propIntData;
238 	}
239 
240 	if (port_ptr->port_attributes.PortSpecificAttribute.\
241 	    SASPort->NumberofPhys > 0) {
242 		if (get_phy_info(portNode, port_ptr) != HBA_STATUS_OK) {
243 			log(LOG_DEBUG, ROUTINE,
244 			    "Failed to get phy info on HBA port %s.",
245 			    port_ptr->port_attributes.OSDeviceName);
246 			S_FREE(port_ptr->port_attributes.
247 			    PortSpecificAttribute.SASPort);
248 			S_FREE(port_ptr);
249 		}
250 	}
251 
252 	/* devtree_attached_devices(portSubtreenode, port_ptr); */
253 	if (devtree_attached_devices(portNode, port_ptr) != HBA_STATUS_OK) {
254 		log(LOG_DEBUG, ROUTINE,
255 		    "Failed to get attached device info HBA port %s.",
256 		    port_ptr->port_attributes.OSDeviceName);
257 		S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
258 		S_FREE(port_ptr);
259 	}
260 
261 	fillDomainPortWWN(port_ptr);
262 
263 	/* add new port onto hba handle list */
264 	if (hba_ptr->first_port == NULL) {
265 		port_ptr->index = 0;
266 		hba_ptr->first_port = port_ptr;
267 	} else {
268 		port_ptr->index = hba_ptr->first_port->index + 1;
269 		port_ptr->next = hba_ptr->first_port;
270 		hba_ptr->first_port = port_ptr;
271 	}
272 
273 	return (HBA_STATUS_OK);
274 }
275 
276 HBA_STATUS
277 refresh_hba(di_node_t hbaNode, struct sun_sas_hba *hba_ptr)
278 {
279 	const char	ROUTINE[] = "refresh_hba";
280 	di_node_t	portNode;
281 	int		protocol = 0;
282 	int		*propIntData;
283 
284 	/*
285 	 * clean up existing hba port, discovered target, phy info.
286 	 * leave open handles intact.
287 	 */
288 	free_hba_port(hba_ptr);
289 
290 	if ((portNode = di_child_node(hbaNode)) == NULL) {
291 		log(LOG_DEBUG, ROUTINE,
292 		    "HBA node doesn't have iport child.");
293 		return (HBA_STATUS_ERROR);
294 	}
295 
296 	if ((di_prop_lookup_ints(DDI_DEV_T_ANY, hbaNode,
297 	    "supported-protocol", &propIntData)) == -1) {
298 		log(LOG_DEBUG, ROUTINE,
299 		    "Unable to get supported-protocol from HBA node.");
300 	} else {
301 		protocol = *propIntData;
302 	}
303 
304 	while (portNode != DI_NODE_NIL) {
305 		if (add_hba_port_info(portNode, hba_ptr, protocol)
306 		    == HBA_STATUS_ERROR) {
307 			S_FREE(hba_ptr->first_port);
308 			S_FREE(hba_ptr);
309 			return (HBA_STATUS_ERROR);
310 		}
311 		portNode = di_sibling_node(portNode);
312 	}
313 
314 	return (HBA_STATUS_OK);
315 }
316 
317 /*
318  * Discover information for one HBA in the device tree.
319  * The di_node_t argument should be a node with smhba-supported prop set
320  * to true.
321  * Without iport support, the devinfo node will represent one port hba.
322  * This routine assumes the locks have been taken.
323  */
324 HBA_STATUS
325 devtree_get_one_hba(di_node_t hbaNode)
326 {
327 	const char		ROUTINE[] = "devtree_get_one_hba";
328 	char			*propdata = NULL;
329 	int			*propIntData = NULL;
330 	struct sun_sas_hba	*new_hba, *hba_ptr;
331 	char			*hbaDevpath, *hba_driver;
332 	int			protocol = 0;
333 	di_node_t		portNode;
334 	int			hba_instance = -1;
335 
336 	hba_instance = di_instance(hbaNode);
337 	if (hba_instance == -1) {
338 		log(LOG_DEBUG, ROUTINE,
339 		    "portNode has instance of -1");
340 		return (DI_WALK_CONTINUE);
341 	}
342 
343 	if ((hbaDevpath = di_devfs_path(hbaNode)) == NULL) {
344 		log(LOG_DEBUG, ROUTINE, "Unable to get "
345 		    "device path from hbaNode");
346 		return (HBA_STATUS_ERROR);
347 	}
348 
349 	/* check to see if this is a repeat HBA */
350 	if (global_hba_head) {
351 		for (hba_ptr = global_hba_head;
352 		    hba_ptr != NULL;
353 		    hba_ptr = hba_ptr->next) {
354 			if ((strncmp(hba_ptr->device_path, hbaDevpath,
355 			    strlen(hbaDevpath))) == 0) {
356 				if (refresh_hba(hbaNode, hba_ptr) !=
357 				    HBA_STATUS_OK) {
358 					log(LOG_DEBUG, ROUTINE, "Refresh failed"
359 					    " on hbaNode %s", hbaDevpath);
360 				}
361 				di_devfs_path_free(hbaDevpath);
362 				return (HBA_STATUS_OK);
363 			}
364 		}
365 	}
366 
367 	/* this is a new hba */
368 	if ((new_hba = (struct sun_sas_hba *)calloc(1,
369 	    sizeof (struct sun_sas_hba))) == NULL) {
370 		OUT_OF_MEMORY(ROUTINE);
371 		di_devfs_path_free(hbaDevpath);
372 		return (HBA_STATUS_ERROR);
373 	}
374 
375 	(void) strlcpy(new_hba->device_path, hbaDevpath,
376 	    sizeof (new_hba->device_path));
377 	di_devfs_path_free(hbaDevpath);
378 
379 	(void) snprintf(new_hba->adapter_attributes.HBASymbolicName,
380 	    sizeof (new_hba->adapter_attributes.HBASymbolicName),
381 	    "%s%s", DEVICES_DIR, new_hba->device_path);
382 
383 	/* Manufacturer */
384 	if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
385 	    "Manufacturer", (char **)&propdata)) == -1) {
386 		(void) strlcpy(new_hba->adapter_attributes.Manufacturer,
387 		    SUN_MICROSYSTEMS,
388 		    sizeof (new_hba->adapter_attributes.Manufacturer));
389 	} else {
390 		(void) strlcpy(new_hba->adapter_attributes.Manufacturer,
391 		    propdata,
392 		    sizeof (new_hba->adapter_attributes.Manufacturer));
393 	}
394 
395 	/* SerialNumber */
396 	if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
397 	    "SerialNumber", (char **)&propdata)) == -1) {
398 		new_hba->adapter_attributes.SerialNumber[0] = '\0';
399 	} else {
400 		(void) strlcpy(new_hba->adapter_attributes.SerialNumber,
401 		    propdata,
402 		    sizeof (new_hba->adapter_attributes.SerialNumber));
403 	}
404 
405 	/* Model */
406 	if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
407 	    "ModelName", (char **)&propdata)) == -1) {
408 		new_hba->adapter_attributes.Model[0] = '\0';
409 	} else {
410 		(void) strlcpy(new_hba->adapter_attributes.Model,
411 		    propdata,
412 		    sizeof (new_hba->adapter_attributes.Model));
413 	}
414 
415 	/* FirmwareVersion */
416 	if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
417 	    "firmware-version", (char **)&propdata)) == -1) {
418 		log(LOG_DEBUG, ROUTINE,
419 		    "Property \"%s\" not found for device \"%s\"",
420 		    "firmware-version", new_hba->device_path);
421 	} else {
422 		(void) strlcpy(new_hba->adapter_attributes.FirmwareVersion,
423 		    propdata,
424 		    sizeof (new_hba->adapter_attributes.FirmwareVersion));
425 	}
426 
427 	/* HardwareVersion */
428 	if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
429 	    "hardware-version", (char **)&propdata)) == -1) {
430 		log(LOG_DEBUG, ROUTINE,
431 		    "Property \"%s\" not found for device \"%s\"",
432 		    "hardware-version", new_hba->device_path);
433 	} else {
434 		(void) strlcpy(new_hba->adapter_attributes.HardwareVersion,
435 		    propdata,
436 		    sizeof (new_hba->adapter_attributes.HardwareVersion));
437 	}
438 
439 	/* DriverVersion */
440 	if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
441 	    "driver-version", (char **)&propdata)) == -1) {
442 		log(LOG_DEBUG, ROUTINE,
443 		    "Property \"%s\" not found for device \"%s\"",
444 		    "driver-version", new_hba->device_path);
445 	} else {
446 		(void) strlcpy(new_hba->adapter_attributes.DriverVersion,
447 		    propdata,
448 		    sizeof (new_hba->adapter_attributes.DriverVersion));
449 	}
450 
451 	if ((di_prop_lookup_ints(DDI_DEV_T_ANY, hbaNode,
452 	    "supported-protocol", &propIntData)) == -1) {
453 		log(LOG_DEBUG, ROUTINE,
454 		    "Unable to get supported-protocol from HBA node.");
455 	} else {
456 		protocol = *propIntData;
457 	}
458 
459 	/* We don't use these */
460 	new_hba->adapter_attributes.OptionROMVersion[0] = '\0';
461 	new_hba->adapter_attributes.RedundantOptionROMVersion[0] = '\0';
462 	new_hba->adapter_attributes.RedundantFirmwareVersion[0] = '\0';
463 	new_hba->adapter_attributes.VendorSpecificID = 0;
464 
465 	if ((hba_driver = di_driver_name(hbaNode)) != NULL) {
466 		(void) strlcpy(new_hba->adapter_attributes.DriverName,
467 		    hba_driver,
468 		    sizeof (new_hba->adapter_attributes.DriverName));
469 	} else {
470 		log(LOG_DEBUG, ROUTINE,
471 		    "HBA driver name not found for device \"%s\"",
472 		    new_hba->device_path);
473 	}
474 
475 	/*
476 	 * Name the adapter: like SUNW-pmcs-1
477 	 * Using di_instance number as the suffix for the name for persistent
478 	 * among rebooting.
479 	 */
480 	(void) snprintf(new_hba->handle_name, HANDLE_NAME_LENGTH, "%s-%s-%d",
481 	    "SUNW", new_hba->adapter_attributes.DriverName, hba_instance);
482 
483 	if ((portNode = di_child_node(hbaNode)) == NULL) {
484 		log(LOG_DEBUG, ROUTINE,
485 		    "HBA driver doesn't have iport child. \"%s\"",
486 		    new_hba->device_path);
487 		/* continue on with an hba without any port. */
488 		new_hba->index = hba_count++;
489 
490 		/*
491 		 * add newly created handle into global_hba_head list
492 		 */
493 		if (global_hba_head != NULL) {
494 			/*
495 			 * Make sure to move the open_handles list to back to
496 			 * the head if it's there (for refresh scenario)
497 			 */
498 			if (global_hba_head->open_handles) {
499 				new_hba->open_handles =
500 				    global_hba_head->open_handles;
501 				global_hba_head->open_handles = NULL;
502 			}
503 			/* Now bump the new one to the head of the list */
504 			new_hba->next = global_hba_head;
505 			global_hba_head = new_hba;
506 		} else {
507 			global_hba_head = new_hba;
508 		}
509 		return (HBA_STATUS_OK);
510 	}
511 
512 	while (portNode != DI_NODE_NIL) {
513 		if (add_hba_port_info(portNode, new_hba, protocol)
514 		    == HBA_STATUS_ERROR) {
515 			S_FREE(new_hba->first_port);
516 			S_FREE(new_hba);
517 			return (HBA_STATUS_ERROR);
518 		}
519 		portNode = di_sibling_node(portNode);
520 	}
521 
522 	new_hba->index = hba_count++;
523 
524 	/*
525 	 * add newly created handle into global_hba_head list
526 	 */
527 	if (global_hba_head != NULL) {
528 		/*
529 		 * Make sure to move the open_handles list to back to the
530 		 * head if it's there (for refresh scenario)
531 		 */
532 		if (global_hba_head->open_handles) {
533 			new_hba->open_handles = global_hba_head->open_handles;
534 			global_hba_head->open_handles = NULL;
535 		}
536 		/* Now bump the new one to the head of the list */
537 		new_hba->next = global_hba_head;
538 		global_hba_head = new_hba;
539 	} else {
540 		global_hba_head = new_hba;
541 	}
542 
543 	return (HBA_STATUS_OK);
544 }
545 
546 /*
547  * Discover information for all HBAs found on the system.
548  * The di_node_t argument should be the root of the device tree.
549  * This routine assumes the locks have been taken
550  */
551 static int
552 lookup_smhba_sas_hba(di_node_t node, void *arg)
553 {
554 	const char	ROUTINE[] = "lookup_smhba_sas_hba";
555 	int 		*propData, rval;
556 	walkarg_t 	*wa = (walkarg_t *)arg;
557 
558 	/* Skip stub(instance -1) nodes */
559 	if (IS_STUB_NODE(node)) {
560 		log(LOG_DEBUG, ROUTINE, "Walk continue");
561 		return (DI_WALK_CONTINUE);
562 	}
563 
564 	rval = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
565 	    "sm-hba-supported", &propData);
566 	if (rval >= 0) {
567 		if (*propData) {
568 			/* add the hba to the hba list */
569 			if (devtree_get_one_hba(node) != HBA_STATUS_OK) {
570 				*(wa->flag) = B_TRUE;
571 			}
572 			/* Found a node. No need to walk the child. */
573 			log(LOG_DEBUG, ROUTINE, "Walk prunechild");
574 			return (DI_WALK_PRUNECHILD);
575 		}
576 	}
577 
578 	return (DI_WALK_CONTINUE);
579 }
580 
581 /*
582  * Discover information for all HBAs found on the system.
583  * The di_node_t argument should be the root of the device tree.
584  * This routine assumes the locks have been taken
585  */
586 HBA_STATUS
587 devtree_get_all_hbas(di_node_t root)
588 {
589 	const char	ROUTINE[] = "devtree_get_all_hbas";
590 	int		rv, ret = HBA_STATUS_ERROR;
591 	walkarg_t	wa;
592 
593 	wa.devpath = NULL;
594 	if ((wa.flag = (boolean_t *)calloc(1,
595 	    sizeof (boolean_t))) == NULL) {
596 		OUT_OF_MEMORY(ROUTINE);
597 		return (HBA_STATUS_ERROR);
598 	}
599 	*wa.flag = B_FALSE;
600 	rv = di_walk_node(root, DI_WALK_SIBFIRST, &wa, lookup_smhba_sas_hba);
601 
602 	if (rv == 0) {
603 		/*
604 		 * Now determine what status code to return, taking
605 		 * partial failure scenarios into consideration.
606 		 *
607 		 * If we have at least one working HBA, then we return an
608 		 * OK status.  If we have no good HBAs, but at least one
609 		 * failed HBA, we return an ERROR status.  If we have
610 		 * no HBAs and no failures, we return OK.
611 		 */
612 		if (global_hba_head) {
613 			/*
614 			 * We've got at least one HBA and possibly some
615 			 * failures.
616 			 */
617 			ret = HBA_STATUS_OK;
618 		} else if (*(wa.flag)) {
619 			/* We have no HBAs but have failures */
620 			ret = HBA_STATUS_ERROR;
621 		} else {
622 			/* We have no HBAs and no failures */
623 			ret = HBA_STATUS_OK;
624 		}
625 	}
626 
627 
628 	S_FREE(wa.flag);
629 
630 	if (ret == HBA_STATUS_OK)
631 		(void) registerSysevent();
632 
633 	return (ret);
634 }
635