xref: /titanic_50/usr/src/cmd/picl/plugins/sun4u/snowbird/frutree/piclscsi.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /* implementation specific to scsi nodes probing */
30 
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <syslog.h>
34 #include <stdlib.h>
35 #include <sys/param.h>
36 #include <config_admin.h>
37 #include <string.h>
38 #include <strings.h>
39 #include <picl.h>
40 #include <picltree.h>
41 #include <libintl.h>
42 #include <libdevinfo.h>
43 #include <sys/types.h>
44 #include <picldefs.h>
45 #include "piclfrutree.h"
46 
47 #define	SCSI_SLOT	"scsi-bus"
48 #define	SCSI_LOC_FORMAT	"t%dd0"
49 #define	TARGET		"target"
50 #define	CLASS		"class"
51 #define	BUF_SIZE	256
52 
53 #define	SCSI_INITIATOR_ID	7
54 #define	DRV_TYPE_DSK	1
55 #define	DRV_TYPE_TAPE	2
56 #define	NUM_DSK_TARGS	15
57 /*
58  * No support for wide tapes for now.
59  * If required wide support, set this to 8
60  * See st.conf.
61  */
62 #define	NUM_TAPE_TARGS	7
63 
64 #define	DIRLINK_DSK	"dsk"
65 #define	DIRLINK_RMT	"rmt"
66 #define	DRV_SCSI_DSK	"sd"
67 #define	DRV_SCSI_TAPE	"st"
68 #define	NULL_ENTRY	0
69 
70 /* currently supported directory strings for SCSI FRUs in cfgadm APs */
71 static char *scsi_dirlink_names[] = { DIRLINK_DSK, DIRLINK_RMT, NULL_ENTRY};
72 /* currently supported SCSI FRU drivers */
73 static struct scsi_drv_info {
74 	char *drv_name;
75 	uint8_t num_targets;
76 	uint8_t drv_type;
77 } scsi_drv[] = {
78 		DRV_SCSI_DSK, NUM_DSK_TARGS, DRV_TYPE_DSK,
79 		DRV_SCSI_TAPE, NUM_TAPE_TARGS, DRV_TYPE_TAPE,
80 		NULL_ENTRY,	NULL_ENTRY,	NULL_ENTRY
81 		};
82 
83 /* the following defs are based on defines in scsi cfgadm plugin */
84 #define	CDROM		"CD-ROM"
85 #define	RMM		"tape"
86 #define	DISK		"disk"
87 
88 extern boolean_t is_location_present_in_subtree(frutree_frunode_t *,
89 	const char *, const char *);
90 extern picl_errno_t create_children(frutree_frunode_t *, char *, char *,
91 	int, char *, boolean_t);
92 extern char *strtok_r(char *s1, const char *s2, char **lasts);
93 extern boolean_t frutree_connects_initiated;
94 extern int frutree_debug;
95 
96 typedef struct node {
97 	struct node *next;
98 	cfga_list_data_t *data;
99 } node_t;
100 
101 typedef struct linked_list {
102 	node_t *first;
103 	int num_nodes;
104 } plist_t;
105 
106 typedef struct scsi_info {
107 	frutree_frunode_t *frup;
108 	cfga_list_data_t *cfgalist;
109 	plist_t *list;
110 	int num_list;
111 	boolean_t compare_cfgadm;
112 	int geo_addr;
113 } scsi_info_t;
114 
115 static plist_t *scsi_list = NULL;
116 static cfga_list_data_t *cfglist = NULL;
117 static int nlist = 0;
118 
119 static void
free_list(plist_t * list)120 free_list(plist_t *list)
121 {
122 	node_t	*tmp = NULL, *tmp1 = NULL;
123 
124 	if (list == NULL)
125 		return;
126 	tmp = list->first;
127 	while (tmp != NULL) {
128 		free(tmp->data);
129 		tmp1 = tmp->next;
130 		free(tmp);
131 		tmp = tmp1;
132 	}
133 }
134 
135 /*
136  * This routine gets the list of scsi controllers present
137  */
138 static cfga_err_t
populate_controllers_list(plist_t * cntrl_list,cfga_list_data_t * list,int num)139 populate_controllers_list(plist_t *cntrl_list, cfga_list_data_t *list, int num)
140 {
141 	int i;
142 	node_t *nodeptr = NULL;
143 	cfga_list_data_t *temp = NULL;
144 
145 	if (cntrl_list == NULL || list == NULL) {
146 		return (CFGA_ATTR_INVAL);
147 	}
148 
149 	cntrl_list->first = NULL;
150 	cntrl_list->num_nodes = 0;
151 
152 	if (num == 0) {
153 		return (CFGA_OK);
154 	}
155 
156 	for (i = 0; i < num; i++) {
157 		if (strcmp(list[i].ap_type, SCSI_SLOT) != 0) {
158 			continue;
159 		}
160 
161 		/* scsi controller */
162 		temp = (cfga_list_data_t *)malloc(sizeof (cfga_list_data_t));
163 		if (temp == NULL) {
164 			return (CFGA_ERROR);
165 		}
166 		(void) memcpy(temp, &list[i], sizeof (cfga_list_data_t));
167 
168 		nodeptr = (node_t *)malloc(sizeof (node_t));
169 		if (nodeptr == NULL) {
170 			free(temp);
171 			return (CFGA_ERROR);
172 		}
173 		nodeptr->data = temp;
174 		nodeptr->next = NULL;
175 
176 		/* append to the list */
177 		if (cntrl_list->first == NULL) {
178 			cntrl_list->first = nodeptr;
179 			cntrl_list->num_nodes++;
180 		} else {
181 			nodeptr->next = cntrl_list->first;
182 			cntrl_list->first = nodeptr;
183 			cntrl_list->num_nodes++;
184 		}
185 	}
186 	return (CFGA_OK);
187 }
188 
189 picl_errno_t
scsi_info_init()190 scsi_info_init()
191 {
192 	cfga_err_t	ap_list_err;
193 
194 	ap_list_err = config_list_ext(0, NULL, &cfglist, &nlist, NULL,
195 		NULL, NULL, CFGA_FLAG_LIST_ALL);
196 
197 	if (ap_list_err != CFGA_OK) {
198 		if (ap_list_err == CFGA_NOTSUPP) {
199 			return (PICL_SUCCESS);
200 		} else {
201 			return (PICL_FAILURE);
202 		}
203 	}
204 
205 	scsi_list = (plist_t *)malloc(sizeof (plist_t));
206 	if (scsi_list == NULL) {
207 		free(cfglist);
208 		return (PICL_NOSPACE);
209 	}
210 
211 	ap_list_err = populate_controllers_list(scsi_list, cfglist, nlist);
212 	if (ap_list_err != CFGA_OK) {
213 		free(cfglist);
214 		free(scsi_list);
215 		return (PICL_FAILURE);
216 	}
217 	return (PICL_SUCCESS);
218 }
219 
220 void
scsi_info_fini()221 scsi_info_fini()
222 {
223 	free(cfglist);
224 	free_list(scsi_list);
225 	free(scsi_list);
226 }
227 
228 /*
229  * This routine searches the controllers list to find the mapping based
230  * on given devfs_path.
231  * caller should allocate memory for ap_id
232  */
233 static picl_errno_t
find_scsi_controller(char * devfs_path,plist_t * list,char * ap_id)234 find_scsi_controller(char *devfs_path, plist_t *list, char *ap_id)
235 {
236 	node_t	*tmp = NULL;
237 	char *lasts = NULL;
238 	char *token = NULL;
239 	char path[MAXPATHLEN];
240 
241 	if (devfs_path == NULL || ap_id == NULL) {
242 		return (PICL_INVALIDARG);
243 	}
244 	(void) snprintf((char *)path, sizeof (path), "/devices%s", devfs_path);
245 
246 	tmp = list->first;
247 	while (tmp != NULL) {
248 		lasts = tmp->data->ap_phys_id;
249 		token = (char *)strtok_r(lasts, (const char *)":",
250 			(char **)&lasts);
251 		if (token == NULL) {
252 			tmp = tmp->next;
253 			continue;
254 		}
255 
256 		if (strcmp(path, token) == 0) {	/* match found */
257 			(void) strncpy(ap_id, tmp->data->ap_log_id,
258 				sizeof (ap_id));
259 			return (PICL_SUCCESS);
260 		}
261 		tmp = tmp->next;
262 	}
263 	return (PICL_NODENOTFOUND);
264 }
265 
266 /*
267  * This routine dynamically determines the cfgadm attachment point
268  * for a given devfspath and target id.
269  * memory for name should be allocated by the caller.
270  */
271 picl_errno_t
get_scsislot_name(char * devfs_path,char * bus_addr,char * name)272 get_scsislot_name(char *devfs_path, char *bus_addr, char *name)
273 {
274 	picl_errno_t	rc;
275 	int target_id = 0;
276 	int numlist;
277 	plist_t			list;
278 	cfga_err_t		ap_list_err;
279 	cfga_list_data_t 	*cfgalist = NULL;
280 	char controller[MAXPATHLEN];
281 
282 	ap_list_err = config_list_ext(0, NULL, &cfgalist,
283 		&numlist, NULL, NULL, NULL, CFGA_FLAG_LIST_ALL);
284 	if (ap_list_err != CFGA_OK) {
285 		return (PICL_NODENOTFOUND);
286 	}
287 
288 	ap_list_err = populate_controllers_list(&list, cfgalist,
289 		numlist);
290 	if (ap_list_err != CFGA_OK) {
291 		free_list(&list);
292 		free(cfgalist);
293 		return (PICL_NODENOTFOUND);
294 	}
295 
296 	if (list.num_nodes <= 0) {
297 		free(cfgalist);
298 		return (PICL_NODENOTFOUND);
299 	}
300 
301 	if ((rc = find_scsi_controller(devfs_path, &list,
302 		controller)) != PICL_SUCCESS) {
303 		free(cfgalist);
304 		free_list(&list);
305 		return (rc);
306 	}
307 	target_id = strtol(bus_addr, (char **)NULL, 16);
308 	(void) sprintf(name, "%s::dsk/%st%dd0", controller,
309 		controller, target_id);
310 	free(cfgalist);
311 	free_list(&list);
312 	return (PICL_SUCCESS);
313 }
314 
315 /*
316  * Arg scsi_loc can be any of the following forms appearing in cfgadm output
317  *	c0::dsk/c0t0d0
318  *	c1::sd56
319  *	c2::rmt/0
320  *	c3::st41
321  *	dsk/c1t1d0
322  *	rmt/1
323  *	/devices/pci@1f,0/pci@1,1/scsi@2:scsi::dsk/c0t0d0
324  *
325  *	On return, bus_addr contains the target id of the device.
326  *	Please note that currently the target id is computed. It is better
327  *	to eventually change this to getting from libdevinfo.
328  *	Also, please note that SCSI_INITIATOR_ID should not
329  *	be hardcoded, but should be dynamically retrieved from an OBP property.
330  */
331 static void
get_bus_addr(char * scsi_loc,char ** bus_addr)332 get_bus_addr(char *scsi_loc, char **bus_addr)
333 {
334 	char *ap, *token, *p, *ap_idp;
335 	int len = 0, i = 0;
336 	char parse_link = 0;
337 	char addr[BUF_SIZE], ap_id[BUF_SIZE];
338 	char fileinfo[BUF_SIZE], ap_id_link[BUF_SIZE];
339 
340 	(void) strncpy(ap_id, scsi_loc, sizeof (ap_id));
341 	ap = strrchr(ap_id, ':');
342 	if (!ap)
343 		ap = ap_idp = ap_id;
344 	else
345 		ap_idp = ++ap;
346 
347 	while (scsi_dirlink_names[i] && !len) {
348 		len = strspn(ap, scsi_dirlink_names[i++]);
349 		/*
350 		 * strspn may return positive len even when there is no
351 		 * complete string matches!!! hence the following check is
352 		 * necessary. So ensure the string match.
353 		 */
354 		if (len && strstr(ap, scsi_dirlink_names[i-1]))
355 			break;
356 		len = 0;
357 	}
358 	if (len)
359 		parse_link = 1;
360 	else {
361 		i = 0;
362 		while (scsi_drv[i].drv_name && !len) {
363 			len = strspn(ap, scsi_drv[i++].drv_name);
364 			if (len && strstr(ap, scsi_drv[i-1].drv_name))
365 				break;
366 			len = 0;
367 		}
368 	}
369 	ap += len;
370 	if (strlen(ap) && parse_link) {
371 
372 		/* slice 0 must be present in the system */
373 		if (strstr(ap, "/c")) {
374 			if (strstr(ap, "s0") == NULL)
375 				(void) strcat(ap, "s0");
376 		}
377 		/* get the devlink and read the target id from minor node */
378 		(void) snprintf(ap_id_link, sizeof (ap_id_link), "/dev/%s",
379 			ap_idp);
380 		(void) bzero(fileinfo, sizeof (fileinfo));
381 		if (readlink(ap_id_link, fileinfo, sizeof (fileinfo)) < 0)
382 			return;
383 		if (!fileinfo[0])
384 			return;
385 		ap = strrchr(fileinfo, '@');
386 		ap++;
387 	}
388 	token = (char *)strtok_r(ap, ",", &p);
389 	(void) strncpy(addr, token, sizeof (addr));
390 	if (!parse_link) {
391 		int drv_inst = atoi(token);
392 		int tmp_targ_id = drv_inst % scsi_drv[i-1].num_targets;
393 		int targ_id = scsi_drv[i-1].drv_type == DRV_TYPE_DSK ?
394 			(tmp_targ_id < SCSI_INITIATOR_ID ?
395 			tmp_targ_id : tmp_targ_id+1):
396 			DRV_TYPE_TAPE ? tmp_targ_id : drv_inst;
397 		(void) snprintf(addr, sizeof (addr), "%d", targ_id);
398 	}
399 	if (strlen(addr)) {
400 		*bus_addr = (char *)malloc(strlen(addr)+1);
401 		if ((*bus_addr) == NULL)
402 			return;
403 		(void) strcpy((char *)*bus_addr, addr);
404 	}
405 }
406 
407 /*
408  * This routine determines all the scsi nodes under a FRU and
409  * creates a subtree of all the scsi nodes with basic properties.
410  */
411 static picl_errno_t
dyn_probe_for_scsi_frus(frutree_frunode_t * frup,cfga_list_data_t * cfgalist,plist_t * list,int numlist)412 dyn_probe_for_scsi_frus(frutree_frunode_t *frup, cfga_list_data_t *cfgalist,
413 	plist_t *list, int numlist)
414 {
415 	picl_errno_t rc;
416 	int i, geo_addr = 0;
417 	node_t *curr = NULL;
418 	char *bus_addr = NULL;
419 	char path[MAXPATHLEN];
420 	char controller_name[MAXPATHLEN];
421 
422 	/* for each controller in the list, find if disk/fru is present */
423 	curr = list->first;
424 	while (curr != NULL) {
425 		/* compare the path */
426 		(void) snprintf((char *)path, sizeof (path),  "/devices%s",
427 			frup->fru_path);
428 		if (strstr(curr->data->ap_phys_id, path) == NULL) {
429 			curr = curr->next;
430 			continue;
431 
432 		}
433 		(void) snprintf(controller_name, sizeof (controller_name),
434 			"%s::", curr->data->ap_log_id);
435 
436 		for (i = 0; i < numlist; i++) {
437 			if (strcmp(cfgalist[i].ap_type, SCSI_SLOT) == 0) {
438 				continue;
439 			}
440 			if (strstr(cfgalist[i].ap_log_id,
441 				controller_name) == NULL) {
442 				continue;
443 			}
444 			/* check if device is under fru */
445 			if (strstr(cfgalist[i].ap_phys_id, path) == NULL) {
446 				continue;
447 			}
448 
449 			/* we found a scsi fru */
450 			geo_addr++;
451 			/* check if the device is present in subtree */
452 			if (is_location_present_in_subtree(frup,
453 				cfgalist[i].ap_log_id, path) == B_TRUE) {
454 				continue;
455 			}
456 			get_bus_addr(cfgalist[i].ap_log_id, &bus_addr);
457 			if (bus_addr == NULL) {
458 				continue;
459 			}
460 			rc = create_children(frup, cfgalist[i].ap_log_id,
461 				bus_addr, geo_addr, SANIBEL_SCSI_SLOT, B_TRUE);
462 			free(bus_addr);
463 			if (rc != PICL_SUCCESS) {
464 				FRUTREE_DEBUG3(FRUTREE_INIT, "SUNW_frutree:"
465 				"Error in creating node %s under %s(error=%d)",
466 					cfgalist[i].ap_log_id, frup->name, rc);
467 			}
468 		}
469 		curr = curr->next;
470 	}
471 	return (PICL_SUCCESS);
472 }
473 
474 /*
475  * data used here is cached information (cfglist, nlist)
476  */
477 static picl_errno_t
cache_probe_for_scsi_frus(frutree_frunode_t * frup)478 cache_probe_for_scsi_frus(frutree_frunode_t *frup)
479 {
480 	int i, geo_addr = 0;
481 	picl_errno_t rc;
482 	node_t *curr = NULL;
483 	char path[MAXPATHLEN];
484 	char controller_name[MAXPATHLEN];
485 	char *bus_addr = NULL;
486 
487 	/* for each controller in the list, find if disk/fru is present */
488 	if (scsi_list == NULL) {
489 		return (PICL_SUCCESS);
490 	}
491 	curr = scsi_list->first;
492 	while (curr != NULL) {
493 		/* compare the path */
494 		(void) snprintf((char *)path, sizeof (path), "/devices%s",
495 			frup->fru_path);
496 		if (strstr(curr->data->ap_phys_id, path) == NULL) {
497 			curr = curr->next;
498 			continue;
499 		}
500 		(void) snprintf(controller_name, sizeof (controller_name),
501 			"%s::", curr->data->ap_log_id);
502 
503 		for (i = 0; i < nlist; i++) {
504 			if (strcmp(cfglist[i].ap_type, SCSI_SLOT) == 0) {
505 				continue;
506 			}
507 			if (strstr(cfglist[i].ap_log_id,
508 				controller_name) == NULL) {
509 				continue;
510 			}
511 			/* check if the device is under fru */
512 			if (strstr(cfglist[i].ap_phys_id, path) == NULL) {
513 				continue;
514 			}
515 
516 			/* we found a scsi fru */
517 			geo_addr++;
518 			/* check if the device is present in subtree */
519 			if (is_location_present_in_subtree(frup,
520 				cfglist[i].ap_log_id, path) == B_TRUE) {
521 				continue;
522 			}
523 			get_bus_addr(cfglist[i].ap_log_id, &bus_addr);
524 			if (bus_addr == NULL) {
525 				continue;
526 			}
527 			rc = create_children(frup, cfglist[i].ap_log_id,
528 				bus_addr, geo_addr, SANIBEL_SCSI_SLOT, B_TRUE);
529 			free(bus_addr);
530 			if (rc != PICL_SUCCESS) {
531 				FRUTREE_DEBUG3(FRUTREE_INIT, "SUNW_frutree:"
532 				"Error in creating node %s under %s(error=%d)",
533 					cfglist[i].ap_log_id, frup->name, rc);
534 			}
535 		}
536 		curr = curr->next;
537 	}
538 	return (PICL_SUCCESS);
539 }
540 
541 /*
542  * This routine checks if the node (scsi device) is present in cfgadm data
543  * Algorithm:
544  * 1. traverse thru list of controllers and find
545  *    the controller of interest
546  * 2. go thru list of devices under controller and compare if the target is same
547  * 3. if yes
548  *      - device is already represented
549  * 4. if No
550  * 	- The node must be repreented in PICL tree.
551  */
552 static boolean_t
is_node_present(scsi_info_t * scsi_info,char * devfs_path,int target)553 is_node_present(scsi_info_t *scsi_info, char *devfs_path, int target)
554 {
555 	node_t	*curr = NULL;
556 	char	path[MAXPATHLEN];
557 	char	controller[MAXPATHLEN];
558 	char 	*bus_addr = NULL;
559 	char 	*lasts = NULL, *token = NULL;
560 	int	i = 0;
561 
562 	if (scsi_info == NULL) {
563 		return (B_FALSE);
564 	}
565 
566 	if (scsi_info->list == NULL) {
567 		return (B_FALSE);
568 	}
569 
570 	(void) snprintf(path, sizeof (path), "/devices%s", devfs_path);
571 
572 	curr = scsi_info->list->first;
573 	while (curr != NULL) {
574 
575 		lasts = curr->data->ap_phys_id;
576 		token = (char *)strtok_r(lasts, (const char *)":",
577 			(char **)&lasts);
578 		if (token == NULL) {
579 			curr = curr->next;
580 			continue;
581 		}
582 
583 		if (strstr(path, token) == NULL) {
584 			/* this controller is not of interest */
585 			curr = curr->next;
586 			continue;
587 		}
588 
589 		(void) snprintf(controller, sizeof (controller), "%s::",
590 			curr->data->ap_log_id);
591 		for (i = 0; i < scsi_info->num_list; i++) {
592 			if (strcmp(scsi_info->cfgalist[i].ap_type,
593 				SCSI_SLOT) == 0) {
594 				continue;
595 			}
596 
597 			if (strstr(scsi_info->cfgalist[i].ap_log_id,
598 				controller) == NULL) {
599 				continue;
600 			}
601 
602 			get_bus_addr(scsi_info->cfgalist[i].ap_phys_id,
603 				&bus_addr);
604 			/*
605 			 * compare  with target value
606 			 */
607 			if (bus_addr == NULL) {
608 				return (B_TRUE);
609 			}
610 			if (strtoul(bus_addr, NULL, 16) == target) {
611 				/*
612 				 * this device is already represented
613 				 * in fru tree
614 				 */
615 				free(bus_addr);
616 				return (B_TRUE);
617 			}
618 			free(bus_addr);
619 		}
620 		curr = curr->next;
621 	}
622 	return (B_FALSE);
623 }
624 
625 static di_prop_t
get_prop_by_name(di_node_t node,char * name)626 get_prop_by_name(di_node_t node, char *name)
627 {
628 	di_prop_t prop = DI_PROP_NIL;
629 	char *prop_name = NULL;
630 
631 	prop = di_prop_next(node, DI_PROP_NIL);
632 	while (prop != DI_PROP_NIL) {
633 		prop_name = di_prop_name(prop);
634 		if (prop_name != NULL) {
635 			if (strcmp(prop_name, name) == 0) {
636 				return (prop);
637 			}
638 		}
639 		prop = di_prop_next(node, prop);
640 	}
641 	return (DI_PROP_NIL);
642 }
643 
644 static int
get_geoaddr(picl_nodehdl_t nodeh,void * c_args)645 get_geoaddr(picl_nodehdl_t nodeh, void *c_args)
646 {
647 	picl_errno_t rc;
648 	uint8_t *geo_addr = NULL;
649 	char slot_type[PICL_PROPNAMELEN_MAX];
650 
651 	if (c_args == NULL)
652 		return (PICL_INVALIDARG);
653 	geo_addr = (uint8_t *)c_args;
654 
655 	if ((rc = ptree_get_propval_by_name(nodeh, PICL_PROP_SLOT_TYPE,
656 		slot_type, sizeof (slot_type))) != PICL_SUCCESS) {
657 		return (rc);
658 	}
659 
660 	if (strcmp(slot_type, SANIBEL_SCSI_SLOT) == 0 ||
661 		strcmp(slot_type, SANIBEL_IDE_SLOT) == 0) {
662 		*geo_addr = *geo_addr + 1;
663 	}
664 	return (PICL_WALK_CONTINUE);
665 }
666 
667 static int
frutree_get_geoaddr(frutree_frunode_t * frup)668 frutree_get_geoaddr(frutree_frunode_t *frup)
669 {
670 	int geo_addr = 1;
671 	if (ptree_walk_tree_by_class(frup->frunodeh, PICL_CLASS_LOCATION,
672 		&geo_addr, get_geoaddr) != PICL_SUCCESS) {
673 		return (geo_addr);
674 	}
675 	return (geo_addr);
676 }
677 
678 static int
probe_disks(di_node_t node,void * arg)679 probe_disks(di_node_t node, void *arg)
680 {
681 	di_prop_t prop;
682 	picl_errno_t rc;
683 	int *target_val = NULL;
684 	char *nodetype = NULL;
685 	char *devfs_path = NULL;
686 	char *bus_addr = NULL;
687 	char *drv_name = NULL;
688 	scsi_info_t *data = NULL;
689 	di_minor_t minor = DI_MINOR_NIL;
690 	char *class = NULL;
691 	char node_name[BUF_SIZE];
692 	char slot_type[PICL_PROPNAMELEN_MAX];
693 
694 	if (arg == NULL)
695 		return (DI_WALK_TERMINATE);
696 
697 	data = *(scsi_info_t **)arg;
698 	if (data == NULL) {
699 		return (DI_WALK_TERMINATE);
700 	}
701 
702 	/* initialize the geo_addr value */
703 	if (data->geo_addr == 0) {
704 		if (data->compare_cfgadm == B_FALSE) {
705 			data->geo_addr = 1;
706 		} else {
707 			data->geo_addr = frutree_get_geoaddr(data->frup);
708 		}
709 	}
710 
711 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
712 		nodetype = di_minor_nodetype(minor);
713 		if (nodetype == NULL) {
714 			continue;
715 		}
716 
717 		if (strcmp(nodetype, DDI_NT_BLOCK_CHAN) == 0 ||
718 			strcmp(nodetype, DDI_NT_BLOCK_WWN) == 0) {
719 			(void) snprintf(node_name, sizeof (node_name),
720 				"%s%d", DISK, data->geo_addr);
721 		} else if (strcmp(nodetype, DDI_NT_TAPE) == 0) {
722 			(void) snprintf(node_name, sizeof (node_name),
723 				"%s%d", RMM, data->geo_addr);
724 		} else if (strcmp(nodetype, DDI_NT_CD) == 0 ||
725 			strcmp(nodetype, DDI_NT_CD_CHAN) == 0) {
726 			(void) snprintf(node_name, sizeof (node_name),
727 				"%s%d", CDROM, data->geo_addr);
728 		} else {
729 			continue;
730 		}
731 
732 		devfs_path = di_devfs_path(node);
733 		drv_name = di_driver_name(node);
734 		bus_addr = di_bus_addr(node);
735 		if (devfs_path == NULL) {
736 			continue;
737 		}
738 		if (drv_name == NULL || bus_addr == NULL) {
739 			di_devfs_path_free(devfs_path);
740 			continue;
741 		}
742 		prop = get_prop_by_name(node, TARGET);
743 		if (prop != DI_PROP_NIL) {
744 			di_prop_ints(prop, &target_val);
745 			if (data->compare_cfgadm) {
746 				/* check if node is present in cfgadm data */
747 				if (is_node_present(data, devfs_path,
748 					*target_val) == B_TRUE) {
749 					di_devfs_path_free(devfs_path);
750 					return (DI_WALK_CONTINUE);
751 				}
752 			}
753 
754 			di_devfs_path_free(devfs_path);
755 			prop = get_prop_by_name(node, CLASS);
756 			if (prop != DI_PROP_NIL) {
757 				di_prop_strings(prop, &class);
758 			}
759 
760 			/* determine the slot type based on class code */
761 			if (class != NULL) {
762 				if (strcmp(class, DEVICE_CLASS_SCSI) == 0) {
763 					(void) strncpy(slot_type,
764 						SANIBEL_SCSI_SLOT,
765 						sizeof (slot_type));
766 				} else if (strcmp(class,
767 					DEVICE_CLASS_IDE) == 0) {
768 					(void) strncpy(slot_type,
769 						SANIBEL_IDE_SLOT,
770 						sizeof (slot_type));
771 				} else {
772 					(void) strncpy(slot_type,
773 						SANIBEL_UNKNOWN_SLOT,
774 						sizeof (slot_type));
775 				}
776 
777 			} else {
778 				(void) strncpy(slot_type, SANIBEL_UNKNOWN_SLOT,
779 					sizeof (slot_type));
780 			}
781 
782 			if ((rc = create_children(data->frup, node_name,
783 				bus_addr, data->geo_addr, slot_type,
784 				B_FALSE)) != PICL_SUCCESS) {
785 				return (rc);
786 			}
787 			/* increment the geo_addr */
788 			data->geo_addr++;
789 		} else {
790 			di_devfs_path_free(devfs_path);
791 			continue;
792 		}
793 		return (DI_WALK_CONTINUE);
794 	}
795 	return (DI_WALK_CONTINUE);
796 }
797 
798 static picl_errno_t
probe_scsi_in_libdevinfo(frutree_frunode_t * frup,cfga_list_data_t * cfgalist,plist_t * list,int num_list,boolean_t compare_cfgadm)799 probe_scsi_in_libdevinfo(frutree_frunode_t *frup, cfga_list_data_t *cfgalist,
800 	plist_t *list, int num_list, boolean_t compare_cfgadm)
801 {
802 	di_node_t	rnode;
803 	scsi_info_t	*scsi_data = NULL;
804 
805 	if (frup == NULL) {
806 		return (PICL_FAILURE);
807 	}
808 
809 	rnode = di_init(frup->fru_path, DINFOCPYALL);
810 	if (rnode == DI_NODE_NIL) {
811 		return (PICL_FAILURE);
812 	}
813 
814 	scsi_data = (scsi_info_t *)malloc(sizeof (scsi_info_t));
815 	if (scsi_data == NULL) {
816 		di_fini(rnode);
817 		return (PICL_NOSPACE);
818 	}
819 
820 	scsi_data->frup = frup;
821 	scsi_data->cfgalist = cfgalist;
822 	scsi_data->list = list;
823 	scsi_data->num_list = num_list;
824 	scsi_data->compare_cfgadm = compare_cfgadm;
825 	scsi_data->geo_addr = 0;
826 	if (di_walk_node(rnode, DI_WALK_CLDFIRST, &scsi_data,
827 		probe_disks) != 0) {
828 		free(scsi_data);
829 		di_fini(rnode);
830 		return (PICL_FAILURE);
831 	}
832 
833 	free(scsi_data);
834 	di_fini(rnode);
835 	return (PICL_SUCCESS);
836 }
837 
838 picl_errno_t
probe_for_scsi_frus(frutree_frunode_t * frup)839 probe_for_scsi_frus(frutree_frunode_t *frup)
840 {
841 	int numlist;
842 	picl_errno_t rc;
843 	plist_t list;
844 	cfga_err_t ap_list_err;
845 	cfga_list_data_t *cfgalist = NULL;
846 
847 	if (frutree_connects_initiated == B_TRUE) { /* probing after hotswap */
848 		ap_list_err = config_list_ext(0, NULL, &cfgalist,
849 			&numlist, NULL, NULL, NULL, CFGA_FLAG_LIST_ALL);
850 
851 		if (ap_list_err != CFGA_OK) {
852 			rc = probe_scsi_in_libdevinfo(frup, NULL, NULL,
853 				0, B_FALSE);
854 			return (rc);
855 		}
856 
857 		/* get list of all controllers in the system */
858 		ap_list_err = populate_controllers_list(&list, cfgalist,
859 			numlist);
860 		if (ap_list_err != CFGA_OK) {
861 			free_list(&list);
862 			free(cfgalist);
863 			rc = probe_scsi_in_libdevinfo(frup, NULL, NULL,
864 				0, B_FALSE);
865 			return (rc);
866 		}
867 
868 		/* no controllers found */
869 		if (list.num_nodes <= 0) {
870 			free_list(&list);
871 			free(cfgalist);
872 			rc = probe_scsi_in_libdevinfo(frup, NULL, NULL,
873 				0, B_FALSE);
874 			return (rc);
875 		}
876 		/*
877 		 * we have to fetch cfgadm, look for scsi controllers
878 		 * dynamically
879 		 */
880 		(void) dyn_probe_for_scsi_frus(frup, cfgalist, &list, numlist);
881 		rc = probe_scsi_in_libdevinfo(frup, cfgalist, &list,
882 			numlist, B_TRUE);
883 		free_list(&list);
884 		free(cfgalist);
885 		return (rc);
886 	} else {
887 		/* during initialization */
888 		/* use the cached cfgadm data */
889 		rc = cache_probe_for_scsi_frus(frup);
890 		if (scsi_list && scsi_list->num_nodes > 0) {
891 			rc = probe_scsi_in_libdevinfo(frup, cfglist,
892 				scsi_list, nlist, B_TRUE);
893 		} else {
894 			rc = probe_scsi_in_libdevinfo(frup, NULL,
895 				NULL, 0, B_FALSE);
896 		}
897 		return (rc);
898 	}
899 }
900