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, &reg, 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, &reg, 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