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#include <strings.h>
29#include <fm/topo_mod.h>
30#include <fm/topo_hc.h>
31#include <sys/fm/protocol.h>
32#include <sys/fm/ldom.h>
33#include <sys/mdesc.h>
34#include <assert.h>
35#include <sys/systeminfo.h>
36#include "xaui.h"
37
38/*
39 * xaui.c
40 *	sun4v specific xaui enumerators
41 */
42
43#ifdef __cplusplus
44extern "C" {
45#endif
46
47#define	XAUI_VERSION		TOPO_VERSION
48#define	XFP_MAX			1	/* max number of xfp per xaui card */
49
50static int xaui_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
51		    topo_instance_t, void *, void *);
52
53static const topo_modops_t xaui_ops =
54	{ xaui_enum, NULL };
55
56const topo_modinfo_t xaui_info =
57	{XAUI, FM_FMRI_SCHEME_HC, XAUI_VERSION, &xaui_ops};
58
59static const topo_pgroup_info_t xaui_auth_pgroup = {
60	FM_FMRI_AUTHORITY,
61	TOPO_STABILITY_PRIVATE,
62	TOPO_STABILITY_PRIVATE,
63	1
64};
65
66static topo_mod_t *xaui_mod_hdl = NULL;
67static int freeprilabel = 0;
68static int ispci = 0;
69
70/*ARGSUSED*/
71void
72_topo_init(topo_mod_t *mod, topo_version_t version)
73{
74	/*
75	 * Turn on module debugging output
76	 */
77	if (getenv("TOPOXAUIDBG") != NULL)
78		topo_mod_setdebug(mod);
79	topo_mod_dprintf(mod, "initializing xaui enumerator\n");
80
81	if (topo_mod_register(mod, &xaui_info, TOPO_VERSION) < 0) {
82		topo_mod_dprintf(mod, "xaui registration failed: %s\n",
83		    topo_mod_errmsg(mod));
84		return; /* mod errno already set */
85	}
86	topo_mod_dprintf(mod, "xaui enum initd\n");
87}
88
89void
90_topo_fini(topo_mod_t *mod)
91{
92	topo_mod_unregister(mod);
93}
94
95static tnode_t *
96xaui_tnode_create(topo_mod_t *mod, tnode_t *parent,
97    const char *name, topo_instance_t i, void *priv)
98{
99	int err;
100	nvlist_t *fmri;
101	tnode_t *ntn;
102	nvlist_t *auth = topo_mod_auth(mod, parent);
103
104	fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i,
105	    NULL, auth, NULL, NULL, NULL);
106	nvlist_free(auth);
107
108	if (fmri == NULL) {
109		topo_mod_dprintf(mod,
110		    "Unable to make nvlist for %s bind: %s.\n",
111		    name, topo_mod_errmsg(mod));
112		return (NULL);
113	}
114
115	ntn = topo_node_bind(mod, parent, name, i, fmri);
116	nvlist_free(fmri);
117	if (ntn == NULL) {
118		topo_mod_dprintf(mod,
119		    "topo_node_bind (%s%d/%s%d) failed: %s\n",
120		    topo_node_name(parent), topo_node_instance(parent),
121		    name, i,
122		    topo_strerror(topo_mod_errno(mod)));
123		return (NULL);
124	}
125
126	topo_node_setspecific(ntn, priv);
127	if (topo_pgroup_create(ntn, &xaui_auth_pgroup, &err) == 0) {
128		(void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
129		    FM_FMRI_AUTH_PRODUCT, &err);
130		(void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
131		    FM_FMRI_AUTH_PRODUCT_SN, &err);
132		(void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
133		    FM_FMRI_AUTH_CHASSIS, &err);
134		(void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
135		    FM_FMRI_AUTH_SERVER, &err);
136	}
137	return (ntn);
138}
139
140
141static int
142xaui_fru_set(topo_mod_t *mp, tnode_t *tn)
143{
144	nvlist_t *fmri;
145	int err, e;
146
147	if (topo_node_resource(tn, &fmri, &err) < 0 ||
148	    fmri == NULL) {
149		topo_mod_dprintf(mp, "FRU_fmri_set error: %s\n",
150		    topo_strerror(topo_mod_errno(mp)));
151		return (topo_mod_seterrno(mp, err));
152	}
153	e = topo_node_fru_set(tn, fmri, 0, &err);
154	nvlist_free(fmri);
155	if (e < 0)
156		return (topo_mod_seterrno(mp, err));
157	return (0);
158}
159
160
161static void *
162xaui_topo_alloc(size_t size)
163{
164	assert(xaui_mod_hdl != NULL);
165	return (topo_mod_alloc(xaui_mod_hdl, size));
166}
167
168
169static void
170xaui_topo_free(void *data, size_t size)
171{
172	assert(xaui_mod_hdl != NULL);
173	topo_mod_free(xaui_mod_hdl, data, size);
174}
175
176
177/*
178 * Remove the 3 character device name (pci/niu) from devfs path.
179 */
180static char *
181xaui_trans_str(topo_mod_t *mod, char *dn, char *p, size_t buf_len)
182{
183	int i = 0;
184	int j = 0;
185	char buf[MAXPATHLEN];
186
187	topo_mod_dprintf(mod, "xaui_trans_str: dev path(%s) dev name(%s)\n",
188	    dn, p);
189	do {
190		/* strip out either "pci" or "niu" */
191		if (dn[i] == p[0] && dn[i + 1] == p[1] && dn[i + 2] == p[2])
192			i += 3;
193		else
194			buf[j++] = dn[i++];
195	} while (i < buf_len);
196
197	topo_mod_dprintf(mod, "xaui_trans_str: return(%s)\n", buf);
198	return (topo_mod_strdup(mod, (char *)buf));
199}
200
201
202static char *
203xaui_get_path(topo_mod_t *mod, void *priv, topo_instance_t inst)
204{
205	di_node_t dnode;
206	char *devfs_path;
207	char *path;
208	char *buf = NULL;
209	size_t buf_len;
210	size_t dev_path_len;
211	size_t path_len;
212
213	/*
214	 * There are two ways to get here:
215	 * 1. niu enum  - private data is the di_node_t for this xaui
216	 *		- instance is the ethernet function number
217	 *    device path looks like: /niu@80/network@0:nxge@0
218	 *    PRI path looks like:    /@80/@0
219	 *
220	 * 2. pcibus enum - private data is the parent tnode_t
221	 *		  - instance is the pci function number
222	 *    device path looks like: /pci@500/pci@0/pci@8/network@0:nxge0
223	 *    PRI path looks like:    /@500/@0/@8/@0
224	 *
225	 *    PRI path for pciex is /@Bus/@Dev/@Func/@Instance
226	 */
227	if (ispci == 1) {
228		/* coming from pcibus */
229		topo_mod_dprintf(mod, "from pcibus\n");
230		dnode = topo_node_getspecific((tnode_t *)priv);
231	} else {
232		/* coming from niu */
233		topo_mod_dprintf(mod, "from niu\n");
234		dnode = (struct di_node *)priv;
235	}
236	if (dnode == DI_NODE_NIL) {
237		topo_mod_dprintf(mod, "DI_NODE_NIL\n");
238		return (NULL);
239	}
240
241	/* get device path */
242	devfs_path = di_devfs_path(dnode);
243	if (devfs_path == NULL) {
244		topo_mod_dprintf(mod, "NULL devfs_path\n");
245		return (NULL);
246	}
247	topo_mod_dprintf(mod, "devfs_path (%s)\n", devfs_path);
248	dev_path_len = strlen(devfs_path) + 1;
249
250	/* remove device name from path */
251	if (ispci == 1) {
252		topo_mod_dprintf(mod, "ispci\n");
253		buf = xaui_trans_str(mod, devfs_path, "pci", dev_path_len);
254		buf_len = strlen(buf) + 1;
255	} else {
256		buf = xaui_trans_str(mod, devfs_path, "niu", dev_path_len);
257		buf_len = strlen(buf) + 1;
258	}
259	di_devfs_path_free(devfs_path);
260
261	/* lop off "/network@" */
262	buf[(strstr(buf, "/network@") - buf)] = '\0';
263
264	/* path: transposed address + '/@instance' (0/1) + '\0' */
265	path_len = strlen(buf) + 3 + 1;
266	path = (char *)xaui_topo_alloc(path_len);
267	if (snprintf(path, path_len, "%s/@%d", buf, inst) < 0) {
268		topo_mod_dprintf(mod, "snprintf failed\n");
269		path = NULL;
270	}
271	xaui_topo_free(buf, buf_len);
272
273	/* should return something like /@500/@0/@8/@0 or /@80/@0 */
274	topo_mod_dprintf(mod, "xaui_get_path: path(%s)\n", path);
275	return (path);
276}
277
278
279static int
280xaui_get_pri_label(topo_mod_t *mod, topo_instance_t n, void *priv,
281    char **labelp)
282{
283	ldom_hdl_t *hdlp;
284	uint32_t type = 0;
285	ssize_t bufsize = 0;
286	uint64_t *bufp;
287	md_t *mdp;
288	int num_nodes, ncomp;
289	mde_cookie_t *listp;
290	char *pstr = NULL;
291	int i;
292	char *path;
293
294	/* Get device path minus the device names */
295	path = xaui_get_path(mod, priv, n);
296	if (path == NULL) {
297		topo_mod_dprintf(mod, "can't get path\n");
298		return (-1);
299	}
300
301	hdlp = ldom_init(xaui_topo_alloc, xaui_topo_free);
302	if (hdlp == NULL) {
303		topo_mod_dprintf(mod, "ldom_init failed\n");
304		return (-1);
305	}
306
307	(void) ldom_get_type(hdlp, &type);
308	if ((type & LDOM_TYPE_CONTROL) != 0) {
309		bufsize = ldom_get_core_md(hdlp, &bufp);
310	} else {
311		bufsize = ldom_get_local_md(hdlp, &bufp);
312	}
313	if (bufsize < 1) {
314		topo_mod_dprintf(mod, "failed to get pri/md (%d)\n", bufsize);
315		ldom_fini(hdlp);
316		return (-1);
317	}
318
319	if ((mdp = md_init_intern(bufp, xaui_topo_alloc, xaui_topo_free)) ==
320	    NULL || (num_nodes = md_node_count(mdp)) < 1) {
321		topo_mod_dprintf(mod, "md_init_intern failed\n");
322		xaui_topo_free(bufp, (size_t)bufsize);
323		ldom_fini(hdlp);
324		return (-1);
325	}
326
327	if ((listp = (mde_cookie_t *)xaui_topo_alloc(
328	    sizeof (mde_cookie_t) * num_nodes)) == NULL) {
329		topo_mod_dprintf(mod, "can't alloc listp\n");
330		xaui_topo_free(bufp, (size_t)bufsize);
331		(void) md_fini(mdp);
332		ldom_fini(hdlp);
333		return (-1);
334	}
335
336	ncomp = md_scan_dag(mdp, MDE_INVAL_ELEM_COOKIE,
337	    md_find_name(mdp, "component"),
338	    md_find_name(mdp, "fwd"), listp);
339	if (ncomp <= 0) {
340		topo_mod_dprintf(mod, "no component nodes found\n");
341		xaui_topo_free(listp, sizeof (mde_cookie_t) * num_nodes);
342		xaui_topo_free(bufp, (size_t)bufsize);
343		(void) md_fini(mdp);
344		ldom_fini(hdlp);
345		return (-1);
346	}
347	topo_mod_dprintf(mod, "number of comps (%d)\n", ncomp);
348
349	for (i = 0; i < ncomp; i++) {
350		/*
351		 * Look for type == "io", topo-hc-name == "xaui";
352		 * match "path" md property.
353		 */
354		if ((md_get_prop_str(mdp, listp[i], "type", &pstr) == 0) &&
355		    (pstr != NULL) &&
356		    (strncmp(pstr, "io", strlen(pstr)) == 0) &&
357		    (md_get_prop_str(mdp, listp[i], "topo-hc-name", &pstr)
358		    == 0) && (pstr != NULL) &&
359		    (strncmp(pstr, "xaui", strlen(pstr)) == 0) &&
360		    (md_get_prop_str(mdp, listp[i], "path", &pstr) == 0) &&
361		    (pstr != NULL)) {
362			/* check node path */
363			if (strncmp(pstr, path, sizeof (path)) == 0) {
364				/* this is the node, grab the label */
365				if (md_get_prop_str(mdp, listp[i], "nac",
366				    &pstr) == 0) {
367					*labelp = topo_mod_strdup(mod, pstr);
368					/* need to free this later */
369					freeprilabel = 1;
370					break;
371				}
372			}
373		}
374	}
375
376	xaui_topo_free(listp, sizeof (mde_cookie_t) * num_nodes);
377	xaui_topo_free(bufp, (size_t)bufsize);
378	(void) md_fini(mdp);
379	ldom_fini(hdlp);
380
381	if (path != NULL) {
382		xaui_topo_free(path, strlen(path) + 1);
383	}
384	return (0);
385}
386
387
388static int
389xaui_label_set(topo_mod_t *mod, tnode_t *node, topo_instance_t n, void *priv)
390{
391	const char *label = NULL;
392	char *plat, *pp;
393	int err;
394	int i, p;
395
396	(void) xaui_get_pri_label(mod, n, priv, (char **)&label);
397	if (label == NULL) {
398		topo_mod_dprintf(mod, "no PRI node for label\n");
399		if (Phyxaui_Names == NULL)
400			return (-1);
401
402		if (topo_prop_get_string(node,
403		    FM_FMRI_AUTHORITY, FM_FMRI_AUTH_PRODUCT, &plat, &err) < 0) {
404			return (topo_mod_seterrno(mod, err));
405		}
406		/*
407		 * Trim SUNW, from the platform name
408		 */
409		pp = strchr(plat, ',');
410		if (pp == NULL)
411			pp = plat;
412		else
413			++pp;
414
415		for (p = 0; p < Phyxaui_Names->psn_nplats; p++) {
416			if (strcmp(Phyxaui_Names->psn_names[p].pnm_platform,
417			    pp) != 0)
418				continue;
419			for (i = 0; i < Phyxaui_Names->psn_names[p].pnm_nnames;
420			    i++) {
421				physnm_t ps;
422				ps = Phyxaui_Names->psn_names[p].pnm_names[i];
423				if (ps.ps_num == n) {
424					label = ps.ps_label;
425					break;
426				}
427			}
428			break;
429		}
430		topo_mod_strfree(mod, plat);
431	}
432
433	if (label != NULL) {
434		if (topo_prop_set_string(node, TOPO_PGROUP_PROTOCOL,
435		    TOPO_PROP_LABEL, TOPO_PROP_IMMUTABLE,
436		    label, &err) != 0) {
437			if (freeprilabel == 1) {
438				topo_mod_strfree(mod, (char *)label);
439			}
440			return (topo_mod_seterrno(mod, err));
441		}
442		if (freeprilabel == 1) {
443			topo_mod_strfree(mod, (char *)label);
444		}
445	}
446
447	return (0);
448}
449
450
451/*ARGSUSED*/
452static tnode_t *
453xaui_declare(tnode_t *parent, const char *name, topo_instance_t i,
454	void *priv, topo_mod_t *mod)
455{
456	tnode_t *ntn;
457	nvlist_t *fmri = NULL;
458	int e;
459
460	if ((ntn = xaui_tnode_create(mod, parent, name, i, NULL)) == NULL) {
461		topo_mod_dprintf(mod, "%s ntn = NULL\n", name);
462		return (NULL);
463	}
464
465	(void) xaui_fru_set(mod, ntn);
466
467	/* when coming from pcibus: private data == parent tnode */
468	if (priv == (void *)parent) {
469		ispci = 1;
470	}
471
472	(void) xaui_label_set(mod, ntn, i, priv);
473
474	/* reset pcibus/niu switch */
475	ispci = 0;
476
477	/* set ASRU to resource fmri */
478	if (topo_prop_get_fmri(ntn, TOPO_PGROUP_PROTOCOL,
479	    TOPO_PROP_RESOURCE, &fmri, &e) == 0)
480		(void) topo_node_asru_set(ntn, fmri, 0, &e);
481	nvlist_free(fmri);
482
483	if (topo_node_range_create(mod, ntn, XFP,
484	    0, XFP_MAX) < 0) {
485		topo_node_unbind(ntn);
486		topo_mod_dprintf(mod, "child_range_add of XFP"
487		    "failed: %s\n",
488		    topo_strerror(topo_mod_errno(mod)));
489		return (NULL); /* mod_errno already set */
490	}
491	return (ntn);
492}
493
494
495static topo_mod_t *
496xfp_enum_load(topo_mod_t *mp)
497{
498	topo_mod_t *rp = NULL;
499
500	if ((rp = topo_mod_load(mp, XFP, TOPO_VERSION)) == NULL) {
501		topo_mod_dprintf(mp,
502		    "%s enumerator could not load %s enum.\n", XAUI, XFP);
503	}
504	return (rp);
505}
506
507
508/*ARGSUSED*/
509static int
510xaui_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
511	topo_instance_t min, topo_instance_t max, void *arg, void *priv)
512{
513	tnode_t *xauin;
514
515	if (strcmp(name, XAUI) != 0) {
516		topo_mod_dprintf(mod,
517		    "Currently only know how to enumerate %s components.\n",
518		    XAUI);
519		return (0);
520	}
521
522	xaui_mod_hdl = mod;
523
524	/*
525	 * Load XFP enum
526	 */
527	if (xfp_enum_load(mod) == NULL)
528		return (-1);
529
530	if ((xauin = xaui_declare(rnode, name, min, priv, mod)) == NULL)
531		return (-1);
532
533	/* set the private data to be the instance number of niufn */
534	if (topo_mod_enumerate(mod,
535	    xauin, XFP, XFP, 0, 0, NULL) != 0) {
536		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
537	}
538	return (0);
539}
540