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