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
45static char *drv_name = NULL;
46
47static const topo_pgroup_info_t io_pgroup =
48	{ TOPO_PGROUP_IO, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
49
50static const topo_pgroup_info_t pci_pgroup =
51	{ TOPO_PGROUP_PCI, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
52
53static int pi_enum_pciexrc_finddev(topo_mod_t *, md_t *, mde_cookie_t,
54    tnode_t *);
55
56static char *pi_enum_pciexrc_findbdf(topo_mod_t *, di_node_t);
57
58static 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 */
65int
66pi_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 */
133static int
134pi_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 */
189int
190pi_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
405out:
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
416static int
417pi_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 */
483static char *
484pi_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