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 /*
28  * Create bay topology node from SMBIOS Type 136 structure, call the disk
29  * enumerator to enumerate a SATA direct attached disk.
30  */
31 
32 #include <sys/types.h>
33 #include <strings.h>
34 #include <fm/topo_mod.h>
35 #include <fm/topo_method.h>
36 #include <fm/topo_hc.h>
37 #include <sys/systeminfo.h>
38 #include <sys/smbios_impl.h>
39 #include <x86pi_impl.h>
40 
41 #define	DEVICES			"/devices"
42 #define	HBA_DRV_NAME		"ahci"
43 
44 #define	BDF(b, df) ((uint16_t)((((uint16_t)(b) << 8) & 0xFF00) | \
45 				((uint16_t)(df) & 0x00FF)));
46 
47 static const topo_pgroup_info_t io_pgroup = {
48 	TOPO_PGROUP_IO,
49 	TOPO_STABILITY_PRIVATE,
50 	TOPO_STABILITY_PRIVATE,
51 	1
52 };
53 
54 static const topo_pgroup_info_t binding_pgroup = {
55 	TOPO_PGROUP_BINDING,
56 	TOPO_STABILITY_PRIVATE,
57 	TOPO_STABILITY_PRIVATE,
58 	1
59 };
60 
61 static const topo_method_t bay_methods[] = {
62 	{ TOPO_METH_OCCUPIED, TOPO_METH_OCCUPIED_DESC,
63 	    TOPO_METH_OCCUPIED_VERSION, TOPO_STABILITY_INTERNAL,
64 	    topo_mod_hc_occupied },
65 	{ NULL }
66 };
67 
68 /*
69  * Return PCI Bus/Dev/Func
70  */
71 int
72 bay_bdf(topo_mod_t *mod, smbios_port_ext_t *epp, uint16_t *bdf)
73 {
74 	int	devt;
75 	id_t	dev_id;
76 	uint8_t	bus, dev_funct;
77 
78 	char	*f = "bay_bdf";
79 	smbios_hdl_t *shp;
80 
81 	shp = topo_mod_smbios(mod);
82 	if (shp == NULL) {
83 		topo_mod_dprintf(mod, "%s: failed to load SMBIOS\n", f);
84 		return (-1);
85 	}
86 	/*
87 	 * Depending on device type, BDF comes from either slot (type-9) or
88 	 * on-board (type-41) SMBIOS structure.
89 	 */
90 	devt = epp->smbporte_dtype;
91 	dev_id = epp->smbporte_devhdl;
92 
93 	if (devt == SMB_TYPE_SLOT) {
94 		smbios_slot_t slot;
95 		(void) smbios_info_slot(shp, dev_id, &slot);
96 		bus = slot.smbl_bus;
97 		dev_funct = slot.smbl_df;
98 	} else if (devt == SMB_TYPE_OBDEVEXT) {
99 		smbios_obdev_ext_t ob;
100 		(void) smbios_info_obdevs_ext(shp, dev_id, &ob);
101 		bus = ob.smboe_bus;
102 		dev_funct = ob.smboe_df;
103 	} else {
104 		topo_mod_dprintf(mod, "%s: unknown device type: %d\n",
105 		    f, devt);
106 		return (-1);
107 	}
108 	topo_mod_dprintf(mod, "%s: %s: bus(0x%02x) dev/func(0x%02x)\n", f,
109 	    devt == SMB_TYPE_SLOT ? "slot" : "ob dev", bus, dev_funct);
110 
111 	*bdf = BDF(bus, dev_funct);
112 
113 	return (0);
114 }
115 
116 /*
117  * Decorate topo node with pgroups.
118  */
119 int
120 bay_pgroups(topo_mod_t *mod, tnode_t *tnp, di_node_t *dnp, di_node_t *sibp,
121     char *minor_name)
122 {
123 	int		rv, err;
124 	char		*ap_path, *oc_path;
125 
126 	char		*f = "bay_pgoups";
127 
128 	/*
129 	 * Create "io" pgroup and attachment point path.
130 	 */
131 	rv = topo_pgroup_create(tnp, &io_pgroup, &err);
132 	if (rv != 0) {
133 		topo_mod_dprintf(mod,
134 		    "%s: failed to create \"io\" pgroup: %s\n",
135 		    f, topo_strerror(err));
136 		(void) topo_mod_seterrno(mod, err);
137 		return (err);
138 	}
139 
140 	ap_path = topo_mod_alloc(mod, MAXPATHLEN);
141 	if (ap_path == NULL) {
142 		topo_mod_dprintf(mod, "%s: ap_path alloc failed\n");
143 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
144 	}
145 	(void) snprintf(ap_path, MAXPATHLEN, "%s%s:%s", DEVICES,
146 	    di_devfs_path(*dnp), minor_name);
147 	topo_mod_dprintf(mod, "%s: ap_path(%s)\n", f, ap_path);
148 
149 	/* add ap-path */
150 	rv = topo_prop_set_string(tnp, TOPO_PGROUP_IO, TOPO_IO_AP_PATH,
151 	    TOPO_PROP_IMMUTABLE, ap_path, &err);
152 	if (rv != 0) {
153 		topo_mod_dprintf(mod, "%s: failed to set ap-path: %s\n",
154 		    f, topo_strerror(err));
155 		topo_mod_free(mod, ap_path, MAXPATHLEN);
156 		(void) topo_mod_seterrno(mod, err);
157 		return (err);
158 	}
159 	topo_mod_free(mod, ap_path, MAXPATHLEN);
160 
161 	/*
162 	 * Create "binding" pgroup and occupant path.
163 	 */
164 	rv = topo_pgroup_create(tnp, &binding_pgroup, &err);
165 	if (rv != 0) {
166 		topo_mod_dprintf(mod,
167 		    "%s: failed to create \"io\" pgroup: %s\n",
168 		    f, topo_strerror(err));
169 		(void) topo_mod_seterrno(mod, err);
170 		return (err);
171 	}
172 
173 	oc_path = di_devfs_path(*sibp);
174 	if (oc_path == NULL) {
175 		topo_mod_dprintf(mod, "%s: no occupant path\n", f);
176 		return (-1);
177 	}
178 	topo_mod_dprintf(mod, "%s: oc_path(%s)\n", f, oc_path);
179 
180 	/* add ocupant-path */
181 	rv = topo_prop_set_string(tnp, TOPO_PGROUP_BINDING,
182 	    TOPO_BINDING_OCCUPANT, TOPO_PROP_IMMUTABLE, oc_path,
183 	    &err);
184 	if (rv != 0) {
185 		topo_mod_dprintf(mod, "%s: failed to set ap-path: %s\n",
186 		    f, topo_strerror(err));
187 		di_devfs_path_free(oc_path);
188 		(void) topo_mod_seterrno(mod, err);
189 		return (err);
190 	}
191 	di_devfs_path_free(oc_path);
192 
193 	return (0);
194 }
195 
196 int
197 bay_update_tnode(topo_mod_t *mod, tnode_t *tnodep, uint16_t bdf, int phy)
198 {
199 	int		rv;
200 	int		minor_cnt = 0;
201 	char		*minor_name = NULL;
202 	di_node_t	devtree, dnode, sib;
203 	di_minor_t	minor = DI_MINOR_NIL;
204 
205 	char		*f = "bay_update_tnode";
206 
207 	/*
208 	 * Find HBA device node from BDF.
209 	 */
210 	devtree = topo_mod_devinfo(mod);
211 	if (devtree == DI_NODE_NIL) {
212 		topo_mod_dprintf(mod, "%s: failed to get dev tree\n", f);
213 		return (-1);
214 	}
215 	for (dnode = di_drv_first_node(HBA_DRV_NAME, devtree);
216 	    dnode != DI_NODE_NIL;
217 	    dnode = di_drv_next_node(dnode)) {
218 		if (bdf == x86pi_bdf(mod, dnode)) {
219 			/*
220 			 * Match child node from PHY.
221 			 */
222 			sib = di_child_node(dnode);
223 			while (sib != DI_NODE_NIL) {
224 				if (phy == x86pi_phy(mod, sib))
225 					break;
226 				sib = di_sibling_node(sib);
227 			}
228 			break;
229 		}
230 	}
231 	if (dnode == DI_NODE_NIL) {
232 		topo_mod_dprintf(mod, "%s: no HBA di_node\n", f);
233 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
234 	}
235 
236 	/*
237 	 * HBA attachment point minor node name.
238 	 */
239 	while ((minor = di_minor_next(dnode, minor)) != DI_MINOR_NIL) {
240 		if (strncmp(DDI_NT_SATA_ATTACHMENT_POINT,
241 		    di_minor_nodetype(minor),
242 		    strlen(DDI_NT_SATA_ATTACHMENT_POINT)) == 0) {
243 			if (phy == minor_cnt++) {
244 				minor_name = di_minor_name(minor);
245 				topo_mod_dprintf(mod,
246 				    "%s: phy(%d) minor name(%s)\n",
247 				    f, phy, minor_name);
248 				break;
249 			}
250 		}
251 	}
252 
253 	rv = bay_pgroups(mod, tnodep, &dnode, &sib, minor_name);
254 	if (rv != 0) {
255 		topo_mod_dprintf(mod, "%s: failed to add pgroups\n", f);
256 		return (-1);
257 	}
258 
259 
260 	return (0);
261 }
262 
263 /*
264  * x86pi_gen_bay:
265  *   create "bay" node
266  *   call "disk" enum passing in "bay" node
267  */
268 int
269 x86pi_gen_bay(topo_mod_t *mod, tnode_t *t_parent, smbios_port_ext_t *eport,
270     int instance)
271 {
272 	int		rv;
273 	int		min = 0, max = 0;
274 	id_t		port_id;
275 	uint16_t	bdf;
276 	smbios_port_t	smb_port;
277 	x86pi_hcfmri_t	hcfmri = {0};
278 	tnode_t		*tn_bay;
279 
280 	char		*f = "x86pi_gen_disk";
281 	smbios_hdl_t *shp;
282 
283 	shp = topo_mod_smbios(mod);
284 	if (shp == NULL) {
285 		topo_mod_dprintf(mod, "%s: failed to load SMBIOS\n", f);
286 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
287 	}
288 
289 	/*
290 	 * Label comes from the port (type-8) SMBIOS structure.
291 	 */
292 	port_id = eport->smbporte_port;
293 
294 	rv = smbios_info_port(shp, port_id, &smb_port);
295 	if (rv != 0) {
296 		topo_mod_dprintf(mod,
297 		    "%s: failed to get port %d SMBIOS struct\n",
298 		    f, port_id);
299 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
300 	}
301 
302 	/*
303 	 * Fill in hcfmri info.
304 	 */
305 	hcfmri.hc_name = BAY;
306 	hcfmri.instance = instance;
307 	hcfmri.location = x86pi_cleanup_smbios_str(mod, smb_port.smbo_eref, 0);
308 
309 	/*
310 	 * Create "bay" node.
311 	 */
312 	rv = x86pi_enum_generic(mod, &hcfmri, t_parent, t_parent, &tn_bay, 0);
313 	if (rv != 0) {
314 		topo_mod_dprintf(mod,
315 		    "%s: failed to create %s topo node: %d\n",
316 		    f, BAY, instance);
317 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
318 	}
319 
320 	/* free up location string */
321 	if (hcfmri.location != NULL) {
322 		topo_mod_strfree(mod, (char *)hcfmri.location);
323 	}
324 
325 	/*
326 	 * Determine the bay BDF.
327 	 */
328 	rv = bay_bdf(mod, eport, &bdf);
329 	if (rv != 0) {
330 		topo_mod_dprintf(mod, "%s: failed to get BDF\n", f);
331 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
332 	}
333 	topo_mod_dprintf(mod, "%s: BDF(0x%04x)\n", f, bdf);
334 
335 	/*
336 	 * Decorate bay topo node.
337 	 */
338 	rv = bay_update_tnode(mod, tn_bay, bdf, eport->smbporte_phy);
339 	if (rv != 0) {
340 		topo_mod_dprintf(mod, "%s: failed to decorate bay node\n", f);
341 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
342 	}
343 
344 	if (topo_method_register(mod, tn_bay, bay_methods) != 0) {
345 		topo_mod_dprintf(mod, "topo_method_register() failed on "
346 		    "%s=%d: %s", BAY, instance,
347 		    topo_mod_errmsg(mod));
348 		/* errno set */
349 		return (-1);
350 	}
351 
352 	/*
353 	 * Call disk enum passing in decorated bay topo node.
354 	 */
355 	if (topo_mod_load(mod, DISK, TOPO_VERSION) == NULL) {
356 		topo_mod_dprintf(mod, "%s: Failed to load %s module: %s\n",
357 		    f, DISK, topo_strerror(topo_mod_errno(mod)));
358 		return (topo_mod_errno(mod));
359 	}
360 
361 	rv = topo_node_range_create(mod, tn_bay, DISK, min, max);
362 	if (rv != 0) {
363 		topo_mod_dprintf(mod, "%s: failed to create range: %s\n", f,
364 		    topo_strerror(topo_mod_errno(mod)));
365 		return (topo_mod_errno(mod));
366 	}
367 
368 	rv = topo_mod_enumerate(mod, tn_bay, DISK, DISK, min, max, NULL);
369 	if (rv != 0) {
370 		topo_mod_dprintf(mod, "%s: %s enumeration failed: %s\n", f,
371 		    DISK, topo_strerror(topo_mod_errno(mod)));
372 		return (topo_mod_errno(mod));
373 	}
374 
375 	topo_mod_dprintf(mod, "%s: done.\n", f);
376 
377 	return (0);
378 }
379