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