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