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) 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright (c) 2019, Joyent, Inc.
25  */
26 
27 #include <sys/fm/protocol.h>
28 #include <strings.h>
29 #include <fm/topo_mod.h>
30 #include <fm/topo_method.h>
31 #include <sys/scsi/impl/inquiry.h>
32 #include <sys/scsi/impl/scsi_sas.h>
33 #include <sys/scsi/scsi_address.h>
34 #include <did_props.h>
35 
36 static const topo_pgroup_info_t storage_pgroup =
37 	{ TOPO_PGROUP_STORAGE, TOPO_STABILITY_PRIVATE,
38 	    TOPO_STABILITY_PRIVATE, 1 };
39 
40 static const topo_method_t recep_methods[] = {
41 	{ TOPO_METH_OCCUPIED, TOPO_METH_OCCUPIED_DESC,
42 	    TOPO_METH_OCCUPIED_VERSION, TOPO_STABILITY_INTERNAL,
43 	    topo_mod_hc_occupied },
44 	{ NULL }
45 };
46 
47 void
pci_di_prop_set(tnode_t * tn,di_node_t din,char * dpnm,char * tpnm)48 pci_di_prop_set(tnode_t *tn, di_node_t din, char *dpnm, char *tpnm)
49 {
50 	int err;
51 	char *tmpbuf;
52 
53 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, din, dpnm, &tmpbuf) == 1)
54 		(void) topo_prop_set_string(tn, TOPO_PGROUP_STORAGE, tpnm,
55 		    TOPO_PROP_IMMUTABLE, tmpbuf, &err);
56 }
57 
58 void
pci_pi_prop_set(tnode_t * tn,di_path_t din,char * dpnm,char * tpnm)59 pci_pi_prop_set(tnode_t *tn, di_path_t din, char *dpnm, char *tpnm)
60 {
61 	int err;
62 	char *tmpbuf;
63 
64 	if (di_path_prop_lookup_strings(din, dpnm, &tmpbuf) == 1)
65 		(void) topo_prop_set_string(tn, TOPO_PGROUP_STORAGE, tpnm,
66 		    TOPO_PROP_IMMUTABLE, tmpbuf, &err);
67 }
68 
69 static void
pci_scsi_device_create(topo_mod_t * mod,nvlist_t * auth,tnode_t * parent,di_node_t cn,int instance,di_path_t pi)70 pci_scsi_device_create(topo_mod_t *mod, nvlist_t *auth, tnode_t *parent,
71     di_node_t cn, int instance, di_path_t pi)
72 {
73 	tnode_t *child;
74 	nvlist_t *fmri;
75 	int e, *val;
76 	int64_t *val64;
77 
78 	fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, SCSI_DEVICE,
79 	    instance, NULL, auth, NULL, NULL, NULL);
80 	if (fmri == NULL)
81 		return;
82 	child = topo_node_bind(mod, parent, SCSI_DEVICE, instance, fmri);
83 	nvlist_free(fmri);
84 	if (child == NULL)
85 		return;
86 	if (topo_pgroup_create(child, &storage_pgroup, &e) < 0)
87 		return;
88 	if (pi != NULL) {
89 		pci_pi_prop_set(child, pi, SCSI_ADDR_PROP_TARGET_PORT,
90 		    TOPO_STORAGE_TARGET_PORT);
91 		pci_pi_prop_set(child, pi, SCSI_ADDR_PROP_ATTACHED_PORT,
92 		    TOPO_STORAGE_ATTACHED_PORT);
93 		pci_pi_prop_set(child, pi, SCSI_ADDR_PROP_TARGET_PORT_PM,
94 		    TOPO_STORAGE_TARGET_PORT_PM);
95 		pci_pi_prop_set(child, pi, SCSI_ADDR_PROP_ATTACHED_PORT_PM,
96 		    TOPO_STORAGE_ATTACHED_PORT_PM);
97 		if (di_path_prop_lookup_int64s(pi,
98 		    SCSI_ADDR_PROP_LUN64, &val64) == 1)
99 			(void) topo_prop_set_int64(child, TOPO_PGROUP_STORAGE,
100 			    TOPO_STORAGE_LUN64, TOPO_PROP_IMMUTABLE, *val64,
101 			    &e);
102 	} else {
103 		pci_di_prop_set(child, cn, SCSI_ADDR_PROP_TARGET_PORT,
104 		    TOPO_STORAGE_TARGET_PORT);
105 		pci_di_prop_set(child, cn, SCSI_ADDR_PROP_ATTACHED_PORT,
106 		    TOPO_STORAGE_ATTACHED_PORT);
107 		pci_di_prop_set(child, cn, SCSI_ADDR_PROP_TARGET_PORT_PM,
108 		    TOPO_STORAGE_TARGET_PORT_PM);
109 		pci_di_prop_set(child, cn, SCSI_ADDR_PROP_ATTACHED_PORT_PM,
110 		    TOPO_STORAGE_ATTACHED_PORT_PM);
111 		if (di_prop_lookup_int64(DDI_DEV_T_ANY, cn,
112 		    SCSI_ADDR_PROP_LUN64, &val64) == 1)
113 			(void) topo_prop_set_int64(child, TOPO_PGROUP_STORAGE,
114 			    TOPO_STORAGE_LUN64, TOPO_PROP_IMMUTABLE, *val64,
115 			    &e);
116 	}
117 	pci_di_prop_set(child, cn, DEVID_PROP_NAME, TOPO_STORAGE_DEVID);
118 	pci_di_prop_set(child, cn, INQUIRY_VENDOR_ID,
119 	    TOPO_STORAGE_MANUFACTURER);
120 	pci_di_prop_set(child, cn, INQUIRY_PRODUCT_ID, TOPO_STORAGE_MODEL);
121 	pci_di_prop_set(child, cn, INQUIRY_REVISION_ID,
122 	    TOPO_STORAGE_FIRMWARE_REV);
123 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, cn,
124 	    INQUIRY_DEVICE_TYPE, &val) == 1)
125 		(void) topo_prop_set_int32(child, TOPO_PGROUP_STORAGE,
126 		    TOPO_STORAGE_DEVICE_TYPE, TOPO_PROP_IMMUTABLE, *val, &e);
127 }
128 
129 static void
pci_smp_device_create(topo_mod_t * mod,nvlist_t * auth,tnode_t * parent,di_node_t cn,int instance)130 pci_smp_device_create(topo_mod_t *mod, nvlist_t *auth, tnode_t *parent,
131     di_node_t cn, int instance)
132 {
133 	tnode_t *child;
134 	nvlist_t *fmri;
135 	int e;
136 
137 	fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, SMP_DEVICE,
138 	    instance, NULL, auth, NULL, NULL, NULL);
139 	if (fmri == NULL)
140 		return;
141 	child = topo_node_bind(mod, parent, SMP_DEVICE, instance, fmri);
142 	nvlist_free(fmri);
143 	if (child == NULL)
144 		return;
145 	if (topo_pgroup_create(child, &storage_pgroup, &e) < 0)
146 		return;
147 	pci_di_prop_set(child, cn, SCSI_ADDR_PROP_TARGET_PORT,
148 	    TOPO_STORAGE_TARGET_PORT);
149 	pci_di_prop_set(child, cn, SCSI_ADDR_PROP_ATTACHED_PORT,
150 	    TOPO_STORAGE_ATTACHED_PORT);
151 	pci_di_prop_set(child, cn, SCSI_ADDR_PROP_TARGET_PORT_PM,
152 	    TOPO_STORAGE_TARGET_PORT_PM);
153 	pci_di_prop_set(child, cn, SCSI_ADDR_PROP_ATTACHED_PORT_PM,
154 	    TOPO_STORAGE_ATTACHED_PORT_PM);
155 	pci_di_prop_set(child, cn, DEVID_PROP_NAME, TOPO_STORAGE_DEVID);
156 	pci_di_prop_set(child, cn, INQUIRY_VENDOR_ID,
157 	    TOPO_STORAGE_MANUFACTURER);
158 	pci_di_prop_set(child, cn, INQUIRY_PRODUCT_ID, TOPO_STORAGE_MODEL);
159 	pci_di_prop_set(child, cn, INQUIRY_REVISION_ID,
160 	    TOPO_STORAGE_FIRMWARE_REV);
161 }
162 
163 static tnode_t *
pci_iport_device_create(topo_mod_t * mod,nvlist_t * auth,tnode_t * parent,di_node_t cn,int instance)164 pci_iport_device_create(topo_mod_t *mod, nvlist_t *auth, tnode_t *parent,
165     di_node_t cn, int instance)
166 {
167 	tnode_t *child;
168 	nvlist_t *fmri;
169 	int e;
170 
171 	fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, IPORT,
172 	    instance, NULL, auth, NULL, NULL, NULL);
173 	if (fmri == NULL)
174 		return (NULL);
175 	child = topo_node_bind(mod, parent, IPORT, instance, fmri);
176 	nvlist_free(fmri);
177 	if (child == NULL)
178 		return (NULL);
179 	if (topo_pgroup_create(child, &storage_pgroup, &e) < 0)
180 		return (child);
181 	pci_di_prop_set(child, cn, SCSI_ADDR_PROP_INITIATOR_PORT,
182 	    TOPO_STORAGE_INITIATOR_PORT);
183 	(void) topo_prop_set_string(child, TOPO_PGROUP_STORAGE,
184 	    TOPO_STORAGE_INITIATOR_PORT_PM, TOPO_PROP_IMMUTABLE,
185 	    di_bus_addr(cn), &e);
186 	return (child);
187 }
188 
189 void
pci_iports_instantiate(topo_mod_t * mod,tnode_t * parent,di_node_t pn,int niports)190 pci_iports_instantiate(topo_mod_t *mod, tnode_t *parent, di_node_t pn,
191     int niports)
192 {
193 	di_node_t cn, smp, sd;
194 	di_path_t pi;
195 	tnode_t *iport;
196 	int i, j;
197 	nvlist_t *auth;
198 
199 	if (topo_node_range_create(mod, parent, IPORT, 0, niports) < 0)
200 		return;
201 	auth = topo_mod_auth(mod, parent);
202 	for (i = 0, cn = di_child_node(pn); cn != DI_NODE_NIL;
203 	    cn = di_sibling_node(cn)) {
204 		/*
205 		 * First create any iport nodes.
206 		 */
207 		if (strcmp(di_node_name(cn), "iport") != 0)
208 			continue;
209 		iport = pci_iport_device_create(mod, auth, parent, cn, i++);
210 		if (iport == NULL)
211 			continue;
212 
213 		/*
214 		 * Now create any scsi-device nodes.
215 		 */
216 		for (j = 0, sd = di_child_node(cn); sd != DI_NODE_NIL;
217 		    sd = di_sibling_node(sd))
218 			if (strcmp(di_node_name(sd), "smp") != 0)
219 				j++;
220 		for (pi = di_path_phci_next_path(cn, DI_PATH_NIL);
221 		    pi != DI_PATH_NIL; pi = di_path_phci_next_path(cn, pi))
222 			if (di_path_client_node(pi) != NULL &&
223 			    strcmp(di_node_name(di_path_client_node(pi)),
224 			    "smp") != 0)
225 				j++;
226 		if (topo_node_range_create(mod, iport, SCSI_DEVICE, 0, j) < 0)
227 			continue;
228 		for (j = 0, sd = di_child_node(cn); sd != DI_NODE_NIL;
229 		    sd = di_sibling_node(sd))
230 			if (strcmp(di_node_name(sd), "smp") != 0)
231 				pci_scsi_device_create(mod, auth, iport, sd,
232 				    j++, NULL);
233 		for (pi = di_path_phci_next_path(cn, DI_PATH_NIL);
234 		    pi != DI_PATH_NIL; pi = di_path_phci_next_path(cn, pi))
235 			if (di_path_client_node(pi) != NULL &&
236 			    strcmp(di_node_name(di_path_client_node(pi)),
237 			    "smp") != 0)
238 				pci_scsi_device_create(mod, auth, iport,
239 				    di_path_client_node(pi),  j++, pi);
240 
241 		/*
242 		 * Now create any smp-device nodes.
243 		 */
244 		for (j = 0, smp = di_child_node(cn); smp != DI_NODE_NIL;
245 		    smp = di_sibling_node(smp))
246 			if (strcmp(di_node_name(smp), "smp") == 0)
247 				j++;
248 		if (topo_node_range_create(mod, iport, SMP_DEVICE, 0, j) < 0)
249 			continue;
250 		for (j = 0, smp = di_child_node(cn); smp != DI_NODE_NIL;
251 		    smp = di_sibling_node(smp))
252 			if (strcmp(di_node_name(smp), "smp") == 0)
253 				pci_smp_device_create(mod, auth, iport, smp,
254 				    j++);
255 	}
256 	nvlist_free(auth);
257 }
258 
259 void
pci_receptacle_instantiate(topo_mod_t * mod,tnode_t * parent,di_node_t pnode)260 pci_receptacle_instantiate(topo_mod_t *mod, tnode_t *parent, di_node_t pnode)
261 {
262 	int err, i, rcnt, lcnt;
263 	char *propstrpm, *propstrlabel, *pm, *label;
264 	nvlist_t *fmri, *auth;
265 	tnode_t	*recep;
266 
267 	rcnt = di_prop_lookup_strings(DDI_DEV_T_ANY, pnode,
268 	    DI_RECEPTACLE_PHYMASK, &propstrpm);
269 	if ((lcnt = di_prop_lookup_strings(DDI_DEV_T_ANY, pnode,
270 	    DI_RECEPTACLE_LABEL, &propstrlabel)) <= 0) {
271 		topo_mod_dprintf(mod,
272 		    "pci_receptacle_instanciate: rececptacle label not "
273 		    "found for the pci function node.\n");
274 		return;
275 	}
276 
277 	if (rcnt != lcnt) {
278 		topo_mod_dprintf(mod,
279 		    "pci_receptacle_instantiate: rececptacle label count %d "
280 		    "doesn match with phy mask count %d\n", lcnt, rcnt);
281 	}
282 
283 	label = propstrlabel;
284 	pm = propstrpm;
285 	auth = topo_mod_auth(mod, parent);
286 	for (i = 0; i < rcnt; i++) {
287 		fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION,
288 		    RECEPTACLE, i, NULL, auth, NULL, NULL, NULL);
289 		if (fmri == NULL) {
290 			topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
291 			    topo_mod_errmsg(mod));
292 			continue;
293 		}
294 		recep = topo_node_bind(mod, parent, RECEPTACLE, i, fmri);
295 		nvlist_free(fmri);
296 		if (recep == NULL) {
297 			topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
298 			    topo_mod_errmsg(mod));
299 			continue;
300 		}
301 
302 		if (label) {
303 			if (topo_node_label_set(recep, label, &err) < 0) {
304 				topo_mod_dprintf(mod,
305 				    "topo_receptacle_instantiate: "
306 				    "topo_node_label_set error(%s)\n",
307 				    topo_strerror(err));
308 			}
309 			if (i < lcnt) {
310 				label = label + strlen(label) + 1;
311 			} else {
312 				label = NULL;
313 			}
314 		}
315 
316 		if (topo_pgroup_create(recep, &storage_pgroup, &err) < 0) {
317 			topo_mod_dprintf(mod, "ses_set_expander_props: "
318 			    "create storage error %s\n", topo_strerror(err));
319 			continue;
320 		}
321 		(void) topo_prop_set_string(recep, TOPO_PGROUP_STORAGE,
322 		    TOPO_STORAGE_SAS_PHY_MASK,
323 		    TOPO_PROP_IMMUTABLE, pm, &err);
324 
325 		if (topo_method_register(mod, recep, recep_methods) != 0) {
326 			topo_mod_dprintf(mod, "topo_method_register() failed "
327 			    "on %s=%d: %s", RECEPTACLE, i,
328 			    topo_mod_errmsg(mod));
329 			/* errno set */
330 			continue;
331 		}
332 
333 		pm = pm + strlen(pm) + 1;
334 	}
335 
336 	nvlist_free(auth);
337 }
338