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 a topology node for a PRI node of type 'pciexrc'
29 */
30 #include <sys/types.h>
31 #include <strings.h>
32 #include <sys/fm/protocol.h>
33 #include <fm/topo_mod.h>
34 #include <fm/topo_hc.h>
35 #include <libdevinfo.h>
36 #include <sys/pci.h>
37 #include "pi_impl.h"
38
39 #define PCIEX_MAX_DEVICE 255
40 #define PCIEX_MAX_BDF_SIZE 23 /* '0x' + sizeof (UNIT64_MAX) + '\0' */
41
42 #define TOPO_PGROUP_PCIEX "pciex"
43 #define _ENUM_NAME "enum_pciexrc"
44
45 static char *drv_name = NULL;
46
47 static const topo_pgroup_info_t io_pgroup =
48 { TOPO_PGROUP_IO, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
49
50 static const topo_pgroup_info_t pci_pgroup =
51 { TOPO_PGROUP_PCI, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
52
53 static int pi_enum_pciexrc_finddev(topo_mod_t *, md_t *, mde_cookie_t,
54 tnode_t *);
55
56 static char *pi_enum_pciexrc_findbdf(topo_mod_t *, di_node_t);
57
58 static int pi_enum_pciexrc_defer(topo_mod_t *, md_t *, mde_cookie_t,
59 topo_instance_t, tnode_t *, const char *, tnode_t *, void *);
60
61
62 /*
63 * Create a pciexrc topo by calling the pciexrc enumerator for this instance.
64 */
65 int
pi_enum_pciexrc(topo_mod_t * mod,md_t * mdp,mde_cookie_t mde_node,topo_instance_t inst,tnode_t * t_parent,const char * hc_name,tnode_t ** t_node)66 pi_enum_pciexrc(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
67 topo_instance_t inst, tnode_t *t_parent, const char *hc_name,
68 tnode_t **t_node)
69 {
70 int result;
71
72 topo_mod_dprintf(mod, "%s called for node_0x%llx type %s\n",
73 _ENUM_NAME, (uint64_t)mde_node, hc_name);
74
75 *t_node = NULL;
76
77 /*
78 * Create the root complex topo node. Use the generic enumerator to
79 * do this, and then we will add more attributes below.
80 */
81 result = pi_enum_generic_impl(mod, mdp, mde_node, inst, t_parent,
82 t_parent, hc_name, _ENUM_NAME, t_node, 0);
83 if (result != 0 || *t_node == NULL) {
84 topo_mod_dprintf(mod,
85 "%s node_0x%llx failed to create topo node: %s\n",
86 _ENUM_NAME, (uint64_t)mde_node,
87 topo_strerror(topo_mod_errno(mod)));
88 return (result);
89 }
90
91 /* Update the topo node with more specific information */
92 result = pi_enum_update(mod, mdp, mde_node, t_parent, *t_node,
93 hc_name);
94 if (result != 0) {
95 topo_mod_dprintf(mod,
96 "%s node_0x%llx failed to create node properites: %s\n",
97 _ENUM_NAME, (uint64_t)mde_node,
98 topo_strerror(topo_mod_errno(mod)));
99 return (result);
100 }
101
102 result = pi_enum_pciexrc_finddev(mod, mdp, mde_node, *t_node);
103 if (result == 0) {
104 /*
105 * The node exists in this domain. We will call the PCIBUS
106 * enumerator after the entire PRI graph has been walked so
107 * that all the possible FRU nodes are available for bus's
108 * that span multiple FRU boundaries.
109 */
110 result = pi_defer_add(mod, mde_node, t_parent, *t_node,
111 pi_enum_pciexrc_defer, NULL);
112 if (result != 0) {
113 /* We cannot defer the call, so we need to do it now */
114 result = pi_enum_pciexrc_defer(mod, mdp, mde_node, inst,
115 t_parent, hc_name, *t_node, NULL);
116 }
117 } else {
118 /*
119 * It is OK if the node does not exist for further PCIBUS
120 * enumeration. We can return success having created the
121 * root complex node itself.
122 */
123 result = 0;
124 }
125 topo_mod_dprintf(mod, "%s added node_0x%llx type %s\n",
126 _ENUM_NAME, (uint64_t)mde_node, hc_name);
127
128 return (result);
129 }
130
131
132 /* ARGSUSED */
133 static int
pi_enum_pciexrc_defer(topo_mod_t * mod,md_t * mdp,mde_cookie_t mde_node,topo_instance_t inst,tnode_t * t_parent,const char * hc_name,tnode_t * t_node,void * private)134 pi_enum_pciexrc_defer(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
135 topo_instance_t inst, tnode_t *t_parent, const char *hc_name,
136 tnode_t *t_node, void *private)
137 {
138 int result;
139 topo_instance_t min;
140 topo_instance_t max;
141
142 topo_mod_dprintf(mod,
143 "%s node_0x%llx deferred enumeration starting\n", _ENUM_NAME,
144 (uint64_t)mde_node);
145
146 /* Make sure our dependent modules are loaded */
147 if (topo_mod_load(mod, PCI_BUS, TOPO_VERSION) == NULL) {
148 topo_mod_dprintf(mod, "%s could not load %s module: %s\n",
149 _ENUM_NAME, PCI_BUS, topo_strerror(topo_mod_errno(mod)));
150 return (-1);
151 }
152
153 /* Create a node range for children of this bus */
154 min = 0;
155 max = PCIEX_MAX_DEVICE;
156 result = topo_node_range_create(mod, t_node, PCI_BUS, min, max);
157 if (result != 0) {
158 topo_mod_dprintf(mod,
159 "%s node_0x%llx failed to create node range: %s\n",
160 _ENUM_NAME, topo_strerror(topo_mod_errno(mod)));
161 return (-1);
162 }
163
164 /*
165 * Invoke the pcibus enumerator for this node.
166 */
167 result = topo_mod_enumerate(mod, t_node, PCI_BUS, PCIEX_BUS,
168 min, max, NULL);
169 if (result != 0) {
170 topo_mod_dprintf(mod,
171 "%s node_0x%llx enumeration failed: %s\n", _ENUM_NAME,
172 (uint64_t)mde_node, topo_strerror(topo_mod_errno(mod)));
173 }
174
175 topo_mod_dprintf(mod, "%s added node_0x%llx type %s\n",
176 _ENUM_NAME, (uint64_t)mde_node, hc_name);
177
178 return (result);
179 }
180
181
182 /*
183 * Update PCIEXRC/HOSTBRIDGE topo node with node-specific information
184 *
185 * The following is mostly a duplicate of code contained in:
186 * usr/src/lib/fm/topo/modules/sun4v/cpuboard/
187 * cpuboard_hostbridge.c:cpuboard_rc_node_create
188 */
189 int
pi_enum_update(topo_mod_t * mod,md_t * mdp,mde_cookie_t mde_node,tnode_t * t_parent,tnode_t * t_node,const char * hc_name)190 pi_enum_update(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
191 tnode_t *t_parent, tnode_t *t_node, const char *hc_name)
192 {
193 int result;
194 int err;
195 int is_hbridge = 0;
196 int is_pciexrc = 0;
197 char *path = NULL;
198 char *bdf = NULL;
199 char *_enum_name;
200 nvlist_t *modfmri;
201 nvlist_t *devfmri;
202 di_node_t dnode;
203
204 /*
205 * Determine if decorating a PCIE root complex or a hostbridge
206 * node.
207 */
208 if (strncmp(hc_name, PCIEX_ROOT, strlen(hc_name)) == 0) {
209 is_pciexrc = 1;
210 _enum_name = "enum_pciexrc";
211 } else if (strncmp(hc_name, HOSTBRIDGE, strlen(hc_name)) == 0) {
212 is_hbridge = 1;
213 _enum_name = "enum_hostbridge";
214 } else {
215 topo_mod_dprintf(mod,
216 "pi_enum_update node_0x%llx unknown hc name %s\n",
217 (uint64_t)mde_node, hc_name);
218 return (-1);
219 }
220
221 if (t_parent == NULL || t_node == NULL) {
222 topo_mod_dprintf(mod, "%s node_0x%llx has no parent\n",
223 _enum_name, (uint64_t)mde_node);
224 return (-1);
225 }
226
227 /*
228 * Calculate the device path for this root complex node.
229 */
230 path = pi_get_path(mod, mdp, mde_node);
231 if (path == NULL) {
232 if (is_hbridge == 1) {
233 /* "path" not required for hostbridge */
234 return (0);
235 }
236 topo_mod_dprintf(mod, "%s node_0x%llx has no path\n",
237 _enum_name, (uint64_t)mde_node);
238 return (-1);
239 }
240
241 /*
242 * Set the ASRU for this node using the dev scheme
243 */
244 devfmri = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION, path, NULL);
245 if (devfmri == NULL) {
246 topo_mod_dprintf(mod, "%s node_0x%llx fmri creation failed\n",
247 _enum_name, (uint64_t)mde_node);
248 result = -1;
249 goto out;
250 }
251
252 result = topo_node_asru_set(t_node, devfmri, 0, &err);
253 nvlist_free(devfmri);
254 if (result != 0) {
255 topo_mod_dprintf(mod, "%s node_0x%llx failed to set ASRU\n",
256 _enum_name, (uint64_t)mde_node);
257 (void) topo_mod_seterrno(mod, err);
258 goto out;
259 }
260
261 /*
262 * Create property groups.
263 */
264 result = topo_pgroup_create(t_node, &io_pgroup, &err);
265 if (result < 0) {
266 topo_mod_dprintf(mod, "%s node_0x%llx "
267 "topo_pgroup_create for io pgroup failed\n",
268 _enum_name, (uint64_t)mde_node);
269 (void) topo_mod_seterrno(mod, err);
270 goto out;
271 }
272
273 if (is_pciexrc == 1) {
274 result = topo_pgroup_create(t_node, &pci_pgroup, &err);
275 if (result < 0) {
276 topo_mod_dprintf(mod, "%s node_0x%llx "
277 "topo_pgroup_create for pci pgroup failed\n",
278 _enum_name, (uint64_t)mde_node);
279 (void) topo_mod_seterrno(mod, err);
280 goto out;
281 }
282 }
283
284 result = topo_prop_set_string(t_node, TOPO_PGROUP_IO, TOPO_IO_DEV,
285 TOPO_PROP_IMMUTABLE, path, &err);
286 if (result != 0) {
287 topo_mod_dprintf(mod,
288 "%s node_0x%llx failed to set DEV property\n",
289 _enum_name, (uint64_t)mde_node);
290 (void) topo_mod_seterrno(mod, err);
291 goto out;
292 }
293
294 /* device type is always "pciex" */
295 result = topo_prop_set_string(t_node, TOPO_PGROUP_IO, TOPO_IO_DEVTYPE,
296 TOPO_PROP_IMMUTABLE, TOPO_PGROUP_PCIEX, &err);
297 if (result < 0) {
298 topo_mod_dprintf(mod,
299 "%s node_0x%llx failed to set DEVTYPE property\n",
300 _enum_name, (uint64_t)mde_node);
301 (void) topo_mod_seterrno(mod, err);
302 goto out;
303 }
304
305 /*
306 * Derived the driver name from the device path.
307 */
308 dnode = di_init(path, DIIOC);
309 if (dnode == DI_NODE_NIL) {
310 topo_mod_dprintf(mod, "%s node_0x%llx failed to get node\n",
311 _enum_name, (uint64_t)mde_node);
312 result = -1;
313 goto out;
314 }
315 drv_name = di_driver_name(dnode);
316 if (drv_name == NULL) {
317 topo_mod_dprintf(mod, "%s node_0x%llx failed to get driver "
318 " name\n", _enum_name, (uint64_t)mde_node);
319 di_fini(dnode);
320 result = -1;
321 goto out;
322 }
323
324 if (is_pciexrc == 1) {
325 /*
326 * Derived the BDF property from the devinfo node.
327 */
328 bdf = pi_enum_pciexrc_findbdf(mod, dnode);
329 if (bdf == NULL) {
330 topo_mod_dprintf(mod, "%s: node_0x%llx failed to "
331 "find BDF", _enum_name, (uint64_t)mde_node);
332 di_fini(dnode);
333 result = -1;
334 goto out;
335 }
336 }
337 di_fini(dnode);
338 topo_mod_dprintf(mod, "%s node_0x%llx driver name is %s\n",
339 _enum_name, (uint64_t)mde_node, drv_name);
340
341 result = topo_prop_set_string(t_node, TOPO_PGROUP_IO, TOPO_IO_DRIVER,
342 TOPO_PROP_IMMUTABLE, drv_name, &err);
343 if (result < 0) {
344 topo_mod_dprintf(mod,
345 "%s node_0x%llx failed to set DRIVER property\n",
346 _enum_name, (uint64_t)mde_node);
347 (void) topo_mod_seterrno(mod, err);
348 goto out;
349 }
350
351 modfmri = topo_mod_modfmri(mod, FM_MOD_SCHEME_VERSION, drv_name);
352 if (modfmri == NULL) {
353 topo_mod_dprintf(mod,
354 "%s node_0x%llx failed to create module fmri\n",
355 _enum_name, (uint64_t)mde_node);
356 (void) topo_mod_seterrno(mod, err);
357 result = -1;
358 goto out;
359 }
360 result = topo_prop_set_fmri(t_node, TOPO_PGROUP_IO, TOPO_IO_MODULE,
361 TOPO_PROP_IMMUTABLE, modfmri, &err);
362 nvlist_free(modfmri);
363 if (result < 0) {
364 topo_mod_dprintf(mod,
365 "%s node_0x%llx failed to set MODULE property\n",
366 _enum_name, (uint64_t)mde_node);
367 (void) topo_mod_seterrno(mod, err);
368 goto out;
369 }
370
371 if (is_pciexrc == 1) {
372 /* This is a PCIEX root complex */
373 result = topo_prop_set_string(t_node, TOPO_PGROUP_PCI,
374 TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_ROOT, &err);
375 if (result < 0) {
376 topo_mod_dprintf(mod,
377 "%s node_0x%llx failed to set EXCAP property\n",
378 _enum_name, (uint64_t)mde_node);
379 (void) topo_mod_seterrno(mod, err);
380 goto out;
381 }
382
383 /* Set BDF for root complex */
384 result = topo_prop_set_string(t_node, TOPO_PGROUP_PCI,
385 TOPO_PCI_BDF, TOPO_PROP_IMMUTABLE, bdf, &err);
386 if (result < 0) {
387 topo_mod_dprintf(mod,
388 "%s node_0x%llx failed to set BDF property\n",
389 _enum_name, (uint64_t)mde_node);
390 (void) topo_mod_seterrno(mod, err);
391 goto out;
392 }
393
394 /* Create a node range for the children of this root complex */
395 result = topo_node_range_create(mod, t_node, PCIEX_BUS, 0,
396 PCIEX_MAX_DEVICE);
397 if (result != 0) {
398 topo_mod_dprintf(mod,
399 "%s node_0x%llx failed to create %s range\n",
400 _enum_name, (uint64_t)mde_node, PCIEX_BUS);
401 result = -1;
402 }
403 }
404
405 out:
406 if (path != NULL) {
407 topo_mod_strfree(mod, path);
408 }
409 if (bdf != NULL) {
410 topo_mod_strfree(mod, bdf);
411 }
412 return (result);
413 }
414
415
416 static int
pi_enum_pciexrc_finddev(topo_mod_t * mod,md_t * mdp,mde_cookie_t mde_node,tnode_t * t_node)417 pi_enum_pciexrc_finddev(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
418 tnode_t *t_node)
419 {
420 di_node_t devtree;
421 di_node_t dnode;
422 char *path;
423
424 /* Initialize the device information structure for this module */
425 devtree = topo_mod_devinfo(mod);
426 if (devtree == DI_NODE_NIL) {
427 topo_mod_dprintf(mod, "devinfo init failed\n");
428 return (-1);
429 }
430
431 /*
432 * Find the PRI node path property. This will be used to associate
433 * the PRI node with the device node.
434 */
435 path = pi_get_path(mod, mdp, mde_node);
436 if (path == NULL) {
437 topo_mod_dprintf(mod, "node_0x%llx has no path\n",
438 (uint64_t)mde_node);
439 return (-1);
440 }
441
442 /*
443 * Scan the device node list and find the node associated with
444 * the given PRI node. Equality is defined when the PRI path
445 * is the same as the device node path.
446 */
447 dnode = di_drv_first_node(drv_name, devtree);
448 while (dnode != DI_NODE_NIL) {
449 char *devfs_path;
450
451 devfs_path = di_devfs_path(dnode);
452 if (devfs_path != NULL) {
453 if (strncmp(devfs_path, path, strlen(path)) == 0) {
454 /* We have found the matching dnode */
455 break;
456 }
457 }
458
459 /* We have not found the matching dnode yet */
460 dnode = di_drv_next_node(dnode);
461 }
462 if (dnode != DI_NODE_NIL) {
463 topo_mod_dprintf(mod, "%s node_0x%llx found dev path %s\n",
464 _ENUM_NAME, (uint64_t)mde_node, path);
465
466 /*
467 * Associate this dnode with the topo node. The PCI
468 * enumerator requires this information.
469 */
470 topo_node_setspecific(t_node, (void *)dnode);
471 }
472
473 topo_mod_strfree(mod, path);
474 return (0);
475 }
476
477
478 /*
479 * Find the BDF property and return as a string.
480 *
481 * The string must be freed with topo_mod_strfree()
482 */
483 static char *
pi_enum_pciexrc_findbdf(topo_mod_t * mod,di_node_t dnode)484 pi_enum_pciexrc_findbdf(topo_mod_t *mod, di_node_t dnode)
485 {
486 uint_t reg;
487 uint_t bdf;
488 char bdf_str[PCIEX_MAX_BDF_SIZE];
489 unsigned char *buf;
490 di_prop_t di_prop;
491 di_prom_handle_t di_prom_hdl;
492 di_prom_prop_t di_prom_prop;
493
494 /*
495 * Look for the "reg" property from the devinfo node.
496 */
497 for (di_prop = di_prop_next(dnode, DI_PROP_NIL);
498 di_prop != DI_PROP_NIL;
499 di_prop = di_prop_next(dnode, di_prop)) {
500 if (strncmp(di_prop_name(di_prop), "reg",
501 sizeof (reg)) == 0) {
502 if (di_prop_bytes(di_prop, &buf) < sizeof (uint_t)) {
503 continue;
504 }
505 bcopy(buf, ®, sizeof (uint_t));
506 break;
507 }
508 }
509
510 /*
511 * If the "reg" property is not found in the di_node; look for it in
512 * OBP prom data.
513 */
514 if (di_prop == DI_PROP_NIL) {
515 if ((di_prom_hdl = topo_mod_prominfo(mod)) ==
516 DI_PROM_HANDLE_NIL) {
517 topo_mod_dprintf(mod,
518 "%s failed to get prom handle\n", _ENUM_NAME);
519 return (NULL);
520 }
521 for (di_prom_prop =
522 di_prom_prop_next(di_prom_hdl, dnode, DI_PROM_PROP_NIL);
523 di_prom_prop != DI_PROM_PROP_NIL;
524 di_prom_prop =
525 di_prom_prop_next(di_prom_hdl, dnode, di_prom_prop)) {
526 if (strncmp(di_prom_prop_name(di_prom_prop), "reg",
527 sizeof (reg)) == 0) {
528 if (di_prom_prop_data(di_prom_prop, &buf) <
529 sizeof (uint_t)) {
530 continue;
531 }
532 bcopy(buf, ®, sizeof (uint_t));
533 break;
534 }
535 }
536 if (di_prom_prop == DI_PROP_NIL) {
537 topo_mod_dprintf(mod,
538 "%s failed to get reg property\n", _ENUM_NAME);
539 return (NULL);
540 }
541 }
542
543 /*
544 * Caculate BDF
545 *
546 * The reg property is divided like this:
547 * -----------------------------------------------------
548 * | 23 Bus 16 | 15 Dev 11 | 10 Fn 8 | 7 Reg 0 |
549 * -----------------------------------------------------
550 *
551 * PCI_REG_* macros strip off Reg and shift to get individual
552 * Bus/Dev/Fn bits. Shift and OR each to get bdf value.
553 */
554 bdf = (PCI_REG_BUS_G(reg) << 8) | (PCI_REG_DEV_G(reg) << 3) |
555 PCI_REG_FUNC_G(reg);
556
557 /* Pass BDF back as a string */
558 (void) snprintf(bdf_str, PCIEX_MAX_BDF_SIZE, "0x%x", bdf);
559 topo_mod_dprintf(mod, "%s found BDF %s\n", _ENUM_NAME, bdf_str);
560
561 return (topo_mod_strdup(mod, bdf_str));
562 }
563