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
bay_bdf(topo_mod_t * mod,smbios_port_ext_t * epp,uint16_t * bdf)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
bay_pgroups(topo_mod_t * mod,tnode_t * tnp,di_node_t * dnp,di_node_t * sibp,char * minor_name)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
bay_update_tnode(topo_mod_t * mod,tnode_t * tnodep,uint16_t bdf,int phy)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
x86pi_gen_bay(topo_mod_t * mod,tnode_t * t_parent,smbios_port_ext_t * eport,int instance)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