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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26
27/*
28 * Subroutines used by various components of the Sun4v PI enumerator
29 */
30
31#include <sys/types.h>
32#include <sys/systeminfo.h>
33#include <sys/utsname.h>
34#include <string.h>
35#include <strings.h>
36#include <sys/fm/protocol.h>
37#include <fm/topo_mod.h>
38#include <fm/topo_hc.h>
39#include <sys/mdesc.h>
40#include <libnvpair.h>
41
42#include "pi_impl.h"
43
44/* max pci path = 256 */
45#define	MAX_PATH_DEPTH	(MAXPATHLEN / 256)
46
47/* max pci path + child + minor */
48#define	MAX_DIPATH_DEPTH	(MAX_PATH_DEPTH + 2)
49
50static const topo_pgroup_info_t sys_pgroup = {
51	TOPO_PGROUP_SYSTEM,
52	TOPO_STABILITY_PRIVATE,
53	TOPO_STABILITY_PRIVATE,
54	1
55};
56
57static const topo_pgroup_info_t auth_pgroup = {
58	FM_FMRI_AUTHORITY,
59	TOPO_STABILITY_PRIVATE,
60	TOPO_STABILITY_PRIVATE,
61	1
62};
63
64
65/*
66 * Search the PRI for MDE nodes using md_scan_dag.  Using this routine
67 * consolodates similar searches used in a few places within the sun4vpi
68 * enumerator.
69 *
70 * The routine returns the number of nodes found, or -1.  If the node array
71 * is non-NULL on return, then it must be freed:
72 *	topo_mod_free(mod, nodes, nsize);
73 *
74 */
75int
76pi_find_mdenodes(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_start,
77    char *type_str, char *arc_str, mde_cookie_t **nodes, size_t *nsize)
78{
79	int	result;
80	int	total_mdenodes;
81
82	mde_str_cookie_t	start_cookie;
83	mde_str_cookie_t	arc_cookie;
84
85	/* Prepare to scan the PRI using the start string and given arc */
86	total_mdenodes	= md_node_count(mdp);
87	start_cookie	= md_find_name(mdp, type_str);
88	arc_cookie	= md_find_name(mdp, arc_str);
89
90	/* Allocate an array to hold the results of the scan */
91	*nsize		= sizeof (mde_cookie_t) * total_mdenodes;
92	*nodes		= topo_mod_zalloc(mod, *nsize);
93	if (*nodes == NULL) {
94		/* We have no memory.  Set an error code and return failure */
95		*nsize = 0;
96		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
97		return (-1);
98	}
99
100	result = md_scan_dag(mdp, mde_start, start_cookie, arc_cookie, *nodes);
101	if (result <= 0) {
102		/* No nodes found.  Free the node array before returning */
103		topo_mod_free(mod, *nodes, *nsize);
104		*nodes = NULL;
105		*nsize = 0;
106	}
107
108	return (result);
109}
110
111
112/*
113 * Determine if this node should be skipped by finding the topo-skip property.
114 */
115int
116pi_skip_node(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
117{
118	int		result;
119	uint64_t	skip;
120
121	if (mod == NULL || mdp == NULL) {
122		/*
123		 * These parameters are required.  Tell the caller to skip
124		 * all nodes.
125		 */
126		return (1);
127	}
128
129	skip = 0;	/* do not skip by default */
130	result = md_get_prop_val(mdp, mde_node, MD_STR_TOPO_SKIP, &skip);
131	if (result != 0) {
132		/*
133		 * There is no topo-skip property.  Assume we are not skipping
134		 * the mde node.
135		 */
136		skip = 0;
137	}
138
139	/*
140	 * If skip is present and non-zero we want to skip this node.  We
141	 * return 1 to indicate this.
142	 */
143	if (skip != 0) {
144		return (1);
145	}
146	return (0);
147}
148
149
150/*
151 * Build a device path without device names (PRI like) from a devinfo
152 * node. Return the PRI like path.
153 *
154 * The string must be freed with topo_mod_strfree()
155 */
156char *
157pi_get_dipath(topo_mod_t *mod, di_node_t dnode)
158{
159	int	i, j, rv;
160	int	depth = 0;
161	char	*bus_addr[MAX_DIPATH_DEPTH] = { NULL };
162	char	*dev_path[MAX_DIPATH_DEPTH] = { NULL };
163	char	*path = NULL;
164	size_t	path_len = 0;
165
166	/* loop through collecting bus addresses */
167	do {
168		/* stop at '/' */
169		if (strcmp(di_devfs_path(dnode), "/") == 0) {
170			break;
171		}
172
173		if (depth < MAX_DIPATH_DEPTH) {
174			bus_addr[depth] = topo_mod_strdup(mod,
175			    di_bus_addr(dnode));
176			++depth;
177		} else {
178			topo_mod_dprintf(mod, "pi_get_dipath: path too "
179			    "long (%d)\n", depth);
180			return (NULL);
181		}
182	} while ((dnode = di_parent_node(dnode)) != DI_NODE_NIL);
183
184	/* prepend '/@' to each bus address */
185	for (i = (depth - 1), j = 0; i >= 0; --i, j++) {
186		int len = strlen(bus_addr[i]) + strlen("/@") + 1;
187		path_len += len;
188		dev_path[j] = (char *)topo_mod_alloc(mod, len);
189		rv = snprintf(dev_path[j], len, "/@%s", bus_addr[i]);
190		if (rv < 0) {
191			return (NULL);
192		}
193	}
194
195	/*
196	 * Build the path from the bus addresses.
197	 */
198	path_len -= (depth - 1); /* leave room for one null char */
199	path = (char *)topo_mod_alloc(mod, path_len);
200	path = strcpy(path, dev_path[0]);
201
202	for (i = 1; i < depth; i++) {
203		path = strncat(path, dev_path[i], strlen(dev_path[i]) + 1);
204	}
205
206	for (i = 0; i < depth; i++) {
207		if (bus_addr[i] != NULL) {
208			topo_mod_strfree(mod, bus_addr[i]);
209		}
210		if (dev_path[i] != NULL) {
211			topo_mod_strfree(mod, dev_path[i]);
212		}
213	}
214
215	topo_mod_dprintf(mod, "pi_get_dipath: path (%s)\n", path);
216	return (path);
217}
218
219
220/*
221 * Get the product serial number (the ID as far as the topo authority is
222 * concerned) either from the current node, if it is of type 'product', or
223 * search for a product node in the PRI.
224 *
225 * The string must be freed with topo_mod_strfree()
226 */
227char *
228pi_get_productsn(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
229{
230	int		result;
231	int		idx;
232	int		num_nodes;
233	char		*id = NULL;
234	char		*type;
235	size_t		size;
236	mde_cookie_t	*nodes = NULL;
237
238	topo_mod_dprintf(mod, "pi_get_productsn: enter\n");
239
240	result = md_get_prop_str(mdp, mde_node, MD_STR_TYPE, &type);
241	if (result == 0 && strcmp(type, MD_STR_PRODUCT) == 0) {
242		/*
243		 * This is a product node.  We need only search for the serial
244		 * number property on this node to return the ID.
245		 */
246		result = md_get_prop_str(mdp, mde_node, MD_STR_SERIAL_NUMBER,
247		    &id);
248		if (result != 0 || id == NULL || strlen(id) == 0)
249			return (NULL);
250
251		topo_mod_dprintf(mod, "pi_get_productsn: product-sn = %s\n",
252		    id);
253		return (topo_mod_strdup(mod, id));
254	}
255
256	/*
257	 * Search the PRI for nodes of type MD_STR_COMPONENT and find the
258	 * first element with type of MD_STR_PRODUCT.  This node
259	 * will contain the MD_STR_SERIAL_NUMBER property to use as the
260	 * product-sn.
261	 */
262	num_nodes = pi_find_mdenodes(mod, mdp, MDE_INVAL_ELEM_COOKIE,
263	    MD_STR_COMPONENT, MD_STR_FWD, &nodes, &size);
264	if (num_nodes <= 0 || nodes == NULL) {
265		/* We did not find any component nodes */
266		return (NULL);
267	}
268	topo_mod_dprintf(mod, "pi_get_productsn: found %d %s nodes\n",
269	    num_nodes, MD_STR_COMPONENT);
270
271	idx = 0;
272	while (id == NULL && idx < num_nodes) {
273		result = md_get_prop_str(mdp, nodes[idx], MD_STR_TYPE, &type);
274		if (result == 0 && strcmp(type, MD_STR_PRODUCT) == 0) {
275			/*
276			 * This is a product node.  Get the serial number
277			 * property from the node.
278			 */
279			result = md_get_prop_str(mdp, nodes[idx],
280			    MD_STR_SERIAL_NUMBER, &id);
281			if (result != 0)
282				topo_mod_dprintf(mod, "pi_get_productsn: "
283				    "failed to read %s from node_0x%llx\n",
284				    MD_STR_SERIAL_NUMBER,
285				    (uint64_t)nodes[idx]);
286		}
287		/* Search the next node, if necessary */
288		idx++;
289	}
290	topo_mod_free(mod, nodes, size);
291
292	/* Everything is freed up and it's time to return the product-sn */
293	if (result != 0 || id == NULL || strlen(id) == 0) {
294		return (NULL);
295	}
296	topo_mod_dprintf(mod, "pi_get_productsn: product-sn %s\n", id);
297
298	return (topo_mod_strdup(mod, id));
299}
300
301
302/*
303 * Get the chassis serial number (the ID as far as the topo authority is
304 * concerned) either from the current node, if it is of type 'chassis', or
305 * search for a chassis node in the PRI.
306 *
307 * The string must be freed with topo_mod_strfree()
308 */
309char *
310pi_get_chassisid(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
311{
312	int		result;
313	int		idx;
314	int		num_nodes;
315	char		*id = NULL;
316	char		*hc_name = NULL;
317	size_t		chassis_size;
318	mde_cookie_t	*chassis_nodes = NULL;
319
320	topo_mod_dprintf(mod, "pi_get_chassis: enter\n");
321
322	hc_name = pi_get_topo_hc_name(mod, mdp, mde_node);
323	if (hc_name != NULL && strcmp(hc_name, MD_STR_CHASSIS) == 0) {
324		topo_mod_strfree(mod, hc_name);
325
326		/*
327		 * This is a chassis node.  We need only search for the serial
328		 * number property on this node to return the ID.
329		 */
330		result = md_get_prop_str(mdp, mde_node, MD_STR_SERIAL_NUMBER,
331		    &id);
332		if (result != 0 || id == NULL || strlen(id) == 0) {
333			return (NULL);
334		}
335		topo_mod_dprintf(mod, "pi_get_chassis: chassis-id = %s\n", id);
336		return (topo_mod_strdup(mod, id));
337	}
338	if (hc_name != NULL) {
339		topo_mod_strfree(mod, hc_name);
340	}
341
342	/*
343	 * Search the PRI for nodes of type MD_STR_COMPONENT and find the
344	 * first element with topo-hc-type of MD_STR_CHASSIS.  This node
345	 * will contain the MD_STR_SERIAL_NUMBER property to use as the
346	 * chassis-id.
347	 */
348	num_nodes = pi_find_mdenodes(mod, mdp, MDE_INVAL_ELEM_COOKIE,
349	    MD_STR_COMPONENT, MD_STR_FWD, &chassis_nodes, &chassis_size);
350	if (num_nodes <= 0 || chassis_nodes == NULL) {
351		/* We did not find any chassis nodes */
352		return (NULL);
353	}
354	topo_mod_dprintf(mod, "pi_get_chassisid: found %d %s nodes\n",
355	    num_nodes, MD_STR_COMPONENT);
356
357	idx = 0;
358	while (id == NULL && idx < num_nodes) {
359		hc_name = pi_get_topo_hc_name(mod, mdp, chassis_nodes[idx]);
360		if (hc_name != NULL && strcmp(hc_name, MD_STR_CHASSIS) == 0) {
361			/*
362			 * This is a chassis node.  Get the serial number
363			 * property from the node.
364			 */
365			result = md_get_prop_str(mdp, chassis_nodes[idx],
366			    MD_STR_SERIAL_NUMBER, &id);
367			if (result != 0) {
368				topo_mod_dprintf(mod, "pi_get_chassisid: "
369				    "failed to read %s from node_0x%llx\n",
370				    MD_STR_SERIAL_NUMBER,
371				    (uint64_t)chassis_nodes[idx]);
372			}
373		}
374		topo_mod_strfree(mod, hc_name);
375
376		/* Search the next node, if necessary */
377		idx++;
378	}
379	topo_mod_free(mod, chassis_nodes, chassis_size);
380
381	/* Everything is freed up and it's time to return the platform-id */
382	if (result != 0 || id == NULL || strlen(id) == 0) {
383		return (NULL);
384	}
385	topo_mod_dprintf(mod, "pi_get_chassis: chassis-id %s\n", id);
386
387	return (topo_mod_strdup(mod, id));
388}
389
390
391/*
392 * Determine if the node is a FRU by checking for the existance and non-zero
393 * value of the 'fru' property on the mde node.
394 */
395int
396pi_get_fru(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node, int *is_fru)
397{
398	int		result;
399	uint64_t	fru;
400
401	if (mod == NULL || mdp == NULL || is_fru == NULL) {
402		return (-1);
403	}
404	fru = 0;
405	*is_fru = 0;
406
407	result = md_get_prop_val(mdp, mde_node, MD_STR_FRU, &fru);
408	if (result != 0) {
409		/* The node is not a FRU. */
410		return (-1);
411	}
412	if (fru != 0) {
413		*is_fru = 1;
414	}
415	return (0);
416}
417
418
419/*
420 * Get the id property value from the given PRI node
421 */
422int
423pi_get_instance(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
424    topo_instance_t *ip)
425{
426	int		result;
427	uint64_t	id;
428
429	id = 0;
430	result = md_get_prop_val(mdp, mde_node, MD_STR_ID, &id);
431	if (result != 0) {
432		/*
433		 * There is no id property.
434		 */
435		topo_mod_dprintf(mod, "node_0x%llx has no id property\n",
436		    (uint64_t)mde_node);
437		return (-1);
438	}
439	*ip = id;
440
441	return (0);
442}
443
444
445/*
446 * If the given MDE node is a FRU return the 'nac' property, if it exists,
447 * to use as the label.
448 *
449 * The string must be freed with topo_mod_strfree()
450 */
451char *
452pi_get_label(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
453{
454	int	result;
455	int	is_fru;
456	char	*lp = NULL;
457	char	*hc_name = pi_get_topo_hc_name(mod, mdp, mde_node);
458
459	/*
460	 * The disk enumerator will set the "bay" node as a FRU and
461	 * expect the label from the PRI. The "fru" property can not
462	 * be set because hostconfig has no way to set the S/N, P/N,
463	 * etc.. in the PRI.
464	 */
465	if (strncmp(hc_name, BAY, strlen(BAY)) != 0) {
466		result = pi_get_fru(mod, mdp, mde_node, &is_fru);
467		if (result != 0 || is_fru == 0) {
468			/* This node is not a FRU.  It has no label */
469			topo_mod_strfree(mod, hc_name);
470			return (NULL);
471		}
472	}
473	topo_mod_strfree(mod, hc_name);
474
475	/*
476	 * The node is a FRU.  Get the NAC name to use as a label.
477	 */
478	result = md_get_prop_str(mdp, mde_node, MD_STR_NAC, &lp);
479	if (result != 0 || lp == NULL || strlen(lp) == 0) {
480		/* No NAC label.  Return NULL */
481		return (NULL);
482	}
483
484	/* Return a copy of the label */
485	return (topo_mod_strdup(mod, lp));
486}
487
488
489/*
490 * Return the "lun" property.
491 */
492int
493pi_get_lun(topo_mod_t *mod, di_node_t node)
494{
495	int		lun;
496	int		*buf;
497	unsigned char	*chbuf;
498	di_prop_t	di_prop = DI_PROP_NIL;
499	di_path_t	dpath = DI_PATH_NIL;
500	di_path_prop_t	di_path_prop = DI_PROP_NIL;
501
502	/* look for pathinfo property */
503	while ((dpath = di_path_phci_next_path(node, dpath)) != DI_PATH_NIL) {
504		while ((di_path_prop =
505		    di_path_prop_next(dpath, di_path_prop)) != DI_PROP_NIL) {
506			if (strcmp("lun",
507			    di_path_prop_name(di_path_prop)) == 0) {
508				(void) di_path_prop_ints(di_path_prop, &buf);
509				bcopy(buf, &lun, sizeof (int));
510				goto found;
511			}
512		}
513	}
514
515	/* look for devinfo property */
516	for (di_prop = di_prop_next(node, DI_PROP_NIL);
517	    di_prop != DI_PROP_NIL;
518	    di_prop = di_prop_next(node, di_prop)) {
519		if (strncmp("lun", di_prop_name(di_prop),
520		    strlen(di_prop_name(di_prop))) == 0) {
521			if (di_prop_bytes(di_prop, &chbuf) < sizeof (uint_t)) {
522				continue;
523			}
524			bcopy(chbuf, &lun, sizeof (uint_t));
525			goto found;
526		}
527	}
528
529	if (di_prop == DI_PROP_NIL && (dpath == DI_PATH_NIL ||
530	    di_path_prop == DI_PROP_NIL)) {
531		return (-1);
532	}
533found:
534	topo_mod_dprintf(mod, "pi_get_lun: lun = (%d)\n", lun);
535	return (lun);
536}
537
538
539/*
540 * Return the complete part number string to the caller.  The complete part
541 * number is made up of the part number attribute concatenated with the dash
542 * number attribute of the mde node.
543 *
544 * The string must be freed with topo_mod_strfree()
545 */
546char *
547pi_get_part(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
548{
549	int	result;
550	size_t	bufsize;
551	char	*buf  = NULL;
552	char	*part = NULL;
553	char	*dash = NULL;
554
555	result = md_get_prop_str(mdp, mde_node, MD_STR_PART_NUMBER, &part);
556	if (result != 0) {
557		part = NULL;
558	}
559	result = md_get_prop_str(mdp, mde_node, MD_STR_DASH_NUMBER, &dash);
560	if (result != 0) {
561		dash = NULL;
562	}
563	bufsize = 1 + (part ? strlen(part) : 0) + (dash ? strlen(dash) : 0);
564	if (bufsize == 1) {
565		return (NULL);
566	}
567
568	/* Construct the part number from the part and dash values */
569	buf = topo_mod_alloc(mod, bufsize);
570	if (buf != NULL) {
571		(void) snprintf(buf, bufsize, "%s%s", (part ? part : ""),
572		    (dash ? dash : ""));
573	}
574
575	return (buf);
576}
577
578
579/*
580 * Return the path string to the caller.
581 *
582 * The string must be freed with topo_mod_strfree()
583 */
584char *
585pi_get_path(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
586{
587	int	result;
588	int	i = 0;
589	size_t	max_addrs;
590	size_t	path_len = 0;
591	size_t	buf_len;
592	char	*propbuf = NULL;
593	char	*buf = NULL;
594	char	*buffree;
595	char	*path = NULL;
596	char	*token;
597	char	*dev_addr[MAX_PATH_DEPTH] = { NULL };
598	char	*dev_path[MAX_PATH_DEPTH] = { NULL };
599	char	*lastp;
600
601	/*
602	 * Get the path property from PRI; should look something
603	 * like "/@600/@0".
604	 */
605	result = md_get_prop_str(mdp, mde_node, MD_STR_PATH, &propbuf);
606	if (result != 0 || propbuf == NULL || strlen(propbuf) == 0) {
607		topo_mod_dprintf(mod, "pi_get_path: failed to get path\n");
608		return (NULL);
609	}
610	buf_len = strlen(propbuf) + 1;
611	buf = topo_mod_alloc(mod, buf_len);
612	if (buf == NULL) {
613		topo_mod_dprintf(mod, "pi_get_path: no memory\n");
614		return (NULL);
615	}
616	buffree = buf; /* strtok_r is destructive */
617	(void) strcpy(buf, propbuf);
618
619	/*
620	 * Grab the address(es) from the path property.
621	 */
622	if ((token = strtok_r(buf, "/@", &lastp)) != NULL) {
623		dev_addr[i] = topo_mod_strdup(mod, token);
624		while ((token = strtok_r(NULL, "/@", &lastp)) != NULL) {
625			if (++i < MAX_PATH_DEPTH) {
626				dev_addr[i] = topo_mod_strdup(mod, token);
627			} else {
628				topo_mod_dprintf(mod, "pi_get_path: path "
629				    "too long (%d)\n", i);
630				topo_mod_free(mod, buffree, buf_len);
631				return (NULL);
632			}
633		}
634	} else {
635		topo_mod_dprintf(mod, "pi_get_path: path wrong\n");
636		topo_mod_free(mod, buffree, buf_len);
637		return (NULL);
638	}
639	max_addrs = ++i;
640	topo_mod_free(mod, buffree, buf_len);
641
642	/*
643	 * Construct the path to look something like "/pci@600/pci@0".
644	 */
645	for (i = 0; i < max_addrs; i++) {
646		int len = strlen(dev_addr[i]) + strlen("/pci@") + 1;
647		path_len += len;
648		dev_path[i] = (char *)topo_mod_alloc(mod, len);
649		result = snprintf(dev_path[i], len, "/pci@%s", dev_addr[i]);
650		if (result < 0) {
651			return (NULL);
652		}
653	}
654
655	path_len -= (i - 1); /* leave room for one null char */
656	path = (char *)topo_mod_alloc(mod, path_len);
657	path = strcpy(path, dev_path[0]);
658
659	/* put the parts together */
660	for (i = 1; i < max_addrs; i++) {
661		path = strncat(path, dev_path[i], strlen(dev_path[i]) + 1);
662	}
663
664	/*
665	 * Cleanup
666	 */
667	for (i = 0; i < max_addrs; i++) {
668		if (dev_addr[i] != NULL) {
669			topo_mod_free(mod, dev_addr[i],
670			    strlen(dev_addr[i]) + 1);
671		}
672		if (dev_path[i] != NULL) {
673			topo_mod_free(mod, dev_path[i],
674			    strlen(dev_path[i]) + 1);
675		}
676	}
677
678	topo_mod_dprintf(mod, "pi_get_path: path = (%s)\n", path);
679	return (path);
680}
681
682
683/*
684 * Get the product ID from the 'platform' node in the PRI
685 *
686 * The string must be freed with topo_mod_strfree()
687 */
688char *
689pi_get_productid(topo_mod_t *mod, md_t *mdp)
690{
691	int		result;
692	char		*id = NULL;
693	size_t		platform_size;
694	mde_cookie_t	*platform_nodes = NULL;
695
696	topo_mod_dprintf(mod, "pi_get_product: enter\n");
697
698	/*
699	 * Search the PRI for nodes of type MD_STR_PLATFORM, which contains
700	 * the product-id in it's MD_STR_NAME property.
701	 */
702	result = pi_find_mdenodes(mod, mdp, MDE_INVAL_ELEM_COOKIE,
703	    MD_STR_PLATFORM, MD_STR_FWD, &platform_nodes, &platform_size);
704	if (result <= 0 || platform_nodes == NULL) {
705		/* We did not find any platform nodes */
706		return (NULL);
707	}
708	topo_mod_dprintf(mod, "pi_get_productid: found %d platform nodes\n",
709	    result);
710
711	/*
712	 * There should only be 1 platform node, so we will always
713	 * use the first if we find any at all.
714	 */
715	result = md_get_prop_str(mdp, platform_nodes[0], MD_STR_NAME, &id);
716	topo_mod_free(mod, platform_nodes, platform_size);
717
718	/* Everything is freed up and it's time to return the platform-id */
719	if (result != 0 || id == NULL || strlen(id) == 0) {
720		return (NULL);
721	}
722	topo_mod_dprintf(mod, "pi_get_product: returning %s\n", id);
723
724	return (topo_mod_strdup(mod, id));
725}
726
727
728/*
729 * If the phy pointer is NULL just return the number of 'phy_number' properties
730 * from the PRI; otherwise pass the 'phy_number' property values back to the
731 * caller.
732 *
733 * The caller is responsible for managing allocated memory.
734 */
735int
736pi_get_priphy(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node, uint8_t *phyp)
737{
738	int	result;
739	uint8_t	*phy_num;
740	int	nphy;
741
742	result = md_get_prop_data(mdp, mde_node, MD_STR_PHY_NUMBER,
743	    &phy_num, &nphy);
744	if (result != 0) {
745		/* no phy_number property */
746		topo_mod_dprintf(mod,
747		    "node_0x%llx has no phy_number property\n",
748		    (uint64_t)mde_node);
749		return (-1);
750	}
751
752	if (phyp != NULL) {
753		bcopy(phy_num, phyp, nphy);
754	}
755	return (nphy);
756}
757
758
759/*
760 * Return "phy-num" devinfo/pathinfo property.
761 */
762int
763pi_get_phynum(topo_mod_t *mod, di_node_t node)
764{
765	int		phy;
766	int		*buf;
767	unsigned char	*chbuf;
768	di_prop_t	di_prop = DI_PROP_NIL;
769	di_path_t	dpath = DI_PATH_NIL;
770	di_path_prop_t	di_path_prop = DI_PROP_NIL;
771
772
773	/* look for pathinfo property */
774	while ((dpath = di_path_phci_next_path(node, dpath)) != DI_PATH_NIL) {
775		while ((di_path_prop =
776		    di_path_prop_next(dpath, di_path_prop)) != DI_PROP_NIL) {
777			if (strcmp("phy-num",
778			    di_path_prop_name(di_path_prop)) == 0) {
779				(void) di_path_prop_ints(di_path_prop, &buf);
780				bcopy(buf, &phy, sizeof (int));
781				goto found;
782			}
783		}
784	}
785
786	/* look for devinfo property */
787	for (di_prop = di_prop_next(node, DI_PROP_NIL);
788	    di_prop != DI_PROP_NIL;
789	    di_prop = di_prop_next(node, di_prop)) {
790		if (strncmp("phy-num", di_prop_name(di_prop),
791		    strlen("phy-num")) == 0) {
792			if (di_prop_bytes(di_prop, &chbuf) < sizeof (uint_t)) {
793				continue;
794			}
795			bcopy(chbuf, &phy, sizeof (uint_t));
796			goto found;
797		}
798	}
799
800	if (di_prop == DI_PROP_NIL && (dpath == DI_PATH_NIL ||
801	    di_path_prop == DI_PROP_NIL)) {
802		return (-1);
803	}
804found:
805	topo_mod_dprintf(mod, "pi_get_phynum: phy = %d\n", phy);
806	return (phy);
807}
808
809
810/*
811 * Return the revision string to the caller.
812 *
813 * The string must be freed with topo_mod_strfree()
814 */
815char *
816pi_get_revision(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
817{
818	int	result;
819	char	*rp = NULL;
820
821	result = md_get_prop_str(mdp, mde_node, MD_STR_REVISION_NUMBER, &rp);
822	if (result != 0 || rp == NULL || strlen(rp) == 0) {
823		return (NULL);
824	}
825
826	return (topo_mod_strdup(mod, rp));
827}
828
829
830/*
831 * Return the serial number string to the caller.
832 *
833 * The string must be freed with topo_mod_strfree()
834 */
835char *
836pi_get_serial(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
837{
838	int		result;
839	uint64_t	sn;
840	char		*sp = NULL;
841	char		buf[MAXNAMELEN];
842
843	result = md_get_prop_str(mdp, mde_node, MD_STR_SERIAL_NUMBER, &sp);
844	if (result != 0 || sp == NULL || strlen(sp) == 0) {
845		/* Is this a uint64_t type serial number? */
846		result = md_get_prop_val(mdp, mde_node, MD_STR_SERIAL_NUMBER,
847		    &sn);
848		if (result != 0) {
849			/* No.  We have failed to find a serial number */
850			return (NULL);
851		}
852		topo_mod_dprintf(mod, "pi_get_serial: node_0x%llx numeric "
853		    "serial number %llx\n", (uint64_t)mde_node, sn);
854
855		/* Convert the acquired value to a string */
856		result = snprintf(buf, sizeof (buf), "%llu", sn);
857		if (result < 0) {
858			return (NULL);
859		}
860		sp = buf;
861	}
862	topo_mod_dprintf(mod, "pi_get_serial: node_0x%llx = %s\n",
863	    (uint64_t)mde_node, (sp == NULL ? "NULL" : sp));
864
865	return (topo_mod_strdup(mod, sp));
866}
867
868
869/*
870 * Get the server hostname (the ID as far as the topo authority is
871 * concerned) from sysinfo and return a copy to the caller.
872 *
873 * The string must be freed with topo_mod_strfree()
874 */
875char *
876pi_get_serverid(topo_mod_t *mod)
877{
878	int	result;
879	char	hostname[MAXNAMELEN];
880
881	topo_mod_dprintf(mod, "pi_get_serverid: enter\n");
882
883	result = sysinfo(SI_HOSTNAME, hostname, sizeof (hostname));
884	/* Everything is freed up and it's time to return the platform-id */
885	if (result == -1) {
886		return (NULL);
887	}
888	topo_mod_dprintf(mod, "pi_get_serverid: hostname = %s\n", hostname);
889
890	return (topo_mod_strdup(mod, hostname));
891}
892
893
894/*
895 * Return the "target-port" property.
896 *
897 * The string must be freed with topo_mod_strfree()
898 */
899char *
900pi_get_target_port(topo_mod_t *mod, di_node_t node)
901{
902	char		*tport;
903	di_prop_t	di_prop = DI_PROP_NIL;
904	di_path_t	dpath = DI_PATH_NIL;
905	di_path_prop_t	di_path_prop = DI_PROP_NIL;
906
907	/* look for pathinfo property */
908	while ((dpath = di_path_phci_next_path(node, dpath)) != DI_PATH_NIL) {
909		while ((di_path_prop =
910		    di_path_prop_next(dpath, di_path_prop)) != DI_PROP_NIL) {
911			if (strcmp("target-port",
912			    di_path_prop_name(di_path_prop)) == 0) {
913				(void) di_path_prop_strings(di_path_prop,
914				    &tport);
915				goto found;
916			}
917		}
918	}
919
920	/* look for devinfo property */
921	for (di_prop = di_prop_next(node, DI_PROP_NIL);
922	    di_prop != DI_PROP_NIL;
923	    di_prop = di_prop_next(node, di_prop)) {
924		if (strcmp("target-port", di_prop_name(di_prop)) == 0) {
925			if (di_prop_strings(di_prop, &tport) < 0) {
926				continue;
927			}
928			goto found;
929		}
930	}
931
932	if (di_prop == DI_PROP_NIL && (dpath == DI_PATH_NIL ||
933	    di_path_prop == DI_PROP_NIL)) {
934		return (NULL);
935	}
936found:
937	topo_mod_dprintf(mod, "pi_get_target_port: 'target-port' = (%s)\n",
938	    tport);
939	return (topo_mod_strdup(mod, tport));
940}
941
942
943/*
944 * Get the hc scheme name for the given node.
945 *
946 * The string must be freed with topo_mod_strfree()
947 */
948char *
949pi_get_topo_hc_name(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
950{
951	int	result;
952	char	*hc_name;
953
954	/*
955	 * Request the hc name from the node.
956	 */
957	hc_name = NULL;
958	result = md_get_prop_str(mdp, mde_node, MD_STR_TOPO_HC_NAME, &hc_name);
959	if (result != 0 || hc_name == NULL) {
960		topo_mod_dprintf(mod,
961		    "failed to get property %s from node_0x%llx\n",
962		    MD_STR_TOPO_HC_NAME, (uint64_t)mde_node);
963		return (NULL);
964	}
965
966	/* Return a copy of the type string */
967	return (topo_mod_strdup(mod, hc_name));
968}
969
970
971/*
972 * Calculate the authority information for a node.  Inherit the data if
973 * possible, but always create an appropriate property group.
974 */
975int
976pi_set_auth(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
977    tnode_t *t_parent, tnode_t *t_node)
978{
979	int 		result;
980	int		err;
981	nvlist_t	*auth;
982	char		*val = NULL;
983	char		*prod = NULL;
984	char		*psn = NULL;
985	char		*csn = NULL;
986	char		*server = NULL;
987
988	if (mod == NULL || mdp == NULL || t_parent == NULL || t_node == NULL) {
989		return (-1);
990	}
991
992	result = topo_pgroup_create(t_node, &auth_pgroup, &err);
993	if (result != 0 && err != ETOPO_PROP_DEFD) {
994		/*
995		 * We failed to create the property group and it was not
996		 * already defined.  Set the err code and return failure.
997		 */
998		(void) topo_mod_seterrno(mod, err);
999		return (-1);
1000	}
1001
1002	/* Get the authority information already available from the parent */
1003	auth = topo_mod_auth(mod, t_parent);
1004
1005	/*
1006	 * Set the authority data, inheriting it if possible, but creating it
1007	 * if necessary.
1008	 */
1009
1010	/* product-id */
1011	result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY,
1012	    FM_FMRI_AUTH_PRODUCT, &err);
1013	if (result != 0 && err != ETOPO_PROP_DEFD) {
1014		val = NULL;
1015		result = nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT,
1016		    &val);
1017		if (result != 0 || val == NULL) {
1018			/*
1019			 * No product information in the parent node or auth
1020			 * list.  Find the product information in the PRI.
1021			 */
1022			prod = pi_get_productid(mod, mdp);
1023			if (prod == NULL) {
1024				topo_mod_dprintf(mod, "pi_set_auth: product "
1025				    "name not found for node_0x%llx\n",
1026				    (uint64_t)mde_node);
1027			}
1028		} else {
1029			/*
1030			 * Dup the string.  If we cannot find it in the auth
1031			 * nvlist we will need to free it, so this lets us
1032			 * have a single code path.
1033			 */
1034			prod = topo_mod_strdup(mod, val);
1035		}
1036
1037		/*
1038		 * We continue even if the product information is not available
1039		 * to enumerate as much as possible.
1040		 */
1041		if (prod != NULL) {
1042			result = topo_prop_set_string(t_node, FM_FMRI_AUTHORITY,
1043			    FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, prod,
1044			    &err);
1045			if (result != 0) {
1046				/* Preserve the error and continue */
1047				(void) topo_mod_seterrno(mod, err);
1048				topo_mod_dprintf(mod, "pi_set_auth: failed to "
1049				    "set property %s (%d) : %s\n",
1050				    FM_FMRI_AUTH_CHASSIS, err,
1051				    topo_strerror(err));
1052			}
1053			topo_mod_strfree(mod, prod);
1054		}
1055	}
1056
1057	/* product-sn */
1058	result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY,
1059	    FM_FMRI_AUTH_PRODUCT_SN, &err);
1060	if (result != 0 && err != ETOPO_PROP_DEFD) {
1061		val = NULL;
1062		result = nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT_SN,
1063		    &val);
1064		if (result != 0 || val == NULL) {
1065			/*
1066			 * No product-sn information in the parent node or auth
1067			 * list.  Find the product-sn information in the PRI.
1068			 */
1069			psn = pi_get_productsn(mod, mdp, mde_node);
1070			if (psn == NULL) {
1071				topo_mod_dprintf(mod, "pi_set_auth: psn "
1072				    "name not found for node_0x%llx\n",
1073				    (uint64_t)mde_node);
1074			}
1075		} else {
1076			/*
1077			 * Dup the string.  If we cannot find it in the auth
1078			 * nvlist we will need to free it, so this lets us
1079			 * have a single code path.
1080			 */
1081			psn = topo_mod_strdup(mod, val);
1082		}
1083
1084		/*
1085		 * We continue even if the product information is not available
1086		 * to enumerate as much as possible.
1087		 */
1088		if (psn != NULL) {
1089			result = topo_prop_set_string(t_node, FM_FMRI_AUTHORITY,
1090			    FM_FMRI_AUTH_PRODUCT_SN, TOPO_PROP_IMMUTABLE, psn,
1091			    &err);
1092			if (result != 0) {
1093				/* Preserve the error and continue */
1094				(void) topo_mod_seterrno(mod, err);
1095				topo_mod_dprintf(mod, "pi_set_auth: failed to "
1096				    "set property %s (%d) : %s\n",
1097				    FM_FMRI_AUTH_PRODUCT_SN, err,
1098				    topo_strerror(err));
1099			}
1100			topo_mod_strfree(mod, psn);
1101		}
1102	}
1103
1104	/* chassis-id */
1105	result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY,
1106	    FM_FMRI_AUTH_CHASSIS, &err);
1107	if (result != 0 && err != ETOPO_PROP_DEFD) {
1108		val = NULL;
1109		result = nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS,
1110		    &val);
1111		if (result != 0 || val == NULL) {
1112			/*
1113			 * No product information in the parent node or auth
1114			 * list.  Find the product information in the PRI.
1115			 */
1116			csn = pi_get_chassisid(mod, mdp, mde_node);
1117			if (csn == NULL) {
1118				topo_mod_dprintf(mod, "pi_set_auth: csn "
1119				    "name not found for node_0x%llx\n",
1120				    (uint64_t)mde_node);
1121			}
1122		} else {
1123			/*
1124			 * Dup the string.  If we cannot find it in the auth
1125			 * nvlist we will need to free it, so this lets us
1126			 * have a single code path.
1127			 */
1128			csn = topo_mod_strdup(mod, val);
1129		}
1130
1131		/*
1132		 * We continue even if the product information is not available
1133		 * to enumerate as much as possible.
1134		 */
1135		if (csn != NULL) {
1136			result = topo_prop_set_string(t_node, FM_FMRI_AUTHORITY,
1137			    FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, csn,
1138			    &err);
1139			if (result != 0) {
1140				/* Preserve the error and continue */
1141				(void) topo_mod_seterrno(mod, err);
1142				topo_mod_dprintf(mod, "pi_set_auth: failed to "
1143				    "set property %s (%d) : %s\n",
1144				    FM_FMRI_AUTH_CHASSIS, err,
1145				    topo_strerror(err));
1146			}
1147			topo_mod_strfree(mod, csn);
1148		}
1149	}
1150
1151	/* server-id */
1152	result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY,
1153	    FM_FMRI_AUTH_SERVER, &err);
1154	if (result != 0 && err != ETOPO_PROP_DEFD) {
1155		val = NULL;
1156		result = nvlist_lookup_string(auth, FM_FMRI_AUTH_SERVER,
1157		    &val);
1158		if (result != 0 || val == NULL) {
1159			/*
1160			 * No product information in the parent node or auth
1161			 * list.  Find the product information in the PRI.
1162			 */
1163			server = pi_get_serverid(mod);
1164			if (server == NULL) {
1165				topo_mod_dprintf(mod, "pi_set_auth: server "
1166				    "name not found for node_0x%llx\n",
1167				    (uint64_t)mde_node);
1168			}
1169		} else {
1170			/*
1171			 * Dup the string.  If we cannot find it in the auth
1172			 * nvlist we will need to free it, so this lets us
1173			 * have a single code path.
1174			 */
1175			server = topo_mod_strdup(mod, val);
1176		}
1177
1178		/*
1179		 * We continue even if the product information is not available
1180		 * to enumerate as much as possible.
1181		 */
1182		if (server != NULL) {
1183			result = topo_prop_set_string(t_node, FM_FMRI_AUTHORITY,
1184			    FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, server,
1185			    &err);
1186			if (result != 0) {
1187				/* Preserve the error and continue */
1188				(void) topo_mod_seterrno(mod, err);
1189				topo_mod_dprintf(mod, "pi_set_auth: failed to "
1190				    "set property %s (%d) : %s\n",
1191				    FM_FMRI_AUTH_SERVER, err,
1192				    topo_strerror(err));
1193			}
1194			topo_mod_strfree(mod, server);
1195		}
1196	}
1197
1198	nvlist_free(auth);
1199
1200	return (0);
1201}
1202
1203
1204/*
1205 * Calculate a generic FRU for the given node.  If the node is not a FRU,
1206 * then inherit the FRU data from the nodes parent.
1207 */
1208int
1209pi_set_frufmri(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
1210    const char *name, topo_instance_t inst, tnode_t *t_parent, tnode_t *t_node)
1211{
1212	int		result;
1213	int		err;
1214	int		is_fru;
1215	char		*part;
1216	char		*rev;
1217	char		*serial;
1218	nvlist_t	*auth = NULL;
1219	nvlist_t	*frufmri = NULL;
1220
1221	if (t_node == NULL || mod == NULL || mdp == NULL) {
1222		return (-1);
1223	}
1224
1225	/*
1226	 * Determine if this node is a FRU
1227	 */
1228	result = pi_get_fru(mod, mdp, mde_node, &is_fru);
1229	if (result != 0 || is_fru == 0) {
1230		/* This node is not a FRU.  Inherit from parent and return */
1231		(void) topo_node_fru_set(t_node, NULL, 0, &result);
1232		return (0);
1233	}
1234
1235	/*
1236	 * This node is a FRU.  Create an FMRI.
1237	 */
1238	part	= pi_get_part(mod, mdp, mde_node);
1239	rev	= pi_get_revision(mod, mdp, mde_node);
1240	serial	= pi_get_serial(mod, mdp, mde_node);
1241	auth	= topo_mod_auth(mod, t_parent);
1242	frufmri	= topo_mod_hcfmri(mod, t_parent, FM_HC_SCHEME_VERSION, name,
1243	    inst, NULL, auth, part, rev, serial);
1244	if (frufmri == NULL) {
1245		topo_mod_dprintf(mod, "failed to create FRU: %s\n",
1246		    topo_strerror(topo_mod_errno(mod)));
1247	}
1248	nvlist_free(auth);
1249	topo_mod_strfree(mod, part);
1250	topo_mod_strfree(mod, rev);
1251	topo_mod_strfree(mod, serial);
1252
1253	/* Set the FRU, whether NULL or not */
1254	result = topo_node_fru_set(t_node, frufmri, 0, &err);
1255	if (result != 0)  {
1256		(void) topo_mod_seterrno(mod, err);
1257	}
1258	nvlist_free(frufmri);
1259
1260	return (result);
1261}
1262
1263
1264int
1265pi_set_label(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node, tnode_t *t_node)
1266{
1267	int	result;
1268	int	err;
1269	char	*label;
1270
1271	if (mod == NULL || mdp == NULL) {
1272		return (-1);
1273	}
1274
1275	/*
1276	 * Get the label, if any, from the mde node and apply it as the label
1277	 * for this topology node.  Note that a NULL label will inherit the
1278	 * label from topology node's parent.
1279	 */
1280	label = pi_get_label(mod, mdp, mde_node);
1281	result = topo_node_label_set(t_node, label, &err);
1282	topo_mod_strfree(mod, label);
1283	if (result != 0) {
1284		(void) topo_mod_seterrno(mod, err);
1285		topo_mod_dprintf(mod, "pi_set_label: failed with label %s "
1286		    "on node_0x%llx: %s\n", (label == NULL ? "NULL" : label),
1287		    (uint64_t)mde_node, topo_strerror(err));
1288	}
1289
1290	return (result);
1291}
1292
1293
1294/*
1295 * Calculate the system information for a node.  Inherit the data if
1296 * possible, but always create an appropriate property group.
1297 */
1298int
1299pi_set_system(topo_mod_t *mod, tnode_t *t_node)
1300{
1301	int		result;
1302	int		err;
1303	struct utsname	uts;
1304	char		isa[MAXNAMELEN];
1305
1306	if (mod == NULL || t_node == NULL) {
1307		return (-1);
1308	}
1309
1310	result = topo_pgroup_create(t_node, &sys_pgroup, &err);
1311	if (result != 0 && err != ETOPO_PROP_DEFD) {
1312		/*
1313		 * We failed to create the property group and it was not
1314		 * already defined.  Set the err code and return failure.
1315		 */
1316		(void) topo_mod_seterrno(mod, err);
1317		return (-1);
1318	}
1319
1320	result = topo_prop_inherit(t_node, TOPO_PGROUP_SYSTEM, TOPO_PROP_ISA,
1321	    &err);
1322	if (result != 0 && err != ETOPO_PROP_DEFD) {
1323		isa[0] = '\0';
1324		result = sysinfo(SI_ARCHITECTURE, isa, sizeof (isa));
1325		if (result == -1) {
1326			/* Preserve the error and continue */
1327			topo_mod_dprintf(mod, "pi_set_system: failed to "
1328			    "read SI_ARCHITECTURE: %d\n", errno);
1329		}
1330		if (strnlen(isa, MAXNAMELEN) > 0) {
1331			result = topo_prop_set_string(t_node,
1332			    TOPO_PGROUP_SYSTEM, TOPO_PROP_ISA,
1333			    TOPO_PROP_IMMUTABLE, isa, &err);
1334			if (result != 0) {
1335				/* Preserve the error and continue */
1336				(void) topo_mod_seterrno(mod, err);
1337				topo_mod_dprintf(mod, "pi_set_auth: failed to "
1338				    "set property %s (%d) : %s\n",
1339				    TOPO_PROP_ISA, err, topo_strerror(err));
1340			}
1341		}
1342	}
1343
1344	result = topo_prop_inherit(t_node, TOPO_PGROUP_SYSTEM,
1345	    TOPO_PROP_MACHINE, &err);
1346	if (result != 0 && err != ETOPO_PROP_DEFD) {
1347		result = uname(&uts);
1348		if (result == -1) {
1349			/* Preserve the error and continue */
1350			(void) topo_mod_seterrno(mod, errno);
1351			topo_mod_dprintf(mod, "pi_set_system: failed to "
1352			    "read uname: %d\n", errno);
1353		}
1354		if (strnlen(uts.machine, sizeof (uts.machine)) > 0) {
1355			result = topo_prop_set_string(t_node,
1356			    TOPO_PGROUP_SYSTEM, TOPO_PROP_MACHINE,
1357			    TOPO_PROP_IMMUTABLE, uts.machine, &err);
1358			if (result != 0) {
1359				/* Preserve the error and continue */
1360				(void) topo_mod_seterrno(mod, err);
1361				topo_mod_dprintf(mod, "pi_set_auth: failed to "
1362				    "set property %s (%d) : %s\n",
1363				    TOPO_PROP_MACHINE, err, topo_strerror(err));
1364			}
1365		}
1366	}
1367
1368	return (0);
1369}
1370
1371
1372tnode_t *
1373pi_node_bind(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
1374    tnode_t *t_parent, const char *hc_name, topo_instance_t inst,
1375    nvlist_t *fmri)
1376{
1377	int	result;
1378	tnode_t	*t_node;
1379
1380	if (t_parent == NULL) {
1381		topo_mod_dprintf(mod,
1382		    "cannot bind for node_0x%llx instance %d type %s\n",
1383		    (uint64_t)mde_node, inst, hc_name);
1384		return (NULL);
1385	}
1386
1387	/* Bind this node to the parent */
1388	t_node = topo_node_bind(mod, t_parent, hc_name, inst, fmri);
1389	if (t_node == NULL) {
1390		topo_mod_dprintf(mod,
1391		    "failed to bind node_0x%llx instance %d: %s\n",
1392		    (uint64_t)mde_node, (uint32_t)inst,
1393		    topo_strerror(topo_mod_errno(mod)));
1394		return (NULL);
1395	}
1396	topo_mod_dprintf(mod, "bound node_0x%llx instance %d type %s\n",
1397	    (uint64_t)mde_node, inst, hc_name);
1398
1399	/*
1400	 * We have bound the node.  Now decorate it with an appropriate
1401	 * FRU and label (which may be inherited from the parent).
1402	 *
1403	 * The disk enumerator requires that 'bay' nodes not set their
1404	 * fru property.
1405	 */
1406	if (strncmp(hc_name, BAY, strlen(BAY)) != 0) {
1407		result = pi_set_frufmri(mod, mdp, mde_node, hc_name, inst,
1408		    t_parent, t_node);
1409		if (result != 0) {
1410			/*
1411			 * Though we have failed to set the FRU FMRI we still
1412			 * continue. The module errno is set by the called
1413			 * routine, so we report the problem and move on.
1414			 */
1415			topo_mod_dprintf(mod,
1416			    "failed to set FRU FMRI for node_0x%llx\n",
1417			    (uint64_t)mde_node);
1418		}
1419	}
1420
1421	result = pi_set_label(mod, mdp, mde_node, t_node);
1422	if (result != 0) {
1423		/*
1424		 * Though we have failed to set the label, we still continue.
1425		 * The module errno is set by the called routine, so we report
1426		 * the problem and move on.
1427		 */
1428		topo_mod_dprintf(mod, "failed to set label for node_0x%llx\n",
1429		    (uint64_t)mde_node);
1430	}
1431
1432	result = pi_set_auth(mod, mdp, mde_node, t_parent, t_node);
1433	if (result != 0) {
1434		/*
1435		 * Though we have failed to set the authority, we still
1436		 * continue. The module errno is set by the called routine, so
1437		 * we report the problem and move on.
1438		 */
1439		topo_mod_dprintf(mod, "failed to set authority for "
1440		    "node_0x%llx\n", (uint64_t)mde_node);
1441	}
1442
1443	result = pi_set_system(mod, t_node);
1444	if (result != 0) {
1445		/*
1446		 * Though we have failed to set the system group, we still
1447		 * continue. The module errno is set by the called routine, so
1448		 * we report the problem and move on.
1449		 */
1450		topo_mod_dprintf(mod, "failed to set system for node_0x%llx\n",
1451		    (uint64_t)mde_node);
1452	}
1453
1454	return (t_node);
1455}
1456