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
45static const topo_pgroup_info_t io_pgroup = {
46	TOPO_PGROUP_IO,
47	TOPO_STABILITY_PRIVATE,
48	TOPO_STABILITY_PRIVATE,
49	1
50};
51
52static 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 */
62int
63bay_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 */
110int
111bay_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
187int
188bay_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 */
259int
260x86pi_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