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 < 0 || imax < 0 || 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