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 #include <strings.h>
28 #include <sys/fm/protocol.h>
29 #include <fm/topo_hc.h>
30 #include <fm/topo_mod.h>
31 
32 #include <hb_sun4.h>
33 #include <hostbridge.h>
34 #include <pcibus.h>
35 #include <did.h>
36 #include <util.h>
37 
38 #include "hb_mdesc.h"
39 
40 static const topo_pgroup_info_t io_pgroup =
41 	{ TOPO_PGROUP_IO, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
42 static const topo_pgroup_info_t pci_pgroup =
43 	{ TOPO_PGROUP_PCI, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
44 
45 /*
46  * get_rcs()
47  * Description:
48  *     Return a list of PX instances in the dev tree.
49  */
50 static busorrc_t *
get_rcs(topo_mod_t * mod)51 get_rcs(topo_mod_t *mod)
52 {
53 	busorrc_t *rcs = NULL;
54 	di_node_t devtree;
55 	di_node_t pnode;
56 
57 	/* Scan for buses, top-level devinfo nodes with the right driver */
58 	devtree = topo_mod_devinfo(mod);
59 	if (devtree == DI_NODE_NIL) {
60 		topo_mod_dprintf(mod, "devinfo init failed.\n");
61 		return (NULL);
62 	}
63 	pnode = di_drv_first_node(PX, devtree);
64 	while (pnode != DI_NODE_NIL) {
65 		if (busorrc_add(mod, &rcs, pnode) < 0) {
66 			topo_mod_dprintf(mod, "busorrc_add() failed.\n");
67 			busorrc_free(mod, rcs);
68 			return (NULL);
69 		}
70 		pnode = di_drv_next_node(pnode);
71 	}
72 	return (rcs);
73 }
74 
75 /*
76  * find_dnode()
77  * Description:
78  *     Find the dev pointer of a rc given its bus address, ba
79  */
80 static di_node_t
find_dnode(busorrc_t * rcs,uint64_t ba)81 find_dnode(busorrc_t *rcs, uint64_t ba)
82 {
83 	busorrc_t *p;
84 	for (p = rcs; p != NULL; p = p->br_nextbus) {
85 		if (ba == p->br_ba_bc) {
86 			return (p->br_din);
87 		}
88 	}
89 	return (NULL);
90 }
91 
92 /*
93  * hb_tnode_create()
94  * Description:
95  *     Create a topo node
96  */
97 static tnode_t *
hb_tnode_create(topo_mod_t * mod,tnode_t * parent,const char * name,int inst,void * priv)98 hb_tnode_create(topo_mod_t *mod, tnode_t *parent, const char *name,
99     int inst, void *priv)
100 {
101 	int err;
102 	tnode_t *node;
103 	nvlist_t *fmri;
104 	nvlist_t *auth = topo_mod_auth(mod, parent);
105 
106 	if (parent == NULL || inst < 0) {
107 		return (NULL);
108 	}
109 
110 	/* Create FMRI */
111 	if ((fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name,
112 	    inst, NULL, auth, NULL, NULL, NULL)) == NULL) {
113 		topo_mod_dprintf(mod, "create of tnode for %s failed: %s\n",
114 		    name, topo_strerror(topo_mod_errno(mod)));
115 		nvlist_free(auth);
116 		return (NULL);
117 	}
118 	nvlist_free(auth);
119 
120 	/* Create and bind node  */
121 	node = topo_node_bind(mod, parent, name, inst, fmri);
122 	if (node == NULL) {
123 		nvlist_free(fmri);
124 		topo_mod_dprintf(mod, "unable to bind a node(%s): %s\n",
125 		    name, topo_strerror(topo_mod_errno(mod)));
126 		return (NULL); /* mod_errno already set */
127 	}
128 
129 	nvlist_free(fmri);
130 	topo_node_setspecific(node, priv);
131 
132 	/* Inherit the parent 's FRU and label */
133 	(void) topo_node_fru_set(node, NULL, 0, &err);
134 	(void) topo_node_label_set(node, NULL, &err);
135 
136 	return (node);
137 }
138 
139 /*
140  * platform_pciexhostbridge_declare()
141  * Description:
142  *     This is a sun4v specific function to create the hostbridge topo node.
143  */
144 tnode_t *
platform_pciexhostbridge_declare(topo_mod_t * mod,tnode_t * parent,topo_instance_t inst)145 platform_pciexhostbridge_declare(topo_mod_t *mod, tnode_t *parent,
146     topo_instance_t inst)
147 {
148 	tnode_t *hbn;
149 	void *priv = NULL;
150 
151 	topo_mod_dprintf(mod, "Create node %s=%d\n", HOSTBRIDGE, inst);
152 
153 	hbn = hb_tnode_create(mod, parent, HOSTBRIDGE, inst, priv);
154 	if (hbn == NULL) {
155 		topo_mod_dprintf(mod, "Failed to create node %s=%d\n",
156 		    HOSTBRIDGE, inst);
157 		return (NULL);
158 	}
159 
160 	/* Make room for children */
161 	(void) topo_node_range_create(mod, hbn, PCIEX_ROOT, 0, MAX_HB_BUSES);
162 
163 	return (hbn);
164 }
165 
166 /*
167  * platform_pciexhostbridge_declare()
168  * Description:
169  *     This is a sun4v specific function to create a root complex topo node,
170  *     but do not enumerate its pci buses.
171  */
172 static tnode_t *
platform_pciexrc_declare(topo_mod_t * mod,tnode_t * parent,int inst,uint64_t ba)173 platform_pciexrc_declare(topo_mod_t *mod, tnode_t *parent, int inst,
174     uint64_t ba)
175 {
176 	int err;
177 	tnode_t *rcn;
178 	char dnpath[MAXPATHLEN];
179 	nvlist_t *fmri;
180 
181 	topo_mod_dprintf(mod, "Create node %s=%d\n", PCIEX_ROOT, inst);
182 
183 	rcn = hb_tnode_create(mod, parent, PCIEX_ROOT, inst, NULL);
184 	if (rcn == NULL) {
185 		topo_mod_dprintf(mod, "Failed to create node %s=%d\n",
186 		    PCIEX_ROOT, inst);
187 		return (NULL);
188 	}
189 
190 	/* Set ASRU to be the dev-scheme asru */
191 	(void) snprintf(dnpath, sizeof (dnpath), "/pci@%llx", ba);
192 	fmri = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION, dnpath, NULL);
193 	if (fmri == NULL) {
194 		topo_mod_dprintf(mod, "dev:///%s fmri creation failed.\n",
195 		    dnpath);
196 		return (NULL);
197 	}
198 	if (topo_node_asru_set(rcn, fmri, 0, &err) < 0) {
199 		topo_mod_dprintf(mod, "topo_node_asru_set failed\n");
200 		(void) topo_mod_seterrno(mod, err);
201 		nvlist_free(fmri);
202 		return (NULL);
203 	}
204 	nvlist_free(fmri);
205 
206 	/*
207 	 * Set properties of the root complex node pciexrc
208 	 */
209 
210 	/* Add the io and pci property groups */
211 	if (topo_pgroup_create(rcn, &io_pgroup, &err) < 0) {
212 		topo_mod_dprintf(mod, "topo_pgroup_create(iogrp) failed\n");
213 		(void) topo_mod_seterrno(mod, err);
214 		return (NULL);
215 	}
216 	if (topo_pgroup_create(rcn, &pci_pgroup, &err) < 0) {
217 		topo_mod_dprintf(mod, "topo_pgroup_create(pcigrp) failed\n");
218 		(void) topo_mod_seterrno(mod, err);
219 		return (NULL);
220 	}
221 	/* Add the devfs path property */
222 	if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DEV,
223 	    TOPO_PROP_IMMUTABLE, dnpath, &err) != 0) {
224 		topo_mod_dprintf(mod, "Failed to set %s property\n",
225 		    TOPO_IO_DEV);
226 		(void) topo_mod_seterrno(mod, err);
227 		return (NULL);
228 	}
229 
230 	/* for sun4v,  device type is always pciex */
231 	if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DEVTYPE,
232 	    TOPO_PROP_IMMUTABLE, PCIEXTYPE, &err) != 0) {
233 		topo_mod_dprintf(mod, "Failed to set %s property\n",
234 		    TOPO_IO_DEVTYPE);
235 	}
236 
237 	/* sun4v rc driver is always "px" */
238 	if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DRIVER,
239 	    TOPO_PROP_IMMUTABLE, PX, &err) != 0) {
240 		topo_mod_dprintf(mod, "Failed to set %s property\n",
241 		    TOPO_IO_DRIVER);
242 	}
243 	if ((fmri = topo_mod_modfmri(mod, FM_MOD_SCHEME_VERSION, PX)) == NULL ||
244 	    (topo_prop_set_fmri(rcn, TOPO_PGROUP_IO, TOPO_IO_MODULE,
245 	    TOPO_PROP_IMMUTABLE, fmri,  &err) != 0)) {
246 		topo_mod_dprintf(mod, "Failed to set %s property\n",
247 		    TOPO_IO_MODULE);
248 	}
249 	nvlist_free(fmri);
250 
251 	/* This is a PCIEX Root Complex */
252 	if (topo_prop_set_string(rcn, TOPO_PGROUP_PCI, TOPO_PCI_EXCAP,
253 	    TOPO_PROP_IMMUTABLE, PCIEX_ROOT, &err) != 0) {
254 		topo_mod_dprintf(mod, "Failed to set %s property\n",
255 		    TOPO_PCI_EXCAP);
256 	}
257 
258 	/* Make room for children */
259 	(void) topo_node_range_create(mod, rcn, PCIEX_BUS, 0, MAX_HB_BUSES);
260 
261 	return (rcn);
262 }
263 
264 /*
265  * platform_hb_enum()
266  * Description:
267  *   This is an entry function to enumerate the sun4v hostbridges. First, it
268  *   reads the hostbridges and their pciexrc root complexes from the PRI or
269  *   MD.
270  *   For the current sun4v platforms, it is assummed that there is only one
271  *   hostbridge. All the pciex root complexes belong to this single hostbridge.
272  *   Given the hostbridge/pciexrc information, this enumerator creates the
273  *   the hostbridge topo node and pciexrc nodes. If the domain owns the
274  *   the root complex, it uses the common hostbridge code to enumerate the
275  *   pcibus. If not, it simply create the hostbridge/pciexrc nodes without the
276  *   fabric.
277  */
278 /*ARGSUSED*/
279 int
platform_hb_enum(topo_mod_t * mod,tnode_t * parent,const char * name,topo_instance_t imin,topo_instance_t imax)280 platform_hb_enum(topo_mod_t *mod, tnode_t *parent, const char *name,
281     topo_instance_t imin, topo_instance_t imax)
282 {
283 	int i, j;
284 	int err = 0;
285 	md_hb_t *hbp;
286 	md_rc_t *rcp;
287 	md_info_t hbmd;
288 	tnode_t **hbnode;
289 	int nhbnode = 0;
290 	tnode_t **rcnode;
291 	int nrcs, nrcnode = 0;
292 	busorrc_t *rcs;
293 
294 	if (imin > imax) {
295 		topo_mod_dprintf(mod, "Invalid hb range(%d,%d)\n", imin, imax);
296 		return (-1);
297 	}
298 
299 	/* get the hostbrige and rootcomplex information in the PRI/MD */
300 	(void) bzero((void *) &hbmd, sizeof (hbmd));
301 	if (hb_mdesc_init(mod, &hbmd) != 0) {
302 		topo_mod_dprintf(mod, "failed to get hb from the PRI/MD\n");
303 		return (-1);
304 	}
305 
306 	/* count the number of hb and rc in the PRI/MD */
307 	nrcs = 0;
308 	for (i = 0, hbp = hbmd.hbs; i < hbmd.shbs; i++, hbp++) {
309 		if (hbp->id < 0)
310 			continue;
311 		nrcs += hbp->srcs;
312 	}
313 	if (hbmd.shbs <= 0 || nrcs <= 0) {
314 		topo_mod_dprintf(mod, "No hostbridge or pciex bus is found\n");
315 		topo_node_range_destroy(parent, HOSTBRIDGE);
316 		hb_mdesc_fini(mod, &hbmd);
317 		return (0);
318 	}
319 	hbnode = topo_mod_zalloc(mod, hbmd.shbs * sizeof (tnode_t *));
320 	rcnode = topo_mod_zalloc(mod, nrcs * sizeof (tnode_t *));
321 	rcs = get_rcs(mod);
322 
323 	/* process the hostbridge */
324 	for (i = imin; (i <= imax) && (err == 0); i++) {
325 		int brd = 0;
326 		di_node_t dnode1, dnode2;
327 
328 		if ((hbp = hb_find_hb(&hbmd, i)) == NULL) {
329 			continue;
330 		}
331 
332 		dnode2 = NULL;
333 		for (j = 0, rcp = hbp->rcs; j < hbp->srcs; j++, rcp++) {
334 			if (rcp->id < 0)
335 				continue;
336 			dnode1 = find_dnode(rcs, rcp->cfg_handle);
337 			if (dnode1 != NULL) {
338 				dnode2 = dnode1;
339 				if (did_create(mod, dnode1, brd, hbp->id,
340 				    rcp->id, rcp->id) == NULL) {
341 					err = -1;
342 					break;
343 				}
344 			}
345 		}
346 
347 		if (err != 0)
348 			break;
349 
350 		/*
351 		 * If this hb has a rc in the dev tree, use the common code to
352 		 * create a hostbridge node
353 		 */
354 		if (dnode2 != NULL) {
355 			hbnode[nhbnode] = pciexhostbridge_declare(mod, parent,
356 			    dnode2, hbp->id);
357 		} else {
358 			/* platformm specific */
359 			hbnode[nhbnode] = platform_pciexhostbridge_declare(mod,
360 			    parent, hbp->id);
361 		}
362 		if (hbnode[nhbnode] == NULL) {
363 			err = -1;
364 			break;
365 		}
366 
367 		/*
368 		 * Create the pciexrc nodes under the hostbridge node
369 		 * If a rc exists in the dev tree, use the common code to
370 		 * create a pciexrc node and enumerate the fabric.
371 		 * Otherwise, only create the pciexrc node.
372 		 */
373 		for (j = 0, rcp = hbp->rcs; j < hbp->nrcs; j++, rcp++) {
374 			if (rcp->id < 0) {
375 				topo_mod_dprintf(mod, "skip invalid rc[%d]\n",
376 				    j);
377 				continue;
378 			}
379 			dnode1 = find_dnode(rcs, rcp->cfg_handle);
380 			if (dnode1 != NULL) {
381 				/* declare a pciexrc and enumerate its pcibus */
382 				rcnode[nrcnode] = rc_process(mod,
383 				    hbnode[nhbnode], rcp->id, dnode1);
384 			} else {
385 				/* only declare the pciexrc */
386 				rcnode[nrcnode] = platform_pciexrc_declare(mod,
387 				    hbnode[nhbnode], rcp->id, rcp->cfg_handle);
388 			}
389 			if (rcnode[nrcnode] == NULL) {
390 				err = -1;
391 				break;
392 			}
393 			nrcnode++;
394 		}
395 
396 		nhbnode++;
397 	}
398 
399 	/* failure: unbind all hb and rc tnodes */
400 	if (err != 0) {
401 		for (i = 0; i < nhbnode; i++)
402 			topo_node_unbind(hbnode[i]);
403 		for (i = 0; i < nrcnode; i++)
404 			topo_node_unbind(rcnode[i]);
405 	}
406 
407 	topo_mod_free(mod, hbnode, hbmd.shbs * sizeof (tnode_t *));
408 	topo_mod_free(mod, rcnode, nrcs * sizeof (tnode_t *));
409 	hb_mdesc_fini(mod, &hbmd);
410 	busorrc_free(mod, rcs);
411 
412 	return (err);
413 }
414 
415 /*ARGSUSED*/
416 int
platform_hb_label(topo_mod_t * mod,tnode_t * node,nvlist_t * in,nvlist_t ** out)417 platform_hb_label(topo_mod_t *mod, tnode_t *node, nvlist_t *in, nvlist_t **out)
418 {
419 	return (labelmethod_inherit(mod, node, in, out));
420 }
421