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 * Main entry points for SUN4V Platform Independent topology enumerator
29 */
30#include <sys/types.h>
31#include <strings.h>
32#include <fm/topo_mod.h>
33#include <fm/topo_hc.h>
34#include <sys/systeminfo.h>
35#include <pi_impl.h>
36
37/*
38 * Entry point called by libtopo when enumeration is required
39 */
40static topo_enum_f	pi_enum;	/* libtopo enumeration entry point */
41
42
43/*
44 * Declare the operations vector and information structure used during
45 * module registration
46 */
47static topo_modops_t	pi_ops	= {pi_enum, NULL};
48static topo_modinfo_t	pi_modinfo	= {
49    SUN4VPI_DESC, SUN4VPI_SCHEME, SUN4VPI_VERSION, &pi_ops
50};
51
52static int pi_enum_components(pi_enum_t *, tnode_t *, const char *,
53    mde_cookie_t, mde_str_cookie_t, mde_str_cookie_t);
54
55
56/*
57 * Called by libtopo when the topo module is loaded.
58 */
59void
60_topo_init(topo_mod_t *mod, topo_version_t version)
61{
62	int		result;
63	char		isa[MAXNAMELEN];
64
65	if (getenv("TOPOSUN4VPIDBG") != NULL) {
66		/* Debugging is requested for this module */
67		topo_mod_setdebug(mod);
68	}
69	topo_mod_dprintf(mod, "sun4vpi module initializing.\n");
70
71	if (version != TOPO_VERSION) {
72		(void) topo_mod_seterrno(mod, EMOD_VER_NEW);
73		topo_mod_dprintf(mod, "incompatible topo version %d\n",
74		    version);
75		return;
76	}
77
78	/* Verify that this is a SUN4V architecture machine */
79	(void) sysinfo(SI_MACHINE, isa, MAXNAMELEN);
80	if (strncmp(isa, "sun4v", MAXNAMELEN) != 0) {
81		topo_mod_dprintf(mod, "not sun4v architecture: %s\n", isa);
82		return;
83	}
84
85	result = topo_mod_register(mod, &pi_modinfo, TOPO_VERSION);
86	if (result < 0) {
87		topo_mod_dprintf(mod, "registration failed: %s\n",
88		    topo_mod_errmsg(mod));
89
90		/* module errno already set */
91		return;
92	}
93	topo_mod_dprintf(mod, "module ready.\n");
94}
95
96
97/*
98 * Clean up any data used by the module before it is unloaded.
99 */
100void
101_topo_fini(topo_mod_t *mod)
102{
103	topo_mod_dprintf(mod, "module finishing.\n");
104
105	/* Unregister from libtopo */
106	topo_mod_unregister(mod);
107}
108
109
110/*
111 * Enumeration entry point for the SUN4V topology enumerator
112 */
113/* ARGSUSED */
114static int
115pi_enum(topo_mod_t *mod, tnode_t *t_parent, const char *name,
116    topo_instance_t min, topo_instance_t max, void *pi_private, void *data)
117{
118	int		result;
119	int		idx;
120	int		num_components;
121	size_t		csize;
122	hrtime_t	starttime;
123
124	pi_enum_t	pi;
125
126	mde_cookie_t	*components;
127	mde_str_cookie_t arc_cookie;
128	mde_str_cookie_t component_cookie;
129
130	/* Begin enumeration */
131	starttime = gethrtime();
132	topo_mod_dprintf(mod, "enumeration starting.\n");
133
134	/* Initialize the walker */
135	result = pi_walker_init(mod);
136	if (result != 0) {
137		(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
138		return (-1);
139	}
140
141	/* Open a connection to the LDOM PRI */
142	bzero(&pi, sizeof (pi_enum_t));
143	result = pi_ldompri_open(mod, &pi);
144	if (result != 0) {
145		pi_walker_fini(mod);
146		topo_mod_dprintf(mod, "could not open LDOM PRI\n");
147		(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
148		return (-1);
149	}
150	pi.mod = mod;
151
152	/*
153	 * Find the top of the components graph in the PRI using the machine
154	 * description library.
155	 */
156	num_components = pi_find_mdenodes(mod, pi.mdp, MDE_INVAL_ELEM_COOKIE,
157	    MD_STR_COMPONENTS, MD_STR_FWD, &components, &csize);
158	if (num_components < 0 || components == NULL) {
159		/* No nodes were found */
160		pi_walker_fini(mod);
161		topo_mod_dprintf(mod, "could not find components in PRI\n");
162		(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
163		return (-1);
164	}
165
166	/*
167	 * There should be a single components node.  But scan all of the
168	 * results just in case a future machine has multiple hierarchies
169	 * for some unknown reason.
170	 *
171	 * We continue to walk components nodes until they are all exhausted
172	 * and do not stop if a node cannot be enumerated.  Instead, we
173	 * enumerate what we can and return a partial-enumeration error if
174	 * there is a problem.
175	 */
176	topo_mod_dprintf(mod, "enumerating %d components hierarchies\n",
177	    num_components);
178
179	component_cookie = md_find_name(pi.mdp, MD_STR_COMPONENT);
180	arc_cookie	 = md_find_name(pi.mdp, MD_STR_FWD);
181	result = 0;
182	for (idx = 0; idx < num_components; idx++) {
183		int	skip;
184
185		/*
186		 * We have found a component hierarchy to process.  First,
187		 * make sure we are not supposed to skip the graph.
188		 */
189		skip = pi_skip_node(mod, pi.mdp, components[idx]);
190		if (skip == 0) {
191			/*
192			 * We have found a components node.  Find the top-
193			 * level nodes and create a topology tree from them.
194			 */
195			result = pi_enum_components(&pi, t_parent, name,
196			    components[idx], component_cookie, arc_cookie);
197		}
198	}
199	topo_mod_free(mod, components, csize);
200
201	/* Close our connection to the PRI */
202	pi_ldompri_close(mod, &pi);
203
204	/* Clean up after the walker */
205	pi_walker_fini(mod);
206
207	/* Complete enumeration */
208	topo_mod_dprintf(mod, "enumeration complete in %lld ms.\n",
209	    ((gethrtime() - starttime)/MICROSEC));
210
211	/* All done */
212	return (result);
213}
214
215
216/*
217 * This routined is called once for each mde node of type 'components'.  It
218 * initiates enumeration of the graph starting with with this node.
219 */
220static int
221pi_enum_components(pi_enum_t *pip, tnode_t *t_parent, const char *hc_name,
222    mde_cookie_t mde_node, mde_str_cookie_t component_cookie,
223    mde_str_cookie_t arc_cookie)
224{
225	int		result;
226
227	int		num_arcs;
228	mde_cookie_t	*arcp;
229	size_t		arcsize;
230	int		arcidx;
231
232	topo_mod_t	*mod = pip->mod;
233	md_t		*mdp = pip->mdp;
234
235	if (t_parent == NULL) {
236		topo_mod_dprintf(mod,
237		    "walker failed to create node range with a NULL parent\n");
238		(void) topo_mod_seterrno(mod, EMOD_METHOD_INVAL);
239		return (-1);
240	}
241
242	/* Determine how many children the given node has */
243	num_arcs = md_get_prop_arcs(mdp, mde_node, MD_STR_FWD, NULL, 0);
244	if (num_arcs == 0) {
245		/*
246		 * This components node has no children and is not a topo
247		 * node itself, so set partial enumeration and return.
248		 */
249		(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
250		return (0);
251	}
252	topo_mod_dprintf(mod, "node_0x%llx has %d children\n",
253	    (uint64_t)mde_node, num_arcs);
254
255	/* Get the indexes for all the child nodes and put them in an array */
256	arcsize = sizeof (mde_cookie_t) * num_arcs;
257	arcp = topo_mod_zalloc(mod, arcsize);
258	if (arcp == NULL) {
259		topo_mod_dprintf(mod, "out of memory\n");
260		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
261		return (-1);
262	}
263	num_arcs = md_get_prop_arcs(mdp, mde_node, MD_STR_FWD, arcp,
264	    arcsize);
265
266	result = 0;
267	for (arcidx = 0; arcidx < num_arcs; arcidx++) {
268		/*
269		 * Initiate walking the PRI graph starting with the current
270		 * child of the components node.
271		 */
272		result = pi_walker(pip, t_parent, hc_name,
273		    arcp[arcidx], component_cookie, arc_cookie);
274		if (result != 0) {
275			(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
276		}
277	}
278	topo_mod_free(mod, arcp, arcsize);
279
280	/*
281	 * We have now walked the entire PRI graph.  Execute any deferred
282	 * enumeration routines that need all the nodes to be available.
283	 */
284	result = pi_defer_exec(mod, mdp);
285
286	return (result);
287}
288