1602ca9eaScth /*
2602ca9eaScth  * CDDL HEADER START
3602ca9eaScth  *
4602ca9eaScth  * The contents of this file are subject to the terms of the
5602ca9eaScth  * Common Development and Distribution License (the "License").
6602ca9eaScth  * You may not use this file except in compliance with the License.
7602ca9eaScth  *
8602ca9eaScth  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9602ca9eaScth  * or http://www.opensolaris.org/os/licensing.
10602ca9eaScth  * See the License for the specific language governing permissions
11602ca9eaScth  * and limitations under the License.
12602ca9eaScth  *
13602ca9eaScth  * When distributing Covered Code, include this CDDL HEADER in each
14602ca9eaScth  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15602ca9eaScth  * If applicable, add the following below this CDDL HEADER, with the
16602ca9eaScth  * fields enclosed by brackets "[]" replaced with your own identifying
17602ca9eaScth  * information: Portions Copyright [yyyy] [name of copyright owner]
18602ca9eaScth  *
19602ca9eaScth  * CDDL HEADER END
20602ca9eaScth  */
21602ca9eaScth 
22602ca9eaScth /*
2344ed9dbbSStephen Hanson  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
243c6ffbabSRob Johnston  * Copyright 2020 Joyent, Inc.
25602ca9eaScth  */
26602ca9eaScth 
27602ca9eaScth /*
28602ca9eaScth  * Functions in this file are shared between the disk and ses enumerators.
29602ca9eaScth  *
30602ca9eaScth  * A topo_list_t of all disks is returned by a successful disk_list_gather()
31602ca9eaScth  * call, and the list is freed by a disk_list_free(). To create a 'disk' topo
32602ca9eaScth  * node below a specific 'bay' parent node either disk_declare_path() or
33602ca9eaScth  * disk_declare_addr() are called. The caller determines which 'disk' is
34602ca9eaScth  * in which 'bay'. A disk's 'label' and 'authority' information come from
35602ca9eaScth  * its parent 'bay' node.
36602ca9eaScth  */
37602ca9eaScth 
38940d71d2Seschrock #include <ctype.h>
39602ca9eaScth #include <strings.h>
40602ca9eaScth #include <libdevinfo.h>
4100d7a6fbSRob Johnston #include <libdiskmgt.h>
42602ca9eaScth #include <devid.h>
43602ca9eaScth #include <sys/libdevid.h>
44602ca9eaScth #include <pthread.h>
45602ca9eaScth #include <inttypes.h>
46602ca9eaScth #include <sys/dkio.h>
47602ca9eaScth #include <sys/scsi/scsi_types.h>
48602ca9eaScth #include <fm/topo_mod.h>
49602ca9eaScth #include <fm/topo_list.h>
50602ca9eaScth #include <fm/libdiskstatus.h>
51602ca9eaScth #include <sys/fm/protocol.h>
52ac88567aSHyon Kim #include <sys/scsi/generic/inquiry.h>
53602ca9eaScth #include "disk.h"
54602ca9eaScth 
55602ca9eaScth /* common callback information for di_walk_node() and di_devlink_walk */
56602ca9eaScth typedef struct disk_cbdata {
57602ca9eaScth 	topo_mod_t		*dcb_mod;
58602ca9eaScth 	topo_list_t		*dcb_list;
59602ca9eaScth 
60602ca9eaScth 	di_devlink_handle_t	dcb_devhdl;
61ac88567aSHyon Kim 	dev_di_node_t		*dcb_dnode;	/* for di_devlink_walk only */
62602ca9eaScth } disk_cbdata_t;
63602ca9eaScth 
64602ca9eaScth /*
65602ca9eaScth  * Methods for disks. This is used by the disk-transport module to
66602ca9eaScth  * generate ereports based off SCSI disk status.
67602ca9eaScth  */
68602ca9eaScth static int disk_status(topo_mod_t *, tnode_t *, topo_version_t,
69602ca9eaScth 	nvlist_t *, nvlist_t **);
70602ca9eaScth 
71602ca9eaScth static const topo_method_t disk_methods[] = {
72602ca9eaScth 	{ TOPO_METH_DISK_STATUS, TOPO_METH_DISK_STATUS_DESC,
73602ca9eaScth 	    TOPO_METH_DISK_STATUS_VERSION, TOPO_STABILITY_INTERNAL,
74602ca9eaScth 	    disk_status },
75602ca9eaScth 	{ NULL }
76602ca9eaScth };
77602ca9eaScth 
7800d7a6fbSRob Johnston static int disk_temp_reading(topo_mod_t *, tnode_t *, topo_version_t,
7900d7a6fbSRob Johnston     nvlist_t *, nvlist_t **);
8000d7a6fbSRob Johnston 
8100d7a6fbSRob Johnston #define	TOPO_METH_DISK_TEMP		"disk_temp_reading"
8200d7a6fbSRob Johnston #define	TOPO_METH_DISK_TEMP_DESC	"Disk Temperature Reading"
8300d7a6fbSRob Johnston #define	TOPO_METH_DISK_TEMP_VERSION	0
8400d7a6fbSRob Johnston 
8500d7a6fbSRob Johnston static const topo_method_t disk_fac_methods[] = {
8600d7a6fbSRob Johnston 	{ TOPO_METH_DISK_TEMP, TOPO_METH_DISK_TEMP_DESC,
8700d7a6fbSRob Johnston 	    TOPO_METH_DISK_TEMP_VERSION, TOPO_STABILITY_INTERNAL,
8800d7a6fbSRob Johnston 	    disk_temp_reading },
8900d7a6fbSRob Johnston 	{ NULL }
9000d7a6fbSRob Johnston };
9100d7a6fbSRob Johnston 
92602ca9eaScth /*
93ac88567aSHyon Kim  * Set the properties of the disk node, from dev_di_node_t data.
94602ca9eaScth  * Properties include:
95602ca9eaScth  *	group: protocol	 properties: resource, asru, label, fru
96602ca9eaScth  *	group: authority properties: product-id, chasis-id, server-id
97602ca9eaScth  *	group: io	 properties: devfs-path, devid
98602ca9eaScth  *	group: storage	 properties:
99602ca9eaScth  *		- logical-disk, disk-model, disk-manufacturer, serial-number
100602ca9eaScth  *		- firmware-revision, capacity-in-bytes
10144ed9dbbSStephen Hanson  *
10244ed9dbbSStephen Hanson  * NOTE: the io and storage groups won't be present if the dnode passed in is
10344ed9dbbSStephen Hanson  * NULL. This happens when a disk is found through ses, but is not enumerated
10444ed9dbbSStephen Hanson  * in the devinfo tree.
105602ca9eaScth  */
106602ca9eaScth static int
disk_set_props(topo_mod_t * mod,tnode_t * parent,tnode_t * dtn,dev_di_node_t * dnode)107602ca9eaScth disk_set_props(topo_mod_t *mod, tnode_t *parent,
108ac88567aSHyon Kim     tnode_t *dtn, dev_di_node_t *dnode)
109602ca9eaScth {
11000d7a6fbSRob Johnston 	nvlist_t	*asru = NULL, *drive_attrs;
111602ca9eaScth 	char		*label = NULL;
112602ca9eaScth 	nvlist_t	*fmri = NULL;
1135cc5d5ceSToomas Soome 	dm_descriptor_t drive_descr = 0;
11400d7a6fbSRob Johnston 	uint32_t	rpm;
115602ca9eaScth 	int		err;
116602ca9eaScth 
117602ca9eaScth 	/* pull the label property down from our parent 'bay' node */
118602ca9eaScth 	if (topo_node_label(parent, &label, &err) != 0) {
119672fc84aSRobert Mustacchi 		if (err != ETOPO_PROP_NOENT) {
120672fc84aSRobert Mustacchi 			topo_mod_dprintf(mod, "disk_set_props: "
121672fc84aSRobert Mustacchi 			    "label error %s\n", topo_strerror(err));
122672fc84aSRobert Mustacchi 			goto error;
123672fc84aSRobert Mustacchi 		}
12436f5a109SRob Johnston 	} else if (topo_prop_set_string(dtn, TOPO_PGROUP_PROTOCOL,
12536f5a109SRob Johnston 	    TOPO_PROP_LABEL, TOPO_PROP_MUTABLE, label, &err) != 0) {
126602ca9eaScth 		topo_mod_dprintf(mod, "disk_set_props: "
127602ca9eaScth 		    "label_set error %s\n", topo_strerror(err));
128602ca9eaScth 		goto error;
129602ca9eaScth 	}
130602ca9eaScth 
131602ca9eaScth 	/* get the resource fmri, and use it as the fru */
132602ca9eaScth 	if (topo_node_resource(dtn, &fmri, &err) != 0) {
133602ca9eaScth 		topo_mod_dprintf(mod, "disk_set_props: "
134602ca9eaScth 		    "resource error: %s\n", topo_strerror(err));
135602ca9eaScth 		goto error;
136602ca9eaScth 	}
137602ca9eaScth 	if (topo_node_fru_set(dtn, fmri, 0, &err) != 0) {
138602ca9eaScth 		topo_mod_dprintf(mod, "disk_set_props: "
139602ca9eaScth 		    "fru_set error: %s\n", topo_strerror(err));
140602ca9eaScth 		goto error;
141602ca9eaScth 	}
142602ca9eaScth 
143602ca9eaScth 	/* create/set the authority group */
144602ca9eaScth 	if ((topo_pgroup_create(dtn, &disk_auth_pgroup, &err) != 0) &&
145602ca9eaScth 	    (err != ETOPO_PROP_DEFD)) {
146602ca9eaScth 		topo_mod_dprintf(mod, "disk_set_props: "
147602ca9eaScth 		    "create disk_auth error %s\n", topo_strerror(err));
148602ca9eaScth 		goto error;
149602ca9eaScth 	}
150602ca9eaScth 
15144ed9dbbSStephen Hanson 	/* create the storage group */
15244ed9dbbSStephen Hanson 	if (topo_pgroup_create(dtn, &storage_pgroup, &err) != 0) {
15344ed9dbbSStephen Hanson 		topo_mod_dprintf(mod, "disk_set_props: "
15444ed9dbbSStephen Hanson 		    "create storage error %s\n", topo_strerror(err));
15544ed9dbbSStephen Hanson 		goto error;
15644ed9dbbSStephen Hanson 	}
15744ed9dbbSStephen Hanson 
15844ed9dbbSStephen Hanson 	/* no dnode was found for this disk - skip the io and storage groups */
15944ed9dbbSStephen Hanson 	if (dnode == NULL) {
16044ed9dbbSStephen Hanson 		err = 0;
16144ed9dbbSStephen Hanson 		goto out;
16244ed9dbbSStephen Hanson 	}
16344ed9dbbSStephen Hanson 
16444ed9dbbSStephen Hanson 	/* form and set the asru */
16544ed9dbbSStephen Hanson 	if ((asru = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION,
16644ed9dbbSStephen Hanson 	    dnode->ddn_dpath, dnode->ddn_devid)) == NULL) {
16744ed9dbbSStephen Hanson 		err = ETOPO_FMRI_UNKNOWN;
16844ed9dbbSStephen Hanson 		topo_mod_dprintf(mod, "disk_set_props: "
16944ed9dbbSStephen Hanson 		    "asru error %s\n", topo_strerror(err));
17044ed9dbbSStephen Hanson 		goto error;
17144ed9dbbSStephen Hanson 	}
17244ed9dbbSStephen Hanson 	if (topo_node_asru_set(dtn, asru, 0, &err) != 0) {
17344ed9dbbSStephen Hanson 		topo_mod_dprintf(mod, "disk_set_props: "
17444ed9dbbSStephen Hanson 		    "asru_set error %s\n", topo_strerror(err));
17544ed9dbbSStephen Hanson 		goto error;
17644ed9dbbSStephen Hanson 	}
17744ed9dbbSStephen Hanson 
178602ca9eaScth 	/* create/set the devfs-path and devid in the io group */
179602ca9eaScth 	if (topo_pgroup_create(dtn, &io_pgroup, &err) != 0) {
180602ca9eaScth 		topo_mod_dprintf(mod, "disk_set_props: "
181602ca9eaScth 		    "create io error %s\n", topo_strerror(err));
182602ca9eaScth 		goto error;
183602ca9eaScth 	}
184602ca9eaScth 
185602ca9eaScth 	if (topo_prop_set_string(dtn, TOPO_PGROUP_IO, TOPO_IO_DEV_PATH,
186602ca9eaScth 	    TOPO_PROP_IMMUTABLE, dnode->ddn_dpath, &err) != 0) {
187602ca9eaScth 		topo_mod_dprintf(mod, "disk_set_props: "
188602ca9eaScth 		    "set dev error %s\n", topo_strerror(err));
189602ca9eaScth 		goto error;
190602ca9eaScth 	}
191602ca9eaScth 
19244ed9dbbSStephen Hanson 	if (dnode->ddn_devid && topo_prop_set_string(dtn, TOPO_PGROUP_IO,
19344ed9dbbSStephen Hanson 	    TOPO_IO_DEVID, TOPO_PROP_IMMUTABLE, dnode->ddn_devid, &err) != 0) {
194602ca9eaScth 		topo_mod_dprintf(mod, "disk_set_props: "
195602ca9eaScth 		    "set devid error %s\n", topo_strerror(err));
196602ca9eaScth 		goto error;
197602ca9eaScth 	}
198602ca9eaScth 
199602ca9eaScth 	if (dnode->ddn_ppath_count != 0 &&
200602ca9eaScth 	    topo_prop_set_string_array(dtn, TOPO_PGROUP_IO, TOPO_IO_PHYS_PATH,
201602ca9eaScth 	    TOPO_PROP_IMMUTABLE, (const char **)dnode->ddn_ppath,
202602ca9eaScth 	    dnode->ddn_ppath_count, &err) != 0) {
203602ca9eaScth 		topo_mod_dprintf(mod, "disk_set_props: "
204602ca9eaScth 		    "set phys-path error %s\n", topo_strerror(err));
205602ca9eaScth 		goto error;
206602ca9eaScth 	}
207602ca9eaScth 
208602ca9eaScth 	/* set the storage group public /dev name */
209f76de749SStephen Hanson 	if (dnode->ddn_lpath != NULL &&
210f76de749SStephen Hanson 	    topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
211602ca9eaScth 	    TOPO_STORAGE_LOGICAL_DISK_NAME, TOPO_PROP_IMMUTABLE,
212602ca9eaScth 	    dnode->ddn_lpath, &err) != 0) {
213602ca9eaScth 		topo_mod_dprintf(mod, "disk_set_props: "
214602ca9eaScth 		    "set disk_name error %s\n", topo_strerror(err));
215602ca9eaScth 		goto error;
216602ca9eaScth 	}
217602ca9eaScth 
218602ca9eaScth 	/* populate other misc storage group properties */
219602ca9eaScth 	if (dnode->ddn_mfg && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
220602ca9eaScth 	    TOPO_STORAGE_MANUFACTURER, TOPO_PROP_IMMUTABLE,
221602ca9eaScth 	    dnode->ddn_mfg, &err) != 0)) {
222602ca9eaScth 		topo_mod_dprintf(mod, "disk_set_props: "
223602ca9eaScth 		    "set mfg error %s\n", topo_strerror(err));
224602ca9eaScth 		goto error;
225602ca9eaScth 	}
226602ca9eaScth 	if (dnode->ddn_model && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
227602ca9eaScth 	    TOPO_STORAGE_MODEL, TOPO_PROP_IMMUTABLE,
228602ca9eaScth 	    dnode->ddn_model, &err) != 0)) {
229602ca9eaScth 		topo_mod_dprintf(mod, "disk_set_props: "
230602ca9eaScth 		    "set model error %s\n", topo_strerror(err));
231602ca9eaScth 		goto error;
232602ca9eaScth 	}
233602ca9eaScth 	if (dnode->ddn_serial && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
234602ca9eaScth 	    TOPO_STORAGE_SERIAL_NUM, TOPO_PROP_IMMUTABLE,
235602ca9eaScth 	    dnode->ddn_serial, &err) != 0)) {
236602ca9eaScth 		topo_mod_dprintf(mod, "disk_set_props: "
237602ca9eaScth 		    "set serial error %s\n", topo_strerror(err));
238602ca9eaScth 		goto error;
239602ca9eaScth 	}
240602ca9eaScth 	if (dnode->ddn_firm && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
241602ca9eaScth 	    TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE,
242602ca9eaScth 	    dnode->ddn_firm, &err) != 0)) {
243602ca9eaScth 		topo_mod_dprintf(mod, "disk_set_props: "
244602ca9eaScth 		    "set firm error %s\n", topo_strerror(err));
245602ca9eaScth 		goto error;
246602ca9eaScth 	}
247602ca9eaScth 	if (dnode->ddn_cap && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
248602ca9eaScth 	    TOPO_STORAGE_CAPACITY, TOPO_PROP_IMMUTABLE,
249602ca9eaScth 	    dnode->ddn_cap, &err) != 0)) {
250602ca9eaScth 		topo_mod_dprintf(mod, "disk_set_props: "
251602ca9eaScth 		    "set cap error %s\n", topo_strerror(err));
252602ca9eaScth 		goto error;
253602ca9eaScth 	}
25400d7a6fbSRob Johnston 
25500d7a6fbSRob Johnston 	if (dnode->ddn_devid == NULL ||
25600d7a6fbSRob Johnston 	    (drive_descr = dm_get_descriptor_by_name(DM_DRIVE,
2575cc5d5ceSToomas Soome 	    dnode->ddn_devid, &err)) == 0 ||
25800d7a6fbSRob Johnston 	    (drive_attrs = dm_get_attributes(drive_descr, &err)) == NULL)
25900d7a6fbSRob Johnston 		goto out;
26000d7a6fbSRob Johnston 
26100d7a6fbSRob Johnston 	if (nvlist_lookup_boolean(drive_attrs, DM_SOLIDSTATE) == 0 ||
26200d7a6fbSRob Johnston 	    nvlist_lookup_uint32(drive_attrs, DM_RPM, &rpm) != 0)
26300d7a6fbSRob Johnston 		goto out;
26400d7a6fbSRob Johnston 
26500d7a6fbSRob Johnston 	if (topo_prop_set_uint32(dtn, TOPO_PGROUP_STORAGE, TOPO_STORAGE_RPM,
26600d7a6fbSRob Johnston 	    TOPO_PROP_IMMUTABLE, rpm, &err) != 0) {
26700d7a6fbSRob Johnston 		topo_mod_dprintf(mod, "disk_set_props: "
26800d7a6fbSRob Johnston 		    "set rpm error %s\n", topo_strerror(err));
26900d7a6fbSRob Johnston 		dm_free_descriptor(drive_descr);
27000d7a6fbSRob Johnston 		goto error;
27100d7a6fbSRob Johnston 	}
272602ca9eaScth 	err = 0;
273602ca9eaScth 
274508a0e8cSRob Johnston 	/*
275508a0e8cSRob Johnston 	 * Create UFM node to capture the drive firmware version
276508a0e8cSRob Johnston 	 */
277508a0e8cSRob Johnston 	if (dnode->ddn_firm != NULL) {
278508a0e8cSRob Johnston 		topo_ufm_slot_info_t slotinfo = { 0 };
279508a0e8cSRob Johnston 
280508a0e8cSRob Johnston 		slotinfo.usi_version = dnode->ddn_firm;
281508a0e8cSRob Johnston 		slotinfo.usi_active = B_TRUE;
282508a0e8cSRob Johnston 		if (strcmp(topo_node_name(parent), USB_DEVICE) == 0)
283508a0e8cSRob Johnston 			slotinfo.usi_mode = TOPO_UFM_SLOT_MODE_NONE;
284508a0e8cSRob Johnston 		else
285508a0e8cSRob Johnston 			slotinfo.usi_mode = TOPO_UFM_SLOT_MODE_WO;
286508a0e8cSRob Johnston 		if (topo_node_range_create(mod, dtn, UFM, 0, 0) != 0 ||
287*744642a2SRobert Mustacchi 		    topo_mod_create_ufm(mod, dtn, 0, "drive firmware",
288508a0e8cSRob Johnston 		    &slotinfo) == NULL) {
289508a0e8cSRob Johnston 			topo_mod_dprintf(mod, "failed to create %s node", UFM);
290508a0e8cSRob Johnston 			goto out;
291508a0e8cSRob Johnston 		}
292508a0e8cSRob Johnston 	}
293508a0e8cSRob Johnston 
294aab83bb8SJosef 'Jeff' Sipek out:
2955cc5d5ceSToomas Soome 	if (drive_descr != 0)
29600d7a6fbSRob Johnston 		dm_free_descriptor(drive_descr);
297aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(fmri);
298602ca9eaScth 	if (label)
299602ca9eaScth 		topo_mod_strfree(mod, label);
300aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(asru);
301602ca9eaScth 	return (err);
302602ca9eaScth 
303602ca9eaScth error:	err = topo_mod_seterrno(mod, err);
304602ca9eaScth 	goto out;
305602ca9eaScth }
306602ca9eaScth 
307940d71d2Seschrock /*
308738c43b5SEric Schrock  * Trim leading and trailing whitespace from the string.
309940d71d2Seschrock  */
310738c43b5SEric Schrock static char *
disk_trim_whitespace(topo_mod_t * mod,const char * begin)311738c43b5SEric Schrock disk_trim_whitespace(topo_mod_t *mod, const char *begin)
312940d71d2Seschrock {
313940d71d2Seschrock 	const char *end;
314738c43b5SEric Schrock 	char *buf;
315940d71d2Seschrock 	size_t count;
316940d71d2Seschrock 
317940d71d2Seschrock 	if (begin == NULL)
318940d71d2Seschrock 		return (NULL);
319940d71d2Seschrock 
320940d71d2Seschrock 	end = begin + strlen(begin);
321940d71d2Seschrock 
322940d71d2Seschrock 	while (begin < end && isspace(*begin))
323940d71d2Seschrock 		begin++;
324940d71d2Seschrock 	while (begin < end && isspace(*(end - 1)))
325940d71d2Seschrock 		end--;
326940d71d2Seschrock 
327940d71d2Seschrock 	count = end - begin;
328940d71d2Seschrock 	if ((buf = topo_mod_alloc(mod, count + 1)) == NULL)
329940d71d2Seschrock 		return (NULL);
330940d71d2Seschrock 
331940d71d2Seschrock 	(void) strlcpy(buf, begin, count + 1);
332940d71d2Seschrock 
333738c43b5SEric Schrock 	return (buf);
334738c43b5SEric Schrock }
335738c43b5SEric Schrock 
33600d7a6fbSRob Johnston /*ARGSUSED*/
33700d7a6fbSRob Johnston static int
disk_temp_reading(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)33800d7a6fbSRob Johnston disk_temp_reading(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
33900d7a6fbSRob Johnston     nvlist_t *in, nvlist_t **out)
340738c43b5SEric Schrock {
34100d7a6fbSRob Johnston 	char *devid;
34200d7a6fbSRob Johnston 	uint32_t temp;
3435cc5d5ceSToomas Soome 	dm_descriptor_t drive_descr = 0;
34400d7a6fbSRob Johnston 	nvlist_t *drive_stats, *pargs, *nvl;
34500d7a6fbSRob Johnston 	int err;
34600d7a6fbSRob Johnston 
34700d7a6fbSRob Johnston 	if (vers > TOPO_METH_DISK_TEMP_VERSION)
34800d7a6fbSRob Johnston 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
34900d7a6fbSRob Johnston 
35000d7a6fbSRob Johnston 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &pargs) != 0 ||
35100d7a6fbSRob Johnston 	    nvlist_lookup_string(pargs, TOPO_IO_DEVID, &devid) != 0) {
35200d7a6fbSRob Johnston 		topo_mod_dprintf(mod, "Failed to lookup %s arg",
35300d7a6fbSRob Johnston 		    TOPO_IO_DEVID);
35400d7a6fbSRob Johnston 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
35500d7a6fbSRob Johnston 	}
35600d7a6fbSRob Johnston 
35700d7a6fbSRob Johnston 	if ((drive_descr = dm_get_descriptor_by_name(DM_DRIVE, devid,
3585cc5d5ceSToomas Soome 	    &err)) == 0) {
35900d7a6fbSRob Johnston 		topo_mod_dprintf(mod, "failed to get drive decriptor for %s",
36000d7a6fbSRob Johnston 		    devid);
36100d7a6fbSRob Johnston 		return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
36200d7a6fbSRob Johnston 	}
36300d7a6fbSRob Johnston 
36400d7a6fbSRob Johnston 	if ((drive_stats = dm_get_stats(drive_descr, DM_DRV_STAT_TEMPERATURE,
36500d7a6fbSRob Johnston 	    &err)) == NULL ||
36600d7a6fbSRob Johnston 	    nvlist_lookup_uint32(drive_stats, DM_TEMPERATURE, &temp) != 0) {
36700d7a6fbSRob Johnston 		topo_mod_dprintf(mod, "failed to read disk temp for %s",
36800d7a6fbSRob Johnston 		    devid);
36900d7a6fbSRob Johnston 		dm_free_descriptor(drive_descr);
37000d7a6fbSRob Johnston 		return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
37100d7a6fbSRob Johnston 	}
37200d7a6fbSRob Johnston 	dm_free_descriptor(drive_descr);
37300d7a6fbSRob Johnston 
37400d7a6fbSRob Johnston 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
37500d7a6fbSRob Johnston 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME,
37600d7a6fbSRob Johnston 	    TOPO_SENSOR_READING) != 0 ||
37700d7a6fbSRob Johnston 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_DOUBLE) !=
37800d7a6fbSRob Johnston 	    0 || nvlist_add_double(nvl, TOPO_PROP_VAL_VAL, (double)temp) != 0) {
37900d7a6fbSRob Johnston 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
38000d7a6fbSRob Johnston 		nvlist_free(nvl);
38100d7a6fbSRob Johnston 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
38200d7a6fbSRob Johnston 	}
38300d7a6fbSRob Johnston 	*out = nvl;
384738c43b5SEric Schrock 
38500d7a6fbSRob Johnston 	return (0);
38600d7a6fbSRob Johnston }
387738c43b5SEric Schrock 
38800d7a6fbSRob Johnston static int
disk_add_temp_sensor(topo_mod_t * mod,tnode_t * pnode,const char * devid)38900d7a6fbSRob Johnston disk_add_temp_sensor(topo_mod_t *mod, tnode_t *pnode, const char *devid)
39000d7a6fbSRob Johnston {
39100d7a6fbSRob Johnston 	tnode_t *fnode;
39200d7a6fbSRob Johnston 	topo_pgroup_info_t pgi;
39300d7a6fbSRob Johnston 	nvlist_t *arg_nvl = NULL;
39400d7a6fbSRob Johnston 	int err;
39500d7a6fbSRob Johnston 
39600d7a6fbSRob Johnston 	if ((fnode = topo_node_facbind(mod, pnode, "temp",
39700d7a6fbSRob Johnston 	    TOPO_FAC_TYPE_SENSOR)) == NULL) {
39800d7a6fbSRob Johnston 		topo_mod_dprintf(mod, "failed to bind facility node");
39900d7a6fbSRob Johnston 		/* errno set */
40000d7a6fbSRob Johnston 		return (-1);
40100d7a6fbSRob Johnston 	}
402738c43b5SEric Schrock 
40300d7a6fbSRob Johnston 	/*
40400d7a6fbSRob Johnston 	 * Set props:
40500d7a6fbSRob Johnston 	 * - facility/sensor-class
40600d7a6fbSRob Johnston 	 * - facility/sensor-type
40700d7a6fbSRob Johnston 	 * - facility/units
40800d7a6fbSRob Johnston 	 */
40900d7a6fbSRob Johnston 	pgi.tpi_name = TOPO_PGROUP_FACILITY;
41000d7a6fbSRob Johnston 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
41100d7a6fbSRob Johnston 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
41200d7a6fbSRob Johnston 	pgi.tpi_version = 1;
41300d7a6fbSRob Johnston 	if (topo_pgroup_create(fnode, &pgi, &err) != 0) {
41400d7a6fbSRob Johnston 		if (err != ETOPO_PROP_DEFD) {
41500d7a6fbSRob Johnston 			topo_mod_dprintf(mod,  "pgroups create failure (%s)\n",
41600d7a6fbSRob Johnston 			    topo_strerror(err));
41700d7a6fbSRob Johnston 			/* errno set */
41800d7a6fbSRob Johnston 			goto err;
41900d7a6fbSRob Johnston 		}
42000d7a6fbSRob Johnston 	}
42100d7a6fbSRob Johnston 	if (topo_prop_set_string(fnode, TOPO_PGROUP_FACILITY,
42200d7a6fbSRob Johnston 	    TOPO_SENSOR_CLASS, TOPO_PROP_IMMUTABLE,
42300d7a6fbSRob Johnston 	    TOPO_SENSOR_CLASS_THRESHOLD, &err) != 0 ||
42400d7a6fbSRob Johnston 	    topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
42500d7a6fbSRob Johnston 	    TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, TOPO_SENSOR_TYPE_TEMP,
42600d7a6fbSRob Johnston 	    &err) != 0 ||
42700d7a6fbSRob Johnston 	    topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
42800d7a6fbSRob Johnston 	    TOPO_SENSOR_UNITS, TOPO_PROP_IMMUTABLE,
42900d7a6fbSRob Johnston 	    TOPO_SENSOR_UNITS_DEGREES_C, &err) != 0) {
43000d7a6fbSRob Johnston 		topo_mod_dprintf(mod, "Failed to set props on facnode (%s)",
43100d7a6fbSRob Johnston 		    topo_strerror(err));
43200d7a6fbSRob Johnston 		/* errno set */
43300d7a6fbSRob Johnston 		goto err;
43400d7a6fbSRob Johnston 	}
435940d71d2Seschrock 
43600d7a6fbSRob Johnston 	/*
43700d7a6fbSRob Johnston 	 * Register a property method for facility/reading
43800d7a6fbSRob Johnston 	 */
43900d7a6fbSRob Johnston 	if (topo_method_register(mod, fnode, disk_fac_methods) < 0) {
44000d7a6fbSRob Johnston 		topo_mod_dprintf(mod, "failed to register facility methods");
44100d7a6fbSRob Johnston 		goto err;
44200d7a6fbSRob Johnston 	}
44300d7a6fbSRob Johnston 	if (topo_mod_nvalloc(mod, &arg_nvl, NV_UNIQUE_NAME) < 0 ||
44400d7a6fbSRob Johnston 	    nvlist_add_string(arg_nvl, TOPO_IO_DEVID, devid) != 0) {
44500d7a6fbSRob Johnston 		topo_mod_dprintf(mod, "Failed build arg nvlist\n");
44600d7a6fbSRob Johnston 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
44700d7a6fbSRob Johnston 		goto err;
44800d7a6fbSRob Johnston 	}
44900d7a6fbSRob Johnston 	if (topo_prop_method_register(fnode, TOPO_PGROUP_FACILITY,
45000d7a6fbSRob Johnston 	    TOPO_SENSOR_READING, TOPO_TYPE_DOUBLE, "disk_temp_reading",
45100d7a6fbSRob Johnston 	    arg_nvl, &err) != 0) {
45200d7a6fbSRob Johnston 		topo_mod_dprintf(mod, "Failed to register %s propmeth "
45300d7a6fbSRob Johnston 		    "on fac node %s (%s)\n", TOPO_SENSOR_READING,
45400d7a6fbSRob Johnston 		    topo_node_name(fnode), topo_strerror(err));
45500d7a6fbSRob Johnston 		/* errno set */
45600d7a6fbSRob Johnston 		goto err;
45700d7a6fbSRob Johnston 	}
45800d7a6fbSRob Johnston 	nvlist_free(arg_nvl);
45900d7a6fbSRob Johnston 	return (0);
46000d7a6fbSRob Johnston err:
46100d7a6fbSRob Johnston 	topo_node_unbind(fnode);
46200d7a6fbSRob Johnston 	nvlist_free(arg_nvl);
46300d7a6fbSRob Johnston 	return (-1);
464940d71d2Seschrock }
465940d71d2Seschrock 
466602ca9eaScth /* create the disk topo node */
46744ed9dbbSStephen Hanson static int
disk_tnode_create(topo_mod_t * mod,tnode_t * parent,dev_di_node_t * dnode,const char * name,topo_instance_t i,tnode_t ** rval)468602ca9eaScth disk_tnode_create(topo_mod_t *mod, tnode_t *parent,
469ac88567aSHyon Kim     dev_di_node_t *dnode, const char *name, topo_instance_t i, tnode_t **rval)
470602ca9eaScth {
471602ca9eaScth 	int		len;
472602ca9eaScth 	nvlist_t	*fmri;
473602ca9eaScth 	tnode_t		*dtn;
474602ca9eaScth 	char		*part = NULL;
475602ca9eaScth 	nvlist_t	*auth;
476940d71d2Seschrock 	char		*mfg, *model, *firm, *serial;
477940d71d2Seschrock 
47844ed9dbbSStephen Hanson 	*rval = NULL;
47944ed9dbbSStephen Hanson 	if (dnode != NULL) {
48000d7a6fbSRob Johnston 		mfg = topo_mod_clean_str(mod, dnode->ddn_mfg);
48100d7a6fbSRob Johnston 		model = topo_mod_clean_str(mod, dnode->ddn_model);
48200d7a6fbSRob Johnston 		firm = topo_mod_clean_str(mod, dnode->ddn_firm);
48300d7a6fbSRob Johnston 		serial = topo_mod_clean_str(mod, dnode->ddn_serial);
48444ed9dbbSStephen Hanson 	} else {
48544ed9dbbSStephen Hanson 		mfg = model = firm = serial = NULL;
48644ed9dbbSStephen Hanson 	}
487602ca9eaScth 
488602ca9eaScth 	/* form 'part=' of fmri as "<mfg>-<model>" */
489940d71d2Seschrock 	if (mfg != NULL && model != NULL) {
490940d71d2Seschrock 		len = strlen(mfg) + 1 + strlen(model) + 1;
491602ca9eaScth 		if ((part = topo_mod_alloc(mod, len)) != NULL)
492602ca9eaScth 			(void) snprintf(part, len, "%s-%s",
493940d71d2Seschrock 			    mfg, model);
494602ca9eaScth 	}
495602ca9eaScth 
496602ca9eaScth 	auth = topo_mod_auth(mod, parent);
497602ca9eaScth 	fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i, NULL,
498940d71d2Seschrock 	    auth, part ? part : model, firm, serial);
499602ca9eaScth 	nvlist_free(auth);
500602ca9eaScth 
501940d71d2Seschrock 	topo_mod_strfree(mod, part);
502940d71d2Seschrock 	topo_mod_strfree(mod, mfg);
503940d71d2Seschrock 	topo_mod_strfree(mod, model);
504940d71d2Seschrock 	topo_mod_strfree(mod, firm);
505940d71d2Seschrock 	topo_mod_strfree(mod, serial);
506602ca9eaScth 
507602ca9eaScth 	if (fmri == NULL) {
508602ca9eaScth 		topo_mod_dprintf(mod, "disk_tnode_create: "
5096597d6fcSRobert Mustacchi 		    "hcfmri (%s%" PRIu64 "/%s%" PRIu64 ") error %s\n",
510602ca9eaScth 		    topo_node_name(parent), topo_node_instance(parent),
511602ca9eaScth 		    name, i, topo_strerror(topo_mod_errno(mod)));
51244ed9dbbSStephen Hanson 		return (-1);
513602ca9eaScth 	}
514602ca9eaScth 
515602ca9eaScth 	if ((dtn = topo_node_bind(mod, parent, name, i, fmri)) == NULL) {
51644ed9dbbSStephen Hanson 		if (topo_mod_errno(mod) == EMOD_NODE_BOUND) {
51744ed9dbbSStephen Hanson 			/*
51844ed9dbbSStephen Hanson 			 * if disk 0 is already there then we're done
51944ed9dbbSStephen Hanson 			 */
52044ed9dbbSStephen Hanson 			nvlist_free(fmri);
52144ed9dbbSStephen Hanson 			return (0);
52244ed9dbbSStephen Hanson 		}
523602ca9eaScth 		topo_mod_dprintf(mod, "disk_tnode_create: "
5246597d6fcSRobert Mustacchi 		    "bind (%s%" PRIu64 "/%s%" PRIu64 ") error %s\n",
525602ca9eaScth 		    topo_node_name(parent), topo_node_instance(parent),
526602ca9eaScth 		    name, i, topo_strerror(topo_mod_errno(mod)));
527602ca9eaScth 		nvlist_free(fmri);
52844ed9dbbSStephen Hanson 		return (-1);
529602ca9eaScth 	}
530602ca9eaScth 	nvlist_free(fmri);
531602ca9eaScth 
532602ca9eaScth 	/* add the properties of the disk */
533602ca9eaScth 	if (disk_set_props(mod, parent, dtn, dnode) != 0) {
534602ca9eaScth 		topo_mod_dprintf(mod, "disk_tnode_create: "
5356597d6fcSRobert Mustacchi 		    "disk_set_props (%s%" PRIu64 "/%s%" PRIu64 ") error %s\n",
536602ca9eaScth 		    topo_node_name(parent), topo_node_instance(parent),
537602ca9eaScth 		    name, i, topo_strerror(topo_mod_errno(mod)));
538602ca9eaScth 		topo_node_unbind(dtn);
53944ed9dbbSStephen Hanson 		return (-1);
540602ca9eaScth 	}
54100d7a6fbSRob Johnston 
542db8547feSRob Johnston 	if (dnode != NULL && dnode->ddn_devid != NULL &&
54300d7a6fbSRob Johnston 	    disk_add_temp_sensor(mod, dtn, dnode->ddn_devid) != 0) {
54400d7a6fbSRob Johnston 		topo_mod_dprintf(mod, "disk_tnode_create: failed to create "
5456597d6fcSRobert Mustacchi 		    "temperature sensor node on bay=%" PRIu64 "/disk=0",
54600d7a6fbSRob Johnston 		    topo_node_instance(parent));
54700d7a6fbSRob Johnston 	}
54844ed9dbbSStephen Hanson 	*rval = dtn;
54944ed9dbbSStephen Hanson 	return (0);
550602ca9eaScth }
551602ca9eaScth 
552602ca9eaScth static int
disk_declare(topo_mod_t * mod,tnode_t * parent,dev_di_node_t * dnode,tnode_t ** childp)553ac88567aSHyon Kim disk_declare(topo_mod_t *mod, tnode_t *parent, dev_di_node_t *dnode,
55444ed9dbbSStephen Hanson     tnode_t **childp)
555602ca9eaScth {
55644ed9dbbSStephen Hanson 	tnode_t		*dtn = NULL;
55744ed9dbbSStephen Hanson 	int		rval;
558602ca9eaScth 
55944ed9dbbSStephen Hanson 	rval = disk_tnode_create(mod, parent, dnode, DISK, 0, &dtn);
560602ca9eaScth 	if (dtn == NULL) {
56144ed9dbbSStephen Hanson 		if (rval == 0)
56244ed9dbbSStephen Hanson 			return (0);
563602ca9eaScth 		topo_mod_dprintf(mod, "disk_declare: "
564602ca9eaScth 		    "disk_tnode_create error %s\n",
565602ca9eaScth 		    topo_strerror(topo_mod_errno(mod)));
566602ca9eaScth 		return (-1);
567602ca9eaScth 	}
568602ca9eaScth 
569602ca9eaScth 	/* register disk_methods against the disk topo node */
570602ca9eaScth 	if (topo_method_register(mod, dtn, disk_methods) != 0) {
571602ca9eaScth 		topo_mod_dprintf(mod, "disk_declare: "
572602ca9eaScth 		    "topo_method_register error %s\n",
573602ca9eaScth 		    topo_strerror(topo_mod_errno(mod)));
574602ca9eaScth 		topo_node_unbind(dtn);
575602ca9eaScth 		return (-1);
576602ca9eaScth 	}
57744ed9dbbSStephen Hanson 	if (childp != NULL)
57844ed9dbbSStephen Hanson 		*childp = dtn;
579602ca9eaScth 	return (0);
580602ca9eaScth }
581602ca9eaScth 
582602ca9eaScth int
disk_declare_path(topo_mod_t * mod,tnode_t * parent,topo_list_t * listp,const char * path)583602ca9eaScth disk_declare_path(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
584602ca9eaScth     const char *path)
585602ca9eaScth {
586ac88567aSHyon Kim 	dev_di_node_t		*dnode;
587602ca9eaScth 	int i;
588602ca9eaScth 
589602ca9eaScth 	/*
590602ca9eaScth 	 * Check for match using physical phci (ddn_ppath). Use
591602ca9eaScth 	 * di_devfs_path_match so generic.vs.non-generic names match.
592602ca9eaScth 	 */
593602ca9eaScth 	for (dnode = topo_list_next(listp); dnode != NULL;
594602ca9eaScth 	    dnode = topo_list_next(dnode)) {
595602ca9eaScth 		if (dnode->ddn_ppath == NULL)
596602ca9eaScth 			continue;
597602ca9eaScth 
598602ca9eaScth 		for (i = 0; i < dnode->ddn_ppath_count; i++) {
599602ca9eaScth 			if (di_devfs_path_match(dnode->ddn_ppath[0], path))
60044ed9dbbSStephen Hanson 				return (disk_declare(mod, parent, dnode, NULL));
601602ca9eaScth 		}
602602ca9eaScth 	}
603602ca9eaScth 
604602ca9eaScth 	topo_mod_dprintf(mod, "disk_declare_path: "
605602ca9eaScth 	    "failed to find disk matching path %s", path);
606602ca9eaScth 	return (0);
607602ca9eaScth }
608602ca9eaScth 
609602ca9eaScth int
disk_declare_addr(topo_mod_t * mod,tnode_t * parent,topo_list_t * listp,const char * addr,tnode_t ** childp)610602ca9eaScth disk_declare_addr(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
61144ed9dbbSStephen Hanson     const char *addr, tnode_t **childp)
612602ca9eaScth {
613ac88567aSHyon Kim 	dev_di_node_t *dnode;
614602ca9eaScth 	int i;
615602ca9eaScth 
616602ca9eaScth 	/* Check for match using addr. */
617602ca9eaScth 	for (dnode = topo_list_next(listp); dnode != NULL;
618602ca9eaScth 	    dnode = topo_list_next(dnode)) {
619602ca9eaScth 		if (dnode->ddn_target_port == NULL)
620602ca9eaScth 			continue;
621602ca9eaScth 
622b4879163SHyon Kim 		for (i = 0; i < dnode->ddn_ppath_count; i++) {
623b4879163SHyon Kim 			if ((dnode->ddn_target_port[i] != NULL) &&
624b4879163SHyon Kim 			    (strncmp(dnode->ddn_target_port[i], addr,
625b4879163SHyon Kim 			    strcspn(dnode->ddn_target_port[i], ":"))) == 0) {
62648ea5fa8SStephen Hanson 				topo_mod_dprintf(mod, "disk_declare_addr: "
62748ea5fa8SStephen Hanson 				    "found disk matching addr %s", addr);
62844ed9dbbSStephen Hanson 				return (disk_declare(mod, parent, dnode,
62944ed9dbbSStephen Hanson 				    childp));
63048ea5fa8SStephen Hanson 			}
631602ca9eaScth 		}
632602ca9eaScth 	}
633602ca9eaScth 
634602ca9eaScth 	topo_mod_dprintf(mod, "disk_declare_addr: "
635602ca9eaScth 	    "failed to find disk matching addr %s", addr);
63644ed9dbbSStephen Hanson 
63744ed9dbbSStephen Hanson 	return (1);
63844ed9dbbSStephen Hanson }
63944ed9dbbSStephen Hanson 
6408221efecSRobert Mustacchi /*
6418221efecSRobert Mustacchi  * Try to find a disk based on the bridge-port property. This is most often used
6428221efecSRobert Mustacchi  * for SATA devices which are attached to a SAS controller and are therefore
6438221efecSRobert Mustacchi  * behind a SATL bridge port. SES only knows of devices based on this SAS WWN,
6448221efecSRobert Mustacchi  * not based on any SATA GUIDs.
6458221efecSRobert Mustacchi  */
6468221efecSRobert Mustacchi int
disk_declare_bridge(topo_mod_t * mod,tnode_t * parent,topo_list_t * listp,const char * addr,tnode_t ** childp)6478221efecSRobert Mustacchi disk_declare_bridge(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
6488221efecSRobert Mustacchi     const char *addr, tnode_t **childp)
6498221efecSRobert Mustacchi {
6508221efecSRobert Mustacchi 	dev_di_node_t *dnode;
6518221efecSRobert Mustacchi 	int i;
6528221efecSRobert Mustacchi 
6538221efecSRobert Mustacchi 	/* Check for match using addr. */
6548221efecSRobert Mustacchi 	for (dnode = topo_list_next(listp); dnode != NULL;
6558221efecSRobert Mustacchi 	    dnode = topo_list_next(dnode)) {
6568221efecSRobert Mustacchi 		if (dnode->ddn_bridge_port == NULL)
6578221efecSRobert Mustacchi 			continue;
6588221efecSRobert Mustacchi 
6598221efecSRobert Mustacchi 		for (i = 0; i < dnode->ddn_ppath_count; i++) {
6608221efecSRobert Mustacchi 			if ((dnode->ddn_bridge_port[i] != NULL) &&
6618221efecSRobert Mustacchi 			    (strncmp(dnode->ddn_bridge_port[i], addr,
6628221efecSRobert Mustacchi 			    strcspn(dnode->ddn_bridge_port[i], ":"))) == 0) {
6638221efecSRobert Mustacchi 				topo_mod_dprintf(mod, "disk_declare_bridge: "
6648221efecSRobert Mustacchi 				    "found disk matching bridge %s", addr);
6658221efecSRobert Mustacchi 				return (disk_declare(mod, parent, dnode,
6668221efecSRobert Mustacchi 				    childp));
6678221efecSRobert Mustacchi 			}
6688221efecSRobert Mustacchi 		}
6698221efecSRobert Mustacchi 	}
6708221efecSRobert Mustacchi 
6718221efecSRobert Mustacchi 	topo_mod_dprintf(mod, "disk_declare_bridge: "
6728221efecSRobert Mustacchi 	    "failed to find disk matching bridge %s", addr);
6738221efecSRobert Mustacchi 
6748221efecSRobert Mustacchi 	return (1);
6758221efecSRobert Mustacchi }
6768221efecSRobert Mustacchi 
67744ed9dbbSStephen Hanson /*
67844ed9dbbSStephen Hanson  * Used to declare a disk that has been discovered through other means (usually
67944ed9dbbSStephen Hanson  * ses), that is not enumerated in the devinfo tree.
68044ed9dbbSStephen Hanson  */
68144ed9dbbSStephen Hanson int
disk_declare_non_enumerated(topo_mod_t * mod,tnode_t * parent,tnode_t ** childp)68244ed9dbbSStephen Hanson disk_declare_non_enumerated(topo_mod_t *mod, tnode_t *parent, tnode_t **childp)
68344ed9dbbSStephen Hanson {
68444ed9dbbSStephen Hanson 	return (disk_declare(mod, parent, NULL, childp));
685602ca9eaScth }
686602ca9eaScth 
687ac88567aSHyon Kim /* di_devlink callback for dev_di_node_add */
688602ca9eaScth static int
disk_devlink_callback(di_devlink_t dl,void * arg)689602ca9eaScth disk_devlink_callback(di_devlink_t dl, void *arg)
690602ca9eaScth {
691602ca9eaScth 	disk_cbdata_t	*cbp = (disk_cbdata_t *)arg;
692602ca9eaScth 	topo_mod_t	*mod = cbp->dcb_mod;
693ac88567aSHyon Kim 	dev_di_node_t	*dnode = cbp->dcb_dnode;
694602ca9eaScth 	const char	*devpath;
695602ca9eaScth 	char		*ctds, *slice;
696602ca9eaScth 
697602ca9eaScth 	devpath = di_devlink_path(dl);
698602ca9eaScth 	if ((dnode == NULL) || (devpath == NULL))
699602ca9eaScth 		return (DI_WALK_TERMINATE);
700602ca9eaScth 
701602ca9eaScth 	/* trim the slice off the public name */
702602ca9eaScth 	if (((ctds = strrchr(devpath, '/')) != NULL) &&
703602ca9eaScth 	    ((slice = strchr(ctds, 's')) != NULL))
704602ca9eaScth 		*slice = '\0';
705602ca9eaScth 
706602ca9eaScth 	/* Establish the public /dev name (no slice) */
707602ca9eaScth 	dnode->ddn_lpath = topo_mod_strdup(mod, ctds ? ctds + 1 : devpath);
708602ca9eaScth 
709602ca9eaScth 	if (ctds && slice)
710602ca9eaScth 		*slice = 's';
711602ca9eaScth 	return (DI_WALK_TERMINATE);
712602ca9eaScth }
713602ca9eaScth 
714602ca9eaScth static void
dev_di_node_free(topo_mod_t * mod,dev_di_node_t * dnode)715ac88567aSHyon Kim dev_di_node_free(topo_mod_t *mod, dev_di_node_t *dnode)
716602ca9eaScth {
717602ca9eaScth 	int i;
718602ca9eaScth 
719602ca9eaScth 	/* free the stuff we point to */
72044ed9dbbSStephen Hanson 	if (dnode->ddn_devid)
72144ed9dbbSStephen Hanson 		topo_mod_strfree(mod, dnode->ddn_devid);
722b4879163SHyon Kim 	for (i = 0; i < dnode->ddn_ppath_count; i++) {
723b4879163SHyon Kim 		/* topo_mod_strfree does NULL checking. */
724602ca9eaScth 		topo_mod_strfree(mod, dnode->ddn_ppath[i]);
725b4879163SHyon Kim 		topo_mod_strfree(mod, dnode->ddn_target_port[i]);
726b4879163SHyon Kim 		topo_mod_strfree(mod, dnode->ddn_attached_port[i]);
727b4879163SHyon Kim 		topo_mod_strfree(mod, dnode->ddn_bridge_port[i]);
728b4879163SHyon Kim 	}
729602ca9eaScth 	topo_mod_free(mod, dnode->ddn_ppath,
730b4879163SHyon Kim 	    dnode->ddn_ppath_count * sizeof (char *));
731b4879163SHyon Kim 	topo_mod_free(mod, dnode->ddn_target_port,
732b4879163SHyon Kim 	    dnode->ddn_ppath_count * sizeof (char *));
733b4879163SHyon Kim 	topo_mod_free(mod, dnode->ddn_attached_port,
734b4879163SHyon Kim 	    dnode->ddn_ppath_count * sizeof (char *));
735b4879163SHyon Kim 	topo_mod_free(mod, dnode->ddn_bridge_port,
736b4879163SHyon Kim 	    dnode->ddn_ppath_count * sizeof (char *));
737602ca9eaScth 	topo_mod_strfree(mod, dnode->ddn_dpath);
738602ca9eaScth 	topo_mod_strfree(mod, dnode->ddn_lpath);
739602ca9eaScth 
740602ca9eaScth 	topo_mod_strfree(mod, dnode->ddn_mfg);
741602ca9eaScth 	topo_mod_strfree(mod, dnode->ddn_model);
742602ca9eaScth 	topo_mod_strfree(mod, dnode->ddn_serial);
743602ca9eaScth 	topo_mod_strfree(mod, dnode->ddn_firm);
744602ca9eaScth 	topo_mod_strfree(mod, dnode->ddn_cap);
745602ca9eaScth 
746602ca9eaScth 	/* free self */
747ac88567aSHyon Kim 	topo_mod_free(mod, dnode, sizeof (dev_di_node_t));
748602ca9eaScth }
749602ca9eaScth 
750602ca9eaScth static int
dev_di_node_add(di_node_t node,char * devid,disk_cbdata_t * cbp)751ac88567aSHyon Kim dev_di_node_add(di_node_t node, char *devid, disk_cbdata_t *cbp)
752602ca9eaScth {
753602ca9eaScth 	topo_mod_t	*mod = cbp->dcb_mod;
754ac88567aSHyon Kim 	dev_di_node_t	*dnode;
755602ca9eaScth 	di_path_t	pnode;
756602ca9eaScth 	char		*path;
757602ca9eaScth 	int		mlen;
758602ca9eaScth 	char		*minorpath;
759602ca9eaScth 	char		*extn = ":a";
760602ca9eaScth 	char		*s;
761602ca9eaScth 	int64_t		*nblocksp;
762602ca9eaScth 	uint64_t	nblocks;
7634a97b82eSRob Johnston 	int		*blksizep;
7644a97b82eSRob Johnston 	uint_t		blksize;
765602ca9eaScth 	char		lentry[MAXPATHLEN];
766b4879163SHyon Kim 	int		pathcount;
767ac88567aSHyon Kim 	int		*inq_dtype, itype;
76800d7a6fbSRob Johnston 	int		i;
769602ca9eaScth 
77044ed9dbbSStephen Hanson 	if (devid) {
77144ed9dbbSStephen Hanson 		/*
77244ed9dbbSStephen Hanson 		 * Check for list duplicate using devid search.
77344ed9dbbSStephen Hanson 		 * Note if there is no devid, then we can end up with duplicates
77444ed9dbbSStephen Hanson 		 * in the list, but this doesn't do any harm.
77544ed9dbbSStephen Hanson 		 */
77644ed9dbbSStephen Hanson 		for (dnode = topo_list_next(cbp->dcb_list);
77744ed9dbbSStephen Hanson 		    dnode != NULL; dnode = topo_list_next(dnode)) {
77844ed9dbbSStephen Hanson 			if (dnode->ddn_devid &&
77944ed9dbbSStephen Hanson 			    devid_str_compare(dnode->ddn_devid, devid) == 0) {
780ac88567aSHyon Kim 				topo_mod_dprintf(mod, "dev_di_node_add: "
78144ed9dbbSStephen Hanson 				    "already there %s\n", devid);
78244ed9dbbSStephen Hanson 				return (0);
78344ed9dbbSStephen Hanson 			}
784602ca9eaScth 		}
785602ca9eaScth 	}
786602ca9eaScth 
787ac88567aSHyon Kim 	if ((dnode = topo_mod_zalloc(mod, sizeof (dev_di_node_t))) == NULL)
788602ca9eaScth 		return (-1);
789602ca9eaScth 
79044ed9dbbSStephen Hanson 	if (devid) {
79144ed9dbbSStephen Hanson 		/* Establish the devid. */
79244ed9dbbSStephen Hanson 		dnode->ddn_devid = topo_mod_strdup(mod, devid);
79344ed9dbbSStephen Hanson 		if (dnode->ddn_devid == NULL)
79444ed9dbbSStephen Hanson 			goto error;
79544ed9dbbSStephen Hanson 	}
796602ca9eaScth 
797602ca9eaScth 	/* Establish the devinfo dpath */
798602ca9eaScth 	if ((path = di_devfs_path(node)) == NULL) {
799888e0559SRobert Johnston 		(void) topo_mod_seterrno(mod, errno);
800602ca9eaScth 		goto error;
801602ca9eaScth 	}
802602ca9eaScth 
803602ca9eaScth 	dnode->ddn_dpath = topo_mod_strdup(mod, path);
804602ca9eaScth 	di_devfs_path_free(path);
805602ca9eaScth 	if (dnode->ddn_dpath == NULL)
806602ca9eaScth 		goto error;
807602ca9eaScth 
808602ca9eaScth 	/*
809602ca9eaScth 	 * Establish the physical ppath and target ports. If the device is
810602ca9eaScth 	 * non-mpxio then dpath and ppath are the same, and the target port is a
811602ca9eaScth 	 * property of the device node.
812602ca9eaScth 	 *
813602ca9eaScth 	 * If dpath is a client node under scsi_vhci, then iterate over all
814602ca9eaScth 	 * paths and get their physical paths and target port properrties.
815602ca9eaScth 	 * di_path_client_next_path call below will
816602ca9eaScth 	 * return non-NULL, and ppath is set to the physical path to the first
817602ca9eaScth 	 * pathinfo node.
818602ca9eaScth 	 *
819602ca9eaScth 	 * NOTE: It is possible to get a generic.vs.non-generic path
820602ca9eaScth 	 * for di_devfs_path.vs.di_path_devfs_path like:
821602ca9eaScth 	 *    xml: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/sd@2,0
822602ca9eaScth 	 *  pnode: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/disk@2,0
823602ca9eaScth 	 * To resolve this issue disk_declare_path() needs to use the
824602ca9eaScth 	 * special di_devfs_path_match() interface.
825602ca9eaScth 	 */
826b4879163SHyon Kim 	pathcount = 0;
827602ca9eaScth 	pnode = NULL;
828602ca9eaScth 	while ((pnode = di_path_client_next_path(node, pnode)) != NULL) {
829602ca9eaScth 		pathcount++;
830602ca9eaScth 	}
831602ca9eaScth 
832602ca9eaScth 	if (pathcount == 0) {
833602ca9eaScth 		if ((dnode->ddn_ppath =
834b4879163SHyon Kim 		    topo_mod_zalloc(mod, sizeof (char *))) == NULL)
835602ca9eaScth 			goto error;
836602ca9eaScth 
837602ca9eaScth 		dnode->ddn_ppath_count = 1;
838602ca9eaScth 		if ((dnode->ddn_ppath[0] = topo_mod_strdup(mod,
839602ca9eaScth 		    dnode->ddn_dpath)) == NULL)
840602ca9eaScth 			goto error;
841602ca9eaScth 
842b4879163SHyon Kim 		if ((dnode->ddn_target_port = topo_mod_zalloc(mod,
843b4879163SHyon Kim 		    sizeof (char *))) == NULL)
844b4879163SHyon Kim 			goto error;
845b4879163SHyon Kim 
846b4879163SHyon Kim 		if ((dnode->ddn_attached_port = topo_mod_zalloc(mod,
847b4879163SHyon Kim 		    sizeof (char *))) == NULL)
848b4879163SHyon Kim 			goto error;
849b4879163SHyon Kim 
850b4879163SHyon Kim 		if ((dnode->ddn_bridge_port = topo_mod_zalloc(mod,
851b4879163SHyon Kim 		    sizeof (char *))) == NULL)
852b4879163SHyon Kim 			goto error;
853b4879163SHyon Kim 
854b4879163SHyon Kim 		/* There should be only one target port for a devinfo node. */
855b4879163SHyon Kim 		if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
856b4879163SHyon Kim 		    SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) {
857b4879163SHyon Kim 			if ((dnode->ddn_target_port[0] =
858b4879163SHyon Kim 			    topo_mod_strdup(mod,
859b4879163SHyon Kim 			    scsi_wwnstr_skip_ua_prefix(s))) ==
860b4879163SHyon Kim 			    NULL)
861602ca9eaScth 				goto error;
862b4879163SHyon Kim 		}
863602ca9eaScth 
864b4879163SHyon Kim 		if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
865b4879163SHyon Kim 		    SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) {
866b4879163SHyon Kim 			/* There should be one attached port if any. */
867b4879163SHyon Kim 			if ((dnode->ddn_attached_port[0] =
868b4879163SHyon Kim 			    topo_mod_strdup(mod,
869b4879163SHyon Kim 			    scsi_wwnstr_skip_ua_prefix(s))) ==
870b4879163SHyon Kim 			    NULL)
871b4879163SHyon Kim 				goto error;
872b4879163SHyon Kim 		}
873602ca9eaScth 
874b4879163SHyon Kim 		if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
875b4879163SHyon Kim 		    SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) {
876b4879163SHyon Kim 			/* There should be one bridge port if any. */
877b4879163SHyon Kim 			if ((dnode->ddn_bridge_port[0] =
878b4879163SHyon Kim 			    topo_mod_strdup(mod,
879b4879163SHyon Kim 			    scsi_wwnstr_skip_ua_prefix(s))) ==
880b4879163SHyon Kim 			    NULL)
881b4879163SHyon Kim 				goto error;
882602ca9eaScth 		}
883b4879163SHyon Kim 
884602ca9eaScth 	} else {
885b4879163SHyon Kim 		/* processing a scsi_vhci device. */
886602ca9eaScth 		if ((dnode->ddn_ppath = topo_mod_zalloc(mod,
887b4879163SHyon Kim 		    pathcount * sizeof (char *))) == NULL)
888602ca9eaScth 			goto error;
889602ca9eaScth 
890602ca9eaScth 		dnode->ddn_ppath_count = pathcount;
891602ca9eaScth 
892b4879163SHyon Kim 		if ((dnode->ddn_target_port = topo_mod_zalloc(mod,
893b4879163SHyon Kim 		    pathcount * sizeof (char *))) == NULL)
894b4879163SHyon Kim 			goto error;
895b4879163SHyon Kim 
896b4879163SHyon Kim 		if ((dnode->ddn_attached_port = topo_mod_zalloc(mod,
897b4879163SHyon Kim 		    pathcount * sizeof (char *))) == NULL)
898602ca9eaScth 			goto error;
899602ca9eaScth 
900b4879163SHyon Kim 		if ((dnode->ddn_bridge_port = topo_mod_zalloc(mod,
901b4879163SHyon Kim 		    pathcount * sizeof (char *))) == NULL)
902b4879163SHyon Kim 			goto error;
903602ca9eaScth 
904602ca9eaScth 		pnode = NULL;
905b4879163SHyon Kim 		pathcount = 0;
906602ca9eaScth 		while ((pnode = di_path_client_next_path(node,
907602ca9eaScth 		    pnode)) != NULL) {
908602ca9eaScth 			if ((path = di_path_devfs_path(pnode)) == NULL) {
909888e0559SRobert Johnston 				(void) topo_mod_seterrno(mod, errno);
910602ca9eaScth 				goto error;
911602ca9eaScth 			}
912602ca9eaScth 
913602ca9eaScth 			dnode->ddn_ppath[pathcount] =
914602ca9eaScth 			    topo_mod_strdup(mod, path);
915602ca9eaScth 			di_devfs_path_free(path);
916602ca9eaScth 			if (dnode->ddn_ppath[pathcount] == NULL)
917602ca9eaScth 				goto error;
918602ca9eaScth 
919b4879163SHyon Kim 			if ((di_path_prop_lookup_strings(pnode,
920b4879163SHyon Kim 			    SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) {
921b4879163SHyon Kim 				if ((dnode->ddn_target_port[pathcount] =
922b4879163SHyon Kim 				    topo_mod_strdup(mod,
923b4879163SHyon Kim 				    scsi_wwnstr_skip_ua_prefix(s))) ==
924b4879163SHyon Kim 				    NULL)
925b4879163SHyon Kim 					goto error;
926b4879163SHyon Kim 			}
927b4879163SHyon Kim 
928b4879163SHyon Kim 			if ((di_path_prop_lookup_strings(pnode,
929b4879163SHyon Kim 			    SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) {
930b4879163SHyon Kim 				if ((dnode->ddn_attached_port[pathcount] =
931b4879163SHyon Kim 				    topo_mod_strdup(mod,
932b4879163SHyon Kim 				    scsi_wwnstr_skip_ua_prefix(s))) ==
933b4879163SHyon Kim 				    NULL)
934b4879163SHyon Kim 					goto error;
935b4879163SHyon Kim 			}
936b4879163SHyon Kim 
937b4879163SHyon Kim 			if ((di_path_prop_lookup_strings(pnode,
938b4879163SHyon Kim 			    SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) {
939b4879163SHyon Kim 				if ((dnode->ddn_bridge_port[pathcount] =
940b4879163SHyon Kim 				    topo_mod_strdup(mod,
941b4879163SHyon Kim 				    scsi_wwnstr_skip_ua_prefix(s))) ==
942b4879163SHyon Kim 				    NULL)
943b4879163SHyon Kim 					goto error;
944602ca9eaScth 			}
945602ca9eaScth 
946602ca9eaScth 			pathcount++;
947602ca9eaScth 		}
948602ca9eaScth 	}
949602ca9eaScth 
950602ca9eaScth 	/*
951ac88567aSHyon Kim 	 * Find the public /dev name for a disk by adding a minor name and using
952602ca9eaScth 	 * di_devlink interface for reverse translation (use devinfo path).
953602ca9eaScth 	 */
954ac88567aSHyon Kim 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
955ac88567aSHyon Kim 	    &inq_dtype) > 0) {
956b4879163SHyon Kim 		dnode->ddn_dtype = *inq_dtype;
957ac88567aSHyon Kim 		itype = (*inq_dtype) & DTYPE_MASK;
958ac88567aSHyon Kim 		if (itype == DTYPE_DIRECT) {
959ac88567aSHyon Kim 			mlen = strlen(dnode->ddn_dpath) + strlen(extn) + 1;
960ac88567aSHyon Kim 			if ((minorpath = topo_mod_alloc(mod, mlen)) == NULL)
961ac88567aSHyon Kim 				goto error;
962ac88567aSHyon Kim 			(void) snprintf(minorpath, mlen, "%s%s",
963ac88567aSHyon Kim 			    dnode->ddn_dpath, extn);
964ac88567aSHyon Kim 			cbp->dcb_dnode = dnode;
965ac88567aSHyon Kim 			(void) di_devlink_walk(cbp->dcb_devhdl, "^dsk/",
966ac88567aSHyon Kim 			    minorpath, DI_PRIMARY_LINK, cbp,
967ac88567aSHyon Kim 			    disk_devlink_callback);
968ac88567aSHyon Kim 			topo_mod_free(mod, minorpath, mlen);
969ac88567aSHyon Kim 			if (dnode->ddn_lpath == NULL) {
970ac88567aSHyon Kim 				topo_mod_dprintf(mod, "dev_di_node_add: "
971ac88567aSHyon Kim 				    "failed to determine logical path");
972ac88567aSHyon Kim 			}
973ac88567aSHyon Kim 		}
974b4879163SHyon Kim 	} else {
975b4879163SHyon Kim 		dnode->ddn_dtype = DTYPE_UNKNOWN;
976602ca9eaScth 	}
977602ca9eaScth 
978ac88567aSHyon Kim 	/* cache various bits of optional information about the device. */
979602ca9eaScth 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
980602ca9eaScth 	    INQUIRY_VENDOR_ID, &s) > 0) {
981738c43b5SEric Schrock 		if ((dnode->ddn_mfg = disk_trim_whitespace(mod, s)) == NULL)
982602ca9eaScth 			goto error;
983602ca9eaScth 	}
984602ca9eaScth 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
985602ca9eaScth 	    INQUIRY_PRODUCT_ID, &s) > 0) {
986738c43b5SEric Schrock 		if ((dnode->ddn_model = disk_trim_whitespace(mod, s)) == NULL)
987602ca9eaScth 			goto error;
988602ca9eaScth 	}
989602ca9eaScth 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
990602ca9eaScth 	    INQUIRY_REVISION_ID, &s) > 0) {
991738c43b5SEric Schrock 		if ((dnode->ddn_firm = disk_trim_whitespace(mod, s)) == NULL)
992602ca9eaScth 			goto error;
993602ca9eaScth 	}
994602ca9eaScth 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
995602ca9eaScth 	    INQUIRY_SERIAL_NO, &s) > 0) {
996738c43b5SEric Schrock 		if ((dnode->ddn_serial = disk_trim_whitespace(mod, s)) == NULL)
997602ca9eaScth 			goto error;
998672fc84aSRobert Mustacchi 	} else {
999672fc84aSRobert Mustacchi 		/*
1000672fc84aSRobert Mustacchi 		 * Many USB disk devices don't emulate serial inquiry number
1001672fc84aSRobert Mustacchi 		 * because their serial number can be longer than the standard
1002672fc84aSRobert Mustacchi 		 * SCSI length. If we didn't get an inquiry serial number, fill
1003672fc84aSRobert Mustacchi 		 * one in this way.
1004672fc84aSRobert Mustacchi 		 */
1005672fc84aSRobert Mustacchi 		di_node_t parent;
1006672fc84aSRobert Mustacchi 
1007672fc84aSRobert Mustacchi 		if ((parent = di_parent_node(node)) != DI_NODE_NIL &&
1008672fc84aSRobert Mustacchi 		    di_prop_lookup_strings(DDI_DEV_T_ANY, parent,
1009672fc84aSRobert Mustacchi 		    "usb-serialno", &s) > 0) {
1010672fc84aSRobert Mustacchi 			if ((dnode->ddn_serial = disk_trim_whitespace(mod,
1011672fc84aSRobert Mustacchi 			    s)) == NULL) {
1012672fc84aSRobert Mustacchi 				goto error;
1013672fc84aSRobert Mustacchi 			}
1014672fc84aSRobert Mustacchi 		}
1015602ca9eaScth 	}
1016672fc84aSRobert Mustacchi 
1017602ca9eaScth 	if (di_prop_lookup_int64(DDI_DEV_T_ANY, node,
1018602ca9eaScth 	    "device-nblocks", &nblocksp) > 0) {
1019602ca9eaScth 		nblocks = (uint64_t)*nblocksp;
1020602ca9eaScth 		/*
1021602ca9eaScth 		 * To save kernel memory, the driver may not define
10224a97b82eSRob Johnston 		 * "device-blksize" when its value is default DEV_BSIZE.
1023602ca9eaScth 		 */
1024602ca9eaScth 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
10254a97b82eSRob Johnston 		    "device-blksize", &blksizep) > 0)
10264a97b82eSRob Johnston 			blksize = (uint_t)*blksizep;
1027602ca9eaScth 		else
10284a97b82eSRob Johnston 			blksize = DEV_BSIZE;		/* default value */
1029602ca9eaScth 		(void) snprintf(lentry, sizeof (lentry),
10304a97b82eSRob Johnston 		    "%" PRIu64, nblocks * blksize);
1031602ca9eaScth 		if ((dnode->ddn_cap = topo_mod_strdup(mod, lentry)) == NULL)
1032602ca9eaScth 			goto error;
1033602ca9eaScth 	}
1034602ca9eaScth 
1035ac88567aSHyon Kim 	topo_mod_dprintf(mod, "dev_di_node_add: "
103644ed9dbbSStephen Hanson 	    "adding %s\n", devid ? dnode->ddn_devid : "NULL devid");
1037602ca9eaScth 	topo_mod_dprintf(mod, "                  "
1038602ca9eaScth 	    "       %s\n", dnode->ddn_dpath);
1039602ca9eaScth 	for (i = 0; i < dnode->ddn_ppath_count; i++) {
1040602ca9eaScth 		topo_mod_dprintf(mod, "                  "
1041602ca9eaScth 		    "       %s\n", dnode->ddn_ppath[i]);
1042602ca9eaScth 	}
1043602ca9eaScth 	topo_list_append(cbp->dcb_list, dnode);
1044602ca9eaScth 	return (0);
1045602ca9eaScth 
1046602ca9eaScth error:
1047ac88567aSHyon Kim 	dev_di_node_free(mod, dnode);
1048602ca9eaScth 	return (-1);
1049602ca9eaScth }
1050602ca9eaScth 
1051602ca9eaScth /* di_walk_node callback for disk_list_gather */
1052602ca9eaScth static int
dev_walk_di_nodes(di_node_t node,void * arg)1053ac88567aSHyon Kim dev_walk_di_nodes(di_node_t node, void *arg)
1054602ca9eaScth {
1055738c43b5SEric Schrock 	char			*devidstr = NULL;
105644ed9dbbSStephen Hanson 	char			*s;
105748ea5fa8SStephen Hanson 	int			*val;
1058602ca9eaScth 
105944ed9dbbSStephen Hanson 	/*
106048ea5fa8SStephen Hanson 	 * If it's not a scsi_vhci client and doesn't have a target_port
106148ea5fa8SStephen Hanson 	 * property and doesn't have a target property then it's not a storage
106248ea5fa8SStephen Hanson 	 * device and we're not interested.
106344ed9dbbSStephen Hanson 	 */
106444ed9dbbSStephen Hanson 	if (di_path_client_next_path(node, NULL) == NULL &&
106544ed9dbbSStephen Hanson 	    di_prop_lookup_strings(DDI_DEV_T_ANY, node,
106648ea5fa8SStephen Hanson 	    SCSI_ADDR_PROP_TARGET_PORT, &s) <= 0 &&
106748ea5fa8SStephen Hanson 	    di_prop_lookup_ints(DDI_DEV_T_ANY, node,
106848ea5fa8SStephen Hanson 	    SCSI_ADDR_PROP_TARGET, &val) <= 0) {
1069602ca9eaScth 		return (DI_WALK_CONTINUE);
1070738c43b5SEric Schrock 	}
107144ed9dbbSStephen Hanson 	(void) di_prop_lookup_strings(DDI_DEV_T_ANY, node,
107244ed9dbbSStephen Hanson 	    DEVID_PROP_NAME, &devidstr);
1073602ca9eaScth 
1074602ca9eaScth 	/* create/find the devid scsi topology node */
1075ac88567aSHyon Kim 	(void) dev_di_node_add(node, devidstr, arg);
1076738c43b5SEric Schrock 
1077602ca9eaScth 	return (DI_WALK_CONTINUE);
1078602ca9eaScth }
1079602ca9eaScth 
1080602ca9eaScth int
dev_list_gather(topo_mod_t * mod,topo_list_t * listp)1081ac88567aSHyon Kim dev_list_gather(topo_mod_t *mod, topo_list_t *listp)
1082602ca9eaScth {
1083602ca9eaScth 	di_node_t		devtree;
1084602ca9eaScth 	di_devlink_handle_t	devhdl;
1085602ca9eaScth 	disk_cbdata_t		dcb;
1086602ca9eaScth 
1087738c43b5SEric Schrock 	if ((devtree = topo_mod_devinfo(mod)) == DI_NODE_NIL) {
1088602ca9eaScth 		topo_mod_dprintf(mod, "disk_list_gather: "
1089525b85dbSEric Schrock 		    "topo_mod_devinfo() failed");
1090602ca9eaScth 		return (-1);
1091602ca9eaScth 	}
1092602ca9eaScth 
1093602ca9eaScth 	if ((devhdl = di_devlink_init(NULL, 0)) == DI_NODE_NIL) {
1094602ca9eaScth 		topo_mod_dprintf(mod, "disk_list_gather: "
1095525b85dbSEric Schrock 		    "di_devlink_init() failed");
1096602ca9eaScth 		return (-1);
1097602ca9eaScth 	}
1098602ca9eaScth 
1099602ca9eaScth 	dcb.dcb_mod = mod;
1100602ca9eaScth 	dcb.dcb_list = listp;
1101602ca9eaScth 	dcb.dcb_devhdl = devhdl;
1102602ca9eaScth 
110344ed9dbbSStephen Hanson 	/* walk the devinfo snapshot looking for disk nodes */
1104602ca9eaScth 	(void) di_walk_node(devtree, DI_WALK_CLDFIRST, &dcb,
1105ac88567aSHyon Kim 	    dev_walk_di_nodes);
1106602ca9eaScth 
1107602ca9eaScth 	(void) di_devlink_fini(&devhdl);
1108602ca9eaScth 
1109602ca9eaScth 	return (0);
1110602ca9eaScth }
1111602ca9eaScth 
1112602ca9eaScth void
dev_list_free(topo_mod_t * mod,topo_list_t * listp)1113ac88567aSHyon Kim dev_list_free(topo_mod_t *mod, topo_list_t *listp)
1114602ca9eaScth {
1115ac88567aSHyon Kim 	dev_di_node_t	*dnode;
1116602ca9eaScth 
1117602ca9eaScth 	while ((dnode = topo_list_next(listp)) != NULL) {
1118602ca9eaScth 		/* order of delete/free is important */
1119602ca9eaScth 		topo_list_delete(listp, dnode);
1120ac88567aSHyon Kim 		dev_di_node_free(mod, dnode);
1121602ca9eaScth 	}
1122602ca9eaScth }
1123602ca9eaScth 
1124602ca9eaScth /*
1125602ca9eaScth  * Query the current disk status. If successful, the disk status is returned
1126602ca9eaScth  * as an nvlist consisting of at least the following members:
1127602ca9eaScth  *
1128602ca9eaScth  *	protocol	string		Supported protocol (currently "scsi")
1129602ca9eaScth  *
1130602ca9eaScth  *	status		nvlist		Arbitrary protocol-specific information
1131602ca9eaScth  *					about the current state of the disk.
1132602ca9eaScth  *
1133602ca9eaScth  *	faults		nvlist		A list of supported faults. Each
1134602ca9eaScth  *					element of this list is a boolean value.
1135602ca9eaScth  *					An element's existence indicates that
1136602ca9eaScth  *					the drive supports detecting this fault,
1137602ca9eaScth  *					and the value indicates the current
1138602ca9eaScth  *					state of the fault.
1139602ca9eaScth  *
1140602ca9eaScth  *	<fault-name>	nvlist		For each fault named in 'faults', a
1141602ca9eaScth  *					nvlist describing protocol-specific
1142602ca9eaScth  *					attributes of the fault.
1143602ca9eaScth  *
1144602ca9eaScth  * This method relies on the libdiskstatus library to query this information.
1145602ca9eaScth  */
1146602ca9eaScth static int
disk_status(topo_mod_t * mod,tnode_t * nodep,topo_version_t vers,nvlist_t * in_nvl,nvlist_t ** out_nvl)1147602ca9eaScth disk_status(topo_mod_t *mod, tnode_t *nodep, topo_version_t vers,
1148602ca9eaScth     nvlist_t *in_nvl, nvlist_t **out_nvl)
1149602ca9eaScth {
1150602ca9eaScth 	disk_status_t	*dsp;
1151602ca9eaScth 	char		*devpath, *fullpath;
1152602ca9eaScth 	size_t		pathlen;
1153602ca9eaScth 	nvlist_t	*status;
1154602ca9eaScth 	int		err;
1155602ca9eaScth 
1156602ca9eaScth 	*out_nvl = NULL;
1157602ca9eaScth 
1158602ca9eaScth 	if (vers != TOPO_METH_DISK_STATUS_VERSION)
1159602ca9eaScth 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
1160602ca9eaScth 
1161602ca9eaScth 	/*
1162602ca9eaScth 	 * If the caller specifies the "path" parameter, then this indicates
1163602ca9eaScth 	 * that we should use this instead of deriving it from the topo node
1164602ca9eaScth 	 * itself.
1165602ca9eaScth 	 */
1166602ca9eaScth 	if (nvlist_lookup_string(in_nvl, "path", &fullpath) == 0) {
1167602ca9eaScth 		devpath = NULL;
116858d4b16fSRobert Mustacchi 		pathlen = 0;
1169602ca9eaScth 	} else {
1170602ca9eaScth 		/*
1171602ca9eaScth 		 * Get the /devices path and attempt to open the disk status
1172602ca9eaScth 		 * handle.
1173602ca9eaScth 		 */
1174602ca9eaScth 		if (topo_prop_get_string(nodep, TOPO_PGROUP_IO,
1175602ca9eaScth 		    TOPO_IO_DEV_PATH, &devpath, &err) != 0)
1176602ca9eaScth 			return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
1177602ca9eaScth 
1178602ca9eaScth 		/*
1179602ca9eaScth 		 * Note that sizeof(string) includes the terminating NULL byte
1180602ca9eaScth 		 */
1181602ca9eaScth 		pathlen = strlen(devpath) + sizeof ("/devices") +
1182602ca9eaScth 		    sizeof (PHYS_EXTN) - 1;
1183602ca9eaScth 
1184602ca9eaScth 		if ((fullpath = topo_mod_alloc(mod, pathlen)) == NULL)
1185602ca9eaScth 			return (topo_mod_seterrno(mod, EMOD_NOMEM));
1186602ca9eaScth 
1187602ca9eaScth 		(void) snprintf(fullpath, pathlen, "/devices%s%s", devpath,
1188602ca9eaScth 		    PHYS_EXTN);
1189602ca9eaScth 
1190602ca9eaScth 		topo_mod_strfree(mod, devpath);
1191602ca9eaScth 	}
1192602ca9eaScth 
1193602ca9eaScth 	if ((dsp = disk_status_open(fullpath, &err)) == NULL) {
1194602ca9eaScth 		if (devpath)
1195602ca9eaScth 			topo_mod_free(mod, fullpath, pathlen);
1196602ca9eaScth 		return (topo_mod_seterrno(mod, err == EDS_NOMEM ?
1197602ca9eaScth 		    EMOD_NOMEM : EMOD_METHOD_NOTSUP));
1198602ca9eaScth 	}
1199602ca9eaScth 
1200602ca9eaScth 	if (devpath)
1201602ca9eaScth 		topo_mod_free(mod, fullpath, pathlen);
1202602ca9eaScth 
1203602ca9eaScth 	if ((status = disk_status_get(dsp)) == NULL) {
1204602ca9eaScth 		err = (disk_status_errno(dsp) == EDS_NOMEM ?
1205602ca9eaScth 		    EMOD_NOMEM : EMOD_METHOD_NOTSUP);
1206602ca9eaScth 		disk_status_close(dsp);
1207602ca9eaScth 		return (topo_mod_seterrno(mod, err));
1208602ca9eaScth 	}
1209602ca9eaScth 
1210602ca9eaScth 	*out_nvl = status;
1211602ca9eaScth 	disk_status_close(dsp);
1212602ca9eaScth 	return (0);
1213602ca9eaScth }
1214