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
36static const topo_pgroup_info_t storage_pgroup =
37	{ TOPO_PGROUP_STORAGE, TOPO_STABILITY_PRIVATE,
38	    TOPO_STABILITY_PRIVATE, 1 };
39
40static 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
47void
48pci_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
58void
59pci_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
69static void
70pci_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
129static void
130pci_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
163static tnode_t *
164pci_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
189void
190pci_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
259void
260pci_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