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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  *  Subroutines used by the i86pc Generic Topology Enumerator
29  */
30 
31 #include <sys/types.h>
32 #include <strings.h>
33 #include <deflt.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <fm/topo_mod.h>
37 #include <fm/topo_hc.h>
38 #include <sys/devfm.h>
39 #include <sys/pci.h>
40 #include <sys/systeminfo.h>
41 #include <sys/fm/protocol.h>
42 #include <sys/utsname.h>
43 #include <sys/smbios.h>
44 #include <sys/smbios_impl.h>
45 #include <x86pi_impl.h>
46 
47 
48 static const topo_pgroup_info_t sys_pgroup = {
49 	TOPO_PGROUP_SYSTEM,
50 	TOPO_STABILITY_PRIVATE,
51 	TOPO_STABILITY_PRIVATE,
52 	1
53 };
54 
55 static const topo_pgroup_info_t auth_pgroup = {
56 	FM_FMRI_AUTHORITY,
57 	TOPO_STABILITY_PRIVATE,
58 	TOPO_STABILITY_PRIVATE,
59 	1
60 };
61 
62 
63 /*
64  * Free hcfmri strings.
65  */
66 void
67 x86pi_hcfmri_info_fini(topo_mod_t *mod, x86pi_hcfmri_t *hc)
68 {
69 	if (hc->hc_name != NULL)
70 		topo_mod_strfree(mod, (char *)hc->hc_name);
71 	if (hc->manufacturer != NULL)
72 		topo_mod_strfree(mod, (char *)hc->manufacturer);
73 	if (hc->product != NULL)
74 		topo_mod_strfree(mod, (char *)hc->product);
75 	if (hc->version != NULL)
76 		topo_mod_strfree(mod, (char *)hc->version);
77 	if (hc->serial_number != NULL)
78 		topo_mod_strfree(mod, (char *)hc->serial_number);
79 	if (hc->asset_tag != NULL)
80 		topo_mod_strfree(mod, (char *)hc->asset_tag);
81 	if (hc->location != NULL)
82 		topo_mod_strfree(mod, (char *)hc->location);
83 	if (hc->part_number != NULL)
84 		topo_mod_strfree(mod, (char *)hc->part_number);
85 }
86 
87 
88 /*
89  * Get the server hostname (the ID as far as the topo authority is
90  * concerned) from sysinfo and return a copy to the caller.
91  *
92  * The string must be freed with topo_mod_strfree()
93  */
94 char *
95 x86pi_get_serverid(topo_mod_t *mod)
96 {
97 	int result;
98 	char hostname[MAXNAMELEN];
99 
100 	topo_mod_dprintf(mod, "x86pi_get_serverid\n");
101 
102 	result = sysinfo(SI_HOSTNAME, hostname, sizeof (hostname));
103 	/* Everything is freed up and it's time to return the platform-id */
104 	if (result == -1) {
105 		return (NULL);
106 	}
107 	topo_mod_dprintf(mod, "x86pi_get_serverid: hostname = %s\n", hostname);
108 
109 	return (topo_mod_strdup(mod, hostname));
110 }
111 
112 
113 /*
114  * Get copy of SMBIOS.
115  */
116 smbios_hdl_t *
117 x86pi_smb_open(topo_mod_t *mod)
118 {
119 	smbios_hdl_t *smb_hdl;
120 	char *f = "x86pi_smb_open";
121 
122 	topo_mod_dprintf(mod, "%s\n", f);
123 
124 	smb_hdl = topo_mod_smbios(mod);
125 	if (smb_hdl == NULL) {
126 		topo_mod_dprintf(mod, "%s: failed to load SMBIOS\n", f);
127 		return (NULL);
128 	}
129 
130 	return (smb_hdl);
131 }
132 
133 
134 /*
135  * Go through the smbios structures looking for a type. Fill in
136  * the structure count as well as the id(s) of the struct types.
137  */
138 void
139 x86pi_smb_strcnt(smbios_hdl_t *shp, smbs_cnt_t *stype)
140 {
141 	const smb_struct_t *sp = shp->sh_structs;
142 	int nstructs = shp->sh_nstructs;
143 	int i, cnt;
144 
145 	for (i = 0, cnt = 0; i < nstructs; i++, sp++) {
146 		if (sp->smbst_hdr->smbh_type == stype->type) {
147 			stype->ids[cnt].node = NULL;
148 			stype->ids[cnt].id = sp->smbst_hdr->smbh_hdl;
149 			cnt++;
150 		}
151 	}
152 
153 	stype->count = cnt;
154 }
155 
156 
157 /*
158  * Calculate the authority information for a node.  Inherit the data if
159  * possible, but always create an appropriate property group.
160  */
161 int
162 x86pi_set_auth(topo_mod_t *mod, x86pi_hcfmri_t *hcfmri, tnode_t *t_parent,
163     tnode_t *t_node)
164 {
165 	int 		result;
166 	int		err;
167 	int		is_chassis = 0;
168 	int		chassis_instance = 0;
169 	nvlist_t	*auth;
170 	char		*val = NULL;
171 	char		*prod = NULL;
172 	char		*psn = NULL;
173 	char		*csn = NULL;
174 	char		*server = NULL;
175 	char		*f = "x86pi_set_auth";
176 
177 	if (mod == NULL || t_parent == NULL || t_node == NULL) {
178 		return (-1);
179 	}
180 
181 	result = topo_pgroup_create(t_node, &auth_pgroup, &err);
182 	if (result != 0 && err != ETOPO_PROP_DEFD) {
183 		/*
184 		 * We failed to create the property group and it was not
185 		 * already defined.  Set the err code and return failure.
186 		 */
187 		(void) topo_mod_seterrno(mod, err);
188 		return (-1);
189 	}
190 
191 	/* Get the authority information already available from the parent */
192 	auth = topo_mod_auth(mod, t_parent);
193 
194 	/* Determnine if this is a chassis node and set it's instance */
195 	if ((strlen(hcfmri->hc_name) == strlen(CHASSIS)) &&
196 	    strncmp(hcfmri->hc_name, CHASSIS, strlen(CHASSIS)) == 0) {
197 		is_chassis = 1;
198 		chassis_instance = hcfmri->instance;
199 	}
200 
201 	/*
202 	 * Set the authority data, inheriting it if possible, but creating it
203 	 * if necessary.
204 	 */
205 
206 	/* product-id */
207 	result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY,
208 	    FM_FMRI_AUTH_PRODUCT, &err);
209 	if (result != 0 && err != ETOPO_PROP_DEFD) {
210 		result = nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT,
211 		    &prod);
212 		if (result != 0 || prod == NULL) {
213 			/*
214 			 * No product information in the parent node or auth
215 			 * list. Use the product information in the hcfrmi
216 			 * struct.
217 			 */
218 			prod = (char *)hcfmri->product;
219 			if (prod == NULL) {
220 				topo_mod_dprintf(mod, "%s: product name not "
221 				    "found for %s node\n", f, hcfmri->hc_name);
222 			}
223 		}
224 
225 		/*
226 		 * We continue even if the product information is not available
227 		 * to enumerate as much as possible.
228 		 */
229 		if (prod != NULL) {
230 			result = topo_prop_set_string(t_node, FM_FMRI_AUTHORITY,
231 			    FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, prod,
232 			    &err);
233 			if (result != 0) {
234 				/* Preserve the error and continue */
235 				(void) topo_mod_seterrno(mod, err);
236 				topo_mod_dprintf(mod, "%s: failed to set "
237 				    "property %s (%d) : %s\n", f,
238 				    FM_FMRI_AUTH_PRODUCT, err,
239 				    topo_strerror(err));
240 			}
241 		}
242 	}
243 
244 	/* product-sn */
245 	result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY,
246 	    FM_FMRI_AUTH_PRODUCT_SN, &err);
247 	if (result != 0 && err != ETOPO_PROP_DEFD) {
248 		result = nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT_SN,
249 		    &psn);
250 		if (result != 0 || psn == NULL) {
251 			/*
252 			 * No product-sn information in the parent node or auth
253 			 * list.
254 			 */
255 			topo_mod_dprintf(mod, "%s: psn not found\n", f);
256 		} else {
257 			/*
258 			 * We continue even if the product-sn information is
259 			 * not available to enumerate as much as possible.
260 			 */
261 			result = topo_prop_set_string(t_node, FM_FMRI_AUTHORITY,
262 			    FM_FMRI_AUTH_PRODUCT_SN, TOPO_PROP_IMMUTABLE, psn,
263 			    &err);
264 			if (result != 0) {
265 				/* Preserve the error and continue */
266 				(void) topo_mod_seterrno(mod, err);
267 				topo_mod_dprintf(mod, "%s: failed to "
268 				    "set property %s (%d) : %s\n", f,
269 				    FM_FMRI_AUTH_PRODUCT_SN, err,
270 				    topo_strerror(err));
271 			}
272 		}
273 	}
274 
275 	/* chassis-id */
276 	if (is_chassis == 0 || (is_chassis == 1 && chassis_instance == 0)) {
277 		/* either not a chassis node, or chassis #0 */
278 		result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY,
279 		    FM_FMRI_AUTH_CHASSIS, &err);
280 	} else {
281 		/* chassis 'n' in a >1 chassis system */
282 		result = err = -1;
283 	}
284 	if (result != 0 && err != ETOPO_PROP_DEFD) {
285 		if (is_chassis == 0) {
286 			result = nvlist_lookup_string(auth,
287 			    FM_FMRI_AUTH_CHASSIS, &csn);
288 			if (result != 0 || csn == NULL) {
289 				/*
290 				 * No chassis information in the parent
291 				 * node or auth list.
292 				 */
293 				topo_mod_dprintf(mod,
294 				    "%s: csn name not found\n", f);
295 			}
296 		} else {
297 			/*
298 			 * So as not to blindly set the chassis-id to
299 			 * chassis #0's serial number.
300 			 */
301 			csn = val = topo_mod_strdup(mod, hcfmri->serial_number);
302 		}
303 
304 		/*
305 		 * We continue even if the chassis information is not available
306 		 * to enumerate as much as possible.
307 		 */
308 		if (csn != NULL) {
309 			if (is_chassis == 1)
310 				result = topo_prop_set_string(t_node,
311 				    FM_FMRI_AUTHORITY, FM_FMRI_AUTH_CHASSIS,
312 				    TOPO_PROP_MUTABLE, csn, &err);
313 			else
314 				result = topo_prop_set_string(t_node,
315 				    FM_FMRI_AUTHORITY, FM_FMRI_AUTH_CHASSIS,
316 				    TOPO_PROP_IMMUTABLE, csn, &err);
317 
318 			if (result != 0) {
319 				/* Preserve the error and continue */
320 				(void) topo_mod_seterrno(mod, err);
321 				topo_mod_dprintf(mod, "%s: failed to "
322 				    "set property %s (%d) : %s\n", f,
323 				    FM_FMRI_AUTH_CHASSIS, err,
324 				    topo_strerror(err));
325 			}
326 		}
327 
328 		if (val != NULL) {
329 			topo_mod_strfree(mod, val);
330 			val = NULL;
331 		}
332 	}
333 
334 	/* server-id */
335 	result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY,
336 	    FM_FMRI_AUTH_SERVER, &err);
337 	if (result != 0 && err != ETOPO_PROP_DEFD) {
338 		result = nvlist_lookup_string(auth, FM_FMRI_AUTH_SERVER,
339 		    &server);
340 		if (result != 0 || server == NULL) {
341 			/*
342 			 * No server information in the parent node or auth
343 			 * list.  Find the server information in hostname.
344 			 */
345 			server = val = x86pi_get_serverid(mod);
346 			if (server == NULL) {
347 				topo_mod_dprintf(mod, "%s: server "
348 				    "name not found for %s node\n", f,
349 				    hcfmri->hc_name);
350 			}
351 		}
352 
353 		/*
354 		 * We continue even if the server information is not available
355 		 * to enumerate as much as possible.
356 		 */
357 		if (server != NULL) {
358 			result = topo_prop_set_string(t_node, FM_FMRI_AUTHORITY,
359 			    FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, server,
360 			    &err);
361 			if (result != 0) {
362 				/* Preserve the error and continue */
363 				(void) topo_mod_seterrno(mod, err);
364 				topo_mod_dprintf(mod, "%s: failed to "
365 				    "set property %s (%d) : %s\n", f,
366 				    FM_FMRI_AUTH_SERVER, err,
367 				    topo_strerror(err));
368 			}
369 		}
370 
371 		if (val != NULL)
372 			topo_mod_strfree(mod, val);
373 	}
374 
375 	nvlist_free(auth);
376 
377 	return (0);
378 }
379 
380 
381 /*
382  * Calculate a generic FRU for the given node.  If the node is not a FRU,
383  * then inherit the FRU data from the nodes parent.
384  */
385 int
386 x86pi_set_frufmri(topo_mod_t *mod, x86pi_hcfmri_t *hcfmri, tnode_t *t_parent,
387     tnode_t *t_node, int flag)
388 {
389 	int		result;
390 	int		err;
391 
392 	nvlist_t	*auth = NULL;
393 	nvlist_t	*frufmri = NULL;
394 
395 	if (t_node == NULL || mod == NULL) {
396 		return (-1);
397 	}
398 
399 	/*
400 	 * Determine if this node is a FRU
401 	 */
402 	if (!(flag & X86PI_ENUM_FRU)) {
403 		/* This node is not a FRU.  Inherit from parent and return */
404 		(void) topo_node_fru_set(t_node, NULL, 0, &result);
405 		return (0);
406 	}
407 
408 	/*
409 	 * This node is a FRU.  Create an FMRI.
410 	 */
411 	auth	= topo_mod_auth(mod, t_parent);
412 	frufmri	= topo_mod_hcfmri(mod, t_parent, FM_HC_SCHEME_VERSION,
413 	    hcfmri->hc_name, hcfmri->instance, NULL, auth,
414 	    hcfmri->part_number, hcfmri->version, hcfmri->serial_number);
415 	if (frufmri == NULL) {
416 		topo_mod_dprintf(mod, "failed to create FRU: %s\n",
417 		    topo_strerror(topo_mod_errno(mod)));
418 	}
419 	nvlist_free(auth);
420 
421 	/* Set the FRU, whether NULL or not */
422 	result = topo_node_fru_set(t_node, frufmri, 0, &err);
423 	if (result != 0)  {
424 		(void) topo_mod_seterrno(mod, err);
425 	}
426 	nvlist_free(frufmri);
427 
428 	return (result);
429 }
430 
431 
432 /*
433  * Set the label for a topo node.
434  */
435 int
436 x86pi_set_label(topo_mod_t *mod, const char *label, const char *name,
437     tnode_t *t_node)
438 {
439 	int	result;
440 	int	err;
441 
442 	if (mod == NULL) {
443 		return (-1);
444 	}
445 
446 	/*
447 	 * Set the label for this topology node.
448 	 * Note that a NULL label will inherit the label from topology
449 	 * node's parent.
450 	 */
451 	result = topo_node_label_set(t_node, (char *)label, &err);
452 	if (result != 0) {
453 		(void) topo_mod_seterrno(mod, err);
454 		topo_mod_dprintf(mod, "x86pi_set_label: failed with label %s "
455 		    "on %s node: %s\n", (label == NULL ? "NULL" : label),
456 		    name, topo_strerror(err));
457 	}
458 
459 	return (result);
460 }
461 
462 
463 /*
464  * Calculate the system information for a node.  Inherit the data if
465  * possible, but always create an appropriate property group.
466  */
467 int
468 x86pi_set_system(topo_mod_t *mod, tnode_t *t_node)
469 {
470 	int		result;
471 	int		err;
472 	struct utsname	uts;
473 	char		isa[MAXNAMELEN];
474 
475 	if (mod == NULL || t_node == NULL) {
476 		return (-1);
477 	}
478 
479 	result = topo_pgroup_create(t_node, &sys_pgroup, &err);
480 	if (result != 0 && err != ETOPO_PROP_DEFD) {
481 		/*
482 		 * We failed to create the property group and it was not
483 		 * already defined.  Set the err code and return failure.
484 		 */
485 		(void) topo_mod_seterrno(mod, err);
486 		return (-1);
487 	}
488 
489 	result = topo_prop_inherit(t_node, TOPO_PGROUP_SYSTEM, TOPO_PROP_ISA,
490 	    &err);
491 	if (result != 0 && err != ETOPO_PROP_DEFD) {
492 		isa[0] = '\0';
493 		result = sysinfo(SI_ARCHITECTURE, isa, sizeof (isa));
494 		if (result == -1) {
495 			/* Preserve the error and continue */
496 			topo_mod_dprintf(mod, "x86pi_set_system: failed to "
497 			    "read SI_ARCHITECTURE: %d\n", errno);
498 		}
499 		if (strnlen(isa, MAXNAMELEN) > 0) {
500 			result = topo_prop_set_string(t_node,
501 			    TOPO_PGROUP_SYSTEM, TOPO_PROP_ISA,
502 			    TOPO_PROP_IMMUTABLE, isa, &err);
503 			if (result != 0) {
504 				/* Preserve the error and continue */
505 				(void) topo_mod_seterrno(mod, err);
506 				topo_mod_dprintf(mod,
507 				    "x86pi_set_auth: failed to "
508 				    "set property %s (%d) : %s\n",
509 				    TOPO_PROP_ISA, err, topo_strerror(err));
510 			}
511 		}
512 	}
513 
514 	result = topo_prop_inherit(t_node, TOPO_PGROUP_SYSTEM,
515 	    TOPO_PROP_MACHINE, &err);
516 	if (result != 0 && err != ETOPO_PROP_DEFD) {
517 		result = uname(&uts);
518 		if (result == -1) {
519 			/* Preserve the error and continue */
520 			(void) topo_mod_seterrno(mod, errno);
521 			topo_mod_dprintf(mod, "x86pi_set_system: failed to "
522 			    "read uname: %d\n", errno);
523 		}
524 		if (strnlen(uts.machine, sizeof (uts.machine)) > 0) {
525 			result = topo_prop_set_string(t_node,
526 			    TOPO_PGROUP_SYSTEM, TOPO_PROP_MACHINE,
527 			    TOPO_PROP_IMMUTABLE, uts.machine, &err);
528 			if (result != 0) {
529 				/* Preserve the error and continue */
530 				(void) topo_mod_seterrno(mod, err);
531 				topo_mod_dprintf(mod,
532 				    "x86pi_set_auth: failed to "
533 				    "set property %s (%d) : %s\n",
534 				    TOPO_PROP_MACHINE, err, topo_strerror(err));
535 			}
536 		}
537 	}
538 
539 	return (0);
540 }
541 
542 /*
543  * All the checks for compatibility are done within the kernel where the
544  * ereport generators are. They'll determine first if there's a problem
545  * and the topo enum will follow suit. The /dev/fm ioclt returns the value
546  * of the x86gentopo_legacy kernel variable which determines if this platform
547  * will provide an x86 generic topo or legacy topo enumeration.
548  */
549 /* ARGSUSED */
550 int
551 x86pi_check_comp(topo_mod_t *mod, smbios_hdl_t *shp)
552 {
553 	int rv;
554 	int fd;
555 	int32_t legacy;
556 	nvlist_t *nvl = NULL;
557 	fm_ioc_data_t fid;
558 	char *ibuf = NULL, *obuf = NULL;
559 	size_t insz = 0, outsz = 0;
560 	char *f = "x86pi_check_comp";
561 
562 	/* open /dev/fm */
563 	fd = open("/dev/fm", O_RDONLY);
564 	if (fd < 0) {
565 		topo_mod_dprintf(mod, "%s: failed to open /dev/fm.\n", f);
566 		return (X86PI_NONE);
567 	}
568 
569 	/* set up buffers and ioctl data structure */
570 	outsz = FM_IOC_MAXBUFSZ;
571 	obuf = topo_mod_alloc(mod, outsz);
572 	if (obuf == NULL) {
573 		perror("umem_alloc");
574 		return (X86PI_NONE);
575 	}
576 
577 	fid.fid_version = 1;
578 	fid.fid_insz = insz;
579 	fid.fid_inbuf = ibuf;
580 	fid.fid_outsz = outsz;
581 	fid.fid_outbuf = obuf;
582 
583 	/* send the ioctl to /dev/fm to retrieve legacy variable */
584 	rv = ioctl(fd, FM_IOC_GENTOPO_LEGACY, &fid);
585 	if (rv < 0) {
586 		topo_mod_dprintf(mod, "%s: ioctl to /dev/fm failed", f);
587 		perror("fm_ioctl");
588 		(void) close(fd);
589 		return (X86PI_NONE);
590 	}
591 	(void) close(fd);
592 
593 	(void) nvlist_unpack(fid.fid_outbuf, fid.fid_outsz, &nvl, 0);
594 	(void) nvlist_lookup_int32(nvl, FM_GENTOPO_LEGACY, &legacy);
595 
596 	nvlist_free(nvl);
597 	topo_mod_free(mod, obuf, outsz);
598 
599 	if (legacy == 1) {
600 		/* legacy kernel variable set; will do the same */
601 		return (X86PI_NONE);
602 	}
603 
604 	/* legacy kernel variable not set; generic topo enum */
605 	return (X86PI_FULL);
606 }
607 
608 const char *
609 x86pi_cleanup_smbios_str(topo_mod_t *mod, const char *begin, int str_type)
610 {
611 	char buf[MAXNAMELEN];
612 	const char *end, *cp;
613 	char *pp;
614 	char c;
615 	int i;
616 
617 	end = begin + strlen(begin);
618 
619 	while (begin < end && isspace(*begin))
620 		begin++;
621 	while (begin < end && isspace(*(end - 1)))
622 		end--;
623 
624 	if (begin >= end)
625 		return (NULL);
626 
627 	cp = begin;
628 	for (i = 0; i < MAXNAMELEN - 1; i++) {
629 		if (cp >= end)
630 			break;
631 		c = *cp;
632 		if (str_type == LABEL) {
633 			if (!isprint(c))
634 				buf[i] = '-';
635 			else
636 				buf[i] = c;
637 		} else {
638 			if (c == ':' || c == '=' || c == '/' ||
639 			    isspace(c) || !isprint(c))
640 				buf[i] = '-';
641 			else
642 				buf[i] = c;
643 		}
644 		cp++;
645 	}
646 	buf[i] = 0;
647 
648 	pp = topo_mod_strdup(mod, buf);
649 
650 	if (str_type == LABEL)
651 		topo_mod_strfree(mod, (char *)begin);
652 
653 	return (pp);
654 }
655 
656 /*
657  * Return Bus/Dev/Func from "reg" devinfo property.
658  */
659 uint16_t
660 x86pi_bdf(topo_mod_t *mod, di_node_t node)
661 {
662 	int *val;
663 
664 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &val) < 0) {
665 		topo_mod_dprintf(mod, "couldn't get \"reg\" prop: %s.\n",
666 		    strerror(errno));
667 		return ((uint16_t)-1);
668 	}
669 
670 	return (uint16_t)((*val & PCI_REG_BDFR_M) >> PCI_REG_FUNC_SHIFT);
671 }
672 
673 /*
674  * Return PHY from "sata-phy" devinfo proporty.
675  */
676 int
677 x86pi_phy(topo_mod_t *mod, di_node_t node)
678 {
679 	int *phy;
680 
681 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "sata-phy", &phy) < 0) {
682 		topo_mod_dprintf(mod, "couldn't get \"sata-phy\" prop: %s.\n",
683 		    strerror(errno));
684 		return (-1);
685 	}
686 
687 	return (*phy);
688 }
689